apicasso 0.4.5 → 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
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