virtuaservices 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/lib/virtuaservices.rb +11 -0
  3. data/lib/virtuaservices/account.rb +72 -0
  4. data/lib/virtuaservices/authentication.rb +7 -0
  5. data/lib/virtuaservices/authentication/session.rb +28 -0
  6. data/lib/virtuaservices/concerns.rb +13 -0
  7. data/lib/virtuaservices/concerns/activable.rb +18 -0
  8. data/lib/virtuaservices/concerns/diagnosticable.rb +22 -0
  9. data/lib/virtuaservices/concerns/enumerable.rb +44 -0
  10. data/lib/virtuaservices/concerns/mime_typable.rb +36 -0
  11. data/lib/virtuaservices/concerns/premiumable.rb +15 -0
  12. data/lib/virtuaservices/concerns/sluggable.rb +27 -0
  13. data/lib/virtuaservices/concerns/typable.rb +17 -0
  14. data/lib/virtuaservices/monitoring.rb +12 -0
  15. data/lib/virtuaservices/monitoring/action.rb +25 -0
  16. data/lib/virtuaservices/monitoring/gateway.rb +37 -0
  17. data/lib/virtuaservices/monitoring/instance.rb +36 -0
  18. data/lib/virtuaservices/monitoring/route.rb +36 -0
  19. data/lib/virtuaservices/monitoring/service.rb +37 -0
  20. data/lib/virtuaservices/monitoring/websocket.rb +25 -0
  21. data/lib/virtuaservices/oauth.rb +7 -0
  22. data/lib/virtuaservices/oauth/application.rb +33 -0
  23. data/lib/virtuaservices/permissions.rb +10 -0
  24. data/lib/virtuaservices/permissions/category.rb +15 -0
  25. data/lib/virtuaservices/permissions/group.rb +30 -0
  26. data/lib/virtuaservices/permissions/right.rb +19 -0
  27. data/lib/virtuaservices/specs.rb +89 -0
  28. data/lib/virtuaservices/utils.rb +10 -0
  29. data/lib/virtuaservices/utils/controllers.rb +8 -0
  30. data/lib/virtuaservices/utils/controllers/base.rb +193 -0
  31. data/lib/virtuaservices/utils/controllers/checked.rb +14 -0
  32. data/lib/virtuaservices/utils/errors.rb +12 -0
  33. data/lib/virtuaservices/utils/errors/bad_request.rb +14 -0
  34. data/lib/virtuaservices/utils/errors/forbidden.rb +14 -0
  35. data/lib/virtuaservices/utils/errors/http_error.rb +30 -0
  36. data/lib/virtuaservices/utils/errors/not_found.rb +14 -0
  37. data/lib/virtuaservices/utils/loaders.rb +7 -0
  38. data/lib/virtuaservices/utils/loaders/heroku.rb +20 -0
  39. data/lib/virtuaservices/utils/micro_service.rb +170 -0
  40. data/lib/virtuaservices/utils/seeder.rb +25 -0
  41. data/lib/virtuaservices/version.rb +3 -0
  42. metadata +321 -0
