Skip to content

Commit 56198dc

Browse files
authored
Merge pull request #633 from code0-tech/616-error-codes-as-enum
Represent service response errors as GraphQL enum
2 parents 3120a76 + 00308f0 commit 56198dc

36 files changed

+190
-30
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
module Types
4+
module Errors
5+
# rubocop:disable GraphQL/GraphqlName -- we don't want the module prefix
6+
class ErrorCodeEnum < Types::BaseEnum
7+
graphql_name 'ErrorCodeEnum'
8+
# rubocop:enable GraphQL/GraphqlName
9+
description 'Represents the available error responses'
10+
11+
ErrorCode.error_codes.each do |error_code, details|
12+
value error_code.upcase, details[:description],
13+
value: error_code,
14+
deprecation_reason: details.fetch(:deprecation_reason, nil)
15+
end
16+
end
17+
end
18+
end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# frozen_string_literal: true
2+
3+
module Types
4+
module Errors
5+
# rubocop:disable GraphQL/GraphqlName -- we don't want the module prefix
6+
class ErrorCodeType < Types::BaseObject
7+
graphql_name 'ErrorCode'
8+
# rubocop:enable GraphQL/GraphqlName
9+
description 'Represents an error code'
10+
11+
field :error_code, ErrorCodeEnum, null: false, description: 'The error code'
12+
end
13+
end
14+
end

app/graphql/types/errors/error_type.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ class ErrorType < BaseUnion
77
graphql_name 'Error'
88
# rubocop:enable GraphQL/GraphqlName
99
description 'Objects that can present an error'
10-
possible_types Errors::ActiveModelErrorType, Errors::MessageErrorType
10+
possible_types Errors::ActiveModelErrorType, Errors::MessageErrorType, Errors::ErrorCodeType
1111

1212
def self.resolve_type(object, _ctx)
1313
case object
1414
when ActiveModel::Error
1515
Errors::ActiveModelErrorType
1616
when Sagittarius::Graphql::ErrorMessageContainer
1717
Errors::MessageErrorType
18+
when Sagittarius::Graphql::ServiceResponseErrorContainer
19+
Errors::ErrorCodeType
1820
else
1921
raise 'Unsupported ErrorType'
2022
end

app/services/application_settings_update_service.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def initialize(current_authentication, params)
1212

1313
def execute
1414
unless Ability.allowed?(current_authentication, :update_application_setting)
15-
return ServiceResponse.error(message: 'Missing permissions', payload: :permission_missing)
15+
return ServiceResponse.error(message: 'Missing permissions', payload: :missing_permission)
1616
end
1717

1818
transactional do |t|

app/services/error_code.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# frozen_string_literal: true
2+
3+
class ErrorCode
4+
InvalidErrorCode = Class.new(StandardError)
5+
6+
def self.validate_error_code!(error_code)
7+
return unless error_code.is_a?(Symbol)
8+
return if Rails.env.production?
9+
10+
raise InvalidErrorCode, error_code unless error_codes.include?(error_code)
11+
end
12+
13+
def self.error_codes
14+
{
15+
missing_permission: { description: 'The user is not permitted to perform this operation' },
16+
missing_parameter: { description: 'Not all required parameters are present' },
17+
cannot_remove_last_administrator: { description: 'This action would remove the last administrator' },
18+
cannot_remove_last_admin_ability: { description: 'This action would remove the last administrative ability' },
19+
cannot_delete_last_admin_role: { description: 'This action would remove the last administrative role' },
20+
inconsistent_namespace: { description: 'Resources are from different namespaces' },
21+
runtime_mismatch: { description: 'Resources are from different runtimes' },
22+
generic_key_not_found: { description: 'The given key was not found in the data type' },
23+
no_primary_runtime: { description: 'The project does not have a primary runtime' },
24+
invalid_external_identity: { description: 'This external identity is invalid' },
25+
external_identity_does_not_exist: { description: 'This external identity does not exist' },
26+
identity_validation_failed: { description: 'Failed to validate the external identity' },
27+
missing_identity_data: { description: 'This external identity is missing data' },
28+
registration_disabled: { description: 'Self-registration is disabled' },
29+
mfa_failed: { description: 'Invalid MFA data provided' },
30+
mfa_required: { description: 'MFA is required' },
31+
invalid_login_data: { description: 'Invalid login data provided' },
32+
totp_secret_already_set: { description: 'This user already has TOTP set up' },
33+
wrong_totp: { description: 'Invalid TOTP code provided' },
34+
invalid_verification_code: { description: 'Invalid verification code provided' },
35+
unmodifiable_field: { description: 'The user is not permitted to modify this field' },
36+
failed_to_invalidate_old_backup_codes: { description: 'The old backup codes could not be deleted' },
37+
failed_to_save_valid_backup_code: { description: 'The new backup codes could not be saved' },
38+
invalid_setting: { description: 'Invalid setting provided' },
39+
40+
primary_level_not_found: { description: '', deprecation_reason: 'Outdated concept' },
41+
secondary_level_not_found: { description: '', deprecation_reason: 'Outdated concept' },
42+
tertiary_level_exceeds_parameters: { description: '', deprecation_reason: 'Outdated concept' },
43+
}
44+
end
45+
end
46+
47+
ErrorCode.prepend_extensions

