@kensio/yulin 1.21.7 → 1.21.9

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 (99) hide show
  1. package/README.md +22 -2
  2. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-properties.js +2 -0
  3. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.js +1 -1
  4. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.d.ts +2 -0
  5. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.js +4 -0
  6. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-refusals.js +0 -5
  7. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping.handler.d.ts +2 -0
  8. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping.handler.js +2 -0
  9. package/dist/service/lambda/command/event-source-mapping/sim-lambda-event-source-mapping-commands.d.ts +2 -0
  10. package/dist/service/lambda/destination/sim-lambda-destination-targets.d.ts +2 -1
  11. package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-shard-poller.d.ts +0 -4
  12. package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-shard-poller.js +10 -15
  13. package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event-source-poller.d.ts +1 -6
  14. package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event-source-poller.js +9 -27
  15. package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-polling.d.ts +19 -0
  16. package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-polling.js +27 -0
  17. package/dist/service/lambda/event-source/poll/sim-lambda-event-source-function.d.ts +1 -1
  18. package/dist/service/lambda/event-source/poll/sim-lambda-event-source-function.js +3 -1
  19. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-age.d.ts +13 -0
  20. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-age.js +27 -0
  21. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-info.d.ts +15 -0
  22. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-info.js +26 -0
  23. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-outcome.d.ts +1 -0
  24. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-outcome.js +7 -1
  25. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-processing.d.ts +17 -0
  26. package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-processing.js +12 -0
  27. package/dist/service/lambda/event-source/poll/sim-lambda-stream-cursor.d.ts +15 -0
  28. package/dist/service/lambda/event-source/poll/sim-lambda-stream-cursor.js +28 -0
  29. package/dist/service/lambda/event-source/poll/sim-lambda-stream-expiry.d.ts +19 -0
  30. package/dist/service/lambda/event-source/poll/sim-lambda-stream-expiry.js +23 -0
  31. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-context.d.ts +3 -0
  32. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-context.js +19 -0
  33. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-destination.d.ts +15 -0
  34. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-destination.js +38 -0
  35. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-record.d.ts +41 -0
  36. package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-record.js +17 -0
  37. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress-state.d.ts +21 -0
  38. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress-state.js +39 -0
  39. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress.d.ts +7 -37
  40. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress.js +22 -62
  41. package/dist/service/lambda/event-source/poll/sim-lambda-stream-retry.d.ts +4 -39
  42. package/dist/service/lambda/event-source/poll/sim-lambda-stream-retry.js +12 -64
  43. package/dist/service/lambda/event-source/sim-lambda-event-source-mapping.d.ts +10 -3
  44. package/dist/service/lambda/event-source/sim-lambda-event-source-mapping.js +12 -3
  45. package/dist/service/lambda/event-source/sim-lambda-stream-destination-config.d.ts +7 -0
  46. package/dist/service/lambda/event-source/sim-lambda-stream-destination-config.js +38 -0
  47. package/dist/service/lambda/event-source/stream/kinesis/sim-kinesis-event-source-streams.js +1 -0
  48. package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-reader.js +1 -0
  49. package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-shard.d.ts +1 -0
  50. package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-shard.js +7 -0
  51. package/dist/service/lambda/event-source/stream/sim-lambda-event-source-streams.d.ts +1 -0
  52. package/dist/service/lambda/index.d.ts +1 -0
  53. package/dist/service/lambda/sim-lambda-commands.js +1 -0
  54. package/dist/service/ses/command/authorize/sim-ses-authorizer.d.ts +9 -0
  55. package/dist/service/ses/command/authorize/sim-ses-authorizer.js +18 -2
  56. package/dist/service/ses/command/send/sim-ses-send-email.js +2 -1
  57. package/docs/README.md +7 -0
  58. package/docs/ai-skill/README.md +6 -1
  59. package/docs/sdk/README.md +15 -0
  60. package/docs/services/acm/README.md +24 -40
  61. package/docs/services/apigateway/README.md +52 -71
  62. package/docs/services/apigatewayv2/README.md +55 -74
  63. package/docs/services/athena/README.md +17 -26
  64. package/docs/services/backup/README.md +29 -39
  65. package/docs/services/bedrock/README.md +38 -52
  66. package/docs/services/cloudformation/README.md +43 -55
  67. package/docs/services/cloudfront/README.md +69 -95
  68. package/docs/services/cloudwatch/README.md +40 -54
  69. package/docs/services/cognito/README.md +30 -45
  70. package/docs/services/dynamodb/README.md +34 -51
  71. package/docs/services/ecr/README.md +36 -77
  72. package/docs/services/ecs/README.md +26 -46
  73. package/docs/services/elbv2/README.md +19 -29
  74. package/docs/services/eventbridge/README.md +14 -18
  75. package/docs/services/firehose/README.md +24 -32
  76. package/docs/services/glue/README.md +41 -78
  77. package/docs/services/iam/README.md +13 -15
  78. package/docs/services/kinesis/README.md +53 -93
  79. package/docs/services/kms/README.md +22 -32
  80. package/docs/services/lambda/README.md +112 -85
  81. package/docs/services/logs/README.md +41 -50
  82. package/docs/services/organizations/README.md +50 -85
  83. package/docs/services/personalize/README.md +28 -44
  84. package/docs/services/rekognition/README.md +26 -38
  85. package/docs/services/route53/README.md +17 -17
  86. package/docs/services/s3/README.md +47 -51
  87. package/docs/services/scheduler/README.md +41 -52
  88. package/docs/services/secretsmanager/README.md +27 -42
  89. package/docs/services/ses/README.md +24 -34
  90. package/docs/services/sns/README.md +18 -26
  91. package/docs/services/sqs/README.md +14 -14
  92. package/docs/services/ssm/README.md +13 -17
  93. package/docs/services/stepfunctions/README.md +18 -20
  94. package/docs/services/sts/README.md +32 -45
  95. package/docs/services/wafv2/README.md +12 -17
  96. package/docs/testing/README.md +228 -0
  97. package/docs/time/README.md +10 -0
  98. package/llms.txt +1 -0
  99. package/package.json +1 -1
