@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,16 +1,11 @@
1
1
  # Simulated API Gateway REST APIs
2
2
 
3
- Yulin includes a simulated API Gateway v1 service, reachable as `simAws.apiGateway()`. It covers the
4
- REST API resource tree, the methods declared on it, a Lambda proxy integration behind each method,
5
- and the deployments and stages that publish them. REST-API-specific types are imported from the
6
- `@kensio/yulin/apigateway` subpath.
3
+ Yulin simulates API Gateway REST APIs (the v1 API) with Lambda proxy integrations. Use
4
+ `simAws.apiGateway()` directly or intercept an `APIGatewayClient`. REST API types are available from
5
+ `@kensio/yulin/apigateway`.
7
6
 
8
- This is the v1 service. HTTP APIs are v2, on a separate SDK client, and they are documented under
9
- [API Gateway HTTP APIs](https://yulinsim.dev/services/apigatewayv2/). The two hold separate state. A REST API created here
10
- stays out of `simAws.apiGatewayV2()`.
11
-
12
- A handler behind a REST API can be tested against a real HTTP request, with no hand-built event to
13
- keep in step.
7
+ HTTP APIs use the separate `simAws.apiGatewayV2()` service. See
8
+ [API Gateway HTTP APIs](https://yulinsim.dev/services/apigatewayv2/).
14
9
 
15
10
  ## Creating a REST API
16
11
 
@@ -48,13 +43,13 @@ console.log(typeof created.rootResourceId);
48
43
  // "string"
49
44
  ```
50
45
 
51
- A REST API name identifies nothing. Two APIs in one account and region may share a name, and the id
52
- is what tells them apart. Hold the id the create returns.
46
+ A REST API name is a label. Two APIs in the same account and region may share a name. Use the ID
47
+ returned by `CreateRestApiCommand` to address the API.
53
48
 
54
49
  ## Building the path tree
55
50
 
56
- A REST API path is a chain of resources, each holding one segment. `CreateResourceCommand` adds a
57
- segment under a parent and reports the full path its place in the tree gives it.
51
+ A REST API stores its path as a tree of resources. Each resource holds one segment.
52
+ `CreateResourceCommand` adds a segment below a parent and returns its full path.
58
53
 
59
54
  ```typescript sim-apigateway-resource-tree
60
55
  /**
@@ -103,18 +98,17 @@ console.log(listed.items.map((resource) => resource.path));
103
98
  // [ "/", "/orders", "/orders/{orderId}" ]
104
99
  ```
105
100
 
106
- A segment is a literal such as `orders`, a path parameter such as `{orderId}`, or a greedy path
107
- parameter such as `{proxy+}`. A greedy segment matches the rest of the request path. A resource
108
- holding one therefore takes no children, and adding under it is refused.
101
+ A segment may be literal (`orders`), a path parameter (`{orderId}`), or a greedy path parameter
102
+ (`{proxy+}`). A greedy segment matches the rest of the path and cannot have children.
109
103
 
110
104
  Deleting a resource deletes everything under it, the way real API Gateway does. The root resource
111
105
  stays, because every REST API has one.
112
106
 
113
107
  ## Methods and their integrations
114
108
 
115
- A method is declared on a resource with `PutMethodCommand`, and what it does with a request goes
116
- behind it with `PutIntegrationCommand`. Both address the same resource id and HTTP method, since a
117
- REST API method has no id of its own.
109
+ `PutMethodCommand` declares a method on a resource. `PutIntegrationCommand` connects that method to
110
+ a Lambda function. Both commands use the resource ID and HTTP method because a method has no ID of
111
+ its own.
118
112
 
119
113
  ```typescript sim-apigateway-method-integration
120
114
  /**
@@ -184,18 +178,15 @@ console.log(method.methodIntegration?.uri);
184
178
  // echoed back as one line, the way it was configured
185
179
  ```
186
180
 
187
- The integration URI is written either as the bare function ARN, which CDK emits, or wrapped in the
188
- API Gateway invoke path above, which CloudFormation templates and OpenAPI documents emit. Both reach
189
- the same function, and the string is echoed back as it was configured, the way real API Gateway does.
190
- A version or alias qualifier on the end of the ARN is kept. An integration built on an alias
191
- therefore follows that alias.
181
+ The integration URI may be a function ARN or the API Gateway invocation URI shown above. CDK emits
182
+ the first form. CloudFormation templates and OpenAPI documents commonly emit the second. A version
183
+ or alias qualifier is preserved. An integration that names an alias follows that alias.
192
184
 
193
185
  Deleting a method deletes its integration, because a REST API integration is part of the method.
194
186
 
195
187
  ## Deployments and stages
196
188
 
197
- A REST API has an invocation URL once a stage exists, and every stage is the first path segment of
198
- that URL. An HTTP API can serve a `$default` stage at the root, and a REST API always carries the
189
+ A REST API gets an invocation URL when it has a stage. The stage name is always the first path
199
190
  segment.
200
191
 
201
192
  ```typescript sim-apigateway-deploy-stage
@@ -249,15 +240,13 @@ console.log(restApi?.invokeUrl("prod"));
249
240
  `invokeUrl` is a simulator accessor. Real API Gateway reports no endpoint for a REST API and leaves
250
241
  callers to build the URL themselves. CDK's `RestApi.urlForPath` builds the same one.
251
242
 
252
- Real API Gateway freezes the resources and methods into a deployment, and an edit made afterwards
253
- reaches no client until another deployment is created. Here a stage serves the API's current
254
- resources. A test that edits a method sees the change straight away, with no redeployment in
255
- between. That is the one place this departs from AWS.
243
+ Stages serve the API's current resources and methods. Changes take effect immediately. AWS freezes
244
+ the configuration in a deployment and requires another deployment before changes become visible.
256
245
 
257
246
  ## Serving a request
258
247
 
259
- A request to the stage's invoke URL walks the resource tree to a method and invokes that method's
260
- integration, and the handler's response becomes the HTTP response.
248
+ A request to a stage selects a resource and method, then invokes its integration. The Lambda result
249
+ becomes the HTTP response.
261
250
 
262
251
  ```typescript sim-apigateway-serve
263
252
  /**
@@ -300,14 +289,13 @@ console.log(await response.text());
300
289
  await srv.close();
301
290
  ```
302
291
 
303
- `simRestApiLambdaProxyFactory` builds the function, the resources, the method, the integration, the
304
- invoke permission and the deployment in one call. A test about serving wants all of them and is
305
- about none of them. A test about the commands themselves sends them one at a time.
292
+ `simRestApiLambdaProxyFactory` creates a ready-to-call REST API in one operation. It creates the
293
+ function, resources, method, integration, invoke permission and deployment.
306
294
 
307
295
  ### The event a handler receives
308
296
 
309
- A REST API sends payload format 1.0. It carries both a single-value and a multi-value map for the
310
- headers and the query string, and it sends `null` for an empty map where format 2.0 omits the field:
297
+ A REST API sends a payload format 1.0 event. Headers and query parameters appear in both single-value
298
+ and multi-value maps. Empty values use `null`:
311
299
 
312
300
  | Field | Empty case |
313
301
  | ---------------------------------------------------------- | ---------- |
@@ -319,18 +307,15 @@ headers and the query string, and it sends `null` for an empty map where format
319
307
  client asked for, stage segment and all. A handler behind a `{proxy+}` reads `resource` to tell which
320
308
  template caught its request.
321
309
 
322
- Each format's handler reads the other format's event wrongly. One function behind both an HTTP API
323
- and a REST API therefore has to pick a side.
310
+ REST API and HTTP API events have different shapes. A handler shared by both must handle both
311
+ formats explicitly.
324
312
 
325
313
  ### The response a handler returns
326
314
 
327
- A REST API proxy integration takes one shape. A result carrying a numeric `statusCode` becomes the
328
- response, and `multiValueHeaders` sends a header more than once. Anything else is a 502 with
329
- `Internal server error`. That is what real API Gateway answers when it cannot read the integration
330
- response. Payload format 2.0 is the lenient one, wrapping an unrecognised value in a 200, and a
331
- handler relying on that behaves differently here for the same reason it does on AWS.
315
+ A REST API proxy result must contain a numeric `statusCode`. Use `multiValueHeaders` to send the
316
+ same header more than once. An invalid result produces a 502 response with `Internal server error`.
332
317
 
333
- ### Answers when the request matches nothing
318
+ ### Responses before an integration runs
334
319
 
335
320
  | Case | Answer |
336
321
  | --------------------------------------------- | ---------------------------------- |
@@ -340,8 +325,8 @@ handler relying on that behaves differently here for the same reason it does on
340
325
  | No integration, no function, or no permission | 502 `Internal server error` |
341
326
  | The handler threw | 502 `Internal server error` |
342
327
 
343
- `Missing Authentication Token` is the wording real API Gateway is well known for. It answers a path
344
- that matched nothing just as much as one that needed credentials.
328
+ API Gateway uses `Missing Authentication Token` for an unmatched path or method, even when the
329
+ request did not require authentication.
345
330
 
346
331
  ### The invoke permission
347
332
 
@@ -467,9 +452,8 @@ is named `ANY`, whatever method the client sent.
467
452
 
468
453
  Real `CreateStage` carries no method settings. AWS sets them with `UpdateStage` patch operations,
469
454
  which are outside this simulation, or from an `AWS::ApiGateway::Stage`. The `methodSettings` input
470
- above is this simulator's own, so that a test can throttle a stage without a template, and the SDK's
471
- `CreateStageCommand` declares no such member. `GetStage` reports the settings the way AWS reports
472
- them.
455
+ above is specific to Yulin and lets a test configure throttling without a template. The SDK's
456
+ `CreateStageCommand` has no such member. `GetStage` reports the settings in the AWS response shape.
473
457
 
474
458
  ## Authorizing a method
475
459
 
@@ -482,8 +466,8 @@ function answers an IAM policy document, evaluated for `execute-api:Invoke` agai
482
466
  request being made. Whatever `context` it returns reaches the handler.
483
467
 
484
468
  A `REQUEST` authorizer sends the whole request to its function (see
485
- [A REQUEST authorizer](#a-request-authorizer)), so it can identify a caller by several headers
486
- together or by the query string. It answers the same policy document.
469
+ [A REQUEST authorizer](#a-request-authorizer)). It can identify a caller from several headers or
470
+ query parameters. It answers the same policy document.
487
471
 
488
472
  ```typescript sim-apigateway-token-authorizer
489
473
  /**
@@ -674,9 +658,8 @@ left out.
674
658
  | `pathParameters`, `stageVariables` | What the resource path captured, and the stage's variables |
675
659
  | `requestContext` | The same block a handler gets, without the `authorizer` member |
676
660
 
677
- The maps are empty objects where the request supplied nothing. An integration event sends `null`
678
- there, and AWS's own example of the authorizer event sends `{}`, so a function reading
679
- `event.queryStringParameters.plan` finds nothing rather than throwing.
661
+ The maps are empty objects when the request supplies no values. Integration events use `null` for
662
+ the same fields. AWS's authorizer event examples use `{}`.
680
663
 
681
664
  ### Answering with a policy
682
665
 
@@ -710,8 +693,8 @@ authorizer's own logs, and IAM never sees it.
710
693
  ### The context the handler receives
711
694
 
712
695
  `context` reaches the handler under `requestContext.authorizer`, flattened alongside `principalId`.
713
- Payload format 2.0 keeps the context in a block of its own, so a handler moved between a REST API and
714
- an HTTP API reads a different shape.
696
+ Payload format 2.0 keeps the context in its own block. A handler shared with an HTTP API must handle
697
+ both shapes.
715
698
 
716
699
  An open method has no caller to describe, and leaves `requestContext.authorizer` out of the event
717
700
  altogether.
@@ -976,9 +959,8 @@ An admitted caller reaches the handler under `requestContext.identity`.
976
959
  | `user` | The caller's ARN |
977
960
  | `userArn` | The caller's ARN |
978
961
 
979
- Real API Gateway puts the unique id of the principal in `caller` and `user`, such as `AIDA...` for a
980
- User. A request carries no such id into the simulation, so the ARN identifying the caller goes in
981
- every field that can be filled from it.
962
+ Real API Gateway puts the principal's unique ID in `caller` and `user`, such as `AIDA...` for a user.
963
+ The simulation receives only the caller ARN and uses it for each identity field it can populate.
982
964
 
983
965
  A method of any other authorization type leaves those four `null`, and so does an `AWS_IAM` method
984
966
  called by a principal with no ARN behind it. `accessKey`, `apiKey`, `apiKeyId`, `principalOrgId` and
@@ -987,8 +969,8 @@ request itself and are filled for every method.
987
969
 
988
970
  ## Authorizing a method with a user pool
989
971
 
990
- A `COGNITO_USER_POOLS` authorizer verifies the token itself against the keys the user pools it names
991
- publish. Nothing is invoked, so there is no function to write and no policy to answer.
972
+ A `COGNITO_USER_POOLS` authorizer verifies the token against the keys published by its user pools.
973
+ It uses no Lambda function or returned policy.
992
974
  `CreateAuthorizerCommand` takes the pools as `providerARNs`, and `PutMethodCommand` binds the
993
975
  authorizer to a method with `authorizationType: "COGNITO_USER_POOLS"`.
994
976
 
@@ -1118,8 +1100,8 @@ two groups arrive as `[Admins Readers]`.
1118
1100
  }
1119
1101
  ```
1120
1102
 
1121
- An HTTP API puts the same claims under `requestContext.authorizer.jwt.claims`, with the scopes
1122
- beside them, so a handler moved between the two reads a different shape.
1103
+ An HTTP API puts the same claims under `requestContext.authorizer.jwt.claims`, with scopes beside
1104
+ them. A handler shared by both API types must handle both shapes.
1123
1105
 
1124
1106
  ### Scopes
1125
1107
 
@@ -1141,9 +1123,8 @@ token, since an id token carries no `scope` claim at all.
1141
1123
  | A token that has expired, or has no `exp` at all | 401 `Unauthorized` |
1142
1124
  | A verified token claiming none of the method's scopes | 403 `User is not authorized to access this resource` |
1143
1125
 
1144
- Every refusal up to and including the claim checks is the same 401, so a client learns that its
1145
- token was not accepted and nothing about which check it failed. An unmet scope is the one 403: the
1146
- token was accepted, and it does not allow this method.
1126
+ Token parsing and claim failures return the same 401 response. An unmet scope returns 403 because
1127
+ the token was valid but did not allow the method.
1147
1128
 
1148
1129
  The token is taken with or without the `Bearer` scheme in front of it. The identity source is one
1149
1130
  header, as it is for a `TOKEN` authorizer.
@@ -1503,7 +1484,7 @@ AWS sorts what an import finds into three categories, and the third is valid Ope
1503
1484
  leaves unsupported without a request validator. AWS ignores it silently, and so does this:
1504
1485
  `requestBody`, the content schemas under `responses`, `components.schemas`, and an operation's
1505
1486
  `parameters`, `summary`, `description` and `tags`. Request validation is refused at the root of the
1506
- document, so a request whose body contradicts a declared schema still reaches the handler.
1487
+ document. A request body is not checked against the declared schema.
1507
1488
  `operationId` is ignored too, since it only supplies the `OperationName` a method carries for
1508
1489
  documentation.
1509
1490
 
@@ -1902,8 +1883,8 @@ and `Stage`, including the template CDK synthesizes from a `RestApi` or a `Lambd
1902
1883
  that is the form the platform's `Headers` hands over. Real API Gateway reports each separately.
1903
1884
  - A Lambda authorizer's `context` reaches the handler as the authorizer returned it. AWS accepts a
1904
1885
  string, a number or a boolean for each value, and how it renders them is not published.
1905
- - A Cognito authorizer reads a `providerARN` for the pool id it names, so a pool in another account
1906
- is verified against whenever this simulation holds it. `identityValidationExpression`, which real
1886
+ - A Cognito authorizer resolves a `providerARN` by pool ID across the simulated accounts.
1887
+ `identityValidationExpression`, which real
1907
1888
  API Gateway matches a token against before verifying it, is outside this.
1908
1889
  - Binary media types negotiated by `Accept`, CORS preflight and gateway responses are outside this.
1909
1890
  A response body is still base64 decoded when the handler says `isBase64Encoded`.
@@ -1,11 +1,9 @@
1
1
  # Simulated API Gateway HTTP APIs
2
2
 
3
- Yulin includes a simulated API Gateway v2 service, reachable as `simAws.apiGatewayV2()`. It covers
4
- HTTP APIs with a Lambda proxy integration. A handler that runs behind an HTTP API can be tested
5
- against a real HTTP request, with no hand-built event to keep in step.
3
+ Yulin simulates API Gateway HTTP APIs with Lambda proxy integrations. Use
4
+ `simAws.apiGatewayV2()` directly or intercept an `ApiGatewayV2Client`.
6
5
 
7
- This is HTTP APIs only. WebSocket APIs and REST APIs (the older API Gateway v1 service, on a
8
- separate SDK client) are outside it.
6
+ REST APIs use the separate `simAws.apiGateway()` service. WebSocket APIs are unsupported.
9
7
 
10
8
  ## Creating an API
11
9
 
@@ -46,21 +44,19 @@ The endpoint names the API id and the region, as a real one does:
46
44
  https://a1b2c3d4e5.execute-api.eu-west-1.amazonaws.com
47
45
  ```
48
46
 
49
- A name is a label here, as it is on real AWS. Two APIs in one Account and Region may share a name,
50
- and only the id tells them apart.
47
+ An API name is a label. Two APIs in the same account and region may share a name. Use the API ID to
48
+ address it.
51
49
 
52
50
  ## Routing requests to a Lambda function
53
51
 
54
- An API needs three more resources before it serves anything. Those are an integration naming the
55
- function, a route pointing at that integration, and a stage to serve it from. The function also has to allow API
56
- Gateway to invoke it. That grant is a permission on the function, not on the API. See
52
+ An API needs an integration, a route and a stage before it can serve requests. The function must
53
+ also grant API Gateway permission to invoke it. See
57
54
  [Granting the API permission to invoke the function](#granting-the-api-permission-to-invoke-the-function).
58
- Once all of that exists, `serveSimAws` answers requests to the generated endpoint by invoking the
59
- function.
60
55
 
61
- Pass the API endpoint through `srv.localUrl(...)`. It keeps the endpoint's hostname and sends the
62
- request to the local server, the way it adapts simulated S3 website and Lambda Function URL
63
- endpoints.
56
+ `serveSimAws` then routes requests from the generated endpoint to the function.
57
+
58
+ Pass the API endpoint to `srv.localUrl(...)` before making a local HTTP request. The returned URL
59
+ keeps the simulated hostname and points at the local server.
64
60
 
65
61
  ```typescript sim-apigatewayv2-lambda-proxy
66
62
  /**
@@ -152,12 +148,11 @@ Region. It is looked up where its ARN says it is.
152
148
 
153
149
  ## Granting the API permission to invoke the function
154
150
 
155
- A Lambda proxy integration works once the function's resource policy allows
156
- `apigateway.amazonaws.com` to invoke it. The console adds that permission for you. An integration
157
- created through CloudFormation, the CLI or an SDK leaves it to you. That is what
158
- `AddPermissionCommand` in the example above does. Without it the request is answered with a 500 and
159
- `{"message":"Internal Server Error"}`, and the handler never runs. CDK's `HttpLambdaIntegration`
160
- emits the same grant as an `AWS::Lambda::Permission`.
151
+ A Lambda proxy integration requires a resource-based permission for
152
+ `apigateway.amazonaws.com`. Add it with `AddPermissionCommand` or deploy an
153
+ `AWS::Lambda::Permission`. Without the permission, the API returns 500 with
154
+ `{"message":"Internal Server Error"}` and does not invoke the handler. CDK's
155
+ `HttpLambdaIntegration` creates this permission.
161
156
 
162
157
  Each request is authorized as `lambda:InvokeFunction` on the function ARN, with the caller being the
163
158
  service principal `apigateway.amazonaws.com`. The function's own resource policy is the whole
@@ -204,9 +199,8 @@ arn:aws:apigateway:eu-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-
204
199
 
205
200
  A Lambda `REQUEST` authorizer's `AuthorizerUri` takes the same qualifier.
206
201
 
207
- The qualifier is read when the request arrives. A route built on the alias `live` runs whichever
208
- version `live` points at now, and `UpdateAliasCommand` carries the route to another version with the
209
- API untouched. (That is how a deployment behind an HTTP API usually moves on AWS.)
202
+ The qualifier is resolved for each request. A route that uses the `live` alias follows changes made
203
+ by `UpdateAliasCommand` without changing the API.
210
204
 
211
205
  The invoke permission is granted on the qualifier as well. An alias holds a resource policy of its
212
206
  own, and a grant made on the function admits an unqualified call and says nothing about `live`. Pass
@@ -336,8 +330,7 @@ was never created does. The endpoint answers 500 and the handler never runs.
336
330
 
337
331
  ## Route keys
338
332
 
339
- A route key is either the literal `$default` or an upper-case HTTP method and a path separated by one
340
- space:
333
+ A route key is `$default` or an uppercase HTTP method followed by a path:
341
334
 
342
335
  ```text
343
336
  GET /pets
@@ -370,7 +363,7 @@ Gateway refuses it too is unestablished. This is stricter than AWS is known to b
370
363
 
371
364
  ## Which route serves a request
372
365
 
373
- More than one route may match a request, and the most specific one takes it. In order:
366
+ When several routes match, Yulin selects one in this order:
374
367
 
375
368
  1. A route matching the whole path beats a route ending in a greedy parameter, which beats
376
369
  `$default`.
@@ -402,9 +395,8 @@ A request whose path matches a route with a different method matches no route at
402
395
 
403
396
  ## Path parameters and named stages
404
397
 
405
- What a route captured reaches the handler as `event.pathParameters`. A named stage is served under
406
- its own path segment. Stage selection runs before route selection, and the routes never see the stage
407
- name.
398
+ Captured route parameters are available as `event.pathParameters`. A named stage uses the first path
399
+ segment. Yulin removes that segment before selecting a route.
408
400
 
409
401
  ```typescript sim-apigatewayv2-routes
410
402
  /**
@@ -529,16 +521,14 @@ left out the same way when the stage has none.
529
521
 
530
522
  ## Throttling a stage and a route
531
523
 
532
- A stage holds a token bucket for every route it serves. `DefaultRouteSettings` sets the rate and the
533
- burst those buckets start with, and a `RouteSettings` entry keyed by route key overrides them for the
534
- route it names. `ThrottlingRateLimit` is requests per second, and `ThrottlingBurstLimit` is how many
535
- requests a route will take at once.
524
+ Each stage keeps a token bucket per route. `DefaultRouteSettings` sets the default rate and burst.
525
+ `RouteSettings` can override them by route key. `ThrottlingRateLimit` is requests per second and
526
+ `ThrottlingBurstLimit` is the number accepted at once.
536
527
 
537
528
  A request that finds an empty bucket is answered 429 with `{"message":"Too Many Requests"}`. The
538
529
  route's authorizer and its integration are both skipped.
539
530
 
540
- The buckets refill against the simulated clock. Freeze it, spend a route's burst, assert on the 429,
541
- then move a second on and watch the route serve again.
531
+ Buckets refill against the simulated clock. Advance the clock to test recovery after a 429 response.
542
532
 
543
533
  ```typescript sim-apigatewayv2-throttling
544
534
  /**
@@ -681,8 +671,8 @@ are refused by `CreateStage`. A template carrying one deploys, and the member it
681
671
 
682
672
  ## Protecting a route with a Cognito user pool
683
673
 
684
- A JWT authorizer verifies a signed token before the integration is invoked. `CreateAuthorizerCommand`
685
- creates one, and a route asks for it with `AuthorizationType: "JWT"` and the authorizer's id.
674
+ A JWT authorizer verifies a signed token before invoking the integration. Create it with
675
+ `CreateAuthorizerCommand`, then set `AuthorizationType: "JWT"` and `AuthorizerId` on the route.
686
676
 
687
677
  The issuer is a URL. Point it at a [simulated Cognito user pool](https://yulinsim.dev/services/cognito/ "Simulated Cognito docs")
688
678
  and the pool's own signing key verifies the token. A token from `InitiateAuthCommand` or
@@ -852,9 +842,8 @@ console.log(expired.status); // 401
852
842
  await srv.close();
853
843
  ```
854
844
 
855
- The verification is real. The token is parsed, its `alg` has to be `RS256`, its `kid` has to name a
856
- key the issuer publishes, and the signature is checked against that key with `node:crypto`. The whole
857
- check runs in process, with no network fetch and no verification library.
845
+ Yulin parses the token, requires `alg` to be `RS256`, resolves `kid` against the issuer's keys and
846
+ checks the signature with `node:crypto`. Verification runs in process.
858
847
 
859
848
  ### What a refused request gets back
860
849
 
@@ -933,9 +922,8 @@ left out of its events entirely.
933
922
 
934
923
  ## Protecting a route with IAM
935
924
 
936
- A route declared `AuthorizationType: "AWS_IAM"` reaches its integration only when the caller is
937
- allowed `execute-api:Invoke` on the ARN of the route being called. IAM itself decides. The route takes
938
- no authorizer, and naming one is refused.
925
+ A route with `AuthorizationType: "AWS_IAM"` requires `execute-api:Invoke` on the request ARN. The
926
+ route has no separate authorizer.
939
927
 
940
928
  The caller comes from the request, through either a SigV4 signature or an `x-sim-aws-caller` header
941
929
  naming a principal directly. A request offering neither is anonymous, owns no policies, and is
@@ -948,14 +936,13 @@ The ARN a request is authorized against is:
948
936
  arn:aws:execute-api:<region>:<account>:<apiId>/<stage>/<METHOD>/<path>
949
937
  ```
950
938
 
951
- - The Account and Region are the API's own, not the caller's.
939
+ - The account and region identify the API.
952
940
  - The stage is the one that served the request, so `$default` for the default stage.
953
941
  - The method is the one the client sent, upper case. A `GET` reaching a route keyed `ANY /orders`
954
942
  gives `GET`.
955
- - The path is the request path with the stage segment and the leading slash taken off, so
956
- `/dev/orders/42` served from stage `dev` gives `orders/42`. This is the path asked for, not the route
957
- key, because a policy is written against it by hand. A request to the API root gives an ARN ending
958
- `/GET/`.
943
+ - The path is the request path without the stage segment or leading slash. `/dev/orders/42` on stage
944
+ `dev` becomes `orders/42`. It uses the requested path, including resolved parameter values. A
945
+ request to the API root produces an ARN ending in `/GET/`.
959
946
 
960
947
  An identity policy may wildcard any part of that. `<apiId>/*`, `<apiId>/$default/*` and
961
948
  `<apiId>/*/GET/orders/*` all allow a `GET` of `/orders/42` on the default stage.
@@ -1132,13 +1119,13 @@ Account.
1132
1119
 
1133
1120
  ## Protecting a route with a Lambda authorizer
1134
1121
 
1135
- A Lambda `REQUEST` authorizer runs a function of its own before the integration is invoked, and that
1136
- function decides. `CreateAuthorizerCommand` with `AuthorizerType: "REQUEST"` creates one, and a route
1137
- asks for it with `AuthorizationType: "CUSTOM"` and the authorizer's id.
1122
+ A Lambda `REQUEST` authorizer invokes a function before the integration. Create it with
1123
+ `AuthorizerType: "REQUEST"`, then configure the route with `AuthorizationType: "CUSTOM"` and the
1124
+ authorizer ID.
1138
1125
 
1139
- `IdentitySource` is what the request has to carry before the function is invoked at all. Each entry
1140
- is `$request.header.<name>` or `$request.querystring.<name>`, and a request missing any one of them is
1141
- refused without the function running.
1126
+ `IdentitySource` lists values required before invoking the authorizer. Each entry is
1127
+ `$request.header.<name>` or `$request.querystring.<name>`. A missing value returns 401 without running
1128
+ the function.
1142
1129
 
1143
1130
  `EnableSimpleResponses: true` asks the function for `{ isAuthorized, context }`. With it off, the
1144
1131
  function answers a `principalId` and an IAM `policyDocument` instead. Either way, the `context` it
@@ -1357,9 +1344,8 @@ is a different grant from the integration's, and a function used for both needs
1357
1344
 
1358
1345
  ### Caching the authorizer's decision
1359
1346
 
1360
- `AuthorizerResultTtlInSeconds` holds a decision for that many seconds, and a request presenting the
1361
- same identity source values within it is served from that decision without the function running
1362
- again. AWS accepts up to 3600. The default, 0, holds no decision at all.
1347
+ `AuthorizerResultTtlInSeconds` caches a decision by identity source values. AWS accepts values up to
1348
+ 3,600 seconds. The default of zero disables caching.
1363
1349
 
1364
1350
  The key is the identity source values and nothing else, so one decision covers every route of the API
1365
1351
  that uses the authorizer. Adding `$context.routeKey` as an identity source puts the route in the key.
@@ -1517,8 +1503,7 @@ ran.
1517
1503
 
1518
1504
  ## The event the handler receives
1519
1505
 
1520
- The handler is invoked with the API Gateway HTTP API payload format 2.0 event, exported as
1521
- `SimPayload2Event`:
1506
+ The handler receives a payload format 2.0 event, exported as `SimPayload2Event`:
1522
1507
 
1523
1508
  ```json
1524
1509
  {
@@ -1630,9 +1615,8 @@ endpoint, and has
1630
1615
 
1631
1616
  ## The response the handler returns
1632
1617
 
1633
- A handler returning an object with a `statusCode` produces that HTTP response. Its `headers` are
1634
- sent as headers, its `cookies` become `set-cookie` headers, and an empty body is sent as no body,
1635
- leaving a 204 a valid response.
1618
+ A result with `statusCode` becomes the HTTP response. `headers` are sent as response headers,
1619
+ `cookies` become `set-cookie` headers and an omitted body stays empty.
1636
1620
 
1637
1621
  A handler returning anything else produces a 200 whose body is that value as JSON. That includes an
1638
1622
  object with no `statusCode` in it. A handler returning `{ body: "hi" }` produces a 200 whose body is
@@ -1640,8 +1624,8 @@ the JSON `{"body":"hi"}` with `content-type: application/json`. Real API Gateway
1640
1624
 
1641
1625
  ## Reading an API back
1642
1626
 
1643
- `GetApisCommand`, `GetIntegrationsCommand`, `GetRoutesCommand` and `GetStagesCommand` list what an
1644
- API has. Each answers in full, as paging is outside the simulation.
1627
+ `GetApisCommand`, `GetIntegrationsCommand`, `GetRoutesCommand` and `GetStagesCommand` list the API's
1628
+ resources. Each command returns all results because pagination is unsupported.
1645
1629
 
1646
1630
  ```typescript sim-apigatewayv2-list-resources
1647
1631
  /**
@@ -1684,10 +1668,9 @@ console.log(stages.Items[0]?.StageVariables);
1684
1668
 
1685
1669
  ## Deleting what an API has
1686
1670
 
1687
- `DeleteRouteCommand`, `DeleteIntegrationCommand` and `DeleteStageCommand` each take one resource off
1688
- an API and leave the rest of it in place. A deleted route stops matching, and a request that used to
1689
- reach it is answered the way any unmatched request is. A deleted stage stops resolving. A request
1690
- addressed to it finds no stage, while the routes it served are still served by the API's other stages.
1671
+ `DeleteRouteCommand`, `DeleteIntegrationCommand` and `DeleteStageCommand` remove one resource. A
1672
+ deleted route stops matching. Deleting a stage removes its URL while leaving the API's routes
1673
+ available through other stages.
1691
1674
 
1692
1675
  An integration a route still points at cannot be deleted. That is a `BadRequestException` naming the
1693
1676
  routes in the way, as it is on real AWS, and an API comes apart routes first and then the integrations
@@ -1761,8 +1744,7 @@ const integrations = await apiGateway.getIntegrations(
1761
1744
  console.log(routes.Items.length, integrations.Items.length);
1762
1745
  ```
1763
1746
 
1764
- `DeleteApiCommand` deletes the API and everything under it, so taking a whole API away needs none of
1765
- these.
1747
+ `DeleteApiCommand` deletes the API and all of its resources.
1766
1748
 
1767
1749
  ## Turning the generated endpoint off
1768
1750
 
@@ -1774,9 +1756,8 @@ status nor the body for that case, so both are what a disabled endpoint was obse
1774
1756
 
1775
1757
  ## Serving an API on a custom domain name
1776
1758
 
1777
- `CreateDomainNameCommand` creates a domain that answers on a hostname the project owns.
1778
- `CreateApiMappingCommand` points a base path of that domain at one API and one of its stages. A
1779
- domain with no mapping answers 404 to everything.
1759
+ `CreateDomainNameCommand` creates a custom domain. `CreateApiMappingCommand` maps a base path to an
1760
+ API stage. A domain with no mapping returns 404.
1780
1761
 
1781
1762
  An empty `ApiMappingKey` maps the root of the domain. Every request reaching the domain goes to that
1782
1763
  API, with the path as the client sent it. A non-empty key is one or more path segments (`orders`,