From 9e0a3aaf08e7d4fc700f7f19d61f5f8fee346b6a Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 10 Jun 2026 17:55:27 +0200 Subject: [PATCH] Resolve links to unknown remote objects when posting (#38711) --- app/services/process_links_service.rb | 7 ++++-- spec/services/process_links_service_spec.rb | 27 +++++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/app/services/process_links_service.rb b/app/services/process_links_service.rb index e7dfb64c8e..7fb29020a0 100644 --- a/app/services/process_links_service.rb +++ b/app/services/process_links_service.rb @@ -23,12 +23,15 @@ class ProcessLinksService < BaseService def scan_text! urls = @status.text.scan(FetchLinkCardService::URL_PATTERN).map { |array| Addressable::URI.parse(array[1]).normalize } + domains = urls.map(&:domain).uniq + valid_domains = Instance.searchable.where(domain: domains).pluck(:domain) + urls.each do |url| # We only support `FeaturedCollection` at this time - # TODO: We probably want to resolve unknown objects at authoring time object = ActivityPub::TagManager.instance.uri_to_resource(url.to_s, Collection) - next if object.nil? + object ||= ResolveURLService.new.call(url.to_s) if valid_domains.include?(url.domain) + next unless object.is_a?(Collection) tagged_object = @previous_objects.find { |x| x.object == object || x.uri == url } tagged_object ||= @current_objects.find { |x| x.object == object || x.uri == url } diff --git a/spec/services/process_links_service_spec.rb b/spec/services/process_links_service_spec.rb index 367eaa0177..41625ec9d2 100644 --- a/spec/services/process_links_service_spec.rb +++ b/spec/services/process_links_service_spec.rb @@ -9,17 +9,34 @@ RSpec.describe ProcessLinksService do context 'when status mentions known collections' do let!(:collection) { Fabricate(:collection) } - let(:status) { Fabricate(:status, account: account, text: "Hello check out this collection! #{ActivityPub::TagManager.instance.uri_for(collection)}", visibility: :public) } + let(:status) { Fabricate(:status, account: account, text:, visibility: :public) } - it 'creates a tagged object' do - expect { subject.call(status) } - .to change { status.tagged_objects.count }.by(1) + context 'when the collection is mentioned by URI' do + let(:text) { "Hello check out this collection! #{ActivityPub::TagManager.instance.uri_for(collection)}" } + + it 'creates a tagged object' do + expect { subject.call(status) } + .to change { status.tagged_objects.count }.by(1) + end + end + + context 'when the collection is mentioned by URL' do + let(:text) { "Hello check out this collection! #{ActivityPub::TagManager.instance.url_for(collection)}" } + + it 'creates a tagged object' do + expect { subject.call(status) } + .to change { status.tagged_objects.count }.by(1) + end end end - context 'when status has a generic link' do + context 'when status has a generic link to something that is not a collection' do let(:status) { Fabricate(:status, account: account, text: 'Hello check out my personal web page: https://example.com/test', visibility: :public) } + before do + stub_request(:get, 'https://example.com/test').to_return(status: 404) + end + it 'skips the link and does not create a tagged object' do expect { expect { subject.call(status) }.to_not raise_error } .to not_change { status.tagged_objects.count }.from(0)