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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +36 -0
  3. data/CONTRIBUTING.md +18 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +37 -0
  6. data/README.md +607 -0
  7. data/circle.yml +14 -0
  8. data/endpoint_flux.gemspec +18 -0
  9. data/lib/endpoint_flux.rb +20 -0
  10. data/lib/endpoint_flux/class_loader.rb +58 -0
  11. data/lib/endpoint_flux/config.rb +85 -0
  12. data/lib/endpoint_flux/config/interceptor.rb +23 -0
  13. data/lib/endpoint_flux/config/middleware.rb +38 -0
  14. data/lib/endpoint_flux/config/rescue_from.rb +28 -0
  15. data/lib/endpoint_flux/endpoint.rb +81 -0
  16. data/lib/endpoint_flux/exceptions.rb +10 -0
  17. data/lib/endpoint_flux/exceptions/base.rb +21 -0
  18. data/lib/endpoint_flux/exceptions/forbidden.rb +12 -0
  19. data/lib/endpoint_flux/exceptions/not_found.rb +12 -0
  20. data/lib/endpoint_flux/exceptions/service_unavailable.rb +12 -0
  21. data/lib/endpoint_flux/exceptions/unauthorized.rb +12 -0
  22. data/lib/endpoint_flux/exceptions/validation.rb +13 -0
  23. data/lib/endpoint_flux/middlewares.rb +8 -0
  24. data/lib/endpoint_flux/middlewares/authenticator/skip.rb +11 -0
  25. data/lib/endpoint_flux/middlewares/authorizator/skip.rb +11 -0
  26. data/lib/endpoint_flux/middlewares/decorator/add_status.rb +12 -0
  27. data/lib/endpoint_flux/middlewares/decorator/skip.rb +11 -0
  28. data/lib/endpoint_flux/middlewares/policy/skip.rb +11 -0
  29. data/lib/endpoint_flux/middlewares/validator/empty.rb +12 -0
  30. data/lib/endpoint_flux/rails/concerns/endpoint_controller.rb +43 -0
  31. data/lib/endpoint_flux/request.rb +17 -0
  32. data/lib/endpoint_flux/response.rb +30 -0
  33. data/lib/endpoint_flux/version.rb +3 -0
  34. data/spec/lib/class_loader_spec.rb +31 -0
  35. data/spec/lib/config/default_middlewares_spec.rb +21 -0
  36. data/spec/lib/config/endpoints_namespace_spec.rb +13 -0
  37. data/spec/lib/config/flow_spec.rb +8 -0
  38. data/spec/lib/config/interceptor_spec.rb +34 -0
  39. data/spec/lib/config/middleware_spec.rb +62 -0
  40. data/spec/lib/config/rescue_from_spec.rb +45 -0
  41. data/spec/lib/endpoint/flow_spec.rb +43 -0
  42. data/spec/lib/endpoint/middlewares_spec.rb +110 -0
  43. data/spec/lib/endpoint/perform_spec.rb +61 -0
  44. data/spec/lib/endpoint/rescue_from_spec.rb +61 -0
  45. data/spec/lib/endpoint_flux/rails/concerns/endpoint_controller_spec.rb +75 -0
  46. data/spec/lib/endpoint_flux/request_spec.rb +44 -0
  47. data/spec/lib/exceptions/forbidden_spec.rb +12 -0
  48. data/spec/lib/exceptions/not_found_spec.rb +12 -0
  49. data/spec/lib/exceptions/service_unavailable_spec.rb +12 -0
  50. data/spec/lib/exceptions/unauthorized_spec.rb +12 -0
  51. data/spec/lib/exceptions/validation_spec.rb +14 -0
  52. data/spec/lib/middlewares/authenticator/skip_spec.rb +5 -0
  53. data/spec/lib/middlewares/authorizator/skip_spec.rb +5 -0
  54. data/spec/lib/middlewares/decorator/add_status_spec.rb +17 -0
  55. data/spec/lib/middlewares/decorator/skip_spec.rb +5 -0
  56. data/spec/lib/middlewares/policy/skip_spec.rb +5 -0
  57. data/spec/lib/middlewares/shared_examples.rb +19 -0
  58. data/spec/lib/middlewares/validator/empty_spec.rb +15 -0
  59. data/spec/lib/response_spec.rb +131 -0
  60. data/spec/spec_helper.rb +52 -0
  61. metadata +157 -0
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,6 @@
1
+ ruby '2.5.3'
2
+ #ruby-gemset=endpoint-flux
3
+
4
+ source 'https://rubygems.org'
5
+
6
+ gemspec
@@ -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
@@ -0,0 +1,607 @@
1
+ # EndpointFlux
2
+ A simple way to organise API endpoints
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/endpoint-flux.svg)](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).