Add ability to search for a collection by URL (#38588)

This commit is contained in:
Claire 2026-04-08 18:03:35 +02:00 committed by GitHub
parent 97ba08113d
commit 4ad54b279d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 70 additions and 6 deletions

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true
class Search < ActiveModelSerializers::Model
attributes :accounts, :statuses, :hashtags
attributes :accounts, :statuses, :hashtags, :collections
end

View File

@ -4,4 +4,5 @@ class REST::SearchSerializer < ActiveModel::Serializer
has_many :accounts, serializer: REST::AccountSerializer
has_many :statuses, serializer: REST::StatusSerializer
has_many :hashtags, serializer: REST::TagSerializer
has_many :collections, serializer: REST::CollectionSerializer
end

View File

@ -3,8 +3,12 @@
class ActivityPub::FetchRemoteFeaturedCollectionService < BaseService
include JsonLdHelper
def call(uri, request_id: nil, on_behalf_of: nil)
json = fetch_resource(uri, true, on_behalf_of)
def call(uri, request_id: nil, prefetched_body: nil, on_behalf_of: nil)
json = if prefetched_body.nil?
fetch_resource(uri, true, on_behalf_of)
else
body_to_json(prefetched_body, compare_id: uri)
end
return unless supported_context?(json)
return unless json['type'] == 'FeaturedCollection'

View File

@ -63,7 +63,7 @@ class FetchResourceService < BaseService
end
def expected_type?(json)
equals_or_includes_any?(json['type'], ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES)
equals_or_includes_any?(json['type'], ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES + %w(FeaturedCollection))
end
def process_html(response)

View File

@ -28,6 +28,10 @@ class ResolveURLService < BaseService
status = FetchRemoteStatusService.new.call(resource_url, prefetched_body: body)
authorize_with @on_behalf_of, status, :show? unless status.nil?
status
elsif type == 'FeaturedCollection' && Mastodon::Feature.collections_enabled?
collection = ActivityPub::FetchRemoteFeaturedCollectionService.new.call(resource_url, prefetched_body: body)
authorize_with @on_behalf_of, collection, :show? unless collection.nil?
collection
end
end
@ -111,9 +115,21 @@ class ResolveURLService < BaseService
Account.find_remote(username, domain)
end
when 'collections'
return unless recognized_params[:action] == 'show'
check_collection(Collection.find_by(id: recognized_params[:id]))
end
end
def check_collection(collection)
return if collection.nil?
authorize_with @on_behalf_of, collection, :show?
rescue Mastodon::NotPermittedError
nil
end
def check_local_status(status)
return if status.nil?

View File

@ -64,7 +64,7 @@ class SearchService < BaseService
end
def default_results
{ accounts: [], hashtags: [], statuses: [] }
{ accounts: [], hashtags: [], statuses: [], collections: [] }
end
def url_query?

View File

@ -30,6 +30,49 @@ RSpec.describe ResolveURLService do
expect(subject.call(url)).to eq known_account
end
context 'when searching for a remote collection', feature: :collections do
let(:account) { Fabricate(:account) }
let(:collection_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub) }
let(:uri) { 'https://example.com/featured_collections/1' }
let(:payload) do
{
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => uri,
'type' => 'FeaturedCollection',
'name' => 'Incredible people',
'summary' => 'These are really amazing',
'attributedTo' => collection_account.uri,
'sensitive' => false,
'discoverable' => true,
'totalItems' => 0,
}
end
before do
stub_request(:get, uri).to_return(status: 200, body: payload.to_json, headers: { 'Content-Type': 'application/activity+json' })
end
it 'returns the collection' do
expect(subject.call(uri, on_behalf_of: account))
.to be_a(Collection)
.and have_attributes(
uri: uri
)
end
end
context 'when searching for a local collection', feature: :collections do
let(:account) { Fabricate(:account) }
let(:collection) { Fabricate(:collection) }
it 'returns the collection' do
expect(subject.call(ActivityPub::TagManager.instance.uri_for(collection), on_behalf_of: account))
.to eq(collection)
end
end
context 'when searching for a remote private status' do
let(:account) { Fabricate(:account) }
let(:poster) { Fabricate(:account, domain: 'example.com') }

View File

@ -86,6 +86,6 @@ RSpec.describe SearchService do
end
def empty_results
{ accounts: [], hashtags: [], statuses: [] }
{ accounts: [], hashtags: [], statuses: [], collections: [] }
end
end