model_driven_api 3.1.9 → 3.1.10
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 +4 -4
- data/app/controllers/api/v2/application_controller.rb +7 -5
- data/app/models/endpoints/test_api.rb +56 -0
- data/app/models/test_api.rb +0 -4
- data/config/routes.rb +9 -9
- data/lib/concerns/api_exception_management.rb +2 -1
- data/lib/endpoint_validation_error.rb +2 -0
- data/lib/model_driven_api/version.rb +1 -1
- data/lib/model_driven_api.rb +2 -0
- data/lib/non_crud_endpoints.rb +54 -0
- metadata +5 -3
- data/app/models/concerns/endpoints/test_api.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0afb3dc1dca5b7277c7ea2bfd601669765d5227fe68ede17fa6fbfc3de19d36f
|
4
|
+
data.tar.gz: 02c3ece69e6237b42f8f04de6660469d528c81a84cf392a34a743360b765d832
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
162
|
-
|
163
|
-
|
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
|
@@ -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/app/models/test_api.rb
CHANGED
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
|
-
# #
|
34
|
-
|
35
|
-
# #
|
36
|
-
|
37
|
-
# #
|
38
|
-
|
39
|
-
#
|
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
|
-
|
43
|
-
|
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:
|
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
|
|
data/lib/model_driven_api.rb
CHANGED
@@ -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.
|
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-
|
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/
|
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,23 +0,0 @@
|
|
1
|
-
module Endpoints::TestApi
|
2
|
-
def self.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"],
|
6
|
-
body: {},
|
7
|
-
query: {},
|
8
|
-
responses: {
|
9
|
-
200 => {
|
10
|
-
message: :string,
|
11
|
-
params: {}
|
12
|
-
},
|
13
|
-
501 => {
|
14
|
-
error: :string
|
15
|
-
}
|
16
|
-
}
|
17
|
-
}
|
18
|
-
|
19
|
-
return explain, 200 if params[:explain] == "true"
|
20
|
-
return { error: "This method responds only to #{explain[:verbs].join(", ")} requests" }, 501 if explain[:verbs].exclude? params[:request_verb]
|
21
|
-
return { message: "Hello World From Test API Custom Action called test", params: params }, 200
|
22
|
-
end
|
23
|
-
end
|