parse-stack-next 5.5.1 → 5.5.2
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/CHANGELOG.md +45 -0
- data/Gemfile.lock +1 -1
- data/lib/parse/client/body_builder.rb +71 -8
- data/lib/parse/query.rb +109 -34
- data/lib/parse/stack/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: 2d234769063a852058f1024815a4cb5e804646bfe54c5bd316e4730e4e451451
|
|
4
|
+
data.tar.gz: cf3d7dcdeee49dd7b74fdaf241d205c98519e0464171c232bc565a2d844fe71a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e32cb99c46fc779dcb7595b7fe8dd95592411856ee079bf969f8c9f2a28cd7739a3785af6cad45fe741b868d69f8c1ce74c7d46bd8cd5a439ea8d7c225434ee4
|
|
7
|
+
data.tar.gz: 51548942c4b24a7e9c3d5323269962ba9212dce7f3b58ab6bddc3d9199df9a8976698b3bfce690c5dbd42726ea1260825dfd8567f777b14bb1793d941cdb302e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
## parse-stack-next Changelog
|
|
2
2
|
|
|
3
|
+
### 5.5.2
|
|
4
|
+
|
|
5
|
+
#### Large aggregation pipelines no longer fail with "Invalid aggregate stage '0'"
|
|
6
|
+
|
|
7
|
+
- **FIXED**: An aggregation whose request URL exceeds ~2KB (for example a
|
|
8
|
+
`group_by`, `group_by_date`, `distinct`, or custom `aggregate` pipeline with
|
|
9
|
+
a large `$in` / `$match`) is rewritten from a GET to a POST carrying
|
|
10
|
+
`_method=GET`, with the query moved into the request body. The pipeline was
|
|
11
|
+
sent in the body as a URL-encoded string, but Parse Server's aggregate
|
|
12
|
+
endpoint only JSON-decodes query-string params, not body params — so the
|
|
13
|
+
pipeline arrived as a raw string and was rejected with
|
|
14
|
+
`Invalid aggregate stage '0'`, causing the aggregation to return an empty
|
|
15
|
+
result. The long-URL override now sends a JSON body for the aggregate
|
|
16
|
+
endpoint so the pipeline is delivered as a real array (boolean params such as
|
|
17
|
+
`rawValues` are preserved as booleans). The historical URL-encoded override is
|
|
18
|
+
unchanged for `find` and other endpoints, which Parse Server already decodes
|
|
19
|
+
correctly.
|
|
20
|
+
|
|
21
|
+
#### Aggregations inside `Parse.with_session` blocks are now scoped
|
|
22
|
+
|
|
23
|
+
- **FIXED**: `group_by_date`, `group_by`, `distinct`, and `count` (aggregation
|
|
24
|
+
branch) now detect the ambient session token set by `Parse.with_session` and
|
|
25
|
+
treat the query as scoped — consistent with how `Parse::Client#request`
|
|
26
|
+
already scopes REST find/get/count calls in the same block. Previously the
|
|
27
|
+
`query_is_scoped?` / `distinct_query_is_scoped?` checks consulted only the
|
|
28
|
+
query instance's own `session_token=` / `scope_to_user` / `scope_to_role`
|
|
29
|
+
and ignored `Parse.current_session_token`, so an aggregation inside a
|
|
30
|
+
`with_session` block ran unscoped as the master key and returned all rows
|
|
31
|
+
regardless of ACL. The checks now include the ambient: when scoped and
|
|
32
|
+
mongo-direct is available the aggregation auto-promotes (ACL/CLP enforced);
|
|
33
|
+
when scoped and mongo-direct is unavailable it fails closed with
|
|
34
|
+
`MongoDirectRequired` rather than silently leaking rows.
|
|
35
|
+
- **FIXED**: `group_by_date` now also fails closed (`MongoDirectRequired`) when
|
|
36
|
+
the query is scoped but mongo-direct is unavailable — matching the existing
|
|
37
|
+
behavior of `group_by`, `distinct`, and `count`. Previously `group_by_date`
|
|
38
|
+
silently fell back to the REST `/aggregate` endpoint in that case.
|
|
39
|
+
- **FIXED**: A regression introduced in 5.5.1 where `group_by_date`,
|
|
40
|
+
`group_by`, and pipeline-based aggregations called inside a
|
|
41
|
+
`Parse.with_session` block returned empty results `{}`. The ambient session
|
|
42
|
+
token was forwarded as an HTTP session-token header (suppressing the master
|
|
43
|
+
key), causing Parse Server's REST `/aggregate` endpoint — which is
|
|
44
|
+
master-key-only — to return a 401/403. The REST aggregate call sites now
|
|
45
|
+
force `use_master_key: true` so the ambient cannot suppress it, unless the
|
|
46
|
+
caller explicitly set `use_master_key: false`.
|
|
47
|
+
|
|
3
48
|
### 5.5.1
|
|
4
49
|
|
|
5
50
|
#### Mongo-direct reads inside `Parse.with_session` are now scoped, not master
|
data/Gemfile.lock
CHANGED
|
@@ -285,14 +285,32 @@ module Parse
|
|
|
285
285
|
# to be POST instead of GET and send the query parameters in the body of the POST request.
|
|
286
286
|
# The standard maximum POST request (which is a server setting), is usually set to 20MBs
|
|
287
287
|
if env[:method] == :get && env[:url].to_s.length >= MAX_URL_LENGTH
|
|
288
|
-
env[:
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
288
|
+
if aggregate_request?(env[:url])
|
|
289
|
+
# Parse Server's AggregateRouter only JSON-decodes query-string
|
|
290
|
+
# params (via JSONFromQuery); it does NOT decode a `pipeline` param
|
|
291
|
+
# that arrives in the request body. The urlencoded override below
|
|
292
|
+
# would therefore deliver `pipeline` as a raw JSON *string*, which
|
|
293
|
+
# AggregateRouter.getPipeline mis-reads character-by-character and
|
|
294
|
+
# rejects with "Invalid aggregate stage '0'". Send a JSON body
|
|
295
|
+
# instead so the pipeline survives as a real Array. `_method=GET`
|
|
296
|
+
# still routes Parse Server to its GET-only aggregate handler.
|
|
297
|
+
env[:request_headers][HTTP_METHOD_OVERRIDE] = "GET"
|
|
298
|
+
env[:request_headers][CONTENT_TYPE] = CONTENT_TYPE_FORMAT
|
|
299
|
+
env[:body] = aggregate_override_body(env[:url].query)
|
|
300
|
+
env[:url].query = nil
|
|
301
|
+
env[:method] = :post
|
|
302
|
+
else
|
|
303
|
+
env[:request_headers][HTTP_METHOD_OVERRIDE] = "GET"
|
|
304
|
+
env[:request_headers][CONTENT_TYPE] = "application/x-www-form-urlencoded"
|
|
305
|
+
# parse-server looks for method overrides in the body under the `_method` param.
|
|
306
|
+
# so we will add it to the query string, which will now go into the body.
|
|
307
|
+
# `.to_s` guards the (contrived but possible) case of a >=2KB URL whose
|
|
308
|
+
# length is all path and no query — nil + String would raise TypeError.
|
|
309
|
+
env[:body] = "_method=GET&" + env[:url].query.to_s
|
|
310
|
+
env[:url].query = nil
|
|
311
|
+
#override
|
|
312
|
+
env[:method] = :post
|
|
313
|
+
end
|
|
296
314
|
# else if not a get, always make sure the request is JSON encoded if the content type matches
|
|
297
315
|
elsif env[:request_headers][CONTENT_TYPE] == CONTENT_TYPE_FORMAT &&
|
|
298
316
|
(env[:body].is_a?(Hash) || env[:body].is_a?(Array))
|
|
@@ -334,6 +352,51 @@ module Parse
|
|
|
334
352
|
response_env[:body] = r
|
|
335
353
|
end
|
|
336
354
|
end
|
|
355
|
+
|
|
356
|
+
private
|
|
357
|
+
|
|
358
|
+
# Whether the request targets Parse Server's `/aggregate/<Class>`
|
|
359
|
+
# endpoint. Used by {#call!} to pick the JSON-body form of the
|
|
360
|
+
# long-URL GET→POST override (the aggregate endpoint does not
|
|
361
|
+
# JSON-decode a body `pipeline` param, unlike `where`).
|
|
362
|
+
#
|
|
363
|
+
# Anchored to the final two path segments: `.../aggregate/<ClassName>`
|
|
364
|
+
# where <ClassName> is the last segment (no further slashes). The
|
|
365
|
+
# className is mandatory and slash-free — see
|
|
366
|
+
# {Parse::API::Aggregate#aggregate_uri_path}, which validates it via
|
|
367
|
+
# PathSegment.identifier! — so a real aggregate URL always ends this way.
|
|
368
|
+
# A `find` request is `.../classes/<ClassName>` (no match), a class
|
|
369
|
+
# merely *named* with "aggregate" (e.g. `MyAggregateData`) does not match,
|
|
370
|
+
# and an `/aggregate/` segment appearing earlier in a custom mount prefix
|
|
371
|
+
# (e.g. `/aggregate/api/classes/Foo`) does not match either.
|
|
372
|
+
# @param url [URI] the request URL.
|
|
373
|
+
# @return [Boolean]
|
|
374
|
+
def aggregate_request?(url)
|
|
375
|
+
url.path.to_s.match?(%r{/aggregate/[^/]+/?\z})
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# Build the JSON request body for a long-URL aggregate GET→POST
|
|
379
|
+
# override. Reconstructs the params from the encoded query string and
|
|
380
|
+
# JSON-decodes each value so the `pipeline` Array (and boolean
|
|
381
|
+
# `rawValues`/`rawFieldNames`) reach Parse Server as real types rather
|
|
382
|
+
# than strings. A value that is not itself JSON is passed through
|
|
383
|
+
# unchanged. `_method=GET` is injected so Parse Server routes the POST
|
|
384
|
+
# to its GET-only aggregate handler.
|
|
385
|
+
# @param query_string [String, nil] the encoded query string.
|
|
386
|
+
# @return [String] the JSON body to send.
|
|
387
|
+
def aggregate_override_body(query_string)
|
|
388
|
+
params = Faraday::Utils.parse_query(query_string.to_s) || {}
|
|
389
|
+
body = { "_method" => "GET" }
|
|
390
|
+
params.each do |key, value|
|
|
391
|
+
body[key] =
|
|
392
|
+
begin
|
|
393
|
+
JSON.parse(value)
|
|
394
|
+
rescue JSON::ParserError, TypeError
|
|
395
|
+
value
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
body.to_json
|
|
399
|
+
end
|
|
337
400
|
end
|
|
338
401
|
end #Middleware
|
|
339
402
|
end
|
data/lib/parse/query.rb
CHANGED
|
@@ -1811,13 +1811,25 @@ module Parse
|
|
|
1811
1811
|
# Whether this query carries a non-master-key auth scope. Used by
|
|
1812
1812
|
# `#distinct` (and group_by aggregations) to decide whether to
|
|
1813
1813
|
# auto-promote the REST aggregate path to mongo-direct so the SDK's
|
|
1814
|
-
# ACLScope / CLPScope enforcement actually runs.
|
|
1814
|
+
# ACLScope / CLPScope enforcement actually runs. Also detects the
|
|
1815
|
+
# fiber-local ambient session set by Parse.with_session so that
|
|
1816
|
+
# aggregations inside a with_session block are treated as scoped —
|
|
1817
|
+
# consistent with how Parse::Client#request already scopes REST calls.
|
|
1815
1818
|
# @return [Boolean]
|
|
1816
1819
|
# @api private
|
|
1817
1820
|
def distinct_query_is_scoped?
|
|
1818
1821
|
return true if @session_token.is_a?(String) && !@session_token.empty?
|
|
1819
1822
|
return true if @acl_user
|
|
1820
1823
|
return true if @acl_role
|
|
1824
|
+
# An ambient Parse.with_session counts as scope ONLY when the query did
|
|
1825
|
+
# not explicitly request master-key mode — mirroring Parse::Client#request,
|
|
1826
|
+
# where an explicit use_master_key: true is a deliberate admin call that
|
|
1827
|
+
# skips the ambient session. Otherwise an admin aggregation inside a
|
|
1828
|
+
# with_session block would be wrongly forced to mongo-direct / fail-closed.
|
|
1829
|
+
unless use_master_key == true
|
|
1830
|
+
ambient = ambient_session_token
|
|
1831
|
+
return true if ambient.is_a?(String) && !ambient.empty?
|
|
1832
|
+
end
|
|
1821
1833
|
false
|
|
1822
1834
|
end
|
|
1823
1835
|
|
|
@@ -1832,12 +1844,13 @@ module Parse
|
|
|
1832
1844
|
def raise_scoped_aggregation_requires_mongo_direct!
|
|
1833
1845
|
raise MongoDirectRequired,
|
|
1834
1846
|
"[Parse::Query] This scoped aggregation (session_token / " \
|
|
1835
|
-
"scope_to_user / scope_to_role
|
|
1836
|
-
"SDK can enforce ACL/CLP.
|
|
1837
|
-
"endpoint is master-key-only and
|
|
1838
|
-
"it there would silently run unscoped
|
|
1839
|
-
"Enable mongo-direct via
|
|
1840
|
-
"rewrite without the
|
|
1847
|
+
"scope_to_user / scope_to_role, or an active Parse.with_session " \
|
|
1848
|
+
"block) requires mongo-direct so the SDK can enforce ACL/CLP. " \
|
|
1849
|
+
"Parse Server's REST /aggregate endpoint is master-key-only and " \
|
|
1850
|
+
"enforces neither, so routing it there would silently run unscoped " \
|
|
1851
|
+
"as the master key. Enable mongo-direct via " \
|
|
1852
|
+
"Parse::MongoDB.configure(...), or rewrite without the " \
|
|
1853
|
+
"aggregation terminal."
|
|
1841
1854
|
end
|
|
1842
1855
|
|
|
1843
1856
|
# Scope a query to a specific user's row-level ACL when it auto-routes
|
|
@@ -5996,13 +6009,21 @@ module Parse
|
|
|
5996
6009
|
if @mongo_direct && defined?(Parse::MongoDB) && Parse::MongoDB.enabled?
|
|
5997
6010
|
@cached_response = execute_direct!
|
|
5998
6011
|
else
|
|
6012
|
+
# REST /aggregate is master-key-only. An ambient Parse.with_session
|
|
6013
|
+
# block would suppress the master key via session_token, causing a
|
|
6014
|
+
# 401/403. Force use_master_key unless the caller explicitly disabled
|
|
6015
|
+
# it (use_master_key: false is a deliberate client-mode decision).
|
|
6016
|
+
# `.dup` keeps the master-key flip local to this call even if `_opts`
|
|
6017
|
+
# ever returns a shared/memoized hash.
|
|
6018
|
+
rest_opts = @query.send(:_opts).dup
|
|
6019
|
+
rest_opts[:use_master_key] = true unless rest_opts[:use_master_key] == false
|
|
5999
6020
|
@cached_response = @query.client.aggregate_pipeline(
|
|
6000
6021
|
@query.instance_variable_get(:@table),
|
|
6001
6022
|
@pipeline,
|
|
6002
6023
|
headers: {},
|
|
6003
6024
|
raw_values: @raw_values,
|
|
6004
6025
|
raw_field_names: @raw_field_names,
|
|
6005
|
-
|
|
6026
|
+
**rest_opts,
|
|
6006
6027
|
)
|
|
6007
6028
|
end
|
|
6008
6029
|
|
|
@@ -6388,16 +6409,24 @@ module Parse
|
|
|
6388
6409
|
# @return [Array<Hash>] raw aggregation results
|
|
6389
6410
|
def raw(operation, aggregation_expr)
|
|
6390
6411
|
formatted_group_field = @query.send(:format_aggregation_field, @group_field)
|
|
6391
|
-
pipeline = build_pipeline(formatted_group_field, aggregation_expr)
|
|
6392
6412
|
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6413
|
+
# Build the same pipeline the count/sum/etc. terminals use, then delegate
|
|
6414
|
+
# to Query#aggregate. That central path handles scoped-query routing
|
|
6415
|
+
# (session_token / acl_user / acl_role / ambient Parse.with_session →
|
|
6416
|
+
# auto-promote to mongo-direct, or fail closed when unavailable) so a
|
|
6417
|
+
# scoped `raw` is never sent to the master-key-only REST /aggregate
|
|
6418
|
+
# endpoint, and it returns the raw Array<Hash> rows this method documents.
|
|
6419
|
+
# `$match` from the query's where constraints is added by Query#aggregate.
|
|
6420
|
+
pipeline = []
|
|
6421
|
+
pipeline << { "$unwind" => "$#{formatted_group_field}" } if @flatten_arrays
|
|
6422
|
+
pipeline << { "$group" => { "_id" => "$#{formatted_group_field}", "count" => aggregation_expr } }
|
|
6423
|
+
add_fields = size_addfields_stage
|
|
6424
|
+
pipeline << add_fields if add_fields
|
|
6425
|
+
sort = sort_stage
|
|
6426
|
+
pipeline << sort if sort
|
|
6427
|
+
pipeline << { "$project" => { "_id" => 0, "objectId" => "$_id", "count" => 1 } }
|
|
6399
6428
|
|
|
6400
|
-
|
|
6429
|
+
@query.aggregate(pipeline, verbose: @query.instance_variable_get(:@verbose_aggregate)).raw || []
|
|
6401
6430
|
end
|
|
6402
6431
|
|
|
6403
6432
|
# Count the number of items in each group.
|
|
@@ -6541,8 +6570,12 @@ module Parse
|
|
|
6541
6570
|
# already does this auto-promotion (lib/parse/agent/tools.rb), this
|
|
6542
6571
|
# is the equivalent at the Query layer for direct SDK callers.
|
|
6543
6572
|
use_mongo_direct = @mongo_direct
|
|
6544
|
-
if !use_mongo_direct && query_is_scoped?
|
|
6545
|
-
|
|
6573
|
+
if !use_mongo_direct && query_is_scoped?
|
|
6574
|
+
if parse_mongodb_available?
|
|
6575
|
+
use_mongo_direct = true
|
|
6576
|
+
else
|
|
6577
|
+
@query.send(:raise_scoped_aggregation_requires_mongo_direct!)
|
|
6578
|
+
end
|
|
6546
6579
|
end
|
|
6547
6580
|
|
|
6548
6581
|
if use_mongo_direct
|
|
@@ -6779,15 +6812,22 @@ module Parse
|
|
|
6779
6812
|
end
|
|
6780
6813
|
|
|
6781
6814
|
# Whether the parent query carries any non-master-key auth scope. A
|
|
6782
|
-
# session_token, acl_user,
|
|
6783
|
-
#
|
|
6784
|
-
# mongo-direct path. Used to decide whether to
|
|
6785
|
-
# aggregation path to mongo-direct.
|
|
6815
|
+
# session_token, acl_user, acl_role, or an active Parse.with_session
|
|
6816
|
+
# ambient means the caller expects ACL-filtered results — which only
|
|
6817
|
+
# the SDK's mongo-direct path provides. Used to decide whether to
|
|
6818
|
+
# auto-promote the REST aggregation path to mongo-direct.
|
|
6786
6819
|
def query_is_scoped?
|
|
6787
6820
|
st = @query.session_token
|
|
6788
6821
|
return true if st.is_a?(String) && !st.empty?
|
|
6789
6822
|
return true if @query.instance_variable_get(:@acl_user)
|
|
6790
6823
|
return true if @query.instance_variable_get(:@acl_role)
|
|
6824
|
+
# Ambient Parse.with_session counts as scope only when the query did not
|
|
6825
|
+
# explicitly set use_master_key: true (matches Parse::Client#request
|
|
6826
|
+
# precedence — an explicit master-key call skips the ambient session).
|
|
6827
|
+
unless @query.use_master_key == true
|
|
6828
|
+
ambient = @query.send(:ambient_session_token)
|
|
6829
|
+
return true if ambient.is_a?(String) && !ambient.empty?
|
|
6830
|
+
end
|
|
6791
6831
|
false
|
|
6792
6832
|
end
|
|
6793
6833
|
|
|
@@ -7228,14 +7268,19 @@ module Parse
|
|
|
7228
7268
|
# Format the date field name
|
|
7229
7269
|
formatted_date_field = @query.send(:format_aggregation_field, @date_field)
|
|
7230
7270
|
|
|
7231
|
-
# Auto-promote scoped queries to mongo-direct.
|
|
7232
|
-
#
|
|
7233
|
-
#
|
|
7234
|
-
#
|
|
7235
|
-
# mongo-direct
|
|
7271
|
+
# Auto-promote scoped queries to mongo-direct. REST `/aggregate` is
|
|
7272
|
+
# master-key-only and enforces neither ACL nor CLP — a scoped query
|
|
7273
|
+
# (session_token / acl_user / acl_role, or an active
|
|
7274
|
+
# Parse.with_session block) must use the SDK's enforcement layers.
|
|
7275
|
+
# Fail closed if mongo-direct is unavailable rather than silently
|
|
7276
|
+
# returning unscoped rows. Mirrors the scoped-query gate in Query#aggregate.
|
|
7236
7277
|
use_mongo_direct = @mongo_direct
|
|
7237
|
-
if !use_mongo_direct && query_is_scoped?
|
|
7238
|
-
|
|
7278
|
+
if !use_mongo_direct && query_is_scoped?
|
|
7279
|
+
if parse_mongodb_available?
|
|
7280
|
+
use_mongo_direct = true
|
|
7281
|
+
else
|
|
7282
|
+
@query.send(:raise_scoped_aggregation_requires_mongo_direct!)
|
|
7283
|
+
end
|
|
7239
7284
|
end
|
|
7240
7285
|
|
|
7241
7286
|
if use_mongo_direct
|
|
@@ -7281,11 +7326,21 @@ module Parse
|
|
|
7281
7326
|
puts "[VERBOSE AGGREGATE] Sending to: #{@query.instance_variable_get(:@table)}"
|
|
7282
7327
|
end
|
|
7283
7328
|
|
|
7329
|
+
# Parse Server's REST /aggregate endpoint is master-key-only. An active
|
|
7330
|
+
# Parse.with_session block sets a fiber-local ambient session token that
|
|
7331
|
+
# Parse::Client#request picks up and uses in place of the master key,
|
|
7332
|
+
# causing a 401/403 on this endpoint. Force use_master_key: true so the
|
|
7333
|
+
# ambient session cannot suppress it — unless the caller explicitly set
|
|
7334
|
+
# use_master_key: false (deliberate client-mode / session-token intent).
|
|
7335
|
+
# `.dup` keeps the master-key flip local to this call (see Aggregation#execute!).
|
|
7336
|
+
rest_opts = @query.send(:_opts).dup
|
|
7337
|
+
rest_opts[:use_master_key] = true unless rest_opts[:use_master_key] == false
|
|
7338
|
+
|
|
7284
7339
|
response = @query.client.aggregate_pipeline(
|
|
7285
7340
|
@query.instance_variable_get(:@table),
|
|
7286
7341
|
pipeline,
|
|
7287
7342
|
headers: {},
|
|
7288
|
-
|
|
7343
|
+
**rest_opts,
|
|
7289
7344
|
)
|
|
7290
7345
|
|
|
7291
7346
|
if @query.instance_variable_get(:@verbose_aggregate)
|
|
@@ -7312,6 +7367,18 @@ module Parse
|
|
|
7312
7367
|
end
|
|
7313
7368
|
result_hash
|
|
7314
7369
|
else
|
|
7370
|
+
unless response.success?
|
|
7371
|
+
# Surface the failure (the result would otherwise be a silent `{}`)
|
|
7372
|
+
# through the configured logger rather than unconditional stderr.
|
|
7373
|
+
# Log the error code + message, not a full `inspect`, to avoid
|
|
7374
|
+
# echoing an unbounded server payload into logs.
|
|
7375
|
+
logger = Parse.respond_to?(:logger) ? Parse.logger : nil
|
|
7376
|
+
logger&.warn(
|
|
7377
|
+
"[Parse::GroupByDate] aggregate failed " \
|
|
7378
|
+
"(#{@query.instance_variable_get(:@table)} :#{@date_field} :#{@interval}): " \
|
|
7379
|
+
"code=#{response.code} #{response.error}"
|
|
7380
|
+
)
|
|
7381
|
+
end
|
|
7315
7382
|
{}
|
|
7316
7383
|
end
|
|
7317
7384
|
end
|
|
@@ -7492,15 +7559,23 @@ module Parse
|
|
|
7492
7559
|
{ "$sort" => { field => dir } }
|
|
7493
7560
|
end
|
|
7494
7561
|
|
|
7495
|
-
# Mirror of {GroupBy#query_is_scoped?}. A session_token, acl_user,
|
|
7496
|
-
# acl_role
|
|
7497
|
-
# which only the mongo-direct path
|
|
7498
|
-
# `/aggregate` is master-key-only and
|
|
7562
|
+
# Mirror of {GroupBy#query_is_scoped?}. A session_token, acl_user,
|
|
7563
|
+
# acl_role, or an active Parse.with_session ambient means the caller
|
|
7564
|
+
# expects ACL-filtered results — which only the mongo-direct path
|
|
7565
|
+
# provides. Parse Server REST `/aggregate` is master-key-only and
|
|
7566
|
+
# unscoped.
|
|
7499
7567
|
def query_is_scoped?
|
|
7500
7568
|
st = @query.session_token
|
|
7501
7569
|
return true if st.is_a?(String) && !st.empty?
|
|
7502
7570
|
return true if @query.instance_variable_get(:@acl_user)
|
|
7503
7571
|
return true if @query.instance_variable_get(:@acl_role)
|
|
7572
|
+
# Ambient Parse.with_session counts as scope only when the query did not
|
|
7573
|
+
# explicitly set use_master_key: true (matches Parse::Client#request
|
|
7574
|
+
# precedence — an explicit master-key call skips the ambient session).
|
|
7575
|
+
unless @query.use_master_key == true
|
|
7576
|
+
ambient = @query.send(:ambient_session_token)
|
|
7577
|
+
return true if ambient.is_a?(String) && !ambient.empty?
|
|
7578
|
+
end
|
|
7504
7579
|
false
|
|
7505
7580
|
end
|
|
7506
7581
|
|
data/lib/parse/stack/version.rb
CHANGED