Federate updates to collections (#37790)

This commit is contained in:
David Roetzel 2026-02-10 11:08:55 +01:00 committed by GitHub
parent 0763ad0d96
commit 03f73377d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 122 additions and 1 deletions

View File

@ -51,7 +51,7 @@ class Api::V1Alpha::CollectionsController < Api::BaseController
def update
authorize @collection, :update?
@collection.update!(collection_update_params) # TODO: Create a service for this to federate changes
UpdateCollectionService.new.call(@collection, collection_update_params)
render json: @collection, serializer: REST::CollectionSerializer, adapter: :json
end

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
class ActivityPub::UpdateFeaturedCollectionSerializer < ActivityPub::Serializer
attributes :id, :type, :actor, :to
has_one :object, serializer: ActivityPub::FeaturedCollectionSerializer
def id
[ActivityPub::TagManager.instance.uri_for(object), '#updates/', object.updated_at.to_i].join
end
def type
'Update'
end
def actor
ActivityPub::TagManager.instance.uri_for(object.account)
end
def to
[ActivityPub::TagManager::COLLECTIONS[:public]]
end
end

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
class UpdateCollectionService
UPDATEABLE_PARAMS = %w(name description language sensitive discoverable tag_id).freeze
def call(collection, params)
@collection = collection
@collection.update!(params)
distribute_update_activity if Mastodon::Feature.collections_federation_enabled?
end
private
def distribute_update_activity
return unless relevant_attributes_changed?
ActivityPub::AccountRawDistributionWorker.perform_async(activity_json, @collection.account.id)
end
def activity_json
ActiveModelSerializers::SerializableResource.new(@collection, serializer: ActivityPub::UpdateFeaturedCollectionSerializer, adapter: ActivityPub::Adapter).to_json
end
def relevant_attributes_changed?
(@collection.saved_changes.keys & UPDATEABLE_PARAMS).any?
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::UpdateFeaturedCollectionSerializer do
subject { serialized_record_json(collection, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:collection) { Fabricate(:collection) }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => "#{tag_manager.uri_for(collection)}#updates/#{collection.updated_at.to_i}",
'type' => 'Update',
'actor' => tag_manager.uri_for(collection.account),
'to' => ['https://www.w3.org/ns/activitystreams#Public'],
'object' => a_hash_including({
'id' => tag_manager.uri_for(collection),
'type' => 'FeaturedCollection',
}),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end

View File

@ -0,0 +1,43 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe UpdateCollectionService do
subject { described_class.new }
let(:collection) { Fabricate(:collection) }
describe '#call' do
context 'when given valid parameters' do
it 'updates the collection' do
subject.call(collection, { name: 'Newly updated name' })
expect(collection.name).to eq 'Newly updated name'
end
context 'when something actually changed' do
it 'federates an `Update` activity', feature: :collections_federation do
subject.call(collection, { name: 'updated' })
expect(ActivityPub::AccountRawDistributionWorker).to have_enqueued_sidekiq_job
end
end
context 'when nothing changed' do
it 'does not federate an activity', feature: :collections_federation do
subject.call(collection, { name: collection.name })
expect(ActivityPub::AccountRawDistributionWorker).to_not have_enqueued_sidekiq_job
end
end
end
context 'when given invalid parameters' do
it 'raises an exception' do
expect do
subject.call(collection, { name: '' })
end.to raise_error(ActiveRecord::RecordInvalid)
end
end
end
end