model_driven_api 3.6.3 → 3.7.1

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.
data/Rakefile CHANGED
@@ -3,6 +3,9 @@
3
3
  require "bundler/gem_tasks"
4
4
  require "rake/testtask"
5
5
 
6
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
7
+ load "rails/tasks/engine.rake"
8
+
6
9
  Rake::TestTask.new(:test) do |t|
7
10
  t.libs << "test"
8
11
  t.libs << "lib"
@@ -13,7 +13,7 @@ class Api::V2::ApplicationController < ActionController::API
13
13
 
14
14
  # GET :controller/
15
15
  def index
16
- authorize! :index, @model
16
+ authorize! :index, @model unless public_custom_action?
17
17
 
18
18
  # Custom Action
19
19
  status, result, status_number = check_for_custom_action
@@ -67,7 +67,7 @@ class Api::V2::ApplicationController < ActionController::API
67
67
  def create
68
68
  # Normal Create Action
69
69
  Rails.logger.debug("Creating a new record #{@record}")
70
- authorize! :create, @record.presence || @model
70
+ authorize! :create, @record.presence || @model unless public_custom_action?
71
71
  # Custom Action
72
72
  status, result, status_number = check_for_custom_action
73
73
  return render json: result, status: (status_number.presence || 200) if status == true
@@ -127,6 +127,19 @@ class Api::V2::ApplicationController < ActionController::API
127
127
 
128
128
  private
129
129
 
130
+ # Returns true if the current request is for a NonCrudEndpoints custom action
131
+ # that has been declared as public (no authentication required).
132
+ # Forces autoloading of the Endpoints::<Model> class so the public_action_registry
133
+ # is populated before authenticate_request checks it.
134
+ def public_custom_action?
135
+ return false unless request.url.include?("/custom_action/")
136
+ model_name = params[:ctrl].to_s.classify
137
+ action_name = params[:action_name].to_s
138
+ # Ensure the endpoint class is loaded so its public_action declarations are registered.
139
+ ("Endpoints::#{model_name}".constantize rescue nil)
140
+ NonCrudEndpoints.public_action?(model_name, action_name)
141
+ end
142
+
130
143
  ## CUSTOM ACTION
131
144
  # [GET|PUT|POST|DELETE] :controller?do=:custom_action
132
145
  # or
@@ -140,43 +153,9 @@ class Api::V2::ApplicationController < ActionController::API
140
153
  # or
141
154
  # [GET|PUT|POST|DELETE] :controller/custom_action/:custom_action/:id
142
155
  def check_for_custom_action
143
- custom_action, token = if !params[:do].blank?
144
- # This also responds to custom actions which have the bearer token in the custom action name. A workaround to remove for some IoT devices
145
- # Which don't support token in header or in querystring
146
- # This is for backward compatibility and in future it can ben removed
147
- params[:do].split("-")
148
- elsif request.url.include? "/custom_action/"
149
- [params[:action_name], nil]
150
- else
151
- # Not a custom action call
152
- false
153
- end
154
- return false unless custom_action
155
- # Poor man's solution to avoid the possibility to
156
- # call an unwanted method in the AR Model.
157
-
158
- # Adding some useful information to the params hash
159
- params[:request_url] = request.url
160
- params[:remote_ip] = request.remote_ip
161
- params[:request_verb] = request.request_method
162
- params[:token] = token.presence || bearer_token
163
- # The endpoint can be expressed in two ways:
164
- # 1. As a method in the model, with suffix custom_action_<custom_action>
165
- # 2. As a module instance method in the model, like Track::Endpoints.inventory
166
- # Example:
167
- # Endpoints::TestApi.new(:test, {request_verb: "POST", is_connected: "Uhhhh"}).result
168
- Rails.logger.debug("Checking for custom action #{custom_action} in #{@model}")
169
- if @model.respond_to?("custom_action_#{custom_action}")
170
- body, status = @model.send("custom_action_#{custom_action}", params)
171
- elsif ("Endpoints::#{@model}".constantize rescue false) && "Endpoints::#{@model}".constantize.instance_methods.include?(custom_action.to_sym)
172
- # Custom endpoint exists and can be called in the sub-modules form
173
- body, status = "Endpoints::#{@model}".constantize.new(custom_action, params).result
174
- else
175
- # Custom endpoint does not exist or cannot be called
176
- raise NoMethodError
177
- end
178
-
179
- return true, body.to_json(json_attrs), status
156
+ dispatched, body, status = Api::CustomActionDispatcher.call(@model, params, request)
157
+ return false unless dispatched
158
+ [true, body.to_json(json_attrs), status]
180
159
  end
181
160
 
182
161
  def bearer_token
@@ -193,6 +172,9 @@ class Api::V2::ApplicationController < ActionController::API
193
172
  end
194
173
 
195
174
  def authenticate_request
175
+ # Skip auth for public NonCrudEndpoints actions (e.g. vapid_public_key).
176
+ return if public_custom_action?
177
+
196
178
  @current_user = nil
197
179
  Settings.ns(:security).allowed_authorization_headers.split(",").each do |header|
198
180
  # puts "Found header #{header}: #{request.headers[header]}"
@@ -229,17 +211,10 @@ class Api::V2::ApplicationController < ActionController::API
229
211
  end
230
212
 
231
213
  def extract_model
232
- # This method is only valid for ActiveRecords
233
- # For any other model-less controller, the actions must be
234
- # defined in the route, and must exist in the controller definition.
235
- # So, if it's not an activerecord, the find model makes no sense at all
236
- # thus must return 404.
237
- @model = (params[:ctrl].classify.constantize rescue params[:path].split("/").first.classify.constantize rescue controller_path.classify.constantize rescue controller_name.classify.constantize rescue nil)
238
- # Getting the body of the request if it exists, it's ok the singular or
239
- # plural form, this helps with automatic tests with Insomnia.
214
+ @model = Api::ModelResolver.resolve(params, controller_path, controller_name)
240
215
  @body = (params[@model.model_name.singular].presence || params[@model.model_name.route_key]) rescue params
241
- # Only ActiveRecords can have this model caputed
242
- return not_found! if (@model != TestApi && !@model.new.is_a?(ActiveRecord::Base) rescue false)
216
+ rescue Api::ModelResolver::NotFound
217
+ not_found!
243
218
  end
244
219
 
245
220
  def check_authorization(cmd)