@@ -0,0 +1,36 @@
1
+ module Virtuaservices
2
+ module Monitoring
3
+ # A route is an endpoint accessible in a service. Each route has to have an associated endpoint in the deployed instances.
4
+ # @param Vincent Courtois <courtois.vincent@outlook.com>
5
+ class Route
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+ include Virtuaservices::Concerns::Premiumable
9
+ include Virtuaservices::Concerns::Activable
10
+
11
+ # @!attribute [rw] path
12
+ # @return [String] the path (URI) of the route in the service?
13
+ field :path, type: String, default: '/'
14
+ # @!attribute [rw] verb
15
+ # @return [String] the verb (HTTP method) of this route in the service.
16
+ field :verb, type: String, default: 'get'
17
+ # @!attribute [rw] authenticated
18
+ # @return [Boolean] if true, the session_id is needed for this route, if false it is not.
19
+ field :authenticated, type: Boolean, default: true
20
+
21
+ # @!attribute [rw] service
22
+ # @return [Virtuaservices::Monitoring::Service] the service in which this route is declared.
23
+ belongs_to :service, class_name: 'Virtuaservices::Monitoring::Service', inverse_of: :routes
24
+
25
+ # @!attribute [rw] groups
26
+ # @return [Array<Virtuaservices::Permissions::Group>] the groups having permission to access this route.
27
+ has_and_belongs_to_many :groups, class_name: 'Virtuaservices::Permissions::Group', inverse_of: :groups
28
+
29
+ validates :path,
30
+ format: {with: /\A(\/|((\/:?[a-zA-Z0-9_]+)+))\z/, message: 'pattern', if: :path?}
31
+
32
+ validates :verb,
33
+ inclusion: {message: 'unknown', in: ['get', 'post', 'put', 'delete', 'patch', 'option']}
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,37 @@
1
+ module Virtuaservices
2
+ module Monitoring
3
+ # A service is the representation of one of the applications composing the API.
4
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
5
+ class Service
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+ include Virtuaservices::Concerns::Activable
9
+ include Virtuaservices::Concerns::Diagnosticable
10
+ include Virtuaservices::Concerns::Premiumable
11
+
12
+ # @!attribute [rw] key
13
+ # @return [String] the name, or title of the service, optionally given to identify it more easily.
14
+ field :key, type: String
15
+ # @!attribute [rw] path
16
+ # @return [String] the path the service will be mapped on in the API.
17
+ field :path, type: String, default: '/'
18
+ # @!attribute [rw] test_mode
19
+ # @return [Boolean] TRUE if the service is currently in test mode and thus the gateway shall only qurty local instances.
20
+ field :test_mode, type: Boolean, default: false
21
+
22
+ # @!attribute [rw] creator
23
+ # @return [Virtuaservices::Account] the creator of this service.
24
+ belongs_to :creator, class_name: 'Virtuaservices::Account', optional: true, inverse_of: :services
25
+ # @!attribute [rw] instances
26
+ # @return [Array<Virtuaservices::Monitoring::Instance>] the instances of this service currently deployed.
27
+ embeds_many :instances, class_name: 'Virtuaservices::Monitoring::Instance', inverse_of: :service
28
+ # @!attribute [rw] routes
29
+ # @return [Array<Virtuaservices::Monitoring::Route>] the routes associated to this service, accessible from the gateway.
30
+ has_many :routes, class_name: 'Virtuaservices::Monitoring::Route', inverse_of: :service
31
+
32
+ validates :key, uniqueness: {message: 'uniq'}
33
+
34
+ validates :path, format: {with: /\A(\/:?[a-z]+)+\z/, message: 'pattern'}
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ module Virtuaservices
2
+ module Monitoring
3
+ # The websocket is a particular kind of service, just like the gateway. It always has the same signature.
4
+ # A websocket document is a particular instance of websocket, located on a server and answering to a URL.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ class Websocket
7
+ include Mongoid::Document
8
+ include Mongoid::Timestamps
9
+ include Virtuaservices::Concerns::Activable
10
+ include Virtuaservices::Concerns::Diagnosticable
11
+
12
+ # @!attribute [rw] url
13
+ # @return [String] the URL of the websocket to be contacted on.
14
+ field :url, type: String
15
+
16
+ # @!attribute [rw] creator
17
+ # @return [Virtuaservices::Account] the account that created this web socket instance in the database.
18
+ belongs_to :creator, class_name: 'Virtuaservices::Account', inverse_of: :web_sockets, optional: true
19
+
20
+ validates :url,
21
+ presence: {message: 'required'},
22
+ format: {with: /\A(ws:\/\/)([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?\z/, message: 'pattern', if: :url?}
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ module Virtuaservices
2
+ # This module holds the logic for the connection of an application to our API.
3
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
4
+ module OAuth
5
+ autoload :Application, 'virtuaservices/oauth/application'
6
+ end
7
+ end
@@ -0,0 +1,33 @@
1
+ module Virtuaservices
2
+ module OAuth
3
+ # An application is what is referred to in the OAuth2.0 RFC as a client, wanting to access private informations about the user.
4
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
5
+ class Application
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+
9
+ # @!attribute [rw] name
10
+ # @return [String] the unique name of the application, mainly used to identify and display it.
11
+ field :name, type: String
12
+ # @!attribute [rw] key
13
+ # @return [String] the unique key for the application, identifying it when requesting a token for the API.
14
+ field :key, type: String, default: ->{ SecureRandom.hex }
15
+ # @!attribute [rw] premium
16
+ # @return [Boolean] a value indicating whether the application should automatically receive a token when an account is created, or not.
17
+ field :premium, type: Boolean, default: false
18
+
19
+ # @!attribute [rw] creator
20
+ # @return [Virtuaservices::Account] the account that has created this application, considered its owner.
21
+ belongs_to :creator, class_name: 'Virtuaservices::Account', inverse_of: :applications
22
+
23
+ validates :name,
24
+ presence: {message: 'required'},
25
+ length: {minimum: 6, message: 'minlength'},
26
+ uniqueness: {message: 'uniq'}
27
+
28
+ validates :key,
29
+ presence: {message: 'required'},
30
+ uniqueness: {message: 'uniq'}
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ module Virtuaservices
2
+ # This module holds the logic for all the classes concerning the permissions abd rights for the user.
3
+ # A permission is restricting the access to one or several features to the users having it.
4
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
5
+ module Permissions
6
+ autoload :Right , 'virtuaservices/permissions/right'
7
+ autoload :Group , 'virtuaservices/permissions/group'
8
+ autoload :Category, 'virtuaservices/permissions/category'
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ module Virtuaservices
2
+ module Permissions
3
+ # A category of rights regroups one or several rights for convenience purposes.
4
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
5
+ class Category
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+ include Virtuaservices::Concerns::Sluggable
9
+
10
+ has_many :rights, class_name: 'Virtuaservices::Permissions::Right', inverse_of: :category
11
+
12
+ make_sluggable 'category'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ module Virtuaservices
2
+ module Permissions
3
+ # A group gathers one or several users to give them the same rights for conviniency purposes.
4
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
5
+ class Group
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+ include Virtuaservices::Concerns::Sluggable
9
+
10
+ # @!attribute [rw] is_default
11
+ # @return [Boolean] a boolean indicating whether this group is given when a new user registered or not.
12
+ field :is_default, type: Boolean, default: false
13
+ # @!attribute [rw] is_superuser
14
+ # @return [Boolean] a boolean indicating whether this group should have access to all groups and rights or not.
15
+ field :is_superuser, type: Boolean, default: false
16
+
17
+ # @!attribute [rw] accounts
18
+ # @return [Array<Virtuaservices::Account>] the accounts having the rights granted by this group.
19
+ has_and_belongs_to_many :accounts, class_name: 'Virtuaservices::Account', inverse_of: :groups
20
+ # @!attribute [rw] rights
21
+ # @return [Array<Virtuaservices::Permissions::Right>] the rights granted by belonging to this group.
22
+ has_and_belongs_to_many :rights, class_name: 'Virtuaservices::Permissions::Right', inverse_of: :groups
23
+ # @!attribute [rw] routes
24
+ # @return [Array<Virtuaservices::Monitoring::Route>] the routes this group can access in the API.
25
+ has_and_belongs_to_many :routes, class_name: 'Virtuaservices::Monitoring::Route', inverse_of: :groups
26
+
27
+ make_sluggable 'group'
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ module Virtuaservices
2
+ module Permissions
3
+ # A right is the access to one or several features in the application. It's applied to a group, and transitively to an account.
4
+ # @author Vincent Courtois <courtois;vincent@outlook.com>
5
+ class Right
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+ include Virtuaservices::Concerns::Sluggable
9
+
10
+ # @!attribute [rw] groups
11
+ # @return [Array<Virtuaservices::Permissions::Group>] the groups granted with the permission to access features opened by this right.
12
+ has_and_belongs_to_many :groups, class_name: 'Virtuaservices::Permissions::Group', inverse_of: :rights
13
+
14
+ belongs_to :category, class_name: 'Virtuaservices::Permissions::Category', inverse_of: :rights
15
+
16
+ make_sluggable 'right'
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,89 @@
1
+ module Virtuaservices
2
+ # This module holds all the logic for the specs tools for all micro services (shared examples and other things).
3
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
4
+ module Specs
5
+
6
+ # Includes all the shared examples you could need, describing the basic behaviour of a route.
7
+ def self.include_shared_examples
8
+ RSpec.shared_examples 'a route' do |_verb, _path|
9
+ let(:verb) { _verb }
10
+ let(:path) { _path }
11
+
12
+ def do_request(parameters)
13
+ public_send verb.to_sym, path, ['get', 'delete'].include?(verb) ? parameters : parameters.to_json
14
+ end
15
+
16
+ describe 'common errors' do
17
+ describe 'bad request errors' do
18
+ describe 'no token error' do
19
+ before do
20
+ do_request({app_key: 'test_key'})
21
+ end
22
+ it 'Raises a bad request (400) error when the parameters don\'t contain the token of the gateway' do
23
+ expect(last_response.status).to be 400
24
+ end
25
+ it 'returns the correct response if the parameters do not contain a gateway token' do
26
+ expect(JSON.parse(last_response.body)).to eq({
27
+ 'status' => 400,
28
+ 'field' => 'token',
29
+ 'error' => 'required',
30
+ 'docs' => 'https://github.com/jdr-tools/wiki/wiki/Common-errors#gateway-token-not-given'
31
+ })
32
+ end
33
+ end
34
+ describe 'no application key error' do
35
+ before do
36
+ do_request({token: 'test_token'})
37
+ end
38
+ it 'Raises a bad request (400) error when the parameters don\'t contain the application key' do
39
+ expect(last_response.status).to be 400
40
+ end
41
+ it 'returns the correct response if the parameters do not contain a application key' do
42
+ expect(JSON.parse(last_response.body)).to eq({
43
+ 'status' => 400,
44
+ 'field' => 'app_key',
45
+ 'error' => 'required',
46
+ 'docs' => 'https://github.com/jdr-tools/wiki/wiki/Common-errors#application-key-not-given'
47
+ })
48
+ end
49
+ end
50
+ end
51
+ describe 'not_found errors' do
52
+ describe 'application not found' do
53
+ before do
54
+ do_request({token: 'test_token', app_key: 'another_key'})
55
+ end
56
+ it 'Raises a not found (404) error when the key doesn\'t belong to any application' do
57
+ expect(last_response.status).to be 404
58
+ end
59
+ it 'returns the correct body when the application doesn\'t exist' do
60
+ expect(JSON.parse(last_response.body)).to eq({
61
+ 'status' => 404,
62
+ 'field' => 'app_key',
63
+ 'error' => 'unknown',
64
+ 'docs' => 'https://github.com/jdr-tools/wiki/wiki/Common-errors#application-key-not-found'
65
+ })
66
+ end
67
+ end
68
+ describe 'gateway not found' do
69
+ before do
70
+ do_request({token: 'other_token', app_key: 'test_key'})
71
+ end
72
+ it 'Raises a not found (404) error when the gateway does\'nt exist' do
73
+ expect(last_response.status).to be 404
74
+ end
75
+ it 'returns the correct body when the gateway doesn\'t exist' do
76
+ expect(JSON.parse(last_response.body)).to eq({
77
+ 'status' => 404,
78
+ 'field' => 'token',
79
+ 'error' => 'unknown',
80
+ 'docs' => 'https://github.com/jdr-tools/wiki/wiki/Common-errors#gateway-token-not-found'
81
+ })
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,10 @@
1
+ module Virtuaservices
2
+ # Utility classes for the different micro-services of the suite.
3
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
4
+ module Utils
5
+ autoload :Controllers , 'virtuaservices/utils/controllers'
6
+ autoload :Errors , 'virtuaservices/utils/errors'
7
+ autoload :Loaders , 'virtuaservices/utils/loaders'
8
+ autoload :MicroService, 'virtuaservices/utils/micro_service'
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ module Arkaan
2
+ module Utils
3
+ module Controllers
4
+ autoload :Base , 'arkaan/utils/controllers/base'
5
+ autoload :Checked, 'arkaan/utils/controllers/checked'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,193 @@
1
+ module Arkaan
2
+ module Utils
3
+ module Controllers
4
+ # Base controller to handle the standard error when accessing the API.
5
+ # @author Vincent Courtois <courtois.vincenet@outlook.com>
6
+ class Base < Sinatra::Base
7
+ register Sinatra::ConfigFile
8
+ helpers Sinatra::CustomLogger
9
+
10
+ configure do
11
+ set :logger, Logger.new(STDOUT)
12
+ logger.level = Logger::ERROR if ENV['RACK_ENV'] == 'test'
13
+ end
14
+
15
+ # Creates a premium route whithin the Sinatra application, and registers it in the database if it does not already exists.
16
+ # @param verb [String] the HTTP method used to create this route.
17
+ # @param path [String] the path, beginning with a /, of the route to create.
18
+ def self.declare_route(verb, path, options: {}, &block)
19
+ self.declare_route_with(verb, path, false, options, &block)
20
+ end
21
+
22
+ # Creates a non premium route whithin the Sinatra application, and registers it in the database if it does not already exists.
23
+ # @param verb [String] the HTTP method used to create this route.
24
+ # @param path [String] the path, beginning with a /, of the route to create.
25
+ def self.declare_premium_route(verb, path, options: {}, &block)
26
+ self.declare_route_with(verb, path, true, options, &block)
27
+ end
28
+
29
+ # Creates a route whithin the Sinatra application, and registers it in the database if it does not already exists.
30
+ # @param verb [String] the HTTP method used to create this route.
31
+ # @param path [String] the path, beginning with a /, of the route to create.
32
+ # @param premium [Boolean] TRUE to make the route premium, FALSE otherwise.
33
+ def self.declare_route_with(verb, path, premium, options, &block)
34
+ service = Arkaan::Utils::MicroService.instance.service
35
+ complete_path = "#{Arkaan::Utils::MicroService.instance.path}#{path == '/' ? '' : path}"
36
+
37
+ unless service.nil? || !service.routes.where(path: path, verb: verb).first.nil?
38
+ route = Arkaan::Monitoring::Route.create(path: path, verb: verb, premium: premium, service: service)
39
+ if !options.nil? && !options[:authenticated].nil?
40
+ route.update_attribute(:authenticated, false)
41
+ end
42
+ Arkaan::Permissions::Group.where(is_superuser: true).each do |group|
43
+ group.routes << route
44
+ group.save!
45
+ end
46
+ end
47
+ if premium
48
+ self.public_send(verb, complete_path) do
49
+ @sinatra_route = parse_current_route
50
+ if !@application.premium?
51
+ custom_error(403, 'common.app_key.forbidden')
52
+ end
53
+ instance_eval(&block)
54
+ end
55
+ else
56
+ logger.info "#{verb} #{complete_path}"
57
+ self.public_send(verb, complete_path, &block)
58
+ end
59
+ end
60
+
61
+ # Loads the errors configuration file from the config folder.
62
+ # @param file [String] send __FILE__
63
+ def self.load_errors_from(file)
64
+ config_file File.join(File.dirname(file), '..', 'config', 'errors.yml')
65
+ end
66
+
67
+ def before_checks
68
+ add_body_to_params
69
+
70
+ check_presence('token', 'app_key', route: 'common')
71
+
72
+ gateway = Arkaan::Monitoring::Gateway.where(token: params['token']).first
73
+ @application = Arkaan::OAuth::Application.where(key: params['app_key']).first
74
+
75
+ if gateway.nil?
76
+ custom_error(404, 'common.token.unknown')
77
+ elsif @application.nil?
78
+ custom_error(404, 'common.app_key.unknown')
79
+ end
80
+ end
81
+
82
+ # Checks the presence of several fields given as parameters and halts the execution if it's not present.
83
+ # @param fields [Array<String>] an array of fields names to search in the parameters
84
+ # @param route [String] the name of the route you're requiring to put in the error message.
85
+ def check_presence(*fields, route:)
86
+ fields.each do |field|
87
+ custom_error(400, "#{route}.#{field}.required") if params[field].nil? || params[field] == ''
88
+ end
89
+ end
90
+
91
+ # Checks the presence of either fields given in parameters. It halts with an error only if ALL parameters are not given.
92
+ # @param fields [Array<String>] an array of fields names to search in the parameters
93
+ # @param route [String] the name of the route you're requiring to put in the error message.
94
+ # @param key [String] the key to search in the errors configuration file.
95
+ def check_either_presence(*fields, route:, key:)
96
+ fields.each do |field|
97
+ return true if !params[field].nil? && params[field] != ''
98
+ end
99
+ custom_error 400, "#{route}.#{key}.required"
100
+ end
101
+
102
+ # Checks if the session ID is given in the parameters and if the session exists.
103
+ # @param action [String] the action used to get the errors from the errors file.
104
+ # @return [Arkaan::Authentication::Session] the session when it exists.
105
+ def check_session(action)
106
+ check_presence('session_id', route: action)
107
+ session = Arkaan::Authentication::Session.where(token: params['session_id']).first
108
+ if session.nil?
109
+ custom_error(404, "#{action}.session_id.unknown")
110
+ end
111
+ return session
112
+ end
113
+
114
+ def check_application(action)
115
+ check_presence('app_key', route: action)
116
+ application = Arkaan::OAuth::Application.where(key: params['app_key']).first
117
+ custom_error(404, "#{action}.app_key.unknown") if application.nil?
118
+ return application
119
+ end
120
+
121
+ # Adds the parsed body to the parameters, overwriting the parameters of the querystring with the values
122
+ # of the SON body if they have similar keys.
123
+ def add_body_to_params
124
+ if request.body.respond_to?(:rewind) && request.body.respond_to?(:read)
125
+ request.body.rewind
126
+ parsed_body = JSON.parse(request.body.read.to_s) rescue {}
127
+ parsed_body.keys.each do |key|
128
+ params[key] = parsed_body[key]
129
+ end
130
+ end
131
+ end
132
+
133
+ # Gets the current route in the database from the sinatra route.
134
+ # @return [Arkaan::Monitoring::Route] the route declared in the services registry.
135
+ def parse_current_route
136
+ splitted = request.env['sinatra.route'].split(' ')
137
+ return Arkaan::Monitoring::Route.where(verb: splitted.first.downcase, path: splitted.last).first
138
+ end
139
+
140
+ # Halts the application and creates the returned body from the parameters and the errors config file.
141
+ # @param status [Integer] the HTTP status to halt the application with.
142
+ # @param path [String] the path in the configuration file to access the URL.
143
+ def custom_error(status, path)
144
+ route, field, error = path.split('.')
145
+ docs = settings.errors[route][field][error] rescue ''
146
+ halt status, {status: status, field: field, error: error, docs: docs}.to_json
147
+ end
148
+
149
+ # Halts the application with a Bad Request error affecting a field of a model.
150
+ # @param instance [Mongoid::Document] the document having a field in error.
151
+ # @param route [String] the type of action you're currently doing (e.g: 'creation')
152
+ def model_error(instance, route)
153
+ messages = instance.errors.messages
154
+ field = messages.keys.first
155
+ custom_error(400, "#{route}.#{field}.#{messages[field].first}")
156
+ end
157
+
158
+ # Select parameters in the params hash, by its keys.
159
+ # @param fields [Array<String>] the keys to select in the params hash.
160
+ # @return [Hash] the selected chunk of the params hash.
161
+ def select_params(*fields)
162
+ return params.select { |key, value| fields.include?(key) }
163
+ end
164
+
165
+ # Creates a custom error from an existing Arkaan exception class.
166
+ # @param exception {StandardError} the exception to transform in a usable error.
167
+ def handle_arkaan_exception(exception)
168
+ custom_error(exception.status, "#{exception.action}.#{exception.field}.#{exception.error}")
169
+ end
170
+
171
+ error Arkaan::Utils::Errors::BadRequest do |exception|
172
+ handle_arkaan_exception(exception)
173
+ end
174
+
175
+ error Arkaan::Utils::Errors::Forbidden do |exception|
176
+ handle_arkaan_exception(exception)
177
+ end
178
+
179
+ error Arkaan::Utils::Errors::NotFound do |exception|
180
+ handle_arkaan_exception(exception)
181
+ end
182
+
183
+ error Arkaan::Factories::Errors::GatewayNotFound do |exception|
184
+ handle_arkaan_exception(exception)
185
+ end
186
+
187
+ error StandardError do |exception|
188
+ custom_error(500, 'system_error.unknown_field.unknown_error')
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end