diff --git a/jest.config.js b/jest.config.js index 84a5267feba..515f9b8cce8 100644 --- a/jest.config.js +++ b/jest.config.js @@ -46,6 +46,7 @@ module.exports = { '/packages/amplify-graphql-docs-generator', '/packages/amplify-graphql-function-transformer', '/packages/amplify-graphql-http-transformer', + '/packages/amplify-graphql-index-transformer', '/packages/amplify-graphql-predictions-transformer', '/packages/amplify-graphql-searchable-transformer', '/packages/amplify-graphql-types-generator', diff --git a/packages/amplify-graphql-index-transformer/.npmignore b/packages/amplify-graphql-index-transformer/.npmignore new file mode 100644 index 00000000000..3ee5d55b0b8 --- /dev/null +++ b/packages/amplify-graphql-index-transformer/.npmignore @@ -0,0 +1,5 @@ +**/__mocks__/** +**/__tests__/** +src +tsconfig.json +tsconfig.tsbuildinfo diff --git a/packages/amplify-graphql-index-transformer/CHANGELOG.md b/packages/amplify-graphql-index-transformer/CHANGELOG.md new file mode 100644 index 00000000000..e4d87c4d45c --- /dev/null +++ b/packages/amplify-graphql-index-transformer/CHANGELOG.md @@ -0,0 +1,4 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. diff --git a/packages/amplify-graphql-index-transformer/README.md b/packages/amplify-graphql-index-transformer/README.md new file mode 100644 index 00000000000..0f8af7b574f --- /dev/null +++ b/packages/amplify-graphql-index-transformer/README.md @@ -0,0 +1,13 @@ +# GraphQL @primaryKey Transformer + +# Reference Documentation + +### @primaryKey + +The `@primaryKey` directive allows you to define the primary key for your relational data. + +#### Definition + +```graphql +directive @primaryKey(sortKeyFields: [String]) on FIELD_DEFINITION +``` diff --git a/packages/amplify-graphql-index-transformer/package.json b/packages/amplify-graphql-index-transformer/package.json new file mode 100644 index 00000000000..bead477e6db --- /dev/null +++ b/packages/amplify-graphql-index-transformer/package.json @@ -0,0 +1,57 @@ +{ + "name": "@aws-amplify/graphql-index-transformer", + "version": "0.1.0", + "description": "Amplify GraphQL index and key transformers", + "repository": { + "type": "git", + "url": "https://github.com/aws-amplify/amplify-cli.git", + "directory": "packages/amplify-graphql-index-transformer" + }, + "author": "Amazon Web Services", + "license": "Apache-2.0", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "keywords": [ + "graphql", + "cloudformation", + "aws", + "amplify" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "clean": "rimraf ./lib", + "test": "jest" + }, + "dependencies": { + "@aws-amplify/graphql-transformer-core": "0.7.2", + "@aws-amplify/graphql-transformer-interfaces": "1.7.0", + "@aws-cdk/aws-appsync": "~1.72.0", + "@aws-cdk/aws-dynamodb": "~1.72.0", + "graphql": "^14.5.8", + "graphql-mapping-template": "4.18.1", + "graphql-transformer-common": "4.19.6" + }, + "devDependencies": { + "@aws-amplify/graphql-model-transformer": "~0.4.6", + "@aws-cdk/assert": "~1.72.0" + }, + "jest": { + "transform": { + "^.+\\.(ts|tsx)?$": "ts-jest" + }, + "testRegex": "(src/__tests__/.*.test.ts)$", + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "jsx", + "json", + "node" + ], + "collectCoverage": true + } +} diff --git a/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-primary-key-transformer.test.ts.snap b/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-primary-key-transformer.test.ts.snap new file mode 100644 index 00000000000..3ebdeb42287 --- /dev/null +++ b/packages/amplify-graphql-index-transformer/src/__tests__/__snapshots__/amplify-graphql-primary-key-transformer.test.ts.snap @@ -0,0 +1,3619 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`a primary key with a composite sort key is properly configured 1`] = ` +Object { + "Mutation.createTest.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $createdAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"id\\", $util.autoId())) +$util.qr($ctx.stash.defaultValues.put(\\"createdAt\\", $createdAt)) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $createdAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.createTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email), + \\"kind#other\\": $util.dynamodb.toDynamoDB(\\"\${ctx.args.input.kind}#\${ctx.args.input.other}\\") +} ) +## [End] Set the primary key. ** +#if( $util.isNull($dynamodbNameOverrideMap) ) + #set( $dynamodbNameOverrideMap = { + \\"kind#other\\": \\"kindOther\\" +} ) +#else + $util.qr($dynamodbNameOverrideMap.put(\\"kind#other\\", \\"kindOther\\")) +#end +$util.qr($ctx.args.input.put(\\"kind#other\\",\\"\${ctx.args.input.kind}#\${ctx.args.input.other}\\")) +## [Start] Create Request template. ** +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.add(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +$util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) +#set( $PutObject = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"PutItem\\", + \\"attributeValues\\": $util.dynamodb.toMapValues($mergedValues), + \\"condition\\": $condition +} ) +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": false +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($PutObject.put(\\"condition\\", $Conditions)) +#end +#if( $ctx.stash.metadata.modelObject ) + $PutObject.put(\\"id\\", $ctx.stash.metadata.modelObject) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($mergedValues.id) +} ) + $util.qr($PutObject.put(\\"key\\", $Key)) +#end +$util.toJson($PutObject) +## [End] Create Request template. **", + "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.deleteTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email), + \\"kind#other\\": $util.dynamodb.toDynamoDB(\\"\${ctx.args.input.kind}#\${ctx.args.input.other}\\") +} ) +## [End] Set the primary key. ** +## [Start] Delete Request template. ** +#set( $DeleteRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"DeleteItem\\" +} ) +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $Key = $ctx.stash.metadata.modelObjectKey ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +$util.qr($DeleteRequest.put(\\"key\\", $Key)) +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($DeleteRequest.put(\\"condition\\", $Conditions)) +#end +$util.toJson($DeleteRequest) +## [End] Delete Request template. **", + "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $updatedAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $updatedAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.updateTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email), + \\"kind#other\\": $util.dynamodb.toDynamoDB(\\"\${ctx.args.input.kind}#\${ctx.args.input.other}\\") +} ) +## [End] Set the primary key. ** +#if( $util.isNull($dynamodbNameOverrideMap) ) + #set( $dynamodbNameOverrideMap = { + \\"kind#other\\": \\"kindOther\\" +} ) +#else + $util.qr($dynamodbNameOverrideMap.put(\\"kind#other\\", \\"kindOther\\")) +#end +$util.qr($ctx.args.input.put(\\"kind#other\\",\\"\${ctx.args.input.kind}#\${ctx.args.input.other}\\")) +## [Start] Mutation Update resolver. ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +## Initialize the vars for creating ddb expression ** +#set( $expNames = {} ) +#set( $expValues = {} ) +#set( $expSet = {} ) +#set( $expAdd = {} ) +#set( $expRemove = [] ) +#if( $ctx.stash.metadata.modelObjetKey ) + #set( $Key = $util.dynamodb.toDynamoDB($ctx.stash.metadata.modelObjetKey) ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +## Model key ** +#if( $ctx.stash.metadata.modelObjetKey ) + #foreach( $entry in $ctx.stash.metadata.modelObjetKey.entrySet() ) + $util.qr($keyFields.add(\\"$entry.key\\")) + #end +#else + #set( $keyFields = [\\"id\\"] ) +#end +#foreach( $entry in $util.map.copyAndRemoveAllKeys($mergedValues, $keyFields).entrySet() ) + #if( $util.isNull($entry.value) ) + #set( $discard = $expRemove.add(\\"#$entry.key\\") ) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + #else + $util.qr($expSet.put(\\"#$entry.key\\", \\":$entry.key\\")) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + $util.qr($expValues.put(\\":$entry.key\\", $util.dynamodb.toDynamoDB($entry.value))) + #end +#end +#set( $expression = \\"\\" ) +#if( !$expSet.isEmpty() ) + #set( $expression = \\"SET\\" ) + #foreach( $entry in $expSet.entrySet() ) + #set( $expression = \\"$expression $entry.key = $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expAdd.isEmpty() ) + #set( $expression = \\"$expression ADD\\" ) + #foreach( $entry in $expAdd.entrySet() ) + #set( $expression = \\"$expression $entry.key $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expRemove.isEmpty() ) + #set( $expression = \\"$expression REMOVE\\" ) + #foreach( $entry in $expRemove ) + #set( $expression = \\"$expression $entry\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#set( $update = {} ) +$util.qr($update.put(\\"expression\\", \\"$expression\\")) +#if( !$expNames.isEmpty() ) + $util.qr($update.put(\\"expressionNames\\", $expNames)) +#end +#if( !$expValues.isEmpty() ) + $util.qr($update.put(\\"expressionValues\\", $expValues)) +#end +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#set( $UpdateItem = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"UpdateItem\\", + \\"key\\": $Key, + \\"update\\": $update +} ) +#if( $Conditions ) + $util.qr($UpdateItem.put(\\"condition\\", $Conditions)) +#end +$util.toJson($UpdateItem) +## [End] Mutation Update resolver. **", + "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.getTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email), + \\"kind#other\\": $util.dynamodb.toDynamoDB(\\"\${ctx.args.kind}#\${ctx.args.other}\\") +} ) +## [End] Set the primary key. ** +## [Start] Get Request template. ** +#set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"GetItem\\" +} ) +#if( $ctx.stash.metadata.modelKeyObject ) + #set( $Key = $ctx.stash.metadata.modelKeyObject ) +#else + #set( $key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) +} ) +#end +$util.qr($GetRequest.put(\\"key\\", $key)) +$util.toJson($GetRequest) +## [End] Get Request template. **", + "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.listTests.req.vtl": "## [Start] Set query expression for key ** +#if( $util.isNull($ctx.args.email) && !$util.isNull($ctx.args.sortDirection) ) + $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'email'.\\", \\"InvalidArgumentsError\\") +#end +#set( $modelQueryExpression = {} ) +## [Start] Validate key arguments. ** +#if( !$util.isNull($ctx.args.kindOther) && $util.isNullOrBlank($ctx.args.email) ) + $util.error(\\"When providing argument 'kindOther' you must also provide 'email'.\\", \\"InvalidArgumentsError\\") +#end +#if( !$util.isNull($ctx.args.kindOther) ) + #set( $sortKeyArgumentOperations = $ctx.args.kindOther.keySet() ) + #if( $sortKeyArgumentOperations.size() > 1 ) + $util.error(\\"Argument kindOther must specify at most one key condition operation.\\", \\"InvalidArgumentsError\\") + #end + #foreach( $operation in $sortKeyArgumentOperations ) + #if( $operation == \\"between\\" ) + #if( $ctx.args.kindOther.between.size() != 2 ) + $util.error(\\"Argument 'kindOther.between' expects exactly two elements.\\", \\"InvalidArgumentsError\\") + #end + #if( !$util.isNullOrBlank($ctx.args.kindOther.between[0].other) && $util.isNullOrBlank($ctx.args.kindOther.between[0].kind) ) + $util.error(\\"When providing argument 'kindOther.between[0].other' you must also provide 'kindOther.between[0].kind'.\\", \\"InvalidArgumentsError\\") + #end + #if( !$util.isNullOrBlank($ctx.args.kindOther.between[1].other) && $util.isNullOrBlank($ctx.args.kindOther.between[1].kind) ) + $util.error(\\"When providing argument 'kindOther.between[1].other' you must also provide 'kindOther.between[1].kind'.\\", \\"InvalidArgumentsError\\") + #end + #else + #if( !$util.isNullOrBlank($ctx.args.kindOther.get(\\"$operation\\").other) && $util.isNullOrBlank($ctx.args.kindOther.get(\\"$operation\\").kind) ) + $util.error(\\"When providing argument 'kindOther.$operation.other' you must also provide 'kindOther.$operation.kind'.\\", \\"InvalidArgumentsError\\") + #end + #end + #end +#end +## [End] Validate key arguments. ** +#if( !$util.isNull($ctx.args.email) ) + #set( $modelQueryExpression.expression = \\"#email = :email\\" ) + #set( $modelQueryExpression.expressionNames = { + \\"#email\\": \\"email\\" +} ) + #set( $modelQueryExpression.expressionValues = { + \\":email\\": { + \\"S\\": \\"$ctx.args.email\\" + } +} ) +#end +## [Start] Applying Key Condition ** +#set( $sortKeyValue = \\"\\" ) +#set( $sortKeyValue2 = \\"\\" ) +#if( !$util.isNull($ctx.args.kindOther) && !$util.isNull($ctx.args.kindOther.beginsWith) ) +#if( !$util.isNull($ctx.args.kindOther.beginsWith.kind) ) #set( $sortKeyValue = \\"$ctx.args.kindOther.beginsWith.kind\\" ) #end +#if( !$util.isNull($ctx.args.kindOther.beginsWith.other) ) #set( $sortKeyValue = \\"$sortKeyValue#$ctx.args.kindOther.beginsWith.other\\" ) #end + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND begins_with(#sortKey, :sortKey)\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind#other\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$sortKeyValue\\" })) +#end +#if( !$util.isNull($ctx.args.kindOther) && !$util.isNull($ctx.args.kindOther.between) ) + #if( $ctx.args.kindOther.between.size() != 2 ) + $util.error(\\"Argument kindOther.between expects exactly 2 elements.\\") + #end +#if( !$util.isNull($ctx.args.kindOther.between[0].kind) ) #set( $sortKeyValue = \\"$ctx.args.kindOther.between[0].kind\\" ) #end +#if( !$util.isNull($ctx.args.kindOther.between[0].other) ) #set( $sortKeyValue = \\"$sortKeyValue#$ctx.args.kindOther.between[0].other\\" ) #end +#if( !$util.isNull($ctx.args.kindOther.between[1].kind) ) #set( $sortKeyValue2 = \\"$ctx.args.kindOther.between[1].kind\\" ) #end +#if( !$util.isNull($ctx.args.kindOther.between[1].other) ) #set( $sortKeyValue2 = \\"$sortKeyValue2#$ctx.args.kindOther.between[1].other\\" ) #end + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey BETWEEN :sortKey0 AND :sortKey1\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind#other\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey0\\", { \\"S\\": \\"$sortKeyValue\\" })) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey1\\", { \\"S\\": \\"$sortKeyValue2\\" })) +#end +#if( !$util.isNull($ctx.args.kindOther) && !$util.isNull($ctx.args.kindOther.eq) ) +#if( !$util.isNull($ctx.args.kindOther.eq.kind) ) #set( $sortKeyValue = \\"$ctx.args.kindOther.eq.kind\\" ) #end +#if( !$util.isNull($ctx.args.kindOther.eq.other) ) #set( $sortKeyValue = \\"$sortKeyValue#$ctx.args.kindOther.eq.other\\" ) #end + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey = :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind#other\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$sortKeyValue\\" })) +#end +#if( !$util.isNull($ctx.args.kindOther) && !$util.isNull($ctx.args.kindOther.lt) ) +#if( !$util.isNull($ctx.args.kindOther.lt.kind) ) #set( $sortKeyValue = \\"$ctx.args.kindOther.lt.kind\\" ) #end +#if( !$util.isNull($ctx.args.kindOther.lt.other) ) #set( $sortKeyValue = \\"$sortKeyValue#$ctx.args.kindOther.lt.other\\" ) #end + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey < :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind#other\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$sortKeyValue\\" })) +#end +#if( !$util.isNull($ctx.args.kindOther) && !$util.isNull($ctx.args.kindOther.le) ) +#if( !$util.isNull($ctx.args.kindOther.le.kind) ) #set( $sortKeyValue = \\"$ctx.args.kindOther.le.kind\\" ) #end +#if( !$util.isNull($ctx.args.kindOther.le.other) ) #set( $sortKeyValue = \\"$sortKeyValue#$ctx.args.kindOther.le.other\\" ) #end + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey <= :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind#other\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$sortKeyValue\\" })) +#end +#if( !$util.isNull($ctx.args.kindOther) && !$util.isNull($ctx.args.kindOther.gt) ) +#if( !$util.isNull($ctx.args.kindOther.gt.kind) ) #set( $sortKeyValue = \\"$ctx.args.kindOther.gt.kind\\" ) #end +#if( !$util.isNull($ctx.args.kindOther.gt.other) ) #set( $sortKeyValue = \\"$sortKeyValue#$ctx.args.kindOther.gt.other\\" ) #end + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey > :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind#other\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$sortKeyValue\\" })) +#end +#if( !$util.isNull($ctx.args.kindOther) && !$util.isNull($ctx.args.kindOther.ge) ) +#if( !$util.isNull($ctx.args.kindOther.ge.kind) ) #set( $sortKeyValue = \\"$ctx.args.kindOther.ge.kind\\" ) #end +#if( !$util.isNull($ctx.args.kindOther.ge.other) ) #set( $sortKeyValue = \\"$sortKeyValue#$ctx.args.kindOther.ge.other\\" ) #end + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey >= :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind#other\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$sortKeyValue\\" })) +#end + + +## [End] Applying Key Condition ** +## [End] Set query expression for key ** +## [Start] List Request. ** +#set( $limit = $util.defaultIfNull($context.args.limit, 100) ) +#set( $ListRequest = { + \\"version\\": \\"2018-05-29\\", + \\"limit\\": $limit +} ) +#if( $context.args.nextToken ) + #set( $ListRequest.nextToken = $context.args.nextToken ) +#end +#if( $context.args.filter ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $ListRequest.filter = $filterExpression ) + #end +#end +#if( !$util.isNull($ctx.stash.modelQuery) ) + #set( $Query = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.modelQuery)) ) + $util.qr($ListRequest.put(\\"operation\\", \\"Query\\")) + $util.qr($ListRequest.put(\\"query\\", $Query)) + #if( !$util.isNull($ctx.args.sortDirection) && $ctx.args.sortDirection == \\"DESC\\" ) + #set( $ListRequest.scanIndexForward = false ) + #else + #set( $ListRequest.scanIndexForward = true ) + #end +#else + $util.qr($ListRequest.put(\\"operation\\", \\"Scan\\")) +#end +#if( !$util.isNull($ctx.stash.metadata.index) ) + #set( $ListRequest.IndexName = $ctx.stash.metadata.index ) +#end +$util.toJson($ListRequest) +## [End] List Request. **", + "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", +} +`; + +exports[`a primary key with a single sort key field is properly configured 1`] = ` +Object { + "Mutation.createTest.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $createdAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"id\\", $util.autoId())) +$util.qr($ctx.stash.defaultValues.put(\\"createdAt\\", $createdAt)) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $createdAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.createTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email), + \\"kind\\": $util.dynamodb.toDynamoDB($ctx.args.input.kind) +} ) +## [End] Set the primary key. ** + +## [Start] Create Request template. ** +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.add(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +$util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) +#set( $PutObject = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"PutItem\\", + \\"attributeValues\\": $util.dynamodb.toMapValues($mergedValues), + \\"condition\\": $condition +} ) +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": false +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($PutObject.put(\\"condition\\", $Conditions)) +#end +#if( $ctx.stash.metadata.modelObject ) + $PutObject.put(\\"id\\", $ctx.stash.metadata.modelObject) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($mergedValues.id) +} ) + $util.qr($PutObject.put(\\"key\\", $Key)) +#end +$util.toJson($PutObject) +## [End] Create Request template. **", + "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.deleteTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email), + \\"kind\\": $util.dynamodb.toDynamoDB($ctx.args.input.kind) +} ) +## [End] Set the primary key. ** +## [Start] Delete Request template. ** +#set( $DeleteRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"DeleteItem\\" +} ) +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $Key = $ctx.stash.metadata.modelObjectKey ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +$util.qr($DeleteRequest.put(\\"key\\", $Key)) +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($DeleteRequest.put(\\"condition\\", $Conditions)) +#end +$util.toJson($DeleteRequest) +## [End] Delete Request template. **", + "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $updatedAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $updatedAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.updateTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email), + \\"kind\\": $util.dynamodb.toDynamoDB($ctx.args.input.kind) +} ) +## [End] Set the primary key. ** + +## [Start] Mutation Update resolver. ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +## Initialize the vars for creating ddb expression ** +#set( $expNames = {} ) +#set( $expValues = {} ) +#set( $expSet = {} ) +#set( $expAdd = {} ) +#set( $expRemove = [] ) +#if( $ctx.stash.metadata.modelObjetKey ) + #set( $Key = $util.dynamodb.toDynamoDB($ctx.stash.metadata.modelObjetKey) ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +## Model key ** +#if( $ctx.stash.metadata.modelObjetKey ) + #foreach( $entry in $ctx.stash.metadata.modelObjetKey.entrySet() ) + $util.qr($keyFields.add(\\"$entry.key\\")) + #end +#else + #set( $keyFields = [\\"id\\"] ) +#end +#foreach( $entry in $util.map.copyAndRemoveAllKeys($mergedValues, $keyFields).entrySet() ) + #if( $util.isNull($entry.value) ) + #set( $discard = $expRemove.add(\\"#$entry.key\\") ) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + #else + $util.qr($expSet.put(\\"#$entry.key\\", \\":$entry.key\\")) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + $util.qr($expValues.put(\\":$entry.key\\", $util.dynamodb.toDynamoDB($entry.value))) + #end +#end +#set( $expression = \\"\\" ) +#if( !$expSet.isEmpty() ) + #set( $expression = \\"SET\\" ) + #foreach( $entry in $expSet.entrySet() ) + #set( $expression = \\"$expression $entry.key = $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expAdd.isEmpty() ) + #set( $expression = \\"$expression ADD\\" ) + #foreach( $entry in $expAdd.entrySet() ) + #set( $expression = \\"$expression $entry.key $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expRemove.isEmpty() ) + #set( $expression = \\"$expression REMOVE\\" ) + #foreach( $entry in $expRemove ) + #set( $expression = \\"$expression $entry\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#set( $update = {} ) +$util.qr($update.put(\\"expression\\", \\"$expression\\")) +#if( !$expNames.isEmpty() ) + $util.qr($update.put(\\"expressionNames\\", $expNames)) +#end +#if( !$expValues.isEmpty() ) + $util.qr($update.put(\\"expressionValues\\", $expValues)) +#end +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#set( $UpdateItem = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"UpdateItem\\", + \\"key\\": $Key, + \\"update\\": $update +} ) +#if( $Conditions ) + $util.qr($UpdateItem.put(\\"condition\\", $Conditions)) +#end +$util.toJson($UpdateItem) +## [End] Mutation Update resolver. **", + "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.getTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email), + \\"kind\\": $util.dynamodb.toDynamoDB($ctx.args.kind) +} ) +## [End] Set the primary key. ** +## [Start] Get Request template. ** +#set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"GetItem\\" +} ) +#if( $ctx.stash.metadata.modelKeyObject ) + #set( $Key = $ctx.stash.metadata.modelKeyObject ) +#else + #set( $key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) +} ) +#end +$util.qr($GetRequest.put(\\"key\\", $key)) +$util.toJson($GetRequest) +## [End] Get Request template. **", + "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.listTests.req.vtl": "## [Start] Set query expression for key ** +#if( $util.isNull($ctx.args.email) && !$util.isNull($ctx.args.sortDirection) ) + $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'email'.\\", \\"InvalidArgumentsError\\") +#end +#set( $modelQueryExpression = {} ) +## [Start] Validate key arguments. ** +#if( !$util.isNull($ctx.args.kind) && $util.isNull($ctx.args.email) ) + $util.error(\\"When providing argument 'kind' you must also provide arguments email\\", \\"InvalidArgumentsError\\") +#end +## [End] Validate key arguments. ** +#if( !$util.isNull($ctx.args.email) ) + #set( $modelQueryExpression.expression = \\"#email = :email\\" ) + #set( $modelQueryExpression.expressionNames = { + \\"#email\\": \\"email\\" +} ) + #set( $modelQueryExpression.expressionValues = { + \\":email\\": { + \\"S\\": \\"$ctx.args.email\\" + } +} ) +#end +## [Start] Applying Key Condition ** +#if( !$util.isNull($ctx.args.kind) && !$util.isNull($ctx.args.kind.beginsWith) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND begins_with(#sortKey, :sortKey)\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"N\\": \\"$ctx.args.kind.beginsWith\\" })) +#end +#if( !$util.isNull($ctx.args.kind) && !$util.isNull($ctx.args.kind.between) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey BETWEEN :sortKey0 AND :sortKey1\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey0\\", { \\"N\\": \\"$ctx.args.kind.between[0]\\" })) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey1\\", { \\"N\\": \\"$ctx.args.kind.between[1]\\" })) +#end +#if( !$util.isNull($ctx.args.kind) && !$util.isNull($ctx.args.kind.eq) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey = :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"N\\": \\"$ctx.args.kind.eq\\" })) +#end +#if( !$util.isNull($ctx.args.kind) && !$util.isNull($ctx.args.kind.lt) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey < :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"N\\": \\"$ctx.args.kind.lt\\" })) +#end +#if( !$util.isNull($ctx.args.kind) && !$util.isNull($ctx.args.kind.le) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey <= :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"N\\": \\"$ctx.args.kind.le\\" })) +#end +#if( !$util.isNull($ctx.args.kind) && !$util.isNull($ctx.args.kind.gt) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey > :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"N\\": \\"$ctx.args.kind.gt\\" })) +#end +#if( !$util.isNull($ctx.args.kind) && !$util.isNull($ctx.args.kind.ge) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey >= :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"kind\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"N\\": \\"$ctx.args.kind.ge\\" })) +#end +## [End] Applying Key Condition ** +## [End] Set query expression for key ** +## [Start] List Request. ** +#set( $limit = $util.defaultIfNull($context.args.limit, 100) ) +#set( $ListRequest = { + \\"version\\": \\"2018-05-29\\", + \\"limit\\": $limit +} ) +#if( $context.args.nextToken ) + #set( $ListRequest.nextToken = $context.args.nextToken ) +#end +#if( $context.args.filter ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $ListRequest.filter = $filterExpression ) + #end +#end +#if( !$util.isNull($ctx.stash.modelQuery) ) + #set( $Query = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.modelQuery)) ) + $util.qr($ListRequest.put(\\"operation\\", \\"Query\\")) + $util.qr($ListRequest.put(\\"query\\", $Query)) + #if( !$util.isNull($ctx.args.sortDirection) && $ctx.args.sortDirection == \\"DESC\\" ) + #set( $ListRequest.scanIndexForward = false ) + #else + #set( $ListRequest.scanIndexForward = true ) + #end +#else + $util.qr($ListRequest.put(\\"operation\\", \\"Scan\\")) +#end +#if( !$util.isNull($ctx.stash.metadata.index) ) + #set( $ListRequest.IndexName = $ctx.stash.metadata.index ) +#end +$util.toJson($ListRequest) +## [End] List Request. **", + "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", +} +`; + +exports[`a primary key with no sort key is properly configured 1`] = ` +Object { + "Mutation.createTest.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $createdAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"id\\", $util.autoId())) +$util.qr($ctx.stash.defaultValues.put(\\"createdAt\\", $createdAt)) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $createdAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.createTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** + +## [Start] Create Request template. ** +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.add(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +$util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) +#set( $PutObject = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"PutItem\\", + \\"attributeValues\\": $util.dynamodb.toMapValues($mergedValues), + \\"condition\\": $condition +} ) +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": false +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($PutObject.put(\\"condition\\", $Conditions)) +#end +#if( $ctx.stash.metadata.modelObject ) + $PutObject.put(\\"id\\", $ctx.stash.metadata.modelObject) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($mergedValues.id) +} ) + $util.qr($PutObject.put(\\"key\\", $Key)) +#end +$util.toJson($PutObject) +## [End] Create Request template. **", + "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.deleteTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** +## [Start] Delete Request template. ** +#set( $DeleteRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"DeleteItem\\" +} ) +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $Key = $ctx.stash.metadata.modelObjectKey ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +$util.qr($DeleteRequest.put(\\"key\\", $Key)) +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($DeleteRequest.put(\\"condition\\", $Conditions)) +#end +$util.toJson($DeleteRequest) +## [End] Delete Request template. **", + "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $updatedAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $updatedAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.updateTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** + +## [Start] Mutation Update resolver. ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +## Initialize the vars for creating ddb expression ** +#set( $expNames = {} ) +#set( $expValues = {} ) +#set( $expSet = {} ) +#set( $expAdd = {} ) +#set( $expRemove = [] ) +#if( $ctx.stash.metadata.modelObjetKey ) + #set( $Key = $util.dynamodb.toDynamoDB($ctx.stash.metadata.modelObjetKey) ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +## Model key ** +#if( $ctx.stash.metadata.modelObjetKey ) + #foreach( $entry in $ctx.stash.metadata.modelObjetKey.entrySet() ) + $util.qr($keyFields.add(\\"$entry.key\\")) + #end +#else + #set( $keyFields = [\\"id\\"] ) +#end +#foreach( $entry in $util.map.copyAndRemoveAllKeys($mergedValues, $keyFields).entrySet() ) + #if( $util.isNull($entry.value) ) + #set( $discard = $expRemove.add(\\"#$entry.key\\") ) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + #else + $util.qr($expSet.put(\\"#$entry.key\\", \\":$entry.key\\")) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + $util.qr($expValues.put(\\":$entry.key\\", $util.dynamodb.toDynamoDB($entry.value))) + #end +#end +#set( $expression = \\"\\" ) +#if( !$expSet.isEmpty() ) + #set( $expression = \\"SET\\" ) + #foreach( $entry in $expSet.entrySet() ) + #set( $expression = \\"$expression $entry.key = $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expAdd.isEmpty() ) + #set( $expression = \\"$expression ADD\\" ) + #foreach( $entry in $expAdd.entrySet() ) + #set( $expression = \\"$expression $entry.key $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expRemove.isEmpty() ) + #set( $expression = \\"$expression REMOVE\\" ) + #foreach( $entry in $expRemove ) + #set( $expression = \\"$expression $entry\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#set( $update = {} ) +$util.qr($update.put(\\"expression\\", \\"$expression\\")) +#if( !$expNames.isEmpty() ) + $util.qr($update.put(\\"expressionNames\\", $expNames)) +#end +#if( !$expValues.isEmpty() ) + $util.qr($update.put(\\"expressionValues\\", $expValues)) +#end +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#set( $UpdateItem = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"UpdateItem\\", + \\"key\\": $Key, + \\"update\\": $update +} ) +#if( $Conditions ) + $util.qr($UpdateItem.put(\\"condition\\", $Conditions)) +#end +$util.toJson($UpdateItem) +## [End] Mutation Update resolver. **", + "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.getTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) +} ) +## [End] Set the primary key. ** +## [Start] Get Request template. ** +#set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"GetItem\\" +} ) +#if( $ctx.stash.metadata.modelKeyObject ) + #set( $Key = $ctx.stash.metadata.modelKeyObject ) +#else + #set( $key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) +} ) +#end +$util.qr($GetRequest.put(\\"key\\", $key)) +$util.toJson($GetRequest) +## [End] Get Request template. **", + "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.listTests.req.vtl": "## [Start] Set query expression for key ** +#if( !$util.isNull($ctx.args.sortDirection) ) + $util.error(\\"sortDirection is not supported for List operations without a Sort key defined.\\", \\"InvalidArgumentsError\\") +#end +#set( $modelQueryExpression = {} ) +#if( !$util.isNull($ctx.args.email) ) + #set( $modelQueryExpression.expression = \\"#email = :email\\" ) + #set( $modelQueryExpression.expressionNames = { + \\"#email\\": \\"email\\" +} ) + #set( $modelQueryExpression.expressionValues = { + \\":email\\": { + \\"S\\": \\"$ctx.args.email\\" + } +} ) +#end +## [End] Set query expression for key ** +## [Start] List Request. ** +#set( $limit = $util.defaultIfNull($context.args.limit, 100) ) +#set( $ListRequest = { + \\"version\\": \\"2018-05-29\\", + \\"limit\\": $limit +} ) +#if( $context.args.nextToken ) + #set( $ListRequest.nextToken = $context.args.nextToken ) +#end +#if( $context.args.filter ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $ListRequest.filter = $filterExpression ) + #end +#end +#if( !$util.isNull($ctx.stash.modelQuery) ) + #set( $Query = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.modelQuery)) ) + $util.qr($ListRequest.put(\\"operation\\", \\"Query\\")) + $util.qr($ListRequest.put(\\"query\\", $Query)) + #if( !$util.isNull($ctx.args.sortDirection) && $ctx.args.sortDirection == \\"DESC\\" ) + #set( $ListRequest.scanIndexForward = false ) + #else + #set( $ListRequest.scanIndexForward = true ) + #end +#else + $util.qr($ListRequest.put(\\"operation\\", \\"Scan\\")) +#end +#if( !$util.isNull($ctx.stash.metadata.index) ) + #set( $ListRequest.IndexName = $ctx.stash.metadata.index ) +#end +$util.toJson($ListRequest) +## [End] List Request. **", + "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", +} +`; + +exports[`enums are supported in keys 1`] = ` +Object { + "Mutation.createTest.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $createdAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"id\\", $util.autoId())) +$util.qr($ctx.stash.defaultValues.put(\\"createdAt\\", $createdAt)) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $createdAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.createTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"status\\": $util.dynamodb.toDynamoDB($ctx.args.input.status), + \\"lastStatus\\": $util.dynamodb.toDynamoDB($ctx.args.input.lastStatus) +} ) +## [End] Set the primary key. ** + +## [Start] Create Request template. ** +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.add(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +$util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) +#set( $PutObject = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"PutItem\\", + \\"attributeValues\\": $util.dynamodb.toMapValues($mergedValues), + \\"condition\\": $condition +} ) +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": false +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($PutObject.put(\\"condition\\", $Conditions)) +#end +#if( $ctx.stash.metadata.modelObject ) + $PutObject.put(\\"id\\", $ctx.stash.metadata.modelObject) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($mergedValues.id) +} ) + $util.qr($PutObject.put(\\"key\\", $Key)) +#end +$util.toJson($PutObject) +## [End] Create Request template. **", + "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.deleteTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"status\\": $util.dynamodb.toDynamoDB($ctx.args.input.status), + \\"lastStatus\\": $util.dynamodb.toDynamoDB($ctx.args.input.lastStatus) +} ) +## [End] Set the primary key. ** +## [Start] Delete Request template. ** +#set( $DeleteRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"DeleteItem\\" +} ) +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $Key = $ctx.stash.metadata.modelObjectKey ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +$util.qr($DeleteRequest.put(\\"key\\", $Key)) +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($DeleteRequest.put(\\"condition\\", $Conditions)) +#end +$util.toJson($DeleteRequest) +## [End] Delete Request template. **", + "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $updatedAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $updatedAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.updateTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"status\\": $util.dynamodb.toDynamoDB($ctx.args.input.status), + \\"lastStatus\\": $util.dynamodb.toDynamoDB($ctx.args.input.lastStatus) +} ) +## [End] Set the primary key. ** + +## [Start] Mutation Update resolver. ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +## Initialize the vars for creating ddb expression ** +#set( $expNames = {} ) +#set( $expValues = {} ) +#set( $expSet = {} ) +#set( $expAdd = {} ) +#set( $expRemove = [] ) +#if( $ctx.stash.metadata.modelObjetKey ) + #set( $Key = $util.dynamodb.toDynamoDB($ctx.stash.metadata.modelObjetKey) ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +## Model key ** +#if( $ctx.stash.metadata.modelObjetKey ) + #foreach( $entry in $ctx.stash.metadata.modelObjetKey.entrySet() ) + $util.qr($keyFields.add(\\"$entry.key\\")) + #end +#else + #set( $keyFields = [\\"id\\"] ) +#end +#foreach( $entry in $util.map.copyAndRemoveAllKeys($mergedValues, $keyFields).entrySet() ) + #if( $util.isNull($entry.value) ) + #set( $discard = $expRemove.add(\\"#$entry.key\\") ) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + #else + $util.qr($expSet.put(\\"#$entry.key\\", \\":$entry.key\\")) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + $util.qr($expValues.put(\\":$entry.key\\", $util.dynamodb.toDynamoDB($entry.value))) + #end +#end +#set( $expression = \\"\\" ) +#if( !$expSet.isEmpty() ) + #set( $expression = \\"SET\\" ) + #foreach( $entry in $expSet.entrySet() ) + #set( $expression = \\"$expression $entry.key = $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expAdd.isEmpty() ) + #set( $expression = \\"$expression ADD\\" ) + #foreach( $entry in $expAdd.entrySet() ) + #set( $expression = \\"$expression $entry.key $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expRemove.isEmpty() ) + #set( $expression = \\"$expression REMOVE\\" ) + #foreach( $entry in $expRemove ) + #set( $expression = \\"$expression $entry\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#set( $update = {} ) +$util.qr($update.put(\\"expression\\", \\"$expression\\")) +#if( !$expNames.isEmpty() ) + $util.qr($update.put(\\"expressionNames\\", $expNames)) +#end +#if( !$expValues.isEmpty() ) + $util.qr($update.put(\\"expressionValues\\", $expValues)) +#end +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#set( $UpdateItem = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"UpdateItem\\", + \\"key\\": $Key, + \\"update\\": $update +} ) +#if( $Conditions ) + $util.qr($UpdateItem.put(\\"condition\\", $Conditions)) +#end +$util.toJson($UpdateItem) +## [End] Mutation Update resolver. **", + "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.getTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"status\\": $util.dynamodb.toDynamoDB($ctx.args.status), + \\"lastStatus\\": $util.dynamodb.toDynamoDB($ctx.args.lastStatus) +} ) +## [End] Set the primary key. ** +## [Start] Get Request template. ** +#set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"GetItem\\" +} ) +#if( $ctx.stash.metadata.modelKeyObject ) + #set( $Key = $ctx.stash.metadata.modelKeyObject ) +#else + #set( $key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) +} ) +#end +$util.qr($GetRequest.put(\\"key\\", $key)) +$util.toJson($GetRequest) +## [End] Get Request template. **", + "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.listTests.req.vtl": "## [Start] Set query expression for key ** +#if( $util.isNull($ctx.args.status) && !$util.isNull($ctx.args.sortDirection) ) + $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'status'.\\", \\"InvalidArgumentsError\\") +#end +#set( $modelQueryExpression = {} ) +## [Start] Validate key arguments. ** +#if( !$util.isNull($ctx.args.lastStatus) && $util.isNull($ctx.args.status) ) + $util.error(\\"When providing argument 'lastStatus' you must also provide arguments status\\", \\"InvalidArgumentsError\\") +#end +## [End] Validate key arguments. ** +#if( !$util.isNull($ctx.args.status) ) + #set( $modelQueryExpression.expression = \\"#status = :status\\" ) + #set( $modelQueryExpression.expressionNames = { + \\"#status\\": \\"status\\" +} ) + #set( $modelQueryExpression.expressionValues = { + \\":status\\": { + \\"S\\": \\"$ctx.args.status\\" + } +} ) +#end +## [Start] Applying Key Condition ** +#if( !$util.isNull($ctx.args.lastStatus) && !$util.isNull($ctx.args.lastStatus.beginsWith) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND begins_with(#sortKey, :sortKey)\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"lastStatus\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.lastStatus.beginsWith\\" })) +#end +#if( !$util.isNull($ctx.args.lastStatus) && !$util.isNull($ctx.args.lastStatus.between) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey BETWEEN :sortKey0 AND :sortKey1\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"lastStatus\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey0\\", { \\"S\\": \\"$ctx.args.lastStatus.between[0]\\" })) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey1\\", { \\"S\\": \\"$ctx.args.lastStatus.between[1]\\" })) +#end +#if( !$util.isNull($ctx.args.lastStatus) && !$util.isNull($ctx.args.lastStatus.eq) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey = :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"lastStatus\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.lastStatus.eq\\" })) +#end +#if( !$util.isNull($ctx.args.lastStatus) && !$util.isNull($ctx.args.lastStatus.lt) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey < :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"lastStatus\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.lastStatus.lt\\" })) +#end +#if( !$util.isNull($ctx.args.lastStatus) && !$util.isNull($ctx.args.lastStatus.le) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey <= :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"lastStatus\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.lastStatus.le\\" })) +#end +#if( !$util.isNull($ctx.args.lastStatus) && !$util.isNull($ctx.args.lastStatus.gt) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey > :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"lastStatus\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.lastStatus.gt\\" })) +#end +#if( !$util.isNull($ctx.args.lastStatus) && !$util.isNull($ctx.args.lastStatus.ge) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey >= :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"lastStatus\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.lastStatus.ge\\" })) +#end +## [End] Applying Key Condition ** +## [End] Set query expression for key ** +## [Start] List Request. ** +#set( $limit = $util.defaultIfNull($context.args.limit, 100) ) +#set( $ListRequest = { + \\"version\\": \\"2018-05-29\\", + \\"limit\\": $limit +} ) +#if( $context.args.nextToken ) + #set( $ListRequest.nextToken = $context.args.nextToken ) +#end +#if( $context.args.filter ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $ListRequest.filter = $filterExpression ) + #end +#end +#if( !$util.isNull($ctx.stash.modelQuery) ) + #set( $Query = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.modelQuery)) ) + $util.qr($ListRequest.put(\\"operation\\", \\"Query\\")) + $util.qr($ListRequest.put(\\"query\\", $Query)) + #if( !$util.isNull($ctx.args.sortDirection) && $ctx.args.sortDirection == \\"DESC\\" ) + #set( $ListRequest.scanIndexForward = false ) + #else + #set( $ListRequest.scanIndexForward = true ) + #end +#else + $util.qr($ListRequest.put(\\"operation\\", \\"Scan\\")) +#end +#if( !$util.isNull($ctx.stash.metadata.index) ) + #set( $ListRequest.IndexName = $ctx.stash.metadata.index ) +#end +$util.toJson($ListRequest) +## [End] List Request. **", + "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", +} +`; + +exports[`individual resolvers can be made null by @model 1`] = ` +Object { + "Mutation.createTest.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $createdAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"id\\", $util.autoId())) +$util.qr($ctx.stash.defaultValues.put(\\"createdAt\\", $createdAt)) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $createdAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.createTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id), + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** + +## [Start] Create Request template. ** +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.add(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +$util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) +#set( $PutObject = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"PutItem\\", + \\"attributeValues\\": $util.dynamodb.toMapValues($mergedValues), + \\"condition\\": $condition +} ) +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": false +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($PutObject.put(\\"condition\\", $Conditions)) +#end +#if( $ctx.stash.metadata.modelObject ) + $PutObject.put(\\"id\\", $ctx.stash.metadata.modelObject) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($mergedValues.id) +} ) + $util.qr($PutObject.put(\\"key\\", $Key)) +#end +$util.toJson($PutObject) +## [End] Create Request template. **", + "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.deleteTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id), + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** +## [Start] Delete Request template. ** +#set( $DeleteRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"DeleteItem\\" +} ) +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $Key = $ctx.stash.metadata.modelObjectKey ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +$util.qr($DeleteRequest.put(\\"key\\", $Key)) +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($DeleteRequest.put(\\"condition\\", $Conditions)) +#end +$util.toJson($DeleteRequest) +## [End] Delete Request template. **", + "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.testCreate.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $createdAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"id\\", $util.autoId())) +$util.qr($ctx.stash.defaultValues.put(\\"createdAt\\", $createdAt)) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $createdAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.testCreate.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id), + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** + +## [Start] Create Request template. ** +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.add(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +$util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) +#set( $PutObject = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"PutItem\\", + \\"attributeValues\\": $util.dynamodb.toMapValues($mergedValues), + \\"condition\\": $condition +} ) +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": false +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($PutObject.put(\\"condition\\", $Conditions)) +#end +#if( $ctx.stash.metadata.modelObject ) + $PutObject.put(\\"id\\", $ctx.stash.metadata.modelObject) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($mergedValues.id) +} ) + $util.qr($PutObject.put(\\"key\\", $Key)) +#end +$util.toJson($PutObject) +## [End] Create Request template. **", + "Mutation.testCreate.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.testDelete.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id), + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** +## [Start] Delete Request template. ** +#set( $DeleteRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"DeleteItem\\" +} ) +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $Key = $ctx.stash.metadata.modelObjectKey ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +$util.qr($DeleteRequest.put(\\"key\\", $Key)) +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($DeleteRequest.put(\\"condition\\", $Conditions)) +#end +$util.toJson($DeleteRequest) +## [End] Delete Request template. **", + "Mutation.testDelete.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.testUpdate.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $updatedAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $updatedAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.testUpdate.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id), + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** + +## [Start] Mutation Update resolver. ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +## Initialize the vars for creating ddb expression ** +#set( $expNames = {} ) +#set( $expValues = {} ) +#set( $expSet = {} ) +#set( $expAdd = {} ) +#set( $expRemove = [] ) +#if( $ctx.stash.metadata.modelObjetKey ) + #set( $Key = $util.dynamodb.toDynamoDB($ctx.stash.metadata.modelObjetKey) ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +## Model key ** +#if( $ctx.stash.metadata.modelObjetKey ) + #foreach( $entry in $ctx.stash.metadata.modelObjetKey.entrySet() ) + $util.qr($keyFields.add(\\"$entry.key\\")) + #end +#else + #set( $keyFields = [\\"id\\"] ) +#end +#foreach( $entry in $util.map.copyAndRemoveAllKeys($mergedValues, $keyFields).entrySet() ) + #if( $util.isNull($entry.value) ) + #set( $discard = $expRemove.add(\\"#$entry.key\\") ) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + #else + $util.qr($expSet.put(\\"#$entry.key\\", \\":$entry.key\\")) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + $util.qr($expValues.put(\\":$entry.key\\", $util.dynamodb.toDynamoDB($entry.value))) + #end +#end +#set( $expression = \\"\\" ) +#if( !$expSet.isEmpty() ) + #set( $expression = \\"SET\\" ) + #foreach( $entry in $expSet.entrySet() ) + #set( $expression = \\"$expression $entry.key = $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expAdd.isEmpty() ) + #set( $expression = \\"$expression ADD\\" ) + #foreach( $entry in $expAdd.entrySet() ) + #set( $expression = \\"$expression $entry.key $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expRemove.isEmpty() ) + #set( $expression = \\"$expression REMOVE\\" ) + #foreach( $entry in $expRemove ) + #set( $expression = \\"$expression $entry\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#set( $update = {} ) +$util.qr($update.put(\\"expression\\", \\"$expression\\")) +#if( !$expNames.isEmpty() ) + $util.qr($update.put(\\"expressionNames\\", $expNames)) +#end +#if( !$expValues.isEmpty() ) + $util.qr($update.put(\\"expressionValues\\", $expValues)) +#end +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#set( $UpdateItem = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"UpdateItem\\", + \\"key\\": $Key, + \\"update\\": $update +} ) +#if( $Conditions ) + $util.qr($UpdateItem.put(\\"condition\\", $Conditions)) +#end +$util.toJson($UpdateItem) +## [End] Mutation Update resolver. **", + "Mutation.testUpdate.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $updatedAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $updatedAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.updateTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id), + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** + +## [Start] Mutation Update resolver. ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +## Initialize the vars for creating ddb expression ** +#set( $expNames = {} ) +#set( $expValues = {} ) +#set( $expSet = {} ) +#set( $expAdd = {} ) +#set( $expRemove = [] ) +#if( $ctx.stash.metadata.modelObjetKey ) + #set( $Key = $util.dynamodb.toDynamoDB($ctx.stash.metadata.modelObjetKey) ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +## Model key ** +#if( $ctx.stash.metadata.modelObjetKey ) + #foreach( $entry in $ctx.stash.metadata.modelObjetKey.entrySet() ) + $util.qr($keyFields.add(\\"$entry.key\\")) + #end +#else + #set( $keyFields = [\\"id\\"] ) +#end +#foreach( $entry in $util.map.copyAndRemoveAllKeys($mergedValues, $keyFields).entrySet() ) + #if( $util.isNull($entry.value) ) + #set( $discard = $expRemove.add(\\"#$entry.key\\") ) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + #else + $util.qr($expSet.put(\\"#$entry.key\\", \\":$entry.key\\")) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + $util.qr($expValues.put(\\":$entry.key\\", $util.dynamodb.toDynamoDB($entry.value))) + #end +#end +#set( $expression = \\"\\" ) +#if( !$expSet.isEmpty() ) + #set( $expression = \\"SET\\" ) + #foreach( $entry in $expSet.entrySet() ) + #set( $expression = \\"$expression $entry.key = $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expAdd.isEmpty() ) + #set( $expression = \\"$expression ADD\\" ) + #foreach( $entry in $expAdd.entrySet() ) + #set( $expression = \\"$expression $entry.key $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expRemove.isEmpty() ) + #set( $expression = \\"$expression REMOVE\\" ) + #foreach( $entry in $expRemove ) + #set( $expression = \\"$expression $entry\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#set( $update = {} ) +$util.qr($update.put(\\"expression\\", \\"$expression\\")) +#if( !$expNames.isEmpty() ) + $util.qr($update.put(\\"expressionNames\\", $expNames)) +#end +#if( !$expValues.isEmpty() ) + $util.qr($update.put(\\"expressionValues\\", $expValues)) +#end +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#set( $UpdateItem = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"UpdateItem\\", + \\"key\\": $Key, + \\"update\\": $update +} ) +#if( $Conditions ) + $util.qr($UpdateItem.put(\\"condition\\", $Conditions)) +#end +$util.toJson($UpdateItem) +## [End] Mutation Update resolver. **", + "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.getTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) +} ) +## [End] Set the primary key. ** +## [Start] Get Request template. ** +#set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"GetItem\\" +} ) +#if( $ctx.stash.metadata.modelKeyObject ) + #set( $Key = $ctx.stash.metadata.modelKeyObject ) +#else + #set( $key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) +} ) +#end +$util.qr($GetRequest.put(\\"key\\", $key)) +$util.toJson($GetRequest) +## [End] Get Request template. **", + "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.listTests.req.vtl": "## [Start] Set query expression for key ** +#if( !$util.isNull($ctx.args.sortDirection) ) + $util.error(\\"sortDirection is not supported for List operations without a Sort key defined.\\", \\"InvalidArgumentsError\\") +#end +#set( $modelQueryExpression = {} ) +#if( !$util.isNull($ctx.args.email) ) + #set( $modelQueryExpression.expression = \\"#email = :email\\" ) + #set( $modelQueryExpression.expressionNames = { + \\"#email\\": \\"email\\" +} ) + #set( $modelQueryExpression.expressionValues = { + \\":email\\": { + \\"S\\": \\"$ctx.args.email\\" + } +} ) +#end +## [End] Set query expression for key ** +## [Start] List Request. ** +#set( $limit = $util.defaultIfNull($context.args.limit, 100) ) +#set( $ListRequest = { + \\"version\\": \\"2018-05-29\\", + \\"limit\\": $limit +} ) +#if( $context.args.nextToken ) + #set( $ListRequest.nextToken = $context.args.nextToken ) +#end +#if( $context.args.filter ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $ListRequest.filter = $filterExpression ) + #end +#end +#if( !$util.isNull($ctx.stash.modelQuery) ) + #set( $Query = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.modelQuery)) ) + $util.qr($ListRequest.put(\\"operation\\", \\"Query\\")) + $util.qr($ListRequest.put(\\"query\\", $Query)) + #if( !$util.isNull($ctx.args.sortDirection) && $ctx.args.sortDirection == \\"DESC\\" ) + #set( $ListRequest.scanIndexForward = false ) + #else + #set( $ListRequest.scanIndexForward = true ) + #end +#else + $util.qr($ListRequest.put(\\"operation\\", \\"Scan\\")) +#end +#if( !$util.isNull($ctx.stash.metadata.index) ) + #set( $ListRequest.IndexName = $ctx.stash.metadata.index ) +#end +$util.toJson($ListRequest) +## [End] List Request. **", + "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.testGet.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) +} ) +## [End] Set the primary key. ** +## [Start] Get Request template. ** +#set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"GetItem\\" +} ) +#if( $ctx.stash.metadata.modelKeyObject ) + #set( $Key = $ctx.stash.metadata.modelKeyObject ) +#else + #set( $key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) +} ) +#end +$util.qr($GetRequest.put(\\"key\\", $key)) +$util.toJson($GetRequest) +## [End] Get Request template. **", + "Query.testGet.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.testList.req.vtl": "## [Start] Set query expression for key ** +#if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) + $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") +#end +#set( $modelQueryExpression = {} ) +## [Start] Validate key arguments. ** +#if( !$util.isNull($ctx.args.email) && $util.isNull($ctx.args.id) ) + $util.error(\\"When providing argument 'email' you must also provide arguments id\\", \\"InvalidArgumentsError\\") +#end +## [End] Validate key arguments. ** +#if( !$util.isNull($ctx.args.id) ) + #set( $modelQueryExpression.expression = \\"#id = :id\\" ) + #set( $modelQueryExpression.expressionNames = { + \\"#id\\": \\"id\\" +} ) + #set( $modelQueryExpression.expressionValues = { + \\":id\\": { + \\"S\\": \\"$ctx.args.id\\" + } +} ) +#end +## [Start] Applying Key Condition ** +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.beginsWith) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND begins_with(#sortKey, :sortKey)\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.email.beginsWith\\" })) +#end +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.between) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey BETWEEN :sortKey0 AND :sortKey1\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey0\\", { \\"S\\": \\"$ctx.args.email.between[0]\\" })) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey1\\", { \\"S\\": \\"$ctx.args.email.between[1]\\" })) +#end +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.eq) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey = :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.email.eq\\" })) +#end +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.lt) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey < :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.email.lt\\" })) +#end +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.le) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey <= :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.email.le\\" })) +#end +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.gt) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey > :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.email.gt\\" })) +#end +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.ge) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey >= :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.email.ge\\" })) +#end +## [End] Applying Key Condition ** +## [End] Set query expression for key ** +## [Start] List Request. ** +#set( $limit = $util.defaultIfNull($context.args.limit, 100) ) +#set( $ListRequest = { + \\"version\\": \\"2018-05-29\\", + \\"limit\\": $limit +} ) +#if( $context.args.nextToken ) + #set( $ListRequest.nextToken = $context.args.nextToken ) +#end +#if( $context.args.filter ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $ListRequest.filter = $filterExpression ) + #end +#end +#if( !$util.isNull($ctx.stash.modelQuery) ) + #set( $Query = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.modelQuery)) ) + $util.qr($ListRequest.put(\\"operation\\", \\"Query\\")) + $util.qr($ListRequest.put(\\"query\\", $Query)) + #if( !$util.isNull($ctx.args.sortDirection) && $ctx.args.sortDirection == \\"DESC\\" ) + #set( $ListRequest.scanIndexForward = false ) + #else + #set( $ListRequest.scanIndexForward = true ) + #end +#else + $util.qr($ListRequest.put(\\"operation\\", \\"Scan\\")) +#end +#if( !$util.isNull($ctx.stash.metadata.index) ) + #set( $ListRequest.IndexName = $ctx.stash.metadata.index ) +#end +$util.toJson($ListRequest) +## [End] List Request. **", + "Query.testList.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", +} +`; + +exports[`resolvers can be renamed by @model 1`] = ` +Object { + "Mutation.createTest.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $createdAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"id\\", $util.autoId())) +$util.qr($ctx.stash.defaultValues.put(\\"createdAt\\", $createdAt)) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $createdAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.createTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** + +## [Start] Create Request template. ** +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.add(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +$util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) +#set( $PutObject = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"PutItem\\", + \\"attributeValues\\": $util.dynamodb.toMapValues($mergedValues), + \\"condition\\": $condition +} ) +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": false +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($PutObject.put(\\"condition\\", $Conditions)) +#end +#if( $ctx.stash.metadata.modelObject ) + $PutObject.put(\\"id\\", $ctx.stash.metadata.modelObject) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($mergedValues.id) +} ) + $util.qr($PutObject.put(\\"key\\", $Key)) +#end +$util.toJson($PutObject) +## [End] Create Request template. **", + "Mutation.createTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.deleteTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** +## [Start] Delete Request template. ** +#set( $DeleteRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"DeleteItem\\" +} ) +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $Key = $ctx.stash.metadata.modelObjectKey ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +$util.qr($DeleteRequest.put(\\"key\\", $Key)) +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($DeleteRequest.put(\\"condition\\", $Conditions)) +#end +$util.toJson($DeleteRequest) +## [End] Delete Request template. **", + "Mutation.deleteTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.testCreate.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $createdAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"id\\", $util.autoId())) +$util.qr($ctx.stash.defaultValues.put(\\"createdAt\\", $createdAt)) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $createdAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.testCreate.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id), + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** + +## [Start] Create Request template. ** +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.add(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +$util.qr($mergedValues.put(\\"__typename\\", \\"Test\\")) +#set( $PutObject = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"PutItem\\", + \\"attributeValues\\": $util.dynamodb.toMapValues($mergedValues), + \\"condition\\": $condition +} ) +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Begin - KeyCondition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": false +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": false + } +})) +#end +## End - KeyCondition ** +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($PutObject.put(\\"condition\\", $Conditions)) +#end +#if( $ctx.stash.metadata.modelObject ) + $PutObject.put(\\"id\\", $ctx.stash.metadata.modelObject) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($mergedValues.id) +} ) + $util.qr($PutObject.put(\\"key\\", $Key)) +#end +$util.toJson($PutObject) +## [End] Create Request template. **", + "Mutation.testCreate.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.testDelete.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id), + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** +## [Start] Delete Request template. ** +#set( $DeleteRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"DeleteItem\\" +} ) +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $Key = $ctx.stash.metadata.modelObjectKey ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +$util.qr($DeleteRequest.put(\\"key\\", $Key)) +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#if( $Conditions ) + $util.qr($DeleteRequest.put(\\"condition\\", $Conditions)) +#end +$util.toJson($DeleteRequest) +## [End] Delete Request template. **", + "Mutation.testDelete.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.testUpdate.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $updatedAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $updatedAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.testUpdate.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id), + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** + +## [Start] Mutation Update resolver. ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +## Initialize the vars for creating ddb expression ** +#set( $expNames = {} ) +#set( $expValues = {} ) +#set( $expSet = {} ) +#set( $expAdd = {} ) +#set( $expRemove = [] ) +#if( $ctx.stash.metadata.modelObjetKey ) + #set( $Key = $util.dynamodb.toDynamoDB($ctx.stash.metadata.modelObjetKey) ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +## Model key ** +#if( $ctx.stash.metadata.modelObjetKey ) + #foreach( $entry in $ctx.stash.metadata.modelObjetKey.entrySet() ) + $util.qr($keyFields.add(\\"$entry.key\\")) + #end +#else + #set( $keyFields = [\\"id\\"] ) +#end +#foreach( $entry in $util.map.copyAndRemoveAllKeys($mergedValues, $keyFields).entrySet() ) + #if( $util.isNull($entry.value) ) + #set( $discard = $expRemove.add(\\"#$entry.key\\") ) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + #else + $util.qr($expSet.put(\\"#$entry.key\\", \\":$entry.key\\")) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + $util.qr($expValues.put(\\":$entry.key\\", $util.dynamodb.toDynamoDB($entry.value))) + #end +#end +#set( $expression = \\"\\" ) +#if( !$expSet.isEmpty() ) + #set( $expression = \\"SET\\" ) + #foreach( $entry in $expSet.entrySet() ) + #set( $expression = \\"$expression $entry.key = $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expAdd.isEmpty() ) + #set( $expression = \\"$expression ADD\\" ) + #foreach( $entry in $expAdd.entrySet() ) + #set( $expression = \\"$expression $entry.key $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expRemove.isEmpty() ) + #set( $expression = \\"$expression REMOVE\\" ) + #foreach( $entry in $expRemove ) + #set( $expression = \\"$expression $entry\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#set( $update = {} ) +$util.qr($update.put(\\"expression\\", \\"$expression\\")) +#if( !$expNames.isEmpty() ) + $util.qr($update.put(\\"expressionNames\\", $expNames)) +#end +#if( !$expValues.isEmpty() ) + $util.qr($update.put(\\"expressionValues\\", $expValues)) +#end +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#set( $UpdateItem = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"UpdateItem\\", + \\"key\\": $Key, + \\"update\\": $update +} ) +#if( $Conditions ) + $util.qr($UpdateItem.put(\\"condition\\", $Conditions)) +#end +$util.toJson($UpdateItem) +## [End] Mutation Update resolver. **", + "Mutation.testUpdate.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Mutation.updateTest.init.1.req.vtl": "## [Start] Initialization default values. ** +$util.qr($ctx.stash.put(\\"defaultValues\\", $util.defaultIfNull($ctx.stash.defaultValues, {}))) +#set( $updatedAt = $util.time.nowISO8601() ) +$util.qr($ctx.stash.defaultValues.put(\\"updatedAt\\", $updatedAt)) +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Initialization default values. **", + "Mutation.updateTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.input.email) +} ) +## [End] Set the primary key. ** + +## [Start] Mutation Update resolver. ** +## Set the default values to put request ** +#set( $mergedValues = $util.defaultIfNull($ctx.stash.defaultValues, {}) ) +## copy the values from input ** +$util.qr($mergedValues.putAll($util.defaultIfNull($ctx.args.input, {}))) +## set the typename ** +## Initialize the vars for creating ddb expression ** +#set( $expNames = {} ) +#set( $expValues = {} ) +#set( $expSet = {} ) +#set( $expAdd = {} ) +#set( $expRemove = [] ) +#if( $ctx.stash.metadata.modelObjetKey ) + #set( $Key = $util.dynamodb.toDynamoDB($ctx.stash.metadata.modelObjetKey) ) +#else + #set( $Key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.input.id) +} ) +#end +## Model key ** +#if( $ctx.stash.metadata.modelObjetKey ) + #foreach( $entry in $ctx.stash.metadata.modelObjetKey.entrySet() ) + $util.qr($keyFields.add(\\"$entry.key\\")) + #end +#else + #set( $keyFields = [\\"id\\"] ) +#end +#foreach( $entry in $util.map.copyAndRemoveAllKeys($mergedValues, $keyFields).entrySet() ) + #if( $util.isNull($entry.value) ) + #set( $discard = $expRemove.add(\\"#$entry.key\\") ) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + #else + $util.qr($expSet.put(\\"#$entry.key\\", \\":$entry.key\\")) + $util.qr($expNames.put(\\"#$entry.key\\", \\"$entry.key\\")) + $util.qr($expValues.put(\\":$entry.key\\", $util.dynamodb.toDynamoDB($entry.value))) + #end +#end +#set( $expression = \\"\\" ) +#if( !$expSet.isEmpty() ) + #set( $expression = \\"SET\\" ) + #foreach( $entry in $expSet.entrySet() ) + #set( $expression = \\"$expression $entry.key = $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expAdd.isEmpty() ) + #set( $expression = \\"$expression ADD\\" ) + #foreach( $entry in $expAdd.entrySet() ) + #set( $expression = \\"$expression $entry.key $entry.value\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#if( !$expRemove.isEmpty() ) + #set( $expression = \\"$expression REMOVE\\" ) + #foreach( $entry in $expRemove ) + #set( $expression = \\"$expression $entry\\" ) + #if( $foreach.hasNext() ) + #set( $expression = \\"$expression,\\" ) + #end + #end +#end +#set( $update = {} ) +$util.qr($update.put(\\"expression\\", \\"$expression\\")) +#if( !$expNames.isEmpty() ) + $util.qr($update.put(\\"expressionNames\\", $expNames)) +#end +#if( !$expValues.isEmpty() ) + $util.qr($update.put(\\"expressionValues\\", $expValues)) +#end +## Begin - key condition ** +#if( $ctx.stash.metadata.modelObjectKey ) + #set( $keyConditionExpr = {} ) + #foreach( $entry in $ctx.stash.metadata.modelObjectKey.entrySet() ) + $util.qr($keyConditionExpr.put(\\"$entry.key\\", { + \\"attributeExists\\": true +})) + #end + $util.qr($ctx.stash.conditions.add($keyConditionExpr)) +#else + $util.qr($ctx.stash.conditions.add({ + \\"id\\": { + \\"attributeExists\\": true + } +})) +#end +## End - key condition ** +#if( $context.args.condition ) + $util.qr($ctx.stash.conditions.add($context.args.condition)) +#end +## Start condition block ** +#if( $ctx.stash.conditions && $ctx.stash.conditions.size() != 0 ) + #set( $mergedConditions = { + \\"and\\": $ctx.stash.conditions +} ) + #set( $Conditions = $util.parseJson($util.transform.toDynamoDBConditionExpression($mergedConditions)) ) + #if( $Conditions.expressionValues && $Conditions.expressionValues.size() == 0 ) + #set( $Conditions = { + \\"expression\\": $Conditions.expression, + \\"expressionNames\\": $Conditions.expressionNames +} ) + #end + ## End condition block ** +#end +#set( $UpdateItem = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"UpdateItem\\", + \\"key\\": $Key, + \\"update\\": $update +} ) +#if( $Conditions ) + $util.qr($UpdateItem.put(\\"condition\\", $Conditions)) +#end +$util.toJson($UpdateItem) +## [End] Mutation Update resolver. **", + "Mutation.updateTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.getTest.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) +} ) +## [End] Set the primary key. ** +## [Start] Get Request template. ** +#set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"GetItem\\" +} ) +#if( $ctx.stash.metadata.modelKeyObject ) + #set( $Key = $ctx.stash.metadata.modelKeyObject ) +#else + #set( $key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) +} ) +#end +$util.qr($GetRequest.put(\\"key\\", $key)) +$util.toJson($GetRequest) +## [End] Get Request template. **", + "Query.getTest.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.listTests.req.vtl": "## [Start] Set query expression for key ** +#if( !$util.isNull($ctx.args.sortDirection) ) + $util.error(\\"sortDirection is not supported for List operations without a Sort key defined.\\", \\"InvalidArgumentsError\\") +#end +#set( $modelQueryExpression = {} ) +#if( !$util.isNull($ctx.args.email) ) + #set( $modelQueryExpression.expression = \\"#email = :email\\" ) + #set( $modelQueryExpression.expressionNames = { + \\"#email\\": \\"email\\" +} ) + #set( $modelQueryExpression.expressionValues = { + \\":email\\": { + \\"S\\": \\"$ctx.args.email\\" + } +} ) +#end +## [End] Set query expression for key ** +## [Start] List Request. ** +#set( $limit = $util.defaultIfNull($context.args.limit, 100) ) +#set( $ListRequest = { + \\"version\\": \\"2018-05-29\\", + \\"limit\\": $limit +} ) +#if( $context.args.nextToken ) + #set( $ListRequest.nextToken = $context.args.nextToken ) +#end +#if( $context.args.filter ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $ListRequest.filter = $filterExpression ) + #end +#end +#if( !$util.isNull($ctx.stash.modelQuery) ) + #set( $Query = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.modelQuery)) ) + $util.qr($ListRequest.put(\\"operation\\", \\"Query\\")) + $util.qr($ListRequest.put(\\"query\\", $Query)) + #if( !$util.isNull($ctx.args.sortDirection) && $ctx.args.sortDirection == \\"DESC\\" ) + #set( $ListRequest.scanIndexForward = false ) + #else + #set( $ListRequest.scanIndexForward = true ) + #end +#else + $util.qr($ListRequest.put(\\"operation\\", \\"Scan\\")) +#end +#if( !$util.isNull($ctx.stash.metadata.index) ) + #set( $ListRequest.IndexName = $ctx.stash.metadata.index ) +#end +$util.toJson($ListRequest) +## [End] List Request. **", + "Query.listTests.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.testGet.req.vtl": "## [Start] Set the primary key. ** +#set( $modelObjectKey = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id), + \\"email\\": $util.dynamodb.toDynamoDB($ctx.args.email) +} ) +## [End] Set the primary key. ** +## [Start] Get Request template. ** +#set( $GetRequest = { + \\"version\\": \\"2018-05-29\\", + \\"operation\\": \\"GetItem\\" +} ) +#if( $ctx.stash.metadata.modelKeyObject ) + #set( $Key = $ctx.stash.metadata.modelKeyObject ) +#else + #set( $key = { + \\"id\\": $util.dynamodb.toDynamoDB($ctx.args.id) +} ) +#end +$util.qr($GetRequest.put(\\"key\\", $key)) +$util.toJson($GetRequest) +## [End] Get Request template. **", + "Query.testGet.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Query.testList.req.vtl": "## [Start] Set query expression for key ** +#if( $util.isNull($ctx.args.id) && !$util.isNull($ctx.args.sortDirection) ) + $util.error(\\"When providing argument 'sortDirection' you must also provide argument 'id'.\\", \\"InvalidArgumentsError\\") +#end +#set( $modelQueryExpression = {} ) +## [Start] Validate key arguments. ** +#if( !$util.isNull($ctx.args.email) && $util.isNull($ctx.args.id) ) + $util.error(\\"When providing argument 'email' you must also provide arguments id\\", \\"InvalidArgumentsError\\") +#end +## [End] Validate key arguments. ** +#if( !$util.isNull($ctx.args.id) ) + #set( $modelQueryExpression.expression = \\"#id = :id\\" ) + #set( $modelQueryExpression.expressionNames = { + \\"#id\\": \\"id\\" +} ) + #set( $modelQueryExpression.expressionValues = { + \\":id\\": { + \\"S\\": \\"$ctx.args.id\\" + } +} ) +#end +## [Start] Applying Key Condition ** +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.beginsWith) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND begins_with(#sortKey, :sortKey)\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.email.beginsWith\\" })) +#end +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.between) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey BETWEEN :sortKey0 AND :sortKey1\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey0\\", { \\"S\\": \\"$ctx.args.email.between[0]\\" })) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey1\\", { \\"S\\": \\"$ctx.args.email.between[1]\\" })) +#end +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.eq) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey = :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.email.eq\\" })) +#end +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.lt) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey < :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.email.lt\\" })) +#end +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.le) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey <= :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.email.le\\" })) +#end +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.gt) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey > :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.email.gt\\" })) +#end +#if( !$util.isNull($ctx.args.email) && !$util.isNull($ctx.args.email.ge) ) + #set( $modelQueryExpression.expression = \\"$modelQueryExpression.expression AND #sortKey >= :sortKey\\" ) + $util.qr($modelQueryExpression.expressionNames.put(\\"#sortKey\\", \\"email\\")) + $util.qr($modelQueryExpression.expressionValues.put(\\":sortKey\\", { \\"S\\": \\"$ctx.args.email.ge\\" })) +#end +## [End] Applying Key Condition ** +## [End] Set query expression for key ** +## [Start] List Request. ** +#set( $limit = $util.defaultIfNull($context.args.limit, 100) ) +#set( $ListRequest = { + \\"version\\": \\"2018-05-29\\", + \\"limit\\": $limit +} ) +#if( $context.args.nextToken ) + #set( $ListRequest.nextToken = $context.args.nextToken ) +#end +#if( $context.args.filter ) + #set( $filterExpression = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.args.filter)) ) + #if( !$util.isNullOrBlank($filterExpression.expression) ) + #if( $filterEpression.expressionValues.size() == 0 ) + $util.qr($filterEpression.remove(\\"expressionValues\\")) + #end + #set( $ListRequest.filter = $filterExpression ) + #end +#end +#if( !$util.isNull($ctx.stash.modelQuery) ) + #set( $Query = $util.parseJson($util.transform.toDynamoDBFilterExpression($ctx.stash.modelQuery)) ) + $util.qr($ListRequest.put(\\"operation\\", \\"Query\\")) + $util.qr($ListRequest.put(\\"query\\", $Query)) + #if( !$util.isNull($ctx.args.sortDirection) && $ctx.args.sortDirection == \\"DESC\\" ) + #set( $ListRequest.scanIndexForward = false ) + #else + #set( $ListRequest.scanIndexForward = true ) + #end +#else + $util.qr($ListRequest.put(\\"operation\\", \\"Scan\\")) +#end +#if( !$util.isNull($ctx.stash.metadata.index) ) + #set( $ListRequest.IndexName = $ctx.stash.metadata.index ) +#end +$util.toJson($ListRequest) +## [End] List Request. **", + "Query.testList.res.vtl": "## [Start] Get ResponseTemplate. ** +#if( $ctx.error ) + $util.error($ctx.error.message, $ctx.error.type) +#else + $util.toJson($ctx.result) +#end +## [End] Get ResponseTemplate. **", + "Subscription.onCreateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onCreateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onDeleteTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onDeleteTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", + "Subscription.onUpdateTest.req.vtl": "## [Start] Subscription Request template. ** +$util.toJson({ + \\"version\\": \\"2018-05-29\\", + \\"payload\\": {} +}) +## [End] Subscription Request template. **", + "Subscription.onUpdateTest.res.vtl": "## [Start] Subscription Resonse template. ** +$util.toJson(null) +## [End] Subscription Resonse template. **", +} +`; diff --git a/packages/amplify-graphql-index-transformer/src/__tests__/amplify-graphql-primary-key-transformer.test.ts b/packages/amplify-graphql-index-transformer/src/__tests__/amplify-graphql-primary-key-transformer.test.ts new file mode 100644 index 00000000000..e574c994cdf --- /dev/null +++ b/packages/amplify-graphql-index-transformer/src/__tests__/amplify-graphql-primary-key-transformer.test.ts @@ -0,0 +1,594 @@ +import { ModelTransformer } from '@aws-amplify/graphql-model-transformer'; +import { GraphQLTransform } from '@aws-amplify/graphql-transformer-core'; +import { expect as cdkExpect, haveResourceLike } from '@aws-cdk/assert'; +import { Kind, parse } from 'graphql'; +import { PrimaryKeyTransformer } from '..'; + +test('throws if multiple primary keys are defined on an object', () => { + const schema = ` + type Test @model { + id: ID! @primaryKey + email: String! @primaryKey + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + + expect(() => { + transformer.transform(schema); + }).toThrow(`You may only supply one primary key on type 'Test'.`); +}); + +test('throws if primary key is nullable', () => { + const schema = ` + type Test @model { + id: ID @primaryKey + email: String + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + + expect(() => { + transformer.transform(schema); + }).toThrow(`The primary key on type 'Test' must reference non-null fields.`); +}); + +test('throws if @primaryKey is used in a non-@model type', () => { + const schema = ` + type Test { + id: ID! @primaryKey + email: String + }`; + + const transformer = new GraphQLTransform({ + transformers: [new PrimaryKeyTransformer()], + }); + + expect(() => { + transformer.transform(schema); + }).toThrow('The @primaryKey directive may only be added to object definitions annotated with @model.'); +}); + +test('throws if @primaryKey is used on a non-scalar field', () => { + const schema = ` + type NonScalar { + id: ID! + } + + type Test @model { + id: NonScalar! @primaryKey + email: String + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + + expect(() => { + transformer.transform(schema); + }).toThrow(`The primary key on type 'Test.id' cannot be a non-scalar.`); +}); + +test('throws if @primaryKey uses a sort key field that does not exist', () => { + const schema = ` + type Test @model { + id: ID! @primaryKey(sortKeyFields: ["doesnotexist"]) + email: String + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + + expect(() => { + transformer.transform(schema); + }).toThrow(`Can't find field 'doesnotexist' in Test, but it was specified in the primary key.`); +}); + +test('throws if @primaryKey uses a sort key field that is a non-scalar', () => { + const schema = ` + type NonScalar { + id: ID! + } + + type Test @model { + id: ID! @primaryKey(sortKeyFields: ["email"]) + email: NonScalar + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + + expect(() => { + transformer.transform(schema); + }).toThrow(`The primary key's sort key on type 'Test.email' cannot be a non-scalar.`); +}); + +test('handles sortKeyFields being a string instead of an array', () => { + const schema = ` + type NonScalar { + id: ID! + } + + type Test @model { + id: ID! @primaryKey(sortKeyFields: "email") + email: NonScalar + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + + expect(() => { + transformer.transform(schema); + }).toThrow(`The primary key's sort key on type 'Test.email' cannot be a non-scalar.`); +}); + +test('a primary key with no sort key is properly configured', () => { + const inputSchema = ` + type Test @model { + email: String! @primaryKey + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + const out = transformer.transform(inputSchema); + const schema = parse(out.schema); + const stack = out.stacks.Test; + + cdkExpect(stack).to( + haveResourceLike('AWS::DynamoDB::Table', { + KeySchema: [{ AttributeName: 'email', KeyType: 'HASH' }], + AttributeDefinitions: [{ AttributeName: 'email', AttributeType: 'S' }], + }), + ); + + expect(out.pipelineFunctions).toMatchSnapshot(); + + const queryType: any = schema.definitions.find((def: any) => def.name && def.name.value === 'Query'); + const getTestField: any = queryType.fields.find((f: any) => f.name && f.name.value === 'getTest'); + expect(getTestField.arguments).toHaveLength(1); + expect(getTestField.arguments[0].name.value).toEqual('email'); + + // The auto-generated 'id' primary key should have been removed. + const createInput: any = schema.definitions.find((d: any) => { + return d.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION && d.name.value === 'CreateTestInput'; + }); + expect(createInput).toBeDefined(); + const defaultIdField = createInput.fields.find((f: any) => f.name.value === 'id'); + expect(defaultIdField).toBeUndefined(); +}); + +test('a primary key with a single sort key field is properly configured', () => { + const inputSchema = ` + type Test @model { + email: String! @primaryKey(sortKeyFields: "kind") + kind: Int! + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + const out = transformer.transform(inputSchema); + const schema = parse(out.schema); + const stack = out.stacks.Test; + + cdkExpect(stack).to( + haveResourceLike('AWS::DynamoDB::Table', { + KeySchema: [ + { AttributeName: 'email', KeyType: 'HASH' }, + { AttributeName: 'kind', KeyType: 'RANGE' }, + ], + AttributeDefinitions: [ + { AttributeName: 'email', AttributeType: 'S' }, + { AttributeName: 'kind', AttributeType: 'N' }, + ], + }), + ); + + expect(out.pipelineFunctions).toMatchSnapshot(); + + const queryType: any = schema.definitions.find((def: any) => def.name && def.name.value === 'Query'); + const getTestField: any = queryType.fields.find((f: any) => f.name && f.name.value === 'getTest'); + expect(getTestField.arguments).toHaveLength(2); + expect(getTestField.arguments[0].name.value).toEqual('email'); + expect(getTestField.arguments[1].name.value).toEqual('kind'); +}); + +test('a primary key with a composite sort key is properly configured', () => { + const inputSchema = ` + type Test @model { + email: String! @primaryKey(sortKeyFields: ["kind", "other"]) + kind: Int! + other: AWSDateTime + yetAnother: String + andAnother: String! + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + const out = transformer.transform(inputSchema); + const schema = parse(out.schema); + const stack = out.stacks.Test; + + cdkExpect(stack).to( + haveResourceLike('AWS::DynamoDB::Table', { + KeySchema: [ + { AttributeName: 'email', KeyType: 'HASH' }, + { AttributeName: 'kind#other', KeyType: 'RANGE' }, + ], + AttributeDefinitions: [ + { AttributeName: 'email', AttributeType: 'S' }, + { AttributeName: 'kind#other', AttributeType: 'S' }, + ], + }), + ); + + expect(out.pipelineFunctions).toMatchSnapshot(); + + const queryType: any = schema.definitions.find((def: any) => def.name && def.name.value === 'Query'); + const getTestField: any = queryType.fields.find((f: any) => f.name && f.name.value === 'getTest'); + + expect(getTestField.arguments).toHaveLength(3); + expect(getTestField.arguments[0].name.value).toEqual('email'); + expect(getTestField.arguments[1].name.value).toEqual('kind'); + expect(getTestField.arguments[2].name.value).toEqual('other'); + + const listTestField: any = queryType.fields.find((f: any) => f.name && f.name.value === 'listTests'); + expect(listTestField.arguments).toHaveLength(6); + expect(listTestField.arguments[0].name.value).toEqual('email'); + expect(listTestField.arguments[1].name.value).toEqual('kindOther'); + expect(listTestField.arguments[2].name.value).toEqual('filter'); + expect(listTestField.arguments[3].name.value).toEqual('limit'); + expect(listTestField.arguments[4].name.value).toEqual('nextToken'); + expect(listTestField.arguments[5].name.value).toEqual('sortDirection'); + + const createInput: any = schema.definitions.find((def: any) => def.name && def.name.value === 'CreateTestInput'); + const updateInput: any = schema.definitions.find((def: any) => def.name && def.name.value === 'UpdateTestInput'); + const deleteInput: any = schema.definitions.find((def: any) => def.name && def.name.value === 'DeleteTestInput'); + + expect(createInput).toBeDefined(); + expect(createInput.fields.find((f: any) => f.name.value === 'email' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(createInput.fields.find((f: any) => f.name.value === 'kind' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(createInput.fields.find((f: any) => f.name.value === 'other' && f.type.kind === Kind.NAMED_TYPE)).toBeDefined(); + expect(createInput.fields.find((f: any) => f.name.value === 'yetAnother' && f.type.kind === Kind.NAMED_TYPE)).toBeDefined(); + expect(createInput.fields.find((f: any) => f.name.value === 'andAnother' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(createInput.fields.find((f: any) => f.name.value === 'id')).toBeUndefined(); + expect(updateInput).toBeDefined(); + expect(updateInput.fields.find((f: any) => f.name.value === 'email' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(updateInput.fields.find((f: any) => f.name.value === 'kind' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(updateInput.fields.find((f: any) => f.name.value === 'other' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(updateInput.fields.find((f: any) => f.name.value === 'yetAnother' && f.type.kind === Kind.NAMED_TYPE)).toBeDefined(); + expect(updateInput.fields.find((f: any) => f.name.value === 'andAnother' && f.type.kind === Kind.NAMED_TYPE)).toBeDefined(); + expect(updateInput.fields.find((f: any) => f.name.value === 'id')).toBeUndefined(); + expect(deleteInput).toBeDefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'email' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'kind' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'other' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'yetAnother')).toBeUndefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'andAnother')).toBeUndefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'id')).toBeUndefined(); +}); + +test('enums are supported in keys', () => { + const inputSchema = ` + enum Status { DELIVERED IN_TRANSIT PENDING UNKNOWN } + + type Test @model { + status: Status! @primaryKey(sortKeyFields: "lastStatus") + lastStatus: Status! + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + const out = transformer.transform(inputSchema); + const schema = parse(out.schema); + const stack = out.stacks.Test; + + cdkExpect(stack).to( + haveResourceLike('AWS::DynamoDB::Table', { + KeySchema: [ + { AttributeName: 'status', KeyType: 'HASH' }, + { AttributeName: 'lastStatus', KeyType: 'RANGE' }, + ], + AttributeDefinitions: [ + { AttributeName: 'status', AttributeType: 'S' }, + { AttributeName: 'lastStatus', AttributeType: 'S' }, + ], + }), + ); + + expect(out.pipelineFunctions).toMatchSnapshot(); + + const queryType: any = schema.definitions.find((def: any) => def.name && def.name.value === 'Query'); + const getTestField: any = queryType.fields.find((f: any) => f.name && f.name.value === 'getTest'); + expect(getTestField.arguments).toHaveLength(2); + expect(getTestField.arguments[0].name.value).toEqual('status'); + expect(getTestField.arguments[1].name.value).toEqual('lastStatus'); +}); + +test('user provided id fields are not removed', () => { + const inputSchema = ` + type Test @model { + id: ID + email: String! @primaryKey + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + const out = transformer.transform(inputSchema); + const schema = parse(out.schema); + const stack = out.stacks.Test; + + cdkExpect(stack).to( + haveResourceLike('AWS::DynamoDB::Table', { + KeySchema: [{ AttributeName: 'email', KeyType: 'HASH' }], + AttributeDefinitions: [{ AttributeName: 'email', AttributeType: 'S' }], + }), + ); + + const createInput: any = schema.definitions.find((d: any) => { + return d.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION && d.name.value === 'CreateTestInput'; + }); + expect(createInput).toBeDefined(); + const defaultIdField = createInput.fields.find((f: any) => f.name.value === 'id'); + expect(defaultIdField).toBeDefined(); +}); + +test('null resolvers on @model are supported', () => { + const inputSchema = ` + type Test @model(queries: null, mutations: null, subscriptions: null) { + id: ID! @primaryKey + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + const out = transformer.transform(inputSchema); + const schema = parse(out.schema); + const stack = out.stacks.Test; + const definitions = schema.definitions.filter((d: any) => { + return ( + (d.kind === Kind.OBJECT_TYPE_DEFINITION && ['Query', 'Mutation', 'Subscription'].includes(d.name.value)) || + (d.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION && ['CreateTestInput', 'UpdateTestInput', 'DeleteTestInput'].includes(d.name.value)) + ); + }); + + expect(definitions).toEqual([]); +}); + +test('@model null resolvers can be overridden', () => { + const inputSchema = ` + type Test @model(queries: null, mutations: null) { + id: ID! @primaryKey + } + + type Mutation { + createTest(input: CreateTestInput!): Test + deleteTest(input: DeleteTestInput!): Test + } + + input CreateTestInput { + id: ID! + } + + input DeleteTestInput { + id: ID! + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + const out = transformer.transform(inputSchema); + const schema = parse(out.schema); + const stack = out.stacks.Test; + const definitions = schema.definitions + .filter((d: any) => { + return ( + (d.kind === Kind.OBJECT_TYPE_DEFINITION && ['Query', 'Mutation'].includes(d.name.value)) || + (d.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION && ['CreateTestInput', 'UpdateTestInput', 'DeleteTestInput'].includes(d.name.value)) + ); + }) + .map((d: any) => d.name.value); + + expect(definitions).toEqual(['Mutation', 'CreateTestInput', 'DeleteTestInput']); +}); + +test('resolvers can be renamed by @model', () => { + const inputSchema = ` + type Test + @model( + queries: { get: "testGet", list: "testList" }, + mutations: { create: "testCreate", delete: "testDelete", update: "testUpdate" } + ) { + id: ID! @primaryKey(sortKeyFields: ["email"]) + email: String + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + const out = transformer.transform(inputSchema); + const schema = parse(out.schema); + const stack = out.stacks.Test; + const query: any = schema.definitions.find((d: any) => d.kind === Kind.OBJECT_TYPE_DEFINITION && d.name.value === 'Query'); + const mutation: any = schema.definitions.find((d: any) => d.kind === Kind.OBJECT_TYPE_DEFINITION && d.name.value === 'Mutation'); + + expect(out.pipelineFunctions).toMatchSnapshot(); + + expect(query).toBeDefined(); + expect(query.fields.length).toEqual(2); + expect(mutation).toBeDefined(); + expect(mutation.fields.length).toEqual(3); + + const getQuery = query.fields.find((f: any) => f.name.value === 'testGet'); + const listQuery = query.fields.find((f: any) => f.name.value === 'testList'); + const createMutation = mutation.fields.find((f: any) => f.name.value === 'testCreate'); + const updateMutation = mutation.fields.find((f: any) => f.name.value === 'testUpdate'); + const deleteMutation = mutation.fields.find((f: any) => f.name.value === 'testDelete'); + + expect(getQuery).toBeDefined(); + expect(getQuery.arguments.length).toEqual(2); + getQuery.arguments.forEach((arg: any) => expect(arg.type.kind).toEqual(Kind.NON_NULL_TYPE)); + + expect(listQuery).toBeDefined(); + expect(listQuery.arguments.map((arg: any) => arg.name.value)).toEqual(['id', 'email', 'filter', 'limit', 'nextToken', 'sortDirection']); + + expect(createMutation).toBeDefined(); + expect(updateMutation).toBeDefined(); + expect(deleteMutation).toBeDefined(); +}); + +test('individual resolvers can be made null by @model', () => { + const inputSchema = ` + type Test @model(queries: { get: "testGet", list: null }) { + id: ID! @primaryKey(sortKeyFields: ["email"]) + email: String + }`; + + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + const out = transformer.transform(inputSchema); + const schema = parse(out.schema); + const stack = out.stacks.Test; + const query: any = schema.definitions.find((d: any) => d.kind === Kind.OBJECT_TYPE_DEFINITION && d.name.value === 'Query'); + + expect(out.pipelineFunctions).toMatchSnapshot(); + expect(query).toBeDefined(); + expect(query.fields.length).toEqual(1); + const getQuery = query.fields.find((f: any) => f.name.value === 'testGet'); + expect(getQuery).toBeDefined(); +}); + +it('id field should be optional in updateInputObjects when it is not a primary key', () => { + const inputSchema = ` + type Review @model { + id: ID! + email: String! + serviceId: ID! + owner: String! @primaryKey(sortKeyFields: "serviceId") + }`; + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + const out = transformer.transform(inputSchema); + const schema = parse(out.schema); + const updateReviewInput: any = schema.definitions.find( + (d: any) => d.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION && d.name.value === 'UpdateReviewInput', + ); + expect(updateReviewInput).toBeDefined(); + const idField = updateReviewInput.fields.find((f: any) => f.name.value === 'id'); + expect(idField).toBeDefined(); + expect(idField.type.kind).toBe(Kind.NAMED_TYPE); +}); + +test('primary key with id as partition key is not required on createInput', () => { + const inputSchema = ` + type Test @model { + id: ID! @primaryKey(sortKeyFields: "kind") + email: String! + kind: Int! + }`; + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + const out = transformer.transform(inputSchema); + const schema = parse(out.schema); + const createInput: any = schema.definitions.find((def: any) => def.name && def.name.value === 'CreateTestInput'); + const updateInput: any = schema.definitions.find((def: any) => def.name && def.name.value === 'UpdateTestInput'); + const deleteInput: any = schema.definitions.find((def: any) => def.name && def.name.value === 'DeleteTestInput'); + + expect(createInput).toBeDefined(); + expect(createInput.fields.find((f: any) => f.name.value === 'kind' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(createInput.fields.find((f: any) => f.name.value === 'email' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(createInput.fields.find((f: any) => f.name.value === 'id' && f.type.kind === Kind.NAMED_TYPE)).toBeDefined(); + expect(updateInput).toBeDefined(); + expect(updateInput.fields.find((f: any) => f.name.value === 'kind' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(updateInput.fields.find((f: any) => f.name.value === 'email' && f.type.kind === Kind.NAMED_TYPE)).toBeDefined(); + expect(updateInput.fields.find((f: any) => f.name.value === 'id' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(deleteInput).toBeDefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'kind' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'email')).toBeUndefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'id' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); +}); + +test('primary key with id and createdAt is not required on createInput', () => { + const inputSchema = ` + type Test @model { + id: ID! @primaryKey(sortKeyFields: "createdAt") + email: String! + createdAt: AWSDateTime! + }`; + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + const out = transformer.transform(inputSchema); + const schema = parse(out.schema); + const createInput: any = schema.definitions.find((def: any) => def.name && def.name.value === 'CreateTestInput'); + const updateInput: any = schema.definitions.find((def: any) => def.name && def.name.value === 'UpdateTestInput'); + const deleteInput: any = schema.definitions.find((def: any) => def.name && def.name.value === 'DeleteTestInput'); + + expect(createInput).toBeDefined(); + expect(createInput.fields.find((f: any) => f.name.value === 'createdAt' && f.type.kind === Kind.NAMED_TYPE)).toBeDefined(); + expect(createInput.fields.find((f: any) => f.name.value === 'email' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(createInput.fields.find((f: any) => f.name.value === 'id' && f.type.kind === Kind.NAMED_TYPE)).toBeDefined(); + expect(updateInput).toBeDefined(); + expect(updateInput.fields.find((f: any) => f.name.value === 'createdAt' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(updateInput.fields.find((f: any) => f.name.value === 'email' && f.type.kind === Kind.NAMED_TYPE)).toBeDefined(); + expect(updateInput.fields.find((f: any) => f.name.value === 'id' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(deleteInput).toBeDefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'createdAt' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'email')).toBeUndefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'id' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); +}); + +test('key with complex fields updates the input objects', () => { + const inputSchema = ` + type Test @model { + email: String! @primaryKey + nonNullListInputOfNonNullStrings: [String!]! + }`; + const transformer = new GraphQLTransform({ + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + }); + + const out = transformer.transform(inputSchema); + const schema = parse(out.schema); + const createInput: any = schema.definitions.find((def: any) => def.name && def.name.value === 'CreateTestInput'); + const updateInput: any = schema.definitions.find((def: any) => def.name && def.name.value === 'UpdateTestInput'); + const deleteInput: any = schema.definitions.find((def: any) => def.name && def.name.value === 'DeleteTestInput'); + + expect(createInput).toBeDefined(); + expect(createInput.fields.find((f: any) => f.name.value === 'email' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect( + createInput.fields.find((f: any) => { + return ( + f.name.value === 'nonNullListInputOfNonNullStrings' && + f.type.kind === Kind.NON_NULL_TYPE && + f.type.type.kind === Kind.LIST_TYPE && + f.type.type.type.kind === Kind.NON_NULL_TYPE + ); + }), + ).toBeDefined(); + expect(updateInput).toBeDefined(); + expect(updateInput.fields.find((f: any) => f.name.value === 'email' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect( + updateInput.fields.find((f: any) => { + return ( + f.name.value === 'nonNullListInputOfNonNullStrings' && f.type.kind === Kind.LIST_TYPE && f.type.type.kind === Kind.NON_NULL_TYPE + ); + }), + ).toBeDefined(); + expect(deleteInput).toBeDefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'email' && f.type.kind === Kind.NON_NULL_TYPE)).toBeDefined(); + expect(deleteInput.fields.find((f: any) => f.name.value === 'nonNullListInputOfNonNullStrings')).toBeUndefined(); +}); diff --git a/packages/amplify-graphql-index-transformer/src/graphql-primary-key-transformer.ts b/packages/amplify-graphql-index-transformer/src/graphql-primary-key-transformer.ts new file mode 100644 index 00000000000..a8c7651667a --- /dev/null +++ b/packages/amplify-graphql-index-transformer/src/graphql-primary-key-transformer.ts @@ -0,0 +1,142 @@ +import { DirectiveWrapper, InvalidDirectiveError, TransformerPluginBase } from '@aws-amplify/graphql-transformer-core'; +import { + TransformerContextProvider, + TransformerSchemaVisitStepContextProvider, + TransformerTransformSchemaStepContextProvider, +} from '@aws-amplify/graphql-transformer-interfaces'; +import { + DirectiveNode, + EnumTypeDefinitionNode, + FieldDefinitionNode, + InterfaceTypeDefinitionNode, + Kind, + ObjectTypeDefinitionNode, +} from 'graphql'; +import { isNonNullType, isScalarOrEnum } from 'graphql-transformer-common'; +import { replaceDdbPrimaryKey, updateResolvers } from './resolvers'; +import { + addKeyConditionInputs, + removeAutoCreatedPrimaryKey, + updateGetField, + updateInputObjects, + updateListField, + updateMutationConditionInput, +} from './schema'; +import { PrimaryKeyDirectiveConfiguration } from './types'; + +const directiveName = 'primaryKey'; +const directiveDefinition = ` + directive @${directiveName}(sortKeyFields: [String]) on FIELD_DEFINITION +`; + +export class PrimaryKeyTransformer extends TransformerPluginBase { + private directiveList: PrimaryKeyDirectiveConfiguration[] = []; + + constructor() { + super('amplify-primary-key-transformer', directiveDefinition); + } + + field = ( + parent: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, + definition: FieldDefinitionNode, + directive: DirectiveNode, + context: TransformerSchemaVisitStepContextProvider, + ): void => { + const directiveWrapped = new DirectiveWrapper(directive); + const args = directiveWrapped.getArguments({ + object: parent as ObjectTypeDefinitionNode, + field: definition, + directive, + } as PrimaryKeyDirectiveConfiguration); + + if (!args.sortKeyFields) { + args.sortKeyFields = []; + } else if (!Array.isArray(args.sortKeyFields)) { + args.sortKeyFields = [args.sortKeyFields]; + } + + args.sortKey = []; + + validate(args, context as TransformerContextProvider); + this.directiveList.push(args); + }; + + // TODO(cjihrig): before() and after() are needed to handle sync queries once they are supported. + + transformSchema = (ctx: TransformerTransformSchemaStepContextProvider): void => { + const context = ctx as TransformerContextProvider; + + for (const config of this.directiveList) { + updateGetField(config, context); + updateListField(config, context); + updateInputObjects(config, context); + removeAutoCreatedPrimaryKey(config, context); + addKeyConditionInputs(config, context); + updateMutationConditionInput(config, context); + } + }; + + generateResolvers = (ctx: TransformerContextProvider): void => { + for (const config of this.directiveList) { + replaceDdbPrimaryKey(config, ctx); + updateResolvers(config, ctx); + } + }; +} + +function validate(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider): void { + const { object, field, sortKeyFields } = config; + const modelDirective = object.directives!.find(directive => { + return directive.name.value === 'model'; + }); + + if (!modelDirective) { + throw new InvalidDirectiveError(`The @${directiveName} directive may only be added to object definitions annotated with @model.`); + } + + config.modelDirective = modelDirective; + + const fieldMap = new Map(); + + for (const objectField of object.fields!) { + fieldMap.set(objectField.name.value, objectField); + + if (field === objectField) { + continue; + } + + for (const directive of objectField.directives!) { + if (directive.name.value === directiveName) { + throw new InvalidDirectiveError(`You may only supply one primary key on type '${object.name.value}'.`); + } + } + } + + if (!isNonNullType(field.type)) { + throw new InvalidDirectiveError(`The primary key on type '${object.name.value}' must reference non-null fields.`); + } + + const enums = ctx.output.getTypeDefinitionsOfKind(Kind.ENUM_TYPE_DEFINITION) as EnumTypeDefinitionNode[]; + + if (!isScalarOrEnum(field.type, enums)) { + throw new InvalidDirectiveError(`The primary key on type '${object.name.value}.${field.name.value}' cannot be a non-scalar.`); + } + + for (const sortKeyFieldName of sortKeyFields) { + const sortField = fieldMap.get(sortKeyFieldName); + + if (!sortField) { + throw new InvalidDirectiveError( + `Can't find field '${sortKeyFieldName}' in ${object.name.value}, but it was specified in the primary key.`, + ); + } + + if (!isScalarOrEnum(sortField.type, enums)) { + throw new InvalidDirectiveError( + `The primary key's sort key on type '${object.name.value}.${sortField.name.value}' cannot be a non-scalar.`, + ); + } + + config.sortKey.push(sortField); + } +} diff --git a/packages/amplify-graphql-index-transformer/src/index.ts b/packages/amplify-graphql-index-transformer/src/index.ts new file mode 100644 index 00000000000..d151c883099 --- /dev/null +++ b/packages/amplify-graphql-index-transformer/src/index.ts @@ -0,0 +1 @@ +export { PrimaryKeyTransformer } from './graphql-primary-key-transformer'; diff --git a/packages/amplify-graphql-index-transformer/src/resolvers.ts b/packages/amplify-graphql-index-transformer/src/resolvers.ts new file mode 100644 index 00000000000..6e9ca2fc2f3 --- /dev/null +++ b/packages/amplify-graphql-index-transformer/src/resolvers.ts @@ -0,0 +1,274 @@ +import assert from 'assert'; +import { TransformerContextProvider, TransformerResolverProvider } from '@aws-amplify/graphql-transformer-interfaces'; +import { DynamoDbDataSource } from '@aws-cdk/aws-appsync'; +import { CfnTable } from '@aws-cdk/aws-dynamodb'; +import { Kind, ObjectTypeDefinitionNode, TypeNode } from 'graphql'; +import { + and, + block, + compoundExpression, + Expression, + ifElse, + iff, + obj, + print, + printBlock, + qref, + raw, + ref, + set, + str, +} from 'graphql-mapping-template'; +import { + applyKeyExpressionForCompositeKey, + attributeTypeFromScalar, + getBaseType, + graphqlName, + ModelResourceIDs, + ResourceConstants, + toCamelCase, +} from 'graphql-transformer-common'; +import { PrimaryKeyDirectiveConfiguration } from './types'; +import { lookupResolverName } from './utils'; + +export function replaceDdbPrimaryKey(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider): void { + // Replace the table's primary key with the value from @primaryKey. + const { object } = config; + const table = getTable(ctx, object); + const tableAttrDefs = table.attributeDefinitions as any[]; + const tableKeySchema = table.keySchema as any[]; + const keySchema = getDdbKeySchema(config); + const attrDefs = attributeDefinitions(config, ctx); + const existingAttrDefSet = new Set(tableAttrDefs.map(ad => ad.attributeName)); + + // First, remove any attribute definitions in the current primary key. + for (const existingKey of tableKeySchema) { + if (existingAttrDefSet.has(existingKey.attributeName)) { + table.attributeDefinitions = tableAttrDefs.filter(ad => { + return ad.attributeName !== existingKey.attributeName; + }); + existingAttrDefSet.delete(existingKey.attributeName); + } + } + + // Next, replace the key schema and add any new attribute definitions back. + table.keySchema = keySchema; + + for (const attr of attrDefs) { + if (!existingAttrDefSet.has(attr.attributeName)) { + (table.attributeDefinitions as any[]).push(attr); + } + } +} + +export function updateResolvers(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider): void { + const getResolver = getResolverObject(config, ctx, 'get'); + const listResolver = getResolverObject(config, ctx, 'list'); + const createResolver = getResolverObject(config, ctx, 'create'); + const updateResolver = getResolverObject(config, ctx, 'update'); + const deleteResolver = getResolverObject(config, ctx, 'delete'); + // TODO(cjihrig): Sync queries need to be supported here as well. + + if (getResolver) { + updateRequestMappingTemplate(getResolver, [setKeySnippet(config, false), (getResolver as any).requestMappingTemplate.content]); + } + + if (listResolver) { + updateRequestMappingTemplate(listResolver, [ + print(setQuerySnippet(config, ctx, true)), + (listResolver as any).requestMappingTemplate.content, + ]); + } + + if (createResolver) { + updateRequestMappingTemplate(createResolver, [ + setKeySnippet(config, true), + ensureCompositeKeySnippet(config, false), + (createResolver as any).requestMappingTemplate.content, + ]); + } + + if (updateResolver) { + updateRequestMappingTemplate(updateResolver, [ + setKeySnippet(config, true), + ensureCompositeKeySnippet(config, false), + (updateResolver as any).requestMappingTemplate.content, + ]); + } + + if (deleteResolver) { + updateRequestMappingTemplate(deleteResolver, [setKeySnippet(config, true), (deleteResolver as any).requestMappingTemplate.content]); + } +} + +function getTable(context: TransformerContextProvider, object: ObjectTypeDefinitionNode): CfnTable { + const ddbDataSource = context.dataSources.get(object) as DynamoDbDataSource; + const tableName = ModelResourceIDs.ModelTableResourceID(object.name.value); + const table = ddbDataSource.ds.stack.node.findChild(tableName) as any; + + assert(table); + return table.table; +} + +function getDdbKeySchema(config: PrimaryKeyDirectiveConfiguration) { + const schema = [{ attributeName: config.field.name.value, keyType: 'HASH' }]; + + if (config.sortKey.length > 0) { + schema.push({ attributeName: getSortKeyName(config), keyType: 'RANGE' }); + } + + return schema; +} + +function attributeTypeFromType(type: TypeNode, ctx: TransformerContextProvider) { + const baseTypeName = getBaseType(type); + const ofType = ctx.output.getType(baseTypeName); + if (ofType && ofType.kind === Kind.ENUM_TYPE_DEFINITION) { + return 'S'; + } + return attributeTypeFromScalar(type); +} + +function attributeDefinitions(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider) { + const { field, sortKey, sortKeyFields } = config; + const definitions = [{ attributeName: field.name.value, attributeType: attributeTypeFromType(field.type, ctx) }]; + + if (sortKeyFields.length === 1) { + definitions.push({ + attributeName: sortKeyFields[0], + attributeType: attributeTypeFromType(sortKey[0].type, ctx), + }); + } else if (sortKeyFields.length > 1) { + definitions.push({ + attributeName: getSortKeyName(config), + attributeType: 'S', + }); + } + + return definitions; +} + +function getSortKeyName(config: PrimaryKeyDirectiveConfiguration): string { + return config.sortKeyFields.join(ModelResourceIDs.ModelCompositeKeySeparator()); +} + +function getResolverObject(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider, op: string) { + // TODO(cjihrig): Need to handle sync queries once they are supported. + const resolverName = lookupResolverName(config, ctx, op); + + if (!resolverName) { + return null; + } + + const objectName = op === 'get' || op === 'list' ? ctx.output.getQueryTypeName() : ctx.output.getMutationTypeName(); + + if (!objectName) { + return null; + } + + return ctx.resolvers.getResolver(objectName, resolverName) ?? null; +} + +function updateRequestMappingTemplate(resolver: TransformerResolverProvider, lines: string[]): void { + (resolver as any).requestMappingTemplate.content = lines.join('\n'); +} + +function setKeySnippet(config: PrimaryKeyDirectiveConfiguration, isMutation: boolean): string { + const cmds: Expression[] = [set(ref(ResourceConstants.SNIPPETS.ModelObjectKey), modelObjectKeySnippet(config, isMutation))]; + return printBlock('Set the primary key')(compoundExpression(cmds)); +} + +function modelObjectKeySnippet(config: PrimaryKeyDirectiveConfiguration, isMutation: boolean) { + const { field, sortKeyFields } = config; + const argsPrefix = isMutation ? 'ctx.args.input' : 'ctx.args'; + const modelObject = { + [field.name.value]: ref(`util.dynamodb.toDynamoDB($${argsPrefix}.${field.name.value})`), + }; + + if (sortKeyFields.length > 1) { + const compositeSortKey = getSortKeyName(config); + const compositeSortKeyValue = sortKeyFields + .map(keyField => { + return `\${${argsPrefix}.${keyField}}`; + }) + .join(ModelResourceIDs.ModelCompositeKeySeparator()); + + modelObject[compositeSortKey] = ref(`util.dynamodb.toDynamoDB("${compositeSortKeyValue}")`); + } else if (sortKeyFields.length === 1) { + modelObject[sortKeyFields[0]] = ref(`util.dynamodb.toDynamoDB($${argsPrefix}.${sortKeyFields[0]})`); + } + + return obj(modelObject); +} + +function ensureCompositeKeySnippet(config: PrimaryKeyDirectiveConfiguration, conditionallySetSortKey: boolean): string { + const { sortKeyFields } = config; + + if (sortKeyFields.length < 2) { + return ''; + } + + const argsPrefix = 'ctx.args.input'; + const condensedSortKey = getSortKeyName(config); + const dynamoDBFriendlySortKeyName = toCamelCase(sortKeyFields.map(f => graphqlName(f))); + const condensedSortKeyValue = sortKeyFields + .map(keyField => { + return `\${${argsPrefix}.${keyField}}`; + }) + .join(ModelResourceIDs.ModelCompositeKeySeparator()); + + return print( + compoundExpression([ + ifElse( + raw(`$util.isNull($${ResourceConstants.SNIPPETS.DynamoDBNameOverrideMap})`), + set( + ref(ResourceConstants.SNIPPETS.DynamoDBNameOverrideMap), + obj({ + [condensedSortKey]: str(dynamoDBFriendlySortKeyName), + }), + ), + qref(`$${ResourceConstants.SNIPPETS.DynamoDBNameOverrideMap}.put("${condensedSortKey}", "${dynamoDBFriendlySortKeyName}")`), + ), + conditionallySetSortKey + ? iff( + ref(ResourceConstants.SNIPPETS.HasSeenSomeKeyArg), + qref(`$ctx.args.input.put("${condensedSortKey}","${condensedSortKeyValue}")`), + ) + : qref(`$ctx.args.input.put("${condensedSortKey}","${condensedSortKeyValue}")`), + ]), + ); +} + +function setQuerySnippet(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider, isListResolver: boolean) { + const { field, sortKey, sortKeyFields } = config; + const keyFields = [field, ...sortKey]; + const keyNames = [field.name.value, ...sortKeyFields]; + const keyTypes = keyFields.map(k => attributeTypeFromType(k.type, ctx)); + const expressions: Expression[] = []; + + if (keyNames.length === 1) { + const sortDirectionValidation = iff( + raw(`!$util.isNull($ctx.args.sortDirection)`), + raw(`$util.error("sortDirection is not supported for List operations without a Sort key defined.", "InvalidArgumentsError")`), + ); + + expressions.push(sortDirectionValidation); + } else if (isListResolver === true && keyNames.length >= 1) { + // This check is only needed for List queries. + const sortDirectionValidation = iff( + and([raw(`$util.isNull($ctx.args.${keyNames[0]})`), raw(`!$util.isNull($ctx.args.sortDirection)`)]), + raw( + `$util.error("When providing argument 'sortDirection' you must also provide argument '${keyNames[0]}'.", "InvalidArgumentsError")`, + ), + ); + + expressions.push(sortDirectionValidation); + } + + expressions.push( + set(ref(ResourceConstants.SNIPPETS.ModelQueryExpression), obj({})), + applyKeyExpressionForCompositeKey(keyNames, keyTypes, ResourceConstants.SNIPPETS.ModelQueryExpression), + ); + + return block(`Set query expression for key`, expressions); +} diff --git a/packages/amplify-graphql-index-transformer/src/schema.ts b/packages/amplify-graphql-index-transformer/src/schema.ts new file mode 100644 index 00000000000..8da13f82956 --- /dev/null +++ b/packages/amplify-graphql-index-transformer/src/schema.ts @@ -0,0 +1,282 @@ +import assert from 'assert'; +import { TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces'; +import { + EnumTypeDefinitionNode, + FieldDefinitionNode, + InputObjectTypeDefinitionNode, + InputValueDefinitionNode, + ObjectTypeDefinitionNode, +} from 'graphql'; +import { + getBaseType, + makeCompositeKeyConditionInputForKey, + makeCompositeKeyInputForKey, + makeInputValueDefinition, + makeNamedType, + makeNonNullType, + makeScalarKeyConditionForType, + ModelResourceIDs, + toCamelCase, + unwrapNonNull, + withNamedNodeNamed, + wrapNonNull, +} from 'graphql-transformer-common'; +import { PrimaryKeyDirectiveConfiguration } from './types'; +import { lookupResolverName } from './utils'; + +export function addKeyConditionInputs(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider): void { + const { object, sortKey } = config; + + if (sortKey.length > 1) { + const keyName = 'Primary'; + const keyConditionInput = makeCompositeKeyConditionInputForKey(object.name.value, keyName, sortKey); + + if (!ctx.output.getType(keyConditionInput.name.value)) { + ctx.output.addInput(keyConditionInput); + } + + const compositeKeyInput = makeCompositeKeyInputForKey(object.name.value, keyName, sortKey); + + if (!ctx.output.getType(compositeKeyInput.name.value)) { + ctx.output.addInput(compositeKeyInput); + } + } else if (sortKey.length === 1) { + const sortKeyField = sortKey[0]; + const typeResolver = (baseType: string) => { + const resolvedEnumType = ctx.output.getType(baseType) as EnumTypeDefinitionNode; + + return resolvedEnumType ? 'String' : undefined; + }; + const sortKeyConditionInput = makeScalarKeyConditionForType(sortKeyField.type, typeResolver as (baseType: string) => string); + assert(sortKeyConditionInput); + + if (!ctx.output.getType(sortKeyConditionInput.name.value)) { + ctx.output.addInput(sortKeyConditionInput); + } + } +} + +export function removeAutoCreatedPrimaryKey(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider): void { + const { object } = config; + const schemaHasIdField = object?.fields?.some(f => f.name.value === 'id'); + + if (schemaHasIdField) { + return; + } + + const obj = ctx.output.getObject(object.name.value) as ObjectTypeDefinitionNode; + const fields = obj.fields!.filter((f: FieldDefinitionNode) => f.name.value !== 'id'); + const newObj: ObjectTypeDefinitionNode = { + ...obj, + fields, + }; + + ctx.output.updateObject(newObj); +} + +export function updateGetField(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider): void { + const resolverName = lookupResolverName(config, ctx, 'get'); + let query = ctx.output.getQuery(); + + if (!(resolverName && query)) { + return; + } + + const { field, sortKey } = config; + let resolverField = query.fields!.find((field: FieldDefinitionNode) => field.name.value === resolverName) as FieldDefinitionNode; + assert(resolverField); + const args = [ + makeInputValueDefinition(field.name.value, makeNonNullType(makeNamedType(getBaseType(field.type)))), + ...sortKey.map(keyField => { + return makeInputValueDefinition(keyField.name.value, makeNonNullType(makeNamedType(getBaseType(keyField.type)))); + }), + ]; + + resolverField = { ...resolverField, arguments: args }; + query = { + ...query, + fields: query.fields!.map((field: FieldDefinitionNode) => { + return field.name.value === resolverField.name.value ? resolverField : field; + }), + }; + ctx.output.updateObject(query); +} + +export function updateListField(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider): void { + const resolverName = lookupResolverName(config, ctx, 'list'); + let query = ctx.output.getQuery(); + + if (!(resolverName && query)) { + return; + } + + const { sortKey } = config; + let listField = query.fields!.find((field: FieldDefinitionNode) => field.name.value === resolverName) as FieldDefinitionNode; + assert(listField); + + const args = [createHashField(config)]; + + if (sortKey.length === 1) { + args.push(createSimpleSortField(config, ctx)); + } else if (sortKey.length > 1) { + args.push(createCompositeSortField(config, ctx)); + } + + if (Array.isArray(listField.arguments)) { + args.push(...listField.arguments); + } + + args.push(makeInputValueDefinition('sortDirection', makeNamedType('ModelSortDirection'))); + + listField = { ...listField, arguments: args }; + query = { + ...query, + fields: query.fields!.map((field: FieldDefinitionNode) => { + return field.name.value === listField.name.value ? listField : field; + }), + }; + ctx.output.updateObject(query); +} + +export function updateInputObjects(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider): void { + const { object, modelDirective } = config; + let shouldMakeCreate = true; + let shouldMakeUpdate = true; + let shouldMakeDelete = true; + + // Check if @model changes the default behavior. + for (const argument of modelDirective.arguments!) { + const arg = argument as any; + + if (arg.name.value === 'mutations' && Array.isArray(arg.value.fields)) { + for (const argField of arg.value.fields) { + const op = argField.name.value; + const val = !!argField.value.value; + + if (op === 'create') { + shouldMakeCreate = val; + } else if (op === 'update') { + shouldMakeUpdate = val; + } else if (op === 'delete') { + shouldMakeDelete = val; + } + } + + break; + } + } + + const hasIdField = object.fields!.some((f: FieldDefinitionNode) => f.name.value === 'id'); + + if (!hasIdField) { + const createInput = ctx.output.getType(ModelResourceIDs.ModelCreateInputObjectName(object.name.value)) as InputObjectTypeDefinitionNode; + + if (createInput && shouldMakeCreate) { + ctx.output.putType(replaceCreateInput(createInput)); + } + } + + const updateInput = ctx.output.getType(ModelResourceIDs.ModelUpdateInputObjectName(object.name.value)) as InputObjectTypeDefinitionNode; + + if (updateInput && shouldMakeUpdate) { + ctx.output.putType(replaceUpdateInput(config, updateInput)); + } + + const deleteInput = ctx.output.getType(ModelResourceIDs.ModelDeleteInputObjectName(object.name.value)) as InputObjectTypeDefinitionNode; + + if (deleteInput && shouldMakeDelete) { + ctx.output.putType(replaceDeleteInput(config, deleteInput)); + } +} + +export function updateMutationConditionInput(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider): void { + const { field, sortKeyFields, object } = config; + const tableXMutationConditionInputName = ModelResourceIDs.ModelConditionInputTypeName(object.name.value); + const tableXMutationConditionInput = ctx.output.getType(tableXMutationConditionInputName) as InputObjectTypeDefinitionNode; + + if (!tableXMutationConditionInput) { + return; + } + + const fieldNames = new Set(['id', field.name.value, ...sortKeyFields]); + const updatedInput = { + ...tableXMutationConditionInput, + fields: tableXMutationConditionInput.fields!.filter(field => { + return !fieldNames.has(field.name.value); + }), + }; + + ctx.output.putType(updatedInput); +} + +function createHashField(config: PrimaryKeyDirectiveConfiguration): InputValueDefinitionNode { + const { field } = config; + + return makeInputValueDefinition(field.name.value, makeNamedType(getBaseType(field.type))); +} + +function createSimpleSortField(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider): InputValueDefinitionNode { + const { sortKey } = config; + assert(sortKey.length === 1); + const key = sortKey[0]; + const baseType = getBaseType(key.type); + const resolvedTypeIfEnum = (ctx.output.getType(baseType) as EnumTypeDefinitionNode) ? 'String' : undefined; + const resolvedType = resolvedTypeIfEnum ?? baseType; + + return makeInputValueDefinition(key.name.value, makeNamedType(ModelResourceIDs.ModelKeyConditionInputTypeName(resolvedType))); +} + +function createCompositeSortField(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider): InputValueDefinitionNode { + const { object, sortKeyFields } = config; + assert(sortKeyFields.length > 1); + const compositeSortKeyName = toCamelCase(sortKeyFields); + + return makeInputValueDefinition( + compositeSortKeyName, + makeNamedType(ModelResourceIDs.ModelCompositeKeyConditionInputTypeName(object.name.value, 'Primary')), + ); +} + +function replaceCreateInput(input: InputObjectTypeDefinitionNode): InputObjectTypeDefinitionNode { + return { ...input, fields: input.fields!.filter(f => f.name.value !== 'id') }; +} + +function replaceUpdateInput(config: PrimaryKeyDirectiveConfiguration, input: InputObjectTypeDefinitionNode): InputObjectTypeDefinitionNode { + const { field, object, sortKey } = config; + const schemaHasIdField = object.fields!.some(f => f.name.value === 'id'); + const keyFields = [field, ...sortKey]; + const inputFields = input.fields!.filter(f => { + if (!schemaHasIdField && f.name.value === 'id') { + return false; + } + + return true; + }); + + return { + ...input, + fields: inputFields.map(f => { + if (keyFields.find(k => k.name.value === f.name.value)) { + return makeInputValueDefinition(f.name.value, wrapNonNull(withNamedNodeNamed(f.type, getBaseType(f.type)))); + } + + if (f.name.value === 'id') { + return makeInputValueDefinition(f.name.value, unwrapNonNull(withNamedNodeNamed(f.type, getBaseType(f.type)))); + } + + return f; + }), + }; +} + +function replaceDeleteInput(config: PrimaryKeyDirectiveConfiguration, input: InputObjectTypeDefinitionNode): InputObjectTypeDefinitionNode { + const { field, sortKey } = config; + const primaryKeyFields = [field, ...sortKey].map((keyField: FieldDefinitionNode) => { + return makeInputValueDefinition(keyField.name.value, makeNonNullType(makeNamedType(getBaseType(keyField.type)))); + }); + const existingFields = input.fields!.filter( + f => !(primaryKeyFields.some(pf => pf.name.value === f.name.value) || (getBaseType(f.type) === 'ID' && f.name.value === 'id')), + ); + + return { ...input, fields: [...primaryKeyFields, ...existingFields] }; +} diff --git a/packages/amplify-graphql-index-transformer/src/types.ts b/packages/amplify-graphql-index-transformer/src/types.ts new file mode 100644 index 00000000000..ea7cb894172 --- /dev/null +++ b/packages/amplify-graphql-index-transformer/src/types.ts @@ -0,0 +1,10 @@ +import { DirectiveNode, FieldDefinitionNode, ObjectTypeDefinitionNode } from 'graphql'; + +export type PrimaryKeyDirectiveConfiguration = { + object: ObjectTypeDefinitionNode; + field: FieldDefinitionNode; + directive: DirectiveNode; + sortKeyFields: string[]; + sortKey: FieldDefinitionNode[]; + modelDirective: DirectiveNode; +}; diff --git a/packages/amplify-graphql-index-transformer/src/utils.ts b/packages/amplify-graphql-index-transformer/src/utils.ts new file mode 100644 index 00000000000..234ba9a1e76 --- /dev/null +++ b/packages/amplify-graphql-index-transformer/src/utils.ts @@ -0,0 +1,33 @@ +import { TransformerContextProvider } from '@aws-amplify/graphql-transformer-interfaces'; +import { PrimaryKeyDirectiveConfiguration } from './types'; + +export function lookupResolverName(config: PrimaryKeyDirectiveConfiguration, ctx: TransformerContextProvider, op: string): string | null { + const { object, modelDirective } = config; + const argName = op === 'get' || op === 'list' ? 'queries' : 'mutations'; + let resolverName; + + // Check if @model overrides the default resolver names. + for (const argument of modelDirective.arguments!) { + const arg = argument as any; + + if (arg.name.value !== argName || !Array.isArray(arg.value.fields)) { + continue; + } + + for (const field of arg.value.fields) { + if (field.name.value === op) { + resolverName = field.value.value; + + if (!resolverName) { + return null; + } + } + } + } + + if (!resolverName) { + resolverName = `${op}${object.name.value}${op === 'list' ? 's' : ''}`; + } + + return resolverName; +} diff --git a/packages/amplify-graphql-index-transformer/tsconfig.json b/packages/amplify-graphql-index-transformer/tsconfig.json new file mode 100644 index 00000000000..7dab9a19f90 --- /dev/null +++ b/packages/amplify-graphql-index-transformer/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib" + }, + "references": [ + {"path": "../amplify-graphql-transformer-core"}, + {"path": "../amplify-graphql-transformer-interfaces"}, + {"path": "../graphql-mapping-template"}, + {"path": "../graphql-transformer-common"} + ] +} diff --git a/packages/amplify-provider-awscloudformation/package.json b/packages/amplify-provider-awscloudformation/package.json index f8d5ed85e81..6f9c0aa9be4 100644 --- a/packages/amplify-provider-awscloudformation/package.json +++ b/packages/amplify-provider-awscloudformation/package.json @@ -28,6 +28,7 @@ "@aws-amplify/graphql-http-transformer": "0.4.3", "@aws-amplify/graphql-model-transformer": "0.4.6", "@aws-amplify/graphql-predictions-transformer": "0.2.2", + "@aws-amplify/graphql-index-transformer": "0.1.0", "@aws-amplify/graphql-searchable-transformer": "0.3.2", "@aws-amplify/graphql-transformer-core": "0.7.2", "@aws-amplify/graphql-transformer-interfaces": "1.7.0", diff --git a/packages/amplify-provider-awscloudformation/src/graphql-transformer/transform-graphql-schema.ts b/packages/amplify-provider-awscloudformation/src/graphql-transformer/transform-graphql-schema.ts index d2f7561d5ee..440510b55da 100644 --- a/packages/amplify-provider-awscloudformation/src/graphql-transformer/transform-graphql-schema.ts +++ b/packages/amplify-provider-awscloudformation/src/graphql-transformer/transform-graphql-schema.ts @@ -14,6 +14,7 @@ import { ModelTransformer } from '@aws-amplify/graphql-model-transformer'; import { FunctionTransformer } from '@aws-amplify/graphql-function-transformer'; import { HttpTransformer } from '@aws-amplify/graphql-http-transformer'; import { PredictionsTransformer } from '@aws-amplify/graphql-predictions-transformer'; +import { PrimaryKeyTransformer } from '@aws-amplify/graphql-index-transformer'; import { SearchableModelTransformer } from '@aws-amplify/graphql-searchable-transformer'; import { ProviderName as providerName } from '../constants'; import { hashDirectory } from '../upload-appsync-files'; @@ -52,6 +53,7 @@ function getTransformerFactory(context, resourceDir) { new FunctionTransformer(), new HttpTransformer(), new PredictionsTransformer(options?.storageConfig), + new PrimaryKeyTransformer(), // TODO: initialize transformer plugins ];