@kensio/yulin 1.21.6 → 1.21.8
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/eventbridge/cfn/bus/sim-cfn-event-bus-creator.js +1 -0
- package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-properties.d.ts +4 -0
- package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-properties.js +26 -1
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-creator.js +3 -1
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-properties.d.ts +0 -7
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-properties.js +0 -20
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-unsimulated-properties.d.ts +13 -0
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-unsimulated-properties.js +59 -0
- package/dist/service/eventbridge/cfn/sim-cfn-event-bridge-resource-error.d.ts +1 -1
- package/dist/service/eventbridge/cfn/sim-cfn-event-bridge-resource-error.js +1 -1
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-properties.js +1 -1
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.d.ts +3 -2
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.js +25 -3
- 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/dist/service/sns/cfn/topic/sim-cfn-sns-topic-creator.js +1 -0
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-properties.d.ts +9 -4
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-properties.js +18 -6
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-property-names.d.ts +10 -0
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-property-names.js +16 -1
- package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-creator.js +1 -1
- package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-properties.d.ts +11 -6
- package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-properties.js +18 -9
- package/docs/README.md +40 -4
- package/docs/ai-skill/README.md +57 -54
- package/docs/cli/README.md +84 -94
- package/docs/factories/README.md +42 -54
- package/docs/lint/README.md +41 -67
- package/docs/non-aws-dependencies/README.md +72 -168
- package/docs/sdk/README.md +109 -95
- package/docs/serve/README.md +192 -898
- 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 +24 -20
- 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 +60 -80
- 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 +31 -33
- package/docs/services/sqs/README.md +14 -14
- package/docs/services/ssm/README.md +19 -21
- 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/terraform/README.md +108 -126
- package/docs/testing/README.md +228 -0
- package/docs/time/README.md +87 -117
- package/llms.txt +2 -1
- package/package.json +1 -1
|
@@ -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`,
|
|
@@ -1,22 +1,15 @@
|
|
|
1
1
|
# Simulated Athena
|
|
2
2
|
|
|
3
|
-
Yulin
|
|
4
|
-
|
|
3
|
+
Yulin simulates Amazon Athena workgroups, named queries and query execution for tests and local
|
|
4
|
+
development. Tests can supply a result for a particular SQL statement, or enable the optional
|
|
5
|
+
[query engine](#running-a-query-for-real) to run SQL against objects in simulated S3.
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
project.
|
|
7
|
+
Both paths use the same query lifecycle. Athena checks tables in the simulated
|
|
8
|
+
[Glue Data Catalog](https://yulinsim.dev/services/glue/ "Simulated Glue usage docs"), applies the
|
|
9
|
+
workgroup's bytes-scanned limit and writes results to the configured S3 location. This lets client
|
|
10
|
+
code start a query, poll its status and read its results through the AWS SDK.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
refuses a query, that results land where the workgroup says, and that a client polls the lifecycle
|
|
14
|
-
correctly. The tables a query names are looked for in the simulated
|
|
15
|
-
[Glue Data Catalog](https://yulinsim.dev/services/glue/ "Simulated Glue usage docs"), and a query
|
|
16
|
-
naming one that is absent fails the way real Athena fails it. The
|
|
17
|
-
[Limitations](#limitations) at the end say what this leaves out.
|
|
18
|
-
|
|
19
|
-
Athena-specific types are imported from the `@kensio/yulin/athena` subpath.
|
|
12
|
+
Import Athena-specific types from `@kensio/yulin/athena`.
|
|
20
13
|
|
|
21
14
|
## Workgroups from a template
|
|
22
15
|
|
|
@@ -177,23 +170,21 @@ const results = await simAws.athena().getQueryResults({
|
|
|
177
170
|
console.log(results.ResultSet?.Rows?.[1]?.Data?.[1]?.VarCharValue);
|
|
178
171
|
```
|
|
179
172
|
|
|
180
|
-
A
|
|
181
|
-
|
|
182
|
-
|
|
173
|
+
A result declared for an exact query takes precedence over a workgroup result, which takes
|
|
174
|
+
precedence over the default result. Use `onWorkGroup` to cover every query in one workgroup and
|
|
175
|
+
`byDefault` as the fallback. Exact matching includes whitespace.
|
|
183
176
|
|
|
184
177
|
The query engine sits between the two tiers. A rule for an exact query is ahead of it and the
|
|
185
178
|
workgroup rule and the default are behind it.
|
|
186
179
|
|
|
187
|
-
`failsWith`
|
|
188
|
-
|
|
189
|
-
reachable.
|
|
180
|
+
Use `failsWith` to declare a failed query and test the client's error handling. Declared results do
|
|
181
|
+
not parse the SQL, so failures must be configured explicitly.
|
|
190
182
|
|
|
191
183
|
## Running a query for real
|
|
192
184
|
|
|
193
|
-
The query engine
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
nineteen queries in twenty of the shapes a test writes run this way.
|
|
185
|
+
The query engine runs a `SELECT` against objects seeded into simulated S3. It reads the table schema
|
|
186
|
+
from the Glue Data Catalog, decodes each object with the table's SerDe, loads the rows into an
|
|
187
|
+
in-memory SQLite database and executes the statement.
|
|
197
188
|
|
|
198
189
|
The engine is off until a test turns it on, and it needs `node-sql-parser` in the project. The
|
|
199
190
|
parser is an optional peer dependency, so a project that never runs a query never installs it. A
|
|
@@ -965,7 +956,7 @@ Every command is authorized against the workgroup ARN,
|
|
|
965
956
|
own and authorizes work on one against the workgroup it belongs to. This asks the same question.
|
|
966
957
|
`ListWorkGroups` names no workgroup, so IAM evaluates it against `*`.
|
|
967
958
|
|
|
968
|
-
##
|
|
959
|
+
## Supported operations
|
|
969
960
|
|
|
970
961
|
- Query executions, moving through `QUEUED` and `RUNNING` to `SUCCEEDED`, `FAILED` or `CANCELLED`
|
|
971
962
|
- `StartQueryExecution`, `GetQueryExecution`, `GetQueryResults` and `StopQueryExecution`
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Simulated AWS Backup
|
|
2
2
|
|
|
3
|
-
Yulin
|
|
4
|
-
|
|
3
|
+
Yulin simulates AWS Backup vaults, plans, selections, jobs and recovery points. Import Backup types
|
|
4
|
+
from `@kensio/yulin/backup`.
|
|
5
5
|
|
|
6
6
|
## Creating a vault, plan and selection
|
|
7
7
|
|
|
8
|
-
`simAws.backup()`
|
|
9
|
-
|
|
8
|
+
Use `simAws.backup()` for the default account and Region. Each plan rule names an existing vault. A
|
|
9
|
+
selection assigns resource ARNs to a plan.
|
|
10
10
|
|
|
11
11
|
```typescript sim-backup-create-plan-selection
|
|
12
12
|
/**
|
|
@@ -78,19 +78,17 @@ console.log(selection.BackupSelection?.Resources);
|
|
|
78
78
|
// ["arn:aws:dynamodb:us-east-1:888888888888:table/orders"]
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
A plan needs at least one
|
|
82
|
-
`cron(0 5 ? * * *)
|
|
83
|
-
|
|
81
|
+
A plan needs at least one named rule and target vault. The default schedule is
|
|
82
|
+
`cron(0 5 ? * * *)`. Yulin accepts six-field AWS cron expressions and rate expressions. It rejects
|
|
83
|
+
`at(...)` expressions.
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
`InvalidParameterValueException`. Set both values to `-1` to retain recovery points indefinitely.
|
|
85
|
+
When both lifecycle values are set, `DeleteAfterDays` must be at least 90 days after
|
|
86
|
+
`MoveToColdStorageAfterDays`. Use `-1` for both values to keep recovery points indefinitely.
|
|
88
87
|
|
|
89
88
|
## Running scheduled backups
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
resource ARN in
|
|
93
|
-
and creation time. The simulation completes backup jobs at the scheduled instant.
|
|
90
|
+
Plan rules run on the simulated clock. A due rule creates one completed job and recovery point for
|
|
91
|
+
each distinct resource ARN in its selections.
|
|
94
92
|
|
|
95
93
|
```typescript sim-backup-run-schedule
|
|
96
94
|
/**
|
|
@@ -161,19 +159,16 @@ console.log(points.RecoveryPoints?.[0]?.ResourceArn);
|
|
|
161
159
|
console.log(jobs.BackupJobs?.[0]?.State); // "COMPLETED"
|
|
162
160
|
```
|
|
163
161
|
|
|
164
|
-
`DeleteAfterDays` removes a recovery point when
|
|
165
|
-
|
|
166
|
-
Repeated schedules keep every unexpired recovery point.
|
|
162
|
+
`DeleteAfterDays` removes a recovery point when simulated time reaches its expiry. Recovery point
|
|
163
|
+
reads apply expiry before returning.
|
|
167
164
|
|
|
168
|
-
Vault Lock bounds
|
|
169
|
-
|
|
170
|
-
vault receives no recovery point for that job. Use `ListBackupJobs` or `DescribeBackupJob` to read
|
|
171
|
-
the failure and its `StatusMessage`.
|
|
165
|
+
Vault Lock bounds are checked when a backup starts. An invalid lifecycle produces a `FAILED` job and
|
|
166
|
+
no recovery point. Read the reason from the job's `StatusMessage`.
|
|
172
167
|
|
|
173
168
|
## Starting an on-demand backup
|
|
174
169
|
|
|
175
|
-
`StartBackupJob` completes
|
|
176
|
-
|
|
170
|
+
`StartBackupJob` completes at the current simulated time. It applies the same lifecycle and Vault
|
|
171
|
+
Lock checks as a scheduled job.
|
|
177
172
|
|
|
178
173
|
```typescript sim-backup-start-job
|
|
179
174
|
/**
|
|
@@ -219,9 +214,8 @@ console.log(point.CreationDate?.toISOString());
|
|
|
219
214
|
|
|
220
215
|
## Vault Lock
|
|
221
216
|
|
|
222
|
-
`PutBackupVaultLockConfiguration`
|
|
223
|
-
|
|
224
|
-
period ends, then becomes immutable.
|
|
217
|
+
`PutBackupVaultLockConfiguration` sets minimum and maximum retention. `ChangeableForDays` adds a
|
|
218
|
+
grace period. The configuration becomes immutable when that period ends.
|
|
225
219
|
|
|
226
220
|
```typescript sim-backup-vault-lock
|
|
227
221
|
/**
|
|
@@ -284,8 +278,7 @@ exceed the maximum, and the maximum cannot exceed 36,500 days. A lock without
|
|
|
284
278
|
## Deploying from CloudFormation
|
|
285
279
|
|
|
286
280
|
Simulated CloudFormation deploys `AWS::Backup::BackupVault`, `AWS::Backup::BackupPlan` and
|
|
287
|
-
`AWS::Backup::BackupSelection`.
|
|
288
|
-
them.
|
|
281
|
+
`AWS::Backup::BackupSelection`.
|
|
289
282
|
|
|
290
283
|
```typescript sim-backup-cloudformation
|
|
291
284
|
/**
|
|
@@ -376,10 +369,9 @@ Stack teardown removes all three resource types from the simulation.
|
|
|
376
369
|
|
|
377
370
|
## Permissions
|
|
378
371
|
|
|
379
|
-
Every supported operation
|
|
380
|
-
and selection operations use the plan ARN. `
|
|
381
|
-
|
|
382
|
-
`DescribeRecoveryPoint` uses the recovery point ARN. Backup job reads use `*`.
|
|
372
|
+
Every supported operation uses simulated IAM. Vault operations authorize against the vault ARN.
|
|
373
|
+
Plan and selection operations use the plan ARN. `DescribeRecoveryPoint` uses the recovery point ARN.
|
|
374
|
+
List and backup job read operations that name no resource use `*`.
|
|
383
375
|
|
|
384
376
|
```typescript sim-backup-iam-policy
|
|
385
377
|
/**
|
|
@@ -432,13 +424,12 @@ const created = await simAws.backup().createBackupVault(
|
|
|
432
424
|
console.log(created.BackupVaultName); // "application-backups"
|
|
433
425
|
```
|
|
434
426
|
|
|
435
|
-
Authorization runs before resource lookup. An unauthorized request for a missing
|
|
436
|
-
|
|
427
|
+
Authorization runs before resource lookup. An unauthorized request for a missing resource raises
|
|
428
|
+
`AccessDeniedException`.
|
|
437
429
|
|
|
438
430
|
## SDK interception
|
|
439
431
|
|
|
440
|
-
`
|
|
441
|
-
when a test owns it, or intercept the class when application code creates the client.
|
|
432
|
+
Intercept a `BackupClient` instance or the client class to route SDK commands to Yulin.
|
|
442
433
|
|
|
443
434
|
```typescript sim-backup-sdk-interception
|
|
444
435
|
/**
|
|
@@ -466,13 +457,12 @@ console.log(listed.BackupVaultList?.[0]?.BackupVaultName);
|
|
|
466
457
|
// "application-backups"
|
|
467
458
|
```
|
|
468
459
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
for class interception and credential handling.
|
|
460
|
+
Client Region and credentials select the simulated scope and caller. See
|
|
461
|
+
[SDK interception](https://yulinsim.dev/sdk/) for details.
|
|
472
462
|
|
|
473
463
|
## Account and Region scoping
|
|
474
464
|
|
|
475
|
-
|
|
465
|
+
Backup state is scoped by account and Region. The same vault name can exist in another scope.
|
|
476
466
|
|
|
477
467
|
```typescript sim-backup-account-region-scoping
|
|
478
468
|
/**
|