model_driven_api 3.2.4 → 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: 010f1acfbe42371a8465ba5eeae1f6a5cc0d8c7d5891c30b52ed2c9056bfd7d0
4
- data.tar.gz: fb81afbec2d0ce91297c68b212a97e213c0b0d7cf15630c82af753f5511ec1f7
3
+ metadata.gz: e73b3aaf951e74746e4f15fd020a5e9811b13f8222bee893e3b657323dbea0f7
4
+ data.tar.gz: 38f200e4c7550d9f15ff356e3323fe2a4bdaa5328f6c4c94e5d05401de1cf60a
5
5
  SHA512:
6
- metadata.gz: '01941ccd88639849a80e1068266b083911915793f0513835e6caf2c61b718d7c4c80d0c57c4a59f25f4a64ddf85a728d18fccada0cf7e53e813b39293b26b339'
7
- data.tar.gz: e9243797670a8dba2dbd87a2e79571dbb5e39dea8fa26379934a08fe74137db7bb053fc73bbb63d811962397bff5e4cb64c11fdf7b65b5abcfe363827a7185df
6
+ metadata.gz: 84c35cf76c50627514dc34702c136f3506fed645ccfb2228d26c21338de8b310e598d18fb302c11675056d0956310641fe0b1d0c60edfb5869ef88e718643833
7
+ data.tar.gz: 003ee1a957b5f8a2dc9c40c68acf8d57ec4d64ae5e29f26452ea7f763db37e93bb2cd6dd410ca4d31535e8b1613252a335b8efa75cd6462a0bbd2131df8a25c0
@@ -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.4".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.4
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-07-22 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.