parse-stack-next 4.5.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.
Files changed (178) hide show
  1. checksums.yaml +7 -0
  2. data/.bundle/config +2 -0
  3. data/.env.sample +112 -0
  4. data/.env.test +10 -0
  5. data/.github/workflows/ruby.yml +36 -0
  6. data/.gitignore +49 -0
  7. data/.ruby-version +1 -0
  8. data/.solargraph.yml +22 -0
  9. data/CHANGELOG.md +5816 -0
  10. data/Gemfile +30 -0
  11. data/Gemfile.lock +175 -0
  12. data/LICENSE.txt +23 -0
  13. data/Makefile +63 -0
  14. data/README.md +5655 -0
  15. data/Rakefile +573 -0
  16. data/bin/console +38 -0
  17. data/bin/parse-console +136 -0
  18. data/bin/server +17 -0
  19. data/bin/setup +7 -0
  20. data/config/parse-config.json +12 -0
  21. data/docs/TEST_SERVER.md +271 -0
  22. data/docs/_config.yml +1 -0
  23. data/docs/mcp_guide.md +3484 -0
  24. data/docs/mongodb_direct_guide.md +1348 -0
  25. data/docs/mongodb_index_optimization_guide.md +631 -0
  26. data/examples/transaction_example.rb +219 -0
  27. data/lib/parse/acl_scope.rb +728 -0
  28. data/lib/parse/agent/cancellation_token.rb +80 -0
  29. data/lib/parse/agent/constraint_translator.rb +480 -0
  30. data/lib/parse/agent/describe.rb +420 -0
  31. data/lib/parse/agent/errors.rb +133 -0
  32. data/lib/parse/agent/mcp_client.rb +557 -0
  33. data/lib/parse/agent/mcp_dispatcher.rb +1023 -0
  34. data/lib/parse/agent/mcp_rack_app.rb +1143 -0
  35. data/lib/parse/agent/mcp_server.rb +376 -0
  36. data/lib/parse/agent/metadata_audit.rb +259 -0
  37. data/lib/parse/agent/metadata_dsl.rb +733 -0
  38. data/lib/parse/agent/metadata_registry.rb +794 -0
  39. data/lib/parse/agent/pipeline_validator.rb +82 -0
  40. data/lib/parse/agent/prompts.rb +351 -0
  41. data/lib/parse/agent/rate_limiter.rb +158 -0
  42. data/lib/parse/agent/relation_graph.rb +162 -0
  43. data/lib/parse/agent/result_formatter.rb +453 -0
  44. data/lib/parse/agent/tools.rb +5489 -0
  45. data/lib/parse/agent.rb +3249 -0
  46. data/lib/parse/api/aggregate.rb +79 -0
  47. data/lib/parse/api/all.rb +26 -0
  48. data/lib/parse/api/analytics.rb +18 -0
  49. data/lib/parse/api/batch.rb +33 -0
  50. data/lib/parse/api/cloud_functions.rb +58 -0
  51. data/lib/parse/api/config.rb +125 -0
  52. data/lib/parse/api/files.rb +29 -0
  53. data/lib/parse/api/hooks.rb +117 -0
  54. data/lib/parse/api/objects.rb +146 -0
  55. data/lib/parse/api/path_segment.rb +75 -0
  56. data/lib/parse/api/push.rb +20 -0
  57. data/lib/parse/api/schema.rb +49 -0
  58. data/lib/parse/api/server.rb +50 -0
  59. data/lib/parse/api/sessions.rb +24 -0
  60. data/lib/parse/api/users.rb +250 -0
  61. data/lib/parse/atlas_search/index_manager.rb +353 -0
  62. data/lib/parse/atlas_search/result.rb +204 -0
  63. data/lib/parse/atlas_search/search_builder.rb +604 -0
  64. data/lib/parse/atlas_search/session.rb +253 -0
  65. data/lib/parse/atlas_search.rb +995 -0
  66. data/lib/parse/client/authentication.rb +97 -0
  67. data/lib/parse/client/batch.rb +234 -0
  68. data/lib/parse/client/body_builder.rb +240 -0
  69. data/lib/parse/client/caching.rb +203 -0
  70. data/lib/parse/client/logging.rb +293 -0
  71. data/lib/parse/client/profiling.rb +181 -0
  72. data/lib/parse/client/protocol.rb +91 -0
  73. data/lib/parse/client/request.rb +233 -0
  74. data/lib/parse/client/response.rb +208 -0
  75. data/lib/parse/client.rb +1104 -0
  76. data/lib/parse/clp_scope.rb +361 -0
  77. data/lib/parse/live_query/circuit_breaker.rb +256 -0
  78. data/lib/parse/live_query/client.rb +1001 -0
  79. data/lib/parse/live_query/configuration.rb +224 -0
  80. data/lib/parse/live_query/event.rb +115 -0
  81. data/lib/parse/live_query/event_queue.rb +272 -0
  82. data/lib/parse/live_query/health_monitor.rb +214 -0
  83. data/lib/parse/live_query/logging.rb +149 -0
  84. data/lib/parse/live_query/subscription.rb +294 -0
  85. data/lib/parse/live_query.rb +163 -0
  86. data/lib/parse/lookup_rewriter.rb +445 -0
  87. data/lib/parse/model/acl.rb +968 -0
  88. data/lib/parse/model/associations/belongs_to.rb +275 -0
  89. data/lib/parse/model/associations/collection_proxy.rb +435 -0
  90. data/lib/parse/model/associations/has_many.rb +597 -0
  91. data/lib/parse/model/associations/has_one.rb +158 -0
  92. data/lib/parse/model/associations/pointer_collection_proxy.rb +134 -0
  93. data/lib/parse/model/associations/relation_collection_proxy.rb +177 -0
  94. data/lib/parse/model/bytes.rb +62 -0
  95. data/lib/parse/model/classes/audience.rb +262 -0
  96. data/lib/parse/model/classes/installation.rb +363 -0
  97. data/lib/parse/model/classes/job_schedule.rb +153 -0
  98. data/lib/parse/model/classes/job_status.rb +264 -0
  99. data/lib/parse/model/classes/product.rb +75 -0
  100. data/lib/parse/model/classes/push_status.rb +263 -0
  101. data/lib/parse/model/classes/role.rb +751 -0
  102. data/lib/parse/model/classes/session.rb +201 -0
  103. data/lib/parse/model/classes/user.rb +943 -0
  104. data/lib/parse/model/clp.rb +544 -0
  105. data/lib/parse/model/core/actions.rb +1268 -0
  106. data/lib/parse/model/core/builder.rb +139 -0
  107. data/lib/parse/model/core/create_lock.rb +386 -0
  108. data/lib/parse/model/core/describe.rb +382 -0
  109. data/lib/parse/model/core/enhanced_change_tracking.rb +159 -0
  110. data/lib/parse/model/core/errors.rb +38 -0
  111. data/lib/parse/model/core/fetching.rb +566 -0
  112. data/lib/parse/model/core/field_guards.rb +220 -0
  113. data/lib/parse/model/core/indexing.rb +382 -0
  114. data/lib/parse/model/core/parse_reference.rb +407 -0
  115. data/lib/parse/model/core/properties.rb +809 -0
  116. data/lib/parse/model/core/querying.rb +491 -0
  117. data/lib/parse/model/core/schema.rb +202 -0
  118. data/lib/parse/model/core/search_indexing.rb +174 -0
  119. data/lib/parse/model/date.rb +88 -0
  120. data/lib/parse/model/email.rb +213 -0
  121. data/lib/parse/model/file.rb +527 -0
  122. data/lib/parse/model/geojson.rb +271 -0
  123. data/lib/parse/model/geopoint.rb +261 -0
  124. data/lib/parse/model/model.rb +260 -0
  125. data/lib/parse/model/object.rb +2068 -0
  126. data/lib/parse/model/phone.rb +520 -0
  127. data/lib/parse/model/pointer.rb +443 -0
  128. data/lib/parse/model/polygon.rb +406 -0
  129. data/lib/parse/model/push.rb +975 -0
  130. data/lib/parse/model/shortnames.rb +8 -0
  131. data/lib/parse/model/time_zone.rb +141 -0
  132. data/lib/parse/model/validations/uniqueness_validator.rb +97 -0
  133. data/lib/parse/model/validations.rb +96 -0
  134. data/lib/parse/mongodb.rb +2300 -0
  135. data/lib/parse/pipeline_security.rb +554 -0
  136. data/lib/parse/query/constraint.rb +198 -0
  137. data/lib/parse/query/constraints.rb +3279 -0
  138. data/lib/parse/query/cursor.rb +434 -0
  139. data/lib/parse/query/n_plus_one_detector.rb +445 -0
  140. data/lib/parse/query/operation.rb +104 -0
  141. data/lib/parse/query/ordering.rb +66 -0
  142. data/lib/parse/query.rb +7028 -0
  143. data/lib/parse/schema/index_migrator.rb +291 -0
  144. data/lib/parse/schema/search_index_migrator.rb +289 -0
  145. data/lib/parse/schema.rb +494 -0
  146. data/lib/parse/stack/generators/rails.rb +40 -0
  147. data/lib/parse/stack/generators/templates/model.erb +51 -0
  148. data/lib/parse/stack/generators/templates/model_installation.rb +4 -0
  149. data/lib/parse/stack/generators/templates/model_role.rb +4 -0
  150. data/lib/parse/stack/generators/templates/model_session.rb +4 -0
  151. data/lib/parse/stack/generators/templates/model_user.rb +11 -0
  152. data/lib/parse/stack/generators/templates/parse.rb +12 -0
  153. data/lib/parse/stack/generators/templates/webhooks.rb +10 -0
  154. data/lib/parse/stack/railtie.rb +18 -0
  155. data/lib/parse/stack/tasks.rb +563 -0
  156. data/lib/parse/stack/version.rb +11 -0
  157. data/lib/parse/stack.rb +455 -0
  158. data/lib/parse/two_factor_auth/user_extension.rb +449 -0
  159. data/lib/parse/two_factor_auth.rb +310 -0
  160. data/lib/parse/webhooks/payload.rb +360 -0
  161. data/lib/parse/webhooks/registration.rb +199 -0
  162. data/lib/parse/webhooks/replay_protection.rb +189 -0
  163. data/lib/parse/webhooks.rb +510 -0
  164. data/lib/parse-stack-next.rb +5 -0
  165. data/lib/parse-stack.rb +5 -0
  166. data/parse-stack-next.gemspec +82 -0
  167. data/parse-stack.png +0 -0
  168. data/scripts/debug-ips.js +35 -0
  169. data/scripts/docker/Dockerfile.parse +13 -0
  170. data/scripts/docker/atlas-init.js +284 -0
  171. data/scripts/docker/docker-compose.atlas.yml +76 -0
  172. data/scripts/docker/docker-compose.test.yml +106 -0
  173. data/scripts/docker/mongo-init.js +21 -0
  174. data/scripts/eval_mcp_with_lm_studio.rb +274 -0
  175. data/scripts/start-parse.sh +90 -0
  176. data/scripts/start_mcp_server.rb +78 -0
  177. data/scripts/test_server_connection.rb +82 -0
  178. metadata +377 -0
