@kensio/yulin 1.21.9 → 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 (73) 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/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-properties.d.ts +1 -1
  50. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-properties.js +4 -3
  51. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-names.d.ts +30 -0
  52. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-names.js +91 -0
  53. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.d.ts +7 -12
  54. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.js +12 -92
  55. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.js +1 -0
  56. package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-refusals.js +0 -5
  57. package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-shard-poller.js +1 -1
  58. package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-polling.js +1 -1
  59. package/dist/service/lambda/event-source/poll/sim-lambda-stream-bisect.d.ts +53 -0
  60. package/dist/service/lambda/event-source/poll/sim-lambda-stream-bisect.js +79 -0
  61. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress-state.d.ts +2 -0
  62. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress-state.js +3 -0
  63. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress.d.ts +14 -1
  64. package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress.js +24 -2
  65. package/dist/service/lambda/event-source/sim-lambda-event-source-retry-limits.d.ts +8 -7
  66. package/dist/service/lambda/event-source/sim-lambda-event-source-retry-limits.js +6 -3
  67. package/dist/service/lambda/event-source/sim-lambda-stream-retry-limits.d.ts +15 -7
  68. package/dist/service/lambda/event-source/sim-lambda-stream-retry-limits.js +15 -7
  69. package/dist/service/logs/group/sim-logs-arn.d.ts +15 -0
  70. package/dist/service/logs/group/sim-logs-arn.js +33 -0
  71. package/docs/services/apigatewayv2/README.md +162 -6
  72. package/docs/services/lambda/README.md +54 -7
  73. 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
  }