@@ -1,20 +1,11 @@
1
1
  # Simulated CloudWatch Metrics
2
2
 
3
- Yulin includes a simulated Amazon CloudWatch for tests and local development. It holds custom
4
- metrics. Those are the datapoints `PutMetricData` publishes, and the statistics
5
- `GetMetricStatistics` and `GetMetricData` read back from them, without an AWS account. It also holds alarms over those
6
- metrics, which evaluate on the simulation's clock and notify an SNS topic when they change state.
7
-
8
- Code that publishes a business metric is code teams already have, and until now the only way to test
9
- it was to assert that the SDK client had been called. That proves the call was made. What it
10
- measured goes untested.
11
-
12
- Most of what lives here is custom metrics. Simulated Lambda publishes its own `AWS/Lambda`
13
- `Invocations`, `Errors`, `Duration` and `IteratorAge`, simulated Cognito publishes a pool's
14
- `AWS/Cognito` counts, and no other simulated service publishes into an `AWS/` namespace yet. A
15
- query for one nothing measures comes back empty, in place of a number that was never taken. A test
16
- can stand a datapoint up for one of those itself, which is what drives an alarm on a service metric
17
- to a state change.
3
+ Yulin simulates CloudWatch metrics and alarms in memory. Application code can publish datapoints and
4
+ read their statistics through the normal CloudWatch commands. Alarms evaluate on simulated time and
5
+ can notify simulated SNS topics.
6
+
7
+ Simulated Lambda publishes `AWS/Lambda` metrics, and simulated Cognito publishes `AWS/Cognito`
8
+ metrics. Tests can seed other AWS-managed metrics through the service writer described below.
18
9
 
19
10
  A custom metric's datapoints arrive either from `PutMetricData` or from a CloudWatch Logs metric
20
11
  filter counting matching log events. See the [CloudWatch Logs docs](../logs/README.md) for the
@@ -24,8 +15,8 @@ CloudWatch specific types are imported from the `@kensio/yulin/cloudwatch` subpa
24
15
 
25
16
  ## Publishing and reading back a metric
26
17
 
27
- A metric is identified by its namespace, its name and its dimensions together. Publishing a value
28
- and asking for it back at a period is the whole loop:
18
+ A metric is identified by its namespace, name, and exact set of dimensions. Publish a value and
19
+ read it back over a period:
29
20
 
30
21
  ```typescript sim-cloudwatch-publish-and-read
31
22
  /**
@@ -84,18 +75,14 @@ the way out. A query naming a unit CloudWatch lacks fails here as it would in an
84
75
 
85
76
  ## Metrics are identified by their dimensions
86
77
 
87
- Real CloudWatch leaves a custom metric unrolled across its dimensions, and so does this. The same
88
- metric name published under two channels is two metrics. A read naming no dimensions reaches only the
89
- metric that was published with none, and never aggregates across the others.
90
-
91
- That is the behaviour teams most often get wrong, and it is worth a test of its own. A dashboard
92
- query written against a metric name alone finds nothing at all if every publish carried a dimension.
78
+ CloudWatch treats each exact set of dimensions as a separate metric. Publishing the same metric name
79
+ with two dimension values creates two metrics. A query without dimensions reads only datapoints
80
+ published without dimensions.
93
81
 
94
82
  ## Metrics and simulated time
95
83
 
96
- A datum carrying no `Timestamp` is stamped from the simulation's clock. A test with
97
- a frozen clock therefore gets timestamps it can assert on exactly, and one that moves time on gets
98
- datapoints in the period it moved to:
84
+ A datum without a `Timestamp` uses the simulation's clock. Set or advance the clock to place
85
+ datapoints in exact periods:
99
86
 
100
87
  ```typescript sim-cloudwatch-simulated-time
