apicasso 0.4.5 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +226 -223
  3. data/Rakefile +23 -23
  4. data/app/controllers/apicasso/apidocs_controller.rb +309 -299
  5. data/app/controllers/apicasso/application_controller.rb +170 -147
  6. data/app/controllers/apicasso/crud_controller.rb +246 -245
  7. data/app/controllers/concerns/orderable.rb +45 -45
  8. data/app/models/apicasso/ability.rb +38 -38
  9. data/app/models/apicasso/application_record.rb +6 -6
  10. data/app/models/apicasso/key.rb +25 -25
  11. data/app/models/apicasso/request.rb +8 -8
  12. data/config/routes.rb +14 -14
  13. data/lib/apicasso/active_record_extension.rb +0 -44
  14. data/lib/apicasso/engine.rb +13 -13
  15. data/lib/apicasso/version.rb +3 -3
  16. data/lib/apicasso.rb +15 -13
  17. data/lib/generators/apicasso/install/install_generator.rb +25 -25
  18. data/lib/generators/apicasso/install/templates/create_apicasso_tables.rb +20 -20
  19. data/spec/dummy/Gemfile +56 -0
  20. data/spec/dummy/Gemfile.lock +237 -0
  21. data/spec/dummy/app/controllers/application_controller.rb +1 -1
  22. data/spec/dummy/app/models/used_model.rb +42 -0
  23. data/spec/dummy/app/serializers/used_model_serializer.rb +3 -0
  24. data/spec/dummy/bin/rails +5 -0
  25. data/spec/dummy/bin/rake +5 -0
  26. data/spec/dummy/bin/setup +0 -3
  27. data/spec/dummy/bin/spring +17 -0
  28. data/spec/dummy/bin/update +0 -3
  29. data/spec/dummy/config/application.rb +14 -10
  30. data/spec/dummy/config/cable.yml +1 -1
  31. data/spec/dummy/config/credentials.yml.enc +1 -0
  32. data/spec/dummy/config/database.yml +5 -14
  33. data/spec/dummy/config/environments/development.rb +6 -10
  34. data/spec/dummy/config/environments/production.rb +1 -10
  35. data/spec/dummy/config/initializers/cors.rb +16 -0
  36. data/spec/dummy/config/locales/en.yml +7 -32
  37. data/spec/dummy/config/routes.rb +1 -1
  38. data/{db/migrate/20180826141433_create_apicasso_tables.rb → spec/dummy/db/migrate/20180918134607_create_apicasso_tables.rb} +1 -0
  39. data/spec/dummy/db/migrate/20180918141254_create_used_models.rb +44 -0
  40. data/spec/dummy/db/migrate/20180919130152_create_active_storage_tables.active_storage.rb +26 -0
  41. data/spec/dummy/db/migrate/20180920133933_change_used_model_to_validates.rb +7 -0
  42. data/spec/dummy/db/schema.rb +98 -0
  43. data/spec/dummy/db/seeds.rb +56 -0
  44. data/spec/factories/used_model.rb +28 -0
  45. data/spec/models/used_model_spec.rb +35 -0
  46. data/spec/rails_helper.rb +66 -0
  47. data/spec/requests/requests_spec.rb +227 -0
  48. data/spec/spec_helper.rb +7 -9
  49. data/spec/support/factory_bot.rb +3 -0
  50. metadata +83 -64
  51. data/spec/controllers/apicasso/aplication_controller_spec.rb +0 -18
  52. data/spec/controllers/apicasso/crud_controller_spec.rb +0 -107
  53. data/spec/dummy/app/assets/config/manifest.js +0 -3
  54. data/spec/dummy/app/assets/javascripts/application.js +0 -15
  55. data/spec/dummy/app/assets/javascripts/cable.js +0 -13
  56. data/spec/dummy/app/assets/stylesheets/application.css +0 -15
  57. data/spec/dummy/app/channels/application_cable/channel.rb +0 -4
  58. data/spec/dummy/app/channels/application_cable/connection.rb +0 -4
  59. data/spec/dummy/app/helpers/application_helper.rb +0 -2
  60. data/spec/dummy/app/jobs/application_job.rb +0 -2
  61. data/spec/dummy/app/mailers/application_mailer.rb +0 -4
  62. data/spec/dummy/app/views/layouts/application.html.erb +0 -15
  63. data/spec/dummy/app/views/layouts/mailer.html.erb +0 -13
  64. data/spec/dummy/app/views/layouts/mailer.text.erb +0 -1
  65. data/spec/dummy/bin/yarn +0 -11
  66. data/spec/dummy/config/initializers/assets.rb +0 -14
  67. data/spec/dummy/config/initializers/content_security_policy.rb +0 -25
  68. data/spec/dummy/config/initializers/cookies_serializer.rb +0 -5
  69. data/spec/dummy/log/development.log +0 -0
  70. data/spec/dummy/public/404.html +0 -67
  71. data/spec/dummy/public/422.html +0 -67
  72. data/spec/dummy/public/500.html +0 -66
  73. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  74. data/spec/dummy/public/apple-touch-icon.png +0 -0
  75. data/spec/dummy/public/favicon.ico +0 -0
  76. data/spec/factories/apicasso_key.rb +0 -9
  77. data/spec/factories/object.rb +0 -5
  78. data/spec/models/apicasso/key.rb +0 -5
  79. data/spec/routing/appointments_routing_spec.rb +0 -38