@@ -19,7 +19,7 @@ export declare class SimCfnLambdaEventSourceMappingProperties {
19
19
  private readonly parser;
20
20
  constructor(properties: SimCfnLambdaEventSourceMappingPropertiesProperties);
21
21
  /**
22
- * The create input this Resource asks for, refusing what is not simulated.
22
+ * The create input this Resource asks for, recording what is left out of it.
23
23
  *
24
24
  * The event source ARN is read before anything else is judged, as the command
25
25
  * reads it first for the same reason: what a mapping may ask for depends on
@@ -1,6 +1,6 @@
1
1
  import { simLambdaStreamDestinationConfig } from "../../event-source/sim-lambda-stream-destination-config.js";
2
2
  import { SimCfnLambdaPropertyParser } from "../function/sim-cfn-lambda-property-parser.js";
3
- import { assertSimulatedEventSourceMappingProperties } from "./sim-cfn-lambda-event-source-mapping-property-rules.js";
3
+ import { recordUnsimulatedEventSourceMappingProperties } from "./sim-cfn-lambda-event-source-mapping-property-rules.js";
4
4
  import { simCfnLambdaTargetFunctionName } from "../function/sim-cfn-lambda-target-function.js";
5
5
  /**
6
6
  * What a CloudFormation Unix time seconds value is multiplied by to reach the
@@ -24,7 +24,7 @@ export class SimCfnLambdaEventSourceMappingProperties {
24
24
  this.properties = properties.properties;
25
25
  }
26
26
  /**
27
- * The create input this Resource asks for, refusing what is not simulated.
27
+ * The create input this Resource asks for, recording what is left out of it.
28
28
  *
29
29
  * The event source ARN is read before anything else is judged, as the command
30
30
  * reads it first for the same reason: what a mapping may ask for depends on
@@ -32,7 +32,7 @@ export class SimCfnLambdaEventSourceMappingProperties {
32
32
  */
33
33
  createInput() {
34
34
  const eventSourceArn = this.parser.requiredString(this.resource, this.properties["EventSourceArn"], "EventSourceArn");
35
- assertSimulatedEventSourceMappingProperties(this.resource, this.properties);
35
+ recordUnsimulatedEventSourceMappingProperties(this.resource, this.properties);
36
36
  return {
37
37
  EventSourceArn: eventSourceArn,
38
38
  DestinationConfig: simLambdaStreamDestinationConfig(this.properties["DestinationConfig"]),
@@ -42,6 +42,7 @@ export class SimCfnLambdaEventSourceMappingProperties {
42
42
  MaximumBatchingWindowInSeconds: this.parser.optionalNumber(this.resource, this.properties["MaximumBatchingWindowInSeconds"], "MaximumBatchingWindowInSeconds"),
43
43
  MaximumRetryAttempts: this.parser.optionalNumber(this.resource, this.properties["MaximumRetryAttempts"], "MaximumRetryAttempts"),
44
44
  MaximumRecordAgeInSeconds: this.parser.optionalNumber(this.resource, this.properties["MaximumRecordAgeInSeconds"], "MaximumRecordAgeInSeconds"),
45
+ BisectBatchOnFunctionError: this.parser.optionalBoolean(this.resource, this.properties["BisectBatchOnFunctionError"], "BisectBatchOnFunctionError"),
45
46
  StartingPosition: this.parser.optionalString(this.resource, this.properties["StartingPosition"], "StartingPosition"),
46
47
  StartingPositionTimestamp: this.startingPositionTimestamp(),
47
48
  FunctionResponseTypes: this.functionResponseTypes(),
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The AWS::Lambda::EventSourceMapping properties this simulation acts on.
3
+ *
4
+ * The two starting position properties are here rather than among the
5
+ * unsimulated ones because whether a mapping may carry one is the event
6
+ * source's own rule: a stream has to be given a position and a queue is refused
7
+ * for naming one. That is decided by CreateEventSourceMapping, which knows
8
+ * which source the ARN names, so both are read and passed on. The two
9
+ * failed-batch limits are read for the same reason: a stream mapping keeps them
10
+ * and a queue mapping is refused for naming one.
11
+ */
12
+ export declare const simulatedPropertyNames: ReadonlySet<string>;
13
+ /**
14
+ * Real AWS::Lambda::EventSourceMapping properties this simulation does not act
15
+ * on, and what the mapping does in place of each.
16
+ *
17
+ * Every one of them changes what the function sees or when it sees it. That is
18
+ * what the record against the Resource is for. A mapping created without one
19
+ * still carries the records the template asked for to the function the
20
+ * template named, and the difference is there to be read.
21
+ */
22
+ export declare const unsimulatedPropertyReasons: ReadonlyMap<string, string>;
23
+ /**
24
+ * Why a property name this simulation has never heard of is recorded.
25
+ *
26
+ * A typo and a property AWS added after this simulation read the docs look
27
+ * identical from here. A mapping that deploys with the unread name reported is
28
+ * more use than a stack that fails over either.
29
+ */
30
+ export declare const unknownPropertyReason: string;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * The AWS::Lambda::EventSourceMapping properties this simulation acts on.
3
+ *
4
+ * The two starting position properties are here rather than among the
5
+ * unsimulated ones because whether a mapping may carry one is the event
6
+ * source's own rule: a stream has to be given a position and a queue is refused
7
+ * for naming one. That is decided by CreateEventSourceMapping, which knows
8
+ * which source the ARN names, so both are read and passed on. The two
9
+ * failed-batch limits are read for the same reason: a stream mapping keeps them
10
+ * and a queue mapping is refused for naming one.
11
+ */
12
+ export const simulatedPropertyNames = new Set([
13
+ "BatchSize",
14
+ "Enabled",
15
+ "EventSourceArn",
16
+ "FunctionName",
17
+ "FunctionResponseTypes",
18
+ "MaximumBatchingWindowInSeconds",
19
+ "MaximumRecordAgeInSeconds",
20
+ "MaximumRetryAttempts",
21
+ "BisectBatchOnFunctionError",
22
+ "DestinationConfig",
23
+ "StartingPosition",
24
+ "StartingPositionTimestamp",
25
+ ]);
26
+ /**
27
+ * The event sources a mapping property can only be configuring, none of which
28
+ * this simulation has.
29
+ *
30
+ * A mapping naming one of these has an event source ARN to match, and that ARN
31
+ * is what the mapping is refused on. The property beside it is recorded.
32
+ */
33
+ const absentEventSource = "the mapping polls the source its EventSourceArn names, and this " +
34
+ "simulation has SQS queues, DynamoDB streams and Kinesis streams";
35
+ /**
36
+ * Real AWS::Lambda::EventSourceMapping properties this simulation does not act
37
+ * on, and what the mapping does in place of each.
38
+ *
39
+ * Every one of them changes what the function sees or when it sees it. That is
40
+ * what the record against the Resource is for. A mapping created without one
41
+ * still carries the records the template asked for to the function the
42
+ * template named, and the difference is there to be read.
43
+ */
44
+ export const unsimulatedPropertyReasons = new Map([
45
+ ["AmazonManagedKafkaEventSourceConfig", absentEventSource],
46
+ ["DocumentDBEventSourceConfig", absentEventSource],
47
+ [
48
+ "FilterCriteria",
49
+ "every record on the event source is delivered to the function, unfiltered",
50
+ ],
51
+ [
52
+ "KmsKeyArn",
53
+ "the key encrypts filter criteria, and filter criteria are not simulated",
54
+ ],
55
+ ["MetricsConfig", "the mapping publishes no CloudWatch metrics"],
56
+ [
57
+ "ParallelizationFactor",
58
+ "polling concurrency is not simulated, and each shard of a stream is read " +
59
+ "by one reader",
60
+ ],
61
+ ["ProvisionedPollerConfig", "polling concurrency is not simulated"],
62
+ ["Queues", absentEventSource],
63
+ ["ScalingConfig", "polling concurrency is not simulated"],
64
+ ["SelfManagedEventSource", absentEventSource],
65
+ ["SelfManagedKafkaEventSourceConfig", absentEventSource],
66
+ [
67
+ "SourceAccessConfigurations",
68
+ "event source authentication is not simulated",
69
+ ],
70
+ [
71
+ "Tags",
72
+ "the mapping is created without them, and nothing reads them back or " +
73
+ "groups or bills by them",
74
+ ],
75
+ ["Topics", absentEventSource],
76
+ [
77
+ "TumblingWindowInSeconds",
78
+ "a batch carries no window state from the batch before it, and the " +
79
+ "function is handed no state to return",
80
+ ],
81
+ ]);
82
+ /**
83
+ * Why a property name this simulation has never heard of is recorded.
84
+ *
85
+ * A typo and a property AWS added after this simulation read the docs look
86
+ * identical from here. A mapping that deploys with the unread name reported is
87
+ * more use than a stack that fails over either.
88
+ */
89
+ export const unknownPropertyReason = "it is not an AWS::Lambda::EventSourceMapping property this simulation " +
90
+ "knows, whether because AWS added it or because the template misspelled " +
91
+ "something";
@@ -1,17 +1,12 @@
1
1
  import type { SimCfnResource } from "../../../cloudformation/resource/sim-cfn-resource.js";
2
2
  import type { SimCfnTemplateValueRecord } from "../../../cloudformation/template/value/sim-cfn-template-value.js";
3
3
  /**
4
- * Build the error a property of an AWS::Lambda::EventSourceMapping Resource is
5
- * refused with.
4
+ * Record everything about an AWS::Lambda::EventSourceMapping Resource that the
5
+ * mapping is created without.
6
6
  *
7
- * The wording matters: sim CloudFormation skips a Resource whose error reads as
8
- * an unsupported Resource type, and skipping is the wrong answer for a mapping
9
- * that cannot be created as the template asks. The stack would deploy with the
10
- * queue and the function and nothing between them.
7
+ * Nothing here fails the Resource. A mapping missing one of these settings
8
+ * still carries records from the source to the function, and a stack that
9
+ * refused it would take every other Resource down over one line of a template.
10
+ * What the mapping does instead is in `stack.ignoredProperties`.
11
11
  */
12
- export declare function eventSourceMappingPropertyError(logicalId: string, reason: string): Error;
13
- /**
14
- * Refuse everything about an AWS::Lambda::EventSourceMapping Resource that is
15
- * not simulated, and record what the mapping is created without.
16
- */
17
- export declare function assertSimulatedEventSourceMappingProperties(resource: SimCfnResource, properties: SimCfnTemplateValueRecord): void;
12
+ export declare function recordUnsimulatedEventSourceMappingProperties(resource: SimCfnResource, properties: SimCfnTemplateValueRecord): void;
@@ -1,100 +1,20 @@
1
+ import { simulatedPropertyNames, unknownPropertyReason, unsimulatedPropertyReasons, } from "./sim-cfn-lambda-event-source-mapping-property-names.js";
1
2
  /**
2
- * The AWS::Lambda::EventSourceMapping properties this simulation acts on.
3
+ * Record everything about an AWS::Lambda::EventSourceMapping Resource that the
4
+ * mapping is created without.
3
5
  *
4
- * The two starting position properties are here rather than among the
5
- * unsimulated ones because whether a mapping may carry one is the event
6
- * source's own rule: a stream has to be given a position and a queue is refused
7
- * for naming one. That is decided by CreateEventSourceMapping, which knows
8
- * which source the ARN names, so both are read and passed on. The two
9
- * failed-batch limits are read for the same reason: a stream mapping keeps them
10
- * and a queue mapping is refused for naming one.
6
+ * Nothing here fails the Resource. A mapping missing one of these settings
7
+ * still carries records from the source to the function, and a stack that
8
+ * refused it would take every other Resource down over one line of a template.
9
+ * What the mapping does instead is in `stack.ignoredProperties`.
11
10
  */
12
- const simulatedPropertyNames = new Set([
13
- "BatchSize",
14
- "Enabled",
15
- "EventSourceArn",
16
- "FunctionName",
17
- "FunctionResponseTypes",
18
- "MaximumBatchingWindowInSeconds",
19
- "MaximumRecordAgeInSeconds",
20
- "MaximumRetryAttempts",
21
- "DestinationConfig",
22
- "StartingPosition",
23
- "StartingPositionTimestamp",
24
- ]);
25
- /**
26
- * Real AWS::Lambda::EventSourceMapping properties this simulation does not
27
- * model.
28
- *
29
- * Every one of them changes what the function sees or when it sees it, so a
30
- * template asking for one fails the Resource rather than deploying a mapping
31
- * that quietly ignores it.
32
- */
33
- const unsimulatedPropertyNames = new Set([
34
- "AmazonManagedKafkaEventSourceConfig",
35
- "BisectBatchOnFunctionError",
36
- "DocumentDBEventSourceConfig",
37
- "FilterCriteria",
38
- "KmsKeyArn",
39
- "MetricsConfig",
40
- "ParallelizationFactor",
41
- "ProvisionedPollerConfig",
42
- "Queues",
43
- "ScalingConfig",
44
- "SelfManagedEventSource",
45
- "SelfManagedKafkaEventSourceConfig",
46
- "SourceAccessConfigurations",
47
- "Topics",
48
- "TumblingWindowInSeconds",
49
- ]);
50
- /**
51
- * Real AWS::Lambda::EventSourceMapping properties this simulation has nothing
52
- * to act on and no reason to fail a stack over.
53
- *
54
- * `Tags` is the whole list. The mapping is created without them and the
55
- * omission is recorded against the Resource. A mapping delivers the same
56
- * records with a tag and without one, and a CDK app calling
57
- * `Tags.of(app).add(...)` tags every mapping in it.
58
- */
59
- const ignoredPropertyReasons = new Map([
60
- [
61
- "Tags",
62
- "AWS::Lambda::EventSourceMapping property Tags is not simulated, so the " +
63
- "mapping is created without them. Nothing reads them back and nothing " +
64
- "is grouped or billed by them.",
65
- ],
66
- ]);
67
- /**
68
- * Build the error a property of an AWS::Lambda::EventSourceMapping Resource is
69
- * refused with.
70
- *
71
- * The wording matters: sim CloudFormation skips a Resource whose error reads as
72
- * an unsupported Resource type, and skipping is the wrong answer for a mapping
73
- * that cannot be created as the template asks. The stack would deploy with the
74
- * queue and the function and nothing between them.
75
- */
76
- export function eventSourceMappingPropertyError(logicalId, reason) {
77
- return new Error(`Invalid AWS::Lambda::EventSourceMapping Resource ${logicalId}: ${reason}`);
78
- }
79
- /**
80
- * Refuse everything about an AWS::Lambda::EventSourceMapping Resource that is
81
- * not simulated, and record what the mapping is created without.
82
- */
83
- export function assertSimulatedEventSourceMappingProperties(resource, properties) {
84
- const logicalId = resource.logicalId;
11
+ export function recordUnsimulatedEventSourceMappingProperties(resource, properties) {
85
12
  for (const name of Object.keys(properties)) {
86
- const ignoredReason = ignoredPropertyReasons.get(name);
87
- if (ignoredReason !== undefined) {
88
- resource.ignoreProperty(name, ignoredReason);
13
+ if (simulatedPropertyNames.has(name)) {
89
14
  continue;
90
15
  }
91
- if (unsimulatedPropertyNames.has(name)) {
92
- throw eventSourceMappingPropertyError(logicalId, `${name} is a real AWS::Lambda::EventSourceMapping property that ` +
93
- "this simulation does not simulate, so it is refused rather than " +
94
- "ignored");
95
- }
96
- if (!simulatedPropertyNames.has(name)) {
97
- throw eventSourceMappingPropertyError(logicalId, `${name} is not an AWS::Lambda::EventSourceMapping property`);
98
- }
16
+ const reason = unsimulatedPropertyReasons.get(name) ?? unknownPropertyReason;
17
+ resource.ignoreProperty(name, `AWS::Lambda::EventSourceMapping property ${name} is not simulated: ` +
18
+ `${reason}.`);
99
19
  }
100
20
  }
@@ -68,6 +68,7 @@ export class SimLambdaEventSourceMappingInput {
68
68
  streamRetryLimits: eventSourceArn.retryLimitRules.limitsIn({
69
69
  maximumRetryAttempts: input.MaximumRetryAttempts,
70
70
  maximumRecordAgeInSeconds: input.MaximumRecordAgeInSeconds,
71
+ bisectBatchOnFunctionError: input.BisectBatchOnFunctionError,
71
72
  }),
72
73
  });
73
74
  }
@@ -12,11 +12,6 @@ const unsimulatedInputs = new Map([
12
12
  ["FilterCriteria", "event filtering is not simulated"],
13
13
  ["ScalingConfig", "polling concurrency is not simulated"],
14
14
  ["ProvisionedPollerConfig", "polling concurrency is not simulated"],
15
- [
16
- "BisectBatchOnFunctionError",
17
- "batch bisection is not simulated, so a failing stream batch is retried " +
18
- "whole",
19
- ],
20
15
  [
21
16
  "ParallelizationFactor",
22
17
  "polling concurrency is not simulated, so each shard of a stream is read " +
@@ -66,7 +66,7 @@ export class SimLambdaKinesisShardPoller {
66
66
  const { progress } = this;
67
67
  // Reading is done as the function's execution role, as on real Lambda, so
68
68
  // simulated IAM decides whether this mapping may read its stream.
69
- const batch = await this.shard.read(progress.position, this.mapping.batchSize);
69
+ const batch = await this.shard.read(progress.position, progress.batchSizeWithin(this.mapping.batchSize));
70
70
  await processSimLambdaStreamBatch({
71
71
  batch,
72
72
  progress,
@@ -15,7 +15,7 @@ export class SimLambdaDynamoDbStreamPolling {
15
15
  const { progress, stream, mapping, delivery } = this.properties;
16
16
  // Reading is done as the function's execution role, as on real Lambda, so
17
17
  // simulated IAM decides whether this mapping may read its stream.
18
- const batch = await stream.read(simFunction.roleArn, progress.position, mapping.batchSize);
18
+ const batch = await stream.read(simFunction.roleArn, progress.position, progress.batchSizeWithin(mapping.batchSize));
19
19
  await processSimLambdaStreamBatch({
20
20
  batch,
21
21
  progress,
@@ -0,0 +1,53 @@
1
+ import type { SimLambdaStreamBatchOutcome } from "./sim-lambda-stream-batch-outcome.js";
2
+ /**
3
+ * How much of a failing batch the next delivery may carry.
4
+ *
5
+ * A mapping asked to bisect halves a batch its function threw on, and halves
6
+ * again on each further error, until a failing batch holds one record. That is
7
+ * how the record that broke the batch ends up delivered on its own, and how the
8
+ * records beside it get through.
9
+ *
10
+ * A split is progress rather than another attempt at the same work, so it puts
11
+ * the retry count back to the start. Without that the simulator's own cap of
12
+ * five attempts would discard a batch of a hundred long before it was down to
13
+ * one record. A batch halves at every step, so the splitting ends on its own,
14
+ * and the single record it ends on is then counted and discarded as any failing
15
+ * batch is.
16
+ *
17
+ * Two numbers say what the next read may take. The half is how many records a
18
+ * delivery may carry, and the remainder is how many records of the batch being
19
+ * split are still to get through. Reading the lower of the two is what keeps a
20
+ * record written while the splitting is going on out of it: the halves are the
21
+ * batch that failed, and a record that arrived afterwards belongs to the batch
22
+ * after it. The mapping's own batch size comes back once the whole of the split
23
+ * batch is through.
24
+ */
25
+ export declare class SimLambdaStreamBisect {
26
+ #private;
27
+ private readonly enabled;
28
+ constructor(enabled: boolean);
29
+ /**
30
+ * How many records the next read may take, within the mapping's batch size.
31
+ */
32
+ sizeWithin(batchSize: number): number;
33
+ /**
34
+ * Split a batch the function threw on, answering with whether it split.
35
+ *
36
+ * A batch the function reported partial failures on is left alone. Lambda
37
+ * bisects around an error the handler raised, and a report already says which
38
+ * record to go back to.
39
+ */
40
+ split(outcome: SimLambdaStreamBatchOutcome): boolean;
41
+ /**
42
+ * Take records of the batch being split that the mapping is finished with,
43
+ * whether the function handled them, they were discarded, or they aged out.
44
+ *
45
+ * The whole of the split batch being through is what puts the mapping back on
46
+ * full batches.
47
+ */
48
+ finished(count: number): void;
49
+ /**
50
+ * Read whole batches again.
51
+ */
52
+ reset(): void;
53
+ }