@reventlessdev/reventless-seed-aws 1.0.0-alpha.6 → 1.0.0-alpha.8
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.
- package/CHANGELOG.md +14 -0
- package/lib/bs/.compiler.log +2 -2
- package/lib/bs/compiler-info.json +1 -1
- package/lib/bs/src/ReventlessSeedAws.ast +0 -0
- package/lib/bs/src/ReventlessSeedAws.cmi +0 -0
- package/lib/bs/src/ReventlessSeedAws.cmj +0 -0
- package/lib/bs/src/ReventlessSeedAws.cmt +0 -0
- package/lib/bs/src/ReventlessSeedAws.res +3 -47
- package/lib/bs/src/ReventlessSeedAws.res.mjs +1 -30
- package/lib/bs/src/ReventlessSeedAws_Reset.ast +0 -0
- package/lib/bs/src/ReventlessSeedAws_Reset.cmi +0 -0
- package/lib/bs/src/ReventlessSeedAws_Reset.cmj +0 -0
- package/lib/bs/src/ReventlessSeedAws_Reset.cmt +0 -0
- package/lib/bs/src/ReventlessSeedAws_Reset.res +261 -6
- package/lib/bs/src/ReventlessSeedAws_Reset.res.mjs +211 -25
- package/lib/bs/tests/EndpointResolutionTest.ast +0 -0
- package/lib/bs/tests/EndpointResolutionTest.cmi +0 -0
- package/lib/bs/tests/EndpointResolutionTest.cmj +0 -0
- package/lib/bs/tests/EndpointResolutionTest.cmt +0 -0
- package/lib/bs/tests/EndpointResolutionTest.res +12 -109
- package/lib/bs/tests/EndpointResolutionTest.res.mjs +4 -97
- package/lib/bs/tests/ObjectStoreResetTest.ast +0 -0
- package/lib/bs/tests/ObjectStoreResetTest.cmi +0 -0
- package/lib/bs/tests/ObjectStoreResetTest.cmj +0 -0
- package/lib/bs/tests/ObjectStoreResetTest.cmt +0 -0
- package/lib/bs/tests/ObjectStoreResetTest.res +170 -0
- package/lib/bs/tests/ObjectStoreResetTest.res.mjs +211 -0
- package/lib/ocaml/.compiler.log +2 -2
- package/lib/ocaml/EndpointResolutionTest.ast +0 -0
- package/lib/ocaml/EndpointResolutionTest.cmi +0 -0
- package/lib/ocaml/EndpointResolutionTest.cmj +0 -0
- package/lib/ocaml/EndpointResolutionTest.cmt +0 -0
- package/lib/ocaml/EndpointResolutionTest.res +12 -109
- package/lib/ocaml/ObjectStoreResetTest.ast +0 -0
- package/lib/ocaml/ObjectStoreResetTest.cmi +0 -0
- package/lib/ocaml/ObjectStoreResetTest.cmj +0 -0
- package/lib/ocaml/ObjectStoreResetTest.cmt +0 -0
- package/lib/ocaml/ObjectStoreResetTest.res +170 -0
- package/lib/ocaml/ReventlessSeedAws.ast +0 -0
- package/lib/ocaml/ReventlessSeedAws.cmi +0 -0
- package/lib/ocaml/ReventlessSeedAws.cmj +0 -0
- package/lib/ocaml/ReventlessSeedAws.cmt +0 -0
- package/lib/ocaml/ReventlessSeedAws.res +3 -47
- package/lib/ocaml/ReventlessSeedAws_Reset.ast +0 -0
- package/lib/ocaml/ReventlessSeedAws_Reset.cmi +0 -0
- package/lib/ocaml/ReventlessSeedAws_Reset.cmj +0 -0
- package/lib/ocaml/ReventlessSeedAws_Reset.cmt +0 -0
- package/lib/ocaml/ReventlessSeedAws_Reset.res +261 -6
- package/package.json +2 -2
- package/src/ReventlessSeedAws.res +3 -47
- package/src/ReventlessSeedAws.res.mjs +1 -30
- package/src/ReventlessSeedAws_Reset.res +261 -6
- package/src/ReventlessSeedAws_Reset.res.mjs +211 -25
- package/tests/EndpointResolutionTest.res +12 -109
- package/tests/EndpointResolutionTest.res.mjs +4 -97
- package/tests/ObjectStoreResetTest.res +170 -0
- package/tests/ObjectStoreResetTest.res.mjs +211 -0
|
@@ -5,6 +5,7 @@ import * as S3$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/S3.res.mjs";
|
|
|
5
5
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
6
6
|
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
7
7
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
8
|
+
import * as Stdlib_String from "@rescript/runtime/lib/es6/Stdlib_String.js";
|
|
8
9
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
9
10
|
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
10
11
|
import * as ReventlessSeedAws from "./ReventlessSeedAws.res.mjs";
|
|
@@ -198,6 +199,97 @@ async function discover(region, stack, platform) {
|
|
|
198
199
|
];
|
|
199
200
|
}
|
|
200
201
|
|
|
202
|
+
function splitQualified(key) {
|
|
203
|
+
return Stdlib_Option.map(Stdlib_String.indexOfOpt(key, "."), i => [
|
|
204
|
+
key.slice(0, i),
|
|
205
|
+
key.slice(i + 1 | 0, key.length)
|
|
206
|
+
]);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function parseObjectStores(json) {
|
|
210
|
+
if (json !== undefined) {
|
|
211
|
+
if (typeof json === "object" && json !== null && !Array.isArray(json)) {
|
|
212
|
+
return Stdlib_Array.reduce(Object.entries(json), {
|
|
213
|
+
TAG: "Ok",
|
|
214
|
+
_0: []
|
|
215
|
+
}, (acc, param) => {
|
|
216
|
+
let entry = param[1];
|
|
217
|
+
let qualified = param[0];
|
|
218
|
+
if (acc.TAG !== "Ok") {
|
|
219
|
+
return acc;
|
|
220
|
+
}
|
|
221
|
+
let match = splitQualified(qualified);
|
|
222
|
+
let match$1 = Stdlib_Option.flatMap(field(entry, "bucketName"), asString);
|
|
223
|
+
let match$2 = Stdlib_Option.flatMap(field(entry, "keyPrefix"), asString);
|
|
224
|
+
if (match !== undefined && match$1 !== undefined && match$2 !== undefined) {
|
|
225
|
+
return {
|
|
226
|
+
TAG: "Ok",
|
|
227
|
+
_0: acc._0.concat([{
|
|
228
|
+
qualified: qualified,
|
|
229
|
+
plugin: match[0],
|
|
230
|
+
store: match[1],
|
|
231
|
+
bucketName: match$1,
|
|
232
|
+
keyPrefix: match$2
|
|
233
|
+
}])
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
TAG: "Error",
|
|
238
|
+
_0: `the platform stack's \`objectStores\` output has a malformed entry for "` + qualified + `" — expected a {plugin}.{store} key carrying bucketName and keyPrefix.`
|
|
239
|
+
};
|
|
240
|
+
});
|
|
241
|
+
} else {
|
|
242
|
+
return {
|
|
243
|
+
TAG: "Error",
|
|
244
|
+
_0: "the platform stack's `objectStores` output is not an object."
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
return {
|
|
249
|
+
TAG: "Ok",
|
|
250
|
+
_0: []
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function validateStores(stores) {
|
|
256
|
+
let s = stores.find(s => {
|
|
257
|
+
if (s.keyPrefix === "") {
|
|
258
|
+
return true;
|
|
259
|
+
} else {
|
|
260
|
+
return s.store.includes("/");
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
if (s !== undefined) {
|
|
264
|
+
return {
|
|
265
|
+
TAG: "Error",
|
|
266
|
+
_0: `store "` + s.qualified + `" has an unusable key prefix ("` + s.keyPrefix + `") — a store name may not be empty or contain "/".`
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
let message = Stdlib_Array.findMap(stores, a => Stdlib_Array.findMap(stores, b => {
|
|
270
|
+
if (a.qualified === b.qualified || a.bucketName !== b.bucketName) {
|
|
271
|
+
return;
|
|
272
|
+
} else if (a.keyPrefix === b.keyPrefix) {
|
|
273
|
+
return `stores "` + a.qualified + `" and "` + b.qualified + `" both live at ` + (a.bucketName + `/` + a.keyPrefix + `/ — a prefix-scoped wipe cannot tell their objects `) + `apart. Rename one store, or qualify the \`@storageRef\` annotation if they were meant to be one shared store.`;
|
|
274
|
+
} else if (b.keyPrefix.startsWith(a.keyPrefix + "/")) {
|
|
275
|
+
return `store "` + a.qualified + `" (` + a.bucketName + `/` + a.keyPrefix + `/) encloses "` + b.qualified + `" ` + (`(` + b.keyPrefix + `/) — wiping the first would delete the second's objects. Rename one.`);
|
|
276
|
+
} else {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
}));
|
|
280
|
+
if (message !== undefined) {
|
|
281
|
+
return {
|
|
282
|
+
TAG: "Error",
|
|
283
|
+
_0: message
|
|
284
|
+
};
|
|
285
|
+
} else {
|
|
286
|
+
return {
|
|
287
|
+
TAG: "Ok",
|
|
288
|
+
_0: undefined
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
201
293
|
async function countTable(table) {
|
|
202
294
|
let loop = async (start, acc) => {
|
|
203
295
|
let out = await DynamoDb_DocumentClient$AwsSdk.ScanCommand.send(new LibDynamodb.ScanCommand({
|
|
@@ -216,10 +308,11 @@ async function countTable(table) {
|
|
|
216
308
|
return await loop(undefined, 0);
|
|
217
309
|
}
|
|
218
310
|
|
|
219
|
-
async function countBucket(bucket) {
|
|
311
|
+
async function countBucket(bucket, prefix) {
|
|
220
312
|
let loop = async (keyMarker, versionMarker, acc) => {
|
|
221
313
|
let out = await S3$AwsSdk.ListObjectVersionsCommand.send(new ClientS3.ListObjectVersionsCommand({
|
|
222
314
|
Bucket: bucket,
|
|
315
|
+
Prefix: prefix,
|
|
223
316
|
KeyMarker: keyMarker,
|
|
224
317
|
VersionIdMarker: versionMarker
|
|
225
318
|
}));
|
|
@@ -306,10 +399,11 @@ async function truncateTable(table) {
|
|
|
306
399
|
return await loop(undefined);
|
|
307
400
|
}
|
|
308
401
|
|
|
309
|
-
async function emptyBucket(bucket) {
|
|
402
|
+
async function emptyBucket(bucket, prefix) {
|
|
310
403
|
let loop = async (keyMarker, versionMarker) => {
|
|
311
404
|
let out = await S3$AwsSdk.ListObjectVersionsCommand.send(new ClientS3.ListObjectVersionsCommand({
|
|
312
405
|
Bucket: bucket,
|
|
406
|
+
Prefix: prefix,
|
|
313
407
|
KeyMarker: keyMarker,
|
|
314
408
|
VersionIdMarker: versionMarker
|
|
315
409
|
}));
|
|
@@ -341,6 +435,10 @@ async function emptyBucket(bucket) {
|
|
|
341
435
|
return await loop(undefined, undefined);
|
|
342
436
|
}
|
|
343
437
|
|
|
438
|
+
function pluginOf(t) {
|
|
439
|
+
return Stdlib_Option.getOr(t.plugin, t.label);
|
|
440
|
+
}
|
|
441
|
+
|
|
344
442
|
async function chooseScope(targets) {
|
|
345
443
|
let domain = targets.filter(t => t.group === "Domain");
|
|
346
444
|
let platform = targets.filter(t => t.group === "Platform");
|
|
@@ -479,6 +577,15 @@ function reportAll(resolvedList, stack, region) {
|
|
|
479
577
|
});
|
|
480
578
|
if (r.bucketCounts.length === 0) {
|
|
481
579
|
console.log(" (none)");
|
|
580
|
+
}
|
|
581
|
+
if (r.storeCounts.length !== 0) {
|
|
582
|
+
console.log(" Object stores:");
|
|
583
|
+
r.storeCounts.forEach(param => {
|
|
584
|
+
let c = param[1];
|
|
585
|
+
let s = param[0];
|
|
586
|
+
total.contents = total.contents + c | 0;
|
|
587
|
+
console.log(` ` + c.toString().padStart(8, " ") + ` ` + s.qualified + ` ` + (s.bucketName + `/` + s.keyPrefix + `/`));
|
|
588
|
+
});
|
|
482
589
|
return;
|
|
483
590
|
}
|
|
484
591
|
});
|
|
@@ -507,7 +614,38 @@ function run(stack, backend, targets, param) {
|
|
|
507
614
|
};
|
|
508
615
|
}
|
|
509
616
|
let selected = await chooseScope(targets);
|
|
510
|
-
let
|
|
617
|
+
let platformTarget = targets.find(t => t.group === "Platform");
|
|
618
|
+
let allStores;
|
|
619
|
+
if (platformTarget !== undefined) {
|
|
620
|
+
let output = ReventlessSeedAws.stackOutputs(platformTarget.projectDir, backend$1, stack$1);
|
|
621
|
+
let stores = parseObjectStores(field(output, "objectStores"));
|
|
622
|
+
if (stores.TAG === "Ok") {
|
|
623
|
+
allStores = stores._0;
|
|
624
|
+
} else {
|
|
625
|
+
throw {
|
|
626
|
+
RE_EXN_ID: Seed$ReventlessSeed.Failed,
|
|
627
|
+
_1: stores._0,
|
|
628
|
+
Error: new Error()
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
} else {
|
|
632
|
+
console.log("");
|
|
633
|
+
console.log("Note: no `platform` target is declared, so declared object stores could not be resolved — any uploaded objects will be left in place.");
|
|
634
|
+
allStores = [];
|
|
635
|
+
}
|
|
636
|
+
let message = validateStores(allStores);
|
|
637
|
+
if (message.TAG !== "Ok") {
|
|
638
|
+
throw {
|
|
639
|
+
RE_EXN_ID: Seed$ReventlessSeed.Failed,
|
|
640
|
+
_1: `refusing: ` + message._0,
|
|
641
|
+
Error: new Error()
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
let storeBucketNames = allStores.map(s => s.bucketName);
|
|
645
|
+
let selectedStores = allStores.filter(s => selected.some(t => pluginOf(t) === s.plugin));
|
|
646
|
+
let match = selectedStores.length !== 0;
|
|
647
|
+
let gated = match && platformTarget !== undefined && !selected.some(t => t.projectDir === platformTarget.projectDir) ? selected.concat([platformTarget]) : selected;
|
|
648
|
+
let regions = gated.map(t => gateTarget(t, backend$1, stack$1));
|
|
511
649
|
let region = regions[0];
|
|
512
650
|
if (regions.some(r => r !== region)) {
|
|
513
651
|
throw {
|
|
@@ -517,14 +655,29 @@ function run(stack, backend, targets, param) {
|
|
|
517
655
|
};
|
|
518
656
|
}
|
|
519
657
|
process.env["AWS_REGION"] = region;
|
|
658
|
+
if (selectedStores.length !== 0 && platformTarget !== undefined) {
|
|
659
|
+
let platformProject = projectName(platformTarget.projectDir);
|
|
660
|
+
let match$1 = await discover(region, stack$1, platformProject);
|
|
661
|
+
let platformBuckets = match$1[1];
|
|
662
|
+
selectedStores.forEach(s => {
|
|
663
|
+
if (platformBuckets.includes(s.bucketName)) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
throw {
|
|
667
|
+
RE_EXN_ID: Seed$ReventlessSeed.Failed,
|
|
668
|
+
_1: `refusing: store "` + s.qualified + `" names bucket ` + s.bucketName + `, which does not ` + (`carry reventless:platform=` + platformProject + ` + reventless:environment=` + stack$1 + `.`),
|
|
669
|
+
Error: new Error()
|
|
670
|
+
};
|
|
671
|
+
});
|
|
672
|
+
}
|
|
520
673
|
let resolvedList = [];
|
|
521
674
|
for (let i = 0, i_finish = selected.length; i < i_finish; ++i) {
|
|
522
675
|
let target = selected[i];
|
|
523
676
|
if (target !== undefined) {
|
|
524
677
|
let platform = projectName(target.projectDir);
|
|
525
|
-
let match = await discover(region, stack$1, platform);
|
|
526
|
-
let tables = match[0].toSorted(Primitive_string.compare);
|
|
527
|
-
let buckets = match[1].toSorted(Primitive_string.compare);
|
|
678
|
+
let match$2 = await discover(region, stack$1, platform);
|
|
679
|
+
let tables = match$2[0].toSorted(Primitive_string.compare);
|
|
680
|
+
let buckets = match$2[1].filter(b => !storeBucketNames.includes(b)).toSorted(Primitive_string.compare);
|
|
528
681
|
let tableCounts = [];
|
|
529
682
|
for (let j = 0, j_finish = tables.length; j < j_finish; ++j) {
|
|
530
683
|
let t = tables[j];
|
|
@@ -538,7 +691,18 @@ function run(stack, backend, targets, param) {
|
|
|
538
691
|
if (b !== undefined) {
|
|
539
692
|
bucketCounts.push([
|
|
540
693
|
b,
|
|
541
|
-
await countBucket(b)
|
|
694
|
+
await countBucket(b, undefined)
|
|
695
|
+
]);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
let stores$1 = selectedStores.filter(s => s.plugin === pluginOf(target));
|
|
699
|
+
let storeCounts = [];
|
|
700
|
+
for (let j$2 = 0, j_finish$2 = stores$1.length; j$2 < j_finish$2; ++j$2) {
|
|
701
|
+
let s = stores$1[j$2];
|
|
702
|
+
if (s !== undefined) {
|
|
703
|
+
storeCounts.push([
|
|
704
|
+
s,
|
|
705
|
+
await countBucket(s.bucketName, s.keyPrefix + `/`)
|
|
542
706
|
]);
|
|
543
707
|
}
|
|
544
708
|
}
|
|
@@ -547,7 +711,8 @@ function run(stack, backend, targets, param) {
|
|
|
547
711
|
platform: platform,
|
|
548
712
|
tables: tables,
|
|
549
713
|
tableCounts: tableCounts,
|
|
550
|
-
bucketCounts: bucketCounts
|
|
714
|
+
bucketCounts: bucketCounts,
|
|
715
|
+
storeCounts: storeCounts
|
|
551
716
|
});
|
|
552
717
|
}
|
|
553
718
|
}
|
|
@@ -576,37 +741,54 @@ function run(stack, backend, targets, param) {
|
|
|
576
741
|
let r = resolvedList[i$1];
|
|
577
742
|
if (r !== undefined) {
|
|
578
743
|
Seed_Runner$ReventlessSeed.heading(`Emptying ` + r.target.label + ` …`);
|
|
579
|
-
for (let j$
|
|
580
|
-
let t$1 = r.tables[j$
|
|
744
|
+
for (let j$3 = 0, j_finish$3 = r.tables.length; j$3 < j_finish$3; ++j$3) {
|
|
745
|
+
let t$1 = r.tables[j$3];
|
|
581
746
|
if (t$1 !== undefined) {
|
|
582
747
|
await truncateTable(t$1);
|
|
583
748
|
console.log(` truncated ` + t$1);
|
|
584
749
|
}
|
|
585
750
|
}
|
|
586
|
-
for (let j$
|
|
587
|
-
let match$
|
|
588
|
-
if (match$
|
|
589
|
-
let b$1 = match$
|
|
590
|
-
await emptyBucket(b$1);
|
|
751
|
+
for (let j$4 = 0, j_finish$4 = r.bucketCounts.length; j$4 < j_finish$4; ++j$4) {
|
|
752
|
+
let match$3 = r.bucketCounts[j$4];
|
|
753
|
+
if (match$3 !== undefined) {
|
|
754
|
+
let b$1 = match$3[0];
|
|
755
|
+
await emptyBucket(b$1, undefined);
|
|
591
756
|
console.log(` emptied ` + b$1);
|
|
592
757
|
}
|
|
593
758
|
}
|
|
759
|
+
for (let j$5 = 0, j_finish$5 = r.storeCounts.length; j$5 < j_finish$5; ++j$5) {
|
|
760
|
+
let match$4 = r.storeCounts[j$5];
|
|
761
|
+
if (match$4 !== undefined) {
|
|
762
|
+
let s$1 = match$4[0];
|
|
763
|
+
await emptyBucket(s$1.bucketName, s$1.keyPrefix + `/`);
|
|
764
|
+
console.log(` emptied ` + s$1.qualified + ` — ` + match$4[1].toString() + ` object(s) removed from ` + (s$1.bucketName + `/` + s$1.keyPrefix + `/`));
|
|
765
|
+
}
|
|
766
|
+
}
|
|
594
767
|
}
|
|
595
768
|
}
|
|
596
769
|
let remaining = 0;
|
|
597
770
|
for (let i$2 = 0, i_finish$2 = resolvedList.length; i$2 < i_finish$2; ++i$2) {
|
|
598
771
|
let r$1 = resolvedList[i$2];
|
|
599
772
|
if (r$1 !== undefined) {
|
|
600
|
-
for (let j$
|
|
601
|
-
let t$2 = r$1.tables[j$
|
|
773
|
+
for (let j$6 = 0, j_finish$6 = r$1.tables.length; j$6 < j_finish$6; ++j$6) {
|
|
774
|
+
let t$2 = r$1.tables[j$6];
|
|
602
775
|
if (t$2 !== undefined) {
|
|
603
776
|
remaining = remaining + await countTable(t$2) | 0;
|
|
604
777
|
}
|
|
605
778
|
}
|
|
606
|
-
for (let j$
|
|
607
|
-
let match$
|
|
608
|
-
if (match$
|
|
609
|
-
remaining = remaining + await countBucket(match$
|
|
779
|
+
for (let j$7 = 0, j_finish$7 = r$1.bucketCounts.length; j$7 < j_finish$7; ++j$7) {
|
|
780
|
+
let match$5 = r$1.bucketCounts[j$7];
|
|
781
|
+
if (match$5 !== undefined) {
|
|
782
|
+
remaining = remaining + await countBucket(match$5[0], undefined) | 0;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
for (let j$8 = 0, j_finish$8 = r$1.storeCounts.length; j$8 < j_finish$8; ++j$8) {
|
|
786
|
+
let match$6 = r$1.storeCounts[j$8];
|
|
787
|
+
if (match$6 !== undefined) {
|
|
788
|
+
let s$2 = match$6[0];
|
|
789
|
+
let left = await countBucket(s$2.bucketName, s$2.keyPrefix + `/`);
|
|
790
|
+
remaining = remaining + left | 0;
|
|
791
|
+
console.log(` verified empty: ` + s$2.qualified + ` — ` + left.toString() + ` object(s) remain under ` + (s$2.bucketName + `/` + s$2.keyPrefix + `/`));
|
|
610
792
|
}
|
|
611
793
|
}
|
|
612
794
|
}
|
|
@@ -623,17 +805,17 @@ function run(stack, backend, targets, param) {
|
|
|
623
805
|
process.exit(0);
|
|
624
806
|
return;
|
|
625
807
|
} catch (raw_message) {
|
|
626
|
-
let message = Primitive_exceptions.internalToException(raw_message);
|
|
627
|
-
if (message.RE_EXN_ID === Seed$ReventlessSeed.Failed) {
|
|
808
|
+
let message$1 = Primitive_exceptions.internalToException(raw_message);
|
|
809
|
+
if (message$1.RE_EXN_ID === Seed$ReventlessSeed.Failed) {
|
|
628
810
|
Seed_Prompt$ReventlessSeed.close();
|
|
629
811
|
console.error("");
|
|
630
|
-
console.error(`Reset aborted — ` + message._1);
|
|
812
|
+
console.error(`Reset aborted — ` + message$1._1);
|
|
631
813
|
process.exit(1);
|
|
632
814
|
} else {
|
|
633
815
|
Seed_Prompt$ReventlessSeed.close();
|
|
634
816
|
console.error("");
|
|
635
817
|
console.error("Reset aborted with an unexpected error:");
|
|
636
|
-
console.error(Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(message), Stdlib_JsExn.message), "unknown"));
|
|
818
|
+
console.error(Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(message$1), Stdlib_JsExn.message), "unknown"));
|
|
637
819
|
process.exit(1);
|
|
638
820
|
}
|
|
639
821
|
return;
|
|
@@ -662,11 +844,15 @@ export {
|
|
|
662
844
|
classify,
|
|
663
845
|
tagValue,
|
|
664
846
|
discover,
|
|
847
|
+
splitQualified,
|
|
848
|
+
parseObjectStores,
|
|
849
|
+
validateStores,
|
|
665
850
|
countTable,
|
|
666
851
|
countBucket,
|
|
667
852
|
sendBatch,
|
|
668
853
|
truncateTable,
|
|
669
854
|
emptyBucket,
|
|
855
|
+
pluginOf,
|
|
670
856
|
chooseScope,
|
|
671
857
|
gateTarget,
|
|
672
858
|
reportAll,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,25 +1,22 @@
|
|
|
1
|
-
// Which document `resolveEndpoints` reads, and
|
|
1
|
+
// Which document `resolveEndpoints` reads, and which GraphQL endpoint each arm takes
|
|
2
|
+
// from it.
|
|
2
3
|
//
|
|
3
4
|
// `endpointsFrom` is the pure half of `resolveEndpoints`; the impure half is a
|
|
4
|
-
// `pulumi stack output` subprocess and a `config.json` fetch, and the decision
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// shell publishes no `hostShellUrl` by construction, so it takes that arm, and
|
|
10
|
-
// the per-store endpoint is its only way to reach the store.
|
|
5
|
+
// `pulumi stack output` subprocess and a `config.json` fetch, and the decision these
|
|
6
|
+
// tests pin is in neither. Upload endpoints are no longer resolved here — under route B
|
|
7
|
+
// the seed mints through the domain API's `Upload_Presign` mutation on the GraphQL
|
|
8
|
+
// endpoint below — so the only decision left is which GraphQL endpoint each source
|
|
9
|
+
// publishes, and the merged-over-per-plugin precedence within the stack-outputs arm.
|
|
11
10
|
|
|
12
11
|
open JestGlobals
|
|
13
12
|
open ReventlessSeed
|
|
14
13
|
|
|
15
|
-
|
|
16
14
|
// Every value read here has an env override, and an ambient one in the runner's
|
|
17
|
-
// environment (AWS_REGION is routinely set) would silently stand in for the
|
|
18
|
-
//
|
|
15
|
+
// environment (AWS_REGION is routinely set) would silently stand in for the document
|
|
16
|
+
// under test. An empty string reads as unset.
|
|
19
17
|
let clearOverrides = () =>
|
|
20
18
|
[
|
|
21
19
|
"REVENTLESS_GRAPHQL_ENDPOINT",
|
|
22
|
-
"REVENTLESS_UPLOAD_ENDPOINT",
|
|
23
20
|
"AWS_REGION",
|
|
24
21
|
"COGNITO_CLIENT_ID",
|
|
25
22
|
"SEED_SKIP_UPLOADS",
|
|
@@ -28,9 +25,6 @@ let clearOverrides = () =>
|
|
|
28
25
|
let obj = entries => JSON.Encode.object(entries->Dict.fromArray)
|
|
29
26
|
let str = JSON.Encode.string
|
|
30
27
|
|
|
31
|
-
// A host shell's config.json and the stack outputs of a platform that serves no
|
|
32
|
-
// shell — the two documents the two arms read, minus the upload keys each test
|
|
33
|
-
// supplies.
|
|
34
28
|
let config = (~extra: array<(string, JSON.t)>) =>
|
|
35
29
|
obj(
|
|
36
30
|
Array.concat(
|
|
@@ -55,84 +49,18 @@ let outputs = (~extra: array<(string, JSON.t)>) =>
|
|
|
55
49
|
),
|
|
56
50
|
)
|
|
57
51
|
|
|
58
|
-
let storeEndpoints = obj([("Catalog.productImages", str("https://presign.example/"))])
|
|
59
|
-
|
|
60
52
|
describe("endpointsFrom — host shell arm", () => {
|
|
61
53
|
beforeEach(clearOverrides)
|
|
62
54
|
|
|
63
|
-
testSync("reads the
|
|
64
|
-
let eps = ReventlessSeedAws.endpointsFrom(
|
|
65
|
-
|
|
66
|
-
HostShellConfig(
|
|
67
|
-
config(
|
|
68
|
-
~extra=[
|
|
69
|
-
("uploadEndpoint", str("https://legacy.example/")),
|
|
70
|
-
("uploadEndpoints", storeEndpoints),
|
|
71
|
-
],
|
|
72
|
-
),
|
|
73
|
-
),
|
|
74
|
-
)
|
|
75
|
-
expect(eps.uploadEndpoint)->toBe("https://legacy.example/")
|
|
76
|
-
expect(eps.uploadEndpoints)->toEqual(
|
|
77
|
-
Dict.fromArray([("Catalog.productImages", "https://presign.example/")]),
|
|
78
|
-
)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
// A shell fronting only declared stores writes no singular key. That used to
|
|
82
|
-
// fail the whole run before a command was sent ("deployment is missing
|
|
83
|
-
// uploadEndpoint"), which is a harder failure than the one this plan fixes.
|
|
84
|
-
testSync("a shell publishing only per-store endpoints resolves an empty legacy service", () => {
|
|
85
|
-
let eps = ReventlessSeedAws.endpointsFrom(
|
|
86
|
-
~stack="alpha",
|
|
87
|
-
HostShellConfig(config(~extra=[("uploadEndpoints", storeEndpoints)])),
|
|
88
|
-
)
|
|
89
|
-
expect(eps.uploadEndpoint)->toBe("")
|
|
90
|
-
expect(eps.uploadEndpoints->Dict.keysToArray)->toEqual(["Catalog.productImages"])
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
testSync("REVENTLESS_UPLOAD_ENDPOINT overrides the legacy service and leaves the map alone", () => {
|
|
94
|
-
NodeProcess.env->Dict.set("REVENTLESS_UPLOAD_ENDPOINT", "https://override.example/")
|
|
95
|
-
let eps = ReventlessSeedAws.endpointsFrom(
|
|
96
|
-
~stack="alpha",
|
|
97
|
-
HostShellConfig(
|
|
98
|
-
config(
|
|
99
|
-
~extra=[
|
|
100
|
-
("uploadEndpoint", str("https://legacy.example/")),
|
|
101
|
-
("uploadEndpoints", storeEndpoints),
|
|
102
|
-
],
|
|
103
|
-
),
|
|
104
|
-
),
|
|
105
|
-
)
|
|
106
|
-
expect(eps.uploadEndpoint)->toBe("https://override.example/")
|
|
107
|
-
expect(eps.uploadEndpoints->Dict.get("Catalog.productImages"))->toEqual(
|
|
108
|
-
Some("https://presign.example/"),
|
|
109
|
-
)
|
|
55
|
+
testSync("reads the GraphQL endpoint from the shell config", () => {
|
|
56
|
+
let eps = ReventlessSeedAws.endpointsFrom(~stack="alpha", HostShellConfig(config(~extra=[])))
|
|
57
|
+
expect(eps.graphql)->toBe("https://api.example/graphql")
|
|
110
58
|
})
|
|
111
59
|
})
|
|
112
60
|
|
|
113
61
|
describe("endpointsFrom — stack outputs arm", () => {
|
|
114
62
|
beforeEach(clearOverrides)
|
|
115
63
|
|
|
116
|
-
// The lookup that was missing entirely: this arm had no upload source but the
|
|
117
|
-
// env var, so a `PlatformOwned` deployment resolved "" whatever it published.
|
|
118
|
-
testSync("reads the uploadEndpoints stack output", () => {
|
|
119
|
-
let eps = ReventlessSeedAws.endpointsFrom(
|
|
120
|
-
~stack="pr-verify",
|
|
121
|
-
StackOutputs(outputs(~extra=[("uploadEndpoints", storeEndpoints)])),
|
|
122
|
-
)
|
|
123
|
-
expect(eps.uploadEndpoints)->toEqual(
|
|
124
|
-
Dict.fromArray([("Catalog.productImages", "https://presign.example/")]),
|
|
125
|
-
)
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
// Declaring no store is a legitimate deployment, not a broken one — it stays a
|
|
129
|
-
// no-op at the data set rather than failing the run.
|
|
130
|
-
testSync("a deployment declaring no store resolves an empty map without failing", () => {
|
|
131
|
-
let eps = ReventlessSeedAws.endpointsFrom(~stack="pr-verify", StackOutputs(outputs(~extra=[])))
|
|
132
|
-
expect(eps.uploadEndpoint)->toBe("")
|
|
133
|
-
expect(eps.uploadEndpoints->Dict.keysToArray)->toEqual([])
|
|
134
|
-
})
|
|
135
|
-
|
|
136
64
|
testSync("prefers the merged API endpoint over the per-plugin one", () => {
|
|
137
65
|
let eps = ReventlessSeedAws.endpointsFrom(
|
|
138
66
|
~stack="pr-verify",
|
|
@@ -153,28 +81,3 @@ describe("endpointsFrom — stack outputs arm", () => {
|
|
|
153
81
|
}
|
|
154
82
|
)
|
|
155
83
|
})
|
|
156
|
-
|
|
157
|
-
describe("endpointsFrom — malformed uploadEndpoints", () => {
|
|
158
|
-
beforeEach(clearOverrides)
|
|
159
|
-
|
|
160
|
-
// One bad member should not cost the run every endpoint it could have used.
|
|
161
|
-
testSync("drops a non-string member rather than the whole map", () => {
|
|
162
|
-
let eps = ReventlessSeedAws.endpointsFrom(
|
|
163
|
-
~stack="pr-verify",
|
|
164
|
-
StackOutputs(
|
|
165
|
-
outputs(
|
|
166
|
-
~extra=[
|
|
167
|
-
(
|
|
168
|
-
"uploadEndpoints",
|
|
169
|
-
obj([
|
|
170
|
-
("Catalog.productImages", str("https://presign.example/")),
|
|
171
|
-
("Catalog.broken", JSON.Encode.int(7)),
|
|
172
|
-
]),
|
|
173
|
-
),
|
|
174
|
-
],
|
|
175
|
-
),
|
|
176
|
-
),
|
|
177
|
-
)
|
|
178
|
-
expect(eps.uploadEndpoints->Dict.keysToArray)->toEqual(["Catalog.productImages"])
|
|
179
|
-
})
|
|
180
|
-
})
|
|
@@ -8,7 +8,6 @@ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_excep
|
|
|
8
8
|
function clearOverrides() {
|
|
9
9
|
[
|
|
10
10
|
"REVENTLESS_GRAPHQL_ENDPOINT",
|
|
11
|
-
"REVENTLESS_UPLOAD_ENDPOINT",
|
|
12
11
|
"AWS_REGION",
|
|
13
12
|
"COGNITO_CLIENT_ID",
|
|
14
13
|
"SEED_SKIP_UPLOADS"
|
|
@@ -59,87 +58,19 @@ function outputs(extra) {
|
|
|
59
58
|
].concat(extra));
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
let storeEndpoints = Object.fromEntries([[
|
|
63
|
-
"Catalog.productImages",
|
|
64
|
-
"https://presign.example/"
|
|
65
|
-
]]);
|
|
66
|
-
|
|
67
61
|
globalThis.describe("endpointsFrom — host shell arm", () => {
|
|
68
62
|
globalThis.beforeEach(clearOverrides);
|
|
69
|
-
globalThis.test("reads the
|
|
70
|
-
let eps = ReventlessSeedAws.endpointsFrom("alpha", {
|
|
71
|
-
TAG: "HostShellConfig",
|
|
72
|
-
_0: config([
|
|
73
|
-
[
|
|
74
|
-
"uploadEndpoint",
|
|
75
|
-
"https://legacy.example/"
|
|
76
|
-
],
|
|
77
|
-
[
|
|
78
|
-
"uploadEndpoints",
|
|
79
|
-
storeEndpoints
|
|
80
|
-
]
|
|
81
|
-
])
|
|
82
|
-
});
|
|
83
|
-
globalThis.expect(eps.uploadEndpoint).toBe("https://legacy.example/");
|
|
84
|
-
globalThis.expect(eps.uploadEndpoints).toEqual(Object.fromEntries([[
|
|
85
|
-
"Catalog.productImages",
|
|
86
|
-
"https://presign.example/"
|
|
87
|
-
]]));
|
|
88
|
-
});
|
|
89
|
-
globalThis.test("a shell publishing only per-store endpoints resolves an empty legacy service", () => {
|
|
63
|
+
globalThis.test("reads the GraphQL endpoint from the shell config", () => {
|
|
90
64
|
let eps = ReventlessSeedAws.endpointsFrom("alpha", {
|
|
91
65
|
TAG: "HostShellConfig",
|
|
92
|
-
_0: config([
|
|
93
|
-
"uploadEndpoints",
|
|
94
|
-
storeEndpoints
|
|
95
|
-
]])
|
|
66
|
+
_0: config([])
|
|
96
67
|
});
|
|
97
|
-
globalThis.expect(eps.
|
|
98
|
-
globalThis.expect(Object.keys(eps.uploadEndpoints)).toEqual(["Catalog.productImages"]);
|
|
99
|
-
});
|
|
100
|
-
globalThis.test("REVENTLESS_UPLOAD_ENDPOINT overrides the legacy service and leaves the map alone", () => {
|
|
101
|
-
process.env["REVENTLESS_UPLOAD_ENDPOINT"] = "https://override.example/";
|
|
102
|
-
let eps = ReventlessSeedAws.endpointsFrom("alpha", {
|
|
103
|
-
TAG: "HostShellConfig",
|
|
104
|
-
_0: config([
|
|
105
|
-
[
|
|
106
|
-
"uploadEndpoint",
|
|
107
|
-
"https://legacy.example/"
|
|
108
|
-
],
|
|
109
|
-
[
|
|
110
|
-
"uploadEndpoints",
|
|
111
|
-
storeEndpoints
|
|
112
|
-
]
|
|
113
|
-
])
|
|
114
|
-
});
|
|
115
|
-
globalThis.expect(eps.uploadEndpoint).toBe("https://override.example/");
|
|
116
|
-
globalThis.expect(eps.uploadEndpoints["Catalog.productImages"]).toEqual("https://presign.example/");
|
|
68
|
+
globalThis.expect(eps.graphql).toBe("https://api.example/graphql");
|
|
117
69
|
});
|
|
118
70
|
});
|
|
119
71
|
|
|
120
72
|
globalThis.describe("endpointsFrom — stack outputs arm", () => {
|
|
121
73
|
globalThis.beforeEach(clearOverrides);
|
|
122
|
-
globalThis.test("reads the uploadEndpoints stack output", () => {
|
|
123
|
-
let eps = ReventlessSeedAws.endpointsFrom("pr-verify", {
|
|
124
|
-
TAG: "StackOutputs",
|
|
125
|
-
_0: outputs([[
|
|
126
|
-
"uploadEndpoints",
|
|
127
|
-
storeEndpoints
|
|
128
|
-
]])
|
|
129
|
-
});
|
|
130
|
-
globalThis.expect(eps.uploadEndpoints).toEqual(Object.fromEntries([[
|
|
131
|
-
"Catalog.productImages",
|
|
132
|
-
"https://presign.example/"
|
|
133
|
-
]]));
|
|
134
|
-
});
|
|
135
|
-
globalThis.test("a deployment declaring no store resolves an empty map without failing", () => {
|
|
136
|
-
let eps = ReventlessSeedAws.endpointsFrom("pr-verify", {
|
|
137
|
-
TAG: "StackOutputs",
|
|
138
|
-
_0: outputs([])
|
|
139
|
-
});
|
|
140
|
-
globalThis.expect(eps.uploadEndpoint).toBe("");
|
|
141
|
-
globalThis.expect(Object.keys(eps.uploadEndpoints)).toEqual([]);
|
|
142
|
-
});
|
|
143
74
|
globalThis.test("prefers the merged API endpoint over the per-plugin one", () => {
|
|
144
75
|
let eps = ReventlessSeedAws.endpointsFrom("pr-verify", {
|
|
145
76
|
TAG: "StackOutputs",
|
|
@@ -174,35 +105,11 @@ globalThis.describe("endpointsFrom — stack outputs arm", () => {
|
|
|
174
105
|
});
|
|
175
106
|
});
|
|
176
107
|
|
|
177
|
-
globalThis.describe("endpointsFrom — malformed uploadEndpoints", () => {
|
|
178
|
-
globalThis.beforeEach(clearOverrides);
|
|
179
|
-
globalThis.test("drops a non-string member rather than the whole map", () => {
|
|
180
|
-
let eps = ReventlessSeedAws.endpointsFrom("pr-verify", {
|
|
181
|
-
TAG: "StackOutputs",
|
|
182
|
-
_0: outputs([[
|
|
183
|
-
"uploadEndpoints",
|
|
184
|
-
Object.fromEntries([
|
|
185
|
-
[
|
|
186
|
-
"Catalog.productImages",
|
|
187
|
-
"https://presign.example/"
|
|
188
|
-
],
|
|
189
|
-
[
|
|
190
|
-
"Catalog.broken",
|
|
191
|
-
7
|
|
192
|
-
]
|
|
193
|
-
])
|
|
194
|
-
]])
|
|
195
|
-
});
|
|
196
|
-
globalThis.expect(Object.keys(eps.uploadEndpoints)).toEqual(["Catalog.productImages"]);
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
|
|
200
108
|
export {
|
|
201
109
|
clearOverrides,
|
|
202
110
|
obj,
|
|
203
111
|
str,
|
|
204
112
|
config,
|
|
205
113
|
outputs,
|
|
206
|
-
storeEndpoints,
|
|
207
114
|
}
|
|
208
|
-
/*
|
|
115
|
+
/* Not a pure module */
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|