model_driven_api 2.3.6 → 2.3.11

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: 4f077fe5622ad57731dec49be396e6a30cd59d643a56c959f7ada5d7b1515302
4
- data.tar.gz: dd18358e8de2ed6813383ee84910b35557207c6de2f31b7244c8a222e382c8f3
3
+ metadata.gz: f318cd8434ed86ff9045ec0a2849774d2d4232f9b057ea70c2313f2255011ff6
4
+ data.tar.gz: b587c4fee867ea15389db03d191c899061856fc8a53beea17bf7b88f0aa86cf3
5
5
  SHA512:
6
- metadata.gz: f6a0cac8c48611a3e78f0f6fd9e6a1ce6dcb99d82d54170803cf42f638cddd7fb6a5ee3bfc0edf6c3900a0d7627e4c6936f011c1e91fb6522599a844677577d4
7
- data.tar.gz: 046ca81694a0051a68368b52a7808ffc961c28a4d336dc5e92fcebfe4e4ec7370bd54b8dc4e0c40a0f2f9c179ae0f8ea2adfae509c0394086dc357e6c97a1213
6
+ metadata.gz: d92b432a32ee4a23c2209f66ab40eb6d2f56bb3fc9b7229da63eba8a105aacdfcfea36b21cb2739f879077e9f736e1b0ba3e5c4d0602b070cc388aaec684cefa
7
+ data.tar.gz: 330b5cbc65fed482fc7af84368967ae3987047d6235f68b5f8fa1b8757c626dbfcde0f6bdde13d3f644666850f24be6cd9a793ffe6ea528ed78b35d9dc8cb933
data/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # Model Driven Api
2
2
 
3
- ## Goal
3
+ ## Goals
4
4
 
5
- To have a comprehensive and meaningful Model driven API right out of the box by just creating migrations in your rails app or engine. With all the CRUD operations in place out of the box and easily expandable with custom actions if needed.
5
+ * To have a comprehensive and meaningful Model driven API right out of the box by just creating migrations in your rails app or engine. With all the CRUD operations in place out of the box and easily expandable with custom actions if needed.
6
+ * To have a plain REST implementation which adapts the returned JSON to the specific needs of the client, **without the need to change backend code**, this may overcome the biggest disadvantage of REST vs GraphQL = client driver presentation.
6
7
 
7
8
  ## TL;DR 5-10 minutes adoption
8
9
 
@@ -27,6 +28,55 @@ I've always been interested in effortless, no-fuss, conventions' based developme
27
28
 
28
29
  Doing this means also narrowing a bit the scope of the tools, taking decisions, at least for the first implementations and versions of this engine, so, this works well if the data is relational, this is a prerequisite (postgres, mysql, mssql, etc.).
29
30
 
