@kensio/yulin 1.21.10 → 1.21.11

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 (52) hide show
  1. package/dist/serve/payload-2/sim-payload-2-endpoint.d.ts +9 -0
  2. package/dist/serve/payload-2/sim-payload-2-request-context.js +1 -1
  3. package/dist/service/apigatewayv2/api/sim-http-api-lambda-proxy.factory.d.ts +6 -0
  4. package/dist/service/apigatewayv2/api/sim-http-api-lambda-proxy.factory.js +2 -0
  5. package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-format.d.ts +8 -0
  6. package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-format.js +36 -0
  7. package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-request.d.ts +53 -0
  8. package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-request.js +1 -0
  9. package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-settings.d.ts +26 -0
  10. package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-settings.js +44 -0
  11. package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-settings.type.d.ts +21 -0
  12. package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-settings.type.js +1 -0
  13. package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-stream-name.d.ts +13 -0
  14. package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-stream-name.js +23 -0
  15. package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-variables.d.ts +17 -0
  16. package/dist/service/apigatewayv2/api/stage/access-log/sim-http-api-access-log-variables.js +86 -0
  17. package/dist/service/apigatewayv2/api/stage/sim-http-api-stage.d.ts +9 -0
  18. package/dist/service/apigatewayv2/api/stage/sim-http-api-stage.js +9 -0
  19. package/dist/service/apigatewayv2/cfn/stage/sim-cfn-http-api-access-log-settings-properties.d.ts +24 -0
  20. package/dist/service/apigatewayv2/cfn/stage/sim-cfn-http-api-access-log-settings-properties.js +30 -0
  21. package/dist/service/apigatewayv2/cfn/stage/sim-cfn-http-api-stage-properties.d.ts +1 -0
  22. package/dist/service/apigatewayv2/cfn/stage/sim-cfn-http-api-stage-properties.js +10 -4
  23. package/dist/service/apigatewayv2/command/stage/sim-http-api-access-log-settings-input.d.ts +15 -0
  24. package/dist/service/apigatewayv2/command/stage/sim-http-api-access-log-settings-input.js +33 -0
  25. package/dist/service/apigatewayv2/command/stage/sim-http-api-stage-commands.js +4 -0
  26. package/dist/service/apigatewayv2/command/stage/stage.command.d.ts +2 -0
  27. package/dist/service/apigatewayv2/serve/auth/sim-http-api-route-authorize-input.d.ts +6 -0
  28. package/dist/service/apigatewayv2/serve/sim-api-gateway-v2-controller.d.ts +13 -15
  29. package/dist/service/apigatewayv2/serve/sim-api-gateway-v2-controller.js +26 -60
  30. package/dist/service/apigatewayv2/serve/sim-http-api-access-log-record.d.ts +31 -0
  31. package/dist/service/apigatewayv2/serve/sim-http-api-access-log-record.js +88 -0
  32. package/dist/service/apigatewayv2/serve/sim-http-api-access-log.d.ts +43 -0
  33. package/dist/service/apigatewayv2/serve/sim-http-api-access-log.js +72 -0
  34. package/dist/service/apigatewayv2/serve/sim-http-api-endpoint.js +1 -0
  35. package/dist/service/apigatewayv2/serve/sim-http-api-integration-input.d.ts +22 -0
  36. package/dist/service/apigatewayv2/serve/sim-http-api-integration-input.js +1 -0
  37. package/dist/service/apigatewayv2/serve/sim-http-api-integration-invocation.d.ts +5 -22
  38. package/dist/service/apigatewayv2/serve/sim-http-api-integration-invocation.js +17 -20
  39. package/dist/service/apigatewayv2/serve/sim-http-api-integration-outcome.d.ts +27 -0
  40. package/dist/service/apigatewayv2/serve/sim-http-api-integration-outcome.js +13 -0
  41. package/dist/service/apigatewayv2/serve/sim-http-api-may-invoke.d.ts +19 -0
  42. package/dist/service/apigatewayv2/serve/sim-http-api-may-invoke.js +19 -0
  43. package/dist/service/apigatewayv2/serve/sim-http-api-serve-pipeline.d.ts +27 -0
  44. package/dist/service/apigatewayv2/serve/sim-http-api-serve-pipeline.js +75 -0
  45. package/dist/service/apigatewayv2/serve/sim-http-api-served.d.ts +22 -0
  46. package/dist/service/apigatewayv2/serve/sim-http-api-served.js +1 -0
  47. package/dist/service/apigatewayv2/serve/sim-http-api-serving.d.ts +12 -0
  48. package/dist/service/apigatewayv2/serve/sim-http-api-serving.js +1 -0
  49. package/dist/service/logs/group/sim-logs-arn.d.ts +15 -0
  50. package/dist/service/logs/group/sim-logs-arn.js +33 -0
  51. package/docs/services/apigatewayv2/README.md +162 -6
  52. package/package.json +1 -1
