graphql_devise 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a254ff0aef5cb9ce45ee53100dccecd33a2b0898afc1261d668be20b4cd8a739
4
- data.tar.gz: d062d1b78f625585c0e613d99b5a252c76d1e16f58aa75711fb63197241d59b6
3
+ metadata.gz: daddc316d120f12ed4a6a781871ec676a0eb11d6d6a407a438e01460fbd5c668
4
+ data.tar.gz: 73f19c61b7484b7cb7801e99d5889ddffdb8a0c8bfed393bd22e669c5143ccf3
5
5
  SHA512:
6
- metadata.gz: 826361a8cd110e817da03f60383513e02a06e7847543c9d183500ed86b381419fe44742607b6c5b5f4bb18afd985b80dc9f68c152ba99e10ca237f3c0442ab56
7
- data.tar.gz: 49a8b2cec607525cc84682cf797d05456c6d462751ac5857dd1a157d72766b2a6654cc74ce40c8a9e34847ab66dd441d1d96d6c696ea526ac2f8c8ec7668bcca
6
+ metadata.gz: 1ca7946d638f1e40e2b0bfe4f427cf1c60fc63e7b947ffb716858ba5278dfbb06717a8919e276e221701729069a8b421f8bd094874b7dc729cbb69e2dbfaaa7c
7
+ data.tar.gz: 31a7f42e8eaa5e268c829ea9b091c8edc3e73f4c0101520eee737de55c8fab1e12d1b67246709e6cca8f7a3278906fb61a675d9f8c439d861422aac26ab17baf
data/.circleci/config.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  version: 2.1
2
2
  orbs:
3
- coveralls: coveralls/coveralls@1.0.6
3
+ coveralls: coveralls/coveralls@2.2.5
4
4
 
5
5
  jobs:
6
6
  test:
@@ -12,9 +12,8 @@ jobs:
12
12
  docker:
13
13
  - image: 'ruby:<< parameters.ruby-version >>'
14
14
  environment:
15
- BUNDLE_GEMFILE: << parameters.gemfile >>
15
+ BUNDLE_GEMFILE: gemfiles/<< parameters.gemfile >>.gemfile
16
16
  BUNDLE_JOBS: '2'
17
- COVERALLS_PARALLEL: 'true'
18
17
  EAGER_LOAD: 'true'
19
18
  RUBYOPT: '-rostruct'
20
19
  steps:
@@ -27,6 +26,13 @@ jobs:
27
26
  name: Run Specs
28
27
  command:
29
28
  bundle exec rspec
29
+ - coveralls/upload:
30
+ parallel: true
31
+ coverage_file: ./coverage/.resultset.json
32
+ coverage_format: simplecov
33
+ # Parallel jobs must each report a unique flag_name or Coveralls
34
+ # collapses them into a single job. Keep it free of slashes.
35
+ flag_name: ruby-<< parameters.ruby-version >>-<< parameters.gemfile >>
30
36
  report-coverage:
31
37
  docker:
32
38
  - image: 'cimg/node:22.1.0'
@@ -47,63 +53,75 @@ workflows:
47
53
  - '3.3'
48
54
  - '3.4'
49
55
  gemfile:
50
- - gemfiles/rails7.0_graphql2.0.gemfile
51
- - gemfiles/rails7.0_graphql2.1.gemfile
52
- - gemfiles/rails7.0_graphql2.2.gemfile
53
- - gemfiles/rails7.0_graphql2.3.gemfile
54
- - gemfiles/rails7.0_graphql2.4.gemfile
55
- - gemfiles/rails7.1_graphql2.0.gemfile
56
- - gemfiles/rails7.1_graphql2.1.gemfile
57
- - gemfiles/rails7.1_graphql2.2.gemfile
58
- - gemfiles/rails7.1_graphql2.3.gemfile
59
- - gemfiles/rails7.1_graphql2.4.gemfile
60
- - gemfiles/rails7.2_graphql2.0.gemfile
61
- - gemfiles/rails7.2_graphql2.1.gemfile
62
- - gemfiles/rails7.2_graphql2.2.gemfile
63
- - gemfiles/rails7.2_graphql2.3.gemfile
64
- - gemfiles/rails7.2_graphql2.4.gemfile
65
- - gemfiles/rails7.2_graphql2.5.gemfile
66
- - gemfiles/rails7.2_graphql2.6.gemfile
67
- - gemfiles/rails8.0_graphql2.2.gemfile
68
- - gemfiles/rails8.0_graphql2.3.gemfile
69
- - gemfiles/rails8.0_graphql2.4.gemfile
70
- - gemfiles/rails8.0_graphql2.5.gemfile
71
- - gemfiles/rails8.0_graphql2.6.gemfile
56
+ - rails7.0_graphql2.3
57
+ - rails7.0_graphql2.4
58
+ - rails7.1_graphql2.0
59
+ - rails7.1_graphql2.1
60
+ - rails7.1_graphql2.2
61
+ - rails7.1_graphql2.3
62
+ - rails7.1_graphql2.4
63
+ - rails7.2_graphql2.0
64
+ - rails7.2_graphql2.1
65
+ - rails7.2_graphql2.2
66
+ - rails7.2_graphql2.3
67
+ - rails7.2_graphql2.4
68
+ - rails7.2_graphql2.5
69
+ - rails7.2_graphql2.6
70
+ - rails8.0_graphql2.2
71
+ - rails8.0_graphql2.3
72
+ - rails8.0_graphql2.4
73
+ - rails8.0_graphql2.5
74
+ - rails8.0_graphql2.6
75
+ - rails8.1_graphql2.4
76
+ - rails8.1_graphql2.5
77
+ - rails8.1_graphql2.6
72
78
  exclude:
