Commit 6f812a5b authored by cmrd Senya's avatar cmrd Senya Committed by Benjamin Neff

Add LinksController

LinksController redirects requests for provided diaspora:// links to
respective entities urls.
parent 2e0b3826
# frozen_string_literal: true
class LinksController < ApplicationController
def resolve
entity = DiasporaLinkService.new(query).find_or_fetch_entity
raise ActiveRecord::RecordNotFound if entity.nil?
redirect_to url_for(entity)
end
private
def query
@query ||= params.fetch(:q)
end
end
......@@ -22,8 +22,7 @@ class Reference < ApplicationRecord
private
def add_reference(author, type, guid)
class_name = DiasporaFederation::Entity.entity_class(type).to_s.rpartition("::").last
entity = Diaspora::Federation::Mappings.model_class_for(class_name).find_by(guid: guid)
entity = Diaspora::EntityFinder.new(type, guid).find
references.find_or_create_by(target: entity) if entity&.diaspora_handle == author
rescue => e # rubocop:disable Lint/RescueWithoutErrorClass
logger.warn "ignoring invalid diaspora-url: diaspora://#{author}/#{type}/#{guid}: #{e.class}: #{e.message}"
......
# frozen_string_literal: true
# Encapsulates logic of processing diaspora:// links
class DiasporaLinkService
attr_reader :type, :author, :guid
def initialize(link)
@link = link.dup
parse
end
def find_or_fetch_entity
entity_finder.find || fetch_entity
end
private
attr_accessor :link
def fetch_entity
DiasporaFederation::Federation::Fetcher.fetch_public(author, type, guid)
entity_finder.find
rescue DiasporaFederation::Federation::Fetcher::NotFetchable
nil
end
def entity_finder
@entity_finder ||= Diaspora::EntityFinder.new(type, guid)
end
def normalize
link.gsub!(%r{^web\+diaspora://}, "diaspora://") ||
link.gsub!(%r{^//}, "diaspora://") ||
%r{^diaspora://}.match(link) ||
self.link = "diaspora://#{link}"
end
def parse
normalize
match = DiasporaFederation::Federation::DiasporaUrlParser::DIASPORA_URL_REGEX.match(link)
@author = match[1]
@type = match[2]
@guid = match[3]
end
end
......@@ -74,6 +74,8 @@ Rails.application.routes.draw do
#Search
get 'search' => "search#search"
get "link" => "links#resolve"
resources :conversations, except: %i(edit update destroy) do
resources :messages, only: %i(create)
delete 'visibility' => 'conversation_visibilities#destroy'
......
# frozen_string_literal: true
module Diaspora
class EntityFinder
def initialize(type, guid)
@type = type
@guid = guid
end
def class_name
@class_name ||= DiasporaFederation::Entity.entity_class(type).to_s.rpartition("::").last
end
def find
Diaspora::Federation::Mappings.model_class_for(class_name).find_by(guid: guid)
end
private
attr_reader :type, :guid
end
end
# frozen_string_literal: true
describe LinksController, type: :controller do
describe "#resolve" do
context "with post" do
let(:post) { FactoryGirl.create(:status_message) }
let(:link_text) { "#{post.author.diaspora_handle}/post/#{post.guid}" }
subject { get :resolve, params: {q: link_query} }
shared_examples "redirects to the post" do
it "redirects to the post" do
expect(subject).to redirect_to(post_url(post))
end
end
context "with stripped link text" do
let(:link_query) { link_text }
include_examples "redirects to the post"
end
context "with link text starting with //" do
let(:link_query) { "//#{link_text}" }
include_examples "redirects to the post"
end
context "with link text starting with diaspora://" do
let(:link_query) { "diaspora://#{link_text}" }
include_examples "redirects to the post"
end
context "with link text starting with web+diaspora://" do
let(:link_query) { "web+diaspora://#{link_text}" }
include_examples "redirects to the post"
end
context "when post is non-fetchable" do
let(:diaspora_id) { FactoryGirl.create(:person).diaspora_handle }
let(:guid) { "1234567890abcdef" }
let(:link_query) { "web+diaspora://#{diaspora_id}/post/#{guid}" }
before do
expect(DiasporaFederation::Federation::Fetcher)
.to receive(:fetch_public)
.with(diaspora_id, "post", guid)
.and_raise(DiasporaFederation::Federation::Fetcher::NotFetchable)
end
it "responds 404" do
expect { subject }.to raise_error(ActiveRecord::RecordNotFound)
end
end
context "when user is non-fetchable" do
let(:diaspora_id) { "unknown@pod.tld" }
let(:guid) { "1234567890abcdef" }
let(:link_query) { "web+diaspora://#{diaspora_id}/post/#{guid}" }
before do
expect(Person)
.to receive(:find_or_fetch_by_identifier)
.with(diaspora_id)
.and_return(nil)
end
it "responds 404" do
expect { subject }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end
end
# frozen_string_literal: true
describe DiasporaLinkService do
let(:service) { described_class.new(link) }
describe "#find_or_fetch_entity" do
context "when entity is known" do
let(:post) { FactoryGirl.create(:status_message) }
let(:link) { "diaspora://#{post.author.diaspora_handle}/post/#{post.guid}" }
it "returns the entity" do
expect(service.find_or_fetch_entity).to eq(post)
end
end
context "when entity is unknown" do
let(:remote_person) { FactoryGirl.create(:person) }
let(:guid) { "1234567890abcdef" }
let(:link) { "diaspora://#{remote_person.diaspora_handle}/post/#{guid}" }
it "fetches entity" do
expect(DiasporaFederation::Federation::Fetcher)
.to receive(:fetch_public)
.with(remote_person.diaspora_handle, "post", guid) {
FactoryGirl.create(:status_message, author: remote_person, guid: guid)
}
entity = service.find_or_fetch_entity
expect(entity).to be_a(StatusMessage)
expect(entity.guid).to eq(guid)
expect(entity.author).to eq(remote_person)
end
it "returns nil when entity is non fetchable" do
expect(DiasporaFederation::Federation::Fetcher)
.to receive(:fetch_public)
.with(remote_person.diaspora_handle, "post", guid)
.and_raise(DiasporaFederation::Federation::Fetcher::NotFetchable)
expect(service.find_or_fetch_entity).to be_nil
end
end
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment