A app/controllers/api/v1/profiles_controller.rb => app/controllers/api/v1/profiles_controller.rb +29 -0
@@ 0,0 1,29 @@
+# frozen_string_literal: true
+
+class Api::V1::ProfilesController < Api::BaseController
+ before_action -> { doorkeeper_authorize! :write, :'write:accounts' }
+ before_action :require_user!
+ before_action :set_image
+ before_action :validate_image_param
+
+ def destroy
+ @account = current_account
+ UpdateAccountService.new.call(@account, { @image => nil }, raise_error: true)
+ ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
+ render json: @account, serializer: REST::CredentialAccountSerializer
+ end
+
+ private
+
+ def set_image
+ @image = params[:image]
+ end
+
+ def validate_image_param
+ raise(Mastodon::InvalidParameterError, 'Image must be either "avatar" or "header"') unless valid_image?
+ end
+
+ def valid_image?
+ %w(avatar header).include?(@image)
+ end
+end
M config/routes/api.rb => config/routes/api.rb +1 -0
@@ 95,6 95,7 @@ namespace :api, format: false do
resources :filters, only: [:index, :create, :show, :update, :destroy]
resources :endorsements, only: [:index]
resources :markers, only: [:index, :create]
+ resources :profile, only: :destroy, param: :image, controller: 'profiles'
namespace :apps do
get :verify_credentials, to: 'credentials#show'
A spec/requests/api/v1/profiles_spec.rb => spec/requests/api/v1/profiles_spec.rb +112 -0
@@ 0,0 1,112 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Deleting profile images' do
+ let(:account) do
+ Fabricate(
+ :account,
+ avatar: fixture_file_upload('avatar.gif', 'image/gif'),
+ header: fixture_file_upload('attachment.jpg', 'image/jpeg')
+ )
+ end
+ let(:token) { Fabricate(:accessible_access_token, resource_owner_id: account.user.id, scopes: scopes) }
+ let(:scopes) { 'write:accounts' }
+ let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
+
+ describe 'DELETE /api/v1/profile' do
+ before do
+ allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async)
+ end
+
+ context 'when deleting an avatar' do
+ context 'with wrong scope' do
+ before do
+ delete '/api/v1/profile/avatar', headers: headers
+ end
+
+ it_behaves_like 'forbidden for wrong scope', 'read'
+ end
+
+ it 'returns http success' do
+ delete '/api/v1/profile/avatar', headers: headers
+
+ expect(response).to have_http_status(200)
+ end
+
+ it 'deletes the avatar' do
+ delete '/api/v1/profile/avatar', headers: headers
+
+ account.reload
+
+ expect(account.avatar).to_not exist
+ end
+
+ it 'does not delete the header' do
+ delete '/api/v1/profile/avatar', headers: headers
+
+ account.reload
+
+ expect(account.header).to exist
+ end
+
+ it 'queues up an account update distribution' do
+ delete '/api/v1/profile/avatar', headers: headers
+
+ expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(account.id)
+ end
+ end
+
+ context 'when deleting a header' do
+ context 'with wrong scope' do
+ before do
+ delete '/api/v1/profile/header', headers: headers
+ end
+
+ it_behaves_like 'forbidden for wrong scope', 'read'
+ end
+
+ it 'returns http success' do
+ delete '/api/v1/profile/header', headers: headers
+
+ expect(response).to have_http_status(200)
+ end
+
+ it 'does not delete the avatar' do
+ delete '/api/v1/profile/header', headers: headers
+
+ account.reload
+
+ expect(account.avatar).to exist
+ end
+
+ it 'deletes the header' do
+ delete '/api/v1/profile/header', headers: headers
+
+ account.reload
+
+ expect(account.header).to_not exist
+ end
+
+ it 'queues up an account update distribution' do
+ delete '/api/v1/profile/header', headers: headers
+
+ expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(account.id)
+ end
+ end
+
+ context 'when provided picture value is invalid' do
+ it 'returns http bad request' do
+ delete '/api/v1/profile/invalid', headers: headers
+
+ expect(response).to have_http_status(400)
+ end
+
+ it 'does not queue up an account update distribution' do
+ delete '/api/v1/profile/invalid', headers: headers
+
+ expect(ActivityPub::UpdateDistributionWorker).to_not have_received(:perform_async).with(account.id)
+ end
+ end
+ end
+end