@@ -3,14 +3,17 @@
3
3
  module Apicasso
4
4
  # Controller used to generate an application Swagger JSON, used by
5
5
  # SwaggerUI to generate beautiful API documentation
6
- class ApidocsController < ActionController::API
6
+ class ApidocsController < Apicasso::ApplicationController
7
+ skip_before_action :restrict_access
8
+
7
9
  include Swagger::Blocks
8
10
 
9
11
  swagger_root do
12
+ MODELS_EXCLUDED = [::ApplicationRecord, ActiveRecord::SchemaMigration, Apicasso::ApplicationRecord, Apicasso::Key, Apicasso::Request, Apicasso::ApidocsController, ActiveStorage::Attachment, ActiveStorage::Blob].freeze
10
13
  key :swagger, '2.0'
11
14
  info do
12
- key :version, ENV.fetch('VERSION', I18n.t('application.version'))
13
15
  key :title, ENV.fetch('APP_NAME', I18n.t('application.name'))
16
+ key 'x-logo', { url: I18n.t('app.logo.url', default: 'https://raw.githubusercontent.com/ErvalhouS/APIcasso/master/APIcasso.png'), altText: I18n.t('app.logo.alttext', default: 'Application Logo')}
14
17
  key :description, ENV.fetch('APP_DESCRIPTION', I18n.t('application.description'))
15
18
  key :termsOfService, I18n.t('application.terms_of_service')
16
19
  contact do
@@ -21,15 +24,23 @@ module Apicasso
21
24
  end
22
25
  end
23
26
  ActiveRecord::Base.descendants.each do |model|
24
- tag do
25
- key :name, I18n.t("activerecord.models.#{model.name.underscore}", default: model.name.underscore)
26
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.description", default: model.name)
27
+ unless MODELS_EXCLUDED.include?(model)
28
+ tag do
29
+ key :name, I18n.t("activerecord.models.#{model.name.underscore}", default: model.name.underscore)
30
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.description", default: model.name)
31
+ end
27
32
  end
28
33
  end
29
34
  key :host, I18n.t('application.apicasso_host', default: ENV.fetch('ROOT', 'localhost:3000'))
30
35
  key :basePath, I18n.t('application.apicasso_path', default: '/')
31
36
  key :consumes, ['application/json']
32
37
  key :produces, ['application/json']
38
+
39
+ security_definition :api_key do
40
+ key :type, :apiKey
41
+ key :name, :api_key
42
+ key :in, :header
43
+ end
33
44
  end
34
45
 
35
46
  # Eager load application to be able to list all models
@@ -40,7 +51,6 @@ module Apicasso
40
51
  *ActiveRecord::Base.descendants,
41
52
  self
42
53
  ].freeze
43
-
44
54
  swagger_schema :ErrorModel do
45
55
  key :required, [:code, :message]
46
56
  property :code do
@@ -52,277 +62,58 @@ module Apicasso
52
62
  end
53
63
  end
54
64
 
55
- ActiveRecord::Base.descendants do |model|
56
- swagger_path "/#{model.name.underscore}" do
57
- operation :get do
58
- key :summary, I18n.t("activerecord.models.#{model.name.underscore}.index.summary", default: model.name)
59
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.index.description", default: model.name)
60
- key :operationId, "find#{model.name.pluralize}"
61
- key :produces, ['application/json']
62
- key :tags, [model.name.underscore]
63
- parameter do
64
- key :name, :sort
65
- key :in, :query
66
- key :description, I18n.t('apicasso.sort.description',
67
- default: 'Parameters sorting splitted by `,` preffixed by `+` or `-` which translates into ascending or descending order')
68
- key :required, false
69
- key :type, :string
70
- key :collectionFormat, :json
71
- end
72
- parameter do
73
- key :name, :q
74
- key :in, :query
75
- key :description, I18n.t('apicasso.q.description',
76
- default: 'Records filtering by attribute and search query as affix. Usage: `?q[{attribute}{search_affix}]={matcher}`. All available search affixes are listed on: https://github.com/activerecord-hackery/ransack#search-matchers')
77
- key :required, false
78
- key :type, :json
79
- end
80
- parameter do
81
- key :name, :page
82
- key :in, :query
83
- key :description, I18n.t('apicasso.page.description',
84
- default: 'Records pagination paging, which offsets collection based on `params[:per_page]`')
85
- key :required, false
86
- key :type, :integer
87
- end
88
- parameter do
89
- key :name, :per_page
90
- key :in, :query
91
- key :description, I18n.t('apicasso.per_page.description',
92
- default: 'Records pagination size, which sets how many records will be rendered per request')
93
- key :required, false
94
- key :type, :integer
95
- end
96
- response 200 do
97
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.index.response",
98
- default: "#{model.name} response, which include records matching current query and pagination metadata")
99
- parameter do
100
- key :name, :total
101
- key :description, I18n.t('apicasso.total.description',
102
- default: 'Total records contained in current collection, as if there was no pagination.')
103
- key :required, true
104
- key :type, :integer
105
- end
106
- parameter do
107
- key :name, :total_pages
108
- key :description, I18n.t('apicasso.total_pages.description',
109
- default: 'How many pages of data the current collection has.')
110
- key :required, true
111
- key :type, :integer
112
- end
113
- parameter do
114
- key :name, :last_page
115
- key :description, I18n.t('apicasso.last_page.description',
116
- default: 'An indication if current request is the last to paginate in the current collection')
117
- key :required, true
118
- key :type, :boolean
119
- end
120
- parameter do
121
- key :name, :previous_page
122
- key :description, I18n.t('apicasso.previous_page.description',
123
- default: "The link of the previous page for the current collection. It can be null if there isn't any")
124
- key :required, false
125
- key :type, :string
126
- end
127
- parameter do
128
- key :name, :next_page
129
- key :description, I18n.t('apicasso.next_page.description',
130
- default: "The link of the next page for the current collection. It can be null if there isn't any")
131
- key :required, false
132
- key :type, :string
133
- end
134
- parameter do
135
- key :name, :out_of_bounds
136
- key :description, I18n.t('apicasso.out_of_bounds.description',
137
- default: 'An indication if current request is out of pagination bounds for the current collection')
138
- key :required, true
139
- key :type, :boolean
140
- end
141
- parameter do
142
- key :name, :offset
143
- key :description, I18n.t('apicasso.offset.description',
144
- default: 'How many records were offsetted from the collection to render the current page')
145
- key :required, true
146
- key :type, :integer
147
- end
148
- parameter do
149
- key :name, :entries
150
- key :description, I18n.t('apicasso.entries.description',
151
- default: 'The records collection in the current pagination scope.')
152
- key :required, true
153
- key :type, :array
154
- items do
155
- key :'$ref', model.name.to_sym
156
- end
157
- end
158
- end
159
- response :default do
160
- key :description, I18n.t("activerecord.errors.models.#{model.name.underscore}",
161
- default: "Unexpected error in #{model.name}")
162
- schema do
163
- key :'$ref', :ErrorModel
164
- end
165
- end
166
- end
167
- operation :options do
168
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.schema.description",
169
- default: "#{model.name} metadata information.")
170
- key :operationId, "schema#{model.name.pluralize}"
171
- key :produces, ['application/json']
172
- key :tags, [model.name.underscore]
173
- response 200 do
174
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.schema.response",
175
- default: "#{model.name} metadata as a json with field names as keys and field types as values.")
176
- schema do
177
- key :'$ref', "#{model.name}Metadata".to_sym
178
- end
179
- end
180
- response :default do
181
- key :description, I18n.t("activerecord.errors.models.#{model.name.underscore}",
182
- default: "Unexpected error in #{model.name}")
183
- schema do
184
- key :'$ref', :ErrorModel
65
+ SWAGGERED_CLASSES.each do |klass|
66
+ unless MODELS_EXCLUDED.include?(klass)
67
+ swagger_schema klass.name.to_sym do
68
+ key :required, klass.presence_validators if klass.presence_validators?
69
+ klass.columns_hash.each do |name, type|
70
+ property name.to_sym do
71
+ key :type, type.type
185
72
  end
