model_driven_api 3.2.3 → 3.2.5

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: 1b024841871b7a4f4bebfe176a593b84521dce949655a46c47470f7c130330f9
4
- data.tar.gz: d61e7ef935e4beeef85a8668636817aa84e97a7114d6d136c408d20e90ba6523
3
+ metadata.gz: e73b3aaf951e74746e4f15fd020a5e9811b13f8222bee893e3b657323dbea0f7
4
+ data.tar.gz: 38f200e4c7550d9f15ff356e3323fe2a4bdaa5328f6c4c94e5d05401de1cf60a
5
5
  SHA512:
6
- metadata.gz: 192f347a882bc659fa85ad38fcf00003b36dfc65053914737fea763cc508195bf6a4d8e6f73d5e7f08f6346eb83c76f89f641a5ebee2eaece50da7736af7aaad
7
- data.tar.gz: bacb69b46cdf43d76668a43e28fd0510b65e1e9290b7c348f432c16d1d4c74618555ccde7735a879c0c4dd8fec64c4743fad0e3890715378f100af80fe7ded16
6
+ metadata.gz: 84c35cf76c50627514dc34702c136f3506fed645ccfb2228d26c21338de8b310e598d18fb302c11675056d0956310641fe0b1d0c60edfb5869ef88e718643833
7
+ data.tar.gz: 003ee1a957b5f8a2dc9c40c68acf8d57ec4d64ae5e29f26452ea7f763db37e93bb2cd6dd410ca4d31535e8b1613252a335b8efa75cd6462a0bbd2131df8a25c0
@@ -53,7 +53,7 @@ class Api::V2::ApplicationController < ActionController::API
53
53
  end
54
54
 
55
55
  def show
56
- authorize! :show, @record_id
56
+ authorize! :show, @record_id.presence || @model
57
57
 
58
58
  # Custom Show Action
59
59
  status, result, status_number = check_for_custom_action
@@ -66,7 +66,8 @@ class Api::V2::ApplicationController < ActionController::API
66
66
 
67
67
  def create
68
68
  # Normal Create Action
69
- authorize! :create, @record
69
+ Rails.logger.debug("Creating a new record #{@record}")
70
+ authorize! :create, @record.presence || @model
70
71
  # Custom Action
71
72
  status, result, status_number = check_for_custom_action
72
73
  return render json: result, status: (status_number.presence || 200) if status == true
@@ -79,7 +80,7 @@ class Api::V2::ApplicationController < ActionController::API
79
80
  end
80
81
 
81
82
  def update
82
- authorize! :update, @record
83
+ authorize! :update, @record.presence || @model
83
84
 
84
85
  # Custom Action
85
86
  status, result, status_number = check_for_custom_action
@@ -102,7 +103,7 @@ class Api::V2::ApplicationController < ActionController::API
102
103
  end
103
104
 
104
105
  def destroy
105
- authorize! :destroy, @record
106
+ authorize! :destroy, @record.presence || @model
106
107
 
107
108
  # Custom Action
108
109
  status, result, status_number = check_for_custom_action
@@ -198,6 +198,98 @@ class Api::V2::InfoController < Api::V2::ApplicationController
198
198
  }
199
199
  }
200
200
  },
