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,284 @@
1
+ // Atlas Local initialization script for Atlas Search integration tests
2
+ // This script runs after the Atlas Local container is ready
3
+ // It seeds test data and creates the Atlas Search index
4
+ //
5
+ // TEST-FIXTURE BANNER (ATLAS-9):
6
+ // The seed data below uses `_rperm: ["U1"]` to exercise the SDK's
7
+ // ACL injection layer (see `Parse::ACLScope` and the ACL `$match`
8
+ // stage Parse::AtlasSearch.search builds). "U1" is a literal Parse
9
+ // objectId string chosen for test convenience — it is NOT a
10
+ // pattern any production deployment should use.
11
+ //
12
+ // In Parse Server, `_rperm` entries are either the special public
13
+ // token "*", a role token like "role:Admin", or a real Parse User
14
+ // objectId (24-char Mongo-style id). The seed rows here ship with
15
+ // "U1" because the SDK unit/integration tests stub a Session that
16
+ // reports `user_id == "U1"`, and the test assertions are easier
17
+ // to read when the perm string matches the test fixture's user_id.
18
+ //
19
+ // Do NOT copy this fixture pattern into production seeds, schema
20
+ // migrations, or examples — a real `_rperm` entry must be a real
21
+ // Parse User objectId. Treat this file as test-only.
22
+
23
+ print("=== Atlas Search Test Setup ===");
24
+ print("Database: " + db.getName());
25
+
26
+ // Clear existing data
27
+ print("\n1. Clearing existing data...");
28
+ db.Song.drop();
29
+
30
+ // Insert test data
31
+ print("\n2. Inserting test song data...");
32
+ const songs = [
33
+ {
34
+ _id: "song1",
35
+ title: "Love Story",
36
+ artist: "Taylor Swift",
37
+ genre: "Pop",
38
+ plays: 5000000,
39
+ _created_at: new Date(),
40
+ _updated_at: new Date()
41
+ },
42
+ {
43
+ _id: "song2",
44
+ title: "Lovely Day",
45
+ artist: "Bill Withers",
46
+ genre: "Soul",
47
+ plays: 3000000,
48
+ _created_at: new Date(),
49
+ _updated_at: new Date()
50
+ },
51
+ {
52
+ _id: "song3",
53
+ title: "Bohemian Rhapsody",
54
+ artist: "Queen",
55
+ genre: "Rock",
56
+ plays: 10000000,
57
+ _created_at: new Date(),
58
+ _updated_at: new Date()
59
+ },
60
+ {
61
+ _id: "song4",
62
+ title: "Rock and Roll",
63
+ artist: "Led Zeppelin",
64
+ genre: "Rock",
65
+ plays: 4000000,
66
+ _created_at: new Date(),
67
+ _updated_at: new Date()
68
+ },
69
+ {
70
+ _id: "song5",
71
+ title: "What Is Love",
72
+ artist: "Haddaway",
73
+ genre: "Dance",
74
+ plays: 2500000,
75
+ _created_at: new Date(),
76
+ _updated_at: new Date()
77
+ },
78
+ {
79
+ _id: "song6",
80
+ title: "I Will Always Love You",
81
+ artist: "Whitney Houston",
82
+ genre: "Pop",
83
+ plays: 8000000,
84
+ _created_at: new Date(),
85
+ _updated_at: new Date()
86
+ },
87
+ {
88
+ _id: "song7",
89
+ title: "Crazy Little Thing Called Love",
90
+ artist: "Queen",
91
+ genre: "Rock",
92
+ plays: 3500000,
93
+ _created_at: new Date(),
94
+ _updated_at: new Date()
95
+ },
96
+ {
97
+ _id: "song8",
98
+ title: "Shape of You",
99
+ artist: "Ed Sheeran",
100
+ genre: "Pop",
101
+ plays: 12000000,
102
+ _created_at: new Date(),
103
+ _updated_at: new Date()
104
+ },
105
+ // Songs 9-11 exercise the ACL injection path of the Atlas Search
106
+ // integration tests. They omit _rperm (public — matched by the
107
+ // $exists: false branch of the ACL predicate), include a role-
108
+ // restricted _rperm, and include an owner-restricted _rperm.
109
+ // The Atlas Search ACL integration test asserts which sessions
110
+ // see which subset of these rows.
111
+ {
112
+ _id: "song9",
113
+ title: "Restricted Anthem",
114
+ artist: "Member Band",
115
+ genre: "Rock",
116
+ plays: 1500000,
117
+ _rperm: ["role:Member"],
118
+ _wperm: ["role:Admin"],
119
+ _created_at: new Date(),
120
+ _updated_at: new Date()
121
+ },
122
+ {
123
+ _id: "song10",
124
+ title: "Owner Only Ballad",
125
+ artist: "U1 Personal",
126
+ genre: "Acoustic",
127
+ plays: 100,
128
+ _rperm: ["U1"],
129
+ _wperm: ["U1"],
130
+ _created_at: new Date(),
131
+ _updated_at: new Date()
132
+ },
133
+ {
134
+ _id: "song11",
135
+ title: "Master Key Vault Track",
136
+ artist: "Locked Down",
137
+ genre: "Vault",
138
+ plays: 0,
139
+ _rperm: [],
140
+ _wperm: [],
141
+ _created_at: new Date(),
142
+ _updated_at: new Date()
143
+ }
144
+ ];
145
+
146
+ db.Song.insertMany(songs);
147
+ print("Inserted " + db.Song.countDocuments() + " songs (8 public, 1 role-restricted, 1 owner-restricted, 1 master-only)");
148
+
149
+ // Create Atlas Search index
150
+ print("\n3. Creating Atlas Search index...");
151
+
152
+ // Drop existing search indexes first
153
+ try {
154
+ const existingIndexes = db.Song.getSearchIndexes();
155
+ existingIndexes.forEach(function(idx) {
156
+ print("Dropping existing index: " + idx.name);
157
+ db.Song.dropSearchIndex(idx.name);
158
+ });
159
+ } catch (e) {
160
+ print("No existing search indexes to drop (or error checking): " + e.message);
161
+ }
162
+
163
+ // Wait a moment for any dropped indexes to be fully removed
164
+ sleep(1000);
165
+
166
+ // Create the search index with autocomplete support
167
+ const indexDefinition = {
168
+ mappings: {
169
+ dynamic: true,
170
+ fields: {
171
+ title: [
172
+ {
173
+ type: "string",
174
+ analyzer: "lucene.standard"
175
+ },
176
+ {
177
+ type: "autocomplete",
178
+ analyzer: "lucene.standard",
179
+ tokenization: "edgeGram",
180
+ minGrams: 2,
181
+ maxGrams: 15,
182
+ foldDiacritics: true
183
+ }
184
+ ],
185
+ artist: {
186
+ type: "string",
187
+ analyzer: "lucene.standard"
188
+ },
189
+ genre: [
190
+ {
191
+ type: "string",
192
+ analyzer: "lucene.standard"
193
+ },
194
+ {
195
+ type: "stringFacet"
196
+ }
197
+ ],
198
+ plays: [
199
+ {
200
+ type: "number"
201
+ },
202
+ {
203
+ type: "numberFacet"
204
+ }
205
+ ]
206
+ }
207
+ }
208
+ };
209
+
210
+ try {
211
+ db.Song.createSearchIndex("default", indexDefinition);
212
+ print("Search index 'default' created successfully");
213
+ } catch (e) {
214
+ print("Error creating search index: " + e.message);
215
+ // Try alternative method
216
+ try {
217
+ db.runCommand({
218
+ createSearchIndexes: "Song",
219
+ indexes: [{ name: "default", definition: indexDefinition }]
220
+ });
221
+ print("Search index created via runCommand");
222
+ } catch (e2) {
223
+ print("Alternative method also failed: " + e2.message);
224
+ }
225
+ }
226
+
227
+ // Wait for index to become queryable
228
+ print("\n4. Waiting for index to become ready...");
229
+ let attempts = 0;
230
+ const maxAttempts = 30;
231
+ let indexReady = false;
232
+
233
+ while (attempts < maxAttempts && !indexReady) {
234
+ try {
235
+ const indexes = db.Song.getSearchIndexes();
236
+ const defaultIndex = indexes.find(idx => idx.name === "default");
237
+ if (defaultIndex && defaultIndex.queryable === true) {
238
+ indexReady = true;
239
+ print("Index is ready and queryable!");
240
+ } else {
241
+ print("Waiting for index... (attempt " + (attempts + 1) + "/" + maxAttempts + ")");
242
+ sleep(2000);
243
+ }
244
+ } catch (e) {
245
+ print("Error checking index status: " + e.message);
246
+ sleep(2000);
247
+ }
248
+ attempts++;
249
+ }
250
+
251
+ if (!indexReady) {
252
+ print("WARNING: Index may not be ready yet. Tests might fail initially.");
253
+ }
254
+
255
+ // Verify setup
256
+ print("\n5. Verification:");
257
+ print(" Songs in collection: " + db.Song.countDocuments());
258
+
259
+ try {
260
+ const searchIndexes = db.Song.getSearchIndexes();
261
+ print(" Search indexes: " + searchIndexes.length);
262
+ searchIndexes.forEach(function(idx) {
263
+ print(" - " + idx.name + " (queryable: " + idx.queryable + ")");
264
+ });
265
+ } catch (e) {
266
+ print(" Could not list search indexes: " + e.message);
267
+ }
268
+
269
+ // Test a simple search to verify it works
270
+ print("\n6. Testing search...");
271
+ try {
272
+ const testResult = db.Song.aggregate([
273
+ { $search: { index: "default", text: { query: "love", path: { wildcard: "*" } } } },
274
+ { $limit: 3 }
275
+ ]).toArray();
276
+ print(" Test search found " + testResult.length + " results for 'love'");
277
+ if (testResult.length > 0) {
278
+ print(" First result: " + testResult[0].title + " by " + testResult[0].artist);
279
+ }
280
+ } catch (e) {
281
+ print(" Search test failed (index may still be building): " + e.message);
282
+ }
283
+
284
+ print("\n=== Atlas Search Setup Complete ===\n");
@@ -0,0 +1,76 @@
1
+ # Docker Compose configuration for Atlas Search integration tests
2
+ # Uses the official mongodb/mongodb-atlas-local image which supports Atlas Search
3
+ #
4
+ # Usage:
5
+ # Start: docker-compose -f scripts/docker/docker-compose.atlas.yml up -d
6
+ # Stop: docker-compose -f scripts/docker/docker-compose.atlas.yml down
7
+ # Logs: docker-compose -f scripts/docker/docker-compose.atlas.yml logs -f atlas-init
8
+ # Reset: docker-compose -f scripts/docker/docker-compose.atlas.yml down -v && docker-compose -f scripts/docker/docker-compose.atlas.yml up -d
9
+ #
10
+ # Run tests:
11
+ # ATLAS_URI="mongodb://localhost:27020/parse_atlas_test?directConnection=true" ruby -Ilib:test test/lib/parse/atlas_search_integration_test.rb
12
+ #
13
+ # Stability notes (see commit history for context):
14
+ # The mongodb-atlas-local image runs a supervisor ("runner") that intentionally
15
+ # restarts mongod 1-2 times during startup to enable the replica set and wire
16
+ # in mongot (Atlas Search). Those startup SIGTERMs are NORMAL and visible in
17
+ # `docker logs` — do not interpret them as crashes. The container is "ready"
18
+ # when the runner's built-in healthcheck (`runner healthcheck`) reports healthy,
19
+ # which checks both mongod AND mongot. We rely on that built-in healthcheck
20
+ # rather than overriding it with a faster mongosh probe.
21
+
22
+ services:
23
+ atlas-local:
24
+ # Pinned patch tag rather than the floating :8.0 — mongodb-atlas-local has
25
+ # known per-patch start-up flakiness (see testcontainers/testcontainers-java#10267
26
+ # and MongoDB community forum 321179). Bump deliberately, not by drift.
27
+ image: mongodb/mongodb-atlas-local:8.0.10
28
+ container_name: parse-stack-atlas-local
29
+ # `hostname` is called out by the official compose example — the embedded
30
+ # single-node replica set advertises this name, and a stable value makes
31
+ # restart-after-down work cleanly.
32
+ hostname: atlas-local
33
+ # Surface runner + mongot logs through `docker logs` so failures during the
34
+ # supervisor's bring-up phase are visible. Pure visibility, no behavior change.
35
+ environment:
36
+ RUNNER_LOG_FILE: /dev/stderr
37
+ MONGOT_LOG_FILE: /dev/stderr
38
+ # Loopback-only by default — Atlas Local has no auth configured.
39
+ ports:
40
+ - "${ATLAS_BIND:-127.0.0.1}:27020:27017"
41
+ volumes:
42
+ # Persist mongod data, replset config, and — critically — the mongot
43
+ # Lucene index directory. Without /data/mongot persistence, every restart
44
+ # forces a full search re-index, which lengthens "ready" time and is a
45
+ # real source of test flakiness. Matches the official compose example.
46
+ - atlas-data:/data/db
47
+ - atlas-configdb:/data/configdb
48
+ - atlas-mongot:/data/mongot
49
+ # Recover from genuine crashes automatically. Test runs `docker compose down`
50
+ # explicitly, so this does not interfere with teardown.
51
+ restart: unless-stopped
52
+ # NOTE: We deliberately do NOT override `healthcheck:` here. The image ships
53
+ # a built-in `runner healthcheck` (interval 30s, timeout 60s, start_period
54
+ # 60s, retries 3) that verifies BOTH mongod and mongot are ready. The
55
+ # previous mongosh ping at 5s interval was both noisier (spawns a mongosh
56
+ # process every 5s) and incorrect (it returned healthy as soon as mongod
57
+ # accepted connections, before mongot was wired in, so atlas-init could
58
+ # race the search-index subsystem).
59
+
60
+ atlas-init:
61
+ image: mongodb/mongodb-atlas-local:8.0.10
62
+ container_name: parse-stack-atlas-init
63
+ depends_on:
64
+ atlas-local:
65
+ # Uses the image's built-in healthcheck (see above), so this now waits
66
+ # for mongot readiness too — not just mongod accepting connections.
67
+ condition: service_healthy
68
+ volumes:
69
+ - ./atlas-init.js:/atlas-init.js:ro
70
+ entrypoint: ["mongosh", "--quiet", "mongodb://atlas-local:27017/parse_atlas_test", "/atlas-init.js"]
71
+ restart: "no"
72
+
73
+ volumes:
74
+ atlas-data:
75
+ atlas-configdb:
76
+ atlas-mongot:
@@ -0,0 +1,106 @@
1
+ version: '3.8'
2
+
3
+ services:
4
+ mongo:
5
+ image: mongo:8
6
+ container_name: parse-stack-test-mongo
7
+ # Bind to loopback so the test database isn't reachable from the LAN
8
+ # when a developer runs `docker-compose up`. Override with
9
+ # `MONGO_BIND=0.0.0.0` if you really want it exposed.
10
+ ports:
11
+ - "${MONGO_BIND:-127.0.0.1}:27019:27017"
12
+ environment:
13
+ MONGO_INITDB_ROOT_USERNAME: ${MONGO_ROOT_USER:-admin}
14
+ MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD:-password}
15
+ MONGO_INITDB_DATABASE: admin
16
+ volumes:
17
+ - mongo-data:/data/db
18
+ - ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
19
+
20
+ parse:
21
+ build:
22
+ context: ..
23
+ dockerfile: docker/Dockerfile.parse
24
+ container_name: parse-stack-test-server
25
+ # Bind to loopback by default — Parse Server with the test master key
26
+ # has no business being reachable on a developer's LAN.
27
+ ports:
28
+ - "${PARSE_BIND:-127.0.0.1}:2337:1337"
29
+ # `host.docker.internal` is needed so Parse Server can POST to the
30
+ # in-process WEBrick that backs webhook integration tests. Docker
31
+ # Desktop on Mac/Windows resolves this natively; on Linux we map it
32
+ # to host-gateway. Without this, end-to-end webhook tests are skipped.
33
+ extra_hosts:
34
+ - "host.docker.internal:host-gateway"
35
+ depends_on:
36
+ mongo:
37
+ condition: service_started
38
+ volumes:
39
+ - ../../test/cloud:/parse-server/cloud
40
+ - ../../config:/parse-server/config
41
+ - ../start-parse.sh:/start-parse.sh:ro
42
+ environment:
43
+ # Application id / master key / database URI are provided to the
44
+ # container so `start-parse.sh` doesn't have to invent fallbacks.
45
+ # `start-parse.sh` will abort if any of these is unset, so any
46
+ # name drift here surfaces immediately rather than booting Parse
47
+ # Server with whatever placeholder default the SDK README documents.
48
+ PARSE_SERVER_APPLICATION_ID: ${PARSE_APP_ID:-myAppId}
49
+ PARSE_SERVER_MASTER_KEY: ${PARSE_MASTER_KEY:-myMasterKey}
50
+ PARSE_SERVER_REST_API_KEY: ${PARSE_API_KEY:-test-rest-key}
51
+ PARSE_SERVER_DATABASE_URI: mongodb://${MONGO_ROOT_USER:-admin}:${MONGO_ROOT_PASSWORD:-password}@mongo:27017/parse?authSource=admin
52
+ # Accept client-supplied objectId on create. Required by the
53
+ # `parse_reference precompute: true` DSL. The SDK only forwards
54
+ # client objectIds under master-key authority; for non-master
55
+ # enforcement across other SDKs, see the Cloud Code hook documented
56
+ # in lib/parse/model/core/parse_reference.rb.
57
+ PARSE_SERVER_ALLOW_CUSTOM_OBJECT_ID: "true"
58
+ # Tests connect from the docker bridge gateway (typically
59
+ # 172.16.0.0/12 or 192.168.x.x), not loopback. Widen the master-key
60
+ # allowlist accordingly. DO NOT mirror this into a deployed
61
+ # environment — production should keep the allowlist tight to the
62
+ # subnets that actually host the Ruby app.
63
+ PARSE_SERVER_MASTER_KEY_IPS: "127.0.0.1/32,::1/128,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8"
64
+ # Remove health check for now since it's causing startup delays
65
+ # healthcheck:
66
+ # test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:1337/parse/health"]
67
+ # interval: 10s
68
+ # timeout: 5s
69
+ # retries: 5
70
+ # start_period: 30s
71
+
72
+ parse-dashboard:
73
+ image: parseplatform/parse-dashboard:9
74
+ container_name: parse-stack-test-dashboard
75
+ # Loopback-only by default. This compose is for `rake test:integration`
76
+ # and local debugging; do not expose to the network.
77
+ ports:
78
+ - "${DASHBOARD_BIND:-127.0.0.1}:4040:4040"
79
+ environment:
80
+ # `allowInsecureHTTP` and `useEncryptedPasswords: false` are kept
81
+ # because this stack runs over plain HTTP on loopback. Do NOT copy
82
+ # these settings to a deployed environment.
83
+ PARSE_DASHBOARD_ALLOW_INSECURE_HTTP: "1"
84
+ PARSE_SERVER_MASTER_KEY: ${PARSE_MASTER_KEY:-myMasterKey}
85
+ PARSE_SERVER_APPLICATION_ID: ${PARSE_APP_ID:-myAppId}
86
+ PARSE_SERVER_URL: http://localhost:2337/parse
87
+ PARSE_DASHBOARD_CONFIG: |
88
+ {
89
+ "apps": [{
90
+ "serverURL": "http://localhost:2337/parse",
91
+ "appId": "${PARSE_APP_ID:-myAppId}",
92
+ "masterKey": "${PARSE_MASTER_KEY:-myMasterKey}",
93
+ "appName": "ParseStackTest"
94
+ }],
95
+ "users": [{
96
+ "user": "${DASHBOARD_USER:-admin}",
97
+ "pass": "${DASHBOARD_PASS:-admin}"
98
+ }],
99
+ "useEncryptedPasswords": false,
100
+ "allowInsecureHTTP": true
101
+ }
102
+ depends_on:
103
+ - parse
104
+
105
+ volumes:
106
+ mongo-data:
@@ -0,0 +1,21 @@
1
+ // MongoDB initialization script
2
+ // This script runs when the MongoDB container is first created
3
+ // It grants the admin user access to the parse database for direct queries
4
+
5
+ // The admin user needs readWriteAnyDatabase role to access all databases
6
+ db = db.getSiblingDB('admin');
7
+
8
+ // Grant admin user roles needed for all database access
9
+ db.grantRolesToUser('admin', [
10
+ { role: 'readWriteAnyDatabase', db: 'admin' },
11
+ { role: 'dbAdminAnyDatabase', db: 'admin' }
12
+ ]);
13
+
14
+ // Initialize the parse database
15
+ db = db.getSiblingDB('parse');
16
+
17
+ // Create a placeholder collection to ensure the database exists
18
+ db.createCollection('_init');
19
+ db.getCollection('_init').drop();
20
+
21
+ print('MongoDB initialization completed - admin user granted full database access');