186
73
  end
187
74
  end
188
- operation :post do
189
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.create.response",
190
- default: "Creates a #{model.name}")
191
- key :operationId, "add#{model.name}"
192
- key :produces, ['application/json']
193
- key :tags, [model.name.underscore]
194
- parameter do
195
- key :name, model.name.underscore.to_sym
196
- key :in, :body
197
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.create.description",
198
- default: "#{model.name} to add into application")
199
- key :required, true
75
+ swagger_schema "#{klass.name}Input".to_sym do
76
+ allOf do
200
77
  schema do
201
- key :'$ref', "#{model.name}Input".to_sym
78
+ key :'$ref', "#{klass.name}Input".to_sym
202
79
  end
203
- end
204
- response 201 do
205
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.show.description",
206
- default: "#{model.name} response")
207
- schema do
208
- key :'$ref', model.name.to_sym
209
- end
210
- end
211
- response :default do
212
- key :description, I18n.t("activerecord.errors.models.#{model.name.underscore}",
213
- default: "Unexpected error in #{model.name}")
214
- schema do
215
- key :'$ref', :ErrorModel
216
- end
217
- end
218
- end
219
- end
220
- swagger_path "/#{model.name.underscore}/{id}" do
221
- operation :patch do
222
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.update.response",
223
- default: "Updates a #{model.name}")
224
- key :operationId, "edit#{model.name}"
225
- key :produces, ['application/json']
226
- key :tags, [model.name.underscore]
227
- parameter do
228
- key :name, :id
229
- key :in, :path
230
- key :description, I18n.t("activerecord.models.attributes.#{model.name.underscore}.id",
231
- default: "ID of #{model.name} to update on the application")
232
- key :required, true
233
80
  schema do
234
- key :'$ref', "#{model.name}Input".to_sym
235
- end
236
- end
237
- parameter do
238
- key :name, model.name.underscore.to_sym
239
- key :in, :body
240
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.update.description",
241
- default: "Existing #{model.name} to update on the application")
242
- key :required, true
243
- schema do
244
- key :'$ref', "#{model.name}Input".to_sym
245
- end
246
- end
247
- response 200 do
248
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.show.description",
249
- default: "#{model.name} response")
250
- schema do
251
- key :'$ref', model.name.to_sym
252
- end
253
- end
254
- end
255
- operation :get do
256
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.show.response",
257
- default: "Creates a #{model.name}")
258
- key :operationId, "show#{model.name}"
259
- key :produces, ['application/json']
260
- key :tags, [model.name.underscore]
261
- parameter do
262
- key :name, :id
263
- key :in, :path
264
- key :description, I18n.t("activerecord.models.attributes.#{model.name.underscore}.id",
265
- default: "ID of #{model.name} to fetch on the application")
266
- key :required, true
267
- schema do
268
- key :'$ref', "#{model.name}Input".to_sym
269
- end
270
- end
271
- response 200 do
272
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.show.description",
273
- default: "#{model.name} response")
274
- schema do
275
- key :'$ref', model.name.to_sym
276
- end
277
- end
278
- response :default do
279
- key :description, I18n.t("activerecord.errors.models.#{model.name.underscore}",
280
- default: "Unexpected error in #{model.name}")
281
- schema do
282
- key :'$ref', :ErrorModel
81
+ key :required, %i[*presence_validators] if klass.presence_validators?
82
+ klass.columns_hash.each do |name, type|
83
+ property name.to_sym do
84
+ key :type, type.type
85
+ end
86
+ end
283
87
  end
284
88
  end
285
89
  end
