model_driven_api 3.1.8 → 3.1.10

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: dcceec80f10a8a7bc91a9f8f32982f6d632f8af71936e8333ffe2f47308da2f7
4
- data.tar.gz: e6b1f3833363f78ff5884a25ac986fb47e44e322193b65e3fc90494de2ab08dd
3
+ metadata.gz: 0afb3dc1dca5b7277c7ea2bfd601669765d5227fe68ede17fa6fbfc3de19d36f
4
+ data.tar.gz: 02c3ece69e6237b42f8f04de6660469d528c81a84cf392a34a743360b765d832
5
5
  SHA512:
6
- metadata.gz: 8675f05d7145638a141e358fac87bb481318f2ea868817ea578d1400e6600291bdbc1d21691277f9c11f2c498be9e2659d724d0e3038f23c706b4a5ef8a81528
7
- data.tar.gz: c1bd46b4cd97f04f50ddef90b3394f4ffce98348f8f73f8e05ac30259c0c16319b785ceeb5f77fb17aa0bbb55ae374009d65eaae7dc1e9e860f06ee7e0ec3565
6
+ metadata.gz: 92c3d537101cb23123ce34564eaac4fe1d767770628dddcd1cc94487253bb82c880e07721a78c2be3dc571c370020b12e13ab30e9981d035c7766634dccdeb7b
7
+ data.tar.gz: 69385bf1c20fe26e8168be63da529edb847002984084e133e431aef6b07815bb88edf1f42ed43d65dc0ef5194a28ede011c29f05aca3de0176dd11a119da3a8a
@@ -66,7 +66,6 @@ class Api::V2::ApplicationController < ActionController::API
66
66
 
67
67
  def create
68
68
  # Normal Create Action
69
- @record = @model.new(@body)
70
69
  authorize! :create, @record
71
70
  # Custom Action
72
71
  status, result, status_number = check_for_custom_action
@@ -74,6 +73,7 @@ class Api::V2::ApplicationController < ActionController::API
74
73
  # Keeping this automation can be too dangerous and lead to unpredicted results
75
74
  # TODO: Remove it
76
75
  # @record.user_id = current_user.id if @model.column_names.include? "user_id"
76
+ @record = @model.new(@body)
77
77
  @record.save!
78
78
  render json: @record.to_json(json_attrs), status: 201
79
79
  end
@@ -158,11 +158,13 @@ class Api::V2::ApplicationController < ActionController::API
158
158
  # The endpoint can be expressed in two ways:
159
159
  # 1. As a method in the model, with suffix custom_action_<custom_action>
160
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}")
161
+ # Example:
162
+ # Endpoints::TestApi.new(:test, {request_verb: "POST", is_connected: "Uhhhh"}).result
163
+ if @model.respond_to?("custom_action_#{custom_action}")
165
164
  body, status = @model.send("custom_action_#{custom_action}", params)
165
+ elsif ("Endpoints::#{@model}".constantize rescue false) && "Endpoints::#{@model}".constantize.instance_methods.include?(custom_action.to_sym)
166
+ # Custom endpoint exists and can be called in the sub-modules form
167
+ body, status = "Endpoints::#{@model}".constantize.new(custom_action, params).result
166
168
  else
167
169
  # Custom endpoint does not exist or cannot be called
168
170
  raise NoMethodError
@@ -521,7 +521,7 @@ class Api::V2::InfoController < Api::V2::ApplicationController
521
521
  custom_actions.each do |action|
522
522
  custom_action_name = action.to_s.gsub("custom_action_", "")
