parse-stack-next 5.8.0 → 5.8.1
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 +318 -0
- data/README.md +25 -19
- data/docs/caching.md +50 -0
- data/docs/mcp_guide.md +15 -3
- data/docs/webhooks_guide.md +59 -7
- data/lib/parse/acl_scope.rb +71 -9
- data/lib/parse/agent/describe.rb +8 -2
- data/lib/parse/agent/mcp_dispatcher.rb +8 -1
- data/lib/parse/agent.rb +205 -20
- data/lib/parse/atlas_search/index_manager.rb +5 -1
- data/lib/parse/atlas_search.rb +49 -1
- data/lib/parse/authorization.rb +235 -3
- data/lib/parse/cache/sub_cache.rb +26 -0
- data/lib/parse/client/authentication.rb +19 -1
- data/lib/parse/client/batch.rb +35 -2
- data/lib/parse/client.rb +26 -2
- data/lib/parse/clp_scope.rb +37 -3
- data/lib/parse/live_query/client.rb +72 -2
- data/lib/parse/lock_backend.rb +4 -3
- data/lib/parse/model/associations/collection_proxy.rb +19 -11
- data/lib/parse/model/associations/has_many.rb +11 -0
- data/lib/parse/model/associations/pointer_collection_proxy.rb +36 -0
- data/lib/parse/model/associations/relation_collection_proxy.rb +97 -12
- data/lib/parse/model/classes/role.rb +23 -0
- data/lib/parse/model/classes/session.rb +279 -19
- data/lib/parse/model/classes/user.rb +33 -12
- data/lib/parse/model/core/actions.rb +19 -4
- data/lib/parse/model/core/fetching.rb +29 -2
- data/lib/parse/model/core/field_guards.rb +16 -8
- data/lib/parse/model/object.rb +52 -1
- data/lib/parse/model/push.rb +11 -1
- data/lib/parse/mongodb.rb +19 -1
- data/lib/parse/query/constraint.rb +1 -1
- data/lib/parse/query/constraints.rb +31 -27
- data/lib/parse/query.rb +793 -55
- data/lib/parse/stack/version.rb +1 -1
- data/lib/parse/stack.rb +19 -0
- data/lib/parse/vector_search/hybrid.rb +74 -15
- data/lib/parse/vector_search.rb +214 -7
- data/lib/parse/webhooks/payload.rb +43 -0
- data/lib/parse/webhooks.rb +312 -9
- metadata +1 -1
data/lib/parse/mongodb.rb
CHANGED
|
@@ -1481,6 +1481,15 @@ module Parse
|
|
|
1481
1481
|
"They are mutually exclusive."
|
|
1482
1482
|
end
|
|
1483
1483
|
|
|
1484
|
+
if master == true && Parse::ACLScope.master_key_suppressed?
|
|
1485
|
+
# The role graph has no public mode to fall back to, so a master
|
|
1486
|
+
# call inside `Parse.without_master_key` is refused outright.
|
|
1487
|
+
raise Parse::ACLScope::ACLRequired,
|
|
1488
|
+
"Parse::MongoDB.#{method_name}: master: true is dropped inside " \
|
|
1489
|
+
"Parse.without_master_key. Pass `as: <Parse::User|Parse::Pointer>`, " \
|
|
1490
|
+
"or run the call inside Parse.with_master_key."
|
|
1491
|
+
end
|
|
1492
|
+
|
|
1484
1493
|
if master == true
|
|
1485
1494
|
return Parse::ACLScope::Resolution.new(
|
|
1486
1495
|
mode: :master, permission_strings: nil, user_id: nil, session: nil,
|
|
@@ -1782,6 +1791,10 @@ module Parse
|
|
|
1782
1791
|
# database with this application's permission strings.
|
|
1783
1792
|
verify_client!(resolution.client)
|
|
1784
1793
|
payload[:scope] = __scope_label(resolution)
|
|
1794
|
+
# `master: true` inside Parse.without_master_key, downgraded to the
|
|
1795
|
+
# public scope. Lets a subscriber explain a master-key caller that
|
|
1796
|
+
# sees only public rows inside the block.
|
|
1797
|
+
payload[:master_dropped] = true if resolution.respond_to?(:master_dropped?) && resolution.master_dropped?
|
|
1785
1798
|
|
|
1786
1799
|
# Validate BEFORE rewrite so the security denylist is applied to the
|
|
1787
1800
|
# caller's original pipeline (which an attacker controls), not to
|
|
@@ -2303,7 +2316,12 @@ module Parse
|
|
|
2303
2316
|
"the caller is authorized. Callers without master scope (e.g. agent " \
|
|
2304
2317
|
"tools, request handlers) must not invoke this method."
|
|
2305
2318
|
end
|
|
2306
|
-
|
|
2319
|
+
# Index metadata, not rows: keep master mode inside
|
|
2320
|
+
# `Parse.without_master_key`, which only governs row access. The
|
|
2321
|
+
# sentinel elevates scope resolution only; it leaves the block's
|
|
2322
|
+
# fiber state alone, so notification subscribers still see it.
|
|
2323
|
+
results = aggregate(collection_name, [{ "$indexStats" => {} }],
|
|
2324
|
+
master: Parse::ACLScope::METADATA_MASTER)
|
|
2307
2325
|
results.each_with_object({}) do |row, h|
|
|
2308
2326
|
name = row["name"] || row[:name]
|
|
2309
2327
|
next unless name
|
|
@@ -151,7 +151,7 @@ module Parse
|
|
|
151
151
|
# d = d.pointer if d.is_a?(Parse::Object) #simplified query object
|
|
152
152
|
# d = d.compile
|
|
153
153
|
if d.is_a?(Parse::Query)
|
|
154
|
-
compiled = d.
|
|
154
|
+
compiled = d.compile_subquery
|
|
155
155
|
# compiled["className"] = d.table
|
|
156
156
|
d = compiled
|
|
157
157
|
end
|
|
@@ -94,7 +94,7 @@ module Parse
|
|
|
94
94
|
# # where this Parse object equals the object in the column `field`.
|
|
95
95
|
# q.where :field => Parse::Pointer("Field", "someObjectId")
|
|
96
96
|
# # alias, shorthand when we infer `:field` maps to `Field` parse class.
|
|
97
|
-
# q.where :field.
|
|
97
|
+
# q.where :field.pointer_id => "someObjectId"
|
|
98
98
|
# # "field":{"__type":"Pointer","className":"Field","objectId":"someObjectId"}}
|
|
99
99
|
#
|
|
100
100
|
# class Artist < Parse::Object
|
|
@@ -114,19 +114,21 @@ module Parse
|
|
|
114
114
|
# You can use the objectId in the query as follows:
|
|
115
115
|
#
|
|
116
116
|
# # shorthand if you are using convention. Will infer class `Artist`
|
|
117
|
-
# Song.all :artist.
|
|
117
|
+
# Song.all :artist.pointer_id => artist_id
|
|
118
118
|
#
|
|
119
119
|
# # other approaches, same result
|
|
120
|
-
# Song.all :artist.
|
|
120
|
+
# Song.all :artist.pointer_id => artist # safely supported Parse::Pointer
|
|
121
121
|
# Song.all :artist => Artist.pointer(artist_id)
|
|
122
122
|
# Song.all :artist => Parse::Pointer.new("Artist", artist_id)
|
|
123
123
|
#
|
|
124
124
|
class ObjectIdConstraint < Constraint
|
|
125
|
-
# @!method
|
|
126
|
-
# A registered method on a symbol to create the constraint.
|
|
125
|
+
# @!method pointer_id
|
|
126
|
+
# A registered method on a symbol to create the constraint. The
|
|
127
|
+
# operator is registered as `:id` and installed on `Symbol` as
|
|
128
|
+
# `pointer_id`.
|
|
127
129
|
# @example
|
|
128
|
-
# q.where :field.
|
|
129
|
-
# q.where :field.
|
|
130
|
+
# q.where :field.pointer_id => "someObjectId"
|
|
131
|
+
# q.where :field.pointer_id => pointer # safely supported
|
|
130
132
|
# @return [ObjectIdConstraint]
|
|
131
133
|
register :id
|
|
132
134
|
|
|
@@ -554,18 +556,18 @@ module Parse
|
|
|
554
556
|
# MongoDB aggregation pipeline with $expr and $size to check array length.
|
|
555
557
|
#
|
|
556
558
|
# # Exact size match
|
|
557
|
-
# q.where :field.
|
|
558
|
-
# q.where :tags.
|
|
559
|
+
# q.where :field.array_size => 2
|
|
560
|
+
# q.where :tags.array_size => 5
|
|
559
561
|
#
|
|
560
562
|
# # Comparison operators via hash
|
|
561
|
-
# q.where :tags.
|
|
562
|
-
# q.where :tags.
|
|
563
|
-
# q.where :tags.
|
|
564
|
-
# q.where :tags.
|
|
565
|
-
# q.where :tags.
|
|
563
|
+
# q.where :tags.array_size => { gt: 3 } # size > 3
|
|
564
|
+
# q.where :tags.array_size => { gte: 2 } # size >= 2
|
|
565
|
+
# q.where :tags.array_size => { lt: 5 } # size < 5
|
|
566
|
+
# q.where :tags.array_size => { lte: 4 } # size <= 4
|
|
567
|
+
# q.where :tags.array_size => { ne: 0 } # size != 0
|
|
566
568
|
#
|
|
567
569
|
# # Combine for range
|
|
568
|
-
# q.where :tags.
|
|
570
|
+
# q.where :tags.array_size => { gte: 2, lt: 10 } # 2 <= size < 10
|
|
569
571
|
#
|
|
570
572
|
# @note This constraint uses aggregation pipeline because Parse Server
|
|
571
573
|
# does not support the $size query operator natively.
|
|
@@ -577,11 +579,13 @@ module Parse
|
|
|
577
579
|
# @see ContainsAllConstraint
|
|
578
580
|
# @see ArraySetEqualsConstraint
|
|
579
581
|
class ArraySizeConstraint < Constraint
|
|
580
|
-
# @!method
|
|
581
|
-
# A registered method on a symbol to create the constraint.
|
|
582
|
+
# @!method array_size
|
|
583
|
+
# A registered method on a symbol to create the constraint. The
|
|
584
|
+
# operator is registered as `:size` and installed on `Symbol` as
|
|
585
|
+
# `array_size`.
|
|
582
586
|
# @example
|
|
583
|
-
# q.where :field.
|
|
584
|
-
# q.where :field.
|
|
587
|
+
# q.where :field.array_size => 2
|
|
588
|
+
# q.where :field.array_size => { gt: 3, lte: 10 }
|
|
585
589
|
# @return [ArraySizeConstraint]
|
|
586
590
|
register :size
|
|
587
591
|
|
|
@@ -1584,10 +1588,10 @@ module Parse
|
|
|
1584
1588
|
unless query.is_a?(Parse::Query)
|
|
1585
1589
|
raise ArgumentError, "Invalid Parse::Query object provided in :query field of value: #{@operation.operand}.#{$dontSelect} => #{@value}"
|
|
1586
1590
|
end
|
|
1587
|
-
query = query.
|
|
1591
|
+
query = query.compile_subquery
|
|
1588
1592
|
elsif @value.is_a?(Parse::Query)
|
|
1589
1593
|
# if its a query, then assume dontSelect key is the same name as operand.
|
|
1590
|
-
query = @value.
|
|
1594
|
+
query = @value.compile_subquery
|
|
1591
1595
|
else
|
|
1592
1596
|
raise ArgumentError, "Invalid `:select` query constraint. It should follow the format: :field.select => { key: 'key', query: '<Parse::Query>' }"
|
|
1593
1597
|
end
|
|
@@ -1635,10 +1639,10 @@ module Parse
|
|
|
1635
1639
|
unless query.is_a?(Parse::Query)
|
|
1636
1640
|
raise ArgumentError, "Invalid Parse::Query object provided in :query field of value: #{@operation.operand}.#{$dontSelect} => #{@value}"
|
|
1637
1641
|
end
|
|
1638
|
-
query = query.
|
|
1642
|
+
query = query.compile_subquery
|
|
1639
1643
|
elsif @value.is_a?(Parse::Query)
|
|
1640
1644
|
# if its a query, then assume dontSelect key is the same name as operand.
|
|
1641
|
-
query = @value.
|
|
1645
|
+
query = @value.compile_subquery
|
|
1642
1646
|
else
|
|
1643
1647
|
raise ArgumentError, "Invalid `:reject` query constraint. It should follow the format: :field.reject => { key: 'key', query: '<Parse::Query>' }"
|
|
1644
1648
|
end
|
|
@@ -2285,10 +2289,10 @@ module Parse
|
|
|
2285
2289
|
unless query.is_a?(Parse::Query)
|
|
2286
2290
|
raise ArgumentError, "Invalid Parse::Query object provided in :query field of value: #{@operation.operand}.matches_key_in_query => #{@value}"
|
|
2287
2291
|
end
|
|
2288
|
-
query = query.
|
|
2292
|
+
query = query.compile_subquery
|
|
2289
2293
|
elsif @value.is_a?(Parse::Query)
|
|
2290
2294
|
# if its a query, then assume key is the same name as operand.
|
|
2291
|
-
query = @value.
|
|
2295
|
+
query = @value.compile_subquery
|
|
2292
2296
|
else
|
|
2293
2297
|
raise ArgumentError, "Invalid `:matches_key_in_query` query constraint. It should follow the format: :field.matches_key_in_query => { key: 'key', query: '<Parse::Query>' }"
|
|
2294
2298
|
end
|
|
@@ -2335,10 +2339,10 @@ module Parse
|
|
|
2335
2339
|
unless query.is_a?(Parse::Query)
|
|
2336
2340
|
raise ArgumentError, "Invalid Parse::Query object provided in :query field of value: #{@operation.operand}.does_not_match_key_in_query => #{@value}"
|
|
2337
2341
|
end
|
|
2338
|
-
query = query.
|
|
2342
|
+
query = query.compile_subquery
|
|
2339
2343
|
elsif @value.is_a?(Parse::Query)
|
|
2340
2344
|
# if its a query, then assume key is the same name as operand.
|
|
2341
|
-
query = @value.
|
|
2345
|
+
query = @value.compile_subquery
|
|
2342
2346
|
else
|
|
2343
2347
|
raise ArgumentError, "Invalid `:does_not_match_key_in_query` query constraint. It should follow the format: :field.does_not_match_key_in_query => { key: 'key', query: '<Parse::Query>' }"
|
|
2344
2348
|
end
|