@kensio/yulin 1.21.1 → 1.21.3
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/dist/service/cloudformation/resource/factory/sim-cfn-resource-factory.type.d.ts +9 -0
- package/dist/service/cloudformation/resource/sim-cfn-resource.type.d.ts +11 -0
- package/dist/service/cloudformation/resource/update/sim-cfn-resource-update-validator.d.ts +26 -0
- package/dist/service/cloudformation/resource/update/sim-cfn-resource-update-validator.js +55 -0
- package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.d.ts +5 -0
- package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.js +17 -0
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-update-plan.d.ts +7 -1
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-update-plan.js +13 -0
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-updater.js +1 -0
- package/dist/service/dynamodb/cfn/sim-cfn-dynamodb-resource-factory.d.ts +5 -0
- package/dist/service/dynamodb/cfn/sim-cfn-dynamodb-resource-factory.js +15 -0
- package/dist/service/dynamodb/cfn/table/sim-cfn-dynamodb-table-update-validator.d.ts +24 -0
- package/dist/service/dynamodb/cfn/table/sim-cfn-dynamodb-table-update-validator.js +38 -0
- package/dist/service/dynamodb/table/sim-dynamodb-index-update.js +1 -2
- package/dist/service/ses/command/configuration-set/sim-ses-configuration-set-commands.js +13 -5
- package/dist/service/ses/command/configuration-set/sim-ses-configuration-set-options.d.ts +1 -1
- package/dist/service/ses/command/configuration-set/sim-ses-configuration-set-options.js +9 -9
- package/dist/service/ses/command/send/send.command.d.ts +10 -1
- package/dist/service/ses/command/send/sim-ses-read-content.d.ts +2 -1
- package/dist/service/ses/command/send/sim-ses-send-email.js +1 -0
- package/dist/service/ses/command/send/sim-ses-service-send.js +1 -0
- package/dist/service/ses/command/send/sim-ses-simple-content.js +26 -5
- package/dist/service/ses/command/send/sim-ses-template-send.js +1 -0
- package/dist/service/ses/command/suppression/suppression.command.d.ts +3 -2
- package/dist/service/ses/configuration-set/sim-ses-configuration-set.d.ts +9 -10
- package/dist/service/ses/configuration-set/sim-ses-configuration-set.js +7 -8
- package/dist/service/ses/email/sim-ses-sent-email-store.d.ts +8 -0
- package/dist/service/ses/email/sim-ses-sent-email-store.js +17 -0
- package/dist/service/ses/email/sim-ses-sent-email.d.ts +18 -0
- package/dist/service/ses/email/sim-ses-sent-email.js +3 -0
- package/dist/service/ses/error/sim-ses.error.d.ts +10 -0
- package/dist/service/ses/error/sim-ses.error.js +10 -0
- package/dist/service/ses/feedback/sim-ses-feedback-recorder.d.ts +34 -0
- package/dist/service/ses/feedback/sim-ses-feedback-recorder.js +55 -0
- package/dist/service/ses/index.d.ts +3 -2
- package/dist/service/ses/index.js +2 -1
- package/dist/service/ses/sim-ses-commands.d.ts +2 -0
- package/dist/service/ses/sim-ses-commands.js +9 -0
- package/dist/service/ses/sim-ses-configuration-sets.d.ts +4 -4
- package/dist/service/ses/sim-ses-configuration-sets.js +4 -4
- package/dist/service/ses/sim-ses-suppression.d.ts +3 -2
- package/dist/service/ses/sim-ses-suppression.js +3 -2
- package/dist/service/ses/sim-ses-v2.d.ts +13 -0
- package/dist/service/ses/sim-ses-v2.js +13 -0
- package/dist/service/ses/suppression/sim-ses-suppressed-destination.d.ts +3 -4
- package/dist/service/ses/suppression/sim-ses-suppressed-destination.js +3 -4
- package/dist/service/ses/suppression/sim-ses-suppression-list.d.ts +4 -4
- package/dist/service/ses/suppression/sim-ses-suppression-list.js +4 -4
- package/docs/services/ses/README.md +164 -41
- package/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SimCfnResource, SimCloudFormationResourceCreateContext, SimCloudFormationResourceDeleteContext } from "../sim-cfn-resource.js";
|
|
2
|
+
import type { SimCloudFormationResourceUpdateContext } from "../sim-cfn-resource.type.js";
|
|
2
3
|
export interface SimCfnServiceResourceFactory {
|
|
3
4
|
create(resourceTypeName: string, resource: SimCfnResource, context: SimCloudFormationResourceCreateContext): Promise<object | undefined>;
|
|
4
5
|
/**
|
|
@@ -15,6 +16,14 @@ export interface SimCfnServiceResourceFactory {
|
|
|
15
16
|
* the teardown records it and carries on.
|
|
16
17
|
*/
|
|
17
18
|
delete(resourceTypeName: string, resource: SimCfnResource, context: SimCloudFormationResourceDeleteContext): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Refuse an invalid replacement before CloudFormation deletes anything.
|
|
21
|
+
*
|
|
22
|
+
* Most simulated Resource types have no update-specific validation. A
|
|
23
|
+
* service can implement this hook when an update carries constraints that a
|
|
24
|
+
* create request cannot check.
|
|
25
|
+
*/
|
|
26
|
+
assertUpdateAllowed?(resourceTypeName: string, current: SimCfnResource, updated: SimCfnResource, context: SimCloudFormationResourceUpdateContext): Promise<void> | void;
|
|
18
27
|
}
|
|
19
28
|
export interface SimCloudFormationParsedResourceType {
|
|
20
29
|
readonly providerName: string;
|
|
@@ -86,3 +86,14 @@ export interface SimCloudFormationResourceDeleteContext {
|
|
|
86
86
|
/** The principal the teardown runs as, as creation carries it. */
|
|
87
87
|
readonly caller?: SimAwsCaller | undefined;
|
|
88
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* The two resolved Resource definitions available to update validation.
|
|
91
|
+
*/
|
|
92
|
+
export interface SimCloudFormationResourceUpdateContext {
|
|
93
|
+
readonly simAws: SimAws;
|
|
94
|
+
readonly currentResources: ReadonlyMap<string, SimCfnResource>;
|
|
95
|
+
readonly updatedResources: ReadonlyMap<string, SimCfnResource>;
|
|
96
|
+
readonly currentResolvedProperties: SimCfnTemplateValueRecord;
|
|
97
|
+
readonly updatedResolvedProperties: SimCfnTemplateValueRecord;
|
|
98
|
+
readonly caller?: SimAwsCaller | undefined;
|
|
99
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SimAws } from "../../../aws/sim-aws.js";
|
|
2
|
+
import type { SimAwsCaller } from "../../../aws/caller/sim-aws-caller.js";
|
|
3
|
+
import type { SimCfnResource } from "../sim-cfn-resource.js";
|
|
4
|
+
interface SimCfnResourceUpdateValidatorProperties {
|
|
5
|
+
readonly current: SimCfnResource;
|
|
6
|
+
readonly updated: SimCfnResource;
|
|
7
|
+
}
|
|
8
|
+
interface AssertSimCfnResourceUpdateAllowedProperties {
|
|
9
|
+
readonly simAws: SimAws;
|
|
10
|
+
readonly currentResources: ReadonlyMap<string, SimCfnResource>;
|
|
11
|
+
readonly updatedResources: ReadonlyMap<string, SimCfnResource>;
|
|
12
|
+
readonly caller?: SimAwsCaller | undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Runs service-specific validation for one Resource replacement.
|
|
16
|
+
*/
|
|
17
|
+
export declare class SimCfnResourceUpdateValidator {
|
|
18
|
+
private readonly current;
|
|
19
|
+
private readonly updated;
|
|
20
|
+
constructor(properties: SimCfnResourceUpdateValidatorProperties);
|
|
21
|
+
/**
|
|
22
|
+
* Refuse an invalid update before its current Resource is deleted.
|
|
23
|
+
*/
|
|
24
|
+
assertAllowed(properties: AssertSimCfnResourceUpdateAllowedProperties): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { parseSimCloudFormationResourceType } from "../parser/sim-cfn-resource-parser.js";
|
|
2
|
+
import { resolveSimCloudFormationServiceResourceFactory } from "../resolve/service/sim-cfn-service-resolver.js";
|
|
3
|
+
import { isSimCfnUnsupportedResourceError } from "../unsupported/sim-cfn-unsupported-resource.js";
|
|
4
|
+
/**
|
|
5
|
+
* Runs service-specific validation for one Resource replacement.
|
|
6
|
+
*/
|
|
7
|
+
export class SimCfnResourceUpdateValidator {
|
|
8
|
+
current;
|
|
9
|
+
updated;
|
|
10
|
+
constructor(properties) {
|
|
11
|
+
this.current = properties.current;
|
|
12
|
+
this.updated = properties.updated;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Refuse an invalid update before its current Resource is deleted.
|
|
16
|
+
*/
|
|
17
|
+
async assertAllowed(properties) {
|
|
18
|
+
const { current, updated } = this;
|
|
19
|
+
const { simAws, currentResources, updatedResources, caller } = properties;
|
|
20
|
+
const { type } = updated;
|
|
21
|
+
if (type === undefined || current.type !== type) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const resourceType = parseSimCloudFormationResourceType(type);
|
|
25
|
+
let factory;
|
|
26
|
+
try {
|
|
27
|
+
factory = resolveSimCloudFormationServiceResourceFactory(simAws, updated.accountRegionScope, resourceType);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (isSimCfnUnsupportedResourceError(error)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
if (factory.assertUpdateAllowed === undefined) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
await factory.assertUpdateAllowed(resourceType.resourceTypeName, current, updated, {
|
|
39
|
+
simAws,
|
|
40
|
+
currentResources,
|
|
41
|
+
updatedResources,
|
|
42
|
+
currentResolvedProperties: await current.resolvedProperties({
|
|
43
|
+
simAws,
|
|
44
|
+
resources: currentResources,
|
|
45
|
+
caller,
|
|
46
|
+
}),
|
|
47
|
+
updatedResolvedProperties: await updated.resolvedProperties({
|
|
48
|
+
simAws,
|
|
49
|
+
resources: updatedResources,
|
|
50
|
+
caller,
|
|
51
|
+
}),
|
|
52
|
+
caller,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -8,6 +8,7 @@ import type { SimCfnResource } from "../resource/sim-cfn-resource.js";
|
|
|
8
8
|
import type { SimCfnTemplate } from "../template/sim-cfn-template.js";
|
|
9
9
|
import type { SimCfnResourceOrder } from "./deploy/sim-cfn-resource-order.js";
|
|
10
10
|
import { SimCfnStackUpdater } from "./update/sim-cfn-stack-updater.js";
|
|
11
|
+
import type { SimCfnStackResourceReplacement } from "./update/sim-cfn-stack-update-plan.js";
|
|
11
12
|
import type { SimCloudFormationStackName } from "./sim-cfn-stack.js";
|
|
12
13
|
interface SimCfnStackUpdateProperties {
|
|
13
14
|
readonly background: BackgroundScheduler;
|
|
@@ -87,6 +88,10 @@ export declare class SimCfnStackResourceOperations {
|
|
|
87
88
|
* Stack.
|
|
88
89
|
*/
|
|
89
90
|
delete(resources: ReadonlyMap<string, SimCfnResource>, deleting: readonly SimCfnResource[]): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Validate every Resource replacement before the update changes the Stack.
|
|
93
|
+
*/
|
|
94
|
+
assertUpdatesAllowed(currentResources: ReadonlyMap<string, SimCfnResource>, updatedResources: ReadonlyMap<string, SimCfnResource>, replacements: readonly SimCfnStackResourceReplacement[]): Promise<void>;
|
|
90
95
|
/**
|
|
91
96
|
* An update of this Stack from a changed template, in the same scope.
|
|
92
97
|
*/
|
|
@@ -2,6 +2,7 @@ import { SimCdkAssetsPublisher } from "../cdk/assets/sim-cdk-assets-publisher.js
|
|
|
2
2
|
import { SimCfnStackResourceCreator } from "./deploy/sim-cfn-stack-resource-creator.js";
|
|
3
3
|
import { SimCfnStackResourceDeleter } from "./teardown/sim-cfn-stack-resource-deleter.js";
|
|
4
4
|
import { SimCfnStackUpdater } from "./update/sim-cfn-stack-updater.js";
|
|
5
|
+
import { SimCfnResourceUpdateValidator } from "../resource/update/sim-cfn-resource-update-validator.js";
|
|
5
6
|
/**
|
|
6
7
|
* What a Stack can ask simulated AWS to do with its Resources.
|
|
7
8
|
*
|
|
@@ -77,6 +78,22 @@ export class SimCfnStackResourceOperations {
|
|
|
77
78
|
async delete(resources, deleting) {
|
|
78
79
|
await this.deleter(resources).delete(deleting);
|
|
79
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Validate every Resource replacement before the update changes the Stack.
|
|
83
|
+
*/
|
|
84
|
+
async assertUpdatesAllowed(currentResources, updatedResources, replacements) {
|
|
85
|
+
await Promise.all(replacements.map(async ({ current, updated }) => {
|
|
86
|
+
await new SimCfnResourceUpdateValidator({
|
|
87
|
+
current,
|
|
88
|
+
updated,
|
|
89
|
+
}).assertAllowed({
|
|
90
|
+
simAws: this.simAws,
|
|
91
|
+
currentResources,
|
|
92
|
+
updatedResources,
|
|
93
|
+
caller: this.caller,
|
|
94
|
+
});
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
80
97
|
/**
|
|
81
98
|
* An update of this Stack from a changed template, in the same scope.
|
|
82
99
|
*/
|
|
@@ -3,6 +3,10 @@ interface SimCfnStackUpdatePlanProperties {
|
|
|
3
3
|
readonly current: ReadonlyMap<string, SimCfnResource>;
|
|
4
4
|
readonly updated: ReadonlyMap<string, SimCfnResource>;
|
|
5
5
|
}
|
|
6
|
+
export interface SimCfnStackResourceReplacement {
|
|
7
|
+
readonly current: SimCfnResource;
|
|
8
|
+
readonly updated: SimCfnResource;
|
|
9
|
+
}
|
|
6
10
|
/**
|
|
7
11
|
* What an update has to do to a Stack's Resources, worked out before any of it
|
|
8
12
|
* happens.
|
|
@@ -17,6 +21,8 @@ interface SimCfnStackUpdatePlanProperties {
|
|
|
17
21
|
* simCfnStackReplacedLogicalIds decides what changed.
|
|
18
22
|
*/
|
|
19
23
|
export declare class SimCfnStackUpdatePlan {
|
|
24
|
+
/** The current and updated halves of each Resource replacement. */
|
|
25
|
+
readonly replacements: readonly SimCfnStackResourceReplacement[];
|
|
20
26
|
/**
|
|
21
27
|
* The deployed Resources to delete: the ones the new template drops, and the
|
|
22
28
|
* deployed halves of the ones it replaces.
|
|
@@ -27,7 +33,7 @@ export declare class SimCfnStackUpdatePlan {
|
|
|
27
33
|
* halves of the ones it replaces.
|
|
28
34
|
*/
|
|
29
35
|
readonly creations: readonly SimCfnResource[];
|
|
30
|
-
|
|
36
|
+
readonly updated: ReadonlyMap<string, SimCfnResource>;
|
|
31
37
|
constructor(properties: SimCfnStackUpdatePlanProperties);
|
|
32
38
|
/**
|
|
33
39
|
* Whether the update changes any Resource at all.
|
|
@@ -13,6 +13,8 @@ import { simCfnStackReplacedLogicalIds } from "./sim-cfn-stack-replaced-resource
|
|
|
13
13
|
* simCfnStackReplacedLogicalIds decides what changed.
|
|
14
14
|
*/
|
|
15
15
|
export class SimCfnStackUpdatePlan {
|
|
16
|
+
/** The current and updated halves of each Resource replacement. */
|
|
17
|
+
replacements;
|
|
16
18
|
/**
|
|
17
19
|
* The deployed Resources to delete: the ones the new template drops, and the
|
|
18
20
|
* deployed halves of the ones it replaces.
|
|
@@ -28,6 +30,17 @@ export class SimCfnStackUpdatePlan {
|
|
|
28
30
|
const { current, updated } = properties;
|
|
29
31
|
const replaced = simCfnStackReplacedLogicalIds({ current, updated });
|
|
30
32
|
this.updated = updated;
|
|
33
|
+
this.replacements = replaced
|
|
34
|
+
.values()
|
|
35
|
+
.map((logicalId) => {
|
|
36
|
+
const currentResource = current.get(logicalId);
|
|
37
|
+
const updatedResource = updated.get(logicalId);
|
|
38
|
+
if (currentResource === undefined || updatedResource === undefined) {
|
|
39
|
+
throw new Error(`CloudFormation replacement ${logicalId} is missing one of its Resource definitions`);
|
|
40
|
+
}
|
|
41
|
+
return { current: currentResource, updated: updatedResource };
|
|
42
|
+
})
|
|
43
|
+
.toArray();
|
|
31
44
|
// Both lists are worked out now rather than on demand, because the Stack's
|
|
32
45
|
// Resource map is the current one and applying the plan changes it.
|
|
33
46
|
this.deletions = current
|
|
@@ -57,6 +57,7 @@ export class SimCfnStackUpdater {
|
|
|
57
57
|
async apply() {
|
|
58
58
|
const { resources, operations } = this.properties;
|
|
59
59
|
const { plan } = this;
|
|
60
|
+
await operations.assertUpdatesAllowed(resources, plan.updated, plan.replacements);
|
|
60
61
|
await operations.delete(resources, plan.deletions);
|
|
61
62
|
plan.applyTo(resources);
|
|
62
63
|
await operations.create(resources, plan.creations);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SimCfnServiceResourceFactory } from "../../cloudformation/resource/factory/sim-cfn-resource-factory.type.js";
|
|
2
2
|
import type { SimCfnResource, SimCloudFormationResourceCreateContext, SimCloudFormationResourceDeleteContext } from "../../cloudformation/resource/sim-cfn-resource.js";
|
|
3
|
+
import type { SimCloudFormationResourceUpdateContext } from "../../cloudformation/resource/sim-cfn-resource.type.js";
|
|
3
4
|
import type { SimDynamoDb } from "../sim-dynamodb.js";
|
|
4
5
|
interface SimDynamoDbCfnResourceFactoryProperties {
|
|
5
6
|
readonly dynamoDb: SimDynamoDb;
|
|
@@ -26,5 +27,9 @@ export declare class SimDynamoDbCfnResourceFactory implements SimCfnServiceResou
|
|
|
26
27
|
* Delete a simulated DynamoDB resource created from a CloudFormation Resource.
|
|
27
28
|
*/
|
|
28
29
|
delete(resourceTypeName: string, resource: SimCfnResource, context: SimCloudFormationResourceDeleteContext): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Validate a DynamoDB Resource replacement before its table is deleted.
|
|
32
|
+
*/
|
|
33
|
+
assertUpdateAllowed(resourceTypeName: string, current: SimCfnResource, updated: SimCfnResource, context: SimCloudFormationResourceUpdateContext): void;
|
|
29
34
|
}
|
|
30
35
|
export {};
|
|
@@ -2,6 +2,7 @@ import { simCfnResourceCallerOptions } from "../../cloudformation/resource/calle
|
|
|
2
2
|
import { SimCfnDynamoDbGlobalTableCreator } from "./global-table/sim-cfn-dynamodb-global-table-creator.js";
|
|
3
3
|
import { SimCfnDynamoDbTableCreator } from "./table/sim-cfn-dynamodb-table-creator.js";
|
|
4
4
|
import { SimCfnDynamoDbResourceDeleter } from "./sim-cfn-dynamodb-resource-deleter.js";
|
|
5
|
+
import { SimCfnDynamoDbTableUpdateValidator } from "./table/sim-cfn-dynamodb-table-update-validator.js";
|
|
5
6
|
/**
|
|
6
7
|
* CloudFormation Resource factory for simulated DynamoDB resources.
|
|
7
8
|
*/
|
|
@@ -50,4 +51,18 @@ export class SimDynamoDbCfnResourceFactory {
|
|
|
50
51
|
async delete(resourceTypeName, resource, context) {
|
|
51
52
|
await this.deleter.delete(resourceTypeName, resource, simCfnResourceCallerOptions(context.caller));
|
|
52
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Validate a DynamoDB Resource replacement before its table is deleted.
|
|
56
|
+
*/
|
|
57
|
+
assertUpdateAllowed(resourceTypeName, current, updated, context) {
|
|
58
|
+
if (resourceTypeName !== "Table") {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
new SimCfnDynamoDbTableUpdateValidator({
|
|
62
|
+
currentResource: current,
|
|
63
|
+
updatedResource: updated,
|
|
64
|
+
currentProperties: context.currentResolvedProperties,
|
|
65
|
+
updatedProperties: context.updatedResolvedProperties,
|
|
66
|
+
}).assertAllowed();
|
|
67
|
+
}
|
|
53
68
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
interface SimCfnDynamoDbTableUpdateValidatorProperties {
|
|
4
|
+
readonly currentResource: SimCfnResource;
|
|
5
|
+
readonly updatedResource: SimCfnResource;
|
|
6
|
+
readonly currentProperties: SimCfnTemplateValueRecord;
|
|
7
|
+
readonly updatedProperties: SimCfnTemplateValueRecord;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Validates constraints that apply when CloudFormation updates a table.
|
|
11
|
+
*/
|
|
12
|
+
export declare class SimCfnDynamoDbTableUpdateValidator {
|
|
13
|
+
private readonly properties;
|
|
14
|
+
constructor(properties: SimCfnDynamoDbTableUpdateValidatorProperties);
|
|
15
|
+
/**
|
|
16
|
+
* Refuse a template diff that asks DynamoDB for too many index operations.
|
|
17
|
+
*
|
|
18
|
+
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html
|
|
19
|
+
*/
|
|
20
|
+
assertAllowed(): void;
|
|
21
|
+
private tableProperties;
|
|
22
|
+
private indexUpdates;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { readSimDynamoDbIndexName } from "../../secondary-index/sim-dynamodb-index-name.js";
|
|
2
|
+
import { SimDynamoDbIndexUpdate } from "../../table/sim-dynamodb-index-update.js";
|
|
3
|
+
import { SimCfnDynamoDbTableProperties } from "./sim-cfn-dynamodb-table-properties.js";
|
|
4
|
+
/**
|
|
5
|
+
* Validates constraints that apply when CloudFormation updates a table.
|
|
6
|
+
*/
|
|
7
|
+
export class SimCfnDynamoDbTableUpdateValidator {
|
|
8
|
+
properties;
|
|
9
|
+
constructor(properties) {
|
|
10
|
+
this.properties = properties;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Refuse a template diff that asks DynamoDB for too many index operations.
|
|
14
|
+
*
|
|
15
|
+
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html
|
|
16
|
+
*/
|
|
17
|
+
assertAllowed() {
|
|
18
|
+
const { currentResource, updatedResource } = this.properties;
|
|
19
|
+
const current = this.tableProperties(currentResource, this.properties.currentProperties).globalSecondaryIndexes();
|
|
20
|
+
const updated = this.tableProperties(updatedResource, this.properties.updatedProperties).globalSecondaryIndexes();
|
|
21
|
+
SimDynamoDbIndexUpdate.fromInput(this.indexUpdates(current, updated));
|
|
22
|
+
}
|
|
23
|
+
tableProperties(resource, properties) {
|
|
24
|
+
return new SimCfnDynamoDbTableProperties({ resource, properties });
|
|
25
|
+
}
|
|
26
|
+
indexUpdates(current, updated) {
|
|
27
|
+
const currentNames = new Set(current.map((index) => readSimDynamoDbIndexName(index.IndexName)));
|
|
28
|
+
const updatedNames = new Set(updated.map((index) => readSimDynamoDbIndexName(index.IndexName)));
|
|
29
|
+
return [
|
|
30
|
+
...updated
|
|
31
|
+
.filter((index) => !currentNames.has(readSimDynamoDbIndexName(index.IndexName)))
|
|
32
|
+
.map((index) => ({ Create: index })),
|
|
33
|
+
...current
|
|
34
|
+
.filter((index) => !updatedNames.has(readSimDynamoDbIndexName(index.IndexName)))
|
|
35
|
+
.map((index) => ({ Delete: { IndexName: index.IndexName } })),
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -73,8 +73,7 @@ export class SimDynamoDbIndexUpdate {
|
|
|
73
73
|
.map((entry) => deletedIndexName(entry))
|
|
74
74
|
.filter((name) => name !== undefined);
|
|
75
75
|
if (created.length + deleted.length > 1) {
|
|
76
|
-
throw new SimDynamoDbValidationException("
|
|
77
|
-
"UpdateTable operation");
|
|
76
|
+
throw new SimDynamoDbValidationException("Cannot perform more than one GSI creation or deletion in a single update");
|
|
78
77
|
}
|
|
79
78
|
return new this({ created: created.at(0), deletedName: deleted.at(0) });
|
|
80
79
|
}
|
|
@@ -82,15 +82,13 @@ export class SimSesConfigurationSetCommands {
|
|
|
82
82
|
/**
|
|
83
83
|
* What GetConfigurationSet answers with.
|
|
84
84
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
85
|
+
* Sending, delivery and reputation options report their applied defaults.
|
|
86
|
+
* Suppression options stay absent unless the set declared an override.
|
|
87
87
|
*/
|
|
88
88
|
function reported(configurationSet) {
|
|
89
89
|
return {
|
|
90
90
|
ConfigurationSetName: configurationSet.configurationSetName,
|
|
91
|
-
|
|
92
|
-
SuppressedReasons: configurationSet.suppressedReasons,
|
|
93
|
-
},
|
|
91
|
+
...reportedSuppressionOptions(configurationSet),
|
|
94
92
|
SendingOptions: { SendingEnabled: configurationSet.sendingEnabled },
|
|
95
93
|
DeliveryOptions: {
|
|
96
94
|
TlsPolicy: configurationSet.deliveryOptions.tlsPolicy,
|
|
@@ -102,3 +100,13 @@ function reported(configurationSet) {
|
|
|
102
100
|
},
|
|
103
101
|
};
|
|
104
102
|
}
|
|
103
|
+
function reportedSuppressionOptions(configurationSet) {
|
|
104
|
+
if (configurationSet.suppressedReasons === undefined) {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
SuppressionOptions: {
|
|
109
|
+
SuppressedReasons: configurationSet.suppressedReasons,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -5,6 +5,6 @@ import type { SimCreateConfigurationSetCommandInput } from "./configuration-set.
|
|
|
5
5
|
* refuses.
|
|
6
6
|
*
|
|
7
7
|
* A set declaring none of these gets the defaults real SES applies: sending
|
|
8
|
-
* on,
|
|
8
|
+
* on, account-level suppression, optional TLS and no reputation metrics.
|
|
9
9
|
*/
|
|
10
10
|
export declare function readSimSesConfigurationSetOptions(input: SimCreateConfigurationSetCommandInput): SimSesConfigurationSetOptions;
|
|
@@ -11,11 +11,11 @@ const tlsPolicies = new Set(["REQUIRE", "OPTIONAL"]);
|
|
|
11
11
|
* refuses.
|
|
12
12
|
*
|
|
13
13
|
* A set declaring none of these gets the defaults real SES applies: sending
|
|
14
|
-
* on,
|
|
14
|
+
* on, account-level suppression, optional TLS and no reputation metrics.
|
|
15
15
|
*/
|
|
16
16
|
export function readSimSesConfigurationSetOptions(input) {
|
|
17
17
|
return {
|
|
18
|
-
suppressedReasons: readSuppressedReasons(input.SuppressionOptions
|
|
18
|
+
suppressedReasons: readSuppressedReasons(input.SuppressionOptions),
|
|
19
19
|
sendingEnabled: input.SendingOptions?.SendingEnabled ?? true,
|
|
20
20
|
deliveryOptions: readDeliveryOptions(input.DeliveryOptions),
|
|
21
21
|
reputationOptions: {
|
|
@@ -26,15 +26,15 @@ export function readSimSesConfigurationSetOptions(input) {
|
|
|
26
26
|
/**
|
|
27
27
|
* The suppression reasons a set names, refusing one SES has no meaning for.
|
|
28
28
|
*
|
|
29
|
-
* An absent `SuppressionOptions`
|
|
30
|
-
* account-level
|
|
31
|
-
*
|
|
29
|
+
* An absent `SuppressionOptions` returns undefined, which preserves the
|
|
30
|
+
* account-level fallback. A present group with no reasons returns an empty
|
|
31
|
+
* override and disables suppression for messages sent through the set.
|
|
32
32
|
*/
|
|
33
|
-
function readSuppressedReasons(
|
|
34
|
-
if (
|
|
35
|
-
return
|
|
33
|
+
function readSuppressedReasons(options) {
|
|
34
|
+
if (options === undefined) {
|
|
35
|
+
return undefined;
|
|
36
36
|
}
|
|
37
|
-
return
|
|
37
|
+
return (options.SuppressedReasons ?? []).map((reason) => {
|
|
38
38
|
if (!isSuppressedReason(reason)) {
|
|
39
39
|
throw new SimSesBadRequestException(`1 validation error detected: Value '${reason}' at ` +
|
|
40
40
|
`'suppressionOptions.suppressedReasons' failed to satisfy ` +
|
|
@@ -11,11 +11,20 @@ export interface SimSesBody {
|
|
|
11
11
|
readonly Text?: SimSesContent | undefined;
|
|
12
12
|
readonly Html?: SimSesContent | undefined;
|
|
13
13
|
}
|
|
14
|
+
export interface SimSesAttachment {
|
|
15
|
+
readonly RawContent?: Uint8Array | undefined;
|
|
16
|
+
readonly ContentDisposition?: "ATTACHMENT" | "INLINE" | undefined;
|
|
17
|
+
readonly FileName?: string | undefined;
|
|
18
|
+
readonly ContentDescription?: string | undefined;
|
|
19
|
+
readonly ContentId?: string | undefined;
|
|
20
|
+
readonly ContentTransferEncoding?: "BASE64" | "QUOTED_PRINTABLE" | "SEVEN_BIT" | undefined;
|
|
21
|
+
readonly ContentType?: string | undefined;
|
|
22
|
+
}
|
|
14
23
|
export interface SimSesMessage {
|
|
15
24
|
readonly Subject?: SimSesContent | undefined;
|
|
16
25
|
readonly Body?: SimSesBody | undefined;
|
|
17
26
|
readonly Headers?: readonly unknown[] | undefined;
|
|
18
|
-
readonly Attachments?: readonly
|
|
27
|
+
readonly Attachments?: readonly SimSesAttachment[] | undefined;
|
|
19
28
|
}
|
|
20
29
|
export interface SimSesRawMessage {
|
|
21
30
|
readonly Data?: Uint8Array | string | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SimSesSentEmailBody } from "../../email/sim-ses-sent-email.js";
|
|
1
|
+
import type { SimSesSentEmailAttachment, SimSesSentEmailBody } from "../../email/sim-ses-sent-email.js";
|
|
2
2
|
/**
|
|
3
3
|
* What a message says, read out of the content of a send.
|
|
4
4
|
*
|
|
@@ -11,6 +11,7 @@ import type { SimSesSentEmailBody } from "../../email/sim-ses-sent-email.js";
|
|
|
11
11
|
export interface SimSesReadContent {
|
|
12
12
|
readonly subject: string;
|
|
13
13
|
readonly body: SimSesSentEmailBody;
|
|
14
|
+
readonly attachments: readonly SimSesSentEmailAttachment[];
|
|
14
15
|
/** The template this was rendered from, if it was rendered from a stored one. */
|
|
15
16
|
readonly templateName: string | undefined;
|
|
16
17
|
/** The data the placeholders were filled from, parsed out of the JSON. */
|
|
@@ -65,6 +65,7 @@ export class SimSesSendEmail {
|
|
|
65
65
|
replyToAddresses: [...(input.ReplyToAddresses ?? [])],
|
|
66
66
|
subject: content.subject,
|
|
67
67
|
body: content.body,
|
|
68
|
+
attachments: content.attachments,
|
|
68
69
|
templateName: content.templateName,
|
|
69
70
|
templateData: content.templateData,
|
|
70
71
|
configurationSetName,
|
|
@@ -62,6 +62,7 @@ export class SimSesServiceSend {
|
|
|
62
62
|
replyToAddresses: [...request.replyToAddresses],
|
|
63
63
|
subject: request.subject,
|
|
64
64
|
body: { text: request.body, html: undefined },
|
|
65
|
+
attachments: [],
|
|
65
66
|
templateName: undefined,
|
|
66
67
|
templateData: undefined,
|
|
67
68
|
configurationSetName,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SimSesBadRequestException
|
|
1
|
+
import { SimSesBadRequestException } from "../../error/sim-ses.error.js";
|
|
2
2
|
/**
|
|
3
3
|
* Read a message a send wrote out in full, refusing what real SES refuses and
|
|
4
4
|
* what this simulation does not model.
|
|
@@ -8,10 +8,6 @@ import { SimSesBadRequestException, SimSesUnsupportedOperationException, } from
|
|
|
8
8
|
* not have accepted.
|
|
9
9
|
*/
|
|
10
10
|
export function readSimSesSimpleMessage(message) {
|
|
11
|
-
if (message.Attachments !== undefined) {
|
|
12
|
-
throw new SimSesUnsupportedOperationException("Attachments are not simulated, so SendEmail refuses them rather than " +
|
|
13
|
-
"recording a message without them");
|
|
14
|
-
}
|
|
15
11
|
const subject = message.Subject?.Data;
|
|
16
12
|
if (subject === undefined) {
|
|
17
13
|
throw new SimSesBadRequestException("1 validation error detected: Value at 'content.simple.subject' " +
|
|
@@ -26,7 +22,32 @@ export function readSimSesSimpleMessage(message) {
|
|
|
26
22
|
return {
|
|
27
23
|
subject,
|
|
28
24
|
body: { text, html },
|
|
25
|
+
attachments: (message.Attachments ?? []).map((attachment, index) => readAttachment(attachment, index)),
|
|
29
26
|
templateName: undefined,
|
|
30
27
|
templateData: undefined,
|
|
31
28
|
};
|
|
32
29
|
}
|
|
30
|
+
/** Read and validate one attachment from a simple message. */
|
|
31
|
+
function readAttachment(attachment, index) {
|
|
32
|
+
if (attachment.RawContent === undefined) {
|
|
33
|
+
throw missingAttachmentMember(index, "rawContent");
|
|
34
|
+
}
|
|
35
|
+
if (attachment.FileName === undefined) {
|
|
36
|
+
throw missingAttachmentMember(index, "fileName");
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
rawContent: Uint8Array.from(attachment.RawContent),
|
|
40
|
+
fileName: attachment.FileName,
|
|
41
|
+
contentType: attachment.ContentType,
|
|
42
|
+
contentDisposition: attachment.ContentDisposition,
|
|
43
|
+
contentDescription: attachment.ContentDescription,
|
|
44
|
+
contentId: attachment.ContentId,
|
|
45
|
+
contentTransferEncoding: attachment.ContentTransferEncoding,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** Build the SES validation error for a missing attachment member. */
|
|
49
|
+
function missingAttachmentMember(index, member) {
|
|
50
|
+
return new SimSesBadRequestException("1 validation error detected: Value at " +
|
|
51
|
+
`'content.simple.attachments.${String(index)}.${member}' failed to ` +
|
|
52
|
+
"satisfy constraint: Member must not be null");
|
|
53
|
+
}
|
|
@@ -2,8 +2,9 @@ import type { SimResponseMetadata } from "../../../aws/metadata/response-metadat
|
|
|
2
2
|
/**
|
|
3
3
|
* What a suppression list entry says, in the shape the SES v2 API reports it.
|
|
4
4
|
*
|
|
5
|
-
* `Attributes` is left out.
|
|
6
|
-
* address on the list
|
|
5
|
+
* `Attributes` is left out. Real SES uses it for details of the feedback event
|
|
6
|
+
* that put the address on the list. Explicit simulator feedback records the
|
|
7
|
+
* reason and time only.
|
|
7
8
|
*/
|
|
8
9
|
export interface SimSesSuppressedDestinationDetail {
|
|
9
10
|
readonly EmailAddress: string;
|
|
@@ -32,7 +32,7 @@ export interface SimSesConfigurationSetReputationOptions {
|
|
|
32
32
|
* Everything a configuration set holds apart from its name.
|
|
33
33
|
*/
|
|
34
34
|
export interface SimSesConfigurationSetOptions {
|
|
35
|
-
readonly suppressedReasons: readonly SimSesSuppressedReason[];
|
|
35
|
+
readonly suppressedReasons: readonly SimSesSuppressedReason[] | undefined;
|
|
36
36
|
readonly sendingEnabled: boolean;
|
|
37
37
|
readonly deliveryOptions: SimSesConfigurationSetDeliveryOptions;
|
|
38
38
|
readonly reputationOptions: SimSesConfigurationSetReputationOptions;
|
|
@@ -48,11 +48,9 @@ interface SimSesConfigurationSetProperties {
|
|
|
48
48
|
* A configuration set is a named group of settings a send can be made under,
|
|
49
49
|
* covering suppression, whether sending is on, how the message is handed on
|
|
50
50
|
* and whether reputation metrics are published. None of it changes what a
|
|
51
|
-
* message says
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* Sending is the exception, and it is left to the work that attaches a set to
|
|
55
|
-
* a send. Here the switch is recorded and acted on by nothing.
|
|
51
|
+
* message says. The sending switch controls acceptance, and the suppression
|
|
52
|
+
* reasons control feedback. Delivery and reputation options remain state for
|
|
53
|
+
* a test to read back.
|
|
56
54
|
*/
|
|
57
55
|
export declare class SimSesConfigurationSet {
|
|
58
56
|
readonly configurationSetName: string;
|
|
@@ -60,11 +58,12 @@ export declare class SimSesConfigurationSet {
|
|
|
60
58
|
/**
|
|
61
59
|
* The reasons that would put a recipient on the account suppression list.
|
|
62
60
|
*
|
|
63
|
-
*
|
|
64
|
-
* to the account setting
|
|
65
|
-
* suppression
|
|
61
|
+
* Undefined where the set declares no `SuppressionOptions`, which makes a
|
|
62
|
+
* send fall back to the account setting. An empty list is an explicit
|
|
63
|
+
* override that disables suppression for feedback recorded for a message
|
|
64
|
+
* sent through this set.
|
|
66
65
|
*/
|
|
67
|
-
readonly suppressedReasons: readonly SimSesSuppressedReason[];
|
|
66
|
+
readonly suppressedReasons: readonly SimSesSuppressedReason[] | undefined;
|
|
68
67
|
/** Whether SES would accept a send made through this set. */
|
|
69
68
|
readonly sendingEnabled: boolean;
|
|
70
69
|
readonly deliveryOptions: SimSesConfigurationSetDeliveryOptions;
|
|
@@ -10,11 +10,9 @@ export const simSesSuppressedReasons = [
|
|
|
10
10
|
* A configuration set is a named group of settings a send can be made under,
|
|
11
11
|
* covering suppression, whether sending is on, how the message is handed on
|
|
12
12
|
* and whether reputation metrics are published. None of it changes what a
|
|
13
|
-
* message says
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* Sending is the exception, and it is left to the work that attaches a set to
|
|
17
|
-
* a send. Here the switch is recorded and acted on by nothing.
|
|
13
|
+
* message says. The sending switch controls acceptance, and the suppression
|
|
14
|
+
* reasons control feedback. Delivery and reputation options remain state for
|
|
15
|
+
* a test to read back.
|
|
18
16
|
*/
|
|
19
17
|
export class SimSesConfigurationSet {
|
|
20
18
|
configurationSetName;
|
|
@@ -22,9 +20,10 @@ export class SimSesConfigurationSet {
|
|
|
22
20
|
/**
|
|
23
21
|
* The reasons that would put a recipient on the account suppression list.
|
|
24
22
|
*
|
|
25
|
-
*
|
|
26
|
-
* to the account setting
|
|
27
|
-
* suppression
|
|
23
|
+
* Undefined where the set declares no `SuppressionOptions`, which makes a
|
|
24
|
+
* send fall back to the account setting. An empty list is an explicit
|
|
25
|
+
* override that disables suppression for feedback recorded for a message
|
|
26
|
+
* sent through this set.
|
|
28
27
|
*/
|
|
29
28
|
suppressedReasons;
|
|
30
29
|
/** Whether SES would accept a send made through this set. */
|