power_api 0.2.0 → 2.0.2
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 +4 -4
- data/.circleci/config.yml +104 -0
- data/.circleci/setup-rubygems.sh +3 -0
- data/.rubocop.yml +0 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +19 -1
- data/Gemfile +2 -0
- data/Gemfile.lock +189 -166
- data/README.md +365 -103
- data/app/controllers/power_api/base_controller.rb +0 -1
- data/app/helpers/power_api/application_helper.rb +57 -0
- data/bin/clean_test_app +2 -0
- data/lib/generators/power_api/controller/controller_generator.rb +27 -15
- data/lib/generators/power_api/exposed_api_config/USAGE +5 -0
- data/lib/generators/power_api/exposed_api_config/exposed_api_config_generator.rb +58 -0
- data/lib/generators/power_api/install/install_generator.rb +2 -44
- data/lib/generators/power_api/internal_api_config/USAGE +5 -0
- data/lib/generators/power_api/internal_api_config/internal_api_config_generator.rb +31 -0
- data/lib/generators/power_api/version/version_generator.rb +2 -2
- data/lib/power_api/engine.rb +6 -1
- data/lib/power_api/errors.rb +2 -0
- data/lib/power_api/generator_helper/active_record_resource.rb +10 -6
- data/lib/power_api/generator_helper/ams_helper.rb +5 -11
- data/lib/power_api/generator_helper/api_helper.rb +61 -0
- data/lib/power_api/generator_helper/controller_helper.rb +45 -15
- data/lib/power_api/generator_helper/routes_helper.rb +22 -7
- data/lib/power_api/generator_helper/rspec_controller_helper.rb +306 -0
- data/lib/power_api/generator_helper/swagger_helper.rb +14 -24
- data/lib/power_api/generator_helpers.rb +2 -1
- data/lib/power_api/version.rb +1 -1
- data/power_api.gemspec +3 -2
- data/spec/dummy/Rakefile +1 -1
- data/spec/dummy/app/assets/config/manifest.js +0 -2
- data/spec/dummy/app/controllers/api/base_controller.rb +2 -0
- data/spec/dummy/app/controllers/api/internal/base_controller.rb +5 -0
- data/spec/dummy/app/controllers/api/internal/blogs_controller.rb +36 -0
- data/spec/dummy/app/controllers/application_controller.rb +0 -1
- data/spec/dummy/app/{assets/javascripts → javascript/packs}/application.js +2 -0
- data/spec/dummy/app/jobs/application_job.rb +5 -0
- data/spec/dummy/app/serializers/api/internal/blog_serializer.rb +12 -0
- data/spec/dummy/app/views/layouts/application.html.erb +3 -2
- data/spec/dummy/bin/rails +2 -2
- data/spec/dummy/bin/rake +2 -2
- data/spec/dummy/bin/setup +7 -12
- data/spec/dummy/config/application.rb +12 -16
- data/spec/dummy/config/boot.rb +1 -1
- data/spec/dummy/config/cable.yml +2 -2
- data/spec/dummy/config/database.yml +8 -16
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/environments/development.rb +28 -6
- data/spec/dummy/config/environments/production.rb +45 -16
- data/spec/dummy/config/environments/test.rb +24 -7
- data/spec/dummy/config/initializers/active_model_serializers.rb +1 -0
- data/spec/dummy/config/initializers/api_pagination.rb +32 -0
- data/spec/dummy/config/initializers/assets.rb +0 -2
- data/spec/dummy/config/initializers/backtrace_silencers.rb +4 -3
- data/spec/dummy/config/initializers/content_security_policy.rb +28 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +3 -1
- data/spec/dummy/config/initializers/permissions_policy.rb +11 -0
- data/spec/dummy/config/locales/en.yml +1 -1
- data/spec/dummy/config/puma.rb +14 -27
- data/spec/dummy/config/routes.rb +2 -7
- data/spec/dummy/config/storage.yml +34 -0
- data/spec/dummy/config.ru +2 -1
- data/spec/dummy/spec/helpers/power_api/application_helper_spec.rb +171 -0
- data/spec/dummy/spec/lib/power_api/generator_helper/ams_helper_spec.rb +50 -12
- data/spec/dummy/spec/lib/power_api/generator_helper/api_helper_spec.rb +115 -0
- data/spec/dummy/spec/lib/power_api/generator_helper/controller_helper_spec.rb +126 -34
- data/spec/dummy/spec/lib/power_api/generator_helper/routes_helper_spec.rb +29 -5
- data/spec/dummy/spec/lib/power_api/generator_helper/rspec_controller_helper_spec.rb +559 -0
- data/spec/dummy/spec/lib/power_api/generator_helper/swagger_helper_spec.rb +10 -20
- data/spec/dummy/spec/support/shared_examples/active_record_resource_atrributes.rb +22 -3
- metadata +114 -87
- data/.hound.yml +0 -4
- data/.travis.yml +0 -16
- data/app/controllers/concerns/api/versioned.rb +0 -36
- data/lib/power_api/generator_helper/version_helper.rb +0 -16
- data/spec/dummy/app/assets/javascripts/cable.js +0 -13
- data/spec/dummy/app/controllers/concerns/api/versioned_spec.rb +0 -64
- data/spec/dummy/bin/bundle +0 -3
- data/spec/dummy/bin/update +0 -29
- data/spec/dummy/bin/yarn +0 -11
- data/spec/dummy/config/secrets.yml +0 -32
- data/spec/dummy/config/spring.rb +0 -6
- data/spec/dummy/spec/lib/power_api/generator_helper/version_helper_spec.rb +0 -55
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Power API
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/power_api)
|
4
|
-
[](https://app.circleci.com/pipelines/github/platanus/power_api)
|
5
5
|
[](https://coveralls.io/github/platanus/power_api?branch=master)
|
6
6
|
|
7
7
|
It's a Rails engine that gathers a set of gems and configurations designed to build incredible REST APIs.
|
@@ -21,31 +21,36 @@ These gems are:
|
|
21
21
|
|
22
22
|
## Content
|
23
23
|
|
24
|
-
- [
|
25
|
-
- [
|
26
|
-
- [
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
- [
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
- [
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
- [
|
45
|
-
- [
|
46
|
-
- [
|
47
|
-
- [
|
48
|
-
- [
|
24
|
+
- [Power API](#power-api)
|
25
|
+
- [Content](#content)
|
26
|
+
- [Installation](#installation)
|
27
|
+
- [Usage](#usage)
|
28
|
+
- [Initial Setup](#initial-setup)
|
29
|
+
- [Exposed API mode](#exposed-api-mode)
|
30
|
+
- [Command options:](#command-options)
|
31
|
+
- [`--authenticated-resources`](#--authenticated-resources)
|
32
|
+
- [Internal API mode](#internal-api-mode)
|
33
|
+
- [Version Creation (exposed mode only)](#version-creation-exposed-mode-only)
|
34
|
+
- [Controller Generation (exposed and internal modes)](#controller-generation-exposed-and-internal-modes)
|
35
|
+
- [Command options (valid for internal and exposed modes):](#command-options-valid-for-internal-and-exposed-modes)
|
36
|
+
- [`--attributes`](#--attributes)
|
37
|
+
- [`--controller-actions`](#--controller-actions)
|
38
|
+
- [`--version-number`](#--version-number)
|
39
|
+
- [`--use-paginator`](#--use-paginator)
|
40
|
+
- [`--allow-filters`](#--allow-filters)
|
41
|
+
- [`--authenticate-with`](#--authenticate-with)
|
42
|
+
- [`--owned-by-authenticated-resource`](#--owned-by-authenticated-resource)
|
43
|
+
- [`--parent-resource`](#--parent-resource)
|
44
|
+
- [Inside the gem](#inside-the-gem)
|
45
|
+
- [The `Api::Error` concern](#the-apierror-concern)
|
46
|
+
- [The `Api::Deprecated` concern](#the-apideprecated-concern)
|
47
|
+
- [The `ApiResponder`](#the-apiresponder)
|
48
|
+
- [The `PowerApi::ApplicationHelper#serialize_resource` helper method](#the-powerapiapplicationhelperserialize_resource-helper-method)
|
49
|
+
- [Testing](#testing)
|
50
|
+
- [Publishing](#publishing)
|
51
|
+
- [Contributing](#contributing)
|
52
|
+
- [Credits](#credits)
|
53
|
+
- [License](#license)
|
49
54
|
|
50
55
|
## Installation
|
51
56
|
|
@@ -86,30 +91,13 @@ After doing this you will get:
|
|
86
91
|
class Api::BaseController < PowerApi::BaseController
|
87
92
|
end
|
88
93
|
```
|
89
|
-
Here you should include everything common to all your
|
90
|
-
|
91
|
-
- A base controller for the first version of your API under `/your_api/app/controllers/api/v1/base_controller.rb`
|
92
|
-
```ruby
|
93
|
-
class Api::V1::BaseController < Api::BaseController
|
94
|
-
before_action do
|
95
|
-
self.namespace_for_serializer = ::Api::V1
|
96
|
-
end
|
97
|
-
end
|
98
|
-
```
|
99
|
-
Everything related to version 1 of your API must be included here.
|
94
|
+
Here you should include everything common to all your APIs. It is usually empty because most of the configuration comes in the `PowerApi::BaseController` that is inside the gem.
|
100
95
|
|
101
96
|
- Some initializers:
|
102
97
|
- `/your_api/config/initializers/active_model_serializers.rb`:
|
103
98
|
```ruby
|
104
|
-
|
105
|
-
def self.default_key_transform
|
106
|
-
:unaltered
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
ActiveModelSerializers.config.adapter = :json_api
|
99
|
+
ActiveModelSerializers.config.adapter = :json
|
111
100
|
```
|
112
|
-
Here we tell AMS that we will use the [json api](https://jsonapi.org/) format.
|
113
101
|
|
114
102
|
- `/your_api/config/initializers/api_pagination.rb`:
|
115
103
|
```ruby
|
@@ -121,6 +109,30 @@ After doing this you will get:
|
|
121
109
|
```
|
122
110
|
We use what comes by default and kaminari as pager.
|
123
111
|
|
112
|
+
After running the installer you must choose an API mode.
|
113
|
+
### Exposed API mode
|
114
|
+
|
115
|
+
Use this mode if your API will be accessed by multiple clients or if your API is served somewhere other than your client application.
|
116
|
+
|
117
|
+
You must run the following command to have the exposed API mode configuration:
|
118
|
+
|
119
|
+
```bash
|
120
|
+
rails generate power_api:exposed_api_config
|
121
|
+
```
|
122
|
+
|
123
|
+
After doing this you will get:
|
124
|
+
|
125
|
+
- A base controller for the first version of your API under `/your_api/app/controllers/api/exposed/v1/base_controller.rb`
|
126
|
+
```ruby
|
127
|
+
class Api::Exposed::V1::BaseController < Api::BaseController
|
128
|
+
before_action do
|
129
|
+
self.namespace_for_serializer = ::Api::V1
|
130
|
+
end
|
131
|
+
end
|
132
|
+
```
|
133
|
+
Everything related to version 1 of your API must be included here.
|
134
|
+
|
135
|
+
- Some initializers:
|
124
136
|
- `/your_api/config/initializers/rswag-api.rb`:
|
125
137
|
```ruby
|
126
138
|
Rswag::Api.configure do |c|
|
@@ -194,7 +206,7 @@ After doing this you will get:
|
|
194
206
|
}
|
195
207
|
end
|
196
208
|
```
|
197
|
-
- An empty directory indicating where you should put your serializers for the first version: `/your_api/app/serializers/api/v1/.gitkeep`
|
209
|
+
- An empty directory indicating where you should put your serializers for the first version: `/your_api/app/serializers/api/exposed/v1/.gitkeep`
|
198
210
|
- An empty directory indicating where you should put your API tests: `/your_api/spec/integration/.gitkeep`
|
199
211
|
- An empty directory indicating where you should put your swagger schemas `/your_api/spec/swagger/v1/schemas/.gitkeep`
|
200
212
|
|
@@ -223,7 +235,39 @@ Running the above code will generate, in addition to everything described in the
|
|
223
235
|
```
|
224
236
|
- The migration `/your_api/db/migrate/20200228173608_add_authentication_token_to_users.rb` to add the `authentication_token` to your users table.
|
225
237
|
|
226
|
-
###
|
238
|
+
### Internal API mode
|
239
|
+
|
240
|
+
Use this mode if your API and your client app will be served on the same place.
|
241
|
+
|
242
|
+
You must run the following command to have the internal API mode configuration:
|
243
|
+
|
244
|
+
```bash
|
245
|
+
rails generate power_api:internal_api_config
|
246
|
+
```
|
247
|
+
|
248
|
+
After doing this you will get:
|
249
|
+
|
250
|
+
- A base controller for your internal API under `/your_api/app/controllers/api/internal/base_controller.rb`
|
251
|
+
```ruby
|
252
|
+
class Api::Internal::BaseController < Api::BaseController
|
253
|
+
before_action do
|
254
|
+
self.namespace_for_serializer = ::Api::Internal
|
255
|
+
end
|
256
|
+
end
|
257
|
+
```
|
258
|
+
Anything shared by the internal API controllers should go here.
|
259
|
+
|
260
|
+
- A modified `/your_api/config/routes.rb` file:
|
261
|
+
```ruby
|
262
|
+
namespace :api, defaults: { format: :json } do
|
263
|
+
namespace :internal do
|
264
|
+
end
|
265
|
+
end
|
266
|
+
```
|
267
|
+
|
268
|
+
- An empty directory indicating where you should put your serializers: `/your_api/app/serializers/api/internal/.gitkeep`
|
269
|
+
|
270
|
+
### Version Creation (exposed mode only)
|
227
271
|
|
228
272
|
To add a new version you must run the following command:
|
229
273
|
```bash
|
@@ -236,7 +280,7 @@ rails g power_api:version 2
|
|
236
280
|
|
237
281
|
Doing this will add the same thing that was added for version one in the initial setup but this time for the number version provided as parameter.
|
238
282
|
|
239
|
-
### Controller Generation
|
283
|
+
### Controller Generation (exposed and internal modes)
|
240
284
|
|
241
285
|
To add a controller you must run the following command:
|
242
286
|
```bash
|
@@ -266,18 +310,29 @@ end
|
|
266
310
|
after doing this you will get:
|
267
311
|
|
268
312
|
- A modified `/your_api/config/routes.rb` file with the new resource:
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
313
|
+
- Exposed mode:
|
314
|
+
```ruby
|
315
|
+
Rails.application.routes.draw do
|
316
|
+
scope path: '/api' do
|
317
|
+
api_version(module: 'Api::V1', path: { value: 'v1' }, defaults: { format: 'json' }) do
|
318
|
+
resources :blogs
|
319
|
+
end
|
274
320
|
end
|
275
321
|
end
|
276
|
-
|
277
|
-
|
278
|
-
|
322
|
+
```
|
323
|
+
- Internal mode:
|
324
|
+
```ruby
|
325
|
+
Rails.application.routes.draw do
|
326
|
+
namespace :api, defaults: { format: :json } do
|
327
|
+
namespace :internal do
|
328
|
+
resources :blogs
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
```
|
333
|
+
- A controller under `/your_api/app/controllers/api/exposed/v1/blogs_controller.rb`
|
279
334
|
```ruby
|
280
|
-
class Api::V1::BlogsController < Api::V1::BaseController
|
335
|
+
class Api::Exposed::V1::BlogsController < Api::Exposed::V1::BaseController
|
281
336
|
def index
|
282
337
|
respond_with Blog.all
|
283
338
|
end
|
@@ -306,18 +361,22 @@ after doing this you will get:
|
|
306
361
|
|
307
362
|
def blog_params
|
308
363
|
params.require(:blog).permit(
|
364
|
+
:id,
|
309
365
|
:title,
|
310
366
|
:body,
|
311
367
|
)
|
312
368
|
end
|
313
369
|
end
|
314
370
|
```
|
315
|
-
|
371
|
+
> With internal mode the file path will be: `/your_api/app/controllers/api/internal/blogs_controller.rb` and the class name: `Api::Internal::BlogsController`
|
372
|
+
|
373
|
+
- A serializer under `/your_api/app/serializers/api/exposed/v1/blog_serializer.rb`
|
316
374
|
```ruby
|
317
|
-
class Api::V1::BlogSerializer < ActiveModel::Serializer
|
375
|
+
class Api::Exposed::V1::BlogSerializer < ActiveModel::Serializer
|
318
376
|
type :blog
|
319
377
|
|
320
378
|
attributes(
|
379
|
+
:id,
|
321
380
|
:title,
|
322
381
|
:body,
|
323
382
|
:created_at,
|
@@ -325,7 +384,10 @@ after doing this you will get:
|
|
325
384
|
)
|
326
385
|
end
|
327
386
|
```
|
328
|
-
|
387
|
+
> With internal mode the file path will be: `/your_api/app/serializers/api/internal/blog_serializer.rb` and the class name: `Api::Internal::BlogSerializer`
|
388
|
+
|
389
|
+
- A spec file under `/your_api/spec/integration/api/exposed/v1/blogs_spec.rb`
|
390
|
+
- Exposed mode:
|
329
391
|
```ruby
|
330
392
|
require 'swagger_helper'
|
331
393
|
|
@@ -344,7 +406,7 @@ after doing this you will get:
|
|
344
406
|
schema('$ref' => '#/definitions/blogs_collection')
|
345
407
|
|
346
408
|
run_test! do |response|
|
347
|
-
expect(JSON.parse(response.body)['
|
409
|
+
expect(JSON.parse(response.body)['blogs'].count).to eq(expected_collection_count)
|
348
410
|
end
|
349
411
|
end
|
350
412
|
end
|
@@ -355,7 +417,7 @@ after doing this you will get:
|
|
355
417
|
produces 'application/json'
|
356
418
|
parameter(name: :blog, in: :body)
|
357
419
|
|
358
|
-
response '201', 'blog
|
420
|
+
response '201', 'blog created' do
|
359
421
|
let(:blog) do
|
360
422
|
{
|
361
423
|
title: 'Some title',
|
@@ -365,6 +427,16 @@ after doing this you will get:
|
|
365
427
|
|
366
428
|
run_test!
|
367
429
|
end
|
430
|
+
|
431
|
+
response '400', 'invalid attributes' do
|
432
|
+
let(:blog) do
|
433
|
+
{
|
434
|
+
title: nil
|
435
|
+
}
|
436
|
+
end
|
437
|
+
|
438
|
+
run_test!
|
439
|
+
end
|
368
440
|
end
|
369
441
|
end
|
370
442
|
|
@@ -405,6 +477,16 @@ after doing this you will get:
|
|
405
477
|
|
406
478
|
run_test!
|
407
479
|
end
|
480
|
+
|
481
|
+
response '400', 'invalid attributes' do
|
482
|
+
let(:blog) do
|
483
|
+
{
|
484
|
+
title: nil
|
485
|
+
}
|
486
|
+
end
|
487
|
+
|
488
|
+
run_test!
|
489
|
+
end
|
408
490
|
end
|
409
491
|
|
410
492
|
delete 'Deletes Blog' do
|
@@ -423,57 +505,203 @@ after doing this you will get:
|
|
423
505
|
end
|
424
506
|
end
|
425
507
|
end
|
508
|
+
|
509
|
+
```
|
510
|
+
- Internal mode:
|
511
|
+
```ruby
|
512
|
+
require 'rails_helper'
|
513
|
+
|
514
|
+
describe 'Api::Internal::BlogsControllers', type: :request do
|
515
|
+
describe 'GET /index' do
|
516
|
+
let!(:blogs) { create_list(:blog, 5) }
|
517
|
+
let(:collection) { JSON.parse(response.body)['blogs'] }
|
518
|
+
let(:params) { {} }
|
519
|
+
|
520
|
+
def perform
|
521
|
+
get '/api/internal/blogs', params: params
|
522
|
+
end
|
523
|
+
|
524
|
+
before do
|
525
|
+
perform
|
526
|
+
end
|
527
|
+
|
528
|
+
it { expect(collection.count).to eq(5) }
|
529
|
+
it { expect(response.status).to eq(200) }
|
530
|
+
end
|
531
|
+
|
532
|
+
describe 'POST /create' do
|
533
|
+
let(:params) do
|
534
|
+
{
|
535
|
+
blog: {
|
536
|
+
title: 'Some title'
|
537
|
+
}
|
538
|
+
}
|
539
|
+
end
|
540
|
+
|
541
|
+
let(:attributes) do
|
542
|
+
JSON.parse(response.body)['blog'].symbolize_keys
|
543
|
+
end
|
544
|
+
|
545
|
+
def perform
|
546
|
+
post '/api/internal/blogs', params: params
|
547
|
+
end
|
548
|
+
|
549
|
+
before do
|
550
|
+
perform
|
551
|
+
end
|
552
|
+
|
553
|
+
it { expect(attributes).to include(params[:blog]) }
|
554
|
+
it { expect(response.status).to eq(201) }
|
555
|
+
|
556
|
+
context 'with invalid attributes' do
|
557
|
+
let(:params) do
|
558
|
+
{
|
559
|
+
blog: {
|
560
|
+
title: nil
|
561
|
+
}
|
562
|
+
}
|
563
|
+
end
|
564
|
+
|
565
|
+
it { expect(response.status).to eq(400) }
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
describe 'GET /show' do
|
570
|
+
let(:blog) { create(:blog) }
|
571
|
+
let(:blog_id) { blog.id.to_s }
|
572
|
+
|
573
|
+
let(:attributes) do
|
574
|
+
JSON.parse(response.body)['blog'].symbolize_keys
|
575
|
+
end
|
576
|
+
|
577
|
+
def perform
|
578
|
+
get '/api/internal/blogs/' + blog_id
|
579
|
+
end
|
580
|
+
|
581
|
+
before do
|
582
|
+
perform
|
583
|
+
end
|
584
|
+
|
585
|
+
it { expect(response.status).to eq(200) }
|
586
|
+
|
587
|
+
context 'with resource not found' do
|
588
|
+
let(:blog_id) { '666' }
|
589
|
+
|
590
|
+
it { expect(response.status).to eq(404) }
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
describe 'PUT /update' do
|
595
|
+
let(:blog) { create(:blog) }
|
596
|
+
let(:blog_id) { blog.id.to_s }
|
597
|
+
|
598
|
+
let(:params) do
|
599
|
+
{
|
600
|
+
blog: {
|
601
|
+
title: 'Some title'
|
602
|
+
}
|
603
|
+
}
|
604
|
+
end
|
605
|
+
|
606
|
+
let(:attributes) do
|
607
|
+
JSON.parse(response.body)['blog'].symbolize_keys
|
608
|
+
end
|
609
|
+
|
610
|
+
def perform
|
611
|
+
put '/api/internal/blogs/' + blog_id, params: params
|
612
|
+
end
|
613
|
+
|
614
|
+
before do
|
615
|
+
perform
|
616
|
+
end
|
617
|
+
|
618
|
+
it { expect(attributes).to include(params[:blog]) }
|
619
|
+
it { expect(response.status).to eq(200) }
|
620
|
+
|
621
|
+
context 'with invalid attributes' do
|
622
|
+
let(:params) do
|
623
|
+
{
|
624
|
+
blog: {
|
625
|
+
title: nil
|
626
|
+
}
|
627
|
+
}
|
628
|
+
end
|
629
|
+
|
630
|
+
it { expect(response.status).to eq(400) }
|
631
|
+
end
|
632
|
+
|
633
|
+
context 'with resource not found' do
|
634
|
+
let(:blog_id) { '666' }
|
635
|
+
|
636
|
+
it { expect(response.status).to eq(404) }
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
describe 'DELETE /destroy' do
|
641
|
+
let(:blog) { create(:blog) }
|
642
|
+
let(:blog_id) { blog.id.to_s }
|
643
|
+
|
644
|
+
def perform
|
645
|
+
get '/api/internal/blogs/' + blog_id
|
646
|
+
end
|
647
|
+
|
648
|
+
before do
|
649
|
+
perform
|
650
|
+
end
|
651
|
+
|
652
|
+
it { expect(response.status).to eq(200) }
|
653
|
+
|
654
|
+
context 'with resource not found' do
|
655
|
+
let(:blog_id) { '666' }
|
656
|
+
|
657
|
+
it { expect(response.status).to eq(404) }
|
658
|
+
end
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
426
662
|
```
|
427
|
-
- A swagger schema definition under `/your_api/spec/swagger/v1/schemas/blog_schema.rb`
|
663
|
+
- A swagger schema definition under `/your_api/spec/swagger/v1/schemas/blog_schema.rb` (only for exposed mode)
|
428
664
|
```ruby
|
429
665
|
BLOG_SCHEMA = {
|
430
666
|
type: :object,
|
431
667
|
properties: {
|
432
|
-
id: { type: :
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
body: { type: :string, example: 'Some body', 'x-nullable': true },
|
439
|
-
created_at: { type: :string, example: '1984-06-04 09:00', 'x-nullable': true },
|
440
|
-
updated_at: { type: :string, example: '1984-06-04 09:00', 'x-nullable': true }
|
441
|
-
},
|
442
|
-
required: [
|
443
|
-
]
|
444
|
-
}
|
668
|
+
id: { type: :integer, example: 666 },
|
669
|
+
title: { type: :string, example: 'Some title' },
|
670
|
+
body: { type: :string, example: 'Some body' },
|
671
|
+
created_at: { type: :string, example: '1984-06-04 09:00', 'x-nullable': true },
|
672
|
+
updated_at: { type: :string, example: '1984-06-04 09:00', 'x-nullable': true },
|
673
|
+
portfolio_id: { type: :integer, example: 666, 'x-nullable': true }
|
445
674
|
},
|
446
675
|
required: [
|
447
|
-
:
|
448
|
-
:
|
449
|
-
:attributes
|
676
|
+
:title,
|
677
|
+
:body
|
450
678
|
]
|
451
679
|
}
|
452
680
|
|
453
681
|
BLOGS_COLLECTION_SCHEMA = {
|
454
682
|
type: "object",
|
455
683
|
properties: {
|
456
|
-
|
684
|
+
blogs: {
|
457
685
|
type: "array",
|
458
686
|
items: { "$ref" => "#/definitions/blog" }
|
459
687
|
}
|
460
688
|
},
|
461
689
|
required: [
|
462
|
-
:
|
690
|
+
:blogs
|
463
691
|
]
|
464
692
|
}
|
465
693
|
|
466
694
|
BLOG_RESOURCE_SCHEMA = {
|
467
695
|
type: "object",
|
468
696
|
properties: {
|
469
|
-
|
697
|
+
blog: { "$ref" => "#/definitions/blog" }
|
470
698
|
},
|
471
699
|
required: [
|
472
|
-
:
|
700
|
+
:blog
|
473
701
|
]
|
474
702
|
}
|
475
703
|
```
|
476
|
-
- An edited version of `your_api/api_example/spec/swagger/v1/definition.rb` with the schema definitions for the `Blog` resource.
|
704
|
+
- An edited version of `your_api/api_example/spec/swagger/v1/definition.rb` with the schema definitions for the `Blog` resource. (only for exposed mode)
|
477
705
|
```ruby
|
478
706
|
API_V1 = {
|
479
707
|
swagger: '2.0',
|
@@ -490,7 +718,7 @@ after doing this you will get:
|
|
490
718
|
}
|
491
719
|
```
|
492
720
|
|
493
|
-
#### Command options:
|
721
|
+
#### Command options (valid for internal and exposed modes):
|
494
722
|
|
495
723
|
##### `--attributes`
|
496
724
|
|
@@ -502,9 +730,9 @@ rails g power_api:controller blog --attributes=title
|
|
502
730
|
|
503
731
|
When you do this, you will see permited_params, serializers, swagger definitions, etc. showing only the selected attributes
|
504
732
|
|
505
|
-
For example, the serializer under `/your_api/app/serializers/api/v1/blog_serializer.rb` will show:
|
733
|
+
For example, the serializer under `/your_api/app/serializers/api/exposed/v1/blog_serializer.rb` will show:
|
506
734
|
```ruby
|
507
|
-
class Api::V1::BlogSerializer < ActiveModel::Serializer
|
735
|
+
class Api::Exposed::V1::BlogSerializer < ActiveModel::Serializer
|
508
736
|
type :blog
|
509
737
|
|
510
738
|
attributes(
|
@@ -526,7 +754,7 @@ When you do this, you will see that only relevant code is generated in controlle
|
|
526
754
|
For example, the controller would only include the `show` and `destroy` actions and wouldn't include the `blog_params` method:
|
527
755
|
|
528
756
|
```ruby
|
529
|
-
class Api::V1::
|
757
|
+
class Api::Exposed::V1::BlogController < Api::Exposed::V1::BaseController
|
530
758
|
def show
|
531
759
|
respond_with blog
|
532
760
|
end
|
@@ -551,6 +779,8 @@ Use this option if you want to decide which version the new controller will belo
|
|
551
779
|
rails g power_api:controller blog --version-number=2
|
552
780
|
```
|
553
781
|
|
782
|
+
> Important! When working with exposed api you should always specify the version, otherwise the controller will be generated for the internal api mode.
|
783
|
+
|
554
784
|
##### `--use-paginator`
|
555
785
|
|
556
786
|
Use this option if you want to paginate the index endpoint collection.
|
@@ -559,10 +789,10 @@ Use this option if you want to paginate the index endpoint collection.
|
|
559
789
|
rails g power_api:controller blog --use-paginator
|
560
790
|
```
|
561
791
|
|
562
|
-
The controller under `/your_api/app/controllers/api/v1/blogs_controller.rb` will be modified to use the paginator like this:
|
792
|
+
The controller under `/your_api/app/controllers/api/exposed/v1/blogs_controller.rb` will be modified to use the paginator like this:
|
563
793
|
|
564
794
|
```ruby
|
565
|
-
class Api::V1::BlogsController < Api::V1::BaseController
|
795
|
+
class Api::Exposed::V1::BlogsController < Api::Exposed::V1::BaseController
|
566
796
|
def index
|
567
797
|
respond_with paginate(Blog.all)
|
568
798
|
end
|
@@ -583,10 +813,10 @@ Use this option if you want to filter your index endpoint collection with [Ransa
|
|
583
813
|
rails g power_api:controller blog --allow-filters
|
584
814
|
```
|
585
815
|
|
586
|
-
The controller under `/your_api/app/controllers/api/v1/blogs_controller.rb` will be modified like this:
|
816
|
+
The controller under `/your_api/app/controllers/api/exposed/v1/blogs_controller.rb` will be modified like this:
|
587
817
|
|
588
818
|
```ruby
|
589
|
-
class Api::V1::BlogsController < Api::V1::BaseController
|
819
|
+
class Api::Exposed::V1::BlogsController < Api::Exposed::V1::BaseController
|
590
820
|
def index
|
591
821
|
respond_with filtered_collection(Blog.all)
|
592
822
|
end
|
@@ -623,13 +853,15 @@ rails g power_api:controller blog --authenticate-with=user
|
|
623
853
|
When you do this your controller will have the following line:
|
624
854
|
|
625
855
|
```ruby
|
626
|
-
class Api::V1::BlogsController < Api::V1::BaseController
|
856
|
+
class Api::Exposed::V1::BlogsController < Api::Exposed::V1::BaseController
|
627
857
|
acts_as_token_authentication_handler_for User, fallback: :exception
|
628
858
|
|
629
859
|
# mode code...
|
630
860
|
end
|
631
861
|
```
|
632
862
|
|
863
|
+
> With internal mode a `before_action :authenticate_user!` statement will be added instead of `acts_as_token_authentication_handler_for` in order to work with devise gem directly.
|
864
|
+
|
633
865
|
In addition, the specs under `/your_api/spec/integration/api/v1/blogs_spec.rb` will add tests related with authorization.
|
634
866
|
|
635
867
|
```ruby
|
@@ -651,7 +883,7 @@ rails g power_api:controller blog --authenticate-with=user --owned-by-authentica
|
|
651
883
|
The controller will look like this:
|
652
884
|
|
653
885
|
```ruby
|
654
|
-
class Api::V1::BlogsController < Api::V1::BaseController
|
886
|
+
class Api::Exposed::V1::BlogsController < Api::Exposed::V1::BaseController
|
655
887
|
acts_as_token_authentication_handler_for User, fallback: :exception
|
656
888
|
|
657
889
|
def index
|
@@ -686,6 +918,7 @@ class Api::V1::BlogsController < Api::V1::BaseController
|
|
686
918
|
|
687
919
|
def blog_params
|
688
920
|
params.require(:blog).permit(
|
921
|
+
:id,
|
689
922
|
:title,
|
690
923
|
:body
|
691
924
|
)
|
@@ -729,9 +962,9 @@ rails g power_api:controller comment --attributes=body --parent-resource=blog
|
|
729
962
|
|
730
963
|
Running the previous code we will get:
|
731
964
|
|
732
|
-
- The controller under `/your_api/app/controllers/api/v1/comments_controller.rb`:
|
965
|
+
- The controller under `/your_api/app/controllers/api/exposed/v1/comments_controller.rb`:
|
733
966
|
```ruby
|
734
|
-
class Api::V1::CommentsController < Api::V1::BaseController
|
967
|
+
class Api::Exposed::V1::CommentsController < Api::Exposed::V1::BaseController
|
735
968
|
def index
|
736
969
|
respond_with comments
|
737
970
|
end
|
@@ -768,6 +1001,7 @@ Running the previous code we will get:
|
|
768
1001
|
|
769
1002
|
def comment_params
|
770
1003
|
params.require(:comment).permit(
|
1004
|
+
:id,
|
771
1005
|
:body
|
772
1006
|
)
|
773
1007
|
end
|
@@ -829,7 +1063,6 @@ module PowerApi
|
|
829
1063
|
class BaseController < ApplicationController
|
830
1064
|
include Api::Error
|
831
1065
|
include Api::Deprecated
|
832
|
-
include Api::Versioned
|
833
1066
|
|
834
1067
|
self.responder = ApiResponder
|
835
1068
|
|
@@ -867,7 +1100,7 @@ This module is useful when you want to mark endpoints as deprecated.
|
|
867
1100
|
For example, if you have the following controller:
|
868
1101
|
|
869
1102
|
```ruby
|
870
|
-
class Api::V1::CommentsController < Api::V1::BaseController
|
1103
|
+
class Api::Exposed::V1::CommentsController < Api::Exposed::V1::BaseController
|
871
1104
|
deprecate :index
|
872
1105
|
|
873
1106
|
def index
|
@@ -882,10 +1115,6 @@ And then in your browser you execute: `GET /api/v1/comments`, you will get a `De
|
|
882
1115
|
|
883
1116
|
This is useful to notify your customers that an endpoint will not be available in the next version of the API.
|
884
1117
|
|
885
|
-
### The `Api::Versioned` concern
|
886
|
-
|
887
|
-
This module includes to your API responses the version of the API in a header. For example: `Content-Type: application/json; charset=utf-8; version=1`
|
888
|
-
|
889
1118
|
### The `ApiResponder`
|
890
1119
|
|
891
1120
|
It look like this:
|
@@ -908,6 +1137,28 @@ end
|
|
908
1137
|
|
909
1138
|
As you can see, this simple [Responder](https://github.com/heartcombo/responders) handles the API response based on the HTTP verbs.
|
910
1139
|
|
1140
|
+
### The `PowerApi::ApplicationHelper#serialize_resource` helper method
|
1141
|
+
|
1142
|
+
This helper method is useful if you want to serialize ActiveRecord resources to use in your views. For example, you can do:
|
1143
|
+
|
1144
|
+
```
|
1145
|
+
<pre>
|
1146
|
+
<%= serialize_resource(@resource, @options) %>
|
1147
|
+
</pre>
|
1148
|
+
```
|
1149
|
+
|
1150
|
+
To get:
|
1151
|
+
|
1152
|
+
```
|
1153
|
+
{"id":1,"title":"lean","body":"bla","createdAt":"2022-01-08T18:15:46.624Z","updatedAt":"2022-01-08T18:15:46.624Z","portfolioId":null}
|
1154
|
+
```
|
1155
|
+
|
1156
|
+
The `@resource` parameter must be an ActiveRecord instance (`ApplicationRecord`) or collection (`ActiveRecord_Relation`).
|
1157
|
+
|
1158
|
+
The `@options` parameter must be a `Hash` and can contain the options you commonly use with Active Model Serializer gem (`fields`, `transform_key`, etc.) and some others:
|
1159
|
+
- `include_root`: to get something like: `{"id":1,"title":"lean"}` or `{"blog": {"id":1,"title":"lean"}}`.
|
1160
|
+
- `output_format`: can be `:hash` or `:json`.
|
1161
|
+
|
911
1162
|
## Testing
|
912
1163
|
|
913
1164
|
To run the specs you need to execute, **in the root path of the gem**, the following command:
|
@@ -918,6 +1169,17 @@ bundle exec guard
|
|
918
1169
|
|
919
1170
|
You need to put **all your tests** in the `/power_api/spec/dummy/spec/` directory.
|
920
1171
|
|
1172
|
+
## Publishing
|
1173
|
+
|
1174
|
+
On master/main branch...
|
1175
|
+
|
1176
|
+
1. Change `VERSION` in `lib/power_api/version.rb`.
|
1177
|
+
2. Change `Unreleased` title to current version in `CHANGELOG.md`.
|
1178
|
+
3. Run `bundle install`.
|
1179
|
+
4. Commit new release. For example: `Releasing v0.1.0`.
|
1180
|
+
5. Create tag. For example: `git tag v0.1.0`.
|
1181
|
+
6. Push tag. For example: `git push origin v0.1.0`.
|
1182
|
+
|
921
1183
|
## Contributing
|
922
1184
|
|
923
1185
|
1. Fork it
|
@@ -936,4 +1198,4 @@ Power API is maintained by [platanus](http://platan.us).
|
|
936
1198
|
|
937
1199
|
## License
|
938
1200
|
|
939
|
-
Power API is ©
|
1201
|
+
Power API is © 2022 platanus, spa. It is free software and may be redistributed under the terms specified in the LICENSE file.
|