endpoint-flux2 1.1.6

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 (93) hide show
  1. checksums.yaml +7 -0
  2. data/.github/FUNDING.yml +3 -0
  3. data/.github/workflows/gem-push.yml +42 -0
  4. data/.gitignore +36 -0
  5. data/CONTRIBUTING.md +18 -0
  6. data/Gemfile +6 -0
  7. data/Gemfile.lock +49 -0
  8. data/README.md +634 -0
  9. data/Rakefile +1 -0
  10. data/_config.yml +1 -0
  11. data/circle.yml +14 -0
  12. data/endpoint_flux.gemspec +20 -0
  13. data/lib/endpoint_flux.rb +22 -0
  14. data/lib/endpoint_flux/class_loader.rb +58 -0
  15. data/lib/endpoint_flux/config.rb +85 -0
  16. data/lib/endpoint_flux/config/interceptor.rb +23 -0
  17. data/lib/endpoint_flux/config/middleware.rb +38 -0
  18. data/lib/endpoint_flux/config/rescue_from.rb +28 -0
  19. data/lib/endpoint_flux/endpoint.rb +81 -0
  20. data/lib/endpoint_flux/exceptions.rb +10 -0
  21. data/lib/endpoint_flux/exceptions/base.rb +21 -0
  22. data/lib/endpoint_flux/exceptions/forbidden.rb +12 -0
  23. data/lib/endpoint_flux/exceptions/not_found.rb +12 -0
  24. data/lib/endpoint_flux/exceptions/service_unavailable.rb +12 -0
  25. data/lib/endpoint_flux/exceptions/unauthorized.rb +12 -0
  26. data/lib/endpoint_flux/exceptions/validation.rb +13 -0
  27. data/lib/endpoint_flux/middlewares.rb +8 -0
  28. data/lib/endpoint_flux/middlewares/authenticator/skip.rb +11 -0
  29. data/lib/endpoint_flux/middlewares/authorizator/skip.rb +11 -0
  30. data/lib/endpoint_flux/middlewares/decorator/add_status.rb +12 -0
  31. data/lib/endpoint_flux/middlewares/decorator/skip.rb +11 -0
  32. data/lib/endpoint_flux/middlewares/policy/skip.rb +11 -0
  33. data/lib/endpoint_flux/middlewares/validator/empty.rb +12 -0
  34. data/lib/endpoint_flux/rails/concerns/endpoint_controller.rb +43 -0
  35. data/lib/endpoint_flux/railtie.rb +14 -0
  36. data/lib/endpoint_flux/request.rb +17 -0
  37. data/lib/endpoint_flux/response.rb +30 -0
  38. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/decorators/articles/base.rb +12 -0
  39. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/decorators/boards/base.rb +12 -0
  40. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/decorators/boards/show.rb +22 -0
  41. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/decorators/tasks/base.rb +12 -0
  42. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/endpoints/articles/create.rb +27 -0
  43. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/endpoints/articles/destroy.rb +23 -0
  44. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/endpoints/articles/index.rb +26 -0
  45. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/endpoints/boards/create.rb +24 -0
  46. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/endpoints/boards/index.rb +21 -0
  47. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/endpoints/boards/show.rb +23 -0
  48. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/endpoints/tasks/create.rb +27 -0
  49. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/endpoints/tasks/destroy.rb +23 -0
  50. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/endpoints/tasks/index.rb +25 -0
  51. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/endpoints/tasks/update.rb +28 -0
  52. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/middlewares/decorator/paginate.rb +19 -0
  53. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/middlewares/decorator/representable.rb +24 -0
  54. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/middlewares/validator/inline.rb +17 -0
  55. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/validations/base.rb +21 -0
  56. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/validations/concern/error.rb +7 -0
  57. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/validations/predicates/base.rb +12 -0
  58. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/validations/predicates/bool.rb +20 -0
  59. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/validations/predicates/dates.rb +34 -0
  60. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/validations/predicates/decimal.rb +24 -0
  61. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/validations/predicates/email.rb +18 -0
  62. data/lib/endpoint_flux/tasks/endpoint_flux/generators/endpoint_flux/validations/predicates/password.rb +29 -0
  63. data/lib/endpoint_flux/tasks/endpoint_flux/generators/initializers/endpoint_flux.rb +41 -0
  64. data/lib/endpoint_flux/tasks/endpoint_flux/init.rake +12 -0
  65. data/lib/endpoint_flux/version.rb +3 -0
  66. data/spec/lib/class_loader_spec.rb +31 -0
  67. data/spec/lib/config/default_middlewares_spec.rb +21 -0
  68. data/spec/lib/config/endpoints_namespace_spec.rb +13 -0
  69. data/spec/lib/config/flow_spec.rb +8 -0
  70. data/spec/lib/config/interceptor_spec.rb +34 -0
  71. data/spec/lib/config/middleware_spec.rb +62 -0
  72. data/spec/lib/config/rescue_from_spec.rb +45 -0
  73. data/spec/lib/endpoint/flow_spec.rb +43 -0
  74. data/spec/lib/endpoint/middlewares_spec.rb +110 -0
  75. data/spec/lib/endpoint/perform_spec.rb +61 -0
  76. data/spec/lib/endpoint/rescue_from_spec.rb +61 -0
  77. data/spec/lib/endpoint_flux/rails/concerns/endpoint_controller_spec.rb +158 -0
  78. data/spec/lib/endpoint_flux/request_spec.rb +44 -0
  79. data/spec/lib/exceptions/forbidden_spec.rb +12 -0
  80. data/spec/lib/exceptions/not_found_spec.rb +12 -0
  81. data/spec/lib/exceptions/service_unavailable_spec.rb +12 -0
  82. data/spec/lib/exceptions/unauthorized_spec.rb +12 -0
  83. data/spec/lib/exceptions/validation_spec.rb +14 -0
  84. data/spec/lib/middlewares/authenticator/skip_spec.rb +5 -0
  85. data/spec/lib/middlewares/authorizator/skip_spec.rb +5 -0
  86. data/spec/lib/middlewares/decorator/add_status_spec.rb +17 -0
  87. data/spec/lib/middlewares/decorator/skip_spec.rb +5 -0
  88. data/spec/lib/middlewares/policy/skip_spec.rb +5 -0
  89. data/spec/lib/middlewares/shared_examples.rb +19 -0
  90. data/spec/lib/middlewares/validator/empty_spec.rb +15 -0
  91. data/spec/lib/response_spec.rb +131 -0
  92. data/spec/spec_helper.rb +56 -0
  93. metadata +203 -0
