model_driven_api 3.1.6 → 3.1.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a72bb6cfea6d9f8e0fd4fff0643ee5f73c4c4434532092bd428c7b048c5f9b0
4
- data.tar.gz: 51c3691214427e0aaa6111cf00a9b476fdb4c91861ba1819a227ea859f924a2d
3
+ metadata.gz: dcceec80f10a8a7bc91a9f8f32982f6d632f8af71936e8333ffe2f47308da2f7
4
+ data.tar.gz: e6b1f3833363f78ff5884a25ac986fb47e44e322193b65e3fc90494de2ab08dd
5
5
  SHA512:
6
- metadata.gz: af0ddbc93b40f315b170630facacb7357022880e560502c83f4e2f2e5ac4342ffb5eaece4be1e1e591d5f0fa6b0a447b1e49ecbd6f9bf2975ee15c2a4b5c8743
7
- data.tar.gz: f6318805dc382e2ce28494a74174d01bd53fb12749151a6ea5b21d4ea610dd23a9a4ac8c3dce09ff241c21202f8c3291694056917024781df012f39ac8e25460
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
- ## CUSTOM ACTION
126
- # [GET|PUT|POST|DELETE] :controller?do=:custom_action
127
- # or
128
- # [GET|PUT|POST|DELETE] :controller/:id?do=:custom_action
129
- unless params[:do].blank?
130
- # Poor man's solution to avoid the possibility to
131
- # call an unwanted method in the AR Model.
132
- custom_action, token = params[:do].split("-")
133
-
134
- params[:request_url] = request.url
135
- params[:remote_ip] = request.remote_ip
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
- # if it's here there is no custom action in the request querystring
153
- return false
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? ActiveRecord::Base rescue false)
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)).keys.map do |k|
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}"] = {
@@ -0,0 +1,5 @@
1
+ module Endpoints::TestApi
2
+ def self.test params
3
+ return { message: "Hello World From Test API Custom Action called test", params: params }, 200
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ class TestApi
2
+ end
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
- put '*path/:id', to: 'application#update'
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
@@ -1,3 +1,3 @@
1
1
  module ModelDrivenApi
2
- VERSION = "3.1.6".freeze
2
+ VERSION = "3.1.8".freeze
3
3
  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.6
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-05 00:00:00.000000000 Z
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