31
+ ## REST Enhanced
32
+
33
+ Thanks to the inclusion of [Ransack](https://github.com/activerecord-hackery/ransack/wiki) and [ActiveModel::Serializer](https://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html), by just adding the querystring keys **q** and **a**, you can create complex queries (q) to obtain just the records you need, which present in the returnd JSON just the attributes (a) needed.
34
+ By combining the two keys, you can obtain just the data you want.
35
+
36
+ The *json_attrs* or *a* query string passed accepts these keys (Please see [ActiveModel::Serializer](https://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html) for reference):
37
+ - only: list [] of model fields to be shown in JSON serialization
38
+ - except: exclude these fields from the JSON serialization, this is a list []
39
+ - methods: include the result of some methods defined in the model, this is a list []
40
+ - include: include associated models, it's an object {} which also accepts the keys described in this document (only, except, methods, include)
41
+
42
+ ### Example
43
+
44
+ ```
45
+ {{ base_url }}/{{ controller }}?a[only][]=locked&a[only][]=username&a[methods][]=jwe_data
46
+ ```
47
+
48
+ Is translated to:
49
+
50
+ ```
51
+ {a: {only: ["locked", "username"], methods: ["jwe_data"]}}
52
+ ```
53
+
54
+ Which tells the API controller just to return this optimized serialization:
55
+
56
+ ```
57
+ [
58
+ {
59
+ "username": "Administrator",
60
+ "locked": false,
61
+ "jwe_data": "eyJhbGciOiJkaXIiLCJlbmMiOiJSMTI4R0NNIn0..yz0tnC6y3BzgoOsO.BjHb9CRIb0vrv7nnEx54Ac8-cATPJ9sTlQSSxRbTmtcPHc5KhvtyE_hyBRnIcK92bzUBRwdy6ASB2XJVy1VfWxAmO8E.4tOzJlfuXi-shaRhDSkOyg"
62
+ }
63
+ ]
64
+ ```
65
+
66
+ By combinig with Ransack's **q** query string key (please read [Ransack](https://github.com/activerecord-hackery/ransack/wiki) documentation to discover all the possible and complex searches you can make), you can obtain right what you want:
67
+
68
+ ```
69
+ {{ base_url }}/{{ controller }}?a[only][]=locked&a[only][]=username&a[methods][]=jwe_data&q[email_cont][]=adm
70
+ ```
71
+
72
+ Which translates to:
73
+
74
+ ```
75
+ {a: {only: ["locked", "username"], methods: ["jwe_data"]}, q: { email_cont: ["adm"]}}
76
+ ```
77
+
78
+ For bigger searches, which may over crowd the querystring length, you can always use the default [Search](#Search) POST endpoint.
79
+
30
80
  ## v2?
31
81
 
32
82
  Yes, this is the second version of such an effort and you can note it from the api calls, which are all under the ```/api/v2``` namespace the [/api/v1](https://github.com/gabrieletassoni/thecore_api) one, was were it all started, many ideas are ported from there, such as the generation of the automatic model based crud actions, as well as custom actions definitions and all the things that make also this gem useful for my daily job were already in place, but it was too coupled with [thecore](https://github.com/gabrieletassoni/thecore)'s [rails_admin](https://github.com/sferik/rails_admin) UI, making it impossible to create a complete UI-less, API only application, out of the box and directly based of the DB schema, with all the bells and whistles I needed (mainly self adapting, data and schema driven API functionalities).
@@ -20,7 +20,10 @@ class Api::V2::ApplicationController < ActionController::API
20
20
  return render json: result, status: 200 if status == true
21
21
 
22
22
  # Normal Index Action with Ransack querying
23
- @q = (@model.column_names.include?("user_id") ? @model.where(user_id: current_user.id) : @model).ransack(@query.presence|| params[:q])
23
+ # Keeping this automation can be too dangerous and lead to unpredicted results
24
+ # TODO: Remove it
25
+ # @q = (@model.column_names.include?("user_id") ? @model.where(user_id: current_user.id) : @model).ransack(@query.presence|| params[:q])
26
+ @q = @model.ransack(@query.presence|| params[:q])
24
27
  @records_all = @q.result # (distinct: true) Removing, but I'm not sure, with it I cannot sort in postgres for associated records (throws an exception on misuse of sort with distinct)
25
28
  page = (@page.presence || params[:page])
26
29
  per = (@per.presence || params[:per])
@@ -66,7 +69,9 @@ class Api::V2::ApplicationController < ActionController::API
66
69
  return render json: result, status: 200 if status == true
67
70
 
68
71
  # Normal Create Action
69
- @record.user_id = current_user.id if @model.column_names.include? "user_id"
72
+ # Keeping this automation can be too dangerous and lead to unpredicted results
73
+ # TODO: Remove it
74
+ # @record.user_id = current_user.id if @model.column_names.include? "user_id"
70
75
  @record.save!
71
76
  render json: @record.to_json(json_attrs), status: 201
72
77
  end
@@ -108,7 +113,8 @@ class Api::V2::ApplicationController < ActionController::API
108
113
  resource = "custom_action_#{params[:do]}"
109
114
  raise NoMethodError unless @model.respond_to?(resource)
110
115
  # return true, MultiJson.dump(params[:id].blank? ? @model.send(resource, params) : @model.send(resource, params[:id].to_i, params))
111
- return true, MultiJson.dump(@model.send(resource, params))
116
+ puts json_attrs
117
+ return true, @model.send(resource, params).to_json(json_attrs)
112
118
  end
113
119
  # if it's here there is no custom action in the request querystring
114
120
  return false
@@ -143,12 +149,19 @@ class Api::V2::ApplicationController < ActionController::API
143
149
 
144
150
  def find_record
145
151
  record_id ||= (params[:path].split("/").second.to_i rescue nil)
146
- @record = @model.column_names.include?("user_id") ? @model.where(id: (record_id.presence || @record_id.presence || params[:id]), user_id: current_user.id).first : @model.find((@record_id.presence || params[:id]))
152
+ # Keeping this automation can be too dangerous and lead to unpredicted results
153
+ # TODO: Remove it
154
+ # @record = @model.column_names.include?("user_id") ? @model.where(id: (record_id.presence || @record_id.presence || params[:id]), user_id: current_user.id).first : @model.find((@record_id.presence || params[:id]))
155
+ @record = @model.find((@record_id.presence || params[:id]))
147
156
  return not_found! if @record.blank?
148
157
  end
149
158
 
150
159
  def json_attrs
151
- ((@model.json_attrs.presence || @json_attrs.presence || {}) rescue {})
160
+ # In order of importance: if you send the configuration via querystring you are ok
161
+ # has precedence over if you have setup the json_attrs in the model concern
162
+ from_params = params[:a].deep_symbolize_keys unless params[:a].blank?
163
+ from_params = params[:json_attrs].deep_symbolize_keys unless params[:json_attrs].blank?
164
+ from_params.presence || @json_attrs.presence || @model.json_attrs.presence || {} rescue {}
152
165
  end
153
166
 
154
167
  def extract_model
@@ -6,7 +6,7 @@ class Api::V2::InfoController < Api::V2::ApplicationController
6
6
 
7
7
  # api :GET, '/api/v2/info/version', "Just prints the APPVERSION."
8
8
  def version
9
- render json: { version: ModelDrivenApi::VERSION }.to_json, status: 200
9
+ render json: { version: "TODO: Find a Way to Dynamically Obtain It" }.to_json, status: 200
10
10
  end
11
11
 
12
12
  # api :GET, '/api/v2/info/roles'
@@ -0,0 +1,8 @@
1
+ # Turns API calls to always UTC, leaving the APP the freedom to use local timezone
2
+ module ActiveSupport
3
+ class TimeWithZone
4
+ def as_json(options = nil)
5
+ utc
6
+ end
7
+ end
8
+ end
@@ -10,6 +10,7 @@ module ApiExceptionManagement
10
10
  rescue_from ActiveModel::ForbiddenAttributesError, with: :fivehundred!
11
11
  rescue_from ActiveRecord::RecordInvalid, with: :invalid!
12
12
  rescue_from ActiveRecord::RecordNotFound, with: :not_found!
13
+ rescue_from ActiveRecord::RecordNotUnique, with: :invalid!
13
14
  end
14
15
 
15
16
  def unauthenticated! exception = AuthenticateUser::AccessDenied.new
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: 2.3.6
4
+ version: 2.3.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriele Tassoni
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-16 00:00:00.000000000 Z
11
+ date: 2021-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thecore_backend_commons
@@ -142,6 +142,7 @@ files:
142
142
  - config/initializers/after_initialize_for_model_driven_api.rb
143
143
  - config/initializers/cors_api_thecore.rb
144
144
  - config/initializers/knock.rb
145
+ - config/initializers/time_with_zone.rb
145
146
  - config/initializers/wrap_parameters.rb
146
147
  - config/routes.rb
147
148
  - lib/concerns/api_exception_management.rb