endpoint-flux2 1.1.6

Sign up to get free protection for your applications and to get access to all the features.
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,158 @@
1
+ module ActiveSupport
2
+ module Concern; end
3
+ end
4
+
5
+ module Endpoints
6
+ module Test; end
7
+ end
8
+
9
+ class String
10
+ def camelize(uppercase_first_letter = true)
11
+ string = self
12
+ if uppercase_first_letter
13
+ string = string.sub(/^[a-z\d]*/) { |match| match.capitalize }
14
+ else
15
+ string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
16
+ end
17
+ string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub("/", "::")
18
+ end
19
+
20
+ def constantize(camel_cased_word =nil)
21
+ camel_cased_word ||= self
22
+ names = camel_cased_word.split("::".freeze)
23
+
24
+ # Trigger a built-in NameError exception including the ill-formed constant in the message.
25
+ Object.const_get(camel_cased_word) if names.empty?
26
+
27
+ # Remove the first blank element in case of '::ClassName' notation.
28
+ names.shift if names.size > 1 && names.first.empty?
29
+
30
+ names.inject(Object) do |constant, name|
31
+ if constant == Object
32
+ constant.const_get(name)
33
+ else
34
+ candidate = constant.const_get(name)
35
+ next candidate if constant.const_defined?(name, false)
36
+ next candidate unless Object.const_defined?(name)
37
+
38
+ # Go down the ancestors to check if it is owned directly. The check
39
+ # stops when we reach Object or the end of ancestors tree.
40
+ constant = constant.ancestors.inject(constant) do |const, ancestor|
41
+ break const if ancestor == Object
42
+ break ancestor if ancestor.const_defined?(name, false)
43
+ const
44
+ end
45
+
46
+ # owner is in Object, so raise
47
+ constant.const_get(name, false)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ require 'spec_helper'
54
+ require 'endpoint_flux/rails/concerns/endpoint_controller'
55
+ require 'ostruct'
56
+
57
+ class RemoteIpDummyClass
58
+ include EndpointFlux::Rails::Concerns::EndpointController
59
+
60
+ def headers
61
+ {}
62
+ end
63
+
64
+ def request
65
+ OpenStruct.new(
66
+ headers: { 'authorization' => 'authorization header' },
67
+ remote_ip: '172.18.0.1',
68
+ env: { 'HTTP_X_FORWARDED_FOR' => '172.18.0.2' }
69
+ )
70
+ end
71
+ end
72
+
73
+ class LocalIpDummyClass
74
+ include EndpointFlux::Rails::Concerns::EndpointController
75
+
76
+ def headers
77
+ {}
78
+ end
79
+
80
+ def request
81
+ OpenStruct.new(
82
+ headers: { 'authorization' => 'authorization header' },
83
+ remote_ip: '127.0.0.1',
84
+ env: { 'HTTP_X_FORWARDED_FOR' => '172.18.0.2' }
85
+ )
86
+ end
87
+
88
+ def params; end
89
+
90
+ end
91
+
92
+ RSpec.describe EndpointFlux::Rails::Concerns::EndpointController do
93
+ describe '#request_object' do
94
+ before do
95
+ allow(subject).to receive(:endpoint_params).and_return({})
96
+ end
97
+
98
+ context 'remote ip' do
99
+ subject { RemoteIpDummyClass.new }
100
+
101
+ it 'sets property as remote_ip' do
102
+ expect(
103
+ ::EndpointFlux::Request
104
+ ).to receive(:new).with(headers: { 'Authorization' => 'authorization header' },
105
+ params: subject.endpoint_params,
106
+ remote_ip: subject.request.remote_ip)
107
+
108
+ subject.request_object
109
+ end
110
+ end
111
+
112
+ context 'local ip' do
113
+ subject { LocalIpDummyClass.new }
114
+
115
+ it 'sets property as HTTP_X_FORWARDED_FOR' do
116
+ expect(
117
+ ::EndpointFlux::Request
118
+ ).to receive(:new).with(headers: { 'Authorization' => 'authorization header' },
119
+ params: subject.endpoint_params,
120
+ remote_ip: subject.request.env['HTTP_X_FORWARDED_FOR'])
121
+
122
+ subject.request_object
123
+ end
124
+ end
125
+
126
+ end
127
+
128
+ describe '#endpoint_params' do
129
+ before do
130
+ allow(subject).to receive_message_chain(:params, :permit!, :except, :to_h, :deep_symbolize_keys)
131
+ .and_return({test: 'test'})
132
+ end
133
+
134
+ context 'request' do
135
+ subject { LocalIpDummyClass.new }
136
+
137
+ it 'permits and excepts params' do
138
+ expect(subject.endpoint_params).to match({test: 'test'})
139
+ end
140
+ end
141
+
142
+ end
143
+
144
+ describe '#endpoint_for' do
145
+ context 'with namespace' do
146
+ subject { LocalIpDummyClass.new }
147
+ it 'return namespace' do
148
+ expect(subject.endpoint_for('test')).to eq(Endpoints::Test)
149
+ end
150
+ end
151
+
152
+ end
153
+
154
+ describe '#present' do
155
+
156
+ end
157
+
158
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe EndpointFlux::Request do
4
+ it { should respond_to :headers }
5
+ it { should respond_to :remote_ip }
6
+ it { should respond_to :params }
7
+ it { should respond_to :namespace }
8
+ it { should respond_to :endpoint }
9
+ it { should respond_to :scope }
10
+
11
+ describe '#initialize' do
12
+ context 'when all params specified' do
13
+ let(:params) { { param: :val } }
14
+ let(:headers) { { header: :val } }
15
+ let(:remote_ip) { '127.0.0.1' }
16
+ let(:namespace) { 'namespace' }
17
+
18
+ it 'should set args' do
19
+ request = EndpointFlux::Request.new(
20
+ params: params,
21
+ headers: headers,
22
+ remote_ip: remote_ip,
23
+ namespace: namespace
24
+ )
25
+
26
+ expect(request.params).to eq params
27
+ expect(request.headers).to eq headers
28
+ expect(request.remote_ip).to eq remote_ip
29
+ expect(request.namespace).to eq namespace
30
+ end
31
+
32
+ context 'when params missed' do
33
+ it 'should set default values' do
34
+ request = EndpointFlux::Request.new
35
+
36
+ expect(request.params).to be_empty
37
+ expect(request.headers).to be_empty
38
+ expect(request.remote_ip).to eq ''
39
+ expect(request.namespace).to be_nil
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe EndpointFlux::Exceptions::Forbidden do
4
+ describe '#to_hash' do
5
+ it 'returns correct hash' do
6
+ expect(subject.to_hash).to eq(
7
+ status: 403,
8
+ message: 'Forbidden'
9
+ )
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe EndpointFlux::Exceptions::NotFound do
4
+ describe '#to_hash' do
5
+ it 'returns correct hash' do
6
+ expect(subject.to_hash).to eq(
7
+ status: 404,
8
+ message: 'not found'
9
+ )
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe EndpointFlux::Exceptions::ServiceUnavailable do
4
+ describe '#to_hash' do
5
+ it 'returns correct hash' do
6
+ expect(subject.to_hash).to eq(
7
+ status: 503,
8
+ message: 'service unavailable'
9
+ )
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe EndpointFlux::Exceptions::Unauthorized do
4
+ describe '#to_hash' do
5
+ it 'returns correct hash' do
6
+ expect(subject.to_hash).to eq(
7
+ status: 401,
8
+ message: 'Unauthorized'
9
+ )
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe EndpointFlux::Exceptions::Validation do
4
+ describe '#to_hash' do
5
+ let(:messages) { {name: 'not valid'} }
6
+ it 'returns correct hash' do
7
+ expect(EndpointFlux::Exceptions::Validation.new(messages).to_hash).to eq(
8
+ status: 422,
9
+ message: 'validation errors',
10
+ errors: messages
11
+ )
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ require_relative '../../middlewares/shared_examples'
2
+
3
+ RSpec.describe EndpointFlux::Middlewares::Authenticator::Skip do
4
+ include_examples 'perform does not change the params'
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative '../../middlewares/shared_examples'
2
+
3
+ RSpec.describe EndpointFlux::Middlewares::Authorizator::Skip do
4
+ include_examples 'perform does not change the params'
5
+ end
@@ -0,0 +1,17 @@
1
+ RSpec.describe EndpointFlux::Middlewares::Decorator::AddStatus do
2
+ describe '#perform' do
3
+ let(:params) { { some: 'value' } }
4
+ let(:body) { { status: 200 } }
5
+ let(:request) { EndpointFlux::Request.new(headers: {}, params: params) }
6
+ let(:response) { EndpointFlux::Response.new(headers: {}, body: body) }
7
+
8
+ it 'returns response with status in body' do
9
+ middleware_request, middleware_response = subject.perform(request, response, {})
10
+
11
+ expect(middleware_request.headers).to eq({})
12
+ expect(middleware_request.params).to eq(params)
13
+ expect(middleware_response.headers).to eq({})
14
+ expect(middleware_response.body).to eq(body)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ require_relative '../../middlewares/shared_examples'
2
+
3
+ RSpec.describe EndpointFlux::Middlewares::Decorator::Skip do
4
+ include_examples 'perform does not change the params'
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative '../../middlewares/shared_examples'
2
+
3
+ RSpec.describe EndpointFlux::Middlewares::Policy::Skip do
4
+ include_examples 'perform does not change the params'
5
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.shared_context 'perform does not change the params', shared_context: :metadata do
4
+ describe '#perform' do
5
+ let(:params) { { some: 'value' } }
6
+ let(:headers) { { 'Authorization' => 'valid' } }
7
+ let(:request) { EndpointFlux::Request.new(headers: headers, params: params) }
8
+ let(:response) { EndpointFlux::Response.new(headers: {}, body: {}) }
9
+
10
+ it 'returns not changed params' do
11
+ middleware_request, middleware_response = subject.perform(request, response, {})
12
+
13
+ expect(middleware_request.params).to eq(params)
14
+ expect(middleware_request.headers).to eq(headers)
15
+ expect(middleware_response.headers).to eq({})
16
+ expect(middleware_response.body).to eq({})
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ RSpec.describe EndpointFlux::Middlewares::Validator::Empty do
2
+ describe '#perform' do
3
+ let(:params) { { some: 'value' } }
4
+ let(:request) { EndpointFlux::Request.new(headers: {}, params: params) }
5
+ let(:response) { EndpointFlux::Response.new }
6
+
7
+ it 'returns empty params' do
8
+ middleware_request, middleware_response = subject.perform(request, response, {})
9
+
10
+ expect(middleware_request.params).to eq({})
11
+ expect(middleware_response.headers).to eq({})
12
+ expect(middleware_response.body).to eq({})
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,131 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe EndpointFlux::Response do
4
+ subject { EndpointFlux::Response.new(params) }
5
+ let(:empty_subject) { EndpointFlux::Response.new() }
6
+
7
+ describe '#success?' do
8
+ context 'when valid params given' do
9
+ Array(200..209).each do |status|
10
+ let(:params) { { headers: {}, body: {status: status} } }
11
+ it "returns true for status #{status}" do
12
+ expect(subject.success?).to be_truthy
13
+ end
14
+ end
15
+ end
16
+
17
+ context 'when wrong params given' do
18
+ let(:status) { 400 }
19
+ let(:params) { { headers: {}, body: {status: status} } }
20
+ it 'returns false for not a success status' do
21
+ expect(subject.success?).to be_falsey
22
+ end
23
+
24
+ describe 'when Response initialized without params' do
25
+ it 'returns false for not a success status' do
26
+ expect(empty_subject.success?).to be_falsey
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ describe '#invalid?' do
33
+ let(:params) { { headers: {}, body: {status: status} } }
34
+ context 'when valid params given' do
35
+ let(:status) { 422 }
36
+
37
+ it 'returns true for status 422' do
38
+ expect(subject.invalid?).to be_truthy
39
+ end
40
+ end
41
+
42
+ context 'when wrong params given' do
43
+ let(:status) { 400 }
44
+
45
+ it 'returns false for not an invalid status' do
46
+ expect(subject.invalid?).to be_falsey
47
+ end
48
+
49
+ describe 'when Response initialized without params' do
50
+ it 'returns false for not an invalid status' do
51
+ expect(empty_subject.invalid?).to be_falsey
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ describe '#forbidden?' do
58
+ let(:params) { { headers: {}, body: {status: status} } }
59
+ context 'when valid params given' do
60
+ let(:status) { 403 }
61
+
62
+ it 'returns true for status 403' do
63
+ expect(subject.forbidden?).to be_truthy
64
+ end
65
+ end
66
+
67
+ context 'when wrong params given' do
68
+ let(:status) { 400 }
69
+
70
+ it 'returns false for not a forbidden status' do
71
+ expect(subject.forbidden?).to be_falsey
72
+ end
73
+
74
+ describe 'when Response initialized without params' do
75
+ it 'returns false for not a forbidden status' do
76
+ expect(empty_subject.forbidden?).to be_falsey
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ describe '#unauthorized?' do
83
+ let(:params) { { headers: {}, body: {status: status} } }
84
+ context 'when valid params given' do
85
+ let(:status) { 401 }
86
+
87
+ it 'returns true for status 401' do
88
+ expect(subject.unauthorized?).to be_truthy
89
+ end
90
+ end
91
+
92
+ context 'when wrong params given' do
93
+ let(:status) { 400 }
94
+
95
+ it 'returns false for not a unauthorized status' do
96
+ expect(subject.unauthorized?).to be_falsey
97
+ end
98
+
99
+ describe 'when Response initialized without params' do
100
+ it 'returns false for not a unauthorized status' do
101
+ expect(empty_subject.unauthorized?).to be_falsey
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ describe '#unauthorized?' do
108
+ let(:params) { { headers: {}, body: {status: status} } }
109
+ context 'when valid params given' do
110
+ let(:status) { 404 }
111
+
112
+ it 'returns true for status 404' do
113
+ expect(subject.not_found?).to be_truthy
114
+ end
115
+ end
116
+
117
+ context 'when wrong params given' do
118
+ let(:status) { 400 }
119
+
120
+ it 'returns false for not a not_found status' do
121
+ expect(subject.not_found?).to be_falsey
122
+ end
123
+
124
+ describe 'when Response initialized without params' do
125
+ it 'returns false for not a not_found status' do
126
+ expect(empty_subject.not_found?).to be_falsey
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end