101
88
  /**
@@ -153,11 +140,14 @@ window drops a metric out of the listing without anything having to expire it.
153
140
 
154
141
  ## Metrics a simulated service publishes
155
142
 
156
- Real CloudWatch holds two kinds of metric. A caller publishes a custom one through `PutMetricData`, and AWS publishes its own under a namespace beginning `AWS/` that no caller may write into. Both kinds are read the same way.
143
+ Callers publish custom metrics through `PutMetricData`. AWS-managed metrics use namespaces beginning
144
+ with `AWS/`, which callers cannot publish into.
157
145
 
158
- Two services publish their own. Simulated Cognito counts what a user pool was asked to do, under `AWS/Cognito` and dimensioned by `UserPool` and `UserPoolClient`, which the [Cognito docs](../cognito/README.md) cover.
146
+ Simulated Cognito publishes counts under `AWS/Cognito`, dimensioned by `UserPool` and
147
+ `UserPoolClient`. See the [Cognito docs](../cognito/README.md).
159
148
 
160
- Simulated Lambda publishes three of its own. Every invocation counts `Invocations` and a `Duration`, and one whose handler threw counts an `Errors` alongside them, all under `AWS/Lambda` and dimensioned by `FunctionName`. Nothing has to be turned on, and no execution Role needs a permission for it, which is how real Lambda behaves.
149
+ Simulated Lambda publishes `Invocations`, `Duration`, and `Errors` under `AWS/Lambda`, dimensioned by
150
+ `FunctionName`. These metrics require no extra configuration or execution-role permission.
161
151
 
162
152
  ```typescript sim-cloudwatch-lambda-errors
163
153
  /**
@@ -217,13 +207,15 @@ const { MetricAlarms } = await simAws
217
207
  console.log(MetricAlarms?.[0]?.StateValue);
218
208
  ```
219
209
 
220
- `Duration` is measured on the simulation's clock rather than the host's, so it is a number a test can assert on. A handler that moves the clock reports the time it moved, and one that returns without touching it reports nothing spent. `IteratorAge` is measured the same way, and the Lambda documentation covers what a stream event source mapping reports.
210
+ `Duration` uses the simulation's clock rather than the host's. A handler that advances the clock
211
+ reports that elapsed time, while a handler that leaves it unchanged reports zero duration.
212
+ `IteratorAge` uses the same clock. The Lambda documentation describes the value reported by a stream
213
+ event source mapping.
221
214
 
222
215
  ## Seeding a metric AWS publishes
223
216
 
224
- `PutMetricData` refuses a namespace beginning `AWS/`, exactly as an account does. An alarm watching a metric no simulated service publishes therefore sits in `INSUFFICIENT_DATA`, and its arithmetic goes untested however carefully the alarm itself is declared.
225
-
226
- The service writer is the way in. It is the same route simulated Lambda's own metrics take, reached from a test through `cloudWatch().serviceWriter()`, and it stands a datapoint up in any namespace.
217
+ `PutMetricData` refuses namespaces beginning with `AWS/`. To test an alarm for another AWS-managed
218
+ metric, add the datapoint through `cloudWatch().serviceWriter()`.
227
219
 
228
220
  ```typescript sim-cloudwatch-seed-service-metric
229
221
  /**
@@ -286,14 +278,13 @@ console.log(MetricAlarms?.[0]?.StateValue);
286
278
 
287
279
  A datapoint arriving without a `timestamp` is stamped with the simulation's clock. One carrying its own lands where it says, which fills a window without the clock having to be walked through it.
288
280
 
289
- `PutMetricData` is untouched by any of this. A caller naming a reserved namespace is refused exactly as before, and only the account's own machinery and a test reach the store this way.
281
+ The service writer is a test setup API. `PutMetricData` keeps its reserved-namespace validation.
290
282
 
291
283
  ## Alarms
292
284
 
293
- An alarm watches one metric and changes state on the simulation's clock, with no real timer behind
294
- it. Each evaluation is scheduled at the next period boundary. A frozen clock evaluates nothing, and
295
- advancing time by twenty minutes walks twenty one-minute evaluations and settles before the next
296
- line of the test runs.
285
+ An alarm watches one metric and changes state on the simulation's clock. Each evaluation is scheduled
286
+ at the next period boundary. Advancing time by twenty minutes runs twenty one-minute evaluations and
287
+ settles before the next line of the test.
297
288
 
298
289
  ```typescript sim-cloudwatch-alarm
299
290
  /**
@@ -353,8 +344,8 @@ console.log(described.MetricAlarms?.at(0)?.StateValue);
353
344
  ```
354
345
 
355
346
  A new alarm is in `INSUFFICIENT_DATA` until it has evaluated a period, as on real CloudWatch. The
356
- window it looks back over reaches behind the moment the alarm was created. An alarm over a metric
357
- nothing publishes into, with `TreatMissingData: "breaching"`, therefore fires on its first
347
+ window it looks back over reaches behind the moment the alarm was created. An alarm over an empty
348
+ metric, with `TreatMissingData: "breaching"`, therefore fires on its first
358
349
  evaluation, without waiting for the periods to accumulate. That is what an account does too.
359
350
 
360
351
  ### Reaching a subscriber
@@ -458,10 +449,9 @@ had never named them.
458
449
 
459
450
  ## Permissions
460
451
 
461
- CloudWatch metrics have no ARN, leaving a policy nothing to name. Every metric action here is
462
- granted on `*`. A policy written against something like
463
- `arn:aws:cloudwatch:eu-west-2:111111111111:metric/Orders/Failed` reaches nothing, here and in an
464
- account.
452
+ CloudWatch metrics have no ARN, so every metric action uses a resource of `*`. A policy written
453
+ against a fabricated metric ARN such as
454
+ `arn:aws:cloudwatch:eu-west-2:111111111111:metric/Orders/Failed` is invalid in Yulin and AWS.
465
455
 
466
456
  Alarms are the exception, and do have an ARN. `PutMetricAlarm`, `DeleteAlarms` and `SetAlarmState`
467
457
  authorize against `arn:aws:cloudwatch:<region>:<account>:alarm:<name>`, while `DescribeAlarms` and
@@ -525,7 +515,7 @@ await simAws.cloudWatch().putMetricData(
525
515
  // Publishing into any other namespace as this Role is denied.
526
516
  ```
527
517
 
528
- ## What is simulated
518
+ ## Supported operations
529
519
 
530
520
  - `PutMetricData`, with `Value`, `StatisticValues` and `Values`/`Counts`.
531
521
  - `ListMetrics`, filtered by namespace, metric name and dimensions, with `RecentlyActive` and
@@ -546,11 +536,9 @@ await simAws.cloudWatch().putMetricData(
546
536
  - `AWS::CloudWatch::Alarm` in simulated CloudFormation, deployed through `PutMetricAlarm` and taken
547
537
  down with the stack.
548
538
 
549
- ## What is refused, and how it says so
539
+ ## Unsupported operations and options
550
540
 
551
- Anything real CloudWatch would accept and this leaves undone is refused with a message saying so,
552
- rather than accepted and ignored. A silently dropped filter is worse than a failure, because the
553
- test still passes and no longer means what it says.
541
+ Yulin rejects unsupported CloudWatch behavior instead of ignoring it:
554
542
 
555
543
  - **Composite and anomaly detection alarms.** `Metrics` and `ThresholdMetricId` on `PutMetricAlarm`
556
544
  are refused. There is no trained model here for an anomaly band to come from.
@@ -571,8 +559,6 @@ test still passes and no longer means what it says.
571
559
  through the service writer. A CloudWatch Logs metric filter naming a reserved namespace is refused
572
560
  when it publishes, as `PutMetricData` refuses a caller naming one.
573
561
 
574
- Two divergences are deliberate, and not refusals. Real CloudWatch rejects a datapoint more than two
575
- weeks old or more than two hours in the future, and this accepts any timestamp, letting a test seed
576
- a window without arranging the clock around it. And datapoints come back earliest first, which real
577
- CloudWatch's contract permits without promising, because a test reading the third period of five
578
- needs an order it can rely on.
562
+ Two behaviors differ from AWS. Yulin accepts datapoints more than two weeks old or more than two
563
+ hours in the future, which makes it easier to seed a test window. It also returns datapoints in
564
+ ascending timestamp order so tests receive deterministic results.
@@ -1,17 +1,14 @@
1
1
  # Simulated Cognito IDP
2
2
 
3
- Yulin includes a simulated Cognito user pool directory for tests and local development. Pools and
4
- their app clients are held in memory, and every operation is authorized by simulated IAM.
3
+ Yulin simulates Cognito user pools, app clients, users, groups, tokens, hosted domains and Lambda
4
+ triggers. Use `simAws.cognitoIdp()` directly or intercept a `CognitoIdentityProviderClient`.
5
+ Cognito types are available from `@kensio/yulin/cognito`.
5
6
 
6
- Only user pools are simulated. Cognito identity pools, which exchange a token for AWS credentials,
7
- are a different service and are outside the simulation.
8
-
9
- Cognito-specific types are imported from the `@kensio/yulin/cognito` subpath.
7
+ Cognito identity pools are unsupported.
10
8
 
11
9
  ## Creating a pool and an app client
12
10
 
13
- A pool needs a name. Everything else has a default, and the defaults here are the ones real Cognito
14
- applies.
11
+ A user pool needs a name. Other properties use Cognito's defaults.
15
12
 
16
13
  ```typescript sim-cognito-create-user-pool
17
14
  /**
@@ -46,16 +43,14 @@ const appClient = await cognito.createUserPoolClient(
46
43
  console.log(appClient.UserPoolClient?.ClientId); // 26 lowercase characters
47
44
  ```
48
45
 
49
- A pool id names the region the pool was created in, as real pool ids do. Application code that
50
- splits the id on the underscore to find the region works here for the same reason it works on AWS.
46
+ A pool ID contains the region before its underscore, matching Cognito's ID format.
51
47
 
52
48
  Two pools may share a name. Only the id identifies one.
53
49
 
54
50
  ## Password policy
55
51
 
56
- A pool created without a `Policies` of its own gets the real default of eight characters, with an
57
- uppercase letter, a lowercase letter, a number and a symbol each required. A request setting some of
58
- those keeps the defaults for the rest.
52
+ A pool created without `Policies` requires at least eight characters with uppercase, lowercase,
53
+ numeric and symbol characters. Setting part of the policy keeps the defaults for omitted fields.
59
54
 
60
55
  ```typescript sim-cognito-password-policy
61
56
  /**
@@ -86,10 +81,9 @@ with `InvalidPasswordException`, saying which rule it broke.
86
81
 
87
82
  ## Users
88
83
 
89
- `AdminCreateUser` creates a user in `FORCE_CHANGE_PASSWORD`, where real Cognito leaves a user an
90
- admin made. It has a temporary password and cannot sign in with it. Setting a permanent password
91
- moves the user to `CONFIRMED`. The sign-in flows read that status. A user in
92
- `FORCE_CHANGE_PASSWORD` gets the `NEW_PASSWORD_REQUIRED` challenge, not tokens.
84
+ `AdminCreateUser` creates a user in `FORCE_CHANGE_PASSWORD` with a temporary password. Sign-in
85
+ returns the `NEW_PASSWORD_REQUIRED` challenge until `AdminSetUserPassword` sets a permanent
86
+ password. A permanent password changes the status to `CONFIRMED`.
93
87
 
94
88
  ```typescript sim-cognito-create-user
95
89
  /**
@@ -145,11 +139,9 @@ console.log(read.UserAttributes?.find((each) => each.Name === "sub")?.Value);
145
139
  A password set without `Permanent: true` is temporary, and leaves the user in
146
140
  `FORCE_CHANGE_PASSWORD` again.
147
141
 
148
- A user's `sub` is a UUID Cognito allocates, reported among its attributes. It is not the username,
149
- and code treating the two as interchangeable fails here, and not in a deployment. Admin
150
- operations here name a user by its username only. Real Cognito also accepts a `sub` where an
151
- operation asks for a username. That is one thing that works there and not here. The refusal says so
152
- when the username given is some user's `sub`.
142
+ A user's `sub` is a generated UUID and differs from the username. Yulin's admin operations accept
143
+ the username only. Cognito also accepts a `sub` for some operations, which Yulin reports as an
144
+ unsupported lookup.
153
145
 
154
146
  Attributes come back under `Attributes` from `AdminCreateUser` and `ListUsers`, and under
155
147
  `UserAttributes` from `AdminGetUser`, as the real API names them.
@@ -160,11 +152,9 @@ Attributes come back under `Attributes` from `AdminCreateUser` and `ListUsers`,
160
152
 
161
153
  ## Signing in by email or phone number
162
154
 
163
- A pool created with `UsernameAttributes` signs its users in by that attribute, not by a username
164
- they chose. Cognito generates a UUID as the username for such a user, and the value the
165
- request called the username goes into the attribute the pool signs in by. That generated username
166
- is what `AdminGetUser` reports and what the `cognito:username` claim carries. An application reading
167
- "the username" off such a pool reads a UUID.
155
+ A pool with `UsernameAttributes` signs users in with an email address or phone number. Cognito
156
+ generates a UUID for the stored username. `AdminGetUser` and the `cognito:username` token claim
157
+ report that UUID.
168
158
 
169
159
  A CDK `UserPool` with `signInAliases: { email: true }` emits `UsernameAttributes: ["email"]`, the
170
160
  usual way to build an email sign-in pool.
@@ -384,13 +374,11 @@ outside the simulation.
384
374
 
385
375
  ## Signing up
386
376
 
387
- `SignUp` is the other way a user gets into a pool. It names an app client rather than a pool, is
388
- authorized by no IAM policy, and leaves the user in `UNCONFIRMED` with the password it chose.
377
+ `SignUp` creates an `UNCONFIRMED` user through an app client. The operation does not use IAM
378
+ authorization.
389
379
 
390
- Real Cognito emails or texts a confirmation code at that point. Nothing here delivers a message, and
391
- the code is readable from the pool instead, through `confirmationCode` on the pool object. That is a
392
- deliberate divergence. Real Cognito never reports a code back to anyone, and reading one is what
393
- makes a registration flow testable at all.
380
+ Read the confirmation code from `confirmationCode` on the simulated pool. Cognito sends the code by
381
+ email or text and never exposes it through the service API.
394
382
 
395
383
  The pool also records the message it would have sent, holding the wording and the code a user
396
384
  would have read. That is in [Messages a pool would have sent](#messages-a-pool-would-have-sent)
@@ -500,9 +488,8 @@ the deployed pool would give. A pool created without the setting allows sign-up,
500
488
 
501
489
  ## Resetting a forgotten password
502
490
 
503
- A user that cannot get in asks for a code with `ForgotPassword` and sets a new password with
504
- `ConfirmForgotPassword`. Both name an app client, and neither is authorized by an IAM policy. They
505
- are the pair an application calls when it has built its own sign-in screens.
491
+ `ForgotPassword` sends a code and `ConfirmForgotPassword` sets the new password. Both operations use
492
+ an app client and bypass IAM authorization.
506
493
 
507
494
  The code goes to the same place a sign-up code goes, and is read back the same way, through
508
495
  `confirmationCode` on the pool object. `ForgotPassword` answers with `CodeDeliveryDetails` naming
@@ -633,9 +620,8 @@ are refused for one.
633
620
 
634
621
  ## Messages a pool would have sent
635
622
 
636
- Nothing here delivers an email or a text message. A pool records what it would have sent instead,
637
- and `sentMessages` on the pool object hands the record over. Each message carries the recipient, the
638
- medium, the subject, the body and the occasion it was sent on.
623
+ The pool records outgoing email and text messages in `sentMessages`. Each entry contains the
624
+ recipient, medium, subject, body and message type.
639
625
 
640
626
  A message is recorded on five occasions. Those are a `SignUp`, a `ResendConfirmationCode`, an
641
627
  `AdminCreateUser` that did not ask for `MessageAction: SUPPRESS`, an MFA code sent by text message,
@@ -2060,10 +2046,9 @@ own users at the same address, which real Cognito keeps as two accounts until
2060
2046
  `AdminLinkProviderForUser` merges them. That operation is unsimulated.
2061
2047
 
2062
2048
  Posting the page carries on into the sign-in the authorize endpoint already runs, ending in the same
2063
- `<ProviderName>_<subject>` user and the same authorization code. Nothing is kept on the provider
2064
- afterwards: a further authorize request asks again, because real Cognito asks the provider afresh
2065
- every time. A provider that `signInAs` has already put somebody at skips the page, which is what
2066
- leaves a test that says who is signing in seeing none of this.
2049
+ `<ProviderName>_<subject>` user and the same authorization code. Provider state is discarded after
2050
+ the request. A later authorize request asks again because real Cognito asks the provider afresh each
2051
+ time. A provider configured with `signInAs` skips the page and uses the configured identity.
2067
2052
 
2068
2053
  `/signup` is a link from that page. Its form asks for a username, a password and the attributes the
2069
2054
  pool needs, which are the ones its `Schema` made required and the ones its `AutoVerifiedAttributes`
@@ -3704,8 +3689,8 @@ console.log(described.UserPoolClient?.ClientName);
3704
3689
 
3705
3690
  A registered pool behaves like any other. It answers `DescribeUserPoolCommand` and
3706
3691
  `ListUserPoolsCommand`, holds users, groups and app clients, and serves its JWKS and OpenID
3707
- configuration on localhost. Everything written against a pool id follows from the id it was
3708
- registered under: its ARN, its issuer URL, the `iss` claim of its tokens and its `ProviderName`. A
3692
+ configuration on localhost. The registered ID determines the pool ARN, issuer URL, token `iss` claim
3693
+ and `ProviderName`. A
3709
3694
  policy naming `arn:aws:cognito-idp:eu-west-2:111111111111:userpool/eu-west-2_aBcDeFgHi` authorizes
3710
3695
  the handler that reads the pool, which is what a template carrying the id in two places needs.
3711
3696
 
@@ -1,18 +1,13 @@
1
1
  # Simulated DynamoDB
2
2
 
3
- Yulin includes a simulated DynamoDB for tests and local development. Tables are held in memory, and
4
- every operation is authorized by simulated IAM.
5
-
6
- This page covers creating, describing, listing and deleting tables. What a request says is checked
7
- the way real DynamoDB checks it. A table that can be created here is one that could be created on
8
- AWS.
9
-
10
- DynamoDB-specific types are imported from the `@kensio/yulin/dynamodb` subpath.
3
+ Yulin simulates DynamoDB tables, indexes, items, streams and time to live in memory. Use
4
+ `simAws.dynamoDb()` directly or intercept a `DynamoDBClient`. DynamoDB types are available from
5
+ `@kensio/yulin/dynamodb`.
11
6
 
12
7
  ## Creating a table
13
8
 
14
- `CreateTable` needs a `TableName`, a `KeySchema`, and an `AttributeDefinitions` entry for every
15
- attribute the key schema names.
9
+ `CreateTable` requires a table name, a key schema and an attribute definition for every key
10
+ attribute.
16
11
 
17
12
  ```typescript sim-dynamodb-create-table
18
13
  /**
@@ -45,8 +40,7 @@ await simAws.backgroundTasksComplete();
45
40
  A new table is `CREATING`, and activation is scheduled as background work. Call
46
41
  `simAws.backgroundTasksComplete()` when a test needs the table to be `ACTIVE`.
47
42
 
48
- The description carries back what the request asked for. That is the key schema, the attribute
49
- definitions, the table ARN, a table ID, and the billing and capacity the table was created with.
43
+ The response describes the table's keys, attribute definitions, ARN, ID, billing mode and capacity.
50
44
 
51
45
  ## Key schema and attribute definitions
52
46
 
@@ -62,9 +56,8 @@ attribute as the type the table declared for it.
62
56
 
63
57
  ## Billing modes and throughput
64
58
 
65
- `BillingMode` defaults to `PROVISIONED`, making `ProvisionedThroughput` required with at least one
66
- read and one write capacity unit. A request that leaves both out asks for a provisioned table with
67
- no capacity, and is refused.
59
+ `BillingMode` defaults to `PROVISIONED`. A provisioned table requires at least one read and one write
60
+ capacity unit in `ProvisionedThroughput`.
68
61
 
69
62
  `PAY_PER_REQUEST` refuses `ProvisionedThroughput`, since an on-demand table has no capacity to
70
63
  provision.
@@ -111,8 +104,8 @@ reports for one.
111
104
 
112
105
  ## Global secondary indexes
113
106
 
114
- `GlobalSecondaryIndexes` on `CreateTable` declares indexes with a key of their own over the same
115
- items. Each index needs an `IndexName`, a `KeySchema` and a `Projection`.
107
+ `GlobalSecondaryIndexes` adds indexes over the table's items. Each index requires an `IndexName`,
108
+ `KeySchema` and `Projection`.
116
109
 
117
110
  ```typescript sim-dynamodb-global-secondary-index
118
111
  /**
@@ -583,9 +576,8 @@ console.log(description.Table?.TableStatus); // "ACTIVE"
583
576
 
584
577
  ## Tagging tables
585
578
 
586
- A table is tagged by `CreateTable`, or afterwards by `TagResource`. `UntagResource` takes tags off,
587
- and `ListTagsOfResource` reads them back. The three tag commands name their resource by ARN, in
588
- `ResourceArn`, where the table commands take a name or an ARN.
579
+ Add tags during `CreateTable` or later with `TagResource`. Remove them with `UntagResource` and read
580
+ them with `ListTagsOfResource`. Tag commands require the table ARN in `ResourceArn`.
589
581
 
590
582
  ```typescript sim-dynamodb-tag-table
591
583
  /**
@@ -683,9 +675,8 @@ calling `Tags.of(stack).add("Environment", "test")` gets a tagged table.
683
675
 
684
676
  ## Writing items
685
677
 
686
- `PutItem` writes one item, replacing the whole item under its primary key rather than merging into
687
- it. The item is there by the time the call returns. A write and the read that follows it need no
688
- step in between.
678
+ `PutItem` replaces the complete item stored under the primary key. The write is visible when the
679
+ command returns.
689
680
 
690
681
  ```typescript sim-dynamodb-put-item
691
682
  /**
@@ -739,10 +730,9 @@ anywhere else in the item.
739
730
 
740
731
  ## Reading and deleting items
741
732
 
742
- `GetItem` reads one item by its primary key, and `DeleteItem` removes one the same way. The `Key`
743
- both take is the whole primary key and nothing else. A missing key element, an attribute outside the
744
- key, or a value whose type fails to match the table's `AttributeDefinitions` is a
745
- `ValidationException` naming the attribute at fault.
733
+ `GetItem` reads an item by primary key. `DeleteItem` removes it. The `Key` must contain every key
734
+ attribute and no others, using the types declared in `AttributeDefinitions`. Invalid keys raise
735
+ `ValidationException`.
746
736
 
747
737
  ```typescript sim-dynamodb-get-delete-item
748
738
  /**
@@ -822,9 +812,8 @@ Both take the table's name or its ARN, as the table commands do.
822
812
 
823
813
  ## Updating items
824
814
 
825
- `UpdateItem` changes part of an item, where `PutItem` replaces the whole thing. What to change is
826
- written as an `UpdateExpression` made of `SET`, `REMOVE`, `ADD` and `DELETE` clauses, in any order.
827
- Each keyword appears at most once, and the actions inside a clause are separated by commas.
815
+ `UpdateItem` changes selected attributes. Its `UpdateExpression` may contain `SET`, `REMOVE`, `ADD`
816
+ and `DELETE` clauses in any order. Each clause may appear once and contain comma-separated actions.
828
817
 
829
818
  A `SET` action is `path = operand`, where an operand is a value from `ExpressionAttributeValues`,
830
819
  another document path, or a call to `if_not_exists(path, operand)` or `list_append(one, other)`. Two
@@ -1066,12 +1055,11 @@ placeholder used by either counts as used.
1066
1055
 
1067
1056
  ## Conditional writes
1068
1057
 
1069
- `PutItem`, `DeleteItem` and `UpdateItem` take a `ConditionExpression`, checked against whatever is
1070
- stored under the key before anything changes. A condition that fails to hold leaves the item exactly
1071
- as it was and throws `ConditionalCheckFailedException`, with the name and message real DynamoDB
1072
- uses.
1058
+ `PutItem`, `DeleteItem` and `UpdateItem` evaluate `ConditionExpression` against the stored item before
1059
+ writing. A failed condition leaves the item unchanged and raises
1060
+ `ConditionalCheckFailedException`.
1073
1061
 
1074
- That is how a write becomes an insert if absent, and how a version attribute becomes optimistic
1062
+ Use `attribute_not_exists` for insert-only writes or compare a version attribute for optimistic
1075
1063
  locking.
1076
1064
 
1077
1065
  ```typescript sim-dynamodb-conditional-write
@@ -1176,9 +1164,8 @@ expression, in both directions. A placeholder the request leaves undefined is a
1176
1164
 
1177
1165
  ## Projecting attributes
1178
1166
 
1179
- `GetItem` takes a `ProjectionExpression`, a comma-separated list of document paths. Only those paths
1180
- come back. A path is an attribute name, then any number of `.attribute` dereferences and `[n]` list
1181
- indexes, such as `address.city` or `lines[0].sku`.
1167
+ `ProjectionExpression` is a comma-separated list of document paths returned by `GetItem`. A path can
1168
+ contain map attributes and list indexes, such as `address.city` or `lines[0].sku`.
1182
1169
 
1183
1170
  An attribute name that is a DynamoDB reserved word, or that has a character an expression cannot
1184
1171
  carry, is written as a `#name` placeholder and defined in `ExpressionAttributeNames`.
@@ -1259,12 +1246,11 @@ index and a path past that depth are each a `ValidationException` naming the pat
1259
1246
 
1260
1247
  ## Querying an item collection
1261
1248
 
1262
- A table with a sort key holds an item collection under each partition key, holding the items with
1263
- that partition key, ordered by their sort key. `Query` reads one of those collections.
1249
+ A table with a sort key keeps one ordered item collection per partition key. `Query` reads one
1250
+ collection.
1264
1251
 
1265
- `KeyConditionExpression` says which. It is one equality on the partition key, optionally joined by
1266
- `AND` to one condition on the sort key. The sort key condition is `=`, `<`, `<=`, `>`, `>=`,
1267
- `BETWEEN` or `begins_with`, and both bounds of a `BETWEEN` are inside the range.
1252
+ `KeyConditionExpression` requires equality on the partition key. It may add one sort key condition
1253
+ with `AND`. Sort key operators are `=`, `<`, `<=`, `>`, `>=`, `BETWEEN` and `begins_with`.
1268
1254
 
1269
1255
  ```typescript sim-dynamodb-query
1270
1256
  /**
@@ -1371,8 +1357,8 @@ in `ExpressionAttributeNames`, as in any other expression.
1371
1357
 
1372
1358
  ### Paging a collection
1373
1359
 
1374
- `Limit` counts the items a query evaluated. `LastEvaluatedKey` is the primary key of the item the
1375
- walk stopped on, and the next request passes it back as `ExclusiveStartKey` to resume after it.
1360
+ `Limit` counts evaluated items. Pass `LastEvaluatedKey` back as `ExclusiveStartKey` to continue after
1361
+ the last evaluated item.
1376
1362
 
1377
1363
  ```typescript sim-dynamodb-query-paging
1378
1364
  /**
@@ -1447,9 +1433,7 @@ from a different partition key is refused, since it names a collection this quer
1447
1433
 
1448
1434
  ## Reading a global secondary index
1449
1435
 
1450
- `IndexName` on `Query` and `Scan` reads an index in place of the table. The key condition is held to
1451
- the index key schema and not the table's, which is the point. The index is how an access pattern the
1452
- table key cannot serve gets served.
1436
+ Set `IndexName` on `Query` or `Scan` to read an index. Key conditions then use the index key schema.
1453
1437
 
1454
1438
  ```typescript sim-dynamodb-query-index
1455
1439
  /**
@@ -1572,9 +1556,8 @@ refused.
1572
1556
 
1573
1557
  ## Scanning a table
1574
1558
 
1575
- `Scan` reads every item in a table. It needs no key knowledge at all. That is what makes it the
1576
- operation test setup and assertions reach for, and the wrong operation for most application access
1577
- patterns, since it reads the whole table however few items the caller wanted.
1559
+ `Scan` reads every item in a table without a key condition. It is useful for test assertions but
1560
+ usually reads more data than application code needs.
1578
1561
 
1579
1562
  ```typescript sim-dynamodb-scan
1580
1563
  /**