app/services/service_response.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ def self.success(message: nil, payload: nil)
66
end
77

88
def self.error(message: nil, payload: nil)
9+
if payload.is_a?(Symbol) || payload.is_a?(Array)
10+
Array.wrap(payload).each { |error_code| ErrorCode.validate_error_code!(error_code) }
11+
end
12+
913
new(status: :error, message: message, payload: payload)
1014
end
1115

@@ -48,7 +52,12 @@ def to_mutation_response(success_key: :object)
4852
{ success_key => nil, errors: payload.errors }
4953
else
5054
errors = Array.wrap(payload).map do |message|
51-
Sagittarius::Graphql::ErrorMessageContainer.new(message: message)
55+
case message
56+
when String
57+
Sagittarius::Graphql::ErrorMessageContainer.new(message: message)
58+
when Symbol
59+
Sagittarius::Graphql::ServiceResponseErrorContainer.new(error_code: message)
60+
end
5261
end
5362

5463
{ success_key => nil, errors: errors }

app/services/users/identity/unlink_service.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def initialize(current_authentication, identity)
1616
def execute
1717
transactional do |t|
1818
if identity.nil?
19-
t.rollback_and_return! ServiceResponse.error(payload: :given_nil_identity,
19+
t.rollback_and_return! ServiceResponse.error(payload: :external_identity_does_not_exist,
2020
message: 'Nil identity given')
2121
end
2222

docs/graphql/enum/errorcodeenum.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: ErrorCodeEnum
3+
---
4+
5+
Represents the available error responses
6+
7+
| Value | Description |
8+
|-------|-------------|
9+
| `CANNOT_DELETE_LAST_ADMIN_ROLE` | This action would remove the last administrative role |
10+
| `CANNOT_REMOVE_LAST_ADMINISTRATOR` | This action would remove the last administrator |
11+
| `CANNOT_REMOVE_LAST_ADMIN_ABILITY` | This action would remove the last administrative ability |
12+
| `EXTERNAL_IDENTITY_DOES_NOT_EXIST` | This external identity does not exist |
13+
| `FAILED_TO_INVALIDATE_OLD_BACKUP_CODES` | The old backup codes could not be deleted |
14+
| `FAILED_TO_SAVE_VALID_BACKUP_CODE` | The new backup codes could not be saved |
15+
| `GENERIC_KEY_NOT_FOUND` | The given key was not found in the data type |
16+
| `IDENTITY_VALIDATION_FAILED` | Failed to validate the external identity |
17+
| `INCONSISTENT_NAMESPACE` | Resources are from different namespaces |
18+
| `INVALID_EXTERNAL_IDENTITY` | This external identity is invalid |
19+
| `INVALID_LOGIN_DATA` | Invalid login data provided |
20+
| `INVALID_SETTING` | Invalid setting provided |
21+
| `INVALID_VERIFICATION_CODE` | Invalid verification code provided |
22+
| `MFA_FAILED` | Invalid MFA data provided |
23+
| `MFA_REQUIRED` | MFA is required |
24+
| `MISSING_IDENTITY_DATA` | This external identity is missing data |
25+
| `MISSING_PARAMETER` | Not all required parameters are present |
26+
| `MISSING_PERMISSION` | The user is not permitted to perform this operation |
27+
| `NO_FREE_LICENSE_SEATS` | There are no free license seats to complete this operation |
28+
| `NO_PRIMARY_RUNTIME` | The project does not have a primary runtime |
29+
| `PRIMARY_LEVEL_NOT_FOUND` | **Deprecated:** Outdated concept |
30+
| `REGISTRATION_DISABLED` | Self-registration is disabled |
31+
| `RUNTIME_MISMATCH` | Resources are from different runtimes |
32+
| `SECONDARY_LEVEL_NOT_FOUND` | **Deprecated:** Outdated concept |
33+
| `TERTIARY_LEVEL_EXCEEDS_PARAMETERS` | **Deprecated:** Outdated concept |
34+
| `TOTP_SECRET_ALREADY_SET` | This user already has TOTP set up |
35+
| `UNMODIFIABLE_FIELD` | The user is not permitted to modify this field |
36+
| `WRONG_TOTP` | Invalid TOTP code provided |

docs/graphql/object/errorcode.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
title: ErrorCode
3+
---
4+
5+
Represents an error code
6+
7+
## Fields without arguments
8+
9+
| Name | Type | Description |
10+
|------|------|-------------|
11+
| `errorCode` | [`ErrorCodeEnum!`](../enum/errorcodeenum.md) | The error code |
12+

docs/graphql/union/error.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ Objects that can present an error
77
## Possible types
88

99
- [`ActiveModelError`](../object/activemodelerror.md)
10+
- [`ErrorCode`](../object/errorcode.md)
1011
- [`MessageError`](../object/messageerror.md)

0 commit comments

Comments
 (0)