201
+ "/raw/sql": {
202
+ "post": {
203
+ "summary": "Raw SQL query execution of SELECT queries",
204
+ "description": "Executes a SQL query on the underlying PostgreSQL database.
205
+
206
+ Designed for SELECT queries that use the json_agg function to aggregate results into JSON arrays.
207
+
208
+ Other query types are not recommended and may be restricted for security and performance reasons.
209
+
210
+ Only SELECT statements are allowed. DDL and DML statements (INSERT, UPDATE, DELETE) are forbidden.
211
+
212
+ Queries can be as simple as
213
+
214
+ SELECT json_agg(u) AS result
215
+ FROM users u
216
+ WHERE u.active = true;
217
+
218
+ or more complex, using joins, subqueries, CTEs, and other SQL features. like:
219
+
220
+ WITH order_details AS (
221
+ SELECT
222
+ o.id AS order_id,
223
+ o.date AS order_date,
224
+ json_agg(
225
+ json_build_object(
226
+ 'item_id', i.id,
227
+ 'item_name', i.name,
228
+ 'quantity', oi.quantity,
229
+ 'price', oi.price
230
+ )
231
+ ) AS items
232
+ FROM orders o
233
+ JOIN order_items oi ON o.id = oi.order_id
234
+ JOIN items i ON i.id = oi.item_id
235
+ GROUP BY o.id
236
+ )
237
+ SELECT json_agg(
238
+ json_build_object(
239
+ 'order_id', od.order_id,
240
+ 'order_date', od.order_date,
241
+ 'customer', json_build_object(
242
+ 'id', c.id,
243
+ 'name', c.name
244
+ ),
245
+ 'items', od.items
246
+ )
247
+ ) AS result
248
+ FROM order_details od
249
+ JOIN customers c ON c.id = od.order_id;
250
+
251
+ ",
252
+ "tags": ["Raw"],
253
+ "security": [
254
+ "bearerAuth": []
255
+ ],
256
+ "responses": {
257
+ "200": {
258
+ "description": "SQL Query Result",
259
+ "content": {
260
+ "application/json": {
261
+ "schema": {
262
+ "type": "array",
263
+ "items": {
264
+ "type": "object",
265
+ "properties": {
266
+ "json_agg": {
267
+ "type": "string"
268
+ }
269
+ }
270
+ }
271
+ }
272
+ }
273
+ }
274
+ }
275
+ },
276
+ "requestBody": {
277
+ "content": {
278
+ "application/json": {
279
+ "schema": {
280
+ "type": "object",
281
+ "properties": {
282
+ "query": {
283
+ "type": "string",
284
+ "example": "SELECT json_agg(u) FROM users u WHERE u.active = true;"
285
+ }
286
+ }
287
+ }
288
+ }
289
+ }
290
+ }
291
+ }
292
+ },
201
293
  "/info/version": {
202
294
  "get": {
203
295
  "summary": "Version",
@@ -0,0 +1,17 @@
1
+ # require 'model_driven_api/version'
2
+ class Api::V2::RawController < Api::V2::ApplicationController
3
+ # Info uses a different auth method: username and password
4
+ # skip_before_action :authenticate_request, only: [:version, :swagger, :openapi], raise: false
5
+ skip_before_action :extract_model
6
+
7
+ # api :GET, '/api/v2/raw/sql'
8
+ def sql
9
+ # if params is nil, render 400
10
+ render json: { error: "Query is required" }, status: 400 and return if params[:query].nil?
11
+
12
+ query = params[:query]
13
+
14
+ render json: SafeSqlExecutor.execute_select(query).first["json_agg"], status: 200
15
+ end
16
+
17
+ end
data/config/routes.rb CHANGED
@@ -18,6 +18,10 @@ Rails.application.routes.draw do
18
18
  get :openapi
19
19
  end
20
20
 
21
+ namespace :raw do
22
+ post :sql
23
+ end
24
+
21
25
  post "authenticate" => "authentication#authenticate"
22
26
  post ":ctrl/search" => 'application#index'
23
27
 
@@ -1,3 +1,3 @@
1
1
  module ModelDrivenApi
2
- VERSION = "3.2.3".freeze
2
+ VERSION = "3.2.5".freeze
3
3
  end
@@ -15,6 +15,8 @@ require 'deep_merge/rails_compat'
15
15
 
16
16
  require "model_driven_api/engine"
17
17
 
18
+ require "safe_sql_executor"
19
+
18
20
  module ModelDrivenApi
19
21
  def self.smart_merge src, dest
20
22
  src.deeper_merge! dest, {
@@ -0,0 +1,20 @@
1
+ module SafeSqlExecutor
2
+ def self.execute_select(query)
3
+ # Validate the query
4
+ validate_select_query(query)
5
+
6
+ # Execute the query
7
+ ActiveRecord::Base.connection.execute(query)
8
+ end
9
+
10
+ private
11
+
12
+ def self.validate_select_query(query)
13
+ sanitized_query = query.strip.gsub(/\s+/, " ").upcase
14
+
15
+ # Allow SELECT or WITH...SELECT queries
16
+ unless sanitized_query.match?(/^(WITH .+)?SELECT /)
17
+ raise ArgumentError, "Only SELECT queries (including with CTEs) are allowed"
18
+ end
19
+ end
20
+ end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: model_driven_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.3
4
+ version: 3.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriele Tassoni
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-06-19 00:00:00.000000000 Z
10
+ date: 2025-01-08 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: thecore_backend_commons
@@ -124,6 +123,7 @@ files:
124
123
  - app/controllers/api/v2/application_controller.rb
125
124
  - app/controllers/api/v2/authentication_controller.rb
126
125
  - app/controllers/api/v2/info_controller.rb
126
+ - app/controllers/api/v2/raw_controller.rb
127
127
  - app/controllers/api/v2/users_controller.rb
128
128
  - app/models/endpoints/test_api.rb
129
129
  - app/models/test_api.rb
@@ -146,13 +146,13 @@ files:
146
146
  - lib/model_driven_api/engine.rb
147
147
  - lib/model_driven_api/version.rb
148
148
  - lib/non_crud_endpoints.rb
149
+ - lib/safe_sql_executor.rb
149
150
  - lib/tasks/model_driven_api_tasks.rake
150
151
  homepage: https://github.com/gabrieletassoni/model_driven_api
151
152
  licenses:
152
153
  - MIT
153
154
  metadata:
154
155
  allowed_push_host: https://rubygems.org
155
- post_install_message:
156
156
  rdoc_options: []
157
157
  require_paths:
158
158
  - lib
@@ -167,8 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
167
  - !ruby/object:Gem::Version
168
168
  version: '0'
169
169
  requirements: []
170
- rubygems_version: 3.5.11
171
- signing_key:
170
+ rubygems_version: 3.6.2
172
171
  specification_version: 4
173
172
  summary: Convention based RoR engine which uses DB schema introspection to create
174
173
  REST APIs.