commons_yellowme 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +17 -0
- data/lib/commons.rb +67 -0
- data/lib/commons/authentication/authenticate_by_jwt.rb +27 -0
- data/lib/commons/authentication/json_web_token.rb +17 -0
- data/lib/commons/concerns/attributes/sex.rb +19 -0
- data/lib/commons/concerns/extensions/deleted.rb +25 -0
- data/lib/commons/concerns/guard/capitalizable.rb +32 -0
- data/lib/commons/concerns/validations/undestroyable.rb +18 -0
- data/lib/commons/config.rb +11 -0
- data/lib/commons/config/locales/en.yml +1 -0
- data/lib/commons/config/locales/es.yml +58 -0
- data/lib/commons/controllers/schema_validable.rb +27 -0
- data/lib/commons/errors/bad_request.rb +15 -0
- data/lib/commons/errors/conflict.rb +15 -0
- data/lib/commons/errors/default_handling.rb +44 -0
- data/lib/commons/errors/error_base.rb +64 -0
- data/lib/commons/errors/error_serializer.rb +10 -0
- data/lib/commons/errors/forbidden.rb +15 -0
- data/lib/commons/errors/internal_server_error.rb +15 -0
- data/lib/commons/errors/invalid_resource.rb +21 -0
- data/lib/commons/errors/maintenance_mode.rb +14 -0
- data/lib/commons/errors/missing_parameter.rb +13 -0
- data/lib/commons/errors/not_unique.rb +14 -0
- data/lib/commons/errors/payment_required.rb +15 -0
- data/lib/commons/errors/precondition_failed.rb +15 -0
- data/lib/commons/errors/resource_not_found.rb +14 -0
- data/lib/commons/errors/route_not_found.rb +14 -0
- data/lib/commons/errors/unauthorized.rb +15 -0
- data/lib/commons/errors/unprocessable_entity.rb +15 -0
- data/lib/commons/formatter/e164_phone.rb +47 -0
- data/lib/commons/formatter/null_attributes_remover.rb +9 -0
- data/lib/commons/formatter/regex_constants.rb +9 -0
- data/lib/commons/formatter/string_utils.rb +9 -0
- data/lib/commons/middleware/catch_json_parse_errors.rb +30 -0
- data/lib/commons/railtie.rb +4 -0
- data/lib/commons/repositories/base_repository.rb +227 -0
- data/lib/commons/repositories/catalogs/base_catalog.rb +68 -0
- data/lib/commons/repositories/catalogs/concerns/model_caching_extention.rb +27 -0
- data/lib/commons/services/concerns/callable.rb +15 -0
- data/lib/commons/version.rb +3 -0
- data/lib/tasks/commons_tasks.rake +4 -0
- metadata +254 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 72561778339f9301bc276ec516c4bf13d3e98323c93ba4a7bfce41d67dcca9d2
|
4
|
+
data.tar.gz: 9036b246544ec395c0cfb7829bdbea2b85deb1cbf14b465c8aacf3eaaf56c709
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 563e23f1dfecd7805b3e274327365fe77a789db503cb421e00773d4ada57fb990456cfc5dea53fb0a411d1622074e4ef5985e851f54bce516ae6237d5863c785
|
7
|
+
data.tar.gz: 87e562f16d6f70f1c870c6db5ade5f5ae3c48601704e7040f7c327f8ad7c3c42b56a345268a68fa2bc1c04965b5ab672512f9d46bd3dc83144eb31afc18c762d
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2019 Juan Ku
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Commons
|
2
|
+
Commons is Yellowme's Rails APIs utilities gem
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
How to use my plugin.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'commons', git: 'https://github.com/yellowme/commons.git'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install commons
|
22
|
+
```
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
Contribution directions go here.
|
26
|
+
|
27
|
+
## License
|
28
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Commons'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'bundler/gem_tasks'
|
data/lib/commons.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'commons/railtie'
|
2
|
+
|
3
|
+
require 'strip_attributes'
|
4
|
+
require 'active_model_serializers'
|
5
|
+
require 'jwt'
|
6
|
+
require 'phonelib'
|
7
|
+
|
8
|
+
require 'commons/config'
|
9
|
+
|
10
|
+
# authentication
|
11
|
+
require 'commons/authentication/json_web_token'
|
12
|
+
require 'commons/authentication/authenticate_by_jwt'
|
13
|
+
|
14
|
+
# controllers
|
15
|
+
require 'commons/controllers/schema_validable'
|
16
|
+
|
17
|
+
# repositories
|
18
|
+
require 'commons/repositories/base_repository'
|
19
|
+
require 'commons/repositories/catalogs/concerns/model_caching_extention'
|
20
|
+
require 'commons/repositories/catalogs/base_catalog'
|
21
|
+
|
22
|
+
# services
|
23
|
+
require 'commons/services/concerns/callable'
|
24
|
+
|
25
|
+
# models
|
26
|
+
require 'commons/concerns/attributes/sex'
|
27
|
+
require 'commons/concerns/extensions/deleted'
|
28
|
+
require 'commons/concerns/guard/capitalizable'
|
29
|
+
require 'commons/concerns/validations/undestroyable'
|
30
|
+
|
31
|
+
# utils
|
32
|
+
require 'commons/formatter/null_attributes_remover'
|
33
|
+
require 'commons/formatter/regex_constants'
|
34
|
+
require 'commons/formatter/string_utils'
|
35
|
+
require 'commons/formatter/e164_phone'
|
36
|
+
|
37
|
+
# errors
|
38
|
+
require 'commons/errors/error_base'
|
39
|
+
require 'commons/errors/error_serializer'
|
40
|
+
require 'commons/errors/bad_request'
|
41
|
+
require 'commons/errors/conflict'
|
42
|
+
require 'commons/errors/default_handling'
|
43
|
+
require 'commons/errors/forbidden'
|
44
|
+
require 'commons/errors/internal_server_error'
|
45
|
+
require 'commons/errors/unprocessable_entity'
|
46
|
+
require 'commons/errors/invalid_resource'
|
47
|
+
require 'commons/errors/maintenance_mode'
|
48
|
+
require 'commons/errors/missing_parameter'
|
49
|
+
require 'commons/errors/not_unique'
|
50
|
+
require 'commons/errors/payment_required'
|
51
|
+
require 'commons/errors/precondition_failed'
|
52
|
+
require 'commons/errors/resource_not_found'
|
53
|
+
require 'commons/errors/route_not_found'
|
54
|
+
require 'commons/errors/unauthorized'
|
55
|
+
|
56
|
+
# middleware
|
57
|
+
require 'commons/middleware/catch_json_parse_errors'
|
58
|
+
|
59
|
+
mydir = __dir__
|
60
|
+
|
61
|
+
I18n.load_path += Dir[File.join(mydir, 'commons', 'config', 'locales', '**/*.yml').to_s]
|
62
|
+
I18n.reload! if I18n.backend.initialized?
|
63
|
+
|
64
|
+
Phonelib.default_country = "MX"
|
65
|
+
|
66
|
+
module Commons
|
67
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Commons
|
2
|
+
module Authentication
|
3
|
+
module AuthenticateByJWT
|
4
|
+
def authorize_jwt!
|
5
|
+
raise Commons::Errors::Unauthorized if jwt.blank?
|
6
|
+
|
7
|
+
begin
|
8
|
+
decoded = JSONWebToken.decode(jwt)
|
9
|
+
@current_user = UserRepository.instance.find_by(id: decoded[:user_id])
|
10
|
+
rescue ActiveRecord::RecordNotFound => e
|
11
|
+
raise Commons::Errors::Unauthorized
|
12
|
+
rescue JWT::DecodeError => e
|
13
|
+
raise Commons::Errors::Unauthorized
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def jwt
|
20
|
+
jwt ||= request.headers['Authorization']
|
21
|
+
return nil unless jwt.instance_of? String
|
22
|
+
|
23
|
+
jwt = jwt.split(' ').last if jwt
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Commons
|
2
|
+
module Authentication
|
3
|
+
class JSONWebToken
|
4
|
+
SECRET_KEY = Commons.secret_key_base.to_s
|
5
|
+
|
6
|
+
def self.encode(payload, exp = 24.hours.from_now)
|
7
|
+
payload[:expires_at] = exp.to_i
|
8
|
+
JWT.encode(payload, SECRET_KEY)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.decode(token)
|
12
|
+
decoded = JWT.decode(token, SECRET_KEY)[0]
|
13
|
+
HashWithIndifferentAccess.new decoded
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Commons
|
2
|
+
module Concerns
|
3
|
+
module Attributes
|
4
|
+
module Sex
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
FEMALE = 'female'.freeze
|
8
|
+
MALE = 'male'.freeze
|
9
|
+
|
10
|
+
included do
|
11
|
+
enum sex: {
|
12
|
+
female: FEMALE,
|
13
|
+
male: MALE
|
14
|
+
}, _suffix: true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Commons
|
2
|
+
module Concerns
|
3
|
+
module Extensions
|
4
|
+
module Deleted
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
before_validation :check_not_deleted, on: [:update]
|
9
|
+
|
10
|
+
def deleted?
|
11
|
+
raise ActiveModel::MissingAttributeError unless self.has_attribute?(:deleted_at)
|
12
|
+
self.deleted_at.present?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def check_not_deleted
|
19
|
+
raise ActiveModel::MissingAttributeError unless self.has_attribute?(:deleted_at)
|
20
|
+
raise ActiveRecord::RecordInvalid if self.deleted_at_in_database.present?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Commons
|
2
|
+
module Concerns
|
3
|
+
module Guard
|
4
|
+
module Capitalizable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
attr_reader :only
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def capitalize(only: [])
|
13
|
+
@only = only
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
included do
|
18
|
+
before_validation :capitalize_attrs
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def capitalize_attrs
|
24
|
+
self.class.only.each do |attr|
|
25
|
+
value = self.instance_eval(attr.to_s)
|
26
|
+
self.send("#{attr.to_s}=", Commons::Formatter::StringUtils.capitalize(value)) unless value.nil?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Commons
|
2
|
+
module Concerns
|
3
|
+
module Validations
|
4
|
+
module Undestroyable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
included do
|
7
|
+
before_destroy :prevent_destroy
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def prevent_destroy
|
13
|
+
raise Commons::Errors::Unauthorized
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
en:
|
@@ -0,0 +1,58 @@
|
|
1
|
+
es:
|
2
|
+
status_code:
|
3
|
+
IER4001_route_not_found:
|
4
|
+
code: 'IER4001'
|
5
|
+
title: 'Ruta no encontrada'
|
6
|
+
detail: 'IER4001: La ruta solicitada no se encuentra registrada'
|
7
|
+
IER4002_unauthorized:
|
8
|
+
code: 'IER4002'
|
9
|
+
title: 'No tienes permisos validos'
|
10
|
+
detail: 'IER4002: Parece que no tienes los permisos/credenciales correctos'
|
11
|
+
IER4003_bad_request:
|
12
|
+
code: 'IER4003'
|
13
|
+
title: 'El servidor no puede o no procesará la solicitud debido a un error aparente del cliente'
|
14
|
+
detail: 'IER4003: Su solicitud probablemente tenga alguna sintaxis con formato incorrecto, un encabezado incorrecto u otra cosa que no sepamos cómo procesar'
|
15
|
+
IER4004_conflict:
|
16
|
+
code: 'IER4004'
|
17
|
+
title: 'Hay un conflicto'
|
18
|
+
detail: 'IER4004: Su solicitud parece chocar con el estado del servidor'
|
19
|
+
IER4005_forbidden:
|
20
|
+
code: 'IER4005'
|
21
|
+
title: 'Tienes prohibido hacer esto.'
|
22
|
+
detail: 'IER4005: Parece que intentaste hacer algo que no debías hacer'
|
23
|
+
IER4006_invalid_resource:
|
24
|
+
code: 'IER4006'
|
25
|
+
title: 'Errores al procesar las validaciones.'
|
26
|
+
detail: 'IER4006: La información que ha enviado no es válida'
|
27
|
+
IER4007_missing_parameter:
|
28
|
+
code: 'IER4007'
|
29
|
+
title: 'Hacen faltan parámetros.'
|
30
|
+
detail: 'IER4007: Hacen faltan parámetros a la información suministrada'
|
31
|
+
IER4008_not_unique:
|
32
|
+
code: 'IER4008'
|
33
|
+
title: 'Hacen falta parámetros.'
|
34
|
+
detail: 'IER4008: Has violado una restricción de unicidad'
|
35
|
+
IER4009_payment_required:
|
36
|
+
code: 'IER4009'
|
37
|
+
title: 'Necesitas hacer un pago para que esta acción funcione.'
|
38
|
+
detail: 'IER4009: El servidor requiere que se realice un pago antes de continuar.'
|
39
|
+
IER4010_precondition_failed:
|
40
|
+
code: 'IER4010'
|
41
|
+
title: 'No se cumplió una condición previa.'
|
42
|
+
detail: 'IER4010: El servidor no cumple con una de las condiciones previas que el solicitante coloca en la solicitud.'
|
43
|
+
IER4011_resource_not_found:
|
44
|
+
code: 'IER4011'
|
45
|
+
title: 'Recurso no encontrado'
|
46
|
+
detail: 'IER4011: El recurso solicitado no se encuentra registrado'
|
47
|
+
IER4222_unprocessable_entity:
|
48
|
+
code: 'IER4222'
|
49
|
+
title: 'Incapaz de procesar la petición'
|
50
|
+
detail: 'IER4222: No se puede procesar la petición'
|
51
|
+
IER5000_internal_server_error:
|
52
|
+
code: 'IER5000'
|
53
|
+
title: 'Server Error.'
|
54
|
+
detail: 'IER5000: Su solicitud parece haber causado un error de servidor'
|
55
|
+
IER5030_maintenance_mode:
|
56
|
+
code: 'IER5030'
|
57
|
+
title: 'El servidor está en mantenimiento'
|
58
|
+
detail: 'IER5030: Mantenimiento en proceso'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Commons
|
2
|
+
module Controllers
|
3
|
+
module SchemaValidable
|
4
|
+
#
|
5
|
+
# Método que valida datos en base a un Schema
|
6
|
+
#
|
7
|
+
# @param [Hash] request_data datos a validar
|
8
|
+
# @param [Dry::Validation::Contract] schema Schema de validación
|
9
|
+
#
|
10
|
+
# @return [Hash]
|
11
|
+
#
|
12
|
+
# @raise [Commons::Errors::UnprocessableEntity]
|
13
|
+
#
|
14
|
+
def validate_request(request_data, schema)
|
15
|
+
validated_params = schema.call(request_data)
|
16
|
+
|
17
|
+
if validated_params.failure?
|
18
|
+
raise Commons::Errors::UnprocessableEntity.new(nil, nil,
|
19
|
+
meta: { validation_errors: validated_params.errors.to_h }
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
validated_params.to_h
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Commons
|
2
|
+
module Errors
|
3
|
+
class BadRequest < ErrorBase
|
4
|
+
def initialize(message = nil, backtrace = nil, title: nil, code: nil, detail: nil, meta: {})
|
5
|
+
super message,
|
6
|
+
backtrace,
|
7
|
+
status: :bad_request,
|
8
|
+
title: title || I18n.t('status_code.IER4003_bad_request.title'),
|
9
|
+
code: code || I18n.t('status_code.IER4003_bad_request.code'),
|
10
|
+
detail: detail || I18n.t('status_code.IER4003_bad_request.detail'),
|
11
|
+
meta: meta
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Commons
|
2
|
+
module Errors
|
3
|
+
class Conflict < ErrorBase
|
4
|
+
def initialize(message = nil, backtrace = nil, title: nil, code: nil, detail: nil, meta: {})
|
5
|
+
super message,
|
6
|
+
backtrace,
|
7
|
+
status: :conflict,
|
8
|
+
title: title || I18n.t('status_code.IER4004_conflict.title'),
|
9
|
+
code: code || I18n.t('status_code.IER4004_conflict.code'),
|
10
|
+
detail: detail || I18n.t('status_code.IER4004_conflict.detail'),
|
11
|
+
meta: meta
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Commons
|
2
|
+
module Errors
|
3
|
+
module DefaultHandling
|
4
|
+
def self.included(cls)
|
5
|
+
cls.class_eval do
|
6
|
+
# WARNING
|
7
|
+
# Avoid setting rescue_from Exception/StandardError since it will override your
|
8
|
+
# own custom rescue_from handlers https://stackoverflow.com/a/9121054/3287738
|
9
|
+
# WARNING
|
10
|
+
rescue_from ActiveRecord::RecordNotFound do |e|
|
11
|
+
respond ResourceNotFound.new e.message,
|
12
|
+
meta: {
|
13
|
+
model: e.model,
|
14
|
+
id: e.id,
|
15
|
+
primary_key: e.primary_key
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
rescue_from ActiveRecord::RecordInvalid do |e|
|
20
|
+
respond InvalidResource.new e.message,
|
21
|
+
validation_errors: e&.record&.errors&.to_hash
|
22
|
+
end
|
23
|
+
|
24
|
+
rescue_from ArgumentError do |e|
|
25
|
+
respond InvalidResource.new e.message
|
26
|
+
end
|
27
|
+
|
28
|
+
rescue_from ActiveRecord::RecordNotUnique do |e|
|
29
|
+
respond NotUnique.new e.message
|
30
|
+
end
|
31
|
+
|
32
|
+
rescue_from ActionController::ParameterMissing do |e|
|
33
|
+
respond MissingParameter.new e.message, param: e.param
|
34
|
+
end
|
35
|
+
|
36
|
+
rescue_from ActionController::RoutingError do |e|
|
37
|
+
respond RouteNotFound.new e.message,
|
38
|
+
detail: { failures: e.failures }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|