yes-read-api 2.7.0 → 2.8.0
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/yes/read/api/queries_controller.rb +57 -12
- data/lib/yes/read/api/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d82a315c34db2028b08ec928dfa762687832077a9f2b5c49bf06c937a7759b61
|
|
4
|
+
data.tar.gz: b604a4bb25010bcf7e3180197fc1caf0dcbba43191849bb0a6e9249c3837e87c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5c772d5d935448c0a1137f16ff5b0afe6aea61181cd28d46e01fc96cf42d6e7304d232456f04f94a44cd5b1f9d006ad59974e0a658ff30b580f38a04663c6e18
|
|
7
|
+
data.tar.gz: 70d305e7f80e9a542654f7472718e300a9da5e27370011b2c0f8d1a7e4c3c93da8aa514e72e420e088d299a94a690f6162687199a81de13dcd678e14ad0da740
|
|
@@ -7,11 +7,15 @@ module Yes
|
|
|
7
7
|
module Api
|
|
8
8
|
class QueriesController < ApplicationController
|
|
9
9
|
before_action :authenticate_with_token
|
|
10
|
+
before_action :reject_ignored_filter_param
|
|
10
11
|
before_action :validate_advanced_payload, only: :advanced
|
|
11
12
|
before_action :process_own_filter, only: :call
|
|
12
13
|
|
|
13
14
|
RETRY_AFTER_SECONDS = 1
|
|
14
|
-
|
|
15
|
+
FILTER_DEFINITION_REQUIRED = 'filter_definition is required for an advanced query'
|
|
16
|
+
FILTERS_IGNORED_BY_ADVANCED = 'filters is not applied by an advanced query, use filter_definition'
|
|
17
|
+
STORED_FILTER_WITHOUT_DEFINITION = 'the stored filter has no filter_definition'
|
|
18
|
+
FILTER_DEFINITION_IGNORED_BY_BASIC = 'filter_definition is not applied by a basic query, use filters'
|
|
15
19
|
|
|
16
20
|
# Covers AuthenticationError raised outside #authenticate_with_token, e.g. by consumer
|
|
17
21
|
# request authorizers, filters or serializers.
|
|
@@ -32,6 +36,8 @@ module Yes
|
|
|
32
36
|
def call
|
|
33
37
|
persisted_filter = filter(read_model_name).persisted_filter_scope.find_by(id: params[:filter_id]) if params[:filter_id].present?
|
|
34
38
|
|
|
39
|
+
return bad_request_response(STORED_FILTER_WITHOUT_DEFINITION) if without_definition?(persisted_filter)
|
|
40
|
+
|
|
35
41
|
render json: response_json(persisted_filter:, filter_type: persisted_filter ? :advanced : :basic).to_json
|
|
36
42
|
end
|
|
37
43
|
|
|
@@ -54,7 +60,7 @@ module Yes
|
|
|
54
60
|
end
|
|
55
61
|
|
|
56
62
|
def response_json(filter_type: :basic, persisted_filter: nil)
|
|
57
|
-
filter_options = applied_filter_options(persisted_filter
|
|
63
|
+
filter_options = applied_filter_options(persisted_filter:)
|
|
58
64
|
|
|
59
65
|
request_authorizer.call(filter_options, auth_data)
|
|
60
66
|
|
|
@@ -67,17 +73,34 @@ module Yes
|
|
|
67
73
|
serialize(paginated_records, filter_options)
|
|
68
74
|
end
|
|
69
75
|
|
|
70
|
-
# The options the query is built from. They are what the request authorizer is handed too
|
|
71
|
-
#
|
|
72
|
-
#
|
|
76
|
+
# The options the query is built from. They are what the request authorizer is handed too.
|
|
77
|
+
# A stored filter body replaces the request params and runs as an advanced query, which
|
|
78
|
+
# applies `filter_definition` only, so a `filters` key in the body is dropped.
|
|
73
79
|
#
|
|
74
80
|
# @param persisted_filter [#body, nil] stored filter whose body replaces the request params
|
|
75
|
-
# @
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
# @return [Hash] the stored body (without `filters`) or the request params
|
|
82
|
+
def applied_filter_options(persisted_filter:)
|
|
83
|
+
return params unless persisted_filter
|
|
84
|
+
|
|
85
|
+
stored_filter_options(persisted_filter).except(:filters)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# An advanced query returns the unbounded base scope without a `filter_definition`.
|
|
89
|
+
#
|
|
90
|
+
# @param persisted_filter [#body, nil] stored filter
|
|
91
|
+
# @return [Boolean] whether a stored filter was found whose body has no `filter_definition`;
|
|
92
|
+
# a body that is not a Hash (the jsonb column also holds nil, arrays, strings) has none
|
|
93
|
+
def without_definition?(persisted_filter)
|
|
94
|
+
return false if persisted_filter.blank?
|
|
95
|
+
return true unless persisted_filter.body.is_a?(Hash)
|
|
79
96
|
|
|
80
|
-
|
|
97
|
+
stored_filter_options(persisted_filter)[:filter_definition].blank?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# @param persisted_filter [#body] stored filter with a Hash body
|
|
101
|
+
# @return [Hash] its body with symbol keys and the requested model
|
|
102
|
+
def stored_filter_options(persisted_filter)
|
|
103
|
+
persisted_filter.body.deep_symbolize_keys.merge(model: params[:model])
|
|
81
104
|
end
|
|
82
105
|
|
|
83
106
|
def request_authorizer
|
|
@@ -162,9 +185,31 @@ module Yes
|
|
|
162
185
|
)
|
|
163
186
|
end
|
|
164
187
|
|
|
165
|
-
|
|
166
|
-
|
|
188
|
+
# Rejects a request that carries the filter parameter its query does not apply, or lacks the one
|
|
189
|
+
# it needs. Request authorizers pick what to check by which parameter is present, so a parameter
|
|
190
|
+
# the query silently ignores could be authorized without bounding the result. It runs as a
|
|
191
|
+
# before_action, so consumer actions of subclasses are covered too.
|
|
192
|
+
#
|
|
193
|
+
# @return [void]
|
|
194
|
+
def reject_ignored_filter_param
|
|
195
|
+
if request.post?
|
|
196
|
+
return bad_request_response(FILTER_DEFINITION_REQUIRED) if params[:filter_definition].blank?
|
|
197
|
+
|
|
198
|
+
bad_request_response(FILTERS_IGNORED_BY_ADVANCED) if params.key?(:filters)
|
|
199
|
+
elsif params.key?(:filter_definition)
|
|
200
|
+
bad_request_response(FILTER_DEFINITION_IGNORED_BY_BASIC)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Renders a 400 with the reason the request is malformed.
|
|
205
|
+
#
|
|
206
|
+
# @param detail [String] why the request is rejected and what to send instead
|
|
207
|
+
# @return [void]
|
|
208
|
+
def bad_request_response(detail)
|
|
209
|
+
render(json: { title: 'Bad Request', detail: }.to_json, status: :bad_request)
|
|
210
|
+
end
|
|
167
211
|
|
|
212
|
+
def validate_advanced_payload
|
|
168
213
|
validation_result = Yes::Read::Api::AdvancedFilterValidator.call(params)
|
|
169
214
|
return if validation_result.success?
|
|
170
215
|
|
data/lib/yes/read/api/version.rb
CHANGED