73
79
  - ruby-version: '3.0'
74
- gemfile: gemfiles/rails7.2_graphql2.0.gemfile
80
+ gemfile: rails7.2_graphql2.0
81
+ - ruby-version: '3.0'
82
+ gemfile: rails7.2_graphql2.1
83
+ - ruby-version: '3.0'
84
+ gemfile: rails7.2_graphql2.2
85
+ - ruby-version: '3.0'
86
+ gemfile: rails7.2_graphql2.3
75
87
  - ruby-version: '3.0'
76
- gemfile: gemfiles/rails7.2_graphql2.1.gemfile
88
+ gemfile: rails7.2_graphql2.4
77
89
  - ruby-version: '3.0'
78
- gemfile: gemfiles/rails7.2_graphql2.2.gemfile
90
+ gemfile: rails7.2_graphql2.5
79
91
  - ruby-version: '3.0'
80
- gemfile: gemfiles/rails7.2_graphql2.3.gemfile
92
+ gemfile: rails7.2_graphql2.6
81
93
  - ruby-version: '3.0'
82
- gemfile: gemfiles/rails7.2_graphql2.4.gemfile
94
+ gemfile: rails8.0_graphql2.2
83
95
  - ruby-version: '3.0'
84
- gemfile: gemfiles/rails7.2_graphql2.5.gemfile
96
+ gemfile: rails8.0_graphql2.3
85
97
  - ruby-version: '3.0'
86
- gemfile: gemfiles/rails7.2_graphql2.6.gemfile
98
+ gemfile: rails8.0_graphql2.4
87
99
  - ruby-version: '3.0'
88
- gemfile: gemfiles/rails8.0_graphql2.2.gemfile
100
+ gemfile: rails8.0_graphql2.5
89
101
  - ruby-version: '3.0'
90
- gemfile: gemfiles/rails8.0_graphql2.3.gemfile
102
+ gemfile: rails8.0_graphql2.6
91
103
  - ruby-version: '3.0'
92
- gemfile: gemfiles/rails8.0_graphql2.4.gemfile
104
+ gemfile: rails8.1_graphql2.4
93
105
  - ruby-version: '3.0'
94
- gemfile: gemfiles/rails8.0_graphql2.5.gemfile
106
+ gemfile: rails8.1_graphql2.5
95
107
  - ruby-version: '3.0'
96
- gemfile: gemfiles/rails8.0_graphql2.6.gemfile
108
+ gemfile: rails8.1_graphql2.6
109
+ - ruby-version: '3.1'
110
+ gemfile: rails8.0_graphql2.2
111
+ - ruby-version: '3.1'
112
+ gemfile: rails8.0_graphql2.3
113
+ - ruby-version: '3.1'
114
+ gemfile: rails8.0_graphql2.4
97
115
  - ruby-version: '3.1'
98
- gemfile: gemfiles/rails8.0_graphql2.2.gemfile
116
+ gemfile: rails8.0_graphql2.5
99
117
  - ruby-version: '3.1'
100
- gemfile: gemfiles/rails8.0_graphql2.3.gemfile
118
+ gemfile: rails8.0_graphql2.6
101
119
  - ruby-version: '3.1'
102
- gemfile: gemfiles/rails8.0_graphql2.4.gemfile
120
+ gemfile: rails8.1_graphql2.4
103
121
  - ruby-version: '3.1'
104
- gemfile: gemfiles/rails8.0_graphql2.5.gemfile
122
+ gemfile: rails8.1_graphql2.5
105
123
  - ruby-version: '3.1'
106
- gemfile: gemfiles/rails8.0_graphql2.6.gemfile
124
+ gemfile: rails8.1_graphql2.6
107
125
  - report-coverage:
108
126
  requires:
109
127
  - test
