Skip to content

Commit 0b911ef

Browse files
authored
feat(appsync): support for read consistency during DynamoDB reads (#20793)
---- Motivation: DynamoDB uses eventually consistent reads unless you specify otherwise. Now the users cannot specify consistent read during DynamoDB reads. This commit allows users to set consistent read while creating resolvers. Use case: To control the amount of replicas contacted during DynamoDB reads. most salient design aspects: Added an boolean parameter to the dynamoDbGetItem, dynamoDbScanTable and dynamoDbQuery methods in mapping-template.ts. Changed relative tests. closes #5980 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features I ran yarn tests. They include integration test snapshots for DynamoDB reads. Do I need to write a new integration test? * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent eed854e commit 0b911ef

33 files changed

+2369
-209
lines changed

packages/@aws-cdk/aws-appsync/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ const demoTable = new dynamodb.Table(this, 'DemoTable', {
7777
const demoDS = api.addDynamoDbDataSource('demoDataSource', demoTable);
7878

7979
// Resolver for the Query "getDemos" that scans the DynamoDb table and returns the entire list.
80+
// Resolver Mapping Template Reference:
81+
// https://docs.aws.amazon.com/appsync/latest/devguide/resolver-mapping-template-reference-dynamodb.html
8082
demoDS.createResolver({
8183
typeName: 'Query',
8284
fieldName: 'getDemos',
@@ -94,8 +96,18 @@ demoDS.createResolver({
9496
),
9597
responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultItem(),
9698
});
99+
100+
//To enable DynamoDB read consistency with the `MappingTemplate`:
101+
demoDS.createResolver({
102+
typeName: 'Query',
103+
fieldName: 'getDemosConsistent',
104+
requestMappingTemplate: appsync.MappingTemplate.dynamoDbScanTable(true),
105+
responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultList(),
106+
});
97107
```
98108

109+
110+
99111
### Aurora Serverless
100112

101113
AppSync provides a data source for executing SQL commands against Amazon Aurora

packages/@aws-cdk/aws-appsync/lib/mapping-template.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,17 @@ export abstract class MappingTemplate {
3636
/**
3737
* Mapping template to scan a DynamoDB table to fetch all entries
3838
*/
39-
public static dynamoDbScanTable(): MappingTemplate {
40-
return this.fromString('{"version" : "2017-02-28", "operation" : "Scan"}');
39+
public static dynamoDbScanTable(consistentRead: boolean = false): MappingTemplate {
40+
return this.fromString(`{"version" : "2017-02-28", "operation" : "Scan", "consistentRead": ${consistentRead}}`);
4141
}
4242

4343
/**
4444
* Mapping template to query a set of items from a DynamoDB table
4545
*
4646
* @param cond the key condition for the query
4747
*/
48-
public static dynamoDbQuery(cond: KeyCondition, indexName?: string): MappingTemplate {
49-
return this.fromString(`{"version" : "2017-02-28", "operation" : "Query", ${indexName ? `"index" : "${indexName}", ` : ''}${cond.renderTemplate()}}`);
48+
public static dynamoDbQuery(cond: KeyCondition, indexName?: string, consistentRead: boolean = false): MappingTemplate {
49+
return this.fromString(`{"version" : "2017-02-28", "operation" : "Query", "consistentRead": ${consistentRead}, ${indexName ? `"index" : "${indexName}", ` : ''}${cond.renderTemplate()}}`);
5050
}
5151

5252
/**
@@ -55,8 +55,8 @@ export abstract class MappingTemplate {
5555
* @param keyName the name of the hash key field
5656
* @param idArg the name of the Query argument
5757
*/
58-
public static dynamoDbGetItem(keyName: string, idArg: string): MappingTemplate {
59-
return this.fromString(`{"version": "2017-02-28", "operation": "GetItem", "key": {"${keyName}": $util.dynamodb.toDynamoDBJson($ctx.args.${idArg})}}`);
58+
public static dynamoDbGetItem(keyName: string, idArg: string, consistentRead: boolean = false): MappingTemplate {
59+
return this.fromString(`{"version": "2017-02-28", "operation": "GetItem", "consistentRead": ${consistentRead}, "key": {"${keyName}": $util.dynamodb.toDynamoDBJson($ctx.args.${idArg})}}`);
6060
}
6161

6262
/**

packages/@aws-cdk/aws-appsync/test/appsync-dynamodb.test.ts

+19
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Template } from '@aws-cdk/assertions';
33
import * as db from '@aws-cdk/aws-dynamodb';
44
import * as cdk from '@aws-cdk/core';
55
import * as appsync from '../lib';
6+
import { KeyCondition } from '../lib';
67

78
function joined(str: string): string {
89
return str.replace(/\s+/g, '');
@@ -80,6 +81,24 @@ describe('DynamoDb Data Source configuration', () => {
8081
});
8182

8283
describe('DynamoDB Mapping Templates', () => {
84+
test('read consistency option for dynamoDbScanTable should render correctly', () => {
85+
const template = appsync.MappingTemplate.dynamoDbScanTable(true);
86+
const rendered = joined(template.renderTemplate());
87+
expect(rendered).toStrictEqual('{\"version\":\"2017-02-28\",\"operation\":\"Scan\",\"consistentRead\":true}');
88+
});
89+
90+
test('read consistency option for dynamoDbGetItem should render correctly', () => {
91+
const template = appsync.MappingTemplate.dynamoDbGetItem('id', 'id', true);
92+
const rendered = joined(template.renderTemplate());
93+
expect(rendered).toStrictEqual('{\"version\":\"2017-02-28\",\"operation\":\"GetItem\",\"consistentRead\":true,\"key\":{\"id\":$util.dynamodb.toDynamoDBJson($ctx.args.id)}}');
94+
});
95+
96+
test('read consistency option for dynamoDbQuery should render correctly', () => {
97+
const template = appsync.MappingTemplate.dynamoDbQuery(KeyCondition.eq('order', 'order'), 'orderIndex', true);
98+
const rendered = joined(template.renderTemplate());
99+
expect(rendered).toStrictEqual('{\"version\":\"2017-02-28\",\"operation\":\"Query\",\"consistentRead\":true,\"index\":\"orderIndex\",\"query\":{\"expression\":\"#order=:order\",\"expressionNames\":{\"#order\":\"order\"},\"expressionValues\":{\":order\":$util.dynamodb.toDynamoDBJson($ctx.args.order)}}}');
100+
});
101+
83102
test('PutItem projecting all', () => {
84103
const template = appsync.MappingTemplate.dynamoDbPutItem(
85104
appsync.PrimaryKey.partition('id').is('id'),

packages/@aws-cdk/aws-appsync/test/auth-apikey.integ.snapshot/aws-appsync-integ.assets.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
22
"version": "20.0.0",
33
"files": {
4-
"edb3fd1d912b400f4857c41a3ff80bcc32d180595affcd6c017f72afd5959482": {
4+
"7eaa4499d04a6696121796bb6e27fab5b4211517a6e0523260984e33c134f84f": {
55
"source": {
66
"path": "aws-appsync-integ.template.json",
77
"packaging": "file"
88
},
99
"destinations": {
1010
"current_account-current_region": {
1111
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12-
"objectKey": "edb3fd1d912b400f4857c41a3ff80bcc32d180595affcd6c017f72afd5959482.json",
12+
"objectKey": "7eaa4499d04a6696121796bb6e27fab5b4211517a6e0523260984e33c134f84f.json",
1313
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
1414
}
1515
}

packages/@aws-cdk/aws-appsync/test/auth-apikey.integ.snapshot/aws-appsync-integ.template.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@
134134
"TypeName": "Query",
135135
"DataSourceName": "testDataSource",
136136
"Kind": "UNIT",
137-
"RequestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\"}",
137+
"RequestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\", \"consistentRead\": false}",
138138
"ResponseMappingTemplate": "$util.toJson($ctx.result.items)"
139139
},
140140
"DependsOn": [

packages/@aws-cdk/aws-appsync/test/auth-apikey.integ.snapshot/tree.json

+14-14
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
}
3232
},
3333
"constructInfo": {
34-
"fqn": "@aws-cdk/aws-appsync.CfnGraphQLApi",
34+
"fqn": "@aws-cdk/core.CfnResource",
3535
"version": "0.0.0"
3636
}
3737
},
@@ -51,7 +51,7 @@
5151
}
5252
},
5353
"constructInfo": {
54-
"fqn": "@aws-cdk/aws-appsync.CfnGraphQLSchema",
54+
"fqn": "@aws-cdk/core.CfnResource",
5555
"version": "0.0.0"
5656
}
5757
},
@@ -70,7 +70,7 @@
7070
}
7171
},
7272
"constructInfo": {
73-
"fqn": "@aws-cdk/aws-appsync.CfnApiKey",
73+
"fqn": "@aws-cdk/core.CfnResource",
7474
"version": "0.0.0"
7575
}
7676
},
@@ -206,7 +206,7 @@
206206
}
207207
},
208208
"constructInfo": {
209-
"fqn": "@aws-cdk/aws-appsync.CfnDataSource",
209+
"fqn": "@aws-cdk/core.CfnResource",
210210
"version": "0.0.0"
211211
}
212212
},
@@ -230,19 +230,19 @@
230230
"typeName": "Query",
231231
"dataSourceName": "testDataSource",
232232
"kind": "UNIT",
233-
"requestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\"}",
233+
"requestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\", \"consistentRead\": false}",
234234
"responseMappingTemplate": "$util.toJson($ctx.result.items)"
235235
}
236236
},
237237
"constructInfo": {
238-
"fqn": "@aws-cdk/aws-appsync.CfnResolver",
238+
"fqn": "@aws-cdk/core.CfnResource",
239239
"version": "0.0.0"
240240
}
241241
}
242242
},
243243
"constructInfo": {
244-
"fqn": "@aws-cdk/aws-appsync.Resolver",
245-
"version": "0.0.0"
244+
"fqn": "constructs.Construct",
245+
"version": "10.1.33"
246246
}
247247
},
248248
"MutationaddTestResolver": {
@@ -270,25 +270,25 @@
270270
}
271271
},
272272
"constructInfo": {
273-
"fqn": "@aws-cdk/aws-appsync.CfnResolver",
273+
"fqn": "@aws-cdk/core.CfnResource",
274274
"version": "0.0.0"
275275
}
276276
}
277277
},
278278
"constructInfo": {
279-
"fqn": "@aws-cdk/aws-appsync.Resolver",
280-
"version": "0.0.0"
279+
"fqn": "constructs.Construct",
280+
"version": "10.1.33"
281281
}
282282
}
283283
},
284284
"constructInfo": {
285-
"fqn": "@aws-cdk/aws-appsync.DynamoDbDataSource",
286-
"version": "0.0.0"
285+
"fqn": "constructs.Construct",
286+
"version": "10.1.33"
287287
}
288288
}
289289
},
290290
"constructInfo": {
291-
"fqn": "@aws-cdk/aws-appsync.GraphqlApi",
291+
"fqn": "@aws-cdk/core.Resource",
292292
"version": "0.0.0"
293293
}
294294
},

packages/@aws-cdk/aws-appsync/test/graphql-iam.integ.snapshot/aws-appsync-integ.assets.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@
1414
}
1515
}
1616
},
17-
"4d9e950f40fbc075b913a37030dfd9c31de11a2230d2c5c7e45b8b9730faaa3f": {
17+
"c06c582f69e71eae955ab50693deb45f0836b7837ef3184a737f87c4bf7952a4": {
1818
"source": {
1919
"path": "aws-appsync-integ.template.json",
2020
"packaging": "file"
2121
},
2222
"destinations": {
2323
"current_account-current_region": {
2424
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
25-
"objectKey": "4d9e950f40fbc075b913a37030dfd9c31de11a2230d2c5c7e45b8b9730faaa3f.json",
25+
"objectKey": "c06c582f69e71eae955ab50693deb45f0836b7837ef3184a737f87c4bf7952a4.json",
2626
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
2727
}
2828
}

packages/@aws-cdk/aws-appsync/test/graphql-iam.integ.snapshot/aws-appsync-integ.template.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@
166166
"TypeName": "Query",
167167
"DataSourceName": "testDataSource",
168168
"Kind": "UNIT",
169-
"RequestMappingTemplate": "{\"version\": \"2017-02-28\", \"operation\": \"GetItem\", \"key\": {\"id\": $util.dynamodb.toDynamoDBJson($ctx.args.id)}}",
169+
"RequestMappingTemplate": "{\"version\": \"2017-02-28\", \"operation\": \"GetItem\", \"consistentRead\": false, \"key\": {\"id\": $util.dynamodb.toDynamoDBJson($ctx.args.id)}}",
170170
"ResponseMappingTemplate": "$util.toJson($ctx.result)"
171171
},
172172
"DependsOn": [
@@ -187,7 +187,7 @@
187187
"TypeName": "Query",
188188
"DataSourceName": "testDataSource",
189189
"Kind": "UNIT",
190-
"RequestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\"}",
190+
"RequestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\", \"consistentRead\": false}",
191191
"ResponseMappingTemplate": "$util.toJson($ctx.result.items)"
192192
},
193193
"DependsOn": [

packages/@aws-cdk/aws-appsync/test/graphql-iam.integ.snapshot/tree.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"path": "Tree",
1010
"constructInfo": {
1111
"fqn": "constructs.Construct",
12-
"version": "10.0.9"
12+
"version": "10.1.33"
1313
}
1414
},
1515
"aws-appsync-integ": {
@@ -273,7 +273,7 @@
273273
"typeName": "Query",
274274
"dataSourceName": "testDataSource",
275275
"kind": "UNIT",
276-
"requestMappingTemplate": "{\"version\": \"2017-02-28\", \"operation\": \"GetItem\", \"key\": {\"id\": $util.dynamodb.toDynamoDBJson($ctx.args.id)}}",
276+
"requestMappingTemplate": "{\"version\": \"2017-02-28\", \"operation\": \"GetItem\", \"consistentRead\": false, \"key\": {\"id\": $util.dynamodb.toDynamoDBJson($ctx.args.id)}}",
277277
"responseMappingTemplate": "$util.toJson($ctx.result)"
278278
}
279279
},
@@ -308,7 +308,7 @@
308308
"typeName": "Query",
309309
"dataSourceName": "testDataSource",
310310
"kind": "UNIT",
311-
"requestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\"}",
311+
"requestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\", \"consistentRead\": false}",
312312
"responseMappingTemplate": "$util.toJson($ctx.result.items)"
313313
}
314314
},
@@ -714,13 +714,13 @@
714714
},
715715
"constructInfo": {
716716
"fqn": "constructs.Construct",
717-
"version": "10.0.9"
717+
"version": "10.1.33"
718718
}
719719
}
720720
},
721721
"constructInfo": {
722722
"fqn": "constructs.Construct",
723-
"version": "10.0.9"
723+
"version": "10.1.33"
724724
}
725725
},
726726
"testFail": {
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"version":"17.0.0"}
1+
{"version":"20.0.0"}

packages/@aws-cdk/aws-appsync/test/graphql-schema.integ.snapshot/code-first-schema.assets.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
2-
"version": "17.0.0",
2+
"version": "20.0.0",
33
"files": {
4-
"13205a5206cf49ce1c9674807fa5b2c3315de95159c5c1c969664271edd96507": {
4+
"56d1a7b725a78aa83ffacc3ba1d3ae4b4c2fa61c3caff19dafd5f77c1e0333c3": {
55
"source": {
66
"path": "code-first-schema.template.json",
77
"packaging": "file"
88
},
99
"destinations": {
1010
"current_account-current_region": {
1111
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12-
"objectKey": "13205a5206cf49ce1c9674807fa5b2c3315de95159c5c1c969664271edd96507.json",
12+
"objectKey": "56d1a7b725a78aa83ffacc3ba1d3ae4b4c2fa61c3caff19dafd5f77c1e0333c3.json",
1313
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
1414
}
1515
}

packages/@aws-cdk/aws-appsync/test/graphql-schema.integ.snapshot/code-first-schema.template.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@
134134
"TypeName": "Query",
135135
"DataSourceName": "planets",
136136
"Kind": "UNIT",
137-
"RequestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\"}",
137+
"RequestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\", \"consistentRead\": false}",
138138
"ResponseMappingTemplate": "$util.toJson($ctx.result.items)"
139139
},
140140
"DependsOn": [

packages/@aws-cdk/aws-appsync/test/graphql-schema.integ.snapshot/manifest.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "17.0.0",
2+
"version": "20.0.0",
33
"artifacts": {
44
"Tree": {
55
"type": "cdk:tree",

packages/@aws-cdk/aws-appsync/test/graphql-schema.integ.snapshot/tree.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"path": "Tree",
1010
"constructInfo": {
1111
"fqn": "constructs.Construct",
12-
"version": "10.0.9"
12+
"version": "10.1.33"
1313
}
1414
},
1515
"code-first-schema": {
@@ -236,7 +236,7 @@
236236
"typeName": "Query",
237237
"dataSourceName": "planets",
238238
"kind": "UNIT",
239-
"requestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\"}",
239+
"requestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\", \"consistentRead\": false}",
240240
"responseMappingTemplate": "$util.toJson($ctx.result.items)"
241241
}
242242
},

packages/@aws-cdk/aws-appsync/test/graphql.integ.snapshot/aws-appsync-integ.assets.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
2-
"version": "17.0.0",
2+
"version": "20.0.0",
33
"files": {
4-
"7b4937a061e73c53a8d1ae8eeb6c778db65cfc02df7a34947b816bcd1332f9c9": {
4+
"410a11ff94f45720614bec412ef8a8f4aa68e8f98e181bbd4b98fb47cbb76284": {
55
"source": {
66
"path": "aws-appsync-integ.template.json",
77
"packaging": "file"
88
},
99
"destinations": {
1010
"current_account-current_region": {
1111
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12-
"objectKey": "7b4937a061e73c53a8d1ae8eeb6c778db65cfc02df7a34947b816bcd1332f9c9.json",
12+
"objectKey": "410a11ff94f45720614bec412ef8a8f4aa68e8f98e181bbd4b98fb47cbb76284.json",
1313
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
1414
}
1515
}

0 commit comments

Comments
 (0)