diff --git a/app/controllers/api/v1_alpha/collection_items_controller.rb b/app/controllers/api/v1_alpha/collection_items_controller.rb index 3f1f10a3ce..5c78de14e9 100644 --- a/app/controllers/api/v1_alpha/collection_items_controller.rb +++ b/app/controllers/api/v1_alpha/collection_items_controller.rb @@ -27,7 +27,7 @@ class Api::V1Alpha::CollectionItemsController < Api::BaseController def destroy authorize @collection, :update? - @collection_item.destroy + DeleteCollectionItemService.new.call(@collection_item) head 200 end diff --git a/app/serializers/activitypub/remove_featured_item_serializer.rb b/app/serializers/activitypub/remove_featured_item_serializer.rb new file mode 100644 index 0000000000..cc1783c7e7 --- /dev/null +++ b/app/serializers/activitypub/remove_featured_item_serializer.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class ActivityPub::RemoveFeaturedItemSerializer < ActivityPub::Serializer + include RoutingHelper + + attributes :type, :actor, :target + has_one :object, serializer: ActivityPub::FeaturedItemSerializer + + def type + 'Remove' + end + + def actor + ActivityPub::TagManager.instance.uri_for(collection.account) + end + + def target + ActivityPub::TagManager.instance.uri_for(collection) + end + + private + + def collection + @collection ||= object.collection + end +end diff --git a/app/services/delete_collection_item_service.rb b/app/services/delete_collection_item_service.rb new file mode 100644 index 0000000000..23f539a685 --- /dev/null +++ b/app/services/delete_collection_item_service.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class DeleteCollectionItemService + def call(collection_item) + @collection_item = collection_item + @collection = collection_item.collection + @collection_item.destroy! + + distribute_remove_activity if Mastodon::Feature.collections_federation_enabled? + end + + private + + def distribute_remove_activity + ActivityPub::AccountRawDistributionWorker.perform_async(activity_json, @collection.account.id) + end + + def activity_json + ActiveModelSerializers::SerializableResource.new(@collection_item, serializer: ActivityPub::RemoveFeaturedItemSerializer, adapter: ActivityPub::Adapter).to_json + end +end diff --git a/spec/serializers/activitypub/remove_featured_item_serializer_spec.rb b/spec/serializers/activitypub/remove_featured_item_serializer_spec.rb new file mode 100644 index 0000000000..bce81c2552 --- /dev/null +++ b/spec/serializers/activitypub/remove_featured_item_serializer_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ActivityPub::RemoveFeaturedItemSerializer do + subject { serialized_record_json(object, described_class, adapter: ActivityPub::Adapter) } + + let(:tag_manager) { ActivityPub::TagManager.instance } + let(:collection) { Fabricate(:collection) } + let(:object) { Fabricate(:collection_item, collection:) } + + it 'serializes to the expected json' do + expect(subject).to include({ + 'type' => 'Remove', + 'actor' => tag_manager.uri_for(collection.account), + 'target' => tag_manager.uri_for(collection), + 'object' => a_hash_including({ + 'type' => 'FeaturedItem', + }), + }) + + expect(subject).to_not have_key('id') + expect(subject).to_not have_key('published') + expect(subject).to_not have_key('to') + expect(subject).to_not have_key('cc') + end +end diff --git a/spec/services/delete_collection_item_service_spec.rb b/spec/services/delete_collection_item_service_spec.rb new file mode 100644 index 0000000000..099671e8fc --- /dev/null +++ b/spec/services/delete_collection_item_service_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe DeleteCollectionItemService do + subject { described_class.new } + + let(:collection_item) { Fabricate(:collection_item) } + let(:collection) { collection_item.collection } + + describe '#call' do + it 'destroys the collection' do + expect { subject.call(collection_item) }.to change(collection.collection_items, :count).by(-1) + end + + it 'federates a `Remove` activity', feature: :collections_federation do + subject.call(collection_item) + + expect(ActivityPub::AccountRawDistributionWorker).to have_enqueued_sidekiq_job + end + end +end