概要
アドベントカレンダー の1日目から5日目で作成したAppSyncとDynamoDBをCloudFormationで自動作成する方法を紹介します。
YMLファイルの作成
cfn.yml
Resources:
DynamoDBRadioTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: Radio
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
BillingMode: PAY_PER_REQUEST
AppSyncDynamoDBRole:
Type: AWS::IAM::Role
Properties:
RoleName: AppSyncDynamoDBRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Principal:
Service:
- appsync.amazonaws.com
Path: '/'
Policies:
- PolicyName: AppSyncDynamoDBPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:PutItem
- dynamodb:DeleteItem
- dynamodb:GetItem
- dynamodb:Scan
- dynamodb:Query
- dynamodb:UpdateItem
Resource: '*'
ApiCache:
Type: AWS::AppSync::ApiCache
Properties:
ApiCachingBehavior: PER_RESOLVER_CACHING
ApiId: !GetAtt GraphQLApi.ApiId
Ttl: 60
Type: SMALL
DataSourceRadio:
Type: AWS::AppSync::DataSource
Properties:
ApiId: !GetAtt GraphQLApi.ApiId
Name: Radio
Type: AMAZON_DYNAMODB
ServiceRoleArn: !GetAtt AppSyncDynamoDBRole.Arn
DynamoDBConfig:
AwsRegion: ap-northeast-1
TableName: !Ref DynamoDBRadioTable
GraphQLApi:
Type: AWS::AppSync::GraphQLApi
Properties:
AuthenticationType: AWS_IAM
Name: Radio App
GraphQLSchema:
Type: AWS::AppSync::GraphQLSchema
Properties:
ApiId: !GetAtt GraphQLApi.ApiId
Definition: |
input CreateRadioInput {
id: ID!
program_name: String
cast: [String!]
weekday: Int
time: String
favorite: Boolean
}
input DeleteRadioInput {
id: ID!
}
type Mutation {
createRadio(input: CreateRadioInput!): Radio
updateRadio(input: UpdateRadioInput!): Radio
deleteRadio(input: DeleteRadioInput!): Radio
}
type Query {
getRadio(id: ID!): Radio
listRadio(filter: TableRadioFilterInput, limit: Int, nextToken: String): RadioConnection
}
type Radio {
id: ID!
program_name: String
cast: [String!]
weekday: Int
time: String
favorite: Boolean
}
type RadioConnection {
items: [Radio]
nextToken: String
}
input TableBooleanFilterInput {
ne: Boolean
eq: Boolean
}
input TableIntFilterInput {
ne: Int
eq: Int
le: Int
lt: Int
ge: Int
gt: Int
contains: Int
notContains: Int
between: [Int]
}
input TableRadioFilterInput {
day: TableIntFilterInput
time: TableStringFilterInput
favorite: TableBooleanFilterInput
}
input TableStringFilterInput {
ne: String
eq: String
le: String
lt: String
ge: String
gt: String
contains: String
notContains: String
between: [String]
beginsWith: String
}
input UpdateRadioInput {
id: ID!
program_name: String
cast: [String!]
day: Int
time: String
favorite: Boolean
}
ResolverCreateRadio:
Type: AWS::AppSync::Resolver
Properties:
ApiId: !GetAtt GraphQLApi.ApiId
DataSourceName: !GetAtt DataSourceRadio.Name
TypeName: "Mutation"
FieldName: "createRadio"
Kind: UNIT
RequestMappingTemplate: |
{
"version": "2017-02-28",
"operation": "PutItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id)
},
"attributeValues": $util.dynamodb.toMapValuesJson($ctx.args.input),
"condition": {
"expression": "attribute_not_exists(#id)",
"expressionNames": {
"#id": "id"
}
}
}
ResponseMappingTemplate: |
$util.toJson($ctx.result)
ResolverUpdateRadio:
Type: AWS::AppSync::Resolver
Properties:
ApiId: !GetAtt GraphQLApi.ApiId
DataSourceName: !GetAtt DataSourceRadio.Name
TypeName: "Mutation"
FieldName: "updateRadio"
Kind: UNIT
RequestMappingTemplate: |
{
"version": "2017-02-28",
"operation": "UpdateItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id),
},
## Set up some space to keep track of things we're updating **
#set( $expNames = {} )
#set( $expValues = {} )
#set( $expSet = {} )
#set( $expAdd = {} )
#set( $expRemove = [] )
## Iterate through each argument, skipping keys **
#foreach( $entry in $util.map.copyAndRemoveAllKeys($ctx.args.input, ["id"]).entrySet() )
#if( $util.isNull($entry.value) )
## If the argument is set to "null", then remove that attribute from the item in DynamoDB **
#set( $discard = ${expRemove.add("#${entry.key}")} )
$!{expNames.put("#${entry.key}", "${entry.key}")}
#else
## Otherwise set (or update) the attribute on the item in DynamoDB **
$!{expSet.put("#${entry.key}", ":${entry.key}")}
$!{expNames.put("#${entry.key}", "${entry.key}")}
$!{expValues.put(":${entry.key}", $util.dynamodb.toDynamoDB($entry.value))}
#end
#end
## Start building the update expression, starting with attributes we're going to SET **
#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
## Continue building the update expression, adding attributes we're going to ADD **
#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
## Continue building the update expression, adding attributes we're going to REMOVE **
#if( !${expRemove.isEmpty()} )
#set( $expression = "${expression} REMOVE" )
#foreach( $entry in $expRemove )
#set( $expression = "${expression} ${entry}" )
#if ( $foreach.hasNext )
#set( $expression = "${expression}," )
#end
#end
#end
## Finally, write the update expression into the document, along with any expressionNames and expressionValues **
"update": {
"expression": "${expression}",
#if( !${expNames.isEmpty()} )
"expressionNames": $utils.toJson($expNames),
#end
#if( !${expValues.isEmpty()} )
"expressionValues": $utils.toJson($expValues),
#end
},
"condition": {
"expression": "attribute_exists(#id)",
"expressionNames": {
"#id": "id",
},
}
}
ResponseMappingTemplate: |
$util.toJson($ctx.result)
ResolverDeleteRadio:
Type: AWS::AppSync::Resolver
Properties:
ApiId: !GetAtt GraphQLApi.ApiId
DataSourceName: !GetAtt DataSourceRadio.Name
TypeName: "Mutation"
FieldName: "deleteRadio"
Kind: UNIT
RequestMappingTemplate: |
{
"version": "2017-02-28",
"operation": "DeleteItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id)
}
}
ResponseMappingTemplate: |
$util.toJson($ctx.result)
ResolverGetRadio:
Type: AWS::AppSync::Resolver
Properties:
ApiId: !GetAtt GraphQLApi.ApiId
DataSourceName: !GetAtt DataSourceRadio.Name
TypeName: "Query"
FieldName: "getRadio"
Kind: UNIT
RequestMappingTemplate: |
{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.id)
}
}
ResponseMappingTemplate: |
$util.toJson($ctx.result)
ResolverListRadio:
Type: AWS::AppSync::Resolver
Properties:
ApiId: !GetAtt GraphQLApi.ApiId
DataSourceName: !GetAtt DataSourceRadio.Name
TypeName: "Query"
FieldName: "listRadio"
Kind: UNIT
RequestMappingTemplate: |
{
"version": "2017-02-28",
"operation": "Scan",
"filter": #if($context.args.filter) $util.transform.toDynamoDBFilterExpression($ctx.args.filter) #else null #end,
"limit": $util.defaultIfNull($ctx.args.limit, 200),
"nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.nextToken, null)),
}
ResponseMappingTemplate: |
$util.toJson($ctx.result)