@@ -0,0 +1,27 @@
1
+ module Endpoints
2
+ module Tasks
3
+ module Create
4
+ include EndpointFlux::Endpoint
5
+
6
+ validator :inline do
7
+ required(:title).value(:str?)
8
+ required(:article_id).value(:number?)
9
+ end
10
+
11
+ process do |request, response|
12
+ task = Task.create!(
13
+ title: request.params[:title],
14
+ article_id: request.params[:article_id]
15
+ )
16
+
17
+ response.body[:task] = task
18
+
19
+ [request, response]
20
+ end
21
+
22
+ decorator :add_status, 201
23
+ decorator :representable,
24
+ decorator: 'Tasks::Base', wrapped_in: :task
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ module Endpoints
2
+ module Tasks
3
+ module Destroy
4
+ include EndpointFlux::Endpoint
5
+
6
+ validator :inline do
7
+ required(:id).value(:number?)
8
+ end
9
+
10
+ process do |request, response|
11
+ task = Task.find(request.params[:id]).destroy
12
+
13
+ response.body[:task] = task
14
+
15
+ [request, response]
16
+ end
17
+
18
+ decorator :add_status, 202
19
+ decorator :representable,
20
+ decorator: 'Tasks::Base', wrapped_in: :task
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ module Endpoints
2
+ module Tasks
3
+ module Index
4
+ include EndpointFlux::Endpoint
5
+
6
+ authorizator :skip
7
+
8
+ validator :inline do
9
+ required(:article_id).value(:number?)
10
+ end
11
+
12
+ process do |request, response|
13
+
14
+ response.body[:tasks] = Task.where(article_id: request.params[:article_id])
15
+
16
+ [request, response]
17
+ end
18
+
19
+ decorator :add_status, 200
20
+ decorator :representable, decorator: 'Tasks::Base',
21
+ collection?: true,
22
+ wrapped_in: :tasks
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ module Endpoints
2
+ module Tasks
3
+ module Update
4
+ include EndpointFlux::Endpoint
5
+
6
+ validator :inline do
7
+ required(:id).value(:number?)
8
+ required(:title).value(:str?)
9
+ required(:article_id).value(:number?)
10
+ end
11
+
12
+ process do |request, response|
13
+ Task.find(request.params[:id]).update(
14
+ title: request.params[:title],
15
+ article_id: request.params[:article_id]
16
+ )
17
+
18
+ response.body[:task] = Task.find(request.params[:id])
19
+
20
+ [request, response]
21
+ end
22
+
23
+ decorator :add_status, 201
24
+ decorator :representable,
25
+ decorator: 'Tasks::Base', wrapped_in: :task
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ module Middlewares
2
+ module Decorator
3
+ module Paginate
4
+ def self.perform(request, response, options)
5
+ page, per_page = request.params.values_at(:page, :per_page)
6
+ resources = response.body[options[:wrapped_in]]
7
+
8
+ if per_page != 'all' && resources
9
+ resources = resources.page(page).per(per_page)
10
+
11
+ response.body[options[:wrapped_in]] = resources
12
+ response.body[:pagination] = { total_pages: resources.total_pages }
13
+ end
14
+
15
+ [request, response]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ module Middlewares
2
+ module Decorator
3
+ module Representable
4
+ def self.perform(request, response, options)
5
+ resource_name = options[:wrapped_in] || options[:decorator]
6
+ resource = response.body[resource_name]
7
+
8
+ response.body[resource_name] = decorate(resource, options) if resource
9
+
10
+ [request, response]
11
+ end
12
+
13
+ def self.decorate(object, options)
14
+ decorator = "::Decorators::#{options[:decorator].to_s.camelize}".constantize
15
+
16
+ if options[:collection?]
17
+ decorator.for_collection.new(object.to_a).to_hash
18
+ else
19
+ decorator.new(object).to_hash
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ module Middlewares
2
+ module Validator
3
+ module Inline
4
+ def self.perform(request, response, _options, &block)
5
+ raise 'InlineValidator requires block with validations' unless block_given?
6
+
7
+ validation = ::Dry::Validation.Schema(::Validations::Base, &block).call(request.params)
8
+ unless validation.success?
9
+ raise EndpointFlux::Exceptions::Validation, validation.messages
10
+ end
11
+ request.params = validation.output
12
+
13
+ [request, response]
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ require 'dry-validation'
2
+ require 'dry-types'
3
+
4
+ module Validations
5
+ module Types
6
+ include Dry::Types.module
7
+ end
8
+
9
+ class Base < Dry::Validation::Schema
10
+ configure do
11
+ predicates(Validations::Predicates::Base)
12
+
13
+ config.input_processor = :sanitizer
14
+ config.messages_file = 'config/locales/en/validation-errors.yml'
15
+ end
16
+ define! do
17
+ optional(:page).value(:number?)
18
+ optional(:per_page) { number? | eql?('all') }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ module Validations
2
+ module Error
3
+ def raise_validation_error(errors)
4
+ raise EndpointFlux::Exceptions::Validation, errors
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module Validations
2
+ module Predicates
3
+ module Base
4
+ include Dry::Logic::Predicates
5
+ include Validations::Predicates::Password
6
+ include Validations::Predicates::Email
7
+ include Validations::Predicates::Bool
8
+ include Validations::Predicates::Dates
9
+ include Validations::Predicates::Decimal
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ module Validations
2
+ module Predicates
3
+ module Bool
4
+ module Methods
5
+ def bool?(value)
6
+ ![true, false, 'true', 'false', '0', '1'].find do |item|
7
+ value == item
8
+ end.nil?
9
+ end
10
+ end
11
+
12
+ extend Methods
13
+
14
+ def self.included(other)
15
+ super
16
+ other.extend(Methods)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,34 @@
1
+ module Validations
2
+ module Predicates
3
+ module Dates
4
+ module Methods
5
+ def in_past?(value)
6
+ value && ::Rich::Date.safe_parse(value).try(:past?)
7
+ end
8
+
9
+ def in_future?(value)
10
+ !self[:in_past?].call(value)
11
+ end
12
+
13
+ def earlier_then?(v1, v2)
14
+ v1 && v2 && ::Rich::Date.safe_parse(v1) > ::Rich::Date.safe_parse(v2)
15
+ end
16
+
17
+ def on_or_later_then?(v1, v2)
18
+ !self[:earlier_then?].call(v1, v2)
19
+ end
20
+
21
+ def parsed_as_date?(value)
22
+ value.nil? || !Validations::Types::Form::DateTime[value].is_a?(String)
23
+ end
24
+ end
25
+
26
+ extend Methods
27
+
28
+ def self.included(other)
29
+ super
30
+ other.extend(Methods)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ require 'bigdecimal'
2
+
3
+ module Validations
4
+ module Predicates
5
+ module Decimal
6
+ module Methods
7
+ def decimal?(input)
8
+ begin
9
+ true if BigDecimal(input)
10
+ rescue ArgumentError, TypeError
11
+ false
12
+ end
13
+ end
14
+ end
15
+
16
+ extend Methods
17
+
18
+ def self.included(other)
19
+ super
20
+ other.extend(Methods)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ module Validations
2
+ module Predicates
3
+ module Email
4
+ module Methods
5
+ def email?(value)
6
+ !%r{\A((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))}i.match(value).nil? # rubocop:disable LineLength
7
+ end
8
+ end
9
+
10
+ extend Methods
11
+
12
+ def self.included(other)
13
+ super
14
+ other.extend(Methods)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ module Validations
2
+ module Predicates
3
+ module Password
4
+ module Methods
5
+ def password?(value)
6
+ rules = [
7
+ %r{[A-Z]}, # at least 1 uppercase character (A-Z)
8
+ %r{[a-z]}, # at least 1 lowercase character (a-z)
9
+ %r{\d}, # at least 1 digit (0-9)
10
+ %r{\W} # at least 1 special character (punctuation)
11
+ ]
12
+
13
+ matches = rules.inject(0) do |n, rule|
14
+ value.match(rule) ? n + 1 : n
15
+ end
16
+
17
+ matches >= 3
18
+ end
19
+ end
20
+
21
+ extend Methods
22
+
23
+ def self.included(other)
24
+ super
25
+ other.extend(Methods)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,41 @@
1
+ require 'endpoint_flux'
2
+
3
+ EndpointFlux.config.middlewares_namespaces << 'middlewares'
4
+
5
+ EndpointFlux.config.default_middlewares :authenticator, :skip
6
+ EndpointFlux.config.default_middlewares :authorizator, :skip
7
+ EndpointFlux.config.default_middlewares :validator, :empty
8
+ EndpointFlux.config.default_middlewares :policy, :skip
9
+ EndpointFlux.config.default_middlewares :decorator, :skip
10
+
11
+
12
+ EndpointFlux::Endpoint.class_eval do
13
+ define_method(:raise_validation_error) do |errors|
14
+ raise EndpointFlux::Exceptions::Validation, errors
15
+ end
16
+ end
17
+
18
+ rescue_from_exceptions = [
19
+ EndpointFlux::Exceptions::Validation,
20
+ EndpointFlux::Exceptions::Forbidden,
21
+ EndpointFlux::Exceptions::Unauthorized,
22
+ EndpointFlux::Exceptions::NotFound,
23
+ EndpointFlux::Exceptions::ServiceUnavailable
24
+ ]
25
+
26
+ EndpointFlux.config.rescue_from(rescue_from_exceptions) do |_, attrs, exception|
27
+ attrs[1].body.merge!(exception.to_hash)
28
+ attrs
29
+ end
30
+
31
+ EndpointFlux::Request.class_eval do
32
+ attr_accessor :current_user_params
33
+
34
+ define_method(:current_user) do
35
+ @current_user ||= begin
36
+ if current_user_params && current_user_params['id']
37
+ User.find_by(id: current_user_params['id'])
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,12 @@
1
+ namespace :endpoint_flux do
2
+ desc 'Initialize project with endpoint_flux'
3
+ task :init => :environment do
4
+ require 'fileutils'
5
+ puts 'Creating files...'
6
+ path = File.expand_path(__dir__)
7
+ FileUtils.cp_r "#{path}/generators/endpoint_flux", 'app/endpoint_flux'
8
+ FileUtils.cp "#{path}/generators/initializers/endpoint_flux.rb", 'config/initializers/endpoint_flux.rb'
9
+ puts 'Finished'
10
+ end
11
+ end
12
+
@@ -0,0 +1,3 @@
1
+ module EndpointFlux
2
+ VERSION = '1.1.6'.freeze
3
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe EndpointFlux::ClassLoader do
4
+ describe '#string_to_class_name' do
5
+ it 'converts string to class name' do
6
+ expect(subject.string_to_class_name('hey/bey/cey')).to eq('Hey::Bey::Cey')
7
+ end
8
+ end
9
+
10
+ describe '#load_class!' do
11
+ context 'when wrong params given' do
12
+ it 'fails if the class is not existing' do
13
+ expect { subject.load_class!('hey/bey') }
14
+ .to raise_error('The [hey/bey] should be a string representing a class')
15
+ end
16
+
17
+ it 'fails if the class name is invalid' do
18
+ expect { subject.load_class!('111/bey') }
19
+ .to raise_error('The [111/bey] should be a string representing a class')
20
+ end
21
+ end
22
+
23
+ context 'when valid params given' do
24
+ let(:sample_class) { stub_const 'SampleClass::SecondLevel', Class.new }
25
+
26
+ it 'returns the class back' do
27
+ expect(subject.load_class!(sample_class)).to be(sample_class)
28
+ end
29
+ end
30
+ end
31
+ end