endpoint-flux 1.1.4
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.
- checksums.yaml +7 -0
- data/.gitignore +36 -0
- data/CONTRIBUTING.md +18 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +37 -0
- data/README.md +607 -0
- data/circle.yml +14 -0
- data/endpoint_flux.gemspec +18 -0
- data/lib/endpoint_flux.rb +20 -0
- data/lib/endpoint_flux/class_loader.rb +58 -0
- data/lib/endpoint_flux/config.rb +85 -0
- data/lib/endpoint_flux/config/interceptor.rb +23 -0
- data/lib/endpoint_flux/config/middleware.rb +38 -0
- data/lib/endpoint_flux/config/rescue_from.rb +28 -0
- data/lib/endpoint_flux/endpoint.rb +81 -0
- data/lib/endpoint_flux/exceptions.rb +10 -0
- data/lib/endpoint_flux/exceptions/base.rb +21 -0
- data/lib/endpoint_flux/exceptions/forbidden.rb +12 -0
- data/lib/endpoint_flux/exceptions/not_found.rb +12 -0
- data/lib/endpoint_flux/exceptions/service_unavailable.rb +12 -0
- data/lib/endpoint_flux/exceptions/unauthorized.rb +12 -0
- data/lib/endpoint_flux/exceptions/validation.rb +13 -0
- data/lib/endpoint_flux/middlewares.rb +8 -0
- data/lib/endpoint_flux/middlewares/authenticator/skip.rb +11 -0
- data/lib/endpoint_flux/middlewares/authorizator/skip.rb +11 -0
- data/lib/endpoint_flux/middlewares/decorator/add_status.rb +12 -0
- data/lib/endpoint_flux/middlewares/decorator/skip.rb +11 -0
- data/lib/endpoint_flux/middlewares/policy/skip.rb +11 -0
- data/lib/endpoint_flux/middlewares/validator/empty.rb +12 -0
- data/lib/endpoint_flux/rails/concerns/endpoint_controller.rb +43 -0
- data/lib/endpoint_flux/request.rb +17 -0
- data/lib/endpoint_flux/response.rb +30 -0
- data/lib/endpoint_flux/version.rb +3 -0
- data/spec/lib/class_loader_spec.rb +31 -0
- data/spec/lib/config/default_middlewares_spec.rb +21 -0
- data/spec/lib/config/endpoints_namespace_spec.rb +13 -0
- data/spec/lib/config/flow_spec.rb +8 -0
- data/spec/lib/config/interceptor_spec.rb +34 -0
- data/spec/lib/config/middleware_spec.rb +62 -0
- data/spec/lib/config/rescue_from_spec.rb +45 -0
- data/spec/lib/endpoint/flow_spec.rb +43 -0
- data/spec/lib/endpoint/middlewares_spec.rb +110 -0
- data/spec/lib/endpoint/perform_spec.rb +61 -0
- data/spec/lib/endpoint/rescue_from_spec.rb +61 -0
- data/spec/lib/endpoint_flux/rails/concerns/endpoint_controller_spec.rb +75 -0
- data/spec/lib/endpoint_flux/request_spec.rb +44 -0
- data/spec/lib/exceptions/forbidden_spec.rb +12 -0
- data/spec/lib/exceptions/not_found_spec.rb +12 -0
- data/spec/lib/exceptions/service_unavailable_spec.rb +12 -0
- data/spec/lib/exceptions/unauthorized_spec.rb +12 -0
- data/spec/lib/exceptions/validation_spec.rb +14 -0
- data/spec/lib/middlewares/authenticator/skip_spec.rb +5 -0
- data/spec/lib/middlewares/authorizator/skip_spec.rb +5 -0
- data/spec/lib/middlewares/decorator/add_status_spec.rb +17 -0
- data/spec/lib/middlewares/decorator/skip_spec.rb +5 -0
- data/spec/lib/middlewares/policy/skip_spec.rb +5 -0
- data/spec/lib/middlewares/shared_examples.rb +19 -0
- data/spec/lib/middlewares/validator/empty_spec.rb +15 -0
- data/spec/lib/response_spec.rb +131 -0
- data/spec/spec_helper.rb +52 -0
- metadata +157 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3383f5adb8daaf805881eecd18df840799d79d93f9205cef9d660626a48e6363
|
4
|
+
data.tar.gz: 8c12c3b6422e751dcc66d8cdf8f9514a031a571977b2ef72d4535c1193e80334
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a7dad5f921168b657530eb65115306cdb6495666f7c8c2295a23c5f95d77cd864aa464bdf1d25e5b19e0e2ce81aff795f1973cf7567ca8b9d37a5449601ea97e
|
7
|
+
data.tar.gz: 8cf2491b1eeeb9450642686aad7c3d1fc11b2ae60dabba5cc04d210b3f5dd7519016faa7980fe96798f74d903f7387d117cd60b5b486af1c0651fd2bcb629bbf
|
data/.gitignore
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Ignore bundler config.
|
2
|
+
/.bundle
|
3
|
+
|
4
|
+
# Ignore all logfiles and tempfiles.
|
5
|
+
/log/*
|
6
|
+
/tmp/*
|
7
|
+
!/log/.keep
|
8
|
+
!/tmp/.keep
|
9
|
+
|
10
|
+
.docker-sync
|
11
|
+
|
12
|
+
.rspec
|
13
|
+
.ruby-*
|
14
|
+
|
15
|
+
.DS_Store
|
16
|
+
.idea
|
17
|
+
.dev_secrets
|
18
|
+
.env
|
19
|
+
.env.test
|
20
|
+
.vimrc
|
21
|
+
|
22
|
+
# vim ignores
|
23
|
+
# swap
|
24
|
+
[._]*.s[a-v][a-z]
|
25
|
+
[._]*.sw[a-p]
|
26
|
+
[._]s[a-v][a-z]
|
27
|
+
[._]sw[a-p]
|
28
|
+
# session
|
29
|
+
Session.vim
|
30
|
+
# temporary
|
31
|
+
.netrwhist
|
32
|
+
*~
|
33
|
+
# auto-generated tag files
|
34
|
+
tags
|
35
|
+
|
36
|
+
.byebug_history
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
## Contributing
|
2
|
+
|
3
|
+
While not required to contribute, we recommend [RBENV](https://github.com/rbenv/rbenv) to manage your rubies.
|
4
|
+
|
5
|
+
1. Read the [Contributor Code of Conduct](https://www.contributor-covenant.org/version/1/0/0/code-of-conduct.html)
|
6
|
+
2. [Fork it](https://help.github.com/articles/about-forks/)
|
7
|
+
3. Clone the project `git clone git@github.com:[YOUR GITHUB USERNAME]/endpoint-flux.git`
|
8
|
+
4. `cd endpoint-flux`
|
9
|
+
5. Create your feature branch `git checkout -b my-new-feature`
|
10
|
+
6. Write tests for your changes (feature/bug)
|
11
|
+
7. Write your (feature/bugfix)
|
12
|
+
8. Install the dependencies `bundle install`
|
13
|
+
9. Run the tests `bundle exec rspec`
|
14
|
+
10. Commit your changes `git commit -am 'Added some feature'`
|
15
|
+
11. Push to the branch `git push origin my-new-feature`
|
16
|
+
12. Create new [Pull Request](https://help.github.com/articles/creating-a-pull-request/)
|
17
|
+
|
18
|
+
If we've missed something please open an [issue](https://github.com/resolving/endpoint-flux/issues/new)
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
endpoint-flux (1.1.3)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
byebug (9.0.6)
|
10
|
+
diff-lcs (1.3)
|
11
|
+
rspec (3.5.0)
|
12
|
+
rspec-core (~> 3.5.0)
|
13
|
+
rspec-expectations (~> 3.5.0)
|
14
|
+
rspec-mocks (~> 3.5.0)
|
15
|
+
rspec-core (3.5.4)
|
16
|
+
rspec-support (~> 3.5.0)
|
17
|
+
rspec-expectations (3.5.0)
|
18
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
19
|
+
rspec-support (~> 3.5.0)
|
20
|
+
rspec-mocks (3.5.0)
|
21
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
22
|
+
rspec-support (~> 3.5.0)
|
23
|
+
rspec-support (3.5.0)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
byebug (>= 9.0)
|
30
|
+
endpoint-flux!
|
31
|
+
rspec (>= 3.5.0)
|
32
|
+
|
33
|
+
RUBY VERSION
|
34
|
+
ruby 2.5.3p105
|
35
|
+
|
36
|
+
BUNDLED WITH
|
37
|
+
1.16.6
|
data/README.md
ADDED
@@ -0,0 +1,607 @@
|
|
1
|
+
# EndpointFlux
|
2
|
+
A simple way to organise API endpoints
|
3
|
+
|
4
|
+
[](http://badge.fury.io/rb/endpoint-flux)
|
5
|
+
|
6
|
+
|
7
|
+
## Index
|
8
|
+
- [Projects code organisation](#projects-code-organisation)
|
9
|
+
- [Usage](#usage)
|
10
|
+
- [Installation](#installation)
|
11
|
+
- [Configuration](#configuration)
|
12
|
+
- [Endpoints](#endpoints)
|
13
|
+
- [Routing](#routing)
|
14
|
+
- [Controllers](#controllers)
|
15
|
+
- [Middlewares](#middlewares)
|
16
|
+
- [Authenticator](#authenticator)
|
17
|
+
- [Authorizator](#authorizator)
|
18
|
+
- [Policy](#policy)
|
19
|
+
- [Validator](#validator)
|
20
|
+
- [Decorator](#decorator)
|
21
|
+
- [Decorators](#decorators)
|
22
|
+
- [Validations](#validations)
|
23
|
+
- [Services](#services)
|
24
|
+
- [Exceptions](#exceptions)
|
25
|
+
- [Response helpers](#response-helpers)
|
26
|
+
- [Contributing](CONTRIBUTING.md)
|
27
|
+
- [Maintainers](https://github.com/resolving/endpoint-flux/graphs/contributors)
|
28
|
+
- [License](#license)
|
29
|
+
|
30
|
+
|
31
|
+
## Projects code organisation
|
32
|
+
|
33
|
+
EndpointFlux offers you a new file and logic organisation in Ruby applications.
|
34
|
+
|
35
|
+
```
|
36
|
+
app
|
37
|
+
├── controllers
|
38
|
+
│ ├── users_controller.rb
|
39
|
+
├── endpoint_flux
|
40
|
+
│ ├── decorators
|
41
|
+
│ │ ├── users
|
42
|
+
│ │ │ ├── project.rb
|
43
|
+
│ │ ├── user.rb
|
44
|
+
│ │ ├── ...
|
45
|
+
│ ├── endpoints
|
46
|
+
│ │ ├── users
|
47
|
+
│ │ ├── create.rb
|
48
|
+
│ │ ├── update.rb
|
49
|
+
│ │ ├── ...
|
50
|
+
│ ├── middlewares
|
51
|
+
│ │ ├── authenticator
|
52
|
+
│ │ │ ├── default.rb
|
53
|
+
│ │ ├── authorizator
|
54
|
+
│ │ │ ├── default.rb
|
55
|
+
│ │ ├── decorator
|
56
|
+
│ │ │ ├── paginate.rb
|
57
|
+
│ │ │ ├── representable.rb
|
58
|
+
│ │ │ ├── ...
|
59
|
+
│ │ ├── policy
|
60
|
+
│ │ │ ├── comment.rb
|
61
|
+
│ │ │ ├── ...
|
62
|
+
│ │ ├── validator
|
63
|
+
│ │ ├── inline.rb
|
64
|
+
│ ├── services
|
65
|
+
│ │ ├── auth.rb
|
66
|
+
│ │ ├── ...
|
67
|
+
│ ├── validations
|
68
|
+
│ │ ├── predicates
|
69
|
+
│ │ │ ├── base.rb
|
70
|
+
│ │ │ ├── date.rb
|
71
|
+
│ │ │ ├── ...
|
72
|
+
│ │ ├── base.rb
|
73
|
+
│ │ ├── error.rb
|
74
|
+
│ │ ├── user.rb
|
75
|
+
```
|
76
|
+
|
77
|
+
|
78
|
+
## Usage
|
79
|
+
|
80
|
+
### Installation
|
81
|
+
Add this line to your application's Gemfile:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
gem 'endpoint-flux'
|
85
|
+
```
|
86
|
+
|
87
|
+
And then execute:
|
88
|
+
```bash
|
89
|
+
$ bundle
|
90
|
+
```
|
91
|
+
|
92
|
+
Or install it yourself as:
|
93
|
+
```bash
|
94
|
+
$ gem install endpoint-flux
|
95
|
+
```
|
96
|
+
|
97
|
+
|
98
|
+
### Configuration
|
99
|
+
|
100
|
+
You can initialize the EndpointFlux before using and can specify a bunch of params as you need. Locate it in
|
101
|
+
`config/initializers/endpoint_flux.rb`.
|
102
|
+
|
103
|
+
With the `EndpointFlux.config.middlewares_namespaces` directive you can specify the location of middleware.
|
104
|
+
```ruby
|
105
|
+
EndpointFlux.config.middlewares_namespaces << 'middlewares'
|
106
|
+
```
|
107
|
+
|
108
|
+
To specify the default middleware that will be used for each response, if nothing specified in Endpoint class, use
|
109
|
+
`EndpointFlux.config.default_middlewares` directive. For example:
|
110
|
+
```ruby
|
111
|
+
EndpointFlux.config.default_middlewares :validator, :inline
|
112
|
+
```
|
113
|
+
Where `:validator` is a middleware and `:inline` is a class that defined in
|
114
|
+
`app/endpoint_flux/middlewares/validator/inline.rb`. More details [here](#validator).
|
115
|
+
|
116
|
+
With `EndpointFlux.config.rescue_from` you can specify how to handle the custom exceptions that would be raised in
|
117
|
+
Application. For example:
|
118
|
+
```ruby
|
119
|
+
not_found_errors = [ActiveRecord::RecordNotFound]
|
120
|
+
EndpointFlux.config.rescue_from(not_found_errors) do |_, attrs, _|
|
121
|
+
attrs[1].body = EndpointFlux::Exceptions::NotFound.new.to_hash
|
122
|
+
attrs
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
126
|
+
Also you can specify interceptor that would be called before processing each response
|
127
|
+
```ruby
|
128
|
+
EndpointFlux.config.interceptor do |attrs|
|
129
|
+
Rails.root.join('maintenance.txt').exist? &&
|
130
|
+
raise(EndpointFlux::Exceptions::ServiceUnavailable)
|
131
|
+
|
132
|
+
attrs
|
133
|
+
end
|
134
|
+
```
|
135
|
+
|
136
|
+
And if you need you can define your own methods like this:
|
137
|
+
```ruby
|
138
|
+
EndpointFlux::Endpoint.class_eval do
|
139
|
+
define_method(:raise_validation_error) do |errors|
|
140
|
+
raise EndpointFlux::Exceptions::Validation, errors
|
141
|
+
end
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
Config example:
|
146
|
+
```ruby
|
147
|
+
# config/initializers/endpoint_flux.rb
|
148
|
+
require 'endpoint_flux'
|
149
|
+
|
150
|
+
EndpointFlux.config.middlewares_namespaces << 'middlewares'
|
151
|
+
|
152
|
+
EndpointFlux.config.default_middlewares :authenticator, :default
|
153
|
+
EndpointFlux.config.default_middlewares :authorizator, :default
|
154
|
+
EndpointFlux.config.default_middlewares :validator, :inline
|
155
|
+
EndpointFlux.config.default_middlewares :policy, :skip
|
156
|
+
EndpointFlux.config.default_middlewares :decorator, :skip
|
157
|
+
|
158
|
+
not_found_errors = [ActiveRecord::RecordNotFound]
|
159
|
+
EndpointFlux.config.rescue_from(not_found_errors) do |_, attrs, _|
|
160
|
+
attrs[1].body = EndpointFlux::Exceptions::NotFound.new.to_hash
|
161
|
+
attrs
|
162
|
+
end
|
163
|
+
|
164
|
+
EndpointFlux.config.interceptor do |attrs|
|
165
|
+
Rails.root.join('maintenance.txt').exist? &&
|
166
|
+
raise(EndpointFlux::Exceptions::ServiceUnavailable)
|
167
|
+
|
168
|
+
attrs
|
169
|
+
end
|
170
|
+
|
171
|
+
EndpointFlux::Endpoint.class_eval do
|
172
|
+
define_method(:raise_validation_error) do |errors|
|
173
|
+
raise EndpointFlux::Exceptions::Validation, errors
|
174
|
+
end
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
### Routing
|
179
|
+
|
180
|
+
EndpointFlux has Rails helper -
|
181
|
+
[`present`](https://github.com/resolving/endpoint-flux/blob/master/lib/endpoint_flux/rails/concerns/endpoint_controller.rb),
|
182
|
+
which integrates with Rails controllers. So, you can use the default Rails routing system to define routes.
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
Rails.application.routes.draw do
|
186
|
+
resources :users
|
187
|
+
end
|
188
|
+
```
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
class UsersController < ApplicationController
|
192
|
+
def index
|
193
|
+
present 'users/index' # it dispatches to Endpoints::Users::Index endpoint class.
|
194
|
+
end
|
195
|
+
end
|
196
|
+
```
|
197
|
+
|
198
|
+
Or if you're using it in not Rails application, you can implement the middleware for providing
|
199
|
+
such data to Endpoints namespace, for example:
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
class BaseHandler
|
203
|
+
def process(msg, options, namespace)
|
204
|
+
params = JSON.parse(msg)
|
205
|
+
action = options[:headers]['action']
|
206
|
+
endpoint = endpoint_for("#{namespace}/#{action}")
|
207
|
+
|
208
|
+
_, response = endpoint.perform(request_object(params))
|
209
|
+
|
210
|
+
response.body
|
211
|
+
end
|
212
|
+
|
213
|
+
private
|
214
|
+
|
215
|
+
def endpoint_for(namespace)
|
216
|
+
if ::EndpointFlux.config.endpoints_namespace
|
217
|
+
::EndpointFlux.config.endpoints_namespace + '/' + namespace
|
218
|
+
else
|
219
|
+
namespace
|
220
|
+
end.camelize.constantize
|
221
|
+
end
|
222
|
+
|
223
|
+
def request_object(params)
|
224
|
+
::EndpointFlux::Request.new(headers: {}, params: params.to_h.deep_symbolize_keys!)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
```
|
228
|
+
|
229
|
+
|
230
|
+
### Controllers
|
231
|
+
|
232
|
+
Controllers are simple endpoints for HTTP. They don't have any business logic and just dispatch to an endpoint class.
|
233
|
+
|
234
|
+
```ruby
|
235
|
+
class UsersController < ApplicationController
|
236
|
+
def index
|
237
|
+
present 'users/index' # it dispatches to Endpoints::Users::Index endpoint class.
|
238
|
+
end
|
239
|
+
end
|
240
|
+
```
|
241
|
+
Finally `present` method renders `response.body` in JSON format (`render json: response.body`).
|
242
|
+
|
243
|
+
|
244
|
+
### Endpoints
|
245
|
+
|
246
|
+
Endpoints encapsulate business logic and it's a central part of applications architecture. It can be used in any Ruby
|
247
|
+
application like Rails, Sinatra and etc.
|
248
|
+
It's a simple coordinator between all layers needed to get the job done. Endpoint needs for defining and implementing
|
249
|
+
steps for the processing data for response. It uses middlewares for data processing that receives the arguments from
|
250
|
+
the caller and returns the array `[request, response]` with `response` that contains `body` and `headers` for API.
|
251
|
+
|
252
|
+
```ruby
|
253
|
+
# app/endpoint_flux/endpoints/users/comments/index.rb
|
254
|
+
module Endpoints
|
255
|
+
module Users
|
256
|
+
module Comments
|
257
|
+
module Index
|
258
|
+
include EndpointFlux::Endpoint
|
259
|
+
|
260
|
+
policy :user
|
261
|
+
policy :comments
|
262
|
+
|
263
|
+
validator :inline do
|
264
|
+
required(:user_id).value(:number?)
|
265
|
+
end
|
266
|
+
|
267
|
+
process do |request, response|
|
268
|
+
response.body[:comments] = request.scope
|
269
|
+
|
270
|
+
[request, response]
|
271
|
+
end
|
272
|
+
|
273
|
+
decorator :add_status, 200
|
274
|
+
decorator :representable, decorator: :comment, collection?: true, wrapped_in: :comments
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
```
|
280
|
+
|
281
|
+
|
282
|
+
|
283
|
+
### Middlewares
|
284
|
+
|
285
|
+
EndpointFlux has 6 types of predefined middlewares. They will be called in the strong defined order - `authenticator,
|
286
|
+
authorizator, validator, policy, process, decorator`. Where `process` should be defined inside the endpoint class for
|
287
|
+
the request processing.
|
288
|
+
|
289
|
+
Also you can add your own middleware class to this flow or change the order. It's possible in two ways:
|
290
|
+
|
291
|
+
* Inside the custom endpoint class, for example:
|
292
|
+
```ruby
|
293
|
+
# app/endpoint_flux/endpoints/users/index.rb
|
294
|
+
|
295
|
+
module Endpoints
|
296
|
+
module Users
|
297
|
+
module Index
|
298
|
+
include EndpointFlux::Endpoint
|
299
|
+
# define new flow with `new_middleware` only for this endpoint
|
300
|
+
flow %i[authenticator authorizator validator policy process new_middleware decorator]
|
301
|
+
|
302
|
+
authorizator :skip
|
303
|
+
#...
|
304
|
+
process do |request, response|
|
305
|
+
# ... some actions
|
306
|
+
[request, response]
|
307
|
+
end
|
308
|
+
|
309
|
+
# define the middleware
|
310
|
+
new_middleware :default
|
311
|
+
|
312
|
+
decorator :add_status, 200
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
```
|
317
|
+
|
318
|
+
* Globally in EndpointFlux config section that will affect all endpoints, for example:
|
319
|
+
```ruby
|
320
|
+
# config/initializers/endpoint_flux.rb
|
321
|
+
# ...
|
322
|
+
# define default value for new middleware
|
323
|
+
EndpointFlux.config.default_middlewares :new_middleware, :default
|
324
|
+
# Global change the middlewares order flow by adding a new one `new_middleware`
|
325
|
+
EndpointFlux.config.flow(%i[authenticator authorizator validator policy process new_middleware decorator])
|
326
|
+
```
|
327
|
+
|
328
|
+
Middleware class definition should contains `self.perform(*args)` method and returns the `[request, response]` as a result
|
329
|
+
```ruby
|
330
|
+
# app/endpoint_flux/middlewares/new_middleware/default.rb
|
331
|
+
module Middlewares
|
332
|
+
module NewMiddleware
|
333
|
+
module Default
|
334
|
+
def self.perform(request, response, _)
|
335
|
+
# ... some actions
|
336
|
+
[request, response]
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
```
|
342
|
+
|
343
|
+
We have implemented the default middlewares that could be used to skip it without any changes to data. It's located
|
344
|
+
[here](https://github.com/resolving/endpoint-flux/tree/master/lib/endpoint_flux/middlewares)
|
345
|
+
|
346
|
+
|
347
|
+
#### Authenticator
|
348
|
+
|
349
|
+
Here you can implement your authenticate system. For example you can user the [JWT gem](https://github.com/jwt/ruby-jwt)
|
350
|
+
Locate it in `app/endpoint_flux/middlewares/authenticator` folder.
|
351
|
+
Also you can skip this middleware in Endpoint class by `authenticator :skip` directive
|
352
|
+
|
353
|
+
|
354
|
+
#### Authorizator
|
355
|
+
|
356
|
+
Here you can implement your authorization system and check the user permissions according to the user role.
|
357
|
+
Locate it in `app/endpoint_flux/middlewares/authorizator` folder.
|
358
|
+
Also you can skip this middleware in the Endpoint class by `authorizator :skip` directive
|
359
|
+
|
360
|
+
|
361
|
+
|
362
|
+
#### Policy
|
363
|
+
|
364
|
+
Here you implement different policy scopes and use them inside the Endpoint class. And also you can chain it to each other by
|
365
|
+
calling in special order. Locate it in `app/endpoint_flux/middlewares/policy` folder.
|
366
|
+
|
367
|
+
For example:
|
368
|
+
* User policy
|
369
|
+
```ruby
|
370
|
+
# app/endpoint_flux/middlewares/policy/user.rb
|
371
|
+
module Middlewares
|
372
|
+
module Policy
|
373
|
+
module User
|
374
|
+
def self.perform(request, response, _)
|
375
|
+
request.scope = ::User.find(request.params[:user_id])
|
376
|
+
|
377
|
+
[request, response]
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
```
|
383
|
+
|
384
|
+
* Comments policy
|
385
|
+
```ruby
|
386
|
+
# app/endpoint_flux/middlewares/policy/comments.rb
|
387
|
+
module Middlewares
|
388
|
+
module Policy
|
389
|
+
module Comments
|
390
|
+
def self.perform(request, response, _)
|
391
|
+
raise 'scope must be set' unless request.scope
|
392
|
+
raise 'scope must be User' unless request.scope.class.name == 'User'
|
393
|
+
|
394
|
+
request.scope = ::Comment.where(user_id: request.scope.id)
|
395
|
+
|
396
|
+
[request, response]
|
397
|
+
end
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
```
|
402
|
+
|
403
|
+
And usage inside the Endpoint class:
|
404
|
+
```ruby
|
405
|
+
# app/endpoint_flux/endpoints/users/comments/index.rb
|
406
|
+
module Endpoints
|
407
|
+
module Users
|
408
|
+
module Comments
|
409
|
+
module Index
|
410
|
+
include EndpointFlux::Endpoint
|
411
|
+
|
412
|
+
policy :user # get user scope
|
413
|
+
policy :comments # get users comments scope
|
414
|
+
|
415
|
+
validator :inline do
|
416
|
+
required(:user_id).value(:number?)
|
417
|
+
end
|
418
|
+
|
419
|
+
process do |request, response|
|
420
|
+
response.body[:comments] = request.scope
|
421
|
+
|
422
|
+
[request, response]
|
423
|
+
end
|
424
|
+
|
425
|
+
decorator :add_status, 200
|
426
|
+
decorator :representable, decorator: :comment, collection?: true, wrapped_in: :comments
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
```
|
432
|
+
|
433
|
+
|
434
|
+
#### Validator
|
435
|
+
|
436
|
+
Here you can implement validation system for request params using the [Dry validation gem](http://dry-rb.org/gems/dry-validation)
|
437
|
+
or another libraries. Locate it in `app/endpoint_flux/middlewares/validator` folder.
|
438
|
+
Also you can skip this middleware in the Endpoint class by `validator :empty` directive
|
439
|
+
|
440
|
+
```ruby
|
441
|
+
# app/endpoint_flux/middlewares/validator/inline.rb
|
442
|
+
module Middlewares
|
443
|
+
module Validator
|
444
|
+
module Inline
|
445
|
+
def self.perform(request, response, _options, &block)
|
446
|
+
validation = ::Services::Validation(&block).call(request.params)
|
447
|
+
unless validation.success?
|
448
|
+
raise ::EndpointFlux::Exceptions::Validation, validation.messages
|
449
|
+
end
|
450
|
+
request.params = validation.result
|
451
|
+
|
452
|
+
[request, response]
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|
457
|
+
```
|
458
|
+
|
459
|
+
Just declare the schema block inside the endpoint to provide it to middleware
|
460
|
+
```ruby
|
461
|
+
# app/endpoint_flux/endpoints/users/create.rb
|
462
|
+
module Endpoints
|
463
|
+
module Users
|
464
|
+
module Create
|
465
|
+
include EndpointFlux::Endpoint
|
466
|
+
|
467
|
+
authenticator :skip
|
468
|
+
authorizator :skip
|
469
|
+
|
470
|
+
validator :inline do
|
471
|
+
required(:user).schema do
|
472
|
+
required(:email).value(:str?, :email?)
|
473
|
+
required(:password).value(:str?, :password?)
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
process do |request, response|
|
478
|
+
# some actions ... like calling checking for user uniqueness, Mailer Sidekiq workers, token generation and etc.
|
479
|
+
|
480
|
+
response.body[:user] = ::User.create(request.params[:user])
|
481
|
+
|
482
|
+
# ...
|
483
|
+
|
484
|
+
[request, response]
|
485
|
+
end
|
486
|
+
|
487
|
+
decorator :add_status, 200
|
488
|
+
decorator :representable, decorator: :user
|
489
|
+
end
|
490
|
+
end
|
491
|
+
end
|
492
|
+
```
|
493
|
+
|
494
|
+
|
495
|
+
#### Decorator
|
496
|
+
|
497
|
+
Here you can implement a decorator system for representing the response.
|
498
|
+
Locate it in `app/endpoint_flux/middlewares/decorator` folder. You can call it inside the endpoint class by using
|
499
|
+
directive like this `decorator :representable, decorator: :user`, where `:representable` it's your decorators class name
|
500
|
+
and `decorator: :user` it's a custom params as you wish (in this situation specialising to use User decorator for
|
501
|
+
representing data).
|
502
|
+
For example
|
503
|
+
|
504
|
+
```ruby
|
505
|
+
# app/endpoint_flux/middlewares/decorator/representable.rb
|
506
|
+
module Middlewares
|
507
|
+
module Decorator
|
508
|
+
module Representable
|
509
|
+
def self.perform(request, response, options)
|
510
|
+
resource_name = options[:decorator]
|
511
|
+
resource = response.body[resource_name]
|
512
|
+
|
513
|
+
response.body[resource_name] = ::Services::Decorator.call(resource, options) if resource
|
514
|
+
|
515
|
+
[request, response]
|
516
|
+
end
|
517
|
+
end
|
518
|
+
end
|
519
|
+
end
|
520
|
+
```
|
521
|
+
|
522
|
+
You can add a custom status to the response body by using directive `decorator :add_status, {status_number}`,
|
523
|
+
for example `decorator :add_status, 200`.
|
524
|
+
Also you can skip this middleware in the Endpoint class by `decorator :skip` directive
|
525
|
+
|
526
|
+
|
527
|
+
|
528
|
+
### Decorators
|
529
|
+
|
530
|
+
Endpoint can use representers from `app/endpoint_flux/decorators` to serialize and parse JSON and XML documents for APIs.
|
531
|
+
For example you can use
|
532
|
+
[Representable gem](http://trailblazer.to/gems/representable), it maps representation documents from and to Ruby objects
|
533
|
+
and includes JSON, XML and YAML support, plain properties and compositions.
|
534
|
+
You can define the decorator schema class in `app/endpoint_flux/decorators` folder and specify it inside of the
|
535
|
+
endpoint class by providing as params for `decorator` directive,
|
536
|
+
for example `decorator :representable, decorator: :user`
|
537
|
+
|
538
|
+
```ruby
|
539
|
+
# app/endpoint_flux/decorators/user.rb
|
540
|
+
module Decorators
|
541
|
+
class User < Representable::Decorator
|
542
|
+
include Representable::JSON
|
543
|
+
|
544
|
+
property :id
|
545
|
+
property :name
|
546
|
+
property :email
|
547
|
+
property :role, exec_context: :decorator
|
548
|
+
|
549
|
+
property :updated_at
|
550
|
+
property :created_at
|
551
|
+
|
552
|
+
def role
|
553
|
+
represented.role.name
|
554
|
+
end
|
555
|
+
end
|
556
|
+
end
|
557
|
+
```
|
558
|
+
|
559
|
+
|
560
|
+
### Validations
|
561
|
+
|
562
|
+
In `app/endpoint_flux/validations` you can locate a custom validation classes and use them with Validator middleware.
|
563
|
+
|
564
|
+
|
565
|
+
### Services
|
566
|
+
|
567
|
+
You can move some business logic from endpoints to service object and locate it here `app/endpoint_flux/services`.
|
568
|
+
|
569
|
+
|
570
|
+
### Exceptions
|
571
|
+
|
572
|
+
You can use EndpointFlux predefined Exceptions for you business logic, for example
|
573
|
+
`raise ::EndpointFlux::Exceptions::Validation`.
|
574
|
+
They defined in
|
575
|
+
[lib/endpoint_flux/exceptions](https://github.com/resolving/endpoint-flux/tree/master/lib/endpoint_flux/exceptions)
|
576
|
+
|
577
|
+
The list of exceptions:
|
578
|
+
* `Forbidden`
|
579
|
+
* `NotFound`
|
580
|
+
* `ServiceUnavailable`
|
581
|
+
* `Unauthorized`
|
582
|
+
* `Validation`
|
583
|
+
|
584
|
+
|
585
|
+
### Response helpers
|
586
|
+
|
587
|
+
If needs you can use the response helpers to check the response body status such as `success?`, `invalid?` or
|
588
|
+
you can define your own helpers in that way. You can use it with an instance of the `EndpointFlux::Response` class.
|
589
|
+
They defined in
|
590
|
+
[lib/endpoint_flux/response.rb](https://github.com/resolving/endpoint-flux/blob/master/lib/endpoint_flux/response.rb)
|
591
|
+
|
592
|
+
The list of helpers:
|
593
|
+
* `success?`
|
594
|
+
* `invalid?`
|
595
|
+
* `forbidden?`
|
596
|
+
* `unauthorized?`
|
597
|
+
* `not_found?`
|
598
|
+
|
599
|
+
|
600
|
+
## [Contributing](CONTRIBUTING.md)
|
601
|
+
|
602
|
+
### [Maintainers](https://github.com/resolving/endpoint-flux/graphs/contributors)
|
603
|
+
|
604
|
+
|
605
|
+
## License
|
606
|
+
|
607
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|