286
- operation :delete do
287
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.destroy.response",
288
- default: "Deletes a #{model.name}")
289
- key :operationId, "destroy#{model.name}"
290
- key :produces, ['application/json']
291
- key :tags, [model.name.underscore]
292
- parameter do
293
- key :name, :id
294
- key :in, :path
295
- key :description, I18n.t("activerecord.models.attributes.#{model.name.underscore}.id",
296
- default: "ID of #{model.name} to delete on the application")
297
- key :required, true
90
+ swagger_schema "#{klass.name}Metadata".to_sym do
91
+ allOf do
298
92
  schema do
299
- key :'$ref', "#{model.name}Input".to_sym
93
+ key :'$ref', "#{klass.name}Metadata".to_sym
300
94
  end
301
- end
302
- response 200 do
303
- key :description, I18n.t("activerecord.models.#{model.name.underscore}.destroy.description",
304
- default: "#{model.name} response")
305
- end
306
- response :default do
307
- key :description, I18n.t("activerecord.errors.models.#{model.name.underscore}",
308
- default: "Unexpected error in #{model.name}")
309
95
  schema do
310
- key :'$ref', :ErrorModel
96
+ klass.columns_hash.each do |name, type|
97
+ property name.to_sym do
98
+ key :description, type.type
99
+ key :type, :string
100
+ end
101
+ end
311
102
  end
312
103
  end
313
104
  end
314
105
  end
106
+ end
315
107
 
316
- model.reflect_on_all_associations.map(&:name).each do |association|
317
- inner_name = extract_klass_name(model: model, association: association)
318
- inner_klass = extract_klass(model: model, association: association)
319
- swagger_path "/#{model.name.underscore}/{id}/#{association}" do
108
+ ActiveRecord::Base.descendants.each do |model|
109
+ unless MODELS_EXCLUDED.include?(model)
110
+ swagger_path "/#{model.name.underscore}" do
320
111
  operation :get do
321
- key :summary, I18n.t("activerecord.models.#{inner_name.underscore}.index.summary", default: inner_name)
322
- key :description, I18n.t("activerecord.models.#{inner_name.underscore}.index.description", default: inner_name)
323
- key :operationId, "find#{inner_name.pluralize}"
112
+ key :summary, I18n.t("activerecord.models.#{model.name.underscore}.index.summary", default: model.name)
113
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.index.description", default: model.name)
114
+ key :operationId, "find#{model.name.pluralize}"
324
115
  key :produces, ['application/json']
325
- key :tags, [inner_name.underscore]
116
+ key :tags, [model.name.underscore]
326
117
  parameter do
327
118
  key :name, :sort
328
119
  key :in, :query
@@ -357,94 +148,326 @@ module Apicasso
357
148
  key :type, :integer
358
149
  end
359
150
  response 200 do
360
- key :description, I18n.t("activerecord.models.#{inner_name.underscore}.index.response",
361
- default: "#{inner_name} response, which include records matching current query and pagination metadata")
362
- parameter do
151
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.index.response",
152
+ default: "#{model.name} response, which include records matching current query and pagination metadata")
153
+ schema do
363
154
  key :name, :total
364
155
  key :description, I18n.t('apicasso.total.description',
365
156
  default: 'Total records contained in current collection, as if there was no pagination.')
366
157
  key :required, true
367
158
  key :type, :integer
368
159
  end
369
- parameter do
160
+ schema do
370
161
  key :name, :total_pages
371
162
  key :description, I18n.t('apicasso.total_pages.description',
372
163
  default: 'How many pages of data the current collection has.')
373
164
  key :required, true
374
165
  key :type, :integer
375
166
  end
376
- parameter do
167
+ schema do
377
168
  key :name, :last_page
378
169
  key :description, I18n.t('apicasso.last_page.description',
379
170
  default: 'An indication if current request is the last to paginate in the current collection')
380
171
  key :required, true
381
172
  key :type, :boolean
382
173
  end
383
- parameter do
174
+ schema do
384
175
  key :name, :previous_page
385
176
  key :description, I18n.t('apicasso.previous_page.description',
386
177
  default: "The link of the previous page for the current collection. It can be null if there isn't any")
387
178
  key :required, false
388
179
  key :type, :string
389
180
  end
390
- parameter do
181
+ schema do
391
182
  key :name, :next_page
392
183
  key :description, I18n.t('apicasso.next_page.description',
393
184
  default: "The link of the next page for the current collection. It can be null if there isn't any")
