@reventlessdev/reventless-aws 3.0.0-alpha.394 → 3.0.0-alpha.396

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 (43) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/package.json +8 -8
  3. package/scripts/DeployApp.res +7 -1
  4. package/scripts/DeployApp.res.mjs +1 -1
  5. package/scripts/ProvisionAccounts.res +89 -4
  6. package/scripts/ProvisionAccounts.res.mjs +101 -5
  7. package/scripts/ProvisionIdentity.res +42 -18
  8. package/scripts/ProvisionIdentity.res.mjs +40 -26
  9. package/src/Platform.res +47 -10
  10. package/src/Platform.res.mjs +44 -10
  11. package/src/Platform_Stack.res +17 -0
  12. package/src/Platform_Stack.res.mjs +7 -1
  13. package/src/adapter/Auth/Auth_ActiveRoleTrigger.res +16 -7
  14. package/src/adapter/Auth/Auth_ActiveRoleTrigger.res.mjs +16 -3
  15. package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res +59 -9
  16. package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res.mjs +53 -6
  17. package/src/adapter/Auth/Auth_Cognito.res +13 -2
  18. package/src/adapter/Auth/Auth_Cognito.res.mjs +9 -2
  19. package/src/adapter/Auth/Auth_IdentityStore.res +52 -0
  20. package/src/adapter/Auth/Auth_IdentityStore.res.mjs +46 -0
  21. package/src/adapter/Auth/Auth_IdentityStore_Schema.res +39 -0
  22. package/src/adapter/Auth/Auth_IdentityStore_Schema.res.mjs +35 -0
  23. package/src/adapter/Identity/IdentityProvider_Cognito_Backend.res +251 -0
  24. package/src/adapter/Identity/IdentityProvider_Cognito_Backend.res.mjs +300 -0
  25. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +7 -4
  26. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +2 -2
  27. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +80 -0
  28. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +53 -2
  29. package/src/components/AutomationSlice_Builder.res.mjs +1 -1
  30. package/src/components/OutboundTranslationSlice_Builder.res +2 -0
  31. package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
  32. package/src/plugin/runtime/PluginRuntime_Builder.res +23 -0
  33. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +29 -0
  34. package/tests/Auth_ActiveRoleTrigger_OpsTest.res +40 -0
  35. package/tests/Auth_ActiveRoleTrigger_OpsTest.res.mjs +59 -8
  36. package/tests/Auth_CognitoTest.res +62 -0
  37. package/tests/Auth_CognitoTest.res.mjs +98 -0
  38. package/tests/Auth_IdentityStore_SchemaTest.res +35 -0
  39. package/tests/Auth_IdentityStore_SchemaTest.res.mjs +43 -0
  40. package/tests/IdentityProvider_Cognito_BackendTest.res +314 -0
  41. package/tests/IdentityProvider_Cognito_BackendTest.res.mjs +442 -0
  42. package/tests/ProvisionAccountsTest.res.mjs +1 -1
  43. package/tests/Util_ShellConfigTest.res.mjs +1 -22
