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,153 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Note: Do not require "../object" here - this file is loaded from object.rb
5
+ # and adding that require would create a circular dependency.
6
+
7
+ module Parse
8
+ # This class represents the data and columns contained in the standard Parse
9
+ # `_JobSchedule` collection. Rows here define recurring runs for background
10
+ # jobs registered via +Parse.Cloud.job(...)+. The collection is populated by
11
+ # the Parse Dashboard's "Schedule a Job" UI and consumed by Parse Server's
12
+ # scheduler.
13
+ #
14
+ # The default schema for {JobSchedule} is as follows:
15
+ #
16
+ # class Parse::JobSchedule < Parse::Object
17
+ # # See Parse::Object for inherited properties...
18
+ #
19
+ # property :job_name
20
+ # property :description
21
+ # property :params # JSON-encoded string of params (server stores as String)
22
+ # property :start_after # ISO 8601 timestamp string for first run
23
+ # property :days_of_week, :array
24
+ # property :time_of_day # "HH:MM:SS"
25
+ # property :last_run, :integer # epoch seconds of the previous run
26
+ # property :repeat_minutes, :integer
27
+ # end
28
+ #
29
+ # *Defining and scheduling a job*
30
+ #
31
+ # The job itself is registered in Parse Server's Cloud Code (server-side
32
+ # JavaScript). See {Parse::JobStatus} for the +Parse.Cloud.job(...)+
33
+ # registration example.
34
+ #
35
+ # Schedules are normally created through the Parse Dashboard "Jobs" tab,
36
+ # which writes the `_JobSchedule` row for you. The dashboard exposes:
37
+ #
38
+ # - the registered job name to invoke
39
+ # - the parameters to pass (serialized to {#params} as JSON)
40
+ # - the start time ({#start_after}) and time-of-day ({#time_of_day})
41
+ # - the days of the week ({#days_of_week}) or repeat interval
42
+ # ({#repeat_minutes}) at which the run should fire
43
+ #
44
+ # `_JobSchedule` is a metadata collection: it stores schedule definitions
45
+ # but Parse Server itself does not auto-trigger jobs from these rows. The
46
+ # actual dispatch is performed by external tooling (e.g.
47
+ # +parse-server-scheduler+, dashboard-driven cron wrappers, or a sidecar
48
+ # process) which reads `_JobSchedule` and fires +POST /parse/jobs/<name>+
49
+ # at the appropriate times. Run status rows then appear in
50
+ # {Parse::JobStatus}.
51
+ #
52
+ # *Reading a schedule from Ruby*
53
+ #
54
+ # schedule = Parse::JobSchedule.for_job("nightlyCleanup").first
55
+ # schedule.parsed_params # => { "dryRun" => false } (decoded from `params`)
56
+ # schedule.time_of_day # => "03:00:00"
57
+ # schedule.days_of_week # => ["mon","tue","wed","thu","fri"]
58
+ #
59
+ # @note This collection is consumed by external scheduling tooling, not by
60
+ # Parse Server itself. {#params} is stored as a JSON string (not an
61
+ # Object) per the canonical Parse Server schema; use {#parsed_params} to
62
+ # decode. Master-key access is typically required.
63
+ # @see Parse::JobStatus
64
+ # @see Parse::Object
65
+ class JobSchedule < Parse::Object
66
+ parse_class Parse::Model::CLASS_JOB_SCHEDULE
67
+
68
+ # Note: This class is marked `agent_hidden` after
69
+ # `Parse::Agent::MetadataDSL` is mixed into `Parse::Object` (the mixin
70
+ # happens in `lib/parse/agent.rb`, which is required after this file
71
+ # via `lib/parse/stack.rb`, so calling `agent_hidden` here in the class
72
+ # body would raise NameError). The actual hide is performed by the
73
+ # `Parse::JobSchedule.agent_hidden` call at the bottom of
74
+ # `lib/parse/agent.rb`. `_JobSchedule` rows define recurring runs and
75
+ # can contain scheduler parameters or credentials in {#params}.
76
+
77
+ # @!attribute job_name
78
+ # The registered job name to invoke on each run.
79
+ # @return [String]
80
+ property :job_name
81
+
82
+ # @!attribute description
83
+ # Free-form description of this scheduled job, as entered in the
84
+ # dashboard.
85
+ # @return [String]
86
+ property :description
87
+
88
+ # @!attribute params
89
+ # JSON-encoded string of parameters to pass to the job. Stored as a String
90
+ # in the canonical Parse Server schema to avoid the nested-key character
91
+ # restrictions that apply to Object columns.
92
+ # @return [String]
93
+ property :params
94
+
95
+ # @!attribute start_after
96
+ # ISO 8601 timestamp string indicating the earliest time the first
97
+ # scheduled run may fire.
98
+ # @return [String]
99
+ property :start_after
100
+
101
+ # @!attribute days_of_week
102
+ # Array of day-of-week identifiers indicating which days the job is
103
+ # eligible to run. The exact token set (e.g. +"mon"+/+"tue"+/... vs.
104
+ # +0+..+6+) is determined by the scheduler tooling that writes the row;
105
+ # the Parse Server schema only requires that the column hold an array.
106
+ # @return [Array]
107
+ property :days_of_week, :array
108
+
109
+ # @!attribute time_of_day
110
+ # "HH:MM:SS" string indicating the time of day at which the job should
111
+ # run on each eligible day.
112
+ # @return [String]
113
+ property :time_of_day
114
+
115
+ # @!attribute last_run
116
+ # Raw `Number` timestamp recording the previous run. The unit is
117
+ # scheduler-defined — most external schedulers write +Date.now()+
118
+ # milliseconds, but the canonical Parse Server schema only declares
119
+ # +Number+ and does not pin a unit. Treat values written by one
120
+ # scheduler as opaque to others.
121
+ # @return [Integer]
122
+ property :last_run, :integer
123
+
124
+ # @!attribute repeat_minutes
125
+ # Interval in minutes between runs, when the schedule is interval-based
126
+ # rather than time-of-day-based.
127
+ # @return [Integer]
128
+ property :repeat_minutes, :integer
129
+
130
+ class << self
131
+ # Query scope for schedules belonging to a specific job by name.
132
+ # @param name [String, Symbol]
133
+ # @return [Parse::Query]
134
+ def for_job(name)
135
+ query(job_name: name.to_s)
136
+ end
137
+ end
138
+
139
+ # Decoded form of {#params}, which is stored on the wire as a JSON
140
+ # string per the canonical Parse Server schema. Returns the parsed
141
+ # Hash, or `nil` if `params` is blank, or `nil` if the stored string is
142
+ # not valid JSON (Parse Dashboard occasionally writes a non-JSON
143
+ # description string here for ad-hoc schedules — we swallow the parse
144
+ # error rather than crash the caller).
145
+ # @return [Hash, nil]
146
+ def parsed_params
147
+ return nil if params.blank?
148
+ JSON.parse(params)
149
+ rescue JSON::ParserError
150
+ nil
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,264 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Note: Do not require "../object" here - this file is loaded from object.rb
5
+ # and adding that require would create a circular dependency.
6
+
7
+ module Parse
8
+ # This class represents the data and columns contained in the standard Parse
9
+ # `_JobStatus` collection. Parse Server writes a row here every time a
10
+ # background job (registered via +Parse.Cloud.job(...)+) runs, recording its
11
+ # outcome and any status/message updates emitted via +response.message(...)+.
12
+ #
13
+ # The default schema for {JobStatus} is as follows:
14
+ #
15
+ # class Parse::JobStatus < Parse::Object
16
+ # # See Parse::Object for inherited properties...
17
+ #
18
+ # property :job_name
19
+ # property :source # how the job was invoked
20
+ # property :status # "running", "succeeded", "failed"
21
+ # property :message # latest status message emitted by the job
22
+ # property :params, :object # parameters the job was invoked with
23
+ # property :finished_at, :date # when the job stopped running
24
+ # end
25
+ #
26
+ # *Defining a job*
27
+ #
28
+ # Jobs are registered in your Parse Server's Cloud Code (server-side
29
+ # JavaScript), not in this Ruby SDK. A minimal example:
30
+ #
31
+ # // cloud/main.js
32
+ # Parse.Cloud.job("nightlyCleanup", async (request) => {
33
+ # const { params, headers, log, message } = request;
34
+ # message("Starting cleanup...");
35
+ # const query = new Parse.Query("_Session");
36
+ # query.lessThan("expiresAt", new Date());
37
+ # const sessions = await query.find({ useMasterKey: true });
38
+ # await Parse.Object.destroyAll(sessions, { useMasterKey: true });
39
+ # message(`Deleted ${sessions.length} sessions`);
40
+ # return `ok`;
41
+ # });
42
+ #
43
+ # *Invoking a job*
44
+ #
45
+ # Once registered, a job can be triggered ad-hoc via REST (requires the
46
+ # master key):
47
+ #
48
+ # POST /parse/jobs/nightlyCleanup
49
+ # X-Parse-Application-Id: ...
50
+ # X-Parse-Master-Key: ...
51
+ # Content-Type: application/json
52
+ #
53
+ # { "someParam": "value" }
54
+ #
55
+ # The request returns immediately with a `_JobStatus` `objectId`; the job
56
+ # itself runs asynchronously, and the `_JobStatus` row is updated as it
57
+ # progresses. For *recurring* runs, configure a {Parse::JobSchedule} row
58
+ # via the Parse Dashboard's "Jobs" tab — Parse Server's scheduler will
59
+ # invoke the job at the configured times.
60
+ #
61
+ # *Reading job status from Ruby*
62
+ #
63
+ # # Has the nightly cleanup run today?
64
+ # latest = Parse::JobStatus.latest_for("nightlyCleanup")
65
+ # puts "Last run: #{latest.status} at #{latest.created_at}"
66
+ # puts "Duration: #{latest.duration}s" if latest.finished?
67
+ #
68
+ # # Find failed jobs in the last 24h
69
+ # yesterday = Time.now - 86_400
70
+ # Parse::JobStatus.failed.where(:created_at.gt => yesterday).all
71
+ #
72
+ # @note This collection is written by Parse Server itself and read access
73
+ # typically requires the master key. Parse Server does not garbage-collect
74
+ # `_JobStatus` rows — long-running deployments accumulate history and
75
+ # should implement their own retention policy.
76
+ # @see Parse::JobSchedule for the corresponding scheduled-run configuration.
77
+ # @see Parse::Object
78
+ class JobStatus < Parse::Object
79
+ parse_class Parse::Model::CLASS_JOB_STATUS
80
+
81
+ # Note: This class is marked `agent_hidden` after
82
+ # `Parse::Agent::MetadataDSL` is mixed into `Parse::Object` (the mixin
83
+ # happens in `lib/parse/agent.rb`, which is required after this file
84
+ # via `lib/parse/stack.rb`, so calling `agent_hidden` here in the class
85
+ # body would raise NameError). The actual hide is performed by the
86
+ # `Parse::JobStatus.agent_hidden` call at the bottom of
87
+ # `lib/parse/agent.rb`. `_JobStatus` carries operational signal
88
+ # (registered job names, status messages, error traces in {#message},
89
+ # scheduler parameters) that an agent surface should not enumerate by
90
+ # default.
91
+
92
+ # @!attribute job_name
93
+ # The name the job was registered under (the first argument to
94
+ # +Parse.Cloud.job+).
95
+ # @return [String]
96
+ property :job_name
97
+
98
+ # @!attribute source
99
+ # How the job was invoked. Parse Server itself hard-codes +"api"+ in
100
+ # +StatusHandler.js+ for runs triggered via +POST /parse/jobs/<name>+;
101
+ # external schedulers (parse-server-scheduler, dashboard cron tooling)
102
+ # may inject other values when they create the +_JobStatus+ row.
103
+ # @return [String]
104
+ property :source
105
+
106
+ # @!attribute status
107
+ # Current state of the job run. Common values are +"running"+,
108
+ # +"succeeded"+, and +"failed"+.
109
+ # @return [String]
110
+ property :status
111
+
112
+ # @!attribute message
113
+ # The most recent status message emitted by the job via
114
+ # +response.message(...)+.
115
+ # @return [String]
116
+ property :message
117
+
118
+ # @!attribute params
119
+ # The parameters the job was invoked with.
120
+ # @return [Hash]
121
+ property :params, :object
122
+
123
+ # @!attribute finished_at
124
+ # Timestamp when the job stopped running. Nil while the job is still
125
+ # in-flight.
126
+ # @return [Parse::Date]
127
+ property :finished_at, :date
128
+
129
+ # Parse Server's terminal status values, written by `setFinalStatus` in
130
+ # `StatusHandler.js`. Mirrored here so callers can compare against named
131
+ # constants instead of hard-coding strings.
132
+ STATUS_RUNNING = "running"
133
+ STATUS_SUCCEEDED = "succeeded"
134
+ STATUS_FAILED = "failed"
135
+
136
+ class << self
137
+ # Query for jobs currently in the running state.
138
+ # @return [Parse::Query]
139
+ def running
140
+ query(status: STATUS_RUNNING)
141
+ end
142
+
143
+ # Query for jobs that completed successfully.
144
+ # @return [Parse::Query]
145
+ def succeeded
146
+ query(status: STATUS_SUCCEEDED)
147
+ end
148
+
149
+ # Query for jobs that failed.
150
+ # @return [Parse::Query]
151
+ def failed
152
+ query(status: STATUS_FAILED)
153
+ end
154
+
155
+ # Query for the most recent job status rows, newest first.
156
+ # @param limit [Integer] number of rows to return (default: 100)
157
+ # @return [Parse::Query]
158
+ def recent(limit: 100)
159
+ query.order(:created_at.desc).limit(limit)
160
+ end
161
+
162
+ # Query scope for runs of a specific job by name.
163
+ # @param name [String, Symbol]
164
+ # @return [Parse::Query]
165
+ def for_job(name)
166
+ query(job_name: name.to_s)
167
+ end
168
+
169
+ # The most recently *started* run of the named job (any status),
170
+ # ordered by `created_at`. Useful for "did the nightly cleanup run
171
+ # yet?" introspection. Note that for a still-running job this may
172
+ # return the in-flight row even after subsequent attempts have
173
+ # finished — `created_at` is the start time, not the finish time.
174
+ # @param name [String, Symbol]
175
+ # @return [Parse::JobStatus, nil]
176
+ def latest_for(name)
177
+ for_job(name).order(:created_at.desc).first
178
+ end
179
+
180
+ # Query scope for `_JobStatus` rows older than the given threshold.
181
+ # @param days [Integer] number of days since the row's `created_at`
182
+ # (default: 30)
183
+ # @return [Parse::Query]
184
+ # @example
185
+ # stale = Parse::JobStatus.older_than(days: 90).all
186
+ def older_than(days: 30)
187
+ cutoff = Time.now - (days * 24 * 60 * 60)
188
+ query(:created_at.lt => cutoff)
189
+ end
190
+
191
+ # Count `_JobStatus` rows older than the given threshold.
192
+ # @param days [Integer] number of days since `created_at` (default: 30)
193
+ # @return [Integer]
194
+ def older_than_count(days: 30)
195
+ older_than(days: days).count
196
+ end
197
+
198
+ # Delete `_JobStatus` rows older than the given threshold. Parse Server
199
+ # does not garbage-collect this collection on its own, so long-running
200
+ # deployments accumulate run history indefinitely. Mirrors
201
+ # {Parse::Installation.cleanup_stale_tokens!}.
202
+ #
203
+ # By default (`terminal_only: true`), only rows in a terminal state
204
+ # (`succeeded` or `failed`) are eligible — an orphaned
205
+ # `status == "running"` row from a crashed worker is preserved, as is
206
+ # any row with an external-scheduler-injected status the SDK doesn't
207
+ # recognize. Set `terminal_only: false` to drop the status guard and
208
+ # reap every row older than the cutoff regardless of state (use with
209
+ # care for orphan cleanup).
210
+ #
211
+ # Use with caution — permanently removes job-history records.
212
+ #
213
+ # @param days [Integer] number of days since `created_at` (default: 30).
214
+ # Negative values are accepted and produce a future cutoff (useful in
215
+ # tests for "delete everything older than now-plus-a-minute").
216
+ # @param terminal_only [Boolean] when true (default), restrict the
217
+ # destroy to rows whose status is `succeeded` or `failed`. When
218
+ # false, every row older than the cutoff is eligible.
219
+ # @return [Integer] the number of rows deleted
220
+ # @example
221
+ # deleted = Parse::JobStatus.cleanup_older_than!(days: 90)
222
+ def cleanup_older_than!(days: 30, terminal_only: true)
223
+ scope = older_than(days: days)
224
+ if terminal_only
225
+ scope = scope.where(:status.in => [STATUS_SUCCEEDED, STATUS_FAILED])
226
+ end
227
+ stale = scope.all
228
+ stale.each(&:destroy)
229
+ stale.count
230
+ end
231
+ end
232
+
233
+ # @return [Boolean] true if this row is in the running state.
234
+ def running?
235
+ status == STATUS_RUNNING
236
+ end
237
+
238
+ # @return [Boolean] true if this run completed successfully.
239
+ def succeeded?
240
+ status == STATUS_SUCCEEDED
241
+ end
242
+
243
+ # @return [Boolean] true if this run failed.
244
+ def failed?
245
+ status == STATUS_FAILED
246
+ end
247
+
248
+ # @return [Boolean] true if the run has reached a terminal state
249
+ # (succeeded or failed). Parse Server writes `finished_at` at the same
250
+ # time it transitions out of `running`, so either signal is acceptable;
251
+ # we check `finished_at` first because it's the more authoritative one.
252
+ def finished?
253
+ !finished_at.nil? || succeeded? || failed?
254
+ end
255
+
256
+ # Wall-clock duration of the run as a `Float` number of seconds, or `nil`
257
+ # while the job is still in-flight (or if either timestamp is missing).
258
+ # @return [Float, nil]
259
+ def duration
260
+ return nil if finished_at.nil? || created_at.nil?
261
+ finished_at.to_time - created_at.to_time
262
+ end
263
+ end
264
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+ # Note: Do not require "../object" here - this file is loaded from object.rb
4
+ # and adding that require would create a circular dependency.
5
+ # user.rb is also loaded from object.rb before this file.
6
+
7
+ module Parse
8
+ # This class represents the data and columns contained in the standard Parse `_Product` collection.
9
+ # These records were used when implementing in-app purchases in mobile applications via the
10
+ # original Parse iOS SDK's `PFProduct` downloadable-content flow.
11
+ #
12
+ # @note This Parse feature is effectively deprecated. The `PFProduct` IAP integration was tied
13
+ # to hosted Parse and is not actively used by modern Parse Server deployments. Most apps now
14
+ # verify in-app purchase receipts directly against the Apple App Store or Google Play. The
15
+ # `_Product` collection and this class are retained for backwards compatibility with legacy
16
+ # applications that still read or write product metadata in this table.
17
+ #
18
+ # The default schema for {Product} is as follows:
19
+ #
20
+ # class Parse::Product < Parse::Object
21
+ # # See Parse::Object for inherited properties...
22
+ #
23
+ # property :download, :file
24
+ # property :icon, :file, required: true
25
+ # property :order, :integer, required: true
26
+ # property :subtitle, required: true
27
+ # property :title, required: true
28
+ # property :product_identifier, required: true
29
+ #
30
+ # end
31
+ # @see Parse::Object
32
+ class Product < Parse::Object
33
+ parse_class Parse::Model::CLASS_PRODUCT
34
+
35
+ # Note: This class is marked `agent_hidden` after `Parse::Agent::MetadataDSL`
36
+ # is mixed into `Parse::Object`. The mixin happens in `lib/parse/agent.rb`,
37
+ # which is required after this file via `lib/parse/stack.rb`, so calling
38
+ # `agent_hidden` here in the class body would raise NameError. See the
39
+ # corresponding `Parse::Product.agent_hidden` call at the bottom of
40
+ # `lib/parse/agent.rb`.
41
+
42
+ # @!attribute download
43
+ # @return [String] the file payload for this product download.
44
+ property :download, :file
45
+
46
+ # @!attribute download_name
47
+ # @return [String] the name of this download.
48
+ property :download_name
49
+
50
+ # @!attribute icon
51
+ # An icon file representing this download. This field is required by Parse.
52
+ # @return [String]
53
+ property :icon, :file, required: true
54
+
55
+ # @!attribute order
56
+ # The product order number. This field is required by Parse.
57
+ # @return [String]
58
+ property :order, :integer, required: true
59
+
60
+ # @!attribute product_identifier
61
+ # The product identifier. This field is required by Parse.
62
+ # @return [String]
63
+ property :product_identifier, required: true
64
+
65
+ # @!attribute subtitle
66
+ # The subtitle description for this product. This field is required by Parse.
67
+ # @return [String]
68
+ property :subtitle, required: true
69
+
70
+ # @!attribute title
71
+ # The title for this product. This field is required by Parse.
72
+ # @return [String] the title for this product.
73
+ property :title, required: true
74
+ end
75
+ end