endpoint-flux 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) 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 +17 -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 +32 -0
  31. data/lib/endpoint_flux/request.rb +15 -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/exceptions/forbidden_spec.rb +12 -0
  46. data/spec/lib/exceptions/not_found_spec.rb +12 -0
  47. data/spec/lib/exceptions/service_unavailable_spec.rb +12 -0
  48. data/spec/lib/exceptions/unauthorized_spec.rb +12 -0
  49. data/spec/lib/exceptions/validation_spec.rb +14 -0
  50. data/spec/lib/middlewares/authenticator/skip_spec.rb +5 -0
  51. data/spec/lib/middlewares/authorizator/skip_spec.rb +5 -0
  52. data/spec/lib/middlewares/decorator/add_status_spec.rb +17 -0
  53. data/spec/lib/middlewares/decorator/skip_spec.rb +5 -0
  54. data/spec/lib/middlewares/policy/skip_spec.rb +5 -0
  55. data/spec/lib/middlewares/shared_examples.rb +19 -0
  56. data/spec/lib/middlewares/validator/empty_spec.rb +15 -0
  57. data/spec/lib/response_spec.rb +131 -0
  58. data/spec/spec_helper.rb +52 -0
  59. metadata +153 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0274fa91a530211dfb8293bd9663d74c67e57eab95c593b2a21187333f6f563a
4
+ data.tar.gz: 2ca3e41032810c54268440850141b22dff6c8ac868c134bfb85ddbd523b5474a
5
+ SHA512:
6
+ metadata.gz: dc4fe82a415890d4b207dfba40193c4e600b78f96f6bf49b0146203ec3f8e9b01d2682316ae0a037f859df77c2cc11c3026ce37b5399506bbcb126f1ba4762e6
7
+ data.tar.gz: ba45e125f2a8508e5fc62a216af336dcc4f84bcc8afeeb57cdca4c2cce8115b9fc448614d16d16c485056a85908184c75b1add80928895d31e1b789a1df60338
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
@@ -0,0 +1,6 @@
1
+ ruby '2.5.1'
2
+ #ruby-gemset=endpoint-flux
3
+
4
+ source 'https://rubygems.org'
5
+
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,37 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ endpoint-flux (1.1.1)
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
30
+ endpoint-flux!
31
+ rspec
32
+
33
+ RUBY VERSION
34
+ ruby 2.5.1p57
35
+
36
+ BUNDLED WITH
37
+ 1.16.4
data/README.md ADDED
@@ -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).