package/CHANGELOG.md CHANGED
@@ -3,6 +3,28 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 3.0.0-alpha.396 (2026-10-23)
7
+
8
+ ### Features
9
+
10
+ * **aws:** a new account gets a domain user id, carried as a token claim from a pool-scoped identity store ([ccfc90c](https://github.com/ReventlessDev/reventless-core/commit/ccfc90ced0c211cab23843864a2b4e580001b3ae))
11
+ * **aws:** a slice declaring the identity provider is handed one over the platform's Cognito pool ([b3a933b](https://github.com/ReventlessDev/reventless-core/commit/b3a933bbda475d0ce932a8de30616f738d639417))
12
+
13
+
14
+ # 3.0.0-alpha.395 (2026-10-22)
15
+
16
+ * feat(aws)!: the host shell's config.json names the identity provider under the neutral keys only ([6ce24ba](https://github.com/ReventlessDev/reventless-core/commit/6ce24ba7dde27da02f76f7f9f6284ad09a48f15a))
17
+ ### Features
18
+
19
+ * **local:** the local platform provisions an identity provider over the accounts file ([1a46867](https://github.com/ReventlessDev/reventless-core/commit/1a46867ac391b3b4a2d6973606d782547190dcb8))
20
+
21
+ ### BREAKING CHANGES
22
+
23
+ * a reader of the host shell's config.json that only knows cognitoClientId
24
+ or cognitoUserPoolId finds neither; read identityProviderClientId and identityProviderId.
25
+
26
+
27
+
6
28
  # 3.0.0-alpha.394 (2026-10-22)
7
29
 
8
30
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-aws",
3
- "version": "3.0.0-alpha.394",
3
+ "version": "3.0.0-alpha.396",
4
4
  "description": "AWS adapters for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "bin": {
@@ -22,19 +22,19 @@
22
22
  "sury": "11.0.0",
23
23
  "uuid": "^13.0.0",
24
24
  "yaml": "^2.8.3",
25
- "@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.33",
25
+ "@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.34",
26
+ "@reventlessdev/rescript-effect": "1.0.0-alpha.1",
26
27
  "@reventlessdev/rescript-jest": "1.0.0-alpha.13",
28
+ "@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.32",
27
29
  "@reventlessdev/rescript-node": "2.0.0-alpha.23",
28
- "@reventlessdev/rescript-effect": "1.0.0-alpha.1",
29
- "@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.31",
30
30
  "@reventlessdev/rescript-pulumi-pulumi": "3.0.0-alpha.0",
31
31
  "@reventlessdev/rescript-uuid": "2.0.0-alpha.1",
32
32
  "@reventlessdev/rescript-web": "1.0.0-alpha.8",
33
- "@reventlessdev/reventless-infra": "3.0.0-alpha.202",
34
- "@reventlessdev/reventless-core": "3.0.0-alpha.305",
35
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.169",
33
+ "@reventlessdev/reventless-core": "3.0.0-alpha.307",
34
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.204",
36
35
  "@reventlessdev/reventless-interop": "3.0.0-alpha.42",
37
- "@reventlessdev/reventless-spec": "3.0.0-alpha.174"
36
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.171",
37
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.175"
38
38
  },
39
39
  "devDependencies": {
40
40
  "rescript": "12.3.1",
@@ -587,7 +587,13 @@ let up = async (~manifest: DeployManifest.resolved, ~stack: string): result<unit
587
587
  Error("the platform stack exports no identityProviderId to create accounts in")
588
588
  | (Some(file), Some(providerId)) =>
589
589
  Console.log("\n── Creating the accounts ──\n")
590
- switch await ProvisionAccounts.provision(~file, ~providerId) {
590
+ // The stack just deployed is the one that exports the store
591
+ // a new account's domain id is written to.
592
+ switch await ProvisionAccounts.provision(
593
+ ~file,
594
+ ~providerId,
595
+ ~identityStore=?platformOutputs->outputString("identityStore"),
596
+ ) {
591
597
  | Error(_) as e => e
592
598
  | Ok() => Ok(Some(file))
593
599
  }
@@ -728,7 +728,7 @@ async function up(manifest, stack) {
728
728
  if (match$2 !== undefined) {
729
729
  if (match$3 !== undefined) {
730
730
  console.log("\n── Creating the accounts ──\n");
731
- let e$6 = await ProvisionAccounts$ReventlessAws.provision(match$2, match$3, undefined);
731
+ let e$6 = await ProvisionAccounts$ReventlessAws.provision(match$2, match$3, outputString(platformOutputs, "identityStore"), undefined);
732
732
  accounts = e$6.TAG === "Ok" ? ({
733
733
  TAG: "Ok",
734
734
  _0: match$2
@@ -178,7 +178,86 @@ drifted from the file is exactly the case a re-run is expected to fix.
178
178
  Returns the id the pool minted, which is the value the manifest cannot know until
179
179
  the account exists and which nobody should be pasting by hand.
180
180
  */
181
- let applyEntry = async (~providerId: string, ~entry: Manifest.entry): option<string> => {
181
+ /**
182
+ Where the rows mapping a subject to its domain id live, for this pool.
183
+
184
+ A stack-owned pool's table carries a generated suffix, so it is read off the
185
+ stack's `identityStore` output; a pool no stack owns has the derived name
186
+ `provision-identity` created it under. `None` when neither can be known, which
187
+ is a pool provisioned before the store existed — accounts made then keep their
188
+ `sub`, exactly as every account before them.
189
+ */
190
+ let // ── The domain id ────────────────────────────────────────────────────────────
191
+
192
+ identityStoreFor = (~providerId: string, ~stack: option<string>): option<string> =>
193
+ switch stack->Option.orElse(ProvisionProvider.selectedStack()) {
194
+ | Some(stack) =>
195
+ switch ProvisionProvider.pulumi(["stack", "output", "identityStore", "--stack", stack]) {
196
+ | "" => Some(Auth_IdentityStore_Schema.derivedStoreName(~identityProviderId=providerId))
197
+ | name => Some(name)
198
+ | exception _ =>
199
+ Some(Auth_IdentityStore_Schema.derivedStoreName(~identityProviderId=providerId))
200
+ }
201
+ | None => Some(Auth_IdentityStore_Schema.derivedStoreName(~identityProviderId=providerId))
202
+ }
203
+
204
+ /** The domain id recorded for a subject, if the store has one. */
205
+ let recordedUserId = async (~store: string, ~sub: string): option<string> =>
206
+ try {
207
+ let out = await DynamoDb_DocumentClient.GetCommand.make({
208
+ tableName: store,
209
+ key: Dict.fromArray([(Auth_IdentityStore_Schema.partitionKey, JSON.Encode.string(sub))]),
210
+ })->DynamoDb_DocumentClient.GetCommand.send
211
+ out.item
212
+ ->Option.flatMap(JSON.Decode.object)
213
+ ->Option.flatMap(o => o->Dict.get(Auth_IdentityStore_Schema.userIdAttribute))
214
+ ->Option.flatMap(JSON.Decode.string)
215
+ } catch {
216
+ | exn if Util_AwsError.isNotFound(exn) => None
217
+ }
218
+
219
+ /**
220
+ The id the manifest should carry for this account.
221
+
222
+ 🚨 **Minted only for an account this run created.** An account that already
223
+ existed has rows keyed by whatever id it has been writing under — its `sub`, or
224
+ a domain id an earlier run minted — and re-minting would orphan every one of
225
+ them. A store that does not exist leaves the account on its `sub`, which is what
226
+ every account before the store did.
227
+ */
228
+ let domainIdFor = async (~store: option<string>, ~sub: string, ~created: bool): option<string> =>
229
+ switch store {
230
+ | None => Some(sub)
231
+ | Some(store) =>
232
+ switch await recordedUserId(~store, ~sub) {
233
+ | Some(_) as recorded => recorded
234
+ | None if created =>
235
+ let minted = ReventlessCore.Auth_UserId.mint()
236
+ try {
237
+ let _ = await DynamoDb_DocumentClient.PutCommand.make({
238
+ tableName: store,
239
+ item: JSON.Encode.object(
240
+ Dict.fromArray([
241
+ (Auth_IdentityStore_Schema.partitionKey, JSON.Encode.string(sub)),
242
+ (Auth_IdentityStore_Schema.userIdAttribute, JSON.Encode.string(minted)),
243
+ ]),
244
+ ),
245
+ })->DynamoDb_DocumentClient.PutCommand.send
246
+ Some(minted)
247
+ } catch {
248
+ | exn if Util_AwsError.isNotFound(exn) =>
249
+ Console.log(`store ${store} (missing — the account keeps its pool id)`)
250
+ Some(sub)
251
+ }
252
+ | None => Some(sub)
253
+ }
254
+ }
255
+
256
+ let applyEntry = async (
257
+ ~providerId: string,
258
+ ~entry: Manifest.entry,
259
+ ~identityStore: option<string>,
260
+ ): option<string> => {
182
261
  let attributes: array<Cognito.AdminCreateUserCommand.attributeType> = looksLikeEmail(
183
262
  entry.username,
184
263
  )
@@ -200,7 +279,10 @@ let applyEntry = async (~providerId: string, ~entry: Manifest.entry): option<str
200
279
  await ProvisionCognito.addToGroup(~providerId, ~username=entry.username, ~group)
201
280
  Console.log(`member ${entry.username} in ${group}`)
202
281
  }
203
- await ProvisionCognito.subOf(~providerId, ~username=entry.username)
282
+ switch await ProvisionCognito.subOf(~providerId, ~username=entry.username) {
283
+ | None => None
284
+ | Some(sub) => await domainIdFor(~store=identityStore, ~sub, ~created=outcome == Created)
285
+ }
204
286
  }
205
287
 
206
288
  // ── Main ─────────────────────────────────────────────────────────────────────
@@ -233,6 +315,7 @@ is a parameter so a test can refuse without a pool.
233
315
  let provision = async (
234
316
  ~file: string,
235
317
  ~providerId: string,
318
+ ~identityStore: option<string>=?,
236
319
  ~checkPool=checkPoolAcceptsUsernames,
237
320
  ): result<unit, string> =>
238
321
  switch Manifest.parseFile(file) {
@@ -256,7 +339,7 @@ let provision = async (
256
339
  let fills = []
257
340
  for index in 0 to entries->Array.length - 1 {
258
341
  let entry = entries->Array.getUnsafe(index)
259
- let userId = await applyEntry(~providerId, ~entry)
342
+ let userId = await applyEntry(~providerId, ~entry, ~identityStore)
260
343
  fills->Array.push({Manifest.index, password: None, userId})
261
344
  }
262
345
  switch Manifest.fillFile(~path=file, ~fills) {
@@ -288,7 +371,9 @@ let run = async (args: args): result<unit, string> =>
288
371
  Error(`${message}. To fill in the manifest without creating anything, run prepare-accounts`)
289
372
  | Ok((providerId, source)) =>
290
373
  Console.log(`provider ${providerId} (from ${source->ProvisionProvider.describe})`)
291
- await provision(~file, ~providerId)
374
+ let identityStore = identityStoreFor(~providerId, ~stack=args.stack)
375
+ identityStore->Option.forEach(store => Console.log(`store ${store}`))
376
+ await provision(~file, ~providerId, ~identityStore?)
292
377
  }
293
378
  }
294
379
 
@@ -1,12 +1,18 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
4
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
3
5
  import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
4
6
  import * as CliArgs$Reventless from "@reventlessdev/reventless-spec/src/CliArgs.res.mjs";
5
7
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
8
+ import * as LibDynamodb from "@aws-sdk/lib-dynamodb";
9
+ import * as Auth_UserId$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Auth/Auth_UserId.res.mjs";
6
10
  import * as AccountsManifest$Reventless from "@reventlessdev/reventless-spec/src/types/AccountsManifest.res.mjs";
7
11
  import * as Util_AwsError$ReventlessAws from "../src/util/Util_AwsError.res.mjs";
12
+ import * as DynamoDb_DocumentClient$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/DynamoDb_DocumentClient.res.mjs";
8
13
  import * as ProvisionCognito$ReventlessAws from "./ProvisionCognito.res.mjs";
9
14
  import * as ProvisionProvider$ReventlessAws from "./ProvisionProvider.res.mjs";
15
+ import * as Auth_IdentityStore_Schema$ReventlessAws from "../src/adapter/Auth/Auth_IdentityStore_Schema.res.mjs";
10
16
 
11
17
  function parseArgs(argv) {
12
18
  return Stdlib_Result.map(Stdlib_Result.flatMap(CliArgs$Reventless.parse([
@@ -103,7 +109,87 @@ async function ensureGroups(providerId, entries) {
103
109
  }
104
110
  }
105
111
 
106
- async function applyEntry(providerId, entry) {
112
+ function identityStoreFor(providerId, stack) {
113
+ let stack$1 = Stdlib_Option.orElse(stack, ProvisionProvider$ReventlessAws.selectedStack());
114
+ if (stack$1 === undefined) {
115
+ return Auth_IdentityStore_Schema$ReventlessAws.derivedStoreName(providerId);
116
+ }
117
+ let name;
118
+ try {
119
+ name = ProvisionProvider$ReventlessAws.pulumi([
120
+ "stack",
121
+ "output",
122
+ "identityStore",
123
+ "--stack",
124
+ stack$1
125
+ ]);
126
+ } catch (exn) {
127
+ return Auth_IdentityStore_Schema$ReventlessAws.derivedStoreName(providerId);
128
+ }
129
+ if (name === "") {
130
+ return Auth_IdentityStore_Schema$ReventlessAws.derivedStoreName(providerId);
131
+ } else {
132
+ return name;
133
+ }
134
+ }
135
+
136
+ async function recordedUserId(store, sub) {
137
+ try {
138
+ let out = await DynamoDb_DocumentClient$AwsSdk.GetCommand.send(new LibDynamodb.GetCommand({
139
+ TableName: store,
140
+ Key: Object.fromEntries([[
141
+ Auth_IdentityStore_Schema$ReventlessAws.partitionKey,
142
+ sub
143
+ ]])
144
+ }));
145
+ return Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(out.Item, Stdlib_JSON.Decode.object), o => o[Auth_IdentityStore_Schema$ReventlessAws.userIdAttribute]), Stdlib_JSON.Decode.string);
146
+ } catch (raw_exn) {
147
+ let exn = Primitive_exceptions.internalToException(raw_exn);
148
+ if (Util_AwsError$ReventlessAws.isNotFound(exn)) {
149
+ return;
150
+ }
151
+ throw exn;
152
+ }
153
+ }
154
+
155
+ async function domainIdFor(store, sub, created) {
156
+ if (store === undefined) {
157
+ return sub;
158
+ }
159
+ let recorded = await recordedUserId(store, sub);
160
+ if (recorded !== undefined) {
161
+ return recorded;
162
+ }
163
+ if (!created) {
164
+ return sub;
165
+ }
166
+ let minted = Auth_UserId$ReventlessCore.mint();
167
+ try {
168
+ await DynamoDb_DocumentClient$AwsSdk.PutCommand.send(new LibDynamodb.PutCommand({
169
+ Item: Object.fromEntries([
170
+ [
171
+ Auth_IdentityStore_Schema$ReventlessAws.partitionKey,
172
+ sub
173
+ ],
174
+ [
175
+ Auth_IdentityStore_Schema$ReventlessAws.userIdAttribute,
176
+ minted
177
+ ]
178
+ ]),
179
+ TableName: store
180
+ }));
181
+ return minted;
182
+ } catch (raw_exn) {
183
+ let exn = Primitive_exceptions.internalToException(raw_exn);
184
+ if (Util_AwsError$ReventlessAws.isNotFound(exn)) {
185
+ console.log(`store ` + store + ` (missing — the account keeps its pool id)`);
186
+ return sub;
187
+ }
188
+ throw exn;
189
+ }
190
+ }
191
+
192
+ async function applyEntry(providerId, entry, identityStore) {
107
193
  let attributes = looksLikeEmail(entry.username) ? [
108
194
  {
109
195
  Name: "email",
@@ -122,7 +208,10 @@ async function applyEntry(providerId, entry) {
122
208
  await ProvisionCognito$ReventlessAws.addToGroup(providerId, entry.username, group);
123
209
  console.log(`member ` + entry.username + ` in ` + group);
124
210
  }
125
- return await ProvisionCognito$ReventlessAws.subOf(providerId, entry.username);
211
+ let sub = await ProvisionCognito$ReventlessAws.subOf(providerId, entry.username);
212
+ if (sub !== undefined) {
213
+ return await domainIdFor(identityStore, sub, outcome === "Created");
214
+ }
126
215
  }
127
216
 
128
217
  function manifestNote(file) {
@@ -133,7 +222,7 @@ function manifestNote(file) {
133
222
  credentials for a deployment, not secrets to reuse anywhere real.`;
134
223
  }
135
224
 
136
- async function provision(file, providerId, checkPoolOpt) {
225
+ async function provision(file, providerId, identityStore, checkPoolOpt) {
137
226
  let checkPool = checkPoolOpt !== undefined ? checkPoolOpt : checkPoolAcceptsUsernames;
138
227
  let message = AccountsManifest$Reventless.parseFile(file);
139
228
  if (message.TAG !== "Ok") {
@@ -168,7 +257,7 @@ async function provision(file, providerId, checkPoolOpt) {
168
257
  let fills = [];
169
258
  for (let index = 0, index_finish = entries.length; index < index_finish; ++index) {
170
259
  let entry = entries[index];
171
- let userId = await applyEntry(providerId, entry);
260
+ let userId = await applyEntry(providerId, entry, identityStore);
172
261
  fills.push({
173
262
  index: index,
174
263
  password: undefined,
@@ -210,7 +299,11 @@ async function run(args) {
210
299
  let match = message._0;
211
300
  let providerId = match[0];
212
301
  console.log(`provider ` + providerId + ` (from ` + ProvisionProvider$ReventlessAws.describe(match[1]) + `)`);
213
- return await provision(file, providerId, undefined);
302
+ let identityStore = identityStoreFor(providerId, args.stack);
303
+ Stdlib_Option.forEach(identityStore, store => {
304
+ console.log(`store ` + store);
305
+ });
306
+ return await provision(file, providerId, identityStore, undefined);
214
307
  }
215
308
 
216
309
  let cli = {
@@ -251,6 +344,9 @@ export {
251
344
  checkPoolAcceptsUsernames,
252
345
  describe,
253
346
  ensureGroups,
347
+ identityStoreFor,
348
+ recordedUserId,
349
+ domainIdFor,
254
350
  applyEntry,
255
351
  manifestNote,
256
352
  provision,
@@ -224,10 +224,34 @@ let resolvePool = async (~args: args): result<string, string> =>
224
224
 
225
225
  // ── The store ────────────────────────────────────────────────────────────────
226
226
 
227
- let keySchema: array<DynamoDb.DynamoDb.DescribeTableCommand.keySchemaElement> = [
228
- {attributeName: Schema.partitionKey, keyType: "HASH"},
229
- {attributeName: Schema.sortKey, keyType: "RANGE"},
230
- ]
227
+ /** One store to provision: its name, its key, and the refusal for a table under
228
+ that name with another key. Two stores share the pool's trigger, and this is
229
+ the shape both are described in. */
230
+ type store = {
231
+ tableName: string,
232
+ keySchema: array<(string, string)>,
233
+ refusal: (~tableName: string, ~actual: array<(string, string)>) => option<string>,
234
+ }
235
+
236
+ let activeRoleStore = (~providerId: string): store => {
237
+ tableName: Schema.derivedStoreName(~identityProviderId=providerId),
238
+ keySchema: Schema.expectedKeySchema,
239
+ refusal: Schema.keySchemaRefusal,
240
+ }
241
+
242
+ let identityStore = (~providerId: string): store => {
243
+ tableName: Auth_IdentityStore_Schema.derivedStoreName(~identityProviderId=providerId),
244
+ keySchema: Auth_IdentityStore_Schema.expectedKeySchema,
245
+ refusal: Auth_IdentityStore_Schema.keySchemaRefusal,
246
+ }
247
+
248
+ let keySchemaOf = (pairs: array<(string, string)>): array<
249
+ DynamoDb.DynamoDb.DescribeTableCommand.keySchemaElement,
250
+ > =>
251
+ pairs->Array.map(((attributeName, keyType)) => {
252
+ DynamoDb.DynamoDb.DescribeTableCommand.attributeName,
253
+ keyType,
254
+ })
231
255
 
232
256
  /** The SDK's shape mapped onto the plain pairs [Schema.keySchemaRefusal] compares.
233
257
  The refusal itself lives with the schema it is about, since the schema is what
@@ -248,13 +272,10 @@ let describeTable = async (~tableName: string): option<
248
272
  | exn if Util_AwsError.isNotFound(exn) => None
249
273
  }
250
274
 
251
- let provisionStore = async (~tableName: string): result<unit, string> =>
275
+ let provisionStore = async ({tableName, keySchema, refusal}: store): result<unit, string> =>
252
276
  switch await describeTable(~tableName) {
253
277
  | Some(described) =>
254
- switch Schema.keySchemaRefusal(
255
- ~tableName,
256
- ~actual=described.keySchema->Option.getOr([])->asPairs,
257
- ) {
278
+ switch refusal(~tableName, ~actual=described.keySchema->Option.getOr([])->asPairs) {
258
279
  | Some(message) => Error(message)
259
280
  | None =>
260
281
  Console.log(`store ${tableName} (adopted)`)
@@ -263,11 +284,11 @@ let provisionStore = async (~tableName: string): result<unit, string> =>
263
284
  | None =>
264
285
  let _ = await DynamoDb.DynamoDb.CreateTableCommand.make({
265
286
  tableName,
266
- keySchema,
267
- attributeDefinitions: [
268
- {attributeName: Schema.partitionKey, attributeType: "S"},
269
- {attributeName: Schema.sortKey, attributeType: "S"},
270
- ],
287
+ keySchema: keySchemaOf(keySchema),
288
+ attributeDefinitions: keySchema->Array.map(((attributeName, _)) => {
289
+ DynamoDb.DynamoDb.CreateTableCommand.attributeName,
290
+ attributeType: "S",
291
+ }),
271
292
  billingMode: "PAY_PER_REQUEST",
272
293
  })->DynamoDb.DynamoDb.CreateTableCommand.send
273
294
 
@@ -315,12 +336,15 @@ let run = async (args: args): result<unit, string> =>
315
336
  switch await resolvePool(~args) {
316
337
  | Error(_) as e => e
317
338
  | Ok(providerId) =>
318
- let tableName = Schema.derivedStoreName(~identityProviderId=providerId)
319
- switch await provisionStore(~tableName) {
339
+ switch await provisionStore(activeRoleStore(~providerId)) {
320
340
  | Error(_) as e => e
321
341
  | Ok() =>
322
- Console.log(nextSteps(~providerId))
323
- Ok()
342
+ switch await provisionStore(identityStore(~providerId)) {
343
+ | Error(_) as e => e
344
+ | Ok() =>
345
+ Console.log(nextSteps(~providerId))
346
+ Ok()
347
+ }
324
348
  }
325
349
  }
326
350
 
@@ -10,6 +10,7 @@ import * as Util_AwsError$ReventlessAws from "../src/util/Util_AwsError.res.mjs"
10
10
  import * as Auth_SignUpMode$ReventlessAws from "../src/adapter/Auth/Auth_SignUpMode.res.mjs";
11
11
  import * as Auth_LoginIdentifier$ReventlessAws from "../src/adapter/Auth/Auth_LoginIdentifier.res.mjs";
12
12
  import * as CognitoIdentityServiceProvider$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/CognitoIdentityServiceProvider.res.mjs";
13
+ import * as Auth_IdentityStore_Schema$ReventlessAws from "../src/adapter/Auth/Auth_IdentityStore_Schema.res.mjs";
13
14
  import * as ClientCognitoIdentityProvider from "@aws-sdk/client-cognito-identity-provider";
14
15
  import * as Auth_ActiveRoleStore_Schema$ReventlessAws from "../src/adapter/Auth/Auth_ActiveRoleStore_Schema.res.mjs";
15
16
 
@@ -185,16 +186,28 @@ async function resolvePool(args) {
185
186
  }
186
187
  }
187
188
 
188
- let keySchema = [
189
- {
190
- AttributeName: Auth_ActiveRoleStore_Schema$ReventlessAws.partitionKey,
191
- KeyType: "HASH"
192
- },
193
- {
194
- AttributeName: Auth_ActiveRoleStore_Schema$ReventlessAws.sortKey,
195
- KeyType: "RANGE"
196
- }
197
- ];
189
+ function activeRoleStore(providerId) {
190
+ return {
191
+ tableName: Auth_ActiveRoleStore_Schema$ReventlessAws.derivedStoreName(providerId),
192
+ keySchema: Auth_ActiveRoleStore_Schema$ReventlessAws.expectedKeySchema,
193
+ refusal: Auth_ActiveRoleStore_Schema$ReventlessAws.keySchemaRefusal
194
+ };
195
+ }
196
+
197
+ function identityStore(providerId) {
198
+ return {
199
+ tableName: Auth_IdentityStore_Schema$ReventlessAws.derivedStoreName(providerId),
200
+ keySchema: Auth_IdentityStore_Schema$ReventlessAws.expectedKeySchema,
201
+ refusal: Auth_IdentityStore_Schema$ReventlessAws.keySchemaRefusal
202
+ };
203
+ }
204
+
205
+ function keySchemaOf(pairs) {
206
+ return pairs.map(param => ({
207
+ AttributeName: param[0],
208
+ KeyType: param[1]
209
+ }));
210
+ }
198
211
 
199
212
  function asPairs(elements) {
200
213
  return elements.map(k => [
@@ -217,10 +230,12 @@ async function describeTable(tableName) {
217
230
  }
218
231
  }
219
232
 
220
- async function provisionStore(tableName) {
233
+ async function provisionStore(param) {
234
+ let keySchema = param.keySchema;
235
+ let tableName = param.tableName;
221
236
  let described = await describeTable(tableName);
222
237
  if (described !== undefined) {
223
- let message = Auth_ActiveRoleStore_Schema$ReventlessAws.keySchemaRefusal(tableName, asPairs(Stdlib_Option.getOr(described.KeySchema, [])));
238
+ let message = param.refusal(tableName, asPairs(Stdlib_Option.getOr(described.KeySchema, [])));
224
239
  if (message !== undefined) {
225
240
  return {
226
241
  TAG: "Error",
@@ -236,17 +251,11 @@ async function provisionStore(tableName) {
236
251
  }
237
252
  await DynamoDb_DynamoDb$AwsSdk.CreateTableCommand.send(new ClientDynamodb.CreateTableCommand({
238
253
  TableName: tableName,
239
- KeySchema: keySchema,
240
- AttributeDefinitions: [
241
- {
242
- AttributeName: Auth_ActiveRoleStore_Schema$ReventlessAws.partitionKey,
243
- AttributeType: "S"
244
- },
245
- {
246
- AttributeName: Auth_ActiveRoleStore_Schema$ReventlessAws.sortKey,
247
- AttributeType: "S"
248
- }
249
- ],
254
+ KeySchema: keySchemaOf(keySchema),
255
+ AttributeDefinitions: keySchema.map(param => ({
256
+ AttributeName: param[0],
257
+ AttributeType: "S"
258
+ })),
250
259
  BillingMode: "PAY_PER_REQUEST"
251
260
  }));
252
261
  let waited = await DynamoDb_DynamoDb$AwsSdk.TableExistsWaiter.wait(tableName, undefined);
@@ -296,11 +305,14 @@ async function run(args) {
296
305
  return e;
297
306
  }
298
307
  let providerId = e._0;
299
- let tableName = Auth_ActiveRoleStore_Schema$ReventlessAws.derivedStoreName(providerId);
300
- let e$1 = await provisionStore(tableName);
308
+ let e$1 = await provisionStore(activeRoleStore(providerId));
301
309
  if (e$1.TAG !== "Ok") {
302
310
  return e$1;
303
311
  }
312
+ let e$2 = await provisionStore(identityStore(providerId));
313
+ if (e$2.TAG !== "Ok") {
314
+ return e$2;
315
+ }
304
316
  console.log(nextSteps(providerId));
305
317
  return {
306
318
  TAG: "Ok",
@@ -347,7 +359,9 @@ export {
347
359
  poolSettings,
348
360
  findPoolByName,
349
361
  resolvePool,
350
- keySchema,
362
+ activeRoleStore,
363
+ identityStore,
364
+ keySchemaOf,
351
365
  asPairs,
352
366
  describeTable,
353
367
  provisionStore,