model_driven_api 3.1.6 → 3.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/application_controller.rb +48 -30
- data/app/controllers/api/v2/info_controller.rb +26 -2
- data/app/models/concerns/endpoints/test_api.rb +5 -0
- data/app/models/test_api.rb +2 -0
- data/config/routes.rb +19 -8
- data/lib/model_driven_api/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dcceec80f10a8a7bc91a9f8f32982f6d632f8af71936e8333ffe2f47308da2f7
|
4
|
+
data.tar.gz: e6b1f3833363f78ff5884a25ac986fb47e44e322193b65e3fc90494de2ab08dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8675f05d7145638a141e358fac87bb481318f2ea868817ea578d1400e6600291bdbc1d21691277f9c11f2c498be9e2659d724d0e3038f23c706b4a5ef8a81528
|
7
|
+
data.tar.gz: c1bd46b4cd97f04f50ddef90b3394f4ffce98348f8f73f8e05ac30259c0c16319b785ceeb5f77fb17aa0bbb55ae374009d65eaae7dc1e9e860f06ee7e0ec3565
|
@@ -121,36 +121,54 @@ class Api::V2::ApplicationController < ActionController::API
|
|
121
121
|
|
122
122
|
private
|
123
123
|
|
124
|
+
## CUSTOM ACTION
|
125
|
+
# [GET|PUT|POST|DELETE] :controller?do=:custom_action
|
126
|
+
# or
|
127
|
+
# [GET|PUT|POST|DELETE] :controller/:id?do=:
|
128
|
+
# or
|
129
|
+
# [GET|PUT|POST|DELETE] :controller?do=:custom_action-token
|
130
|
+
# or
|
131
|
+
# [GET|PUT|POST|DELETE] :controller/:id?do=:custom_action-token
|
132
|
+
# or
|
133
|
+
# [GET|PUT|POST|DELETE] :controller/custom_action/:custom_action
|
134
|
+
# or
|
135
|
+
# [GET|PUT|POST|DELETE] :controller/custom_action/:custom_action/:id
|
124
136
|
def check_for_custom_action
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
params[:token] = token.presence || bearer_token
|
137
|
-
# The endpoint can be expressed in two wayy:
|
138
|
-
# 1. As a method in the model, with suffix custom_action_<custom_action>
|
139
|
-
# 2. As a module instance method in the model, like Track::Endpoints.inventory
|
140
|
-
if defined?("Endpoints::#{@model}.#{custom_action}")
|
141
|
-
# Custom endpoint exists and can be called in the sub-modules form
|
142
|
-
body, status = "Endpoints::#{@model}".constantize.send(custom_action, params)
|
143
|
-
elsif @model.respond_to?("custom_action_#{custom_action}")
|
144
|
-
body, status = @model.send("custom_action_#{custom_action}", params)
|
145
|
-
else
|
146
|
-
# Custom endpoint does not exist or cannot be called
|
147
|
-
raise NoMethodError
|
148
|
-
end
|
149
|
-
|
150
|
-
return true, body.to_json(json_attrs), status
|
137
|
+
|
138
|
+
custom_action, token = if !params[:do].blank?
|
139
|
+
# This also responds to custom actions which have the bearer token in the custom action name. A workaround to remove for some IoT devices
|
140
|
+
# Which don't support token in header or in querystring
|
141
|
+
# This is for backward compatibility and in future it can ben removed
|
142
|
+
params[:do].split("-")
|
143
|
+
elsif request.url.include? "/custom_action/"
|
144
|
+
[params[:action_name], nil]
|
145
|
+
else
|
146
|
+
# Not a custom action call
|
147
|
+
false
|
151
148
|
end
|
152
|
-
|
153
|
-
|
149
|
+
return false unless custom_action
|
150
|
+
# Poor man's solution to avoid the possibility to
|
151
|
+
# call an unwanted method in the AR Model.
|
152
|
+
|
153
|
+
# Adding some useful information to the params hash
|
154
|
+
params[:request_url] = request.url
|
155
|
+
params[:remote_ip] = request.remote_ip
|
156
|
+
params[:request_verb] = request.request_method
|
157
|
+
params[:token] = token.presence || bearer_token
|
158
|
+
# The endpoint can be expressed in two ways:
|
159
|
+
# 1. As a method in the model, with suffix custom_action_<custom_action>
|
160
|
+
# 2. As a module instance method in the model, like Track::Endpoints.inventory
|
161
|
+
if defined?("Endpoints::#{@model}.#{custom_action}")
|
162
|
+
# Custom endpoint exists and can be called in the sub-modules form
|
163
|
+
body, status = "Endpoints::#{@model}".constantize.send(custom_action, params)
|
164
|
+
elsif @model.respond_to?("custom_action_#{custom_action}")
|
165
|
+
body, status = @model.send("custom_action_#{custom_action}", params)
|
166
|
+
else
|
167
|
+
# Custom endpoint does not exist or cannot be called
|
168
|
+
raise NoMethodError
|
169
|
+
end
|
170
|
+
|
171
|
+
return true, body.to_json(json_attrs), status
|
154
172
|
end
|
155
173
|
|
156
174
|
def bearer_token
|
@@ -209,9 +227,9 @@ class Api::V2::ApplicationController < ActionController::API
|
|
209
227
|
@model = (params[:ctrl].classify.constantize rescue params[:path].split("/").first.classify.constantize rescue controller_path.classify.constantize rescue controller_name.classify.constantize rescue nil)
|
210
228
|
# Getting the body of the request if it exists, it's ok the singular or
|
211
229
|
# plural form, this helps with automatic tests with Insomnia.
|
212
|
-
@body = params[@model.model_name.singular].presence || params[@model.model_name.route_key]
|
230
|
+
@body = (params[@model.model_name.singular].presence || params[@model.model_name.route_key]) rescue params
|
213
231
|
# Only ActiveRecords can have this model caputed
|
214
|
-
return not_found! if (!@model.new.is_a?
|
232
|
+
return not_found! if (@model != TestApi && !@model.new.is_a?(ActiveRecord::Base) rescue false)
|
215
233
|
end
|
216
234
|
|
217
235
|
def check_authorization(cmd)
|
@@ -84,14 +84,36 @@ class Api::V2::InfoController < Api::V2::ApplicationController
|
|
84
84
|
method_key.type.to_s
|
85
85
|
end
|
86
86
|
|
87
|
+
def integer?(str)
|
88
|
+
true if Integer(str) rescue false
|
89
|
+
end
|
90
|
+
|
91
|
+
def number?(str)
|
92
|
+
true if Float(str) rescue false
|
93
|
+
end
|
94
|
+
|
95
|
+
def datetime?(str)
|
96
|
+
true if DateTime.parse(str) rescue false
|
97
|
+
end
|
98
|
+
|
87
99
|
def create_properties_from_model(model, dsl, remove_reserved = false)
|
88
|
-
JSON.parse(model.new.to_json(dsl))
|
100
|
+
parsed_json = JSON.parse(model.new.to_json(dsl))
|
101
|
+
parsed_json.keys.map do |k|
|
89
102
|
type = compute_type(model, k)
|
90
103
|
|
91
104
|
# Remove fields that cannot be created or updated
|
92
105
|
if remove_reserved && %w( id created_at updated_at lock_version).include?(k.to_s)
|
93
106
|
nil
|
107
|
+
elsif type == "method" && (parsed_json[k].is_a?(FalseClass) || parsed_json[k].is_a?(TrueClass))
|
108
|
+
[k, { "type": "boolean" }]
|
109
|
+
elsif type == "method" && parsed_json[k].is_a?(String) && number?(parsed_json[k])
|
110
|
+
[k, { "type": "number" }]
|
111
|
+
elsif type == "method" && parsed_json[k].is_a?(String) && integer?(parsed_json[k])
|
112
|
+
[k, { "type": "integer" }]
|
113
|
+
elsif type == "method" && parsed_json[k].is_a?(String) && datetime?(parsed_json[k])
|
114
|
+
[k, { "type": "string", "format": "date-time" }]
|
94
115
|
elsif type == "method"
|
116
|
+
# Unknown or complex format returned
|
95
117
|
[k, { "type": "object", "additionalProperties": true }]
|
96
118
|
elsif type == "date"
|
97
119
|
[k, { "type": "string", "format": "date" }]
|
@@ -422,7 +444,7 @@ class Api::V2::InfoController < Api::V2::ApplicationController
|
|
422
444
|
}
|
423
445
|
}
|
424
446
|
}
|
425
|
-
ApplicationRecord.subclasses.each do |d|
|
447
|
+
ApplicationRecord.subclasses.sort_by { |d| d.to_s }.each do |d|
|
426
448
|
# Only if current user can read the model
|
427
449
|
if true # can? :read, d
|
428
450
|
model = d.to_s.underscore.tableize
|
@@ -494,6 +516,8 @@ class Api::V2::InfoController < Api::V2::ApplicationController
|
|
494
516
|
}
|
495
517
|
# Non CRUD or Search, but custom, usually bulk operations endpoints
|
496
518
|
custom_actions = d.methods(false).select do |m| m.to_s.starts_with?("custom_action_") end
|
519
|
+
# Add also custom actions created using th enew Endpoints Interface
|
520
|
+
custom_actions += "Endpoints::#{d.model_name.name}".constantize.methods(false) rescue []
|
497
521
|
custom_actions.each do |action|
|
498
522
|
custom_action_name = action.to_s.gsub("custom_action_", "")
|
499
523
|
pivot["/#{model}/custom_action/#{custom_action_name}"] = {
|
data/config/routes.rb
CHANGED
@@ -21,19 +21,30 @@ Rails.application.routes.draw do
|
|
21
21
|
post "authenticate" => "authentication#authenticate"
|
22
22
|
post ":ctrl/search" => 'application#index'
|
23
23
|
|
24
|
+
# Add a route with placeholders for custom actions, the custom actions routes have a form like: :ctrl/custom_action/:action_name or :ctrl/custom_action/:action_name/:id
|
25
|
+
# Can have all the verbs, but the most common are: get, post, put, delete
|
26
|
+
get ":ctrl/custom_action/:action_name", to: 'application#index'
|
27
|
+
get ":ctrl/custom_action/:action_name/:id", to: 'application#show'
|
28
|
+
post ":ctrl/custom_action/:action_name", to: 'application#create'
|
29
|
+
put ":ctrl/custom_action/:action_name/:id", to: 'application#update'
|
30
|
+
patch ":ctrl/custom_action/:action_name/:id", to: 'application#update'
|
31
|
+
delete ":ctrl/custom_action/:action_name/:id", to: 'application#destroy'
|
24
32
|
# Catchall routes
|
25
|
-
# # CRUD Show
|
26
|
-
get '*path/:id', to: 'application#show'
|
27
|
-
# # CRUD Index
|
28
|
-
get '*path', to: 'application#index'
|
29
|
-
# # CRUD Create
|
30
|
-
post '*path', to: 'application#create'
|
33
|
+
# # # CRUD Show
|
34
|
+
# get '*path/:id', to: 'application#show'
|
35
|
+
# # # CRUD Index
|
36
|
+
# get '*path', to: 'application#index'
|
37
|
+
# # # CRUD Create
|
38
|
+
# post '*path', to: 'application#create'
|
31
39
|
# # CRUD Update
|
32
40
|
put '*path/:id/multi', to: 'application#update_multi'
|
33
|
-
|
41
|
+
patch '*path/:id/multi', to: 'application#update_multi'
|
42
|
+
# put '*path/:id', to: 'application#update'
|
43
|
+
# patch '*path/:id', to: 'application#patch'
|
44
|
+
|
34
45
|
# # CRUD Delete
|
35
46
|
delete '*path/:id/multi', to: 'application#destroy_multi'
|
36
|
-
delete '*path/:id', to: 'application#destroy'
|
47
|
+
# delete '*path/:id', to: 'application#destroy'
|
37
48
|
end
|
38
49
|
end
|
39
50
|
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.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriele Tassoni
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-03-
|
11
|
+
date: 2024-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thecore_backend_commons
|
@@ -125,6 +125,8 @@ files:
|
|
125
125
|
- app/controllers/api/v2/authentication_controller.rb
|
126
126
|
- app/controllers/api/v2/info_controller.rb
|
127
127
|
- app/controllers/api/v2/users_controller.rb
|
128
|
+
- app/models/concerns/endpoints/test_api.rb
|
129
|
+
- app/models/test_api.rb
|
128
130
|
- app/models/used_token.rb
|
129
131
|
- config/initializers/after_initialize_for_model_driven_api.rb
|
130
132
|
- config/initializers/cors_api_thecore.rb
|