AP/AS serialization of Collections (#37434)

This commit is contained in:
David Roetzel
2026-01-12 09:39:25 +01:00
committed by GitHub
parent a4b8b9fe98
commit a9cfddf28e
8 changed files with 139 additions and 0 deletions

View File

@@ -62,6 +62,8 @@ class ActivityPub::TagManager
emoji_url(target)
when :flag
target.uri
when :featured_collection
ap_account_featured_collection_url(target.account.id, target)
end
end

View File

@@ -60,6 +60,10 @@ class Collection < ApplicationRecord
self.tag = Tag.find_or_create_by_names(new_name).first
end
def object_type
:featured_collection
end
private
def tag_is_usable

View File

@@ -0,0 +1,54 @@
# frozen_string_literal: true
class ActivityPub::FeaturedCollectionSerializer < ActivityPub::Serializer
class FeaturedItemSerializer < ActivityPub::Serializer
attributes :type, :featured_object, :featured_object_type
def type
'FeaturedItem'
end
def featured_object
ActivityPub::TagManager.instance.uri_for(object.account)
end
def featured_object_type
object.account.actor_type || 'Person'
end
end
attributes :id, :type, :total_items, :name, :summary, :attributed_to,
:sensitive, :discoverable, :published, :updated
has_one :tag, key: :topic, serializer: ActivityPub::NoteSerializer::TagSerializer
has_many :collection_items, key: :ordered_items, serializer: FeaturedItemSerializer
def id
ActivityPub::TagManager.instance.uri_for(object)
end
def type
'FeaturedCollection'
end
def summary
object.description
end
def attributed_to
ActivityPub::TagManager.instance.uri_for(object.account)
end
def total_items
object.collection_items.size
end
def published
object.created_at.iso8601
end
def updated
object.updated_at.iso8601
end
end

View File

@@ -96,6 +96,7 @@ Rails.application.routes.draw do
get '/authorize_follow', to: redirect { |_, request| "/authorize_interaction?#{request.params.to_query}" }
concern :account_resources do
resources :featured_collections, only: [:show]
resources :followers, only: [:index], controller: :follower_accounts
resources :following, only: [:index], controller: :following_accounts

View File

@@ -8,3 +8,10 @@ Fabricator(:collection) do
sensitive false
discoverable true
end
Fabricator(:remote_collection, from: :collection) do
account { Fabricate.build(:remote_account) }
local false
uri { sequence(:uri) { |i| "https://example.com/collections/#{i}" } }
original_number_of_items 0
end

View File

@@ -192,6 +192,23 @@ RSpec.describe ActivityPub::TagManager do
expect(subject.uri_for(status.conversation)).to eq status.conversation.uri
end
end
context 'with a local collection' do
let(:collection) { Fabricate(:collection) }
it 'returns a string starting with web domain and with the expected path' do
expect(subject.uri_for(collection))
.to eq("#{host_prefix}/ap/users/#{collection.account.id}/featured_collections/#{collection.id}")
end
end
context 'with a remote collection' do
let(:collection) { Fabricate(:remote_collection) }
it 'returns the expected URL' do
expect(subject.uri_for(collection)).to eq collection.uri
end
end
end
describe '#key_uri_for' do

View File

@@ -126,4 +126,10 @@ RSpec.describe Collection do
end
end
end
describe '#object_type' do
it 'returns `:featured_collection`' do
expect(subject.object_type).to eq :featured_collection
end
end
end

View File

@@ -0,0 +1,48 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::FeaturedCollectionSerializer do
subject { serialized_record_json(collection, described_class, adapter: ActivityPub::Adapter) }
let(:collection) do
Fabricate(:collection,
name: 'Incredible people',
description: 'These are really amazing',
tag_name: '#people',
discoverable: false)
end
let!(:collection_items) { Fabricate.times(2, :collection_item, collection:) }
it 'serializes to the expected structure' do
expect(subject).to include({
'type' => 'FeaturedCollection',
'id' => ActivityPub::TagManager.instance.uri_for(collection),
'name' => 'Incredible people',
'summary' => 'These are really amazing',
'attributedTo' => ActivityPub::TagManager.instance.uri_for(collection.account),
'sensitive' => false,
'discoverable' => false,
'topic' => {
'href' => match(%r{/tags/people$}),
'type' => 'Hashtag',
'name' => '#people',
},
'totalItems' => 2,
'orderedItems' => [
{
'type' => 'FeaturedItem',
'featuredObject' => ActivityPub::TagManager.instance.uri_for(collection_items.first.account),
'featuredObjectType' => 'Person',
},
{
'type' => 'FeaturedItem',
'featuredObject' => ActivityPub::TagManager.instance.uri_for(collection_items.last.account),
'featuredObjectType' => 'Person',
},
],
'published' => match_api_datetime_format,
'updated' => match_api_datetime_format,
})
end
end