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