data/Appraisals CHANGED
@@ -1,37 +1,3 @@
1
- appraise 'rails6.1-graphql1.10' do
2
- gem 'sqlite3', '~> 1.5.4'
3
- gem 'devise', '>= 4.7'
4
- gem 'rails', git: 'https://github.com/rails/rails', branch: '6-1-stable'
5
- gem 'graphql', '~> 1.10.0'
6
- gem 'factory_bot', '<= 6.4.4'
7
- end
8
-
9
- appraise 'rails6.1-graphql1.11' do
10
- gem 'sqlite3', '~> 1.5.4'
11
- gem 'public_suffix', '< 5'
12
- gem 'devise', '>= 4.7'
13
- gem 'rails', git: 'https://github.com/rails/rails', branch: '6-1-stable'
14
- gem 'graphql', '~> 1.11.0'
15
- gem 'factory_bot', '<= 6.4.4'
16
- end
17
-
18
- appraise 'rails6.1-graphql1.12' do
19
- gem 'sqlite3', '~> 1.5.4'
20
- gem 'public_suffix', '< 5'
21
- gem 'devise', '>= 4.7'
22
- gem 'rails', git: 'https://github.com/rails/rails', branch: '6-1-stable'
23
- gem 'graphql', '~> 1.12.0'
24
- gem 'factory_bot', '<= 6.4.4'
25
- end
26
-
27
- appraise 'rails6.1-graphql1.13' do
28
- gem 'sqlite3', '~> 1.5.4'
29
- gem 'devise', '>= 4.7'
30
- gem 'rails', git: 'https://github.com/rails/rails', branch: '6-1-stable'
31
- gem 'graphql', '~> 1.13.0'
32
- gem 'factory_bot', '<= 6.4.4'
33
- end
34
-
35
1
  appraise 'rails6.1-graphql2.0' do
36
2
  gem 'public_suffix', '< 5'
37
3
  gem 'sqlite3', '~> 1.5.4'
@@ -41,60 +7,6 @@ appraise 'rails6.1-graphql2.0' do
41
7
  gem 'factory_bot', '<= 6.4.4'
42
8
  end
43
9
 
44
- appraise 'rails7.0-graphql1.11' do
45
- gem 'sassc-rails'
46
- gem 'sqlite3', '~> 1.5.4'
47
- gem 'devise', '>= 4.7'
48
- gem 'devise_token_auth', '>= 1.2.1'
49
- gem 'rails', git: 'https://github.com/rails/rails', branch: '7-0-stable'
50
- gem 'graphql', '~> 1.11.0'
51
- end
52
-
53
- appraise 'rails7.0-graphql1.12' do
54
- gem 'sassc-rails'
55
- gem 'sqlite3', '~> 1.5.4'
56
- gem 'devise', '>= 4.7'
57
- gem 'devise_token_auth', '>= 1.2.1'
58
- gem 'rails', git: 'https://github.com/rails/rails', branch: '7-0-stable'
59
- gem 'graphql', '~> 1.12.0'
60
- end
61
-
62
- appraise 'rails7.0-graphql1.13' do
63
- gem 'sassc-rails'
64
- gem 'sqlite3', '~> 1.5.4'
65
- gem 'devise', '>= 4.7'
66
- gem 'devise_token_auth', '>= 1.2.1'
67
- gem 'rails', git: 'https://github.com/rails/rails', branch: '7-0-stable'
68
- gem 'graphql', '~> 1.13.0'
69
- end
70
-
71
- appraise 'rails7.0-graphql2.0' do
72
- gem 'sassc-rails'
73
- gem 'sqlite3', '~> 1.5.4'
74
- gem 'devise_token_auth', '>= 1.2.1'
75
- gem 'devise', '>= 4.7'
76
- gem 'rails', git: 'https://github.com/rails/rails', branch: '7-0-stable'
77
- gem 'graphql', '>= 2.0', '< 2.1'
78
- end
79
-
80
- appraise 'rails7.0-graphql2.1' do
81
- gem 'sassc-rails'
82
- gem 'sqlite3', '~> 1.5.4'
83
- gem 'devise_token_auth', '>= 1.2.1'
84
- gem 'devise', '>= 4.7'
85
- gem 'rails', git: 'https://github.com/rails/rails', branch: '7-0-stable'
86
- gem 'graphql', '>= 2.1', '< 2.2'
87
- end
88
-
89
- appraise 'rails7.0-graphql2.2' do
90
- gem 'sassc-rails'
91
- gem 'sqlite3', '~> 1.5.4'
92
- gem 'devise_token_auth', '>= 1.2.1'
93
- gem 'devise', '>= 4.7'
94
- gem 'rails', git: 'https://github.com/rails/rails', branch: '7-0-stable'
95
- gem 'graphql', '>= 2.2', '< 2.3'
96
- end
97
-
98
10
  appraise 'rails7.0-graphql2.3' do
