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.
- checksums.yaml +7 -0
- data/.bundle/config +2 -0
- data/.env.sample +112 -0
- data/.env.test +10 -0
- data/.github/workflows/ruby.yml +36 -0
- data/.gitignore +49 -0
- data/.ruby-version +1 -0
- data/.solargraph.yml +22 -0
- data/CHANGELOG.md +5816 -0
- data/Gemfile +30 -0
- data/Gemfile.lock +175 -0
- data/LICENSE.txt +23 -0
- data/Makefile +63 -0
- data/README.md +5655 -0
- data/Rakefile +573 -0
- data/bin/console +38 -0
- data/bin/parse-console +136 -0
- data/bin/server +17 -0
- data/bin/setup +7 -0
- data/config/parse-config.json +12 -0
- data/docs/TEST_SERVER.md +271 -0
- data/docs/_config.yml +1 -0
- data/docs/mcp_guide.md +3484 -0
- data/docs/mongodb_direct_guide.md +1348 -0
- data/docs/mongodb_index_optimization_guide.md +631 -0
- data/examples/transaction_example.rb +219 -0
- data/lib/parse/acl_scope.rb +728 -0
- data/lib/parse/agent/cancellation_token.rb +80 -0
- data/lib/parse/agent/constraint_translator.rb +480 -0
- data/lib/parse/agent/describe.rb +420 -0
- data/lib/parse/agent/errors.rb +133 -0
- data/lib/parse/agent/mcp_client.rb +557 -0
- data/lib/parse/agent/mcp_dispatcher.rb +1023 -0
- data/lib/parse/agent/mcp_rack_app.rb +1143 -0
- data/lib/parse/agent/mcp_server.rb +376 -0
- data/lib/parse/agent/metadata_audit.rb +259 -0
- data/lib/parse/agent/metadata_dsl.rb +733 -0
- data/lib/parse/agent/metadata_registry.rb +794 -0
- data/lib/parse/agent/pipeline_validator.rb +82 -0
- data/lib/parse/agent/prompts.rb +351 -0
- data/lib/parse/agent/rate_limiter.rb +158 -0
- data/lib/parse/agent/relation_graph.rb +162 -0
- data/lib/parse/agent/result_formatter.rb +453 -0
- data/lib/parse/agent/tools.rb +5489 -0
- data/lib/parse/agent.rb +3249 -0
- data/lib/parse/api/aggregate.rb +79 -0
- data/lib/parse/api/all.rb +26 -0
- data/lib/parse/api/analytics.rb +18 -0
- data/lib/parse/api/batch.rb +33 -0
- data/lib/parse/api/cloud_functions.rb +58 -0
- data/lib/parse/api/config.rb +125 -0
- data/lib/parse/api/files.rb +29 -0
- data/lib/parse/api/hooks.rb +117 -0
- data/lib/parse/api/objects.rb +146 -0
- data/lib/parse/api/path_segment.rb +75 -0
- data/lib/parse/api/push.rb +20 -0
- data/lib/parse/api/schema.rb +49 -0
- data/lib/parse/api/server.rb +50 -0
- data/lib/parse/api/sessions.rb +24 -0
- data/lib/parse/api/users.rb +250 -0
- data/lib/parse/atlas_search/index_manager.rb +353 -0
- data/lib/parse/atlas_search/result.rb +204 -0
- data/lib/parse/atlas_search/search_builder.rb +604 -0
- data/lib/parse/atlas_search/session.rb +253 -0
- data/lib/parse/atlas_search.rb +995 -0
- data/lib/parse/client/authentication.rb +97 -0
- data/lib/parse/client/batch.rb +234 -0
- data/lib/parse/client/body_builder.rb +240 -0
- data/lib/parse/client/caching.rb +203 -0
- data/lib/parse/client/logging.rb +293 -0
- data/lib/parse/client/profiling.rb +181 -0
- data/lib/parse/client/protocol.rb +91 -0
- data/lib/parse/client/request.rb +233 -0
- data/lib/parse/client/response.rb +208 -0
- data/lib/parse/client.rb +1104 -0
- data/lib/parse/clp_scope.rb +361 -0
- data/lib/parse/live_query/circuit_breaker.rb +256 -0
- data/lib/parse/live_query/client.rb +1001 -0
- data/lib/parse/live_query/configuration.rb +224 -0
- data/lib/parse/live_query/event.rb +115 -0
- data/lib/parse/live_query/event_queue.rb +272 -0
- data/lib/parse/live_query/health_monitor.rb +214 -0
- data/lib/parse/live_query/logging.rb +149 -0
- data/lib/parse/live_query/subscription.rb +294 -0
- data/lib/parse/live_query.rb +163 -0
- data/lib/parse/lookup_rewriter.rb +445 -0
- data/lib/parse/model/acl.rb +968 -0
- data/lib/parse/model/associations/belongs_to.rb +275 -0
- data/lib/parse/model/associations/collection_proxy.rb +435 -0
- data/lib/parse/model/associations/has_many.rb +597 -0
- data/lib/parse/model/associations/has_one.rb +158 -0
- data/lib/parse/model/associations/pointer_collection_proxy.rb +134 -0
- data/lib/parse/model/associations/relation_collection_proxy.rb +177 -0
- data/lib/parse/model/bytes.rb +62 -0
- data/lib/parse/model/classes/audience.rb +262 -0
- data/lib/parse/model/classes/installation.rb +363 -0
- data/lib/parse/model/classes/job_schedule.rb +153 -0
- data/lib/parse/model/classes/job_status.rb +264 -0
- data/lib/parse/model/classes/product.rb +75 -0
- data/lib/parse/model/classes/push_status.rb +263 -0
- data/lib/parse/model/classes/role.rb +751 -0
- data/lib/parse/model/classes/session.rb +201 -0
- data/lib/parse/model/classes/user.rb +943 -0
- data/lib/parse/model/clp.rb +544 -0
- data/lib/parse/model/core/actions.rb +1268 -0
- data/lib/parse/model/core/builder.rb +139 -0
- data/lib/parse/model/core/create_lock.rb +386 -0
- data/lib/parse/model/core/describe.rb +382 -0
- data/lib/parse/model/core/enhanced_change_tracking.rb +159 -0
- data/lib/parse/model/core/errors.rb +38 -0
- data/lib/parse/model/core/fetching.rb +566 -0
- data/lib/parse/model/core/field_guards.rb +220 -0
- data/lib/parse/model/core/indexing.rb +382 -0
- data/lib/parse/model/core/parse_reference.rb +407 -0
- data/lib/parse/model/core/properties.rb +809 -0
- data/lib/parse/model/core/querying.rb +491 -0
- data/lib/parse/model/core/schema.rb +202 -0
- data/lib/parse/model/core/search_indexing.rb +174 -0
- data/lib/parse/model/date.rb +88 -0
- data/lib/parse/model/email.rb +213 -0
- data/lib/parse/model/file.rb +527 -0
- data/lib/parse/model/geojson.rb +271 -0
- data/lib/parse/model/geopoint.rb +261 -0
- data/lib/parse/model/model.rb +260 -0
- data/lib/parse/model/object.rb +2068 -0
- data/lib/parse/model/phone.rb +520 -0
- data/lib/parse/model/pointer.rb +443 -0
- data/lib/parse/model/polygon.rb +406 -0
- data/lib/parse/model/push.rb +975 -0
- data/lib/parse/model/shortnames.rb +8 -0
- data/lib/parse/model/time_zone.rb +141 -0
- data/lib/parse/model/validations/uniqueness_validator.rb +97 -0
- data/lib/parse/model/validations.rb +96 -0
- data/lib/parse/mongodb.rb +2300 -0
- data/lib/parse/pipeline_security.rb +554 -0
- data/lib/parse/query/constraint.rb +198 -0
- data/lib/parse/query/constraints.rb +3279 -0
- data/lib/parse/query/cursor.rb +434 -0
- data/lib/parse/query/n_plus_one_detector.rb +445 -0
- data/lib/parse/query/operation.rb +104 -0
- data/lib/parse/query/ordering.rb +66 -0
- data/lib/parse/query.rb +7028 -0
- data/lib/parse/schema/index_migrator.rb +291 -0
- data/lib/parse/schema/search_index_migrator.rb +289 -0
- data/lib/parse/schema.rb +494 -0
- data/lib/parse/stack/generators/rails.rb +40 -0
- data/lib/parse/stack/generators/templates/model.erb +51 -0
- data/lib/parse/stack/generators/templates/model_installation.rb +4 -0
- data/lib/parse/stack/generators/templates/model_role.rb +4 -0
- data/lib/parse/stack/generators/templates/model_session.rb +4 -0
- data/lib/parse/stack/generators/templates/model_user.rb +11 -0
- data/lib/parse/stack/generators/templates/parse.rb +12 -0
- data/lib/parse/stack/generators/templates/webhooks.rb +10 -0
- data/lib/parse/stack/railtie.rb +18 -0
- data/lib/parse/stack/tasks.rb +563 -0
- data/lib/parse/stack/version.rb +11 -0
- data/lib/parse/stack.rb +455 -0
- data/lib/parse/two_factor_auth/user_extension.rb +449 -0
- data/lib/parse/two_factor_auth.rb +310 -0
- data/lib/parse/webhooks/payload.rb +360 -0
- data/lib/parse/webhooks/registration.rb +199 -0
- data/lib/parse/webhooks/replay_protection.rb +189 -0
- data/lib/parse/webhooks.rb +510 -0
- data/lib/parse-stack-next.rb +5 -0
- data/lib/parse-stack.rb +5 -0
- data/parse-stack-next.gemspec +82 -0
- data/parse-stack.png +0 -0
- data/scripts/debug-ips.js +35 -0
- data/scripts/docker/Dockerfile.parse +13 -0
- data/scripts/docker/atlas-init.js +284 -0
- data/scripts/docker/docker-compose.atlas.yml +76 -0
- data/scripts/docker/docker-compose.test.yml +106 -0
- data/scripts/docker/mongo-init.js +21 -0
- data/scripts/eval_mcp_with_lm_studio.rb +274 -0
- data/scripts/start-parse.sh +90 -0
- data/scripts/start_mcp_server.rb +78 -0
- data/scripts/test_server_connection.rb +82 -0
- 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');
|