@@ -0,0 +1,19 @@
1
+ import type { SimHttpApiMatch } from "../api/sim-http-api-match.js";
2
+ import type { SimHttpApi } from "../api/sim-http-api.js";
3
+ import type { SimHttpApiFunctionTarget } from "./sim-http-api-function-target.js";
4
+ interface SimHttpApiMayInvokeInput {
5
+ readonly api: SimHttpApi;
6
+ readonly match: SimHttpApiMatch;
7
+ readonly request: Request;
8
+ }
9
+ /**
10
+ * Whether the API lacks permission to invoke a function behind one of its
11
+ * routes.
12
+ *
13
+ * The route the request matched is supplied as the source ARN, so a permission
14
+ * granted for one route does not open another. This is the API's own question
15
+ * rather than the client's, whose was already answered by the route's
16
+ * authorization.
17
+ */
18
+ export declare function simHttpApiMayNotInvoke(input: SimHttpApiMayInvokeInput, target: SimHttpApiFunctionTarget): boolean;
19
+ export {};
@@ -0,0 +1,19 @@
1
+ import { SimHttpApiExecuteApiArn } from "../api/sim-http-api-execute-api-arn.js";
2
+ import { SimHttpApiInvokeAuthorizer } from "./auth/sim-http-api-invoke-authorizer.js";
3
+ /**
4
+ * Whether the API lacks permission to invoke a function behind one of its
5
+ * routes.
6
+ *
7
+ * The route the request matched is supplied as the source ARN, so a permission
8
+ * granted for one route does not open another. This is the API's own question
9
+ * rather than the client's, whose was already answered by the route's
10
+ * authorization.
11
+ */
12
+ export function simHttpApiMayNotInvoke(input, target) {
13
+ const { api, match, request } = input;
14
+ return new SimHttpApiInvokeAuthorizer({ iam: target.iam }).authorize({
15
+ api,
16
+ target,
17
+ sourceArn: SimHttpApiExecuteApiArn.forMatchedRoute(api, match, request.method),
18
+ }).isDenied;
19
+ }
@@ -0,0 +1,27 @@
1
+ import type { SimAwsServiceRequest } from "../../../serve/controller/sim-service-controller.js";
2
+ import type { SimApiGatewayV2Router } from "./sim-api-gateway-v2-router.js";
3
+ import type { SimHttpApiServed, SimHttpApiServedRequest } from "./sim-http-api-served.js";
4
+ interface SimHttpApiServePipelineProperties {
5
+ readonly router: SimApiGatewayV2Router;
6
+ }
7
+ /**
8
+ * Takes one request the resolver matched to a stage through the three things
9
+ * that stand between it and a response.
10
+ *
11
+ * The stage's throttle is asked first. A flood of requests to a throttled
12
+ * route invokes neither the authorizer nor the integration, and AWS publishes
13
+ * no order between the two. The client's own authorization comes next, so a
14
+ * request with no credentials is refused whether or not the integration
15
+ * behind the route would work. What each step decided is carried out with the
16
+ * response, because the stage's access log describes the request afterwards
17
+ * and a refused request is the case that log exists for.
18
+ */
19
+ export declare class SimHttpApiServePipeline {
20
+ #private;
21
+ constructor(properties: SimHttpApiServePipelineProperties);
22
+ /**
23
+ * Serve one request, and report what each step of serving it decided.
24
+ */
25
+ run(serving: SimHttpApiServedRequest, serviceRequest: SimAwsServiceRequest): Promise<SimHttpApiServed>;
26
+ }
27
+ export {};
@@ -0,0 +1,75 @@
1
+ import { SimHttpApiRouteAuthorizer } from "./auth/sim-http-api-route-authorizer.js";
2
+ import { SimApiGatewayV2ErrorResponse } from "./sim-api-gateway-v2-error-response.js";
3
+ import { SimHttpApiIntegrationInvocation } from "./sim-http-api-integration-invocation.js";
4
+ import { SimHttpApiRefusalResponse } from "./sim-http-api-refusal-response.js";
5
+ /**
6
+ * Takes one request the resolver matched to a stage through the three things
7
+ * that stand between it and a response.
8
+ *
9
+ * The stage's throttle is asked first. A flood of requests to a throttled
10
+ * route invokes neither the authorizer nor the integration, and AWS publishes
11
+ * no order between the two. The client's own authorization comes next, so a
12
+ * request with no credentials is refused whether or not the integration
13
+ * behind the route would work. What each step decided is carried out with the
14
+ * response, because the stage's access log describes the request afterwards
15
+ * and a refused request is the case that log exists for.
16
+ */
17
+ export class SimHttpApiServePipeline {
18
+ #router;
19
+ #routeAuthorizer;
20
+ #integration;
21
+ #errorResponse = new SimApiGatewayV2ErrorResponse();
22
+ #refusalResponse = new SimHttpApiRefusalResponse();
23
+ constructor(properties) {
24
+ this.#router = properties.router;
25
+ // The clock is the router's, so the event timestamps and the token expiry
26
+ // checks belong to the same simulation.
27
+ this.#routeAuthorizer = new SimHttpApiRouteAuthorizer({
28
+ clock: properties.router.simAws,
29
+ functions: properties.router,
30
+ });
31
+ this.#integration = new SimHttpApiIntegrationInvocation({
32
+ router: properties.router,
33
+ clock: properties.router.simAws,
34
+ });
35
+ }
36
+ /**
37
+ * Serve one request, and report what each step of serving it decided.
38
+ */
39
+ async run(serving, serviceRequest) {
40
+ const { request } = serviceRequest;
41
+ const { api, match, domainName, rawPath, requestId } = serving;
42
+ // What the authorizer's event and the integration's event both describe.
43
+ const asked = { api, match, request, domainName, rawPath, requestId };
44
+ // The throttle takes a token from the route's bucket, and a route whose
45
+ // bucket is empty is answered here. Neither the authorizer nor the
46
+ // integration runs for it, and the stage's access log is the only place
47
+ // the refusal is recorded.
48
+ if (!match.stage.admits(match.route.routeKey)) {
49
+ return { response: this.#errorResponse.tooManyRequests() };
50
+ }
51
+ const authorization = await this.#routeAuthorizer.authorize({
52
+ ...asked,
53
+ caller: serviceRequest.caller,
54
+ iam: this.#router.iamFor(api),
55
+ });
56
+ // A refusal is carried out alongside its response, because the access log
57
+ // line reports what the authorizer said. An unmet route scope, an
58
+ // `AWS_IAM` route IAM did not allow, and a Lambda authorizer that said no
59
+ // all arrive here.
60
+ if (!authorization.admitted) {
61
+ return {
62
+ response: this.#refusalResponse.build(authorization),
63
+ authorization,
64
+ };
65
+ }
66
+ const clock = this.#router.simAws;
67
+ const invokedAt = clock.now().getTime();
68
+ const outcome = await this.#integration.invoke({ ...asked, authorization });
69
+ const integration = {
70
+ ...outcome,
71
+ integrationLatency: clock.now().getTime() - invokedAt,
72
+ };
73
+ return { response: integration.response, authorization, integration };
74
+ }
75
+ }
@@ -0,0 +1,22 @@
1
+ import type { SimHttpApiAuthorization } from "../api/authorizer/sim-http-api-authorization.js";
2
+ import type { SimHttpApiIntegrationOutcome } from "./sim-http-api-integration-outcome.js";
3
+ import type { SimHttpApiServing } from "./sim-http-api-serving.js";
4
+ /**
5
+ * What one request through the endpoint produced, gathered as it happened so
6
+ * that the stage's access log can describe it afterwards.
7
+ *
8
+ * A request the stage's throttle refused has neither an authorization nor an
9
+ * integration, and one an authorizer refused has an authorization alone.
10
+ */
11
+ export interface SimHttpApiServed {
12
+ readonly response: Response;
13
+ readonly authorization?: SimHttpApiAuthorization | undefined;
14
+ readonly integration?: SimHttpApiIntegrationOutcome | undefined;
15
+ }
16
+ /**
17
+ * A serving the controller has stamped with the id this request is logged and
18
+ * reported under, which every path below it then names.
19
+ */
20
+ export interface SimHttpApiServedRequest extends SimHttpApiServing {
21
+ readonly requestId: string;
22
+ }
@@ -21,6 +21,18 @@ export interface SimHttpApiServing {
21
21
  * carrying.
22
22
  */
23
23
  readonly rawPath: string;
24
+ /**
25
+ * The id this request is logged and reported under, settled by the
26
+ * controller before anything runs. The resolver leaves it out, and the
27
+ * controller adds it to the serving it passes on.
28
+ */
29
+ readonly requestId?: string | undefined;
30
+ /**
31
+ * The API mapping base path this request matched, for a request that
32
+ * reached a custom domain. It is the empty string for a mapping serving the
33
+ * root of its domain, and absent for the generated endpoint.
34
+ */
35
+ readonly basePathMatched?: string | undefined;
24
36
  }
