power_api 0.2.0 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +104 -0
  3. data/.circleci/setup-rubygems.sh +3 -0
  4. data/.rubocop.yml +0 -1
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +19 -1
  7. data/Gemfile +2 -0
  8. data/Gemfile.lock +189 -166
  9. data/README.md +365 -103
  10. data/app/controllers/power_api/base_controller.rb +0 -1
  11. data/app/helpers/power_api/application_helper.rb +57 -0
  12. data/bin/clean_test_app +2 -0
  13. data/lib/generators/power_api/controller/controller_generator.rb +27 -15
  14. data/lib/generators/power_api/exposed_api_config/USAGE +5 -0
  15. data/lib/generators/power_api/exposed_api_config/exposed_api_config_generator.rb +58 -0
  16. data/lib/generators/power_api/install/install_generator.rb +2 -44
  17. data/lib/generators/power_api/internal_api_config/USAGE +5 -0
  18. data/lib/generators/power_api/internal_api_config/internal_api_config_generator.rb +31 -0
  19. data/lib/generators/power_api/version/version_generator.rb +2 -2
  20. data/lib/power_api/engine.rb +6 -1
  21. data/lib/power_api/errors.rb +2 -0
  22. data/lib/power_api/generator_helper/active_record_resource.rb +10 -6
  23. data/lib/power_api/generator_helper/ams_helper.rb +5 -11
  24. data/lib/power_api/generator_helper/api_helper.rb +61 -0
  25. data/lib/power_api/generator_helper/controller_helper.rb +45 -15
  26. data/lib/power_api/generator_helper/routes_helper.rb +22 -7
  27. data/lib/power_api/generator_helper/rspec_controller_helper.rb +306 -0
  28. data/lib/power_api/generator_helper/swagger_helper.rb +14 -24
  29. data/lib/power_api/generator_helpers.rb +2 -1
  30. data/lib/power_api/version.rb +1 -1
  31. data/power_api.gemspec +3 -2
  32. data/spec/dummy/Rakefile +1 -1
  33. data/spec/dummy/app/assets/config/manifest.js +0 -2
  34. data/spec/dummy/app/controllers/api/base_controller.rb +2 -0
  35. data/spec/dummy/app/controllers/api/internal/base_controller.rb +5 -0
  36. data/spec/dummy/app/controllers/api/internal/blogs_controller.rb +36 -0
  37. data/spec/dummy/app/controllers/application_controller.rb +0 -1
  38. data/spec/dummy/app/{assets/javascripts → javascript/packs}/application.js +2 -0
  39. data/spec/dummy/app/jobs/application_job.rb +5 -0
  40. data/spec/dummy/app/serializers/api/internal/blog_serializer.rb +12 -0
  41. data/spec/dummy/app/views/layouts/application.html.erb +3 -2
  42. data/spec/dummy/bin/rails +2 -2
  43. data/spec/dummy/bin/rake +2 -2
  44. data/spec/dummy/bin/setup +7 -12
  45. data/spec/dummy/config/application.rb +12 -16
  46. data/spec/dummy/config/boot.rb +1 -1
  47. data/spec/dummy/config/cable.yml +2 -2
  48. data/spec/dummy/config/database.yml +8 -16
  49. data/spec/dummy/config/environment.rb +1 -1
  50. data/spec/dummy/config/environments/development.rb +28 -6
  51. data/spec/dummy/config/environments/production.rb +45 -16
  52. data/spec/dummy/config/environments/test.rb +24 -7
  53. data/spec/dummy/config/initializers/active_model_serializers.rb +1 -0
  54. data/spec/dummy/config/initializers/api_pagination.rb +32 -0
  55. data/spec/dummy/config/initializers/assets.rb +0 -2
  56. data/spec/dummy/config/initializers/backtrace_silencers.rb +4 -3
  57. data/spec/dummy/config/initializers/content_security_policy.rb +28 -0
  58. data/spec/dummy/config/initializers/filter_parameter_logging.rb +3 -1
  59. data/spec/dummy/config/initializers/permissions_policy.rb +11 -0
  60. data/spec/dummy/config/locales/en.yml +1 -1
  61. data/spec/dummy/config/puma.rb +14 -27
  62. data/spec/dummy/config/routes.rb +2 -7
  63. data/spec/dummy/config/storage.yml +34 -0
  64. data/spec/dummy/config.ru +2 -1
  65. data/spec/dummy/spec/helpers/power_api/application_helper_spec.rb +171 -0
  66. data/spec/dummy/spec/lib/power_api/generator_helper/ams_helper_spec.rb +50 -12
  67. data/spec/dummy/spec/lib/power_api/generator_helper/api_helper_spec.rb +115 -0
  68. data/spec/dummy/spec/lib/power_api/generator_helper/controller_helper_spec.rb +126 -34
  69. data/spec/dummy/spec/lib/power_api/generator_helper/routes_helper_spec.rb +29 -5
  70. data/spec/dummy/spec/lib/power_api/generator_helper/rspec_controller_helper_spec.rb +559 -0
  71. data/spec/dummy/spec/lib/power_api/generator_helper/swagger_helper_spec.rb +10 -20
  72. data/spec/dummy/spec/support/shared_examples/active_record_resource_atrributes.rb +22 -3
  73. metadata +114 -87
  74. data/.hound.yml +0 -4
  75. data/.travis.yml +0 -16
  76. data/app/controllers/concerns/api/versioned.rb +0 -36
  77. data/lib/power_api/generator_helper/version_helper.rb +0 -16
  78. data/spec/dummy/app/assets/javascripts/cable.js +0 -13
  79. data/spec/dummy/app/controllers/concerns/api/versioned_spec.rb +0 -64
  80. data/spec/dummy/bin/bundle +0 -3
  81. data/spec/dummy/bin/update +0 -29
  82. data/spec/dummy/bin/yarn +0 -11
  83. data/spec/dummy/config/secrets.yml +0 -32
  84. data/spec/dummy/config/spring.rb +0 -6
  85. 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
  [![Gem Version](https://badge.fury.io/rb/power_api.svg)](https://badge.fury.io/rb/power_api)
4
- [![Build Status](https://travis-ci.org/platanus/power_api.svg?branch=master)](https://travis-ci.org/platanus/power_api)
4
+ [![CircleCI](https://circleci.com/gh/platanus/power_api.svg?style=shield)](https://app.circleci.com/pipelines/github/platanus/power_api)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/platanus/power_api/badge.svg?branch=master)](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
- - [Installation](#installation)
25
- - [Usage](#usage)
26
- - [Initial Setup](#initial-setup)
27
- - [Command Options](#command-options)
28
- - [--authenticated-resources](#--authenticated-resources)
29
- - [Version Creation](#version-creation)
30
- - [Controller Generation](#controller-generation)
31
- - [Command Options](#command-options-1)
32
- - [--attributes](#--attributes)
33
- - [--controller-actions](#--controller-actions)
34
- - [--version-number](#--version-number)
35
- - [--use-paginator](#--use-paginator)
36
- - [--allow-filters](#--allow-filters)
37
- - [--authenticate-with](#--authenticate-with)
38
- - [--owned-by-authenticated-resource](#--owned-by-authenticated-resource)
39
- - [--parent-resource](#--parent-resource)
40
- - [Inside the gem](#inside-the-gem)
41
- - [The Api::Error Concern](#the-apierror-concern)
42
- - [The Api::Deprecated Concern](#the-apideprecated-concern)
43
- - [The Api::Versioned Concern](#the-apiversioned-concern)
44
- - [The ApiResponder](#the-apiresponder)
45
- - [Testing](#testing)
46
- - [Contributing](#contributing)
47
- - [Credits](#credits)
48
- - [License](#license)
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 API versions. It is usually empty because most of the configuration comes in the `PowerApi::BaseController` that es inside the gem.
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
- class ActiveModelSerializers::Adapter::JsonApi
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
- ### Version Creation
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
- ```ruby
270
- Rails.application.routes.draw do
271
- scope path: '/api' do
272
- api_version(module: 'Api::V1', path: { value: 'v1' }, defaults: { format: 'json' }) do
273
- resources :blogs
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
- end
277
- ```
278
- - A controller under `/your_api/app/controllers/api/v1/blogs_controller.rb`
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
- - A serializer under `/your_api/app/serializers/api/v1/blog_serializer.rb`
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
- - A spec file under `/your_api/spec/integration/api/v1/blogs_spec.rb`
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)['data'].count).to eq(expected_collection_count)
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 creaed' do
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: :string, example: '1' },
433
- type: { type: :string, example: 'blog' },
434
- attributes: {
435
- type: :object,
436
- properties: {
437
- title: { type: :string, example: 'Some title', 'x-nullable': true },
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
- :id,
448
- :type,
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
- data: {
684
+ blogs: {
457
685
  type: "array",
458
686
  items: { "$ref" => "#/definitions/blog" }
459
687
  }
460
688
  },
461
689
  required: [
462
- :data
690
+ :blogs
463
691
  ]
464
692
  }
465
693
 
466
694
  BLOG_RESOURCE_SCHEMA = {
467
695
  type: "object",
468
696
  properties: {
469
- data: { "$ref" => "#/definitions/blog" }
697
+ blog: { "$ref" => "#/definitions/blog" }
470
698
  },
471
699
  required: [
472
- :data
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::BlogSerializer < Api::V1::BaseController
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 © 2019 platanus, spa. It is free software and may be redistributed under the terms specified in the LICENSE file.
1201
+ Power API is © 2022 platanus, spa. It is free software and may be redistributed under the terms specified in the LICENSE file.