@@ -0,0 +1,275 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../pointer"
5
+ require_relative "collection_proxy"
6
+ require_relative "pointer_collection_proxy"
7
+ require_relative "relation_collection_proxy"
8
+
9
+ module Parse
10
+ # Defines all the types of Parse object associations.
11
+ module Associations
12
+ # This association creates a one-to-one association with another Parse model.
13
+ # BelongsTo relation is the simplies association in which the local
14
+ # Parse table constrains a column that has a Parse::Pointer to a foreign table record.
15
+ #
16
+ # This association says that this class contains a foreign pointer column
17
+ # which references a different class. Utilizing the `belongs_to` method in
18
+ # defining a property in a Parse::Object subclass sets up an association
19
+ # between the local table and a foreign table. Specifying the `belongs_to`
20
+ # in the class, tells the framework that the Parse table contains a local
21
+ # column in its schema that has a reference to a record in a foreign table.
22
+ # The argument to `belongs_to` should be the singularized version of the
23
+ # foreign Parse::Object class. you should specify the foreign table as the
24
+ # snake_case singularized version of the foreign table class.
25
+ #
26
+ # Note that the reverse relationship on the foreign class is not generated automatically.
27
+ # You can use a `has_one` on the foreign model to create it.
28
+ # @example
29
+ # class Author < Parse::Object
30
+ # property :name
31
+ # end
32
+ #
33
+ #
34
+ # class Post < Parse::Object
35
+ # belongs_to :author
36
+ # end
37
+ #
38
+ # Post.references # => {:author=>"Author"}
39
+ #
40
+ # post = Post.first
41
+ # post.author? # => true if has a pointer
42
+ #
43
+ # # Follow the author pointer and get name
44
+ # post.author.name
45
+ #
46
+ # other_author = Author.first
47
+ # # change author by setting new pointer
48
+ # post.author = other_author
49
+ # post.save
50
+ #
51
+ # @see Parse::Associations::HasOne
52
+ # @see Parse::Associations::HasMany
53
+ module BelongsTo
54
+
55
+ # @!attribute [rw] self.references
56
+ # A hash mapping of all belongs_to associations for this model.
57
+ # @return [Hash]
58
+
59
+ # @!method self.belongs_to(key, opts = {})
60
+ # Creates a one-to-one association with another Parse model.
61
+ # @param [Symbol] key The singularized version of the foreign class and the name of the
62
+ # local column in the remote Parse table where the pointer is stored.
63
+ # @option opts [Symbol] :field override the name of the remote column
64
+ # where the pointer is stored. By default this is inferred as
65
+ # the columnized of the key parameter.
66
+ # @option opts [Symbol] :as override the inferred Parse::Object subclass.
67
+ # By default this is inferred as the singularized camel case version of
68
+ # the key parameter. This option allows you to override the typecast of
69
+ # foreign Parse model of the association, while allowing you to have a
70
+ # different accessor name.
71
+ # @option opts [Boolean] :required Setting to `true`, automatically creates
72
+ # an ActiveModel validation of `validates_presence_of` for the
73
+ # association. This will not prevent the save, but affects the validation
74
+ # check when `valid?` is called on an instance. Default is false.
75
+ # @example
76
+ # # Assumes 'Artist' is foreign class.
77
+ # belongs_to :artist
78
+ #
79
+ # # uses Parse::User as foreign class
80
+ # belongs_to :manager, as: :user
81
+ #
82
+ # # sets attribute name to `featured_song` for foreign class Song with the remote
83
+ # # column name in Parse as 'theFeaturedSong'.
84
+ # belongs_to :featured_song, as: :song, field: :theFeaturedSong
85
+ #
86
+ # @see String#columnize
87
+ # @see #key?
88
+ # @return [Parse::Object] a Parse::Object subclass when using the accessor
89
+ # when fetching the association.
90
+
91
+ # @!method key?
92
+ # A dynamically generated method based on the value of `key` passed to the
93
+ # belongs_to method, which returns true if this instance has a pointer for
94
+ # this field.
95
+ # @example
96
+ #
97
+ # class Post < Parse::Object
98
+ # belongs_to :author # generates 'author?'
99
+ # end
100
+ #
101
+ # post = Post.new
102
+ # post.author? # => false
103
+ # post.author = Author.new
104
+ # post.author? # => true
105
+ # @return [Boolean] true if field contains a Parse::Pointer or subclass.
106
+
107
+ # @!visibility private
108
+ def self.included(base)
109
+ base.extend(ClassMethods)
110
+ end
111
+
112
+ # @!visibility private
113
+ module ClassMethods
114
+ attr_writer :references
115
+ # We can keep references to all "belong_to" properties
116
+ def references
117
+ @references ||= {}
118
+ end
119
+
120
+ # These items are added as attributes with the special data type of :pointer
121
+ def belongs_to(key, opts = {})
122
+ opts = { as: key, field: key.to_s.camelize(:lower), required: false }.merge(opts)
123
+ # `opts[:class_name]` is the explicit target Parse class name; it takes
124
+ # precedence over the legacy `as: :symbol` shorthand (where the
125
+ # symbol is converted to a class name via `.to_parse_class`). The
126
+ # references map needs the real target class so `RelationGraph` and
127
+ # `Parse::Agent::Tools.assert_include_paths_accessible!` can resolve
128
+ # pointer paths to their model classes.
129
+ klassName = opts[:class_name] || opts[:as].to_parse_class
130
+ parse_field = opts[:field].to_sym
131
+
132
+ ivar = :"@#{key}"
133
+ will_change_method = :"#{key}_will_change!"
134
+ set_attribute_method = :"#{key}_set_attribute!"
135
+
136
+ if self.fields[key].present? && Parse::Properties::BASE_FIELD_MAP[key].nil?
137
+ warn "Belongs relation #{self}##{key} already defined with type #{klassName}"
138
+ return false
139
+ end
140
+ if self.fields[parse_field].present?
141
+ warn "Alias belongs_to #{self}##{parse_field} conflicts with previously defined property."
142
+ return false
143
+ end
144
+ # store this attribute in the attributes hash with the proper remote column name.
145
+ # we know the type is pointer.
146
+ self.attributes.merge!(parse_field => :pointer)
147
+ # Add them to our list of pointer references
148
+ self.references.merge!(parse_field => klassName)
149
+ # Add them to the list of fields in our class model
150
+ self.fields.merge!(key => :pointer, parse_field => :pointer)
151
+ # Mapping between local attribute name and the remote column name
152
+ self.field_map.merge!(key => parse_field)
153
+
154
+ # used for dirty tracking
155
+ define_attribute_methods key
156
+
157
+ # validations
158
+ validates_presence_of(key) if opts[:required]
159
+
160
+ # We generate the getter method
161
+ # Store the key and class name for N+1 detection
162
+ association_key = key
163
+ owner_class_name = self.name
164
+
165
+ define_method(key) do
166
+ val = instance_variable_get ivar
167
+ # We provide autofetch functionality. If the value is nil and the
168
+ # current Parse::Object is a pointer, or if this is a selectively fetched
169
+ # object and this field wasn't included in the fetch, then auto fetch it.
170
+ should_autofetch = val.nil? && (pointer? || (has_selective_keys? && !field_was_fetched?(association_key)))
171
+ if should_autofetch
172
+ # If autofetch is disabled and we're accessing an unfetched field on a
173
+ # selectively fetched object, raise an error to make the issue explicit
174
+ if autofetch_disabled? && has_selective_keys? && !field_was_fetched?(association_key)
175
+ raise Parse::UnfetchedFieldAccessError.new(association_key, self.class.name)
176
+ end
177
+ autofetch!(association_key)
178
+ val = instance_variable_get ivar
179
+ end
180
+
181
+ # if for some reason we retrieved either from store or fetching a
182
+ # hash, lets try to build a Pointer of that type.
183
+
184
+ if val.is_a?(Hash) && (val["__type"] == "Pointer" || val["__type"] == "Object")
185
+ # Get nested fetched keys for this field if available
186
+ nested_keys = nested_keys_for(association_key)
187
+ # Always trust the declared klassName — never the className the
188
+ # server (or attacker-controlled mass assignment) supplied. This
189
+ # prevents type confusion where a pointer to a different class
190
+ # (e.g. _Session or _Role) gets shoved into a typed slot.
191
+ incoming_class = val[Parse::Model::KEY_CLASS_NAME]
192
+ if incoming_class && incoming_class != klassName
193
+ warn "[#{self.class}] belongs_to :#{association_key} expected className=#{klassName.inspect}, ignoring incoming className=#{incoming_class.inspect}"
194
+ end
195
+ val = Parse::Object.build val, klassName, fetched_keys: nested_keys
196
+ instance_variable_set ivar, val
197
+ end
198
+
199
+ # Track association source for N+1 detection when returning an unfetched pointer
200
+ # Uses a registry instead of setting instance variables on the pointer object
201
+ if val.is_a?(Parse::Pointer) && val.pointer? && Parse.warn_on_n_plus_one
202
+ Parse::NPlusOneDetector.register_source(val,
203
+ source_class: owner_class_name,
204
+ association: association_key)
205
+ end
206
+
207
+ val
208
+ end
209
+
210
+ if self.method_defined?("#{key}?")
211
+ warn "Creating belongs_to helper :#{key}?. Will overwrite existing method #{self}##{key}?."
212
+ end
213
+
214
+ define_method("#{key}?") do
215
+ self.send(key).is_a?(Parse::Pointer)
216
+ end
217
+
218
+ # A proxy setter method that has dirty tracking enabled.
219
+ define_method("#{key}=") do |val|
220
+ send set_attribute_method, val, true
221
+ end
222
+
223
+ # We only support pointers, either by object or by transforming a hash.
224
+ define_method(set_attribute_method) do |val, track = true|
225
+ if val == Parse::Properties::DELETE_OP
226
+ val = nil
227
+ elsif val.is_a?(Hash) && (val["__type"] == "Pointer" || val["__type"] == "Object")
228
+ # Get nested fetched keys for this field if available
229
+ nested_keys = nested_keys_for(key)
230
+ # Always trust declared klassName over incoming hash className.
231
+ incoming_class = val[Parse::Model::KEY_CLASS_NAME]
232
+ if incoming_class && incoming_class != klassName
233
+ warn "[#{self.class}] belongs_to :#{key} expected className=#{klassName.inspect}, ignoring incoming className=#{incoming_class.inspect}"
234
+ end
235
+ val = Parse::Object.build val, klassName, fetched_keys: nested_keys
236
+ end
237
+
238
+ if track == true
239
+ prepare_for_dirty_tracking!(key)
240
+ send will_change_method unless val == instance_variable_get(ivar)
241
+ else
242
+ # During fetch (track=false), preserve existing embedded objects if the server
243
+ # only returned a pointer. This prevents autofetch from wiping out nested
244
+ # fetched data (e.g., user.first_name) when fetching unfetched fields.
245
+ existing = instance_variable_get(ivar)
246
+ if existing.is_a?(Parse::Pointer) && val.is_a?(Parse::Pointer) &&
247
+ existing.id == val.id && !existing.pointer? && val.pointer?
248
+ # Existing object has embedded data, new value is just a pointer with same ID
249
+ # Preserve the existing richer object
250
+ val = existing
251
+ end
252
+ end
253
+
254
+ # Never set an object that is not a Parse::Pointer
255
+ if val.nil? || val.is_a?(Parse::Pointer)
256
+ instance_variable_set(ivar, val)
257
+ else
258
+ warn "[#{self.class}] Invalid value #{val} set for belongs_to field #{key}"
259
+ end
260
+ end
261
+ # don't create method aliases if the fields are the same
262
+ return if parse_field.to_sym == key.to_sym
263
+
264
+ if self.method_defined?(parse_field) == false
265
+ alias_method parse_field, key
266
+ alias_method "#{parse_field}=", "#{key}="
267
+ alias_method "#{parse_field}_set_attribute!", set_attribute_method
268
+ elsif parse_field.to_sym != :objectId
269
+ warn "Alias belongs_to method #{self}##{parse_field} already defined."
270
+ end
271
+ end
272
+ end # ClassMethod
273
+ end #BelongsTo
274
+ end #Associations
275
+ end