commons_yellowme 0.11.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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +17 -0
  5. data/lib/commons.rb +67 -0
  6. data/lib/commons/authentication/authenticate_by_jwt.rb +27 -0
  7. data/lib/commons/authentication/json_web_token.rb +17 -0
  8. data/lib/commons/concerns/attributes/sex.rb +19 -0
  9. data/lib/commons/concerns/extensions/deleted.rb +25 -0
  10. data/lib/commons/concerns/guard/capitalizable.rb +32 -0
  11. data/lib/commons/concerns/validations/undestroyable.rb +18 -0
  12. data/lib/commons/config.rb +11 -0
  13. data/lib/commons/config/locales/en.yml +1 -0
  14. data/lib/commons/config/locales/es.yml +58 -0
  15. data/lib/commons/controllers/schema_validable.rb +27 -0
  16. data/lib/commons/errors/bad_request.rb +15 -0
  17. data/lib/commons/errors/conflict.rb +15 -0
  18. data/lib/commons/errors/default_handling.rb +44 -0
  19. data/lib/commons/errors/error_base.rb +64 -0
  20. data/lib/commons/errors/error_serializer.rb +10 -0
  21. data/lib/commons/errors/forbidden.rb +15 -0
  22. data/lib/commons/errors/internal_server_error.rb +15 -0
  23. data/lib/commons/errors/invalid_resource.rb +21 -0
  24. data/lib/commons/errors/maintenance_mode.rb +14 -0
  25. data/lib/commons/errors/missing_parameter.rb +13 -0
  26. data/lib/commons/errors/not_unique.rb +14 -0
  27. data/lib/commons/errors/payment_required.rb +15 -0
  28. data/lib/commons/errors/precondition_failed.rb +15 -0
  29. data/lib/commons/errors/resource_not_found.rb +14 -0
  30. data/lib/commons/errors/route_not_found.rb +14 -0
  31. data/lib/commons/errors/unauthorized.rb +15 -0
  32. data/lib/commons/errors/unprocessable_entity.rb +15 -0
  33. data/lib/commons/formatter/e164_phone.rb +47 -0
  34. data/lib/commons/formatter/null_attributes_remover.rb +9 -0
  35. data/lib/commons/formatter/regex_constants.rb +9 -0
  36. data/lib/commons/formatter/string_utils.rb +9 -0
  37. data/lib/commons/middleware/catch_json_parse_errors.rb +30 -0
  38. data/lib/commons/railtie.rb +4 -0
  39. data/lib/commons/repositories/base_repository.rb +227 -0
  40. data/lib/commons/repositories/catalogs/base_catalog.rb +68 -0
  41. data/lib/commons/repositories/catalogs/concerns/model_caching_extention.rb +27 -0
  42. data/lib/commons/services/concerns/callable.rb +15 -0
  43. data/lib/commons/version.rb +3 -0
  44. data/lib/tasks/commons_tasks.rake +4 -0
  45. metadata +254 -0
@@ -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
@@ -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.
@@ -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).
@@ -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'
@@ -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,11 @@
1
+ module Commons
2
+ class << self
3
+ def secret_key_base=(secret_key_base)
4
+ @secret_key_base = secret_key_base
5
+ end
6
+
7
+ def secret_key_base
8
+ @secret_key_base || ENV["secret_key_base"]
9
+ end
10
+ end
11
+ end
@@ -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