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,8 @@
1
+ require_relative "object"
2
+
3
+ # Simple include to use short verion of core class names
4
+ ::Installation = Parse::Installation unless defined?(::Installation)
5
+ ::Role = Parse::Role unless defined?(::Role)
6
+ ::Product = Parse::Product unless defined?(::Product)
7
+ ::Session = Parse::Session unless defined?(::Session)
8
+ ::User = Parse::User unless defined?(::User)
@@ -0,0 +1,141 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ require "active_support"
5
+ require "active_support/values/time_zone"
6
+ require_relative "model"
7
+
8
+ module Parse
9
+ # This class a wrapper around ActiveSupport::TimeZone when using Parse columns that
10
+ # store IANA time zone identifiers (ex. Installation collection). Parse does not have a
11
+ # native time zone data type, but this class is provided to manage and perform timezone-like
12
+ # operation on those properties which you have marked as type _:timezone_.
13
+ #
14
+ # When declaring a property of type :timezone, you may also define a default just like
15
+ # any other property. In addition, the framework will automatically add a validation
16
+ # to make sure that your property is either nil or one of the valid IANA time zone identifiers.
17
+ #
18
+ # Each instance of {Parse::TimeZone} has a {Parse::TimeZone#zone} attribute that provides access to
19
+ # the underlying {http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html ActiveSupport::TimeZone}
20
+ # instance, which you can use to perform time zone operations.
21
+ # @example
22
+ # class Event < Parse::Object
23
+ # # an event occurs in a time zone.
24
+ # property :time_zone, :timezone, default: 'America/Los_Angeles'
25
+ # end
26
+ #
27
+ # event = Event.new
28
+ # event.time_zone.name # => 'America/Los_Angeles'
29
+ # event.time_zone.valid? # => true
30
+ #
31
+ # event.time_zone.zone # => ActiveSupport::TimeZone
32
+ # event.time_zone.formatted_offset # => "-08:00"
33
+ #
34
+ # event.time_zone = 'Europe/Paris'
35
+ # event.time_zone.formatted_offset # => +01:00"
36
+ #
37
+ # event.time_zone = 'Galaxy/Andromeda'
38
+ # event.time_zone.valid? # => false
39
+ # @version 1.7.1
40
+ class TimeZone
41
+ # The mapping of TimeZones
42
+ MAPPING = ActiveSupport::TimeZone::MAPPING
43
+
44
+ # Create methods based on the allowable public methods on ActiveSupport::TimeZone.
45
+ # Basically sets up sending forwarding calls to the `zone` object for a Parse::TimeZone object.
46
+ (ActiveSupport::TimeZone.public_instance_methods(false) - [:to_s, :name, :as_json]).each do |meth|
47
+ Parse::TimeZone.class_eval do
48
+ define_method meth do |*args|
49
+ zone.send meth, *args
50
+ end
51
+ end
52
+ end
53
+
54
+ # Creates a new instance given the IANA identifier (ex. America/Los_Angeles)
55
+ # @overload new(iana)
56
+ # @param iana [String] the IANA identifier (ex. America/Los_Angeles)
57
+ # @return [Parse::TimeZone]
58
+ # @overload new(timezone)
59
+ # You can instantiate a new instance with either a {Parse::TimeZone} or an {http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html ActiveSupport::TimeZone}
60
+ # object.
61
+ # @param timezone [Parse::TimeZone|ActiveSupport::TimeZone] an instance of either timezone class.
62
+ # @return [Parse::TimeZone]
63
+ def initialize(iana)
64
+ if iana.is_a?(String)
65
+ @name = iana
66
+ @zone = nil
67
+ elsif iana.is_a?(::Parse::TimeZone)
68
+ @zone = iana.zone
69
+ @name = nil
70
+ elsif iana.is_a?(::ActiveSupport::TimeZone)
71
+ @zone = iana
72
+ @name = nil
73
+ end
74
+ end
75
+
76
+ # @!attribute [rw] name
77
+ # @raise ArgumentError if value is not a string type.
78
+ # @return [String] the IANA identifier for this time zone.
79
+ def name
80
+ @zone.present? ? zone.name : @name
81
+ end
82
+
83
+ def name=(iana)
84
+ unless iana.nil? || iana.is_a?(String)
85
+ raise ArgumentError, "Parse::TimeZone#name should be an IANA time zone identifier."
86
+ end
87
+ @name = iana
88
+ @zone = nil
89
+ end
90
+
91
+ # @!attribute [rw] zone
92
+ # Returns an instance of {http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html ActiveSupport::TimeZone}
93
+ # based on the IANA identifier. The setter may allow usign an IANA string identifier,
94
+ # a {Parse::TimeZone} or an
95
+ # {http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html ActiveSupport::TimeZone}
96
+ # object.
97
+ # @see #name
98
+ # @raise ArgumentError
99
+ # @return [ActiveSupport::TimeZone]
100
+ def zone
101
+ # lazy load the TimeZone object only when the user requests it, otherwise
102
+ # just keep the name of the string around. Makes encoding/decoding faster.
103
+ if @zone.nil? && @name.present?
104
+ @zone = ::ActiveSupport::TimeZone.new(@name)
105
+ @name = nil # clear out the cache
106
+ end
107
+ @zone
108
+ end
109
+
110
+ def zone=(timezone)
111
+ if timezone.is_a?(::ActiveSupport::TimeZone)
112
+ @zone = timezone
113
+ @name = nil
114
+ elsif timezone.is_a?(Parse::TimeZone)
115
+ @name = timezone.name
116
+ @zone = nil
117
+ elsif timezone.nil? || timezone.is_a?(String)
118
+ @name = timezone
119
+ @zone = nil
120
+ else
121
+ raise ArgumentError, "Invalid value passed to Parse::TimeZone#zone."
122
+ end
123
+ end
124
+
125
+ # (see #to_s)
126
+ def as_json(*args)
127
+ name
128
+ end
129
+
130
+ # @return [String] the IANA identifier for this timezone or nil.
131
+ def to_s
132
+ name
133
+ end
134
+
135
+ # Returns true or false whether the time zone exists in the {http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html ActiveSupport::TimeZone} mapping.
136
+ # @return [Bool] true if it contains a valid time zone
137
+ def valid?
138
+ ActiveSupport::TimeZone[to_s].present?
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,97 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ require "active_model"
5
+
6
+ module Parse
7
+ module Validations
8
+ # A custom validator that checks if a field value is unique in the Parse collection.
9
+ #
10
+ # This validator queries Parse Server to check if another record exists with the
11
+ # same value for the specified field. It properly handles:
12
+ # - New records (no id yet)
13
+ # - Existing records (excludes self from the check)
14
+ # - Case-insensitive matching (optional)
15
+ # - Scoped uniqueness (unique within a subset of records)
16
+ #
17
+ # @example Basic uniqueness
18
+ # class User < Parse::Object
19
+ # property :email, :string
20
+ # validates :email, uniqueness: true
21
+ # end
22
+ #
23
+ # @example Case-insensitive uniqueness
24
+ # class User < Parse::Object
25
+ # property :username, :string
26
+ # validates :username, uniqueness: { case_sensitive: false }
27
+ # end
28
+ #
29
+ # @example Scoped uniqueness (unique within an organization)
30
+ # class Employee < Parse::Object
31
+ # property :employee_id, :string
32
+ # belongs_to :organization
33
+ # validates :employee_id, uniqueness: { scope: :organization }
34
+ # end
35
+ #
36
+ # @example With custom message
37
+ # class User < Parse::Object
38
+ # property :email, :string
39
+ # validates :email, uniqueness: { message: "is already registered" }
40
+ # end
41
+ #
42
+ class UniquenessValidator < ActiveModel::EachValidator
43
+ # @param record [Parse::Object] the object being validated
44
+ # @param attribute [Symbol] the attribute name being validated
45
+ # @param value [Object] the current value of the attribute
46
+ def validate_each(record, attribute, value)
47
+ return if value.blank? && options[:allow_blank]
48
+ return if value.nil? && options[:allow_nil]
49
+
50
+ # Build the query to check for existing records
51
+ klass = record.class
52
+
53
+ # Get the Parse field name for this attribute (available for debugging)
54
+ _parse_field = klass.field_map[attribute] || attribute.to_s.columnize
55
+
56
+ # Build query conditions
57
+ conditions = {}
58
+
59
+ if options[:case_sensitive] == false && value.is_a?(String)
60
+ # Case-insensitive search using regex
61
+ conditions[attribute.to_sym] = /\A#{Regexp.escape(value)}\z/i
62
+ else
63
+ conditions[attribute.to_sym] = value
64
+ end
65
+
66
+ # Add scope conditions if specified
67
+ if options[:scope]
68
+ scope_fields = Array(options[:scope])
69
+ scope_fields.each do |scope_field|
70
+ scope_value = record.send(scope_field)
71
+ conditions[scope_field.to_sym] = scope_value
72
+ end
73
+ end
74
+
75
+ # Build and execute the query
76
+ query = klass.query(conditions)
77
+
78
+ # Exclude the current record if it's not new
79
+ unless record.new?
80
+ query.where(:id.not => record.id)
81
+ end
82
+
83
+ # Check if any matching records exist
84
+ query.limit(1)
85
+ existing = query.first
86
+
87
+ if existing.present?
88
+ error_message = options[:message] || "has already been taken"
89
+ record.errors.add(attribute, error_message)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ # Register the validator with ActiveModel so it can be used with validates helper
97
+ ActiveModel::Validations::UniquenessValidator = Parse::Validations::UniquenessValidator
@@ -0,0 +1,96 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Load all custom validators for Parse Stack
5
+ require_relative "validations/uniqueness_validator"
6
+
7
+ module Parse
8
+ # The Validations module provides custom validators for Parse::Object subclasses.
9
+ #
10
+ # Parse Stack builds on ActiveModel::Validations, which means all standard Rails
11
+ # validations are available:
12
+ #
13
+ # - `validates :field, presence: true`
14
+ # - `validates :field, length: { minimum: 1, maximum: 200 }`
15
+ # - `validates :field, numericality: { greater_than: 0 }`
16
+ # - `validates :field, format: { with: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i }`
17
+ # - `validates :field, inclusion: { in: %w[small medium large] }`
18
+ # - `validates :field, exclusion: { in: %w[admin root] }`
19
+ #
20
+ # In addition, Parse Stack provides:
21
+ #
22
+ # - `validates :field, uniqueness: true` - Queries Parse to ensure uniqueness
23
+ #
24
+ # @example Full validation example
25
+ # class User < Parse::Object
26
+ # property :email, :string
27
+ # property :username, :string
28
+ # property :age, :integer
29
+ # property :status, :string
30
+ #
31
+ # # Standard ActiveModel validations
32
+ # validates :email, presence: true,
33
+ # format: { with: URI::MailTo::EMAIL_REGEXP }
34
+ # validates :username, presence: true,
35
+ # length: { minimum: 3, maximum: 30 }
36
+ # validates :age, numericality: { greater_than_or_equal_to: 0 },
37
+ # allow_nil: true
38
+ # validates :status, inclusion: { in: %w[active inactive pending] }
39
+ #
40
+ # # Parse-specific uniqueness validation
41
+ # validates :email, uniqueness: true
42
+ # validates :username, uniqueness: { case_sensitive: false }
43
+ #
44
+ # # Custom validation method
45
+ # validate :email_domain_allowed
46
+ #
47
+ # private
48
+ #
49
+ # def email_domain_allowed
50
+ # return if email.blank?
51
+ # domain = email.split('@').last
52
+ # unless %w[company.com partner.org].include?(domain)
53
+ # errors.add(:email, "must be from an allowed domain")
54
+ # end
55
+ # end
56
+ # end
57
+ #
58
+ # @example Validation callbacks
59
+ # class Song < Parse::Object
60
+ # property :title, :string
61
+ #
62
+ # validates :title, presence: true
63
+ #
64
+ # before_validation :normalize_title
65
+ # after_validation :log_validation_result
66
+ #
67
+ # private
68
+ #
69
+ # def normalize_title
70
+ # self.title = title.strip.titleize if title.present?
71
+ # end
72
+ #
73
+ # def log_validation_result
74
+ # if errors.any?
75
+ # puts "Validation failed: #{errors.full_messages.join(', ')}"
76
+ # end
77
+ # end
78
+ # end
79
+ #
80
+ # @example Conditional validations
81
+ # class Order < Parse::Object
82
+ # property :status, :string
83
+ # property :shipping_address, :string
84
+ # property :tracking_number, :string
85
+ #
86
+ # validates :shipping_address, presence: true, if: :requires_shipping?
87
+ # validates :tracking_number, presence: true, if: -> { status == "shipped" }
88
+ #
89
+ # def requires_shipping?
90
+ # status.in?(%w[processing shipped delivered])
91
+ # end
92
+ # end
93
+ #
94
+ module Validations
95
+ end
96
+ end