Add LinksController
LinksController redirects requests for provided diaspora:// links to respective entities urls.
This commit is contained in:
parent
2e0b382699
commit
6f812a5b8f
7 changed files with 201 additions and 2 deletions
16
app/controllers/links_controller.rb
Normal file
16
app/controllers/links_controller.rb
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# 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}"
|
||||
|
|
|
|||
45
app/services/diaspora_link_service.rb
Normal file
45
app/services/diaspora_link_service.rb
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# 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'
|
||||
|
|
|
|||
22
lib/diaspora/entity_finder.rb
Normal file
22
lib/diaspora/entity_finder.rb
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# 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
|
||||
71
spec/controllers/links_controller_spec.rb
Normal file
71
spec/controllers/links_controller_spec.rb
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# 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
|
||||
44
spec/services/diaspora_link_service_spec.rb
Normal file
44
spec/services/diaspora_link_service_spec.rb
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# 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
|
||||
Loading…
Reference in a new issue