Skip to content

Instantly share code, notes, and snippets.

@ffos
Forked from adrianhall/AppSync-Example.yaml
Created October 27, 2018 06:44
Show Gist options
  • Save ffos/04a8d75e39a894948c1dfaf87aa8e7eb to your computer and use it in GitHub Desktop.
Save ffos/04a8d75e39a894948c1dfaf87aa8e7eb to your computer and use it in GitHub Desktop.

Revisions

  1. @adrianhall adrianhall created this gist Apr 13, 2018.
    497 changes: 497 additions & 0 deletions AppSync-Example.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,497 @@
    ---
    Description: AWSAppSync DynamoDB Example
    Resources:
    GraphQLApi:
    Type: "AWS::AppSync::GraphQLApi"
    Properties:
    Name: AWSAppSync DynamoDB Example
    AuthenticationType: AWS_IAM

    PostDynamoDBTableDataSource:
    Type: "AWS::AppSync::DataSource"
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    Name: PostDynamoDBTable
    Description: The Post DynamoDB table in us-west-2
    Type: AMAZON_DYNAMODB
    ServiceRoleArn: "arn:aws:iam::YOUR_ACCOUNT_ID:role/AppSyncTutorialAmazonDynamoDBRole"
    DynamoDBConfig:
    AwsRegion: "us-west-2"
    TableName: "AppSyncTutorial-Post"

    QueryGetPostResolver:
    Type: "AWS::AppSync::Resolver"
    DependsOn: Schema
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    TypeName: Query
    FieldName: getPost
    DataSourceName: !GetAtt PostDynamoDBTableDataSource.Name
    RequestMappingTemplate: |
    {
    "version" : "2017-02-28",
    "operation" : "GetItem",
    "key" : {
    "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
    }
    }
    ResponseMappingTemplate: "$utils.toJson($ctx.result)"

    QueryAllPostResolver:
    Type: "AWS::AppSync::Resolver"
    DependsOn: Schema
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    TypeName: Query
    FieldName: allPost
    DataSourceName: !GetAtt PostDynamoDBTableDataSource.Name
    RequestMappingTemplate: |
    {
    "version" : "2017-02-28",
    "operation" : "Scan",
    #if( $ctx.args.count )
    "limit": $ctx.args.count,
    #end
    #if( ${ctx.args.nextToken} )
    "nextToken": "${ctx.args.nextToken}"
    #end
    }
    ResponseMappingTemplate: |
    {
    "posts": $utils.toJson($ctx.result.items),
    #if( ${ctx.result.nextToken} )
    "nextToken": "${ctx.result.nextToken}",
    #end
    }
    QueryAllPostsByAuthorResolver:
    Type: "AWS::AppSync::Resolver"
    DependsOn: Schema
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    TypeName: Query
    FieldName: allPostsByAuthor
    DataSourceName: !GetAtt PostDynamoDBTableDataSource.Name
    RequestMappingTemplate: |
    {
    "version" : "2017-02-28",
    "operation" : "Query",
    "index" : "author-index",
    "query" : {
    "expression": "author = :author",
    "expressionValues" : {
    ":author" : $util.dynamodb.toDynamoDBJson($ctx.args.author)
    }
    },
    #if( $ctx.args.count )
    "limit": $ctx.args.count,
    #end
    #if( ${ctx.args.nextToken} )
    "nextToken": "${ctx.args.nextToken}",
    #end
    }
    ResponseMappingTemplate: |
    {
    "posts": $utils.toJson($ctx.result.items),
    #if( ${ctx.result.nextToken} )
    "nextToken": "${ctx.result.nextToken}",
    #end
    }
    QueryAllPostsByTagResolver:
    Type: "AWS::AppSync::Resolver"
    DependsOn: Schema
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    TypeName: Query
    FieldName: allPostsByTag
    DataSourceName: !GetAtt PostDynamoDBTableDataSource.Name
    RequestMappingTemplate: |
    {
    "version" : "2017-02-28",
    "operation" : "Scan",
    "filter": {
    "expression": "contains (tags, :tag)",
    "expressionValues": {
    ":tag": $util.dynamodb.toStringJson($ctx.args.tag)
    }
    },
    #if( $ctx.args.count )
    "limit": $ctx.args.count,
    #end
    #if( ${ctx.args.nextToken} )
    "nextToken": "${ctx.args.nextToken}"
    #end
    }
    ResponseMappingTemplate: |
    {
    "posts": $utils.toJson($ctx.result.items),
    #if( ${ctx.result.nextToken} )
    "nextToken": "${ctx.result.nextToken}",
    #end
    }
    MutationaddPostResolver:
    Type: "AWS::AppSync::Resolver"
    DependsOn: Schema
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    TypeName: Mutation
    FieldName: addPost
    DataSourceName: !GetAtt PostDynamoDBTableDataSource.Name
    RequestMappingTemplate: |
    #set( $d = $util.dynamodb )
    #set( $values = $d.toMapValues($ctx.args))
    $!{values.put("ups", $d.toNumber(1))}
    $!{values.put("downs", $d.toNumber(0))}
    $!{values.put("version", $d.toNumber(1))}
    $!{values.put("created", $d.toDynamoDB($util.time.nowISO8601()))}
    $!{values.put("lastUpdated", $values.get("created"))}
    {
    "version" : "2017-02-28",
    "operation" : "PutItem",
    "key" : {
    "id" : $d.toStringJson($utils.autoId())
    },
    "attributeValues" : $util.toJson($values),
    }
    ResponseMappingTemplate: "$util.toJson($ctx.result)"

    MutationAddCommentResolver:
    Type: "AWS::AppSync::Resolver"
    DependsOn: Schema
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    TypeName: Mutation
    FieldName: addComment
    DataSourceName: !GetAtt PostDynamoDBTableDataSource.Name
    RequestMappingTemplate: |
    {
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
    "id" : $util.dynamodb.toStringJson($ctx.args.id)
    },
    "update" : {
    "expression" : "SET comments = list_append(if_not_exists(comments, :emptyList), :newComment), lastUpdated = :lastUpdated ADD version :plusOne",
    "expressionValues" : {
    ":emptyList": $util.dynamodb.toListJson([]),
    ":newComment" : $util.dynamodb.toListJson([$util.map.copyAndRetainAllKeys($ctx.args, ["author","comment"])]),
    ":plusOne" : $util.dynamodb.toNumberJson(1),
    ":lastUpdated" : $util.dynamodb.toDynamoDBJson($util.time.nowISO8601())
    }
    },
    "condition" : {
    "expression" : "attribute_exists(id)"
    },
    }
    ResponseMappingTemplate: "$util.toJson($ctx.result)"

    MutationAddTagResolver:
    Type: "AWS::AppSync::Resolver"
    DependsOn: Schema
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    TypeName: Mutation
    FieldName: addTag
    DataSourceName: !GetAtt PostDynamoDBTableDataSource.Name
    RequestMappingTemplate: |
    {
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
    "id" : $util.dynamodb.toStringJson($ctx.args.id)
    },
    "update" : {
    "expression" : "ADD tags :tags, version :plusOne SET lastUpdated = :lastUpdated",
    "expressionValues" : {
    ":tags" : $util.dynamodb.toStringSetJson([$ctx.args.tag]),
    ":plusOne" : $util.dynamodb.toNumberJson(1),
    ":lastUpdated" : $util.dynamodb.toDynamoDBJson($util.time.nowISO8601()),
    }
    },
    "condition" : {
    "expression" : "attribute_exists(id)"
    },
    }
    ResponseMappingTemplate: "$util.toJson($ctx.result)"

    MutationRemoveTagResolver:
    Type: "AWS::AppSync::Resolver"
    DependsOn: Schema
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    TypeName: Mutation
    FieldName: removeTag
    DataSourceName: !GetAtt PostDynamoDBTableDataSource.Name
    RequestMappingTemplate: |
    {
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
    "id" : $util.dynamodb.toStringJson($ctx.args.id)
    },
    "update" : {
    "expression" : "DELETE tags :tags ADD version :plusOne SET lastUpdated = :lastUpdated",
    "expressionValues" : {
    ":tags" : $util.dynamodb.toStringSetJson([$ctx.args.tag]),
    ":plusOne" : $util.dynamodb.toNumberJson(1),
    ":lastUpdated" : $util.dynamodb.toDynamoDBJson($util.time.nowISO8601()),
    }
    },
    "condition" : {
    "expression" : "attribute_exists(id)"
    },
    }
    ResponseMappingTemplate: "$util.toJson($ctx.result)"

    MutationDeletePostResolver:
    Type: "AWS::AppSync::Resolver"
    DependsOn: Schema
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    TypeName: Mutation
    FieldName: deletePost
    DataSourceName: !GetAtt PostDynamoDBTableDataSource.Name
    RequestMappingTemplate: |
    {
    "version" : "2017-02-28",
    "operation" : "DeleteItem",
    "key": {
    "id": $util.dynamodb.toStringJson($ctx.args.id)
    },
    #if( $ctx.args.containsKey("expectedVersion") )
    "condition" : {
    "expression" : "attribute_not_exists(id) OR version = :expectedVersion",
    "expressionValues" : {
    ":expectedVersion" : $util.dynamodb.toNumberJson($ctx.args.expectedVersion)
    }
    },
    #end
    }
    ResponseMappingTemplate: "$util.toJson($ctx.result)"

    MutationUpvotePostResolver:
    Type: "AWS::AppSync::Resolver"
    DependsOn: Schema
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    TypeName: Mutation
    FieldName: upvotePost
    DataSourceName: !GetAtt PostDynamoDBTableDataSource.Name
    RequestMappingTemplate: |
    {
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
    "id" : $util.dynamodb.toStringJson($ctx.args.id)
    },
    "update" : {
    "expression" : "ADD ups :plusOne, version :plusOne SET lastUpdated = :lastUpdated",
    "expressionValues" : {
    ":plusOne" : $util.dynamodb.toNumberJson(1),
    ":lastUpdated" : $util.dynamodb.toDynamoDBJson($util.time.nowISO8601())
    }
    },
    "condition" : {
    "expression" : "attribute_exists(id)"
    },
    }
    ResponseMappingTemplate: "$util.toJson($ctx.result)"

    MutationDownvotePostResolver:
    Type: "AWS::AppSync::Resolver"
    DependsOn: Schema
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    TypeName: Mutation
    FieldName: downvotePost
    DataSourceName: !GetAtt PostDynamoDBTableDataSource.Name
    RequestMappingTemplate: |
    {
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
    "id" : $util.dynamodb.toStringJson($ctx.args.id)
    },
    "update" : {
    "expression" : "ADD downs :plusOne, version :plusOne SET lastUpdated = :lastUpdated",
    "expressionValues" : {
    ":plusOne" : $util.dynamodb.toNumberJson(1),
    ":lastUpdated" : $util.dynamodb.toDynamoDBJson($util.time.nowISO8601()),
    }
    },
    "condition" : {
    "expression" : "attribute_exists(id)"
    },
    }
    ResponseMappingTemplate: "$util.toJson($ctx.result)"

    MutationUpdatePostResolver:
    Type: "AWS::AppSync::Resolver"
    DependsOn: Schema
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    TypeName: Mutation
    FieldName: updatePost
    DataSourceName: !GetAtt PostDynamoDBTableDataSource.Name
    RequestMappingTemplate: |
    #set( $ddb = $util.dynamodb )
    {
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
    "id" : $ddb.toDynamoDBJson($ctx.args.id)
    },
    ## Set up some space to keep track of things we're updating **
    #set( $expNames = {} )
    #set( $expValues = {} )
    #set( $expSet = {} )
    #set( $expAdd = {} )
    #set( $expRemove = [] )
    ## Increment "version" by 1 **
    $!{expAdd.put("version", ":one")}
    $!{expValues.put(":one", $ddb.toDynamoDB(1))}
    ## Set the "lastUpdated" timestamp **
    $!{expSet.put("lastUpdated", ":lastUpdated")}
    $!{expValues.put(":lastUpdated", $ddb.toDynamoDB($util.time.nowISO8601()))}
    ## Iterate through each argument, skipping "id" and "expectedVersion" **
    #foreach( $entry in $util.map.copyAndRemoveAllKeys($ctx.args, ["id","expectedVersion"]).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}", $ddb.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) and version = :expectedVersion",
    "expressionValues" : {
    ":expectedVersion" : $ddb.toDynamoDBJson($context.arguments.expectedVersion)
    }
    }
    }
    ResponseMappingTemplate: "$util.toJson($ctx.result)"

    Schema:
    Type: "AWS::AppSync::GraphQLSchema"
    Properties:
    ApiId: !GetAtt GraphQLApi.ApiId
    Definition: |
    type Comment {
    author: String!
    comment: String!
    }
    type Mutation {
    addComment(id: ID!, author: String!, comment: String!): Post
    addTag(id: ID!, tag: String!): Post
    removeTag(id: ID!, tag: String!): Post
    deletePost(id: ID!, expectedVersion: Int): Post
    upvotePost(id: ID!): Post
    downvotePost(id: ID!): Post
    updatePost(
    id: ID!,
    author: String,
    title: String,
    content: String,
    url: String,
    expectedVersion: Int!
    ): Post
    addPost(
    author: String!,
    title: String!,
    content: String!,
    url: String!
    ): Post!
    }
    type PaginatedPosts {
    posts: [Post!]!
    nextToken: String
    }
    type Post {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
    tags: [String!]
    comments: [Comment!]
    created: String
    lastUpdated: String
    }
    type Query {
    allPostsByTag(tag: String!, count: Int, nextToken: String): PaginatedPosts!
    allPostsByAuthor(author: String!, count: Int, nextToken: String): PaginatedPosts!
    allPost(count: Int, nextToken: String): PaginatedPosts!
    getPost(id: ID!): Post
    }
    schema {
    query: Query
    mutation: Mutation
    }
    Outputs:
    GraphQLApiARN:
    Description: The App ID of the GraphQL endpoint.
    Value: !Ref GraphQLApi
    GraphQLApiId:
    Description: The App ID of the GraphQL endpoint.
    Value: !GetAtt GraphQLApi.ApiId
    GraphQLApiEndpoint:
    Description: The URL for the GraphQL endpoint.
    Value: !GetAtt GraphQLApi.GraphQLUrl
    PostDynamoDBTableDataSourceARN:
    Description: The ARN for the Post DynamoDB table DataSource.
    Value: !Ref PostDynamoDBTableDataSource
    PostDynamoDBTableDataSourceName:
    Description: The ARN for the Post DynamoDB table DataSource.
    Value: !GetAtt PostDynamoDBTableDataSource.Name