523
523
  pivot["/#{model}/custom_action/#{custom_action_name}"] = {
524
- "post": {
524
+ "get": {
525
525
  "summary": "Custom Action #{custom_action_name.titleize}",
526
526
  "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.",
527
527
  "tags": [model.classify],
@@ -0,0 +1,56 @@
1
+ class Endpoints::TestApi < NonCrudEndpoints
2
+ def test(params)
3
+ # Define an explain var to be used to validate and document the action behavior when using ?explain=true in query string
4
+ explain = {
5
+ verbs: ["GET", "POST"],
6
+ body: {
7
+ messages: {
8
+ type: :array,
9
+ optional: true,
10
+ items: {
11
+ type: :string,
12
+ optional: false
13
+ }
14
+ },
15
+ is_connected: {
16
+ type: :boolean,
17
+ optional: false
18
+ },
19
+ user: {
20
+ type: :object,
21
+ optional: true,
22
+ properties: {
23
+ name: {
24
+ type: :string,
25
+ optional: false
26
+ },
27
+ age: {
28
+ type: :integer,
29
+ optional: true
30
+ }
31
+ }
32
+ }
33
+ },
34
+ query: {
35
+ explain: {
36
+ type: :boolean,
37
+ optional: true
38
+ }
39
+ },
40
+ responses: {
41
+ 200 => {
42
+ message: :string,
43
+ params: {},
44
+ },
45
+ 501 => {
46
+ error: :string,
47
+ },
48
+ },
49
+ }
50
+ return explain, 200 if params[:explain].to_s == "true" && !explain.blank?
51
+
52
+
53
+ return { message: "Hello World From Test API Custom Action called test", params: params }, 200
54
+ end
55
+ end
56
+ # end
data/config/routes.rb CHANGED
@@ -30,17 +30,17 @@ Rails.application.routes.draw do
30
30
  patch ":ctrl/custom_action/:action_name/:id", to: 'application#update'
31
31
  delete ":ctrl/custom_action/:action_name/:id", to: 'application#destroy'
32
32
  # Catchall routes
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'
39
- # # CRUD Update
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'
39
+ # CRUD Update
40
40
  put '*path/:id/multi', to: 'application#update_multi'
41
41
  patch '*path/:id/multi', to: 'application#update_multi'
42
- # put '*path/:id', to: 'application#update'
43
- # patch '*path/:id', to: 'application#patch'
42
+ put '*path/:id', to: 'application#update'
43
+ patch '*path/:id', to: 'application#patch'
44
44
 
45
45
  # # CRUD Delete
46
46
  delete '*path/:id/multi', to: 'application#destroy_multi'
@@ -11,6 +11,7 @@ module ApiExceptionManagement
11
11
  rescue_from ActiveRecord::RecordInvalid, with: :invalid!
12
12
  rescue_from ActiveRecord::RecordNotFound, with: :not_found!
13
13
  rescue_from ActiveRecord::RecordNotUnique, with: :invalid!
14
+ rescue_from EndpointValidationError, with: :api_error
14
15
  end
15
16
 
16
17
  def unauthenticated! exception = AuthenticateUser::AccessDenied.new
@@ -34,7 +35,7 @@ module ApiExceptionManagement
34
35
  return api_error status: 500, errors: exception.message
35
36
  end
36
37
 
37
- def api_error(status: 500, errors: [])
38
+ def api_error(status: 501, errors: [])
38
39
  # puts errors.full_messages if !Rails.env.production? && errors.respond_to?(:full_messages)
39
40
  head status && return if errors.blank?
40
41
 
@@ -0,0 +1,2 @@
1
+ class EndpointValidationError < StandardError
2
+ end
@@ -1,3 +1,3 @@
1
1
  module ModelDrivenApi
2
- VERSION = "3.1.8".freeze
2
+ VERSION = "3.1.10".freeze
3
3
  end
@@ -1,3 +1,5 @@
1
+ require 'endpoint_validation_error'
2
+ require 'non_crud_endpoints'
1
3
  require 'thecore_backend_commons'
2
4
  require 'rack/cors'
3
5
  require 'ransack'
@@ -0,0 +1,54 @@
1
+ class NonCrudEndpoints
2
+ attr_accessor :result
3
+ # Add a validation method which will be inherited by all the instances, and automatically run before any method call
4
+ def initialize(m, params)
5
+ # Check if self hase the m method, if not, raise a NoMethodError
6
+ raise NoMethodError, "The method #{m} does not exist in #{self.class}" unless self.respond_to? m
7
+ definition = self.send(m, { explain: true }) rescue []
8
+ validate_request(definition.first.presence || {}, params)
9
+ @result = self.send(m, params)
10
+ end
11
+
12
+ def validate_request(definition, params)
13
+ # If there is no definition, return
14
+ return if definition.blank?
15
+ # puts "Called Class is: #{self.class}"
16
+ # puts "Which is son of: #{self.class.superclass}"
17
+ body_mandatory_keys = definition[:body].select { |k, v| v[:optional] == false }.keys
18
+ query_mandatory_keys = definition[:query].select { |k, v| v[:optional] == false }.keys
19
+ # Raise a ValidationError if the request does not match the definition
20
+ raise EndpointValidationError, "The verb \"#{params[:request_verb].presence || "No Verb Provided"}\" is not present in #{definition[:verbs].join(", ")}." if definition[:verbs].exclude? params[:request_verb]
21
+ # Raise an exception if the verb is put or post and the body keys in definition are not all present as params keys, both params and definition[:body] can have nested objects
22
+ raise EndpointValidationError, "The request body does not match the definition: in #{params[:request_verb]} requests all of the params must be present in definition. The body definition is #{definition[:body]}." if params[:request_verb] != "GET" && (body_mandatory_keys & params.keys) != body_mandatory_keys
23
+ # Raise an exception if the verb is put or post and the body keys in definition are not all present as params keys, both params and definition[:body] can have nested objects
24
+ raise EndpointValidationError, "The request query does not match the definition. The query definition is: #{definition[:query]}." if (query_mandatory_keys & params.keys) != query_mandatory_keys
25
+ # Rais if the type of the param is not the same as the definition
26
+ definition[:body].each do |k, v|
27
+ next if params[k].nil?
28
+ computed_type = get_type(params[k])
29
+ raise EndpointValidationError, "The type of the param #{k} is not the same as the definition. The definition is #{v[:type]} and the param is #{computed_type}." if v[:type] != computed_type
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def get_type(type)
36
+ case type
37
+ when String
38
+ :string
39
+ when Integer
40
+ :integer
41
+ when Float
42
+ :number
43
+ when TrueClass, FalseClass
44
+ :boolean
45
+ when Array
46
+ :array
47
+ when Hash
48
+ :object
49
+ else
50
+ :undefined
51
+ end
52
+ end
53
+ end
54
+
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.8
4
+ version: 3.1.10
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-06 00:00:00.000000000 Z
11
+ date: 2024-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thecore_backend_commons
@@ -125,7 +125,7 @@ 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
128
+ - app/models/endpoints/test_api.rb
129
129
  - app/models/test_api.rb
130
130
  - app/models/used_token.rb
131
131
  - config/initializers/after_initialize_for_model_driven_api.rb
@@ -140,10 +140,12 @@ files:
140
140
  - lib/concerns/model_driven_api_application_record.rb
141
141
  - lib/concerns/model_driven_api_role.rb
142
142
  - lib/concerns/model_driven_api_user.rb
143
+ - lib/endpoint_validation_error.rb
143
144
  - lib/json_web_token.rb
144
145
  - lib/model_driven_api.rb
145
146
  - lib/model_driven_api/engine.rb
146
147
  - lib/model_driven_api/version.rb
148
+ - lib/non_crud_endpoints.rb
147
149
  - lib/tasks/model_driven_api_tasks.rake
148
150
  homepage: https://github.com/gabrieletassoni/model_driven_api
149
151
  licenses:
@@ -1,5 +0,0 @@
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