@reventlessdev/reventless-seed-aws 1.0.0-alpha.3 → 1.0.0-alpha.5

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 (46) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/lib/bs/.compiler.log +2 -2
  3. package/lib/bs/.sourcedirs.json +1 -0
  4. package/lib/bs/compiler-info.json +2 -2
  5. package/lib/bs/src/ReventlessSeedAws.ast +0 -0
  6. package/lib/bs/src/ReventlessSeedAws.cmi +0 -0
  7. package/lib/bs/src/ReventlessSeedAws.cmj +0 -0
  8. package/lib/bs/src/ReventlessSeedAws.cmt +0 -0
  9. package/lib/bs/src/ReventlessSeedAws.res +125 -68
  10. package/lib/bs/src/ReventlessSeedAws.res.mjs +63 -30
  11. package/lib/bs/src/ReventlessSeedAws_Reset.ast +0 -0
  12. package/lib/bs/src/ReventlessSeedAws_Reset.cmi +0 -0
  13. package/lib/bs/src/ReventlessSeedAws_Reset.cmj +0 -0
  14. package/lib/bs/src/ReventlessSeedAws_Reset.cmt +0 -0
  15. package/lib/bs/src/ReventlessSeedAws_Reset.res +700 -0
  16. package/lib/bs/src/ReventlessSeedAws_Reset.res.mjs +675 -0
  17. package/lib/bs/tests/EndpointResolutionTest.ast +0 -0
  18. package/lib/bs/tests/EndpointResolutionTest.cmi +0 -0
  19. package/lib/bs/tests/EndpointResolutionTest.cmj +0 -0
  20. package/lib/bs/tests/EndpointResolutionTest.cmt +0 -0
  21. package/lib/bs/tests/EndpointResolutionTest.res +181 -0
  22. package/lib/bs/tests/EndpointResolutionTest.res.mjs +208 -0
  23. package/lib/ocaml/.compiler.log +2 -2
  24. package/lib/ocaml/EndpointResolutionTest.ast +0 -0
  25. package/lib/ocaml/EndpointResolutionTest.cmi +0 -0
  26. package/lib/ocaml/EndpointResolutionTest.cmj +0 -0
  27. package/lib/ocaml/EndpointResolutionTest.cmt +0 -0
  28. package/lib/ocaml/EndpointResolutionTest.res +181 -0
  29. package/lib/ocaml/ReventlessSeedAws.ast +0 -0
  30. package/lib/ocaml/ReventlessSeedAws.cmi +0 -0
  31. package/lib/ocaml/ReventlessSeedAws.cmj +0 -0
  32. package/lib/ocaml/ReventlessSeedAws.cmt +0 -0
  33. package/lib/ocaml/ReventlessSeedAws.res +125 -68
  34. package/lib/ocaml/ReventlessSeedAws_Reset.ast +0 -0
  35. package/lib/ocaml/ReventlessSeedAws_Reset.cmi +0 -0
  36. package/lib/ocaml/ReventlessSeedAws_Reset.cmj +0 -0
  37. package/lib/ocaml/ReventlessSeedAws_Reset.cmt +0 -0
  38. package/lib/ocaml/ReventlessSeedAws_Reset.res +700 -0
  39. package/package.json +6 -4
  40. package/rescript.json +8 -1
  41. package/src/ReventlessSeedAws.res +125 -68
  42. package/src/ReventlessSeedAws.res.mjs +63 -30
  43. package/src/ReventlessSeedAws_Reset.res +700 -0
  44. package/src/ReventlessSeedAws_Reset.res.mjs +675 -0
  45. package/tests/EndpointResolutionTest.res +181 -0
  46. package/tests/EndpointResolutionTest.res.mjs +208 -0
@@ -41,6 +41,18 @@ let asString = (json: JSON.t): option<string> =>
41
41
  | _ => None
42
42
  }
43
43
 
44
+ // Non-string members are dropped rather than failing the whole map: one
45
+ // malformed entry should not cost a run every endpoint it could have used.
46
+ let asStringDict = (json: JSON.t): dict<string> =>
47
+ switch json {
48
+ | Object(obj) =>
49
+ obj
50
+ ->Dict.toArray
51
+ ->Array.filterMap(((k, v)) => v->asString->Option.map(s => (k, s)))
52
+ ->Dict.fromArray
53
+ | _ => Dict.make()
54
+ }
55
+
44
56
  // ── Stack discovery ───────────────────────────────────────────────────────────
45
57
 
46
58
  // Which Pulumi backend the `pulumi` subprocess reads from. When `backend` is
@@ -147,70 +159,118 @@ let resolveField = (~envKey: string, ~fromSource: option<string>, ~human: string
147
159
  }
148
160
  }
149
161
 
