model_driven_api 3.1.1 → 3.1.4
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe66f672447c0aa658740f932e8245bf57a02fc0531e79520e5c426410e2c9a3
|
4
|
+
data.tar.gz: 131b5eb93497c89b0d03506398e6beb2ac3b2fb5b74d8af19b7488151eb4b1cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22ddc373d73b8c08c0dd44f735c7c8faccea38850557d51418ca1d07e4cb9eb393df2fb9fa51f491074cefec20a55b4ede0c98eff1dbfbd8d8081e56c935ebcd
|
7
|
+
data.tar.gz: 103e01d9c18ab24e997bf10b2212cc8fddf56fbc3764c591ce72444aacb79c1b75c139e02d5bf74b825dc25839df0c4182d60c9417d515ea266ee2f505e5cfc6
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# require 'model_driven_api/version'
|
2
2
|
class Api::V2::InfoController < Api::V2::ApplicationController
|
3
3
|
# Info uses a different auth method: username and password
|
4
|
-
skip_before_action :authenticate_request, only: [:version], raise: false
|
4
|
+
skip_before_action :authenticate_request, only: [:version, :swagger, :openapi], raise: false
|
5
5
|
skip_before_action :extract_model
|
6
6
|
|
7
7
|
# api :GET, '/api/v2/info/version', "Just prints the APPVERSION."
|
@@ -57,13 +57,760 @@ class Api::V2::InfoController < Api::V2::ApplicationController
|
|
57
57
|
render json: pivot.to_json, status: 200
|
58
58
|
end
|
59
59
|
|
60
|
+
def compute_type(model, key)
|
61
|
+
# if it's a file, a date or a text, then return string
|
62
|
+
instance = model.new
|
63
|
+
# If it's a method, it is a peculiar case, in which we have to return "object" and additionalProperties: true
|
64
|
+
return "method" if model.methods.include?(:json_attrs) && model.json_attrs && model.json_attrs.include?(:methods) && model.json_attrs[:methods].include?(key.to_sym)
|
65
|
+
# If it's not the case of a method, then it's a field
|
66
|
+
method_class = instance.send(key).class.to_s
|
67
|
+
method_key = model.columns_hash[key]
|
68
|
+
|
69
|
+
# Not columns
|
70
|
+
return "object" if method_class == "ActiveStorage::Attached::One"
|
71
|
+
return "array" if method_class == "ActiveStorage::Attached::Many" || method_class == "Array" || method_class.ends_with?("Array") || method_class.ends_with?("Collection") || method_class.ends_with?("Relation") || method_class.ends_with?("Set") || method_class.ends_with?("List") || method_class.ends_with?("Queue") || method_class.ends_with?("Stack") || method_class.ends_with?("ActiveRecord_Associations_CollectionProxy")
|
72
|
+
|
73
|
+
# Columns
|
74
|
+
case method_key.type
|
75
|
+
when :json, :jsonb
|
76
|
+
return "object"
|
77
|
+
when :enum
|
78
|
+
return "array"
|
79
|
+
when :text, :hstore
|
80
|
+
return "string"
|
81
|
+
when :decimal, :float, :bigint
|
82
|
+
return "number"
|
83
|
+
end
|
84
|
+
method_key.type.to_s
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_properties_from_model(model, dsl)
|
88
|
+
JSON.parse(model.new.to_json(dsl)).keys.map do |k|
|
89
|
+
# Rails.logger.debug "###################### Model #{model.model_name.human rescue ""} Key #{k}"
|
90
|
+
type = compute_type(model, k)
|
91
|
+
# Rails.logger.debug "###################### Model #{model.model_name.human rescue ""} Type for #{k} is #{type}"
|
92
|
+
if type == "method"
|
93
|
+
[k, { "type": "object", "additionalProperties": true }]
|
94
|
+
elsif type == "date"
|
95
|
+
[k, { "type": "string", "format": "date" }]
|
96
|
+
elsif type == "datetime"
|
97
|
+
[k, { "type": "string", "format": "date-time" }]
|
98
|
+
elsif type == "object" && (k.classify.constantize rescue false)
|
99
|
+
sub_model = k.classify.constantize
|
100
|
+
properties = dsl[:include].present? && dsl[:include].include?(k) ? create_properties_from_model(sub_model, dsl[:include][k.to_sym]) : create_properties_from_model(sub_model, {})
|
101
|
+
[k, { "type": "object", "properties": properties }] rescue nil
|
102
|
+
elsif type == "array" && (k.classify.constantize rescue false)
|
103
|
+
sub_model = k.classify.constantize
|
104
|
+
properties = dsl[:include].present? && dsl[:include].include?(k) ? create_properties_from_model(sub_model, dsl[:include][k.to_sym]) : create_properties_from_model(sub_model, {})
|
105
|
+
[k, { "type": "array", "items": { "type": "object", "properties": properties } }] rescue nil
|
106
|
+
else
|
107
|
+
[k, { "type": type }]
|
108
|
+
end
|
109
|
+
end.compact.to_h
|
110
|
+
end
|
111
|
+
|
112
|
+
def generate_paths
|
113
|
+
pivot = {
|
114
|
+
"/authenticate": {
|
115
|
+
"post": {
|
116
|
+
"summary": "Authenticate",
|
117
|
+
"tags": ["Authentication"],
|
118
|
+
"description": "Authenticate the user and return a JWT token in the header and the current user as body",
|
119
|
+
"security": [
|
120
|
+
"basicAuth": []
|
121
|
+
],
|
122
|
+
"requestBody": {
|
123
|
+
"content": {
|
124
|
+
"application/json": {
|
125
|
+
"schema": {
|
126
|
+
"type": "object",
|
127
|
+
"properties": {
|
128
|
+
"auth": {
|
129
|
+
"type": "object",
|
130
|
+
"properties": {
|
131
|
+
"email": {
|
132
|
+
"type": "string",
|
133
|
+
"format": "email"
|
134
|
+
},
|
135
|
+
"password": {
|
136
|
+
"type": "string",
|
137
|
+
"format": "password"
|
138
|
+
}
|
139
|
+
}
|
140
|
+
}
|
141
|
+
},
|
142
|
+
"required": ["email", "password"]
|
143
|
+
}
|
144
|
+
}
|
145
|
+
}
|
146
|
+
},
|
147
|
+
"responses": {
|
148
|
+
"200": {
|
149
|
+
"description": "User authenticated",
|
150
|
+
"headers": {
|
151
|
+
"token": {
|
152
|
+
"description": "JWT",
|
153
|
+
"schema": {
|
154
|
+
"type": "string"
|
155
|
+
}
|
156
|
+
}
|
157
|
+
},
|
158
|
+
"content": {
|
159
|
+
"application/json": {
|
160
|
+
"schema": {
|
161
|
+
"type": "object",
|
162
|
+
# ["id", "email", "created_at", "admin", "locked", "supplier_id", "location_id", "roles"]
|
163
|
+
"properties": create_properties_from_model(User, User.json_attrs)
|
164
|
+
}
|
165
|
+
}
|
166
|
+
}
|
167
|
+
},
|
168
|
+
"401": {
|
169
|
+
"description": "Unauthorized"
|
170
|
+
}
|
171
|
+
}
|
172
|
+
}
|
173
|
+
},
|
174
|
+
"/info/version": {
|
175
|
+
"get": {
|
176
|
+
"summary": "Version",
|
177
|
+
"description": "Just prints the APPVERSION",
|
178
|
+
"tags": ["Info"],
|
179
|
+
"responses": {
|
180
|
+
"200": {
|
181
|
+
"description": "APPVERSION",
|
182
|
+
"content": {
|
183
|
+
"application/json": {
|
184
|
+
"schema": {
|
185
|
+
"type": "string"
|
186
|
+
}
|
187
|
+
}
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}
|
191
|
+
}
|
192
|
+
},
|
193
|
+
"/info/heartbeat": {
|
194
|
+
"get": {
|
195
|
+
"summary": "Heartbeat",
|
196
|
+
"description": "Just keeps the session alive by returning a new token",
|
197
|
+
"tags": ["Info"],
|
198
|
+
"security": [
|
199
|
+
"bearerAuth": []
|
200
|
+
],
|
201
|
+
"responses": {
|
202
|
+
"200": {
|
203
|
+
"description": "Session alive",
|
204
|
+
"headers": {
|
205
|
+
"token": {
|
206
|
+
"description": "JWT",
|
207
|
+
"schema": {
|
208
|
+
"type": "string"
|
209
|
+
}
|
210
|
+
}
|
211
|
+
}
|
212
|
+
}
|
213
|
+
}
|
214
|
+
}
|
215
|
+
},
|
216
|
+
"/info/roles": {
|
217
|
+
"get": {
|
218
|
+
"summary": "Roles",
|
219
|
+
"description": "Returns the roles list",
|
220
|
+
"tags": ["Info"],
|
221
|
+
"security": [
|
222
|
+
"bearerAuth": []
|
223
|
+
],
|
224
|
+
"responses": {
|
225
|
+
"200": {
|
226
|
+
"description": "Roles list",
|
227
|
+
"content": {
|
228
|
+
"application/json": {
|
229
|
+
"schema": {
|
230
|
+
"type": "array",
|
231
|
+
"items": {
|
232
|
+
"type": "object",
|
233
|
+
"properties": {
|
234
|
+
"id": {
|
235
|
+
"type": "integer"
|
236
|
+
},
|
237
|
+
"name": {
|
238
|
+
"type": "string"
|
239
|
+
},
|
240
|
+
"description": {
|
241
|
+
"type": "string"
|
242
|
+
}
|
243
|
+
}
|
244
|
+
}
|
245
|
+
}
|
246
|
+
}
|
247
|
+
}
|
248
|
+
}
|
249
|
+
}
|
250
|
+
}
|
251
|
+
},
|
252
|
+
"/info/schema": {
|
253
|
+
"get": {
|
254
|
+
"summary": "Schema",
|
255
|
+
"description": "Returns the schema of the models",
|
256
|
+
"tags": ["Info"],
|
257
|
+
"security": [
|
258
|
+
"bearerAuth": []
|
259
|
+
],
|
260
|
+
"responses": {
|
261
|
+
"200": {
|
262
|
+
"description": "Schema of the models",
|
263
|
+
"content": {
|
264
|
+
"application/json": {
|
265
|
+
"schema": {
|
266
|
+
"type": "array",
|
267
|
+
"items": {
|
268
|
+
"type": "object",
|
269
|
+
"properties": {
|
270
|
+
"id": {
|
271
|
+
"type": "integer"
|
272
|
+
},
|
273
|
+
"created_at": {
|
274
|
+
"type": "string",
|
275
|
+
"format": "date-time"
|
276
|
+
},
|
277
|
+
"updated_at": {
|
278
|
+
"type": "string",
|
279
|
+
"format": "date-time"
|
280
|
+
},
|
281
|
+
}
|
282
|
+
}
|
283
|
+
}
|
284
|
+
}
|
285
|
+
}
|
286
|
+
}
|
287
|
+
}
|
288
|
+
}
|
289
|
+
},
|
290
|
+
"/info/dsl": {
|
291
|
+
"get": {
|
292
|
+
"summary": "DSL",
|
293
|
+
"description": "Returns the DSL of the models",
|
294
|
+
"tags": ["Info"],
|
295
|
+
"security": [
|
296
|
+
"bearerAuth": []
|
297
|
+
],
|
298
|
+
"responses": {
|
299
|
+
"200": {
|
300
|
+
"description": "DSL of the models",
|
301
|
+
"content": {
|
302
|
+
"application/json": {
|
303
|
+
"schema": {
|
304
|
+
"type": "object",
|
305
|
+
"properties": {
|
306
|
+
"id": {
|
307
|
+
"type": "integer"
|
308
|
+
},
|
309
|
+
"created_at": {
|
310
|
+
"type": "string",
|
311
|
+
"format": "date-time"
|
312
|
+
},
|
313
|
+
"updated_at": {
|
314
|
+
"type": "string",
|
315
|
+
"format": "date-time"
|
316
|
+
}
|
317
|
+
}
|
318
|
+
}
|
319
|
+
}
|
320
|
+
}
|
321
|
+
}
|
322
|
+
}
|
323
|
+
}
|
324
|
+
},
|
325
|
+
"/info/translations": {
|
326
|
+
"get": {
|
327
|
+
"summary": "Translations",
|
328
|
+
"description": "Returns the translations of the entire App",
|
329
|
+
"tags": ["Info"],
|
330
|
+
"security": [
|
331
|
+
"bearerAuth": []
|
332
|
+
],
|
333
|
+
"responses": {
|
334
|
+
"200": {
|
335
|
+
"description": "Translations",
|
336
|
+
"content": {
|
337
|
+
"application/json": {
|
338
|
+
"schema": {
|
339
|
+
"type": "object",
|
340
|
+
"properties": {
|
341
|
+
"key": {
|
342
|
+
"type": "string"
|
343
|
+
},
|
344
|
+
"value": {
|
345
|
+
"type": "string"
|
346
|
+
}
|
347
|
+
}
|
348
|
+
}
|
349
|
+
}
|
350
|
+
}
|
351
|
+
}
|
352
|
+
}
|
353
|
+
}
|
354
|
+
},
|
355
|
+
"/info/settings": {
|
356
|
+
"get": {
|
357
|
+
"summary": "Settings",
|
358
|
+
"description": "Returns the settings of the App",
|
359
|
+
"tags": ["Info"],
|
360
|
+
"security": [
|
361
|
+
"bearerAuth": []
|
362
|
+
],
|
363
|
+
"responses": {
|
364
|
+
"200": {
|
365
|
+
"description": "Settings",
|
366
|
+
"content": {
|
367
|
+
"application/json": {
|
368
|
+
"schema": {
|
369
|
+
"type": "object",
|
370
|
+
"properties": {
|
371
|
+
"ns": {
|
372
|
+
"type": "object",
|
373
|
+
"properties": {
|
374
|
+
"key": {
|
375
|
+
"type": "string"
|
376
|
+
},
|
377
|
+
"value": {
|
378
|
+
"type": "string"
|
379
|
+
}
|
380
|
+
}
|
381
|
+
}
|
382
|
+
}
|
383
|
+
}
|
384
|
+
}
|
385
|
+
}
|
386
|
+
}
|
387
|
+
}
|
388
|
+
}
|
389
|
+
},
|
390
|
+
"/info/swagger": {
|
391
|
+
"get": {
|
392
|
+
"summary": "Swagger",
|
393
|
+
"description": "Returns the Swagger",
|
394
|
+
"tags": ["Info"],
|
395
|
+
"responses": {
|
396
|
+
"200": {
|
397
|
+
"description": "Swagger",
|
398
|
+
"content": {
|
399
|
+
"application/json": {
|
400
|
+
"schema": {
|
401
|
+
"type": "object",
|
402
|
+
"properties": {
|
403
|
+
"id": {
|
404
|
+
"type": "integer"
|
405
|
+
},
|
406
|
+
"created_at": {
|
407
|
+
"type": "string",
|
408
|
+
"format": "date-time"
|
409
|
+
},
|
410
|
+
"updated_at": {
|
411
|
+
"type": "string",
|
412
|
+
"format": "date-time"
|
413
|
+
}
|
414
|
+
}
|
415
|
+
}
|
416
|
+
}
|
417
|
+
}
|
418
|
+
}
|
419
|
+
}
|
420
|
+
}
|
421
|
+
}
|
422
|
+
}
|
423
|
+
ApplicationRecord.subclasses.each do |d|
|
424
|
+
# Only if current user can read the model
|
425
|
+
if true # can? :read, d
|
426
|
+
model = d.to_s.underscore.tableize
|
427
|
+
# CRUD and Search endpoints
|
428
|
+
pivot["/#{model}"] = {
|
429
|
+
"get": {
|
430
|
+
"summary": "Index",
|
431
|
+
"description": "Returns the list of #{model}",
|
432
|
+
"tags": [model.classify],
|
433
|
+
"security": [
|
434
|
+
"bearerAuth": []
|
435
|
+
],
|
436
|
+
"responses": {
|
437
|
+
"200": {
|
438
|
+
"description": "List of #{model}",
|
439
|
+
"content": {
|
440
|
+
"application/json": {
|
441
|
+
"schema": {
|
442
|
+
"type": "array",
|
443
|
+
"items": {
|
444
|
+
"type": "object",
|
445
|
+
"properties": create_properties_from_model(d, (d.json_attrs rescue {}))
|
446
|
+
}
|
447
|
+
}
|
448
|
+
}
|
449
|
+
}
|
450
|
+
},
|
451
|
+
"404": {
|
452
|
+
"description": "No #{model} found"
|
453
|
+
}
|
454
|
+
}
|
455
|
+
},
|
456
|
+
"post": {
|
457
|
+
"summary": "Create",
|
458
|
+
"description": "Creates a new #{model}",
|
459
|
+
"tags": [model.classify],
|
460
|
+
"security": [
|
461
|
+
"bearerAuth": []
|
462
|
+
],
|
463
|
+
"requestBody": {
|
464
|
+
"content": {
|
465
|
+
"application/json": {
|
466
|
+
"schema": {
|
467
|
+
"type": "object",
|
468
|
+
"properties": {
|
469
|
+
"#{model.singularize}": {
|
470
|
+
"type": "object",
|
471
|
+
"properties": create_properties_from_model(d, (d.json_attrs rescue {}))
|
472
|
+
}
|
473
|
+
}
|
474
|
+
}
|
475
|
+
}
|
476
|
+
}
|
477
|
+
},
|
478
|
+
"responses": {
|
479
|
+
"200": {
|
480
|
+
"description": "#{model} Created",
|
481
|
+
"content": {
|
482
|
+
"application/json": {
|
483
|
+
"schema": {
|
484
|
+
"type": "object",
|
485
|
+
"properties": create_properties_from_model(d, (d.json_attrs rescue {}))
|
486
|
+
}
|
487
|
+
}
|
488
|
+
}
|
489
|
+
}
|
490
|
+
}
|
491
|
+
}
|
492
|
+
}
|
493
|
+
# Non CRUD or Search, but custom, usually bulk operations endpoints
|
494
|
+
custom_actions = d.methods(false).select do |m| m.to_s.starts_with?("custom_action_") end
|
495
|
+
custom_actions.each do |action|
|
496
|
+
custom_action_name = action.to_s.gsub("custom_action_", "")
|
497
|
+
pivot["/#{model}/custom_action/#{custom_action_name}"] = {
|
498
|
+
"post": {
|
499
|
+
"summary": "Custom Action #{custom_action_name.titleize}",
|
500
|
+
"description": "This is just an example of a custom action, they can accept a wide range of payloads and response with a wide range of responses, also all verbs are valid. Please refer to the documentation for more information.",
|
501
|
+
"tags": [model.classify],
|
502
|
+
"security": [
|
503
|
+
"bearerAuth": []
|
504
|
+
],
|
505
|
+
"responses": {
|
506
|
+
"200": {
|
507
|
+
"description": "Custom Action",
|
508
|
+
"content": {
|
509
|
+
"application/json": {
|
510
|
+
"schema": {
|
511
|
+
"type": "object",
|
512
|
+
"properties": {
|
513
|
+
"id": {
|
514
|
+
"type": "integer"
|
515
|
+
},
|
516
|
+
"created_at": {
|
517
|
+
"type": "string",
|
518
|
+
"format": "date-time"
|
519
|
+
},
|
520
|
+
"updated_at": {
|
521
|
+
"type": "string",
|
522
|
+
"format": "date-time"
|
523
|
+
}
|
524
|
+
}
|
525
|
+
}
|
526
|
+
}
|
527
|
+
}
|
528
|
+
},
|
529
|
+
"404": {
|
530
|
+
"description": "No #{model} found"
|
531
|
+
}
|
532
|
+
}
|
533
|
+
}
|
534
|
+
}
|
535
|
+
end
|
536
|
+
pivot["/#{model}/search"] = {
|
537
|
+
# Complex queries are made using ranskac search via a post endpoint
|
538
|
+
"post": {
|
539
|
+
"summary": "Search",
|
540
|
+
"description": "Searches the #{model} using complex queries. Please refer to the [documentation](https://activerecord-hackery.github.io/ransack/) for the query syntax. In this swagger are presented only some examples, please refer to the complete documentation for more complex queries.\nThe primary method of searching in Ransack is by using what is known as predicates.\nPredicates are used within Ransack search queries to determine what information to match. For instance, the cont predicate will check to see if an attribute called 'name' or 'description' contains a value using a wildcard query.",
|
541
|
+
"tags": [model.classify],
|
542
|
+
"security": [
|
543
|
+
"bearerAuth": []
|
544
|
+
],
|
545
|
+
"requestBody": {
|
546
|
+
"content": {
|
547
|
+
"application/json": {
|
548
|
+
"schema": {
|
549
|
+
"type": "object",
|
550
|
+
"properties": {
|
551
|
+
"q": {
|
552
|
+
"type": "object",
|
553
|
+
"properties": {
|
554
|
+
"name_or_description_cont": {
|
555
|
+
"type": "string"
|
556
|
+
},
|
557
|
+
"first_name_eq": {
|
558
|
+
"type": "string"
|
559
|
+
}
|
560
|
+
}
|
561
|
+
}
|
562
|
+
}
|
563
|
+
}
|
564
|
+
}
|
565
|
+
}
|
566
|
+
},
|
567
|
+
"responses": {
|
568
|
+
"200": {
|
569
|
+
"description": "List of #{model}",
|
570
|
+
"content": {
|
571
|
+
"application/json": {
|
572
|
+
"schema": {
|
573
|
+
"type": "array",
|
574
|
+
"items": {
|
575
|
+
"type": "object",
|
576
|
+
"properties": create_properties_from_model(d, (d.json_attrs rescue {}))
|
577
|
+
}
|
578
|
+
}
|
579
|
+
}
|
580
|
+
}
|
581
|
+
},
|
582
|
+
"404": {
|
583
|
+
"description": "No #{model} found"
|
584
|
+
}
|
585
|
+
}
|
586
|
+
}
|
587
|
+
}
|
588
|
+
pivot["/#{model}/{id}"] = {
|
589
|
+
"put": {
|
590
|
+
"summary": "Update",
|
591
|
+
"description": "Updates the complete #{model}",
|
592
|
+
"parameters": [
|
593
|
+
{
|
594
|
+
"name": "id",
|
595
|
+
"in": "path",
|
596
|
+
"required": true,
|
597
|
+
"schema": {
|
598
|
+
"type": "integer"
|
599
|
+
}
|
600
|
+
}
|
601
|
+
],
|
602
|
+
"tags": [model.classify],
|
603
|
+
"security": [
|
604
|
+
"bearerAuth": []
|
605
|
+
],
|
606
|
+
"requestBody": {
|
607
|
+
"content": {
|
608
|
+
"application/json": {
|
609
|
+
"schema": {
|
610
|
+
"type": "object",
|
611
|
+
"properties": {
|
612
|
+
"#{model.singularize}": {
|
613
|
+
"type": "object",
|
614
|
+
"properties": create_properties_from_model(d, {})
|
615
|
+
}
|
616
|
+
}
|
617
|
+
}
|
618
|
+
}
|
619
|
+
}
|
620
|
+
},
|
621
|
+
"responses": {
|
622
|
+
"200": {
|
623
|
+
"description": "#{model} Updated",
|
624
|
+
"content": {
|
625
|
+
"application/json": {
|
626
|
+
"schema": {
|
627
|
+
"type": "object",
|
628
|
+
"properties": create_properties_from_model(d, (d.json_attrs rescue {}))
|
629
|
+
}
|
630
|
+
}
|
631
|
+
}
|
632
|
+
},
|
633
|
+
"404": {
|
634
|
+
"description": "No #{model} found"
|
635
|
+
}
|
636
|
+
}
|
637
|
+
},
|
638
|
+
"patch": {
|
639
|
+
"summary": "Patch",
|
640
|
+
"description": "Updates the partial #{model}",
|
641
|
+
"parameters": [
|
642
|
+
{
|
643
|
+
"name": "id",
|
644
|
+
"in": "path",
|
645
|
+
"required": true,
|
646
|
+
"schema": {
|
647
|
+
"type": "integer"
|
648
|
+
}
|
649
|
+
}
|
650
|
+
],
|
651
|
+
"tags": [model.classify],
|
652
|
+
"security": [
|
653
|
+
"bearerAuth": []
|
654
|
+
],
|
655
|
+
"requestBody": {
|
656
|
+
"content": {
|
657
|
+
"application/json": {
|
658
|
+
"schema": {
|
659
|
+
"type": "object",
|
660
|
+
"properties": {
|
661
|
+
"#{model.singularize}": {
|
662
|
+
"type": "object",
|
663
|
+
"properties": create_properties_from_model(d, {})
|
664
|
+
}
|
665
|
+
}
|
666
|
+
}
|
667
|
+
}
|
668
|
+
}
|
669
|
+
},
|
670
|
+
"responses": {
|
671
|
+
"200": {
|
672
|
+
"description": "#{model} Patched",
|
673
|
+
"content": {
|
674
|
+
"application/json": {
|
675
|
+
"schema": {
|
676
|
+
"type": "object",
|
677
|
+
"properties": create_properties_from_model(d, (d.json_attrs rescue {}))
|
678
|
+
}
|
679
|
+
}
|
680
|
+
}
|
681
|
+
},
|
682
|
+
"404": {
|
683
|
+
"description": "No #{model} found"
|
684
|
+
}
|
685
|
+
}
|
686
|
+
},
|
687
|
+
"delete": {
|
688
|
+
"summary": "Delete",
|
689
|
+
"description": "Deletes the #{model}",
|
690
|
+
"parameters": [
|
691
|
+
{
|
692
|
+
"name": "id",
|
693
|
+
"in": "path",
|
694
|
+
"required": true,
|
695
|
+
"schema": {
|
696
|
+
"type": "integer"
|
697
|
+
}
|
698
|
+
}
|
699
|
+
],
|
700
|
+
"tags": [model.classify],
|
701
|
+
"security": [
|
702
|
+
"bearerAuth": []
|
703
|
+
],
|
704
|
+
"responses": {
|
705
|
+
"200": {
|
706
|
+
"description": "#{model} Deleted"
|
707
|
+
},
|
708
|
+
"404": {
|
709
|
+
"description": "No #{model} found"
|
710
|
+
}
|
711
|
+
}
|
712
|
+
},
|
713
|
+
"get": {
|
714
|
+
"summary": "Show",
|
715
|
+
"description": "Shows the #{model}",
|
716
|
+
"parameters": [
|
717
|
+
{
|
718
|
+
"name": "id",
|
719
|
+
"in": "path",
|
720
|
+
"required": true,
|
721
|
+
"schema": {
|
722
|
+
"type": "integer"
|
723
|
+
}
|
724
|
+
}
|
725
|
+
],
|
726
|
+
"tags": [model.classify],
|
727
|
+
"security": [
|
728
|
+
"bearerAuth": []
|
729
|
+
],
|
730
|
+
"responses": {
|
731
|
+
"200": {
|
732
|
+
"description": "Show #{model}",
|
733
|
+
"content": {
|
734
|
+
"application/json": {
|
735
|
+
"schema": {
|
736
|
+
"type": "object",
|
737
|
+
"properties": create_properties_from_model(d, (d.json_attrs rescue {}))
|
738
|
+
}
|
739
|
+
}
|
740
|
+
}
|
741
|
+
},
|
742
|
+
"404": {
|
743
|
+
"description": "No #{model} found"
|
744
|
+
}
|
745
|
+
}
|
746
|
+
}
|
747
|
+
}
|
748
|
+
# d.columns_hash.each_pair do |key, val|
|
749
|
+
# pivot[model][key] = val.type unless key.ends_with? "_id"
|
750
|
+
# end
|
751
|
+
# # Only application record descendants in order to have a clean schema
|
752
|
+
# pivot[model][:associations] ||= {
|
753
|
+
# has_many: d.reflect_on_all_associations(:has_many).map { |a|
|
754
|
+
# a.name if (((a.options[:class_name].presence || a.name).to_s.classify.constantize.new.is_a? ApplicationRecord) rescue false)
|
755
|
+
# }.compact,
|
756
|
+
# belongs_to: d.reflect_on_all_associations(:belongs_to).map { |a|
|
757
|
+
# a.name if (((a.options[:class_name].presence || a.name).to_s.classify.constantize.new.is_a? ApplicationRecord) rescue false)
|
758
|
+
# }.compact
|
759
|
+
# }
|
760
|
+
# pivot[model][:methods] ||= (d.instance_methods(false).include?(:json_attrs) && !d.json_attrs.blank?) ? d.json_attrs[:methods] : nil
|
761
|
+
end
|
762
|
+
end
|
763
|
+
pivot
|
764
|
+
end
|
765
|
+
|
766
|
+
# GET '/api/v2/info/schema'
|
767
|
+
def openapi
|
768
|
+
uri = URI(request.url)
|
769
|
+
pivot = {
|
770
|
+
"openapi": "3.0.0",
|
771
|
+
"info": {
|
772
|
+
"title": "#{Settings.ns(:main).app_name} API",
|
773
|
+
"description": "Model Driven Backend [API](https://github.com/gabrieletassoni/thecore/blob/master/docs/04_REST_API.md) created to reflect the actual Active Record Models present in the project in a dynamic way",
|
774
|
+
"version": "v2"
|
775
|
+
},
|
776
|
+
"servers": [
|
777
|
+
{
|
778
|
+
# i.e. "http://localhost:3001/api/v2"
|
779
|
+
"url": "#{uri.scheme}://#{uri.host}#{":#{uri.port}" if uri.port.present?}/api/v2",
|
780
|
+
"description": "The URL at which this API responds."
|
781
|
+
}
|
782
|
+
],
|
783
|
+
# 1) Define the security scheme type (HTTP bearer)
|
784
|
+
"components":{
|
785
|
+
"securitySchemes": {
|
786
|
+
"basicAuth": {
|
787
|
+
"type": "http",
|
788
|
+
"scheme": "basic"
|
789
|
+
},
|
790
|
+
"bearerAuth": { # arbitrary name for the security scheme
|
791
|
+
"type": "http",
|
792
|
+
"scheme": "bearer",
|
793
|
+
"bearerFormat": "JWT" # optional, arbitrary value for documentation purposes
|
794
|
+
}
|
795
|
+
}
|
796
|
+
},
|
797
|
+
# 2) Apply the security globally to all operations
|
798
|
+
"security": [
|
799
|
+
{
|
800
|
+
"bearerAuth": [] # use the same name as above
|
801
|
+
}
|
802
|
+
],
|
803
|
+
"paths": generate_paths
|
804
|
+
}
|
805
|
+
|
806
|
+
render json: pivot.to_json, status: 200
|
807
|
+
end
|
808
|
+
|
809
|
+
alias swagger openapi
|
810
|
+
|
60
811
|
# GET '/api/v2/info/dsl'
|
61
812
|
def dsl
|
62
813
|
pivot = {}
|
63
|
-
# if Rails.env.development?
|
64
|
-
# Rails.configuration.eager_load_namespaces.each(&:eager_load!) if Rails.version.to_i == 5 #Rails 5
|
65
|
-
# Zeitwerk::Loader.eager_load_all if Rails.version.to_i >= 6 #Rails 6
|
66
|
-
# end
|
67
814
|
ApplicationRecord.subclasses.each do |d|
|
68
815
|
# Only if current user can read the model
|
69
816
|
if can? :read, d
|
@@ -1,8 +1,11 @@
|
|
1
|
+
require 'concerns/model_driven_api_application_record'
|
1
2
|
require 'concerns/model_driven_api_user'
|
2
3
|
require 'concerns/model_driven_api_role'
|
3
4
|
|
4
5
|
Rails.application.configure do
|
5
6
|
config.after_initialize do
|
7
|
+
# Fixes: https://stackoverflow.com/a/76781489
|
8
|
+
ApplicationRecord.send(:include, ModelDrivenApiApplicationRecord)
|
6
9
|
User.send(:include, ModelDrivenApiUser)
|
7
10
|
Role.send(:include, ModelDrivenApiRole)
|
8
11
|
end
|
data/config/routes.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
module ModelDrivenApiApplicationRecord
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
# Fixes: https://stackoverflow.com/a/76781489
|
5
|
+
included do
|
6
|
+
def self.ransackable_attributes(auth_object = nil)
|
7
|
+
column_names + _ransackers.keys
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.ransackable_associations(auth_object = nil)
|
11
|
+
reflect_on_all_associations.map { |a| a.name.to_s } + _ransackers.keys
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: model_driven_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1.
|
4
|
+
version: 3.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriele Tassoni
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-03-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thecore_backend_commons
|
@@ -58,28 +58,28 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '4.1'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '4.1'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rack-cors
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '2.0'
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '2.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: deep_merge
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -135,6 +135,7 @@ files:
|
|
135
135
|
- db/migrate/20210519145438_create_used_tokens.rb
|
136
136
|
- db/migrate/20210528111450_rename_valid_to_is_valid_in_used_token.rb
|
137
137
|
- lib/concerns/api_exception_management.rb
|
138
|
+
- lib/concerns/model_driven_api_application_record.rb
|
138
139
|
- lib/concerns/model_driven_api_role.rb
|
139
140
|
- lib/concerns/model_driven_api_user.rb
|
140
141
|
- lib/json_web_token.rb
|
@@ -162,7 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
162
163
|
- !ruby/object:Gem::Version
|
163
164
|
version: '0'
|
164
165
|
requirements: []
|
165
|
-
rubygems_version: 3.
|
166
|
+
rubygems_version: 3.5.3
|
166
167
|
signing_key:
|
167
168
|
specification_version: 4
|
168
169
|
summary: Convention based RoR engine which uses DB schema introspection to create
|