394
185
  key :required, false
395
186
  key :type, :string
396
187
  end
397
- parameter do
188
+ schema do
398
189
  key :name, :out_of_bounds
399
190
  key :description, I18n.t('apicasso.out_of_bounds.description',
400
191
  default: 'An indication if current request is out of pagination bounds for the current collection')
401
192
  key :required, true
402
193
  key :type, :boolean
403
194
  end
404
- parameter do
195
+ schema do
405
196
  key :name, :offset
406
197
  key :description, I18n.t('apicasso.offset.description',
407
198
  default: 'How many records were offsetted from the collection to render the current page')
408
199
  key :required, true
409
200
  key :type, :integer
410
201
  end
411
- parameter do
202
+ schema do
412
203
  key :name, :entries
413
204
  key :description, I18n.t('apicasso.entries.description',
414
205
  default: 'The records collection in the current pagination scope.')
415
206
  key :required, true
416
207
  key :type, :array
417
208
  items do
418
- key :'$ref', inner_name.to_sym
209
+ key :'$ref', model.name.to_sym
419
210
  end
420
211
  end
421
212
  end
422
- response :default do
423
- key :description, I18n.t("activerecord.errors.models.#{inner_name.underscore}",
424
- default: "Unexpected error in #{inner_name}")
213
+ end
214
+ operation :options do
215
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.schema.description",
216
+ default: "#{model.name} metadata information.")
217
+ key :operationId, "schema#{model.name.pluralize}"
218
+ key :produces, ['application/json']
219
+ key :tags, [model.name.underscore]
220
+ response 200 do
221
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.schema.response",
222
+ default: "#{model.name} metadata as a json with field names as keys and field types as values.")
425
223
  schema do
426
- key :'$ref', :ErrorModel
224
+ key :'$ref', "#{model.name}".to_sym
427
225
  end
428
226
  end
429
227
  end
430
- operation :options do
431
- key :description, I18n.t("activerecord.models.#{inner_name.underscore}.schema.description",
432
- default: "#{inner_name} metadata information.")
433
- key :operationId, "schema#{inner_name.pluralize}"
228
+ operation :post do
229
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.create.response",
230
+ default: "Creates a #{model.name}")
231
+ key :operationId, "add#{model.name}"
434
232
  key :produces, ['application/json']
435
- key :tags, [inner_name.underscore]
233
+ key :tags, [model.name.underscore]
234
+ parameter do
235
+ key :name, model.name.underscore.to_sym
236
+ key :in, :body
237
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.create.description",
238
+ default: "#{model.name} to add into application")
239
+ key :required, true
240
+ schema do
241
+ key :'$ref', "#{model.name}".to_sym
242
+ end
243
+ end
244
+ response 201 do
245
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.show.description",
246
+ default: "#{model.name} response")
247
+ schema do
248
+ key :'$ref', model.name.to_sym
249
+ end
250
+ end
251
+ end
252
+ end
253
+ swagger_path "/#{model.name.underscore}/{id}" do
254
+ operation :patch do
255
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.update.response",
256
+ default: "Updates a #{model.name}")
257
+ key :operationId, "edit#{model.name}"
258
+ key :produces, ['application/json']
259
+ key :tags, [model.name.underscore]
260
+ parameter do
261
+ key :name, :id
262
+ key :in, :path
263
+ key :description, I18n.t("activerecord.models.attributes.#{model.name.underscore}.id",
264
+ default: "ID of #{model.name} to update on the application")
265
+ key :required, true
266
+ schema do
267
+ key :'$ref', "#{model.name}".to_sym
268
+ end
269
+ end
270
+ parameter do
271
+ key :name, model.name.underscore.to_sym
272
+ key :in, :body
273
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.update.description",
274
+ default: "Existing #{model.name} to update on the application")
275
+ key :required, true
276
+ schema do
277
+ key :'$ref', "#{model.name}".to_sym
278
+ end
279
+ end
436
280
  response 200 do
437
- key :description, I18n.t("activerecord.models.#{inner_name.underscore}.schema.response",
438
- default: "#{inner_name} metadata as a json with field names as keys and field types as values.")
281
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.show.description",
282
+ default: "#{model.name} response")
439
283
  schema do
440
- key :'$ref', "#{inner_name}Metadata".to_sym
284
+ key :'$ref', model.name.to_sym
441
285
  end
