Skip to content

Commit cf7a5f9

Browse files
committed
Add mutation to delete user
1 parent 625a103 commit cf7a5f9

File tree

9 files changed

+201
-0
lines changed

9 files changed

+201
-0
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# ruby
2+
# frozen_string_literal: true
3+
4+
module Mutations
5+
module Users
6+
class Delete < BaseMutation
7+
description 'Delete an existing user.'
8+
9+
field :user, Types::UserType, null: true, description: 'The deleted user.'
10+
11+
argument :user_id, Types::GlobalIdType[::User], required: true,
12+
description: 'The user to delete.'
13+
14+
def resolve(user_id:)
15+
user = SagittariusSchema.object_from_id(user_id)
16+
17+
if user.nil?
18+
return { user: nil,
19+
errors: [create_error(:user_not_found, 'Invalid user')] }
20+
end
21+
22+
::Users::DeleteService.new(
23+
current_authentication,
24+
user
25+
).execute.to_mutation_response(success_key: :user)
26+
end
27+
end
28+
end
29+
end

app/graphql/types/mutation_type.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class MutationType < Types::BaseObject
3535
mount_mutation Mutations::Users::Mfa::BackupCodes::Rotate
3636
mount_mutation Mutations::Users::Mfa::Totp::GenerateSecret
3737
mount_mutation Mutations::Users::Mfa::Totp::ValidateSecret
38+
mount_mutation Mutations::Users::Delete
3839
mount_mutation Mutations::Users::EmailVerification
3940
mount_mutation Mutations::Users::Login
4041
mount_mutation Mutations::Users::Logout

app/models/audit_event.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class AuditEvent < ApplicationRecord
3939
email_verified: 35,
4040
password_reset_requested: 36,
4141
password_reset: 37,
42+
user_deleted: 38,
4243
}.with_indifferent_access
4344

4445
# rubocop:disable Lint/StructNewOverride

app/policies/user_policy.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class UserPolicy < BasePolicy
1111
enable :read_user_identity
1212
enable :update_attachment_avatar
1313
enable :read_email
14+
enable :delete_user
1415
end
1516

1617
rule { user_is_self }.policy do
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# frozen_string_literal: true
2+
3+
module Users
4+
class DeleteService
5+
include Sagittarius::Database::Transactional
6+
7+
attr_reader :current_authentication, :user
8+
9+
def initialize(current_authentication, user)
10+
@current_authentication = current_authentication
11+
@user = user
12+
end
13+
14+
def execute
15+
unless Ability.allowed?(current_authentication, :delete_user, user)
16+
return ServiceResponse.error(message: 'Missing permission', error_code: :missing_permission)
17+
end
18+
19+
transactional do |t|
20+
unless user.delete
21+
t.rollback_and_return! ServiceResponse.error(
22+
message: 'Failed to delete user',
23+
error_code: :invalid_user,
24+
details: user.errors
25+
)
26+
end
27+
28+
AuditService.audit(
29+
:user_deleted,
30+
author_id: current_authentication.user.id,
31+
entity: user,
32+
target: AuditEvent::GLOBAL_TARGET,
33+
details: {}
34+
)
35+
36+
ServiceResponse.success(message: 'Deleted user', payload: user)
37+
end
38+
end
39+
end
40+
end
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
title: usersDelete
3+
---
4+
5+
Delete an existing user.
6+
7+
## Arguments
8+
9+
| Name | Type | Description |
10+
|------|------|-------------|
11+
| `clientMutationId` | [`String`](../scalar/string.md) | A unique identifier for the client performing the mutation. |
12+
| `userId` | [`UserID!`](../scalar/userid.md) | The user to delete. |
13+
14+
## Fields
15+
16+
| Name | Type | Description |
17+
|------|------|-------------|
18+
| `clientMutationId` | [`String`](../scalar/string.md) | A unique identifier for the client performing the mutation. |
19+
| `errors` | [`[Error!]!`](../object/error.md) | Errors encountered during execution of the mutation. |
20+
| `user` | [`User`](../object/user.md) | The deleted user. |
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
5+
RSpec.describe Mutations::Users::Delete do
6+
it { expect(described_class.graphql_name).to eq('UsersDelete') }
7+
end
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
5+
RSpec.describe 'usersDelete Mutation' do
6+
include GraphqlHelpers
7+
8+
let(:mutation) do
9+
<<~QUERY
10+
mutation($input: UsersDeleteInput!) {
11+
usersDelete(input: $input) {
12+
#{error_query}
13+
user {
14+
id
15+
username
16+
admin
17+
}
18+
}
19+
}
20+
QUERY
21+
end
22+
23+
let(:input) do
24+
{
25+
userId: user.to_global_id.to_s,
26+
}
27+
end
28+
29+
let(:user) { create(:user) }
30+
let(:variables) { { input: input } }
31+
let(:current_user) { create(:user, :admin) }
32+
33+
before do
34+
post_graphql mutation, variables: variables, current_user: current_user
35+
end
36+
37+
it 'deletes user' do
38+
expect(graphql_data_at(:users_delete, :user, :id)).to be_present
39+
expect(SagittariusSchema.object_from_id(graphql_data_at(:users_delete, :user, :id))).to be_nil
40+
41+
is_expected.to create_audit_event(
42+
:user_deleted,
43+
author_id: current_user.id,
44+
entity_type: 'User',
45+
entity_id: user.id,
46+
details: {},
47+
target_type: 'global',
48+
target_id: 0
49+
)
50+
end
51+
52+
context 'when current user lacks permission' do
53+
let(:current_user) { create(:user) }
54+
55+
it 'returns a missing permission error' do
56+
expect(graphql_data_at(:users_delete, :user)).to be_nil
57+
expect(graphql_data_at(:users_delete, :errors, :error_code)).to include('MISSING_PERMISSION')
58+
59+
expect(User.exists?(user.id)).to be true
60+
is_expected.not_to create_audit_event(:user_deleted)
61+
end
62+
end
63+
end
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
5+
RSpec.describe Users::DeleteService do
6+
subject(:service_response) do
7+
described_class.new(create_authentication(current_user), user).execute
8+
end
9+
10+
let(:user) { create(:user) }
11+
let(:current_user) { create(:user, :admin) }
12+
13+
it 'deletes the user successfully' do
14+
expect { service_response }.to change { User.exists?(user.id) }.from(true).to(false)
15+
expect(service_response).to be_success
16+
expect(service_response.payload).to eq(user)
17+
18+
is_expected.to create_audit_event(
19+
:user_deleted,
20+
author_id: current_user.id,
21+
entity_type: 'User',
22+
entity_id: user.id,
23+
details: {},
24+
target_type: 'global',
25+
target_id: 0
26+
)
27+
end
28+
29+
context 'when current user lacks permission' do
30+
let(:current_user) { create(:user) }
31+
32+
it 'returns a missing permission error' do
33+
expect(service_response).not_to be_success
34+
expect(service_response.payload[:error_code]).to eq(:missing_permission)
35+
expect(User.exists?(user.id)).to be true
36+
is_expected.not_to create_audit_event(:user_deleted)
37+
end
38+
end
39+
end

0 commit comments

Comments
 (0)