@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
package/README.md
CHANGED
|
@@ -66,6 +66,7 @@ npm i -D @kensio/yulin
|
|
|
66
66
|
- [Non-AWS dependencies](https://yulinsim.dev/non-aws-dependencies/ "Dependencies Yulin does not simulate docs")
|
|
67
67
|
- [Serving on localhost](https://yulinsim.dev/serve/ "Serving simulated AWS on localhost docs")
|
|
68
68
|
- [Simulated time](https://yulinsim.dev/time/ "Simulated time docs")
|
|
69
|
+
- [Test suite setup](https://yulinsim.dev/testing/ "Sharing one Yulin environment across a test suite")
|
|
69
70
|
- [Terraform](https://yulinsim.dev/terraform/ "Deploying Terraform into simulated AWS docs")
|
|
70
71
|
|
|
71
72
|
Every page listed above also ships inside the package as markdown, documenting the version
|
|
@@ -74,6 +75,24 @@ reach can read them from there.
|
|
|
74
75
|
|
|
75
76
|
## Usage
|
|
76
77
|
|
|
78
|
+
### Set up Yulin once for the test suite
|
|
79
|
+
|
|
80
|
+
An application test suite should normally create one Yulin environment in its Vitest setup. Install
|
|
81
|
+
SDK interception and deploy the application's simulated infrastructure there, then let every test
|
|
82
|
+
use that state. Treat the environment like an AWS account or a LocalStack container that belongs to
|
|
83
|
+
the suite.
|
|
84
|
+
|
|
85
|
+
Keep tests independent by giving their records and resources unique names. A fresh `SimAws` or
|
|
86
|
+
`SimSdk` for every test or test file is supported, but it is intended for cases that specifically
|
|
87
|
+
need an empty simulated account.
|
|
88
|
+
|
|
89
|
+
The simulated clock is shared too. Most tests should leave it alone and use the suite environment.
|
|
90
|
+
Put tests that call `freeze()`, `setTo(...)`, `advanceBy(...)`, or `resume()` in a separate group with
|
|
91
|
+
a fresh Yulin environment for each test.
|
|
92
|
+
|
|
93
|
+
The [test suite setup guide](https://yulinsim.dev/testing/ "Sharing one Yulin environment across a test suite")
|
|
94
|
+
shows the Vitest configuration, shared deployment, and SDK interception.
|
|
95
|
+
|
|
77
96
|
### Intercept AWS SDK clients
|
|
78
97
|
|
|
79
98
|
If your code uses the AWS SDK, you can intercept its clients and route their Commands to simulated
|
|
@@ -166,8 +185,9 @@ const accountId = simAwsAccountId("111111111111");
|
|
|
166
185
|
const someOtherAccountId = makeSimAwsAccountId();
|
|
167
186
|
```
|
|
168
187
|
|
|
169
|
-
Each instance of `SimAws` is
|
|
170
|
-
|
|
188
|
+
Each instance of `SimAws` is encapsulated. Application tests should usually share one instance from
|
|
189
|
+
suite setup. Create another instance when a test needs a separate simulated account with no shared
|
|
190
|
+
state.
|
|
171
191
|
|
|
172
192
|
If you prefer, you can also instantiate simulated services individually:
|
|
173
193
|
|
|
@@ -24,6 +24,7 @@ export class SimCfnEventBusCreator {
|
|
|
24
24
|
properties,
|
|
25
25
|
});
|
|
26
26
|
busProperties.refuseUnsimulated();
|
|
27
|
+
busProperties.recordIgnoredProperties();
|
|
27
28
|
const name = busProperties.name();
|
|
28
29
|
const description = busProperties.description();
|
|
29
30
|
return await simCfnEventBridgeResourceCreation(eventBusResourceType, resource.logicalId, async () => {
|
|
@@ -27,6 +27,10 @@ export declare class SimCfnEventBusProperties {
|
|
|
27
27
|
* Refuse what this simulation does not model, naming the property.
|
|
28
28
|
*/
|
|
29
29
|
refuseUnsimulated(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Record the properties the bus is created without acting on.
|
|
32
|
+
*/
|
|
33
|
+
recordIgnoredProperties(): void;
|
|
30
34
|
private propertyError;
|
|
31
35
|
}
|
|
32
36
|
export {};
|
|
@@ -19,8 +19,23 @@ const unsimulatedProperties = [
|
|
|
19
19
|
["EventSourceName", "partner event buses are not simulated"],
|
|
20
20
|
["KmsKeyIdentifier", "events are not encrypted with a customer managed key"],
|
|
21
21
|
["LogConfig", "event bus logging is not simulated"],
|
|
22
|
-
["Tags", "event bus tags are not simulated"],
|
|
23
22
|
];
|
|
23
|
+
/**
|
|
24
|
+
* What a template can say about an event bus that this simulation has nothing
|
|
25
|
+
* to act on, and why.
|
|
26
|
+
*
|
|
27
|
+
* `Tags` is the whole list, and it is recorded rather than refused. A bus
|
|
28
|
+
* behaves the same way with a tag and without one, and a CDK app calling
|
|
29
|
+
* `Tags.of(app).add(...)` tags every bus in it.
|
|
30
|
+
*/
|
|
31
|
+
const ignoredPropertyReasons = new Map([
|
|
32
|
+
[
|
|
33
|
+
"Tags",
|
|
34
|
+
`${eventBusResourceType} property Tags is not simulated, so the bus is ` +
|
|
35
|
+
"created without them. Nothing reads them back and nothing is grouped " +
|
|
36
|
+
"or billed by them.",
|
|
37
|
+
],
|
|
38
|
+
]);
|
|
24
39
|
/**
|
|
25
40
|
* Reads AWS::Events::EventBus properties into the shape CreateEventBus takes.
|
|
26
41
|
*/
|
|
@@ -71,6 +86,16 @@ export class SimCfnEventBusProperties {
|
|
|
71
86
|
}
|
|
72
87
|
}
|
|
73
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Record the properties the bus is created without acting on.
|
|
91
|
+
*/
|
|
92
|
+
recordIgnoredProperties() {
|
|
93
|
+
for (const [property, reason] of ignoredPropertyReasons) {
|
|
94
|
+
if (this.properties.has(property)) {
|
|
95
|
+
this.resource.ignoreProperty(property, reason);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
74
99
|
propertyError(reason) {
|
|
75
100
|
return simCfnEventBridgeResourceError(eventBusResourceType, this.resource.logicalId, reason);
|
|
76
101
|
}
|
|
@@ -2,6 +2,7 @@ import { assertDefined } from "../../../../util/type-guard/defined.js";
|
|
|
2
2
|
import { simCfnEventBridgeResourceCreation } from "../sim-cfn-event-bridge-resource-error.js";
|
|
3
3
|
import { eventRuleResourceType } from "../sim-cfn-event-bridge-resource-types.js";
|
|
4
4
|
import { SimCfnEventRuleProperties } from "./sim-cfn-event-rule-properties.js";
|
|
5
|
+
import { recordIgnoredEventRuleProperties, refuseUnsimulatedEventRuleProperties, } from "./sim-cfn-event-rule-unsimulated-properties.js";
|
|
5
6
|
/**
|
|
6
7
|
* Creates simulated rules from AWS::Events::Rule Resources.
|
|
7
8
|
*
|
|
@@ -27,7 +28,8 @@ export class SimCfnEventRuleCreator {
|
|
|
27
28
|
resource,
|
|
28
29
|
properties,
|
|
29
30
|
});
|
|
30
|
-
|
|
31
|
+
refuseUnsimulatedEventRuleProperties(resource, properties);
|
|
32
|
+
recordIgnoredEventRuleProperties(resource, properties);
|
|
31
33
|
const name = ruleProperties.name();
|
|
32
34
|
const busName = ruleProperties.busName();
|
|
33
35
|
const targets = ruleProperties.targets();
|
|
@@ -48,13 +48,6 @@ export declare class SimCfnEventRuleProperties {
|
|
|
48
48
|
* The rule's inline targets.
|
|
49
49
|
*/
|
|
50
50
|
targets(): readonly SimCfnEventRuleTarget[];
|
|
51
|
-
/**
|
|
52
|
-
* Refuse what this simulation does not model, naming the property.
|
|
53
|
-
*
|
|
54
|
-
* Both are asked by key rather than by value, so a template writing `null`
|
|
55
|
-
* for one is refused rather than read as having left it out.
|
|
56
|
-
*/
|
|
57
|
-
refuseUnsimulated(): void;
|
|
58
51
|
private stringProperty;
|
|
59
52
|
private propertyError;
|
|
60
53
|
}
|
|
@@ -89,26 +89,6 @@ export class SimCfnEventRuleProperties {
|
|
|
89
89
|
targets() {
|
|
90
90
|
return simCfnEventRuleTargets(this.properties.get("Targets"), (reason) => this.propertyError(reason));
|
|
91
91
|
}
|
|
92
|
-
/**
|
|
93
|
-
* Refuse what this simulation does not model, naming the property.
|
|
94
|
-
*
|
|
95
|
-
* Both are asked by key rather than by value, so a template writing `null`
|
|
96
|
-
* for one is refused rather than read as having left it out.
|
|
97
|
-
*/
|
|
98
|
-
refuseUnsimulated() {
|
|
99
|
-
if (this.properties.has("RoleArn")) {
|
|
100
|
-
throw this.propertyError("RoleArn is not simulated, so the Resource is refused rather than " +
|
|
101
|
-
"deployed without it: a rule reaches its target as the EventBridge " +
|
|
102
|
-
"service principal, admitted by the target's own resource policy");
|
|
103
|
-
}
|
|
104
|
-
// Rule tags never reach PutRule, which is the one place that would
|
|
105
|
-
// otherwise refuse them, so a tagged rule would deploy and quietly lose
|
|
106
|
-
// its tags.
|
|
107
|
-
if (this.properties.has("Tags")) {
|
|
108
|
-
throw this.propertyError("Tags are not simulated, so the Resource is refused rather than " +
|
|
109
|
-
"deployed without them");
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
92
|
stringProperty(name) {
|
|
113
93
|
const value = this.properties.get(name);
|
|
114
94
|
if (value === undefined) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SimCfnResource } from "../../../cloudformation/resource/sim-cfn-resource.js";
|
|
2
|
+
import type { SimCfnTemplateValueRecord } from "../../../cloudformation/template/value/sim-cfn-template-value.js";
|
|
3
|
+
/**
|
|
4
|
+
* Refuse what this simulation does not model, naming the property.
|
|
5
|
+
*
|
|
6
|
+
* Asked by key rather than by value, so a template writing `null` for one of
|
|
7
|
+
* these is refused rather than read as having left it out.
|
|
8
|
+
*/
|
|
9
|
+
export declare function refuseUnsimulatedEventRuleProperties(resource: SimCfnResource, properties: SimCfnTemplateValueRecord): void;
|
|
10
|
+
/**
|
|
11
|
+
* Record the properties the rule is created without acting on.
|
|
12
|
+
*/
|
|
13
|
+
export declare function recordIgnoredEventRuleProperties(resource: SimCfnResource, properties: SimCfnTemplateValueRecord): void;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { simCfnEventBridgeResourceError } from "../sim-cfn-event-bridge-resource-error.js";
|
|
2
|
+
import { eventRuleResourceType } from "../sim-cfn-event-bridge-resource-types.js";
|
|
3
|
+
/**
|
|
4
|
+
* What a template can say about a rule that this simulation does not model,
|
|
5
|
+
* and why each one fails the Resource.
|
|
6
|
+
*
|
|
7
|
+
* `RoleArn` is the whole list. A rule reaches its target as the EventBridge
|
|
8
|
+
* service principal, admitted by the target's own resource policy, so a rule
|
|
9
|
+
* deployed with a role would sit in a test looking as though the role decided
|
|
10
|
+
* something.
|
|
11
|
+
*/
|
|
12
|
+
const refusedPropertyReasons = new Map([
|
|
13
|
+
[
|
|
14
|
+
"RoleArn",
|
|
15
|
+
"RoleArn is not simulated, so the Resource is refused rather than " +
|
|
16
|
+
"deployed without it: a rule reaches its target as the EventBridge " +
|
|
17
|
+
"service principal, admitted by the target's own resource policy",
|
|
18
|
+
],
|
|
19
|
+
]);
|
|
20
|
+
/**
|
|
21
|
+
* What a template can say about a rule that this simulation has nothing to act
|
|
22
|
+
* on, and why each one is recorded rather than refused.
|
|
23
|
+
*
|
|
24
|
+
* `Tags` is the whole list. Rule tags never reach `PutRule`, so the record is
|
|
25
|
+
* the only place a tagged rule says it lost them. A rule routes the same
|
|
26
|
+
* events with a tag and without one, and a CDK app calling
|
|
27
|
+
* `Tags.of(app).add(...)` tags every rule in it.
|
|
28
|
+
*/
|
|
29
|
+
const ignoredPropertyReasons = new Map([
|
|
30
|
+
[
|
|
31
|
+
"Tags",
|
|
32
|
+
`${eventRuleResourceType} property Tags is not simulated, so the rule is ` +
|
|
33
|
+
"created without them. Nothing reads them back and nothing is grouped " +
|
|
34
|
+
"or billed by them.",
|
|
35
|
+
],
|
|
36
|
+
]);
|
|
37
|
+
/**
|
|
38
|
+
* Refuse what this simulation does not model, naming the property.
|
|
39
|
+
*
|
|
40
|
+
* Asked by key rather than by value, so a template writing `null` for one of
|
|
41
|
+
* these is refused rather than read as having left it out.
|
|
42
|
+
*/
|
|
43
|
+
export function refuseUnsimulatedEventRuleProperties(resource, properties) {
|
|
44
|
+
for (const [property, reason] of refusedPropertyReasons) {
|
|
45
|
+
if (Object.hasOwn(properties, property)) {
|
|
46
|
+
throw simCfnEventBridgeResourceError(eventRuleResourceType, resource.logicalId, reason);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Record the properties the rule is created without acting on.
|
|
52
|
+
*/
|
|
53
|
+
export function recordIgnoredEventRuleProperties(resource, properties) {
|
|
54
|
+
for (const [property, reason] of ignoredPropertyReasons) {
|
|
55
|
+
if (Object.hasOwn(properties, property)) {
|
|
56
|
+
resource.ignoreProperty(property, reason);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -15,7 +15,7 @@ export declare function simCfnEventBridgeResourceError(resourceType: string, log
|
|
|
15
15
|
* Every Resource type here goes through the ordinary EventBridge commands, so
|
|
16
16
|
* what a template may ask for is decided once, by simulated EventBridge, rather
|
|
17
17
|
* than again in the CloudFormation layer. What that leaves out is where the
|
|
18
|
-
* request came from: a deployment failing with `Rule
|
|
18
|
+
* request came from: a deployment failing with `Rule pattern is not valid`
|
|
19
19
|
* says nothing about which Resource asked for it. Only EventBridge's own errors
|
|
20
20
|
* are renamed, so a refusal the CloudFormation layer decided keeps the wording
|
|
21
21
|
* it was written with.
|
|
@@ -20,7 +20,7 @@ export function simCfnEventBridgeResourceError(resourceType, logicalId, reason,
|
|
|
20
20
|
* Every Resource type here goes through the ordinary EventBridge commands, so
|
|
21
21
|
* what a template may ask for is decided once, by simulated EventBridge, rather
|
|
22
22
|
* than again in the CloudFormation layer. What that leaves out is where the
|
|
23
|
-
* request came from: a deployment failing with `Rule
|
|
23
|
+
* request came from: a deployment failing with `Rule pattern is not valid`
|
|
24
24
|
* says nothing about which Resource asked for it. Only EventBridge's own errors
|
|
25
25
|
* are renamed, so a refusal the CloudFormation layer decided keeps the wording
|
|
26
26
|
* it was written with.
|
|
@@ -31,7 +31,7 @@ export class SimCfnLambdaEventSourceMappingProperties {
|
|
|
31
31
|
*/
|
|
32
32
|
createInput() {
|
|
33
33
|
const eventSourceArn = this.parser.requiredString(this.resource, this.properties["EventSourceArn"], "EventSourceArn");
|
|
34
|
-
assertSimulatedEventSourceMappingProperties(this.resource
|
|
34
|
+
assertSimulatedEventSourceMappingProperties(this.resource, this.properties);
|
|
35
35
|
return {
|
|
36
36
|
EventSourceArn: eventSourceArn,
|
|
37
37
|
FunctionName: simCfnLambdaTargetFunctionName(this.parser.requiredString(this.resource, this.properties["FunctionName"], "FunctionName")),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SimCfnResource } from "../../../cloudformation/resource/sim-cfn-resource.js";
|
|
1
2
|
import type { SimCfnTemplateValueRecord } from "../../../cloudformation/template/value/sim-cfn-template-value.js";
|
|
2
3
|
/**
|
|
3
4
|
* Build the error a property of an AWS::Lambda::EventSourceMapping Resource is
|
|
@@ -11,6 +12,6 @@ import type { SimCfnTemplateValueRecord } from "../../../cloudformation/template
|
|
|
11
12
|
export declare function eventSourceMappingPropertyError(logicalId: string, reason: string): Error;
|
|
12
13
|
/**
|
|
13
14
|
* Refuse everything about an AWS::Lambda::EventSourceMapping Resource that is
|
|
14
|
-
* not simulated.
|
|
15
|
+
* not simulated, and record what the mapping is created without.
|
|
15
16
|
*/
|
|
16
|
-
export declare function assertSimulatedEventSourceMappingProperties(
|
|
17
|
+
export declare function assertSimulatedEventSourceMappingProperties(resource: SimCfnResource, properties: SimCfnTemplateValueRecord): void;
|
|
@@ -44,10 +44,26 @@ const unsimulatedPropertyNames = new Set([
|
|
|
44
44
|
"SelfManagedEventSource",
|
|
45
45
|
"SelfManagedKafkaEventSourceConfig",
|
|
46
46
|
"SourceAccessConfigurations",
|
|
47
|
-
"Tags",
|
|
48
47
|
"Topics",
|
|
49
48
|
"TumblingWindowInSeconds",
|
|
50
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
|
+
]);
|
|
51
67
|
/**
|
|
52
68
|
* Build the error a property of an AWS::Lambda::EventSourceMapping Resource is
|
|
53
69
|
* refused with.
|
|
@@ -62,10 +78,16 @@ export function eventSourceMappingPropertyError(logicalId, reason) {
|
|
|
62
78
|
}
|
|
63
79
|
/**
|
|
64
80
|
* Refuse everything about an AWS::Lambda::EventSourceMapping Resource that is
|
|
65
|
-
* not simulated.
|
|
81
|
+
* not simulated, and record what the mapping is created without.
|
|
66
82
|
*/
|
|
67
|
-
export function assertSimulatedEventSourceMappingProperties(
|
|
83
|
+
export function assertSimulatedEventSourceMappingProperties(resource, properties) {
|
|
84
|
+
const logicalId = resource.logicalId;
|
|
68
85
|
for (const name of Object.keys(properties)) {
|
|
86
|
+
const ignoredReason = ignoredPropertyReasons.get(name);
|
|
87
|
+
if (ignoredReason !== undefined) {
|
|
88
|
+
resource.ignoreProperty(name, ignoredReason);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
69
91
|
if (unsimulatedPropertyNames.has(name)) {
|
|
70
92
|
throw eventSourceMappingPropertyError(logicalId, `${name} is a real AWS::Lambda::EventSourceMapping property that ` +
|
|
71
93
|
"this simulation does not simulate, so it is refused rather than " +
|
|
@@ -30,6 +30,15 @@ export declare class SimSesAuthorizer {
|
|
|
30
30
|
* ARN the identity is about to have.
|
|
31
31
|
*/
|
|
32
32
|
authorizeIdentity(action: string, emailIdentity: string, caller?: SimAwsCaller): SimAwsResolvedCaller;
|
|
33
|
+
/**
|
|
34
|
+
* Ensure the caller may send from one email identity and address.
|
|
35
|
+
*
|
|
36
|
+
* SES supplies the bare sender address as `ses:FromAddress`. A policy can
|
|
37
|
+
* use this key to grant one address under a verified domain identity.
|
|
38
|
+
*
|
|
39
|
+
* https://docs.aws.amazon.com/ses/latest/dg/sending-authorization-policy-examples.html#sending-authorization-policy-example-from
|
|
40
|
+
*/
|
|
41
|
+
authorizeSendEmail(emailIdentity: string, fromAddress: string, caller?: SimAwsCaller): SimAwsResolvedCaller;
|
|
33
42
|
/**
|
|
34
43
|
* Ensure the caller may perform an action on one email template.
|
|
35
44
|
*
|
|
@@ -40,6 +40,17 @@ export class SimSesAuthorizer {
|
|
|
40
40
|
authorizeIdentity(action, emailIdentity, caller) {
|
|
41
41
|
return this.authorizeResource(action, simSesIdentityArn(this.#accountRegionScope, emailIdentity), caller);
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Ensure the caller may send from one email identity and address.
|
|
45
|
+
*
|
|
46
|
+
* SES supplies the bare sender address as `ses:FromAddress`. A policy can
|
|
47
|
+
* use this key to grant one address under a verified domain identity.
|
|
48
|
+
*
|
|
49
|
+
* https://docs.aws.amazon.com/ses/latest/dg/sending-authorization-policy-examples.html#sending-authorization-policy-example-from
|
|
50
|
+
*/
|
|
51
|
+
authorizeSendEmail(emailIdentity, fromAddress, caller) {
|
|
52
|
+
return this.authorizeResource("ses:SendEmail", simSesIdentityArn(this.#accountRegionScope, emailIdentity), caller, { "ses:FromAddress": fromAddress });
|
|
53
|
+
}
|
|
43
54
|
/**
|
|
44
55
|
* Ensure the caller may perform an action on one email template.
|
|
45
56
|
*
|
|
@@ -73,8 +84,13 @@ export class SimSesAuthorizer {
|
|
|
73
84
|
authorizeNoResource(action, caller) {
|
|
74
85
|
return this.authorizeResource(action, noResource, caller);
|
|
75
86
|
}
|
|
76
|
-
authorizeResource(action, resource, caller) {
|
|
77
|
-
const decision = this.#iam.authorize({
|
|
87
|
+
authorizeResource(action, resource, caller, conditionContext) {
|
|
88
|
+
const decision = this.#iam.authorize({
|
|
89
|
+
action,
|
|
90
|
+
resource,
|
|
91
|
+
caller,
|
|
92
|
+
conditionContext,
|
|
93
|
+
});
|
|
78
94
|
if (decision.isDenied) {
|
|
79
95
|
throw new SimIamAccessDenied({
|
|
80
96
|
principal: decision.caller.principal,
|
|
@@ -42,8 +42,9 @@ export class SimSesSendEmail {
|
|
|
42
42
|
handle(command, options) {
|
|
43
43
|
const input = command.input;
|
|
44
44
|
const fromEmailAddress = requiredSimSesFromAddress(input.FromEmailAddress);
|
|
45
|
+
const bareFromAddress = simSesBareAddress(fromEmailAddress);
|
|
45
46
|
refuseUnsimulatedSendInput(input);
|
|
46
|
-
this.#authorizer.
|
|
47
|
+
this.#authorizer.authorizeSendEmail(this.#identities.covering(bareFromAddress), bareFromAddress, options?.caller);
|
|
47
48
|
const destination = readDestination(input.Destination);
|
|
48
49
|
const recipients = allRecipients(destination);
|
|
49
50
|
if (recipients.length === 0) {
|
|
@@ -23,6 +23,7 @@ export class SimCfnSnsTopicCreator {
|
|
|
23
23
|
resource,
|
|
24
24
|
properties,
|
|
25
25
|
});
|
|
26
|
+
topicProperties.recordIgnoredProperties();
|
|
26
27
|
const name = topicProperties.name();
|
|
27
28
|
const attributes = topicProperties.attributes();
|
|
28
29
|
const inline = topicProperties.inlineSubscriptions();
|
|
@@ -43,13 +43,18 @@ export declare class SimCfnSnsTopicProperties {
|
|
|
43
43
|
* The subscriptions the topic declares inside itself.
|
|
44
44
|
*/
|
|
45
45
|
inlineSubscriptions(): readonly SimCfnSnsInlineSubscription[];
|
|
46
|
+
/**
|
|
47
|
+
* Record the properties the topic is created without acting on.
|
|
48
|
+
*/
|
|
49
|
+
recordIgnoredProperties(): void;
|
|
46
50
|
/**
|
|
47
51
|
* Whether a property is handed to CreateTopic as an attribute.
|
|
48
52
|
*
|
|
49
|
-
* The two properties the CloudFormation layer reads itself are not
|
|
50
|
-
* are
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
+
* The two properties the CloudFormation layer reads itself are not, and
|
|
54
|
+
* neither are the ones recorded as ignored. The rest are refused as they are
|
|
55
|
+
* reached: the ones with a reason of their own with that reason, and
|
|
56
|
+
* anything else as a property AWS::SNS::Topic does not have, which is how
|
|
57
|
+
* real CloudFormation answers one too.
|
|
53
58
|
*/
|
|
54
59
|
private isAttributeProperty;
|
|
55
60
|
private propertyError;
|
|
@@ -3,7 +3,7 @@ import { simCfnSnsResourceError } from "../sim-cfn-sns-resource-error.js";
|
|
|
3
3
|
import { snsTopicResourceType } from "../sim-cfn-sns-resource-types.js";
|
|
4
4
|
import { simCfnSnsInlineSubscriptions, } from "./sim-cfn-sns-inline-subscriptions.js";
|
|
5
5
|
import { SimCfnSnsTopicName } from "./sim-cfn-sns-topic-name.js";
|
|
6
|
-
import { attributePropertyNames, topicNamePropertyName, topicSubscriptionPropertyName, unsimulatedPropertyReasons, } from "./sim-cfn-sns-topic-property-names.js";
|
|
6
|
+
import { attributePropertyNames, ignoredPropertyReasons, topicNamePropertyName, topicSubscriptionPropertyName, unsimulatedPropertyReasons, } from "./sim-cfn-sns-topic-property-names.js";
|
|
7
7
|
/**
|
|
8
8
|
* Reads AWS::SNS::Topic CloudFormation properties into the shape CreateTopic
|
|
9
9
|
* takes.
|
|
@@ -63,17 +63,29 @@ export class SimCfnSnsTopicProperties {
|
|
|
63
63
|
inlineSubscriptions() {
|
|
64
64
|
return simCfnSnsInlineSubscriptions(this.resource.logicalId, this.properties.get(topicSubscriptionPropertyName));
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Record the properties the topic is created without acting on.
|
|
68
|
+
*/
|
|
69
|
+
recordIgnoredProperties() {
|
|
70
|
+
for (const [property, reason] of ignoredPropertyReasons) {
|
|
71
|
+
if (this.properties.has(property)) {
|
|
72
|
+
this.resource.ignoreProperty(property, reason);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
66
76
|
/**
|
|
67
77
|
* Whether a property is handed to CreateTopic as an attribute.
|
|
68
78
|
*
|
|
69
|
-
* The two properties the CloudFormation layer reads itself are not
|
|
70
|
-
* are
|
|
71
|
-
*
|
|
72
|
-
*
|
|
79
|
+
* The two properties the CloudFormation layer reads itself are not, and
|
|
80
|
+
* neither are the ones recorded as ignored. The rest are refused as they are
|
|
81
|
+
* reached: the ones with a reason of their own with that reason, and
|
|
82
|
+
* anything else as a property AWS::SNS::Topic does not have, which is how
|
|
83
|
+
* real CloudFormation answers one too.
|
|
73
84
|
*/
|
|
74
85
|
isAttributeProperty(name) {
|
|
75
86
|
if (name === topicNamePropertyName ||
|
|
76
|
-
name === topicSubscriptionPropertyName
|
|
87
|
+
name === topicSubscriptionPropertyName ||
|
|
88
|
+
ignoredPropertyReasons.has(name)) {
|
|
77
89
|
return false;
|
|
78
90
|
}
|
|
79
91
|
if (attributePropertyNames.has(name)) {
|
|
@@ -28,3 +28,13 @@ export declare const attributePropertyNames: ReadonlySet<string>;
|
|
|
28
28
|
* become fifteen separate attributes.
|
|
29
29
|
*/
|
|
30
30
|
export declare const unsimulatedPropertyReasons: ReadonlyMap<string, string>;
|
|
31
|
+
/**
|
|
32
|
+
* The real AWS::SNS::Topic properties this simulation has nothing to act on
|
|
33
|
+
* and no reason to fail a stack over.
|
|
34
|
+
*
|
|
35
|
+
* `Tags` is the whole list. The topic is created without them and the omission
|
|
36
|
+
* is recorded against the Resource. A topic behaves the same way with a tag
|
|
37
|
+
* and without one, and a CDK app calling `Tags.of(app).add(...)` tags every
|
|
38
|
+
* topic in it.
|
|
39
|
+
*/
|
|
40
|
+
export declare const ignoredPropertyReasons: ReadonlyMap<string, string>;
|
|
@@ -47,5 +47,20 @@ export const unsimulatedPropertyReasons = new Map([
|
|
|
47
47
|
"DeliveryStatusLogging",
|
|
48
48
|
"delivery status logging writes to CloudWatch Logs, which is not simulated",
|
|
49
49
|
],
|
|
50
|
-
|
|
50
|
+
]);
|
|
51
|
+
/**
|
|
52
|
+
* The real AWS::SNS::Topic properties this simulation has nothing to act on
|
|
53
|
+
* and no reason to fail a stack over.
|
|
54
|
+
*
|
|
55
|
+
* `Tags` is the whole list. The topic is created without them and the omission
|
|
56
|
+
* is recorded against the Resource. A topic behaves the same way with a tag
|
|
57
|
+
* and without one, and a CDK app calling `Tags.of(app).add(...)` tags every
|
|
58
|
+
* topic in it.
|
|
59
|
+
*/
|
|
60
|
+
export const ignoredPropertyReasons = new Map([
|
|
61
|
+
[
|
|
62
|
+
"Tags",
|
|
63
|
+
"AWS::SNS::Topic property Tags is not simulated, so the topic is created " +
|
|
64
|
+
"without them. No simulated service reads a topic tag.",
|
|
65
|
+
],
|
|
51
66
|
]);
|
|
@@ -28,6 +28,7 @@ export class SimCfnSsmParameterCreator {
|
|
|
28
28
|
properties,
|
|
29
29
|
});
|
|
30
30
|
const name = parameterProperties.name();
|
|
31
|
+
parameterProperties.recordIgnoredTags();
|
|
31
32
|
await this.ssm.putParameter({
|
|
32
33
|
input: {
|
|
33
34
|
Name: name,
|
|
@@ -38,7 +39,6 @@ export class SimCfnSsmParameterCreator {
|
|
|
38
39
|
AllowedPattern: parameterProperties.allowedPattern(),
|
|
39
40
|
DataType: parameterProperties.dataType(),
|
|
40
41
|
Policies: parameterProperties.policies(),
|
|
41
|
-
Tags: parameterProperties.tags(),
|
|
42
42
|
},
|
|
43
43
|
}, options);
|
|
44
44
|
const parameter = this.ssm.findParameter(name);
|
|
@@ -1,6 +1,5 @@
|
|
|
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
|
-
import type { SimSsmTag } from "../../command/parameter/parameter.command.js";
|
|
4
3
|
interface SimCfnSsmParameterPropertiesProperties {
|
|
5
4
|
readonly resource: SimCfnResource;
|
|
6
5
|
readonly properties: SimCfnTemplateValueRecord;
|
|
@@ -60,13 +59,19 @@ export declare class SimCfnSsmParameterProperties {
|
|
|
60
59
|
*/
|
|
61
60
|
policies(): string | undefined;
|
|
62
61
|
/**
|
|
63
|
-
*
|
|
62
|
+
* Record the tags the parameter is written without.
|
|
64
63
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
64
|
+
* `Tags` is the one difference from `PutParameter`, which refuses it
|
|
65
|
+
* outright. A template's tags are usually the whole stack's, and a CDK app
|
|
66
|
+
* calling `Tags.of(app).add(...)` tags every parameter in it. The parameter
|
|
67
|
+
* holds the same value either way, so the deploy stands and the omission is
|
|
68
|
+
* recorded.
|
|
69
|
+
*
|
|
70
|
+
* The shape is still read, because CloudFormation carries these as a map of
|
|
71
|
+
* names to values for this Resource type and a template AWS refuses should
|
|
72
|
+
* not deploy here.
|
|
68
73
|
*/
|
|
69
|
-
|
|
74
|
+
recordIgnoredTags(): void;
|
|
70
75
|
private string;
|
|
71
76
|
private stringValue;
|
|
72
77
|
private propertyError;
|
|
@@ -88,23 +88,32 @@ export class SimCfnSsmParameterProperties {
|
|
|
88
88
|
return this.string(this.properties["Policies"], "Policies");
|
|
89
89
|
}
|
|
90
90
|
/**
|
|
91
|
-
*
|
|
91
|
+
* Record the tags the parameter is written without.
|
|
92
92
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
93
|
+
* `Tags` is the one difference from `PutParameter`, which refuses it
|
|
94
|
+
* outright. A template's tags are usually the whole stack's, and a CDK app
|
|
95
|
+
* calling `Tags.of(app).add(...)` tags every parameter in it. The parameter
|
|
96
|
+
* holds the same value either way, so the deploy stands and the omission is
|
|
97
|
+
* recorded.
|
|
98
|
+
*
|
|
99
|
+
* The shape is still read, because CloudFormation carries these as a map of
|
|
100
|
+
* names to values for this Resource type and a template AWS refuses should
|
|
101
|
+
* not deploy here.
|
|
96
102
|
*/
|
|
97
|
-
|
|
103
|
+
recordIgnoredTags() {
|
|
98
104
|
const tags = this.properties["Tags"];
|
|
99
105
|
if (tags === undefined) {
|
|
100
|
-
return
|
|
106
|
+
return;
|
|
101
107
|
}
|
|
102
108
|
if (typeof tags !== "object" || tags === null || Array.isArray(tags)) {
|
|
103
109
|
throw this.propertyError("Tags must be an object");
|
|
104
110
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
111
|
+
for (const [key, value] of Object.entries(tags)) {
|
|
112
|
+
this.stringValue(value, `Tags.${key}`);
|
|
113
|
+
}
|
|
114
|
+
this.resource.ignoreProperty("Tags", "AWS::SSM::Parameter property Tags is not simulated, so the parameter " +
|
|
115
|
+
"is written without them. Nothing reads them back and no " +
|
|
116
|
+
"aws:ResourceTag condition key matches them.");
|
|
108
117
|
}
|
|
109
118
|
string(value, name) {
|
|
110
119
|
if (value === undefined) {
|