99
11
  gem 'sassc-rails'
100
12
  gem 'sqlite3', '~> 1.5.4'
@@ -232,3 +144,18 @@ appraise 'rails8.0-graphql2.6' do
232
144
  gem 'rails', git: 'https://github.com/rails/rails', branch: '8-0-stable'
233
145
  gem 'graphql', '>= 2.6', '< 2.7'
234
146
  end
147
+
148
+ appraise 'rails8.1-graphql2.4' do
149
+ gem 'rails', git: 'https://github.com/rails/rails', branch: '8-1-stable'
150
+ gem 'graphql', '>= 2.4', '< 2.5'
151
+ end
152
+
153
+ appraise 'rails8.1-graphql2.5' do
154
+ gem 'rails', git: 'https://github.com/rails/rails', branch: '8-1-stable'
155
+ gem 'graphql', '>= 2.5', '< 2.6'
156
+ end
157
+
158
+ appraise 'rails8.1-graphql2.6' do
159
+ gem 'rails', git: 'https://github.com/rails/rails', branch: '8-1-stable'
160
+ gem 'graphql', '>= 2.6', '< 2.7'
161
+ end
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [v2.4.0](https://github.com/graphql-devise/graphql_devise/tree/v2.4.0) (2026-07-23)
4
+
5
+ [Full Changelog](https://github.com/graphql-devise/graphql_devise/compare/v2.3.0...v2.4.0)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Add Rails 8.1 support [\#297](https://github.com/graphql-devise/graphql_devise/pull/297) ([mcelicalderon](https://github.com/mcelicalderon))
10
+ - Add opt-in destroy\_account mutation [\#296](https://github.com/graphql-devise/graphql_devise/pull/296) ([mcelicalderon](https://github.com/mcelicalderon))
11
+
3
12
  ## [v2.3.0](https://github.com/graphql-devise/graphql_devise/tree/v2.3.0) (2026-07-20)
4
13
 
5
14
  [Full Changelog](https://github.com/graphql-devise/graphql_devise/compare/v2.2.0...v2.3.0)
data/README.md CHANGED
@@ -294,6 +294,13 @@ Please note the gem provided schema is shared by all mounted resources, so setti
294
294
  mount will disable introspection for all of them. **This option only works if you are using the mount method.**
295
295
  If you are mounting the auth operations into your own schema, use the `public_introspection` option of the
296
296
  `SchemaPlugin` instead.
297
+ 1. `allow_destroy`: Defaults to `false`. The `destroy_account` operation is opt-in: it is **not** mounted
298
+ automatically, so upgrading the gem will never add a way to delete accounts to an existing schema. Set
299
+ `allow_destroy: true` to mount it (works alongside `skip`, and additively when no `skip`/`only` is given), or
300
+ list `:destroy_account` in the `only` option. While the operation is available but not mounted, a warning is
301
+ logged on boot explaining how to opt in. If you have decided you don't want the mutation and want to silence
302
+ the warning, add `:destroy_account` to the `skip` option (for example `skip: [:destroy_account]`, or combined
303
+ with the flag as `allow_destroy: true, skip: [:destroy_account]`).
297
304
 
298
305
  Additional mutations and queries will be added to the schema regardless
299
306
  of other options you might have specified like `skip` or `only`.
@@ -315,6 +322,7 @@ The following is a list of the symbols you can provide to the `operations`, `ski
315
322
  :send_password_reset_with_token
316
323
  :resend_confirmation_with_token
317
324
  :confirm_registration_with_token
325
+ :destroy_account # opt-in, see the `allow_destroy` option above
318
326
  ```
319
327
 
320
328
  ### Configuring Model
@@ -479,6 +487,7 @@ register | The parameter `confirmUrl` is optional unless you are using the `conf
479
487
  sendPasswordResetWithToken | Sends an email to the provided address with a link to reset the password of the resource. First step of the most recently implemented password reset flow. | userSendPasswordResetWithToken(email: String!, redirectUrl: String!): UserSendPasswordResetWithTokenPayload |
480
488
  updatePasswordWithToken | Uses a `resetPasswordToken` to update the password of a resource. Second and last step of the most recently implemented password reset flow. | userSendPasswordResetWithToken(resetPasswordToken: String!, password: String!, passwordConfirmation: String!): UserUpdatePasswordWithTokenPayload |
481
489
  resendConfirmationWithToken | The `UserResendConfirmationWithTokenPayload` will return a `message: String!` that can be used to notify a user what to do after the instructions were sent to them. Email will contain a link to the provided `confirmUrl` and a `confirmationToken` query param. | userResendConfirmationWithToken(email: String!, confirmUrl: String!): UserResendConfirmationWithTokenPayload |
490
+ destroyAccount | Opt-in (see the `allow_destroy` mount option). Requires authentication headers. Destroys the current resource and clears the session so no auth headers are returned. | userDestroyAccount: UserDestroyAccountPayload |
482
491
 
483
492
  ### Reset Password Flow
484
493
  This gem supports two password recovery flows. The most recently implemented is preferred and
@@ -2,6 +2,7 @@ en:
2
2
  graphql_devise:
3
3
  redirect_url_not_allowed: "Redirect to '%{redirect_url}' not allowed."
4
4
  registration_failed: "User couldn't be registered"
5
+ destroy_account_failed: "Account couldn't be destroyed"
5
6
  resource_build_failed: "Resource couldn't be built, execution stopped."
6
7
  not_authenticated: "User is not logged in."
7
8
  user_not_found: "User was not found or was not logged in."
@@ -2,6 +2,7 @@ pt-BR:
2
2
  graphql_devise:
3
3
  redirect_url_not_allowed: "Redirecionamento para '%{redirect_url}' não permitido."
4
4
  registration_failed: "Usuário não pôde ser registrado"
5
+ destroy_account_failed: "A conta não pôde ser excluída"
5
6
  resource_build_failed: "O recurso não pôde ser construído, execução interrompida."
6
7
  not_authenticated: "Usuário não logado."
7
8
  user_not_found: "Usuário não encontrado ou não logado."
@@ -29,11 +29,10 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  spec.add_dependency 'devise_token_auth', '>= 0.1.43', '< 2.0'
31
31
  spec.add_dependency 'graphql', '>= 1.8', '< 2.7'
32
- spec.add_dependency 'rails', '>= 6.0', '< 8.1'
32
+ spec.add_dependency 'rails', '>= 6.0', '< 8.2'
33
33
  spec.add_dependency 'zeitwerk'
34
34
 
35
35
  spec.add_development_dependency 'appraisal'
36
- spec.add_development_dependency 'coveralls_reborn'
37
36
  spec.add_development_dependency 'factory_bot'
38
37
  spec.add_development_dependency 'faker'
39
38
  spec.add_development_dependency 'generator_spec'
@@ -46,5 +45,6 @@ Gem::Specification.new do |spec|
46
45
  spec.add_development_dependency 'rubocop-performance', '< 1.6.0'
47
46
  spec.add_development_dependency 'rubocop-rails', '< 2.6.0'
48
47
  spec.add_development_dependency 'rubocop-rspec', '< 1.39.0'
48
+ spec.add_development_dependency 'simplecov'
49
49
  spec.add_development_dependency 'sqlite3'
50
50
  end
@@ -10,7 +10,8 @@ module GraphqlDevise
10
10
  update_password_with_token: { klass: Mutations::UpdatePasswordWithToken, authenticatable: true },
11
11
  send_password_reset_with_token: { klass: Mutations::SendPasswordResetWithToken, authenticatable: false },
12
12
  resend_confirmation_with_token: { klass: Mutations::ResendConfirmationWithToken, authenticatable: false },
13
- confirm_registration_with_token: { klass: Mutations::ConfirmRegistrationWithToken, authenticatable: true }
13
+ confirm_registration_with_token: { klass: Mutations::ConfirmRegistrationWithToken, authenticatable: true },
14
+ destroy_account: { klass: Mutations::DestroyAccount, authenticatable: true, allow_destroy: true }
14
15
  }.freeze
15
16
  end
16
17
  end
@@ -17,7 +17,7 @@ module GraphqlDevise
17
17
  @selected_operations.except(*@custom_keys).each_with_object({}) do |(action, operation_info), result|
18
18
  mapped_action = "#{mapping_name}_#{action}"
19
19
  operation = operation_info[:klass]
20
- options = operation_info.except(:klass, :deprecation_reason)
20
+ options = operation_info.except(:klass, :deprecation_reason, :allow_destroy)
21
21
 
22
22
  result[mapped_action.to_sym] = [
23
23
  OperationPreparers::GqlNameSetter.new(mapped_action),
@@ -3,29 +3,56 @@
3
3
  module GraphqlDevise
4
4
  module MountMethod
5
5
  class OperationSanitizer
6
- def self.call(default:, only:, skipped:)
6
+ def self.call(default:, only:, skipped:, allow_destroy: false)
7
7
  new(
8
- default: default,
9
- only: only,
10
- skipped: skipped
8
+ default: default,
9
+ only: only,
10
+ skipped: skipped,
11
+ allow_destroy: allow_destroy
11
12
  ).call
12
13
  end
13
14
 
14
- def initialize(default:, only:, skipped:)
15
- @default = default
16
- @only = only
17
- @skipped = skipped
15
+ def initialize(default:, only:, skipped:, allow_destroy: false)
16
+ @default = default
17
+ @only = only
18
+ @skipped = skipped
19
+ @allow_destroy = allow_destroy
18
20
  end
19
21
 
20
22
  def call
21
- operations = if @only.present?
22
- @default.slice(*@only)
23
- elsif @skipped.present?
24
- @default.except(*@skipped)
25
- else
26
- @default
23
+ # When `only` is provided the list is explicit, so `allow_destroy` operations are included
24
+ # (and never warned about) whenever they are named in it.
25
+ return @default.slice(*@only) if @only.present?
26
+
27
+ selected = @skipped.present? ? @default.except(*@skipped) : @default
28
+
29
+ reject_allow_destroy(selected)
30
+ end
31
+
32
+ private
33
+
34
+ def reject_allow_destroy(operations)
35
+ return operations if @allow_destroy
36
+
37
+ operations.reject do |name, options|
38
+ next false unless requires_allow_destroy?(options)
39
+
40
+ warn_excluded(name)
41
+ true
27
42
  end
28
43
  end
44
+
45
+ def requires_allow_destroy?(options)
46
+ options.is_a?(Hash) && options[:allow_destroy]
47
+ end
48
+
49
+ def warn_excluded(name)
50
+ ::Rails.logger&.warn(
51
+ "[graphql_devise] The `#{name}` operation is not mounted because it is opt-in. " \
52
+ "Pass `allow_destroy: true` (or add it to the `only:` option) to include it. " \
53
+ "To keep it out and silence this warning, add `#{name}` to the `skip:` option."
54
+ )
55
+ end
29
56
  end
30
57
  end
31
58
  end
@@ -10,7 +10,8 @@ module GraphqlDevise
10
10
  additional_queries: OptionSanitizers::HashChecker.new(GraphQL::Schema::Resolver),
11
11
  additional_mutations: OptionSanitizers::HashChecker.new(GraphQL::Schema::Mutation),
12
12
  authenticatable_type: OptionSanitizers::ClassChecker.new(GraphQL::Schema::Member),
13
- public_introspection: OptionSanitizers::BooleanChecker.new(true)
13
+ public_introspection: OptionSanitizers::BooleanChecker.new(true),
14
+ allow_destroy: OptionSanitizers::BooleanChecker.new(false)
14
15
  }.freeze
15
16
  end
16
17
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphqlDevise
4
+ module Mutations
5
+ class DestroyAccount < Base
6
+ def resolve
7
+ raise_user_error(I18n.t('graphql_devise.user_not_found')) unless current_resource
8
+
9
+ if current_resource.destroy
10
+ # Clear the controller resource/token so devise_token_auth's `update_auth_header`
11
+ # after_action doesn't try to reload the now deleted record.
12
+ remove_resource
13
+
14
+ yield current_resource if block_given?
15
+
16
+ { authenticatable: current_resource }
17
+ else
18
+ raise_user_error_list(
19
+ I18n.t('graphql_devise.destroy_account_failed'),
20
+ resource: current_resource
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -117,7 +117,10 @@ module GraphqlDevise
117
117
  additional_operations: clean_options.additional_mutations,
118
118
  preparer: MountMethod::OperationPreparers::MutationFieldSetter.new(authenticatable_type),
119
119
  selected_operations: MountMethod::OperationSanitizer.call(
120
- default: DefaultOperations::MUTATIONS, only: clean_options.only, skipped: clean_options.skip
120
+ default: DefaultOperations::MUTATIONS,
121
+ only: clean_options.only,
122
+ skipped: clean_options.skip,
123
+ allow_destroy: clean_options.allow_destroy
121
124
  )
122
125
  ).call
123
126
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GraphqlDevise
4
- VERSION = '2.3.0'
4
+ VERSION = '2.4.0'
5
5
  end
@@ -7,13 +7,15 @@ Rails.application.routes.draw do
7
7
  base_controller: CookiesController,
8
8
  operations: { login: Mutations::Login, register: Mutations::Register },
9
9
  additional_mutations: { register_confirmed_user: Mutations::RegisterConfirmedUser },
10
- additional_queries: { public_user: Resolvers::PublicUser }
10
+ additional_queries: { public_user: Resolvers::PublicUser },
11
+ allow_destroy: true
11
12
  )
12
13
 
13
14
  mount_graphql_devise_for(
14
15
  Admin,
15
16
  authenticatable_type: Types::CustomAdminType,
16
17
  skip: [:register],
18
+ allow_destroy: true,
17
19
  operations: {
18
20
  update_password_with_token: Mutations::ResetAdminPasswordWithToken
19
21
  },
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe 'Destroy Account Requests' do
6
+ include_context 'with graphql query request'
7
+
8
+ let(:user) { create(:user, :confirmed) }
9
+ let(:query) do
10
+ <<-GRAPHQL
11
+ mutation {
12
+ userDestroyAccount {
13
+ authenticatable { email }
14
+ }
15
+ }
16
+ GRAPHQL
17
+ end
18
+
19
+ before { post_request }
20
+
21
+ context 'when user is logged in' do
22
+ let(:headers) { user.create_new_auth_token }
23
+
24
+ it 'destroys the account and does not set auth headers' do
25
+ expect(response).not_to include_auth_headers
26
+ expect(User.exists?(user.id)).to be(false)
27
+ expect(json_response[:data][:userDestroyAccount]).to match(
28
+ authenticatable: { email: user.email }
29
+ )
30
+ expect(json_response[:errors]).to be_nil
31
+ end
32
+ end
33
+
34
+ context 'when user is not logged in' do
35
+ it 'returns an error and does not delete anything' do
36
+ expect(response).not_to include_auth_headers
37
+ expect(User.exists?(user.id)).to be(true)
38
+ expect(json_response[:data][:userDestroyAccount]).to be_nil
39
+ expect(json_response[:errors]).to contain_exactly(
40
+ hash_including(message: 'User was not found or was not logged in.', extensions: { code: 'USER_ERROR' })
41
+ )
42
+ end
43
+ end
44
+
45
+ context 'when using the admin model' do
46
+ let(:query) do
47
+ <<-GRAPHQL
48
+ mutation {
49
+ adminDestroyAccount {
50
+ authenticatable { email }
51
+ }
52
+ }
53
+ GRAPHQL
54
+ end
55
+ let(:admin) { create(:admin, :confirmed) }
56
+ let(:headers) { admin.create_new_auth_token }
57
+
58
+ it 'destroys the admin account' do
59
+ expect(response).not_to include_auth_headers
60
+ expect(Admin.exists?(admin.id)).to be(false)
61
+ expect(json_response[:data][:adminDestroyAccount]).to match(
62
+ authenticatable: { email: admin.email }
63
+ )
64
+ expect(json_response[:errors]).to be_nil
65
+ end
66
+ end
67
+ end
@@ -4,11 +4,14 @@ require 'rails_helper'
4
4
 
5
5
  RSpec.describe GraphqlDevise::MountMethod::OperationSanitizer do
6
6
  describe '.call' do
7
- subject { described_class.call(default: default, only: only, skipped: skipped) }
7
+ subject(:sanitized) do
8
+ described_class.call(default: default, only: only, skipped: skipped, allow_destroy: allow_destroy)
9
+ end
8
10
 
9
11
  let(:op_class1) { Class.new }
10
12
  let(:op_class2) { Class.new }
11
13
  let(:op_class3) { Class.new }
14
+ let(:allow_destroy) { false }
12
15
 
13
16
  context 'when the operations passed are mutations' do
14
17
  let(:skipped) { [] }
@@ -31,5 +34,59 @@ RSpec.describe GraphqlDevise::MountMethod::OperationSanitizer do
31
34
  it { is_expected.to eq(operation1: { klass: op_class1 }) }
32
35
  end
33
36
  end
37
+
38
+ context 'when an operation requires the allow_destroy option' do
39
+ let(:skipped) { [] }
40
+ let(:only) { [] }
41
+ let(:default) do
42
+ {
43
+ operation1: { klass: op_class1 },
44
+ destroy_account: { klass: op_class2, allow_destroy: true }
45
+ }
46
+ end
47
+
48
+ context 'when no only/skip option and allow_destroy is off' do
49
+ it 'excludes the operation and logs a warning' do
50
+ expect(Rails.logger).to receive(:warn).with(/destroy_account/)
51
+ expect(sanitized).to eq(operation1: { klass: op_class1 })
52
+ end
53
+ end
54
+
55
+ context 'when using skip on other operations without allow_destroy' do
56
+ let(:skipped) { [:operation1] }
57
+
58
+ it 'still excludes the operation and logs a warning' do
59
+ expect(Rails.logger).to receive(:warn).with(/destroy_account/)
60
+ expect(sanitized).to eq({})
61
+ end
62
+ end
63
+
64
+ context 'when the operation itself is skipped' do
65
+ let(:skipped) { [:destroy_account] }
66
+
67
+ it 'excludes the operation without logging a warning' do
68
+ expect(Rails.logger).not_to receive(:warn)
69
+ expect(sanitized).to eq(operation1: { klass: op_class1 })
70
+ end
71
+ end
72
+
73
+ context 'when allow_destroy is on' do
74
+ let(:allow_destroy) { true }
75
+
76
+ it 'includes the operation without warning' do
77
+ expect(Rails.logger).not_to receive(:warn)
78
+ expect(sanitized).to eq(default)
79
+ end
80
+ end
81
+
82
+ context 'when named explicitly in only' do
83
+ let(:only) { [:destroy_account] }
84
+
85
+ it 'includes the operation without warning' do
86
+ expect(Rails.logger).not_to receive(:warn)
87
+ expect(sanitized).to eq(destroy_account: { klass: op_class2, allow_destroy: true })
88
+ end
89
+ end
90
+ end
34
91
  end
35
92
  end
data/spec/spec_helper.rb CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  if ENV['CI'] && !ENV['SKIP_COVERALLS']
4
4
  require 'simplecov'
5
- require 'coveralls'
6
5
 
7
- SimpleCov.formatter = Coveralls::SimpleCov::Formatter
6
+ # SimpleCov writes coverage/.resultset.json, which the Coveralls coverage-reporter
7
+ # (invoked by the coveralls CircleCI orb) picks up and uploads. We no longer upload
8
+ # from within Ruby so that every parallel job and the parallel_finished webhook share
9
+ # the same service number and aggregate into a single Coveralls build.
8
10
  SimpleCov.start 'rails' do
9
11
  add_filter ['spec']
10
12
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql_devise
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mario Celi
@@ -59,7 +59,7 @@ dependencies:
59
59
  version: '6.0'
60
60
  - - "<"
61
61
  - !ruby/object:Gem::Version
62
- version: '8.1'
62
+ version: '8.2'
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
@@ -69,7 +69,7 @@ dependencies:
69
69
  version: '6.0'
70
70
  - - "<"
71
71
  - !ruby/object:Gem::Version
72
- version: '8.1'
72
+ version: '8.2'
73
73
  - !ruby/object:Gem::Dependency
74
74
  name: zeitwerk
75
75
  requirement: !ruby/object:Gem::Requirement
@@ -98,20 +98,6 @@ dependencies:
98
98
  - - ">="
99
99
  - !ruby/object:Gem::Version
100
100
  version: '0'
101
- - !ruby/object:Gem::Dependency
102
- name: coveralls_reborn
103
- requirement: !ruby/object:Gem::Requirement
104
- requirements:
105
- - - ">="
106
- - !ruby/object:Gem::Version
107
- version: '0'
108
- type: :development
109
- prerelease: false
110
- version_requirements: !ruby/object:Gem::Requirement
111
- requirements:
112
- - - ">="
113
- - !ruby/object:Gem::Version
114
- version: '0'
115
101
  - !ruby/object:Gem::Dependency
116
102
  name: factory_bot
117
103
  requirement: !ruby/object:Gem::Requirement
@@ -280,6 +266,20 @@ dependencies:
280
266
  - - "<"
281
267
  - !ruby/object:Gem::Version
282
268
  version: 1.39.0
269
+ - !ruby/object:Gem::Dependency
270
+ name: simplecov
271
+ requirement: !ruby/object:Gem::Requirement
272
+ requirements:
273
+ - - ">="
274
+ - !ruby/object:Gem::Version
275
+ version: '0'
276
+ type: :development
277
+ prerelease: false
278
+ version_requirements: !ruby/object:Gem::Requirement
279
+ requirements:
280
+ - - ">="
281
+ - !ruby/object:Gem::Version
282
+ version: '0'
283
283
  - !ruby/object:Gem::Dependency
284
284
  name: sqlite3
285
285
  requirement: !ruby/object:Gem::Requirement
@@ -369,6 +369,7 @@ files:
369
369
  - lib/graphql_devise/mount_method/supported_options.rb
370
370
  - lib/graphql_devise/mutations/base.rb
371
371
  - lib/graphql_devise/mutations/confirm_registration_with_token.rb
372
+ - lib/graphql_devise/mutations/destroy_account.rb
372
373
  - lib/graphql_devise/mutations/login.rb
373
374
  - lib/graphql_devise/mutations/logout.rb
374
375
  - lib/graphql_devise/mutations/register.rb
@@ -475,6 +476,7 @@ files:
475
476
  - spec/requests/mutations/additional_mutations_spec.rb
476
477
  - spec/requests/mutations/additional_queries_spec.rb
477
478
  - spec/requests/mutations/confirm_registration_with_token_spec.rb
479
+ - spec/requests/mutations/destroy_account_spec.rb
478
480
  - spec/requests/mutations/login_spec.rb
479
481
  - spec/requests/mutations/logout_spec.rb
480
482
  - spec/requests/mutations/register_spec.rb
@@ -622,6 +624,7 @@ test_files:
622
624
  - spec/requests/mutations/additional_mutations_spec.rb
623
625
  - spec/requests/mutations/additional_queries_spec.rb
624
626
  - spec/requests/mutations/confirm_registration_with_token_spec.rb
627
+ - spec/requests/mutations/destroy_account_spec.rb
625
628
  - spec/requests/mutations/login_spec.rb
626
629
  - spec/requests/mutations/logout_spec.rb
627
630
  - spec/requests/mutations/register_spec.rb