@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.
- package/README.md +22 -2
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-properties.js +2 -0
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.js +1 -1
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.d.ts +2 -0
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.js +4 -0
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-refusals.js +0 -5
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping.handler.d.ts +2 -0
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping.handler.js +2 -0
- package/dist/service/lambda/command/event-source-mapping/sim-lambda-event-source-mapping-commands.d.ts +2 -0
- package/dist/service/lambda/destination/sim-lambda-destination-targets.d.ts +2 -1
- package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-shard-poller.d.ts +0 -4
- package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-shard-poller.js +10 -15
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event-source-poller.d.ts +1 -6
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event-source-poller.js +9 -27
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-polling.d.ts +19 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-polling.js +27 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-event-source-function.d.ts +1 -1
- package/dist/service/lambda/event-source/poll/sim-lambda-event-source-function.js +3 -1
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-age.d.ts +13 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-age.js +27 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-info.d.ts +15 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-info.js +26 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-outcome.d.ts +1 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-outcome.js +7 -1
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-processing.d.ts +17 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-processing.js +12 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-cursor.d.ts +15 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-cursor.js +28 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-expiry.d.ts +19 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-expiry.js +23 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-context.d.ts +3 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-context.js +19 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-destination.d.ts +15 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-destination.js +38 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-record.d.ts +41 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-record.js +17 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress-state.d.ts +21 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress-state.js +39 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress.d.ts +7 -37
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress.js +22 -62
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-retry.d.ts +4 -39
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-retry.js +12 -64
- package/dist/service/lambda/event-source/sim-lambda-event-source-mapping.d.ts +10 -3
- package/dist/service/lambda/event-source/sim-lambda-event-source-mapping.js +12 -3
- package/dist/service/lambda/event-source/sim-lambda-stream-destination-config.d.ts +7 -0
- package/dist/service/lambda/event-source/sim-lambda-stream-destination-config.js +38 -0
- package/dist/service/lambda/event-source/stream/kinesis/sim-kinesis-event-source-streams.js +1 -0
- package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-reader.js +1 -0
- package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-shard.d.ts +1 -0
- package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-shard.js +7 -0
- package/dist/service/lambda/event-source/stream/sim-lambda-event-source-streams.d.ts +1 -0
- package/dist/service/lambda/index.d.ts +1 -0
- package/dist/service/lambda/sim-lambda-commands.js +1 -0
- package/dist/service/ses/command/authorize/sim-ses-authorizer.d.ts +9 -0
- package/dist/service/ses/command/authorize/sim-ses-authorizer.js +18 -2
- package/dist/service/ses/command/send/sim-ses-send-email.js +2 -1
- package/docs/README.md +7 -0
- package/docs/ai-skill/README.md +6 -1
- package/docs/sdk/README.md +15 -0
- package/docs/services/acm/README.md +24 -40
- package/docs/services/apigateway/README.md +52 -71
- package/docs/services/apigatewayv2/README.md +55 -74
- package/docs/services/athena/README.md +17 -26
- package/docs/services/backup/README.md +29 -39
- package/docs/services/bedrock/README.md +38 -52
- package/docs/services/cloudformation/README.md +43 -55
- package/docs/services/cloudfront/README.md +69 -95
- package/docs/services/cloudwatch/README.md +40 -54
- package/docs/services/cognito/README.md +30 -45
- package/docs/services/dynamodb/README.md +34 -51
- package/docs/services/ecr/README.md +36 -77
- package/docs/services/ecs/README.md +26 -46
- package/docs/services/elbv2/README.md +19 -29
- package/docs/services/eventbridge/README.md +14 -18
- package/docs/services/firehose/README.md +24 -32
- package/docs/services/glue/README.md +41 -78
- package/docs/services/iam/README.md +13 -15
- package/docs/services/kinesis/README.md +53 -93
- package/docs/services/kms/README.md +22 -32
- package/docs/services/lambda/README.md +112 -85
- package/docs/services/logs/README.md +41 -50
- package/docs/services/organizations/README.md +50 -85
- package/docs/services/personalize/README.md +28 -44
- package/docs/services/rekognition/README.md +26 -38
- package/docs/services/route53/README.md +17 -17
- package/docs/services/s3/README.md +47 -51
- package/docs/services/scheduler/README.md +41 -52
- package/docs/services/secretsmanager/README.md +27 -42
- package/docs/services/ses/README.md +24 -34
- package/docs/services/sns/README.md +18 -26
- package/docs/services/sqs/README.md +14 -14
- package/docs/services/ssm/README.md +13 -17
- package/docs/services/stepfunctions/README.md +18 -20
- package/docs/services/sts/README.md +32 -45
- package/docs/services/wafv2/README.md +12 -17
- package/docs/testing/README.md +228 -0
- package/docs/time/README.md +10 -0
- package/llms.txt +1 -0
- package/package.json +1 -1
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
# Simulated API Gateway REST APIs
|
|
2
2
|
|
|
3
|
-
Yulin
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
9
|
-
[API Gateway HTTP APIs](https://yulinsim.dev/services/apigatewayv2/).
|
|
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
|
|
52
|
-
|
|
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
|
|
57
|
-
segment
|
|
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
|
|
107
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
188
|
-
|
|
189
|
-
|
|
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
|
|
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
|
-
|
|
253
|
-
|
|
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
|
|
260
|
-
|
|
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`
|
|
304
|
-
invoke permission and
|
|
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.
|
|
310
|
-
|
|
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
|
-
|
|
323
|
-
|
|
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
|
|
328
|
-
|
|
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
|
-
###
|
|
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`
|
|
344
|
-
|
|
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
|
|
471
|
-
`CreateStageCommand`
|
|
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))
|
|
486
|
-
|
|
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
|
|
678
|
-
|
|
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
|
|
714
|
-
|
|
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
|
|
980
|
-
|
|
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
|
|
991
|
-
|
|
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
|
|
1122
|
-
|
|
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
|
-
|
|
1145
|
-
token was
|
|
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
|
|
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
|
|
1906
|
-
|
|
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
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
|
55
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
|
156
|
-
`apigateway.amazonaws.com
|
|
157
|
-
|
|
158
|
-
`
|
|
159
|
-
`
|
|
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
|
|
208
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
406
|
-
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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
|
-
|
|
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
|
|
685
|
-
|
|
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
|
-
|
|
856
|
-
|
|
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
|
|
937
|
-
|
|
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
|
|
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
|
|
956
|
-
|
|
957
|
-
|
|
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
|
|
1136
|
-
|
|
1137
|
-
|
|
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`
|
|
1140
|
-
|
|
1141
|
-
|
|
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`
|
|
1361
|
-
|
|
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
|
|
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
|
|
1634
|
-
|
|
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
|
|
1644
|
-
|
|
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`
|
|
1688
|
-
|
|
1689
|
-
|
|
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
|
|
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
|
|
1778
|
-
|
|
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`,
|