commons_yellowme 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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