25
37
  /**
26
38
  * What an HTTP API endpoint decided about one request before any of it ran.
@@ -107,6 +107,7 @@ export class SimHttpApiServingResolver {
107
107
  match,
108
108
  domainName: domain.domainName,
109
109
  rawPath: mapping.apiMappingKey.remainingPath(path),
110
+ basePathMatched: mapping.apiMappingKey.value,
110
111
  },
111
112
  };
112
113
  }
@@ -33,3 +33,18 @@ export declare function simLogsAnyLogGroupArn(scope: SimAwsAccountRegionScope):
33
33
  * The ARN of a log stream inside a log group.
34
34
  */
35
35
  export declare function simLogsLogStreamArn(scope: SimAwsAccountRegionScope, logGroupName: string, logStreamName: string): string;
36
+ /**
37
+ * The account, region and group name a log group ARN carries, or undefined
38
+ * for a string that is not one.
39
+ *
40
+ * Both forms are read. `DescribeLogGroups` reports `logGroupArn` without a
41
+ * trailing wildcard, while the `arn` field and CDK's `logGroup.logGroupArn`
42
+ * both end in `:*`, and a template naming either means the same group. A log
43
+ * group name cannot contain a colon, so what follows `log-group:` is the name
44
+ * up to an optional trailing `:*`.
45
+ */
46
+ export declare function simLogsParsedLogGroupArn(arn: string): {
47
+ readonly accountId: string;
48
+ readonly regionName: string;
49
+ readonly logGroupName: string;
50
+ } | undefined;
@@ -42,3 +42,36 @@ export function simLogsAnyLogGroupArn(scope) {
42
42
  export function simLogsLogStreamArn(scope, logGroupName, logStreamName) {
43
43
  return `${simLogsLogGroupArn(scope, logGroupName)}:log-stream:${logStreamName}`;
44
44
  }
45
+ /**
46
+ * The account, region and group name a log group ARN carries, or undefined
47
+ * for a string that is not one.
48
+ *
49
+ * Both forms are read. `DescribeLogGroups` reports `logGroupArn` without a
50
+ * trailing wildcard, while the `arn` field and CDK's `logGroup.logGroupArn`
51
+ * both end in `:*`, and a template naming either means the same group. A log
52
+ * group name cannot contain a colon, so what follows `log-group:` is the name
53
+ * up to an optional trailing `:*`.
54
+ */
55
+ export function simLogsParsedLogGroupArn(arn) {
56
+ const [prefix, partition, service, region, account, resourceType, ...rest] = arn.split(":");
57
+ if (prefix !== "arn" ||
58
+ service !== "logs" ||
59
+ resourceType !== "log-group" ||
60
+ !isPresent(partition) ||
61
+ !isPresent(region) ||
62
+ !isPresent(account)) {
63
+ return undefined;
64
+ }
65
+ const [logGroupName, wildcard, ...extra] = rest;
66
+ if (logGroupName === undefined ||
67
+ logGroupName.length === 0 ||
68
+ extra.length > 0 ||
69
+ (wildcard !== undefined && wildcard !== "*")) {
70
+ return undefined;
71
+ }
72
+ return { accountId: account, regionName: region, logGroupName };
73
+ }
74
+ /** Whether an ARN component was written at all. */
75
+ function isPresent(component) {
76
+ return component !== undefined && component.length > 0;
77
+ }
@@ -666,8 +666,146 @@ route configured that way is served unthrottled.
666
666
  `GetStages` answers with the settings a stage was created with. `AWS::ApiGatewayV2::Stage` deploys
667
667
  both properties as well (see [CloudFormation](#cloudformation)). The three members of `RouteSettings`
668
668
  that say nothing about throttling, `DetailedMetricsEnabled`, `LoggingLevel` and `DataTraceEnabled`,
669
- are refused by `CreateStage`. A template carrying one deploys, and the member it named is recorded on
670
- `stack.ignoredProperties`.
669
+ are refused by `CreateStage`. They are execution logging, which is a different log from the one
670
+ [`AccessLogSettings` writes](#writing-a-stages-access-log). A template carrying one deploys, and the
671
+ member it named is recorded on `stack.ignoredProperties`.
672
+
673
+ ## Writing a stage's access log
674
+
675
+ `AccessLogSettings` sends one line per request to a CloudWatch Logs log group. `DestinationArn` names
676
+ the group and `Format` is the line, with `$context` variables substituted from the request that
677
+ produced it.
678
+
679
+ The line is written whether or not an integration ran. A request the stage throttle refused, one a
680
+ Lambda authorizer denied, and one with no identity source header all reach the log group, and for
681
+ those the access log is the only record of the request.
682
+
683
+ ```typescript sim-apigatewayv2-access-log
684
+ /**
685
+ * Recording a simulated HTTP API stage's access log, including a request the
686
+ * stage's throttle refused.
687
+ */
688
+
689
+ import {
690
+ CreateApiCommand,
691
+ CreateIntegrationCommand,
692
+ CreateRouteCommand,
693
+ CreateStageCommand,
694
+ } from "@aws-sdk/client-apigatewayv2";
695
+ import {
696
+ CreateLogGroupCommand,
697
+ FilterLogEventsCommand,
698
+ } from "@aws-sdk/client-cloudwatch-logs";
699
+ import {
700
+ AddPermissionCommand,
701
+ CreateFunctionCommand,
702
+ } from "@aws-sdk/client-lambda";
703
+
704
+ import { SimAws } from "@kensio/yulin";
705
+ import { makeLambdaZipFileInput } from "@kensio/yulin/lambda";
706
+ import { serveSimAws } from "@kensio/yulin/serve";
707
+
708
+ const simAws = new SimAws();
709
+ const logGroupName = "/aws/vendedlogs/user-api";
710
+
711
+ await simAws.logs().createLogGroup(new CreateLogGroupCommand({ logGroupName }));
712
+
713
+ const { FunctionArn } = await simAws.lambda().createFunction(
714
+ new CreateFunctionCommand({
715
+ FunctionName: "users",
716
+ Role: "arn:aws:iam::111111111111:role/UsersRole",
717
+ Code: {
718
+ ZipFile: makeLambdaZipFileInput(() => ({
719
+ statusCode: 200,
720
+ headers: { "content-type": "text/plain" },
721
+ body: "ok",
722
+ })),
723
+ },
724
+ }),
725
+ );
726
+
727
+ const apiGateway = simAws.apiGatewayV2();
728
+ const { ApiId, ApiEndpoint } = await apiGateway.createApi(
729
+ new CreateApiCommand({ Name: "users", ProtocolType: "HTTP" }),
730
+ );
731
+
732
+ const { IntegrationId } = await apiGateway.createIntegration(
733
+ new CreateIntegrationCommand({
734
+ ApiId,
735
+ IntegrationType: "AWS_PROXY",
736
+ IntegrationUri: FunctionArn,
737
+ PayloadFormatVersion: "2.0",
738
+ }),
739
+ );
740
+
741
+ await apiGateway.createRoute(
742
+ new CreateRouteCommand({
743
+ ApiId,
744
+ RouteKey: "GET /user/profile",
745
+ Target: `integrations/${IntegrationId}`,
746
+ }),
747
+ );
748
+
749
+ await apiGateway.createStage(
750
+ new CreateStageCommand({
751
+ ApiId,
752
+ StageName: "$default",
753
+ AutoDeploy: true,
754
+ DefaultRouteSettings: { ThrottlingRateLimit: 1, ThrottlingBurstLimit: 1 },
755
+ AccessLogSettings: {
756
+ DestinationArn: `arn:aws:logs:us-east-1:888888888888:log-group:${logGroupName}:*`,
757
+ Format:
758
+ "$context.httpMethod $context.path $context.status " +
759
+ "$context.error.message",
760
+ },
761
+ }),
762
+ );
763
+
764
+ await simAws.lambda().addPermission(
765
+ new AddPermissionCommand({
766
+ FunctionName: "users",
767
+ StatementId: "api-gateway-invoke",
768
+ Action: "lambda:InvokeFunction",
769
+ Principal: "apigateway.amazonaws.com",
770
+ SourceArn: `arn:aws:execute-api:us-east-1:888888888888:${ApiId}/*/*`,
771
+ }),
772
+ );
773
+
774
+ const srv = await serveSimAws({ simAws });
775
+ simAws.clock().freeze();
776
+
777
+ const profile = srv.localUrl(`${ApiEndpoint}/user/profile`);
778
+ await fetch(profile);
779
+ await fetch(profile);
780
+
781
+ const { events } = await simAws
782
+ .logs()
783
+ .filterLogEvents(new FilterLogEventsCommand({ logGroupName }));
784
+
785
+ const lines = events ?? [];
786
+
787
+ for (const event of lines) {
788
+ console.log(event.message);
789
+ }
790
+
791
+ await srv.close();
792
+ ```
793
+
794
+ The served request and the throttled one are both there. A variable with no value renders as a dash,
795
+ which is what `$context.error.message` does for the request the integration answered:
796
+
797
+ ```text
798
+ GET /user/profile 200 -
799
+ GET /user/profile 429 Too Many Requests
800
+ ```
801
+
802
+ The lines go to the log group and nowhere else. An API answering a few hundred requests in one test
803
+ file would otherwise bury the suite's own output, so nothing here is forwarded to the console the way
804
+ a [Lambda handler's output](https://yulinsim.dev/services/lambda/#capturing-handler-output) is.
805
+
806
+ A `DestinationArn` naming a log group nothing has created yet is written to anyway, and the group is
807
+ made by the first line. `Format` is copied through apart from its `$context` references, so a JSON
808
+ format string arrives as JSON.
671
809
 
672
810
  ## Protecting a route with a Cognito user pool
673
811
 
@@ -2287,7 +2425,7 @@ parts behaves differently to the template. The simulated properties are:
2287
2425
  - `Integration`: `ApiId`, `IntegrationType`, `IntegrationUri`, `PayloadFormatVersion`, `Description`
2288
2426
  - `Route`: `ApiId`, `RouteKey`, `Target`, `AuthorizationType`, `AuthorizerId`, `AuthorizationScopes`
2289
2427
  - `Stage`: `ApiId`, `StageName`, `AutoDeploy`, `StageVariables`, `Description`,
2290
- `DefaultRouteSettings`, `RouteSettings`
2428
+ `DefaultRouteSettings`, `RouteSettings`, `AccessLogSettings`
2291
2429
 
2292
2430
  CDK's `HttpIamAuthorizer` deploys too. It emits no `AWS::ApiGatewayV2::Authorizer` and no
2293
2431
  `AuthorizerId`, only `AuthorizationType: "AWS_IAM"` on the `Route`, and the deployed route then
@@ -2436,6 +2574,9 @@ the simulation without being given one. See the
2436
2574
  under their own path segment, including stage variables
2437
2575
  - `DefaultRouteSettings` and `RouteSettings` throttling, with a token bucket per route refilling
2438
2576
  against the simulated clock and a 429 for the requests past it
2577
+ - `AccessLogSettings`, writing one line per request to the log group `DestinationArn` names, with the
2578
+ `Format` string's `$context` variables substituted, including for a request the throttle or an
2579
+ authorizer refused before any integration ran
2439
2580
  - Serving the generated endpoint through `serveSimAws`, invoking the integrated function with a
2440
2581
  payload format 2.0 event and turning its result back into an HTTP response
2441
2582
  - The invoke permission of an integration's function and of an authorizer's, each evaluated against
@@ -2474,9 +2615,24 @@ Current documented limitations:
2474
2615
  - Deployments are outside the simulation, so `CreateStage` requires `AutoDeploy: true`. A stage
2475
2616
  without it serves whichever Deployment it was given, which on real AWS is nothing until one is
2476
2617
  created.
2477
- - `AccessLogSettings` on a stage is refused, as is any other option `CreateStage` takes and this one
2478
- lacks. `RouteSettings` and `DefaultRouteSettings` are taken, and only their throttling members are
2479
- read. `DetailedMetricsEnabled`, `LoggingLevel` and `DataTraceEnabled` are refused by name.
2618
+ - Any option `CreateStage` takes and this one lacks is refused. `RouteSettings` and
2619
+ `DefaultRouteSettings` are taken, and only their throttling members are read.
2620
+ `DetailedMetricsEnabled`, `LoggingLevel` and `DataTraceEnabled` are refused by name. Those three
2621
+ are execution logging, which is a different log from the access log and is not simulated.
2622
+ - An access log `DestinationArn` has to name a CloudWatch Logs log group. A Kinesis Data Firehose
2623
+ delivery stream, which a REST API stage may name, is refused.
2624
+ - The access log variables carrying a value are `accountId`, `apiId`, `domainName`, `domainPrefix`,
2625
+ `stage`, `routeKey`, `httpMethod`, `path`, `protocol`, `requestId`, `extendedRequestId`,
2626
+ `requestTime`, `requestTimeEpoch`, `status`, `responseLength`, `responseLatency`,
2627
+ `identity.sourceIp`, `identity.userAgent`, `integrationStatus`, `integrationLatency`,
2628
+ `integrationErrorMessage`, `authorizer.error`, `error.message`, `error.messageString`,
2629
+ `customDomain.basePathMatched`, the `integration.*` aliases, and `authorizer.claims.<property>` and
2630
+ `authorizer.<property>` from the route's authorizer. Anything else AWS documents renders as a dash.
2631
+ - `responseLatency` and `integrationLatency` are simulated milliseconds, so a test holding the clock
2632
+ still logs zero rather than however long the process took.
2633
+ - The access log stream is named for the hour it was written in, dated from the simulation's clock.
2634
+ Real API Gateway appends an identifier this simulation has no counterpart for, so a test should
2635
+ filter the log group rather than name the stream.
2480
2636
  - `AWS_PROXY` is the only integration type, and its URI must name a Lambda function ARN, written
2481
2637
  either as that ARN or as the
2482
2638
  `arn:aws:apigateway:<region>:lambda:path/2015-03-31/functions/<function-arn>/invocations` form.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kensio/yulin",
3
- "version": "1.21.10",
3
+ "version": "1.21.11",
4
4
  "description": "AWS system behaviour simulation for isolated unit testing",
5
5
  "repository": "https://github.com/KensioSoftware/yulin",
6
6
  "homepage": "https://yulinsim.dev/",