442
286
  end
443
- response :default do
444
- key :description, I18n.t("activerecord.errors.models.#{inner_name.underscore}",
445
- default: "Unexpected error in #{inner_name}")
287
+ end
288
+ operation :get do
289
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.show.response",
290
+ default: "Creates a #{model.name}")
291
+ key :operationId, "show#{model.name}"
292
+ key :produces, ['application/json']
293
+ key :tags, [model.name.underscore]
294
+ parameter do
295
+ key :name, :id
296
+ key :in, :path
297
+ key :description, I18n.t("activerecord.models.attributes.#{model.name.underscore}.id",
298
+ default: "ID of #{model.name} to fetch on the application")
299
+ key :required, true
446
300
  schema do
447
- key :'$ref', :ErrorModel
301
+ key :'$ref', "#{model.name}".to_sym
302
+ end
303
+ end
304
+ response 200 do
305
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.show.description",
306
+ default: "#{model.name} response")
307
+ schema do
308
+ key :'$ref', model.name.to_sym
309
+ end
310
+ end
311
+ end
312
+ operation :delete do
313
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.destroy.response",
314
+ default: "Deletes a #{model.name}")
315
+ key :operationId, "destroy#{model.name}"
316
+ key :produces, ['application/json']
317
+ key :tags, [model.name.underscore]
318
+ parameter do
319
+ key :name, :id
320
+ key :in, :path
321
+ key :description, I18n.t("activerecord.models.attributes.#{model.name.underscore}.id",
322
+ default: "ID of #{model.name} to delete on the application")
323
+ key :required, true
324
+ schema do
325
+ key :'$ref', "#{model.name}".to_sym
326
+ end
327
+ end
328
+ response 200 do
329
+ key :description, I18n.t("activerecord.models.#{model.name.underscore}.destroy.description",
330
+ default: "#{model.name} response")
331
+ end
332
+ end
333
+ end
334
+
335
+ model.reflect_on_all_associations.map(&:name).each do |association|
336
+ inner_name = model.reflect_on_all_associations.select{ |ass| ass.name == association }.first.class_name
337
+ ASSOCIATION_EXCLUDED = ['ActiveStorage::Attachment', 'ActiveStorage::Blob'].freeze
338
+ unless ASSOCIATION_EXCLUDED.include?(inner_name)
339
+ inner_klass = begin inner_name.constantize rescue NameError; false end
340
+ swagger_path "/#{model.name.underscore}/{id}/#{association}" do
341
+ operation :get do
342
+ key :summary, I18n.t("activerecord.models.#{inner_name.underscore}.index.summary", default: inner_name)
343
+ key :description, I18n.t("activerecord.models.#{inner_name.underscore}.index.description", default: inner_name)
344
+ key :operationId, "find#{inner_name.pluralize}"
345
+ key :produces, ['application/json']
346
+ key :tags, [inner_name.underscore]
347
+ parameter do
348
+ key :name, :sort
349
+ key :in, :query
350
+ key :description, I18n.t('apicasso.sort.description',
351
+ default: 'Parameters sorting splitted by `,` preffixed by `+` or `-` which translates into ascending or descending order')
352
+ key :required, false
353
+ key :type, :string
354
+ key :collectionFormat, :json
355
+ end
356
+ parameter do
357
+ key :name, :q
358
+ key :in, :query
359
+ key :description, I18n.t('apicasso.q.description',
360
+ default: 'Records filtering by attribute and search query as affix. Usage: `?q[{attribute}{search_affix}]={matcher}`. All available search affixes are listed on: https://github.com/activerecord-hackery/ransack#search-matchers')
361
+ key :required, false
362
+ key :type, :json
363
+ end
364
+ parameter do
365
+ key :name, :page
366
+ key :in, :query
367
+ key :description, I18n.t('apicasso.page.description',
368
+ default: 'Records pagination paging, which offsets collection based on `params[:per_page]`')
369
+ key :required, false
370
+ key :type, :integer
371
+ end
372
+ parameter do
373
+ key :name, :per_page
374
+ key :in, :query
375
+ key :description, I18n.t('apicasso.per_page.description',
376
+ default: 'Records pagination size, which sets how many records will be rendered per request')
377
+ key :required, false
378
+ key :type, :integer
379
+ end
380
+ response 200 do
381
+ key :description, I18n.t("activerecord.models.#{inner_name.underscore}.index.response",
382
+ default: "#{inner_name} response, which include records matching current query and pagination metadata")
383
+ schema do
384
+ key :name, :total
385
+ key :description, I18n.t('apicasso.total.description',
386
+ default: 'Total records contained in current collection, as if there was no pagination.')
387
+ key :required, true
388
+ key :type, :integer
389
+ end
390
+ schema do
391
+ key :name, :total_pages
392
+ key :description, I18n.t('apicasso.total_pages.description',
393
+ default: 'How many pages of data the current collection has.')
394
+ key :required, true
395
+ key :type, :integer
396
+ end
397
+ schema do
398
+ key :name, :last_page
399
+ key :description, I18n.t('apicasso.last_page.description',
400
+ default: 'An indication if current request is the last to paginate in the current collection')
401
+ key :required, true
402
+ key :type, :boolean
403
+ end
404
+ schema do
405
+ key :name, :previous_page
406
+ key :description, I18n.t('apicasso.previous_page.description',
407
+ default: "The link of the previous page for the current collection. It can be null if there isn't any")
408
+ key :required, false
409
+ key :type, :string
410
+ end
411
+ schema do
412
+ key :name, :next_page
413
+ key :description, I18n.t('apicasso.next_page.description',
414
+ default: "The link of the next page for the current collection. It can be null if there isn't any")
415
+ key :required, false
416
+ key :type, :string
417
+ end
418
+ schema do
419
+ key :name, :out_of_bounds
420
+ key :description, I18n.t('apicasso.out_of_bounds.description',
421
+ default: 'An indication if current request is out of pagination bounds for the current collection')
422
+ key :required, true
423
+ key :type, :boolean
424
+ end
425
+ schema do
426
+ key :name, :offset
427
+ key :description, I18n.t('apicasso.offset.description',
428
+ default: 'How many records were offsetted from the collection to render the current page')
429
+ key :required, true
430
+ key :type, :integer
431
+ end
432
+ schema do
433
+ key :name, :entries
434
+ key :description, I18n.t('apicasso.entries.description',
435
+ default: 'The records collection in the current pagination scope.')
436
+ key :required, true
437
+ key :type, :array
438
+ items do
439
+ key :'$ref', "#{inner_name}".to_sym
440
+ end
441
+ end
442
+ end
443
+ response :default do
444
+ key :description, I18n.t("activerecord.errors.models.#{inner_name.underscore}",
445
+ default: "Unexpected error in #{inner_name}")
446
+ schema do
447
+ key :'$ref', :ErrorModel
448
+ end
449
+ end
450
+ end
451
+ operation :options do
452
+ key :description, I18n.t("activerecord.models.#{inner_name.underscore}.schema.description",
453
+ default: "#{inner_name} metadata information.")
454
+ key :operationId, "schema#{inner_name.pluralize}"
455
+ key :produces, ['application/json']
456
+ key :tags, [inner_name.underscore]
457
+ response 200 do
458
+ key :description, I18n.t("activerecord.models.#{inner_name.underscore}.schema.response",
459
+ default: "#{inner_name} metadata as a json with field names as keys and field types as values.")
460
+ schema do
461
+ key :'$ref', "#{inner_name}".to_sym
462
+ end
463
+ end
464
+ response :default do
465
+ key :description, I18n.t("activerecord.errors.models.#{inner_name.underscore}",
466
+ default: "Unexpected error in #{inner_name}")
467
+ schema do
468
+ key :'$ref', :ErrorModel
469
+ end
470
+ end
448
471
  end
449
472
  end
450
473
  end
@@ -453,20 +476,7 @@ module Apicasso
453
476
  end
454
477
 
455
478
  def index
456
- render json: Swagger::Blocks.build_root_json(SWAGGERED_CLASSES)
457
- end
458
-
459
- private
460
-
461
- def extract_klass_name(opts = {})
462
- association = opts[:model].reflect_on_all_associations.select{ |ass| ass.name == opts[:association] }.first
463
- association.class_name
464
- end
465
-
466
- def extract_klass(opts = {})
467
- extract_klass_name(opts).constantize
468
- rescue NameError
469
- return false
479
+ render json: Swagger::Blocks.build_root_json(SWAGGERED_CLASSES).to_json
470
480
  end
471
481
  end
472
482
  end