150
- // Returns (graphqlEndpoint, uploadEndpoint, cognitoRegion, cognitoClientId).
151
- // `uploadEndpoint` is "" when the deployment publishes none the data set's
152
- // upload phase no-ops on empty.
153
- let resolveEndpoints = async (~projectDir: string, ~backend: option<string>, ~stack: string): (
154
- string,
155
- string,
156
- string,
157
- string,
158
- ) => {
159
- let outputs = stackOutputs(~projectDir, ~backend, stack)
160
- switch outputs->field("hostShellUrl")->Option.flatMap(asString) {
161
- | Some(hostShellUrl) =>
162
- let cfg = await fetchConfig(hostShellUrl)
162
+ // Same precedence, for a value whose absence is a legitimate answer rather than
163
+ // a broken deployment: a stack that declares no store publishes no upload
164
+ // endpoint, and that must stay a no-op at the data set instead of failing the
165
+ // run before a single command is sent.
166
+ let optionalField = (~envKey: string, ~fromSource: option<string>): string =>
167
+ switch Seed.Prompt.envValue(envKey) {
168
+ | Some(v) => v
169
+ | None => fromSource->Option.getOr("")
170
+ }
171
+
172
+ // Which document publishes the deployment's endpoints. A stack that serves a
173
+ // host shell publishes them in the shell's `config.json` under the client's key
174
+ // names; one that does not publishes them as stack outputs under Pulumi's. The
175
+ // two arms name different keys for the same values, which is why this is a
176
+ // variant and not a merged lookup.
177
+ type source = HostShellConfig(JSON.t) | StackOutputs(JSON.t)
178
+
179
+ type endpoints = {
180
+ graphql: string,
181
+ // The legacy single presign service; "" when the deployment publishes none.
182
+ uploadEndpoint: string,
183
+ // Qualified `{plugin}.{store}` → that store's presign endpoint. Empty when the
184
+ // deployment declares no store.
185
+ uploadEndpoints: dict<string>,
186
+ cognitoRegion: string,
187
+ cognitoClientId: string,
188
+ }
189
+
190
+ /**
191
+ * The endpoints a source document publishes — pure, so the branch selection and
192
+ * every key it reads can be exercised with synthetic documents.
193
+ *
194
+ * `uploadEndpoints` is read on **both** arms. It was read on neither: the
195
+ * host-shell arm looked only at the singular `uploadEndpoint`, and the
196
+ * stack-output arm had no upload lookup at all, so a platform that publishes its
197
+ * stores and no host shell resolved "" and reported itself as serving no
198
+ * uploads.
199
+ *
200
+ * `REVENTLESS_UPLOAD_ENDPOINT` still overrides the singular endpoint and does
201
+ * not touch the map: one string cannot express a map, so it could only ever
202
+ * override one store and would need a naming convention to say which.
203
+ */
204
+ let endpointsFrom = (~stack: string, source: source): endpoints =>
205
+ switch source {
206
+ | HostShellConfig(cfg) =>
163
207
  let fromCfg = key => cfg->field(key)->Option.flatMap(asString)
164
- let endpoint = resolveField(
165
- ~envKey="REVENTLESS_GRAPHQL_ENDPOINT",
166
- ~fromSource=fromCfg("apiEndpoint"),
167
- ~human="apiEndpoint",
168
- )
169
- let uploadEndpoint = resolveField(
170
- ~envKey="REVENTLESS_UPLOAD_ENDPOINT",
171
- ~fromSource=fromCfg("uploadEndpoint"),
172
- ~human="uploadEndpoint",
173
- )
174
- let region = resolveField(
175
- ~envKey="AWS_REGION",
176
- ~fromSource=fromCfg("region"),
177
- ~human="region",
178
- )
179
- let clientId = resolveField(
180
- ~envKey="COGNITO_CLIENT_ID",
181
- ~fromSource=fromCfg("cognitoClientId"),
182
- ~human="cognitoClientId",
183
- )
184
- (endpoint, uploadEndpoint, region, clientId)
185
- | None =>
208
+ {
209
+ graphql: resolveField(
210
+ ~envKey="REVENTLESS_GRAPHQL_ENDPOINT",
211
+ ~fromSource=fromCfg("apiEndpoint"),
212
+ ~human="apiEndpoint",
213
+ ),
214
+ uploadEndpoint: optionalField(
215
+ ~envKey="REVENTLESS_UPLOAD_ENDPOINT",
216
+ ~fromSource=fromCfg("uploadEndpoint"),
217
+ ),
218
+ uploadEndpoints: cfg->field("uploadEndpoints")->Option.mapOr(Dict.make(), asStringDict),
219
+ cognitoRegion: resolveField(
220
+ ~envKey="AWS_REGION",
221
+ ~fromSource=fromCfg("region"),
222
+ ~human="region",
223
+ ),
224
+ cognitoClientId: resolveField(
225
+ ~envKey="COGNITO_CLIENT_ID",
226
+ ~fromSource=fromCfg("cognitoClientId"),
227
+ ~human="cognitoClientId",
228
+ ),
229
+ }
230
+ | StackOutputs(outputs) =>
186
231
  let out = key => outputs->field(key)->Option.flatMap(asString)
187
- let endpoint = switch (
188
- Seed.Prompt.envValue("REVENTLESS_GRAPHQL_ENDPOINT"),
189
- out("domainMergedApiEndpoint"),
190
- out("domainApiEndpoint"),
191
- ) {
192
- | (Some(v), _, _) | (_, Some(v), _) | (_, _, Some(v)) => v
193
- | _ =>
194
- throw(
195
- Seed.Failed(
196
- `stack "${stack}" exports neither domainMergedApiEndpoint nor domainApiEndpoint`,
197
- ),
198
- )
232
+ {
233
+ graphql: switch (
234
+ Seed.Prompt.envValue("REVENTLESS_GRAPHQL_ENDPOINT"),
235
+ out("domainMergedApiEndpoint"),
236
+ out("domainApiEndpoint"),
237
+ ) {
238
+ | (Some(v), _, _) | (_, Some(v), _) | (_, _, Some(v)) => v
239
+ | _ =>
240
+ throw(
241
+ Seed.Failed(
242
+ `stack "${stack}" exports neither domainMergedApiEndpoint nor domainApiEndpoint`,
243
+ ),
244
+ )
245
+ },
246
+ // No stack output carries the legacy single service — it exists only in a
247
+ // host shell's config.json — so here the env override is its only source.
248
+ uploadEndpoint: optionalField(~envKey="REVENTLESS_UPLOAD_ENDPOINT", ~fromSource=None),
249
+ uploadEndpoints: outputs->field("uploadEndpoints")->Option.mapOr(Dict.make(), asStringDict),
250
+ cognitoRegion: resolveField(
251
+ ~envKey="AWS_REGION",
252
+ ~fromSource=out("cognitoRegion"),
253
+ ~human="cognitoRegion",
254
+ ),
255
+ cognitoClientId: resolveField(
256
+ ~envKey="COGNITO_CLIENT_ID",
257
+ ~fromSource=out("cognitoUserPoolClientId"),
258
+ ~human="cognitoUserPoolClientId",
259
+ ),
199
260
  }
200
- let region = resolveField(
201
- ~envKey="AWS_REGION",
202
- ~fromSource=out("cognitoRegion"),
203
- ~human="cognitoRegion",
204
- )
205
- let clientId = resolveField(
206
- ~envKey="COGNITO_CLIENT_ID",
207
- ~fromSource=out("cognitoUserPoolClientId"),
208
- ~human="cognitoUserPoolClientId",
209
- )
210
- // Absent → "" → the data set skips its upload phase.
211
- let uploadEndpoint = Seed.Prompt.envValue("REVENTLESS_UPLOAD_ENDPOINT")->Option.getOr("")
212
- (endpoint, uploadEndpoint, region, clientId)
213
261
  }
262
+
263
+ let resolveEndpoints = async (
264
+ ~projectDir: string,
265
+ ~backend: option<string>,
266
+ ~stack: string,
267
+ ): endpoints => {
268
+ let outputs = stackOutputs(~projectDir, ~backend, stack)
269
+ let source = switch outputs->field("hostShellUrl")->Option.flatMap(asString) {
270
+ | Some(hostShellUrl) => HostShellConfig(await fetchConfig(hostShellUrl))
271
+ | None => StackOutputs(outputs)
272
+ }
273
+ endpointsFrom(~stack, source)
214
274
  }
215
275
 
216
276
  // ── Cognito login ─────────────────────────────────────────────────────────────
@@ -302,15 +362,12 @@ let connect = (~projectDir: string=".", ~stack=?, ~backend=?, ()): (
302
362
  | None => backend
303
363
  }
304
364
  let stackName = await resolveStack(~projectDir, ~backend, ~stack)
305
- let (endpoint, uploadEndpoint, region, clientId) = await resolveEndpoints(
306
- ~projectDir,
307
- ~backend,
308
- ~stack=stackName,
309
- )
365
+ let eps = await resolveEndpoints(~projectDir, ~backend, ~stack=stackName)
310
366
  await Seed.Connect.make(
311
367
  ~label=stackName,
312
- ~endpoint,
313
- ~uploadEndpoint,
314
- ~login=cognito(~region, ~clientId),
368
+ ~endpoint=eps.graphql,
369
+ ~uploadEndpoint=eps.uploadEndpoint,
370
+ ~uploadEndpoints=eps.uploadEndpoints,
371
+ ~login=cognito(~region=eps.cognitoRegion, ~clientId=eps.cognitoClientId),
315
372
  )
316
373
  }