Skip to content

Instantly share code, notes, and snippets.

@dabit3
Last active December 20, 2022 06:38
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dabit3/c13bac2e61adcef4fd5e4d77385a9370 to your computer and use it in GitHub Desktop.
Save dabit3/c13bac2e61adcef4fd5e4d77385a9370 to your computer and use it in GitHub Desktop.
CDK Chat App CDK
import * as cdk from '@aws-cdk/core';
import { UserPool, VerificationEmailStyle, UserPoolClient, AccountRecovery } from '@aws-cdk/aws-cognito';
import { GraphQLApi, AuthorizationType, FieldLogLevel, MappingTemplate, SchemaDefinition } from '@aws-cdk/aws-appsync';
import { AttributeType, BillingMode, Table } from '@aws-cdk/aws-dynamodb';
import { Role, ServicePrincipal, Effect, PolicyStatement } from '@aws-cdk/aws-iam';
export class CdkChatStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const userPool = new UserPool(this, 'chat-app-user-pool', {
selfSignUpEnabled: true,
accountRecovery: AccountRecovery.PHONE_AND_EMAIL,
userVerification: {
emailStyle: VerificationEmailStyle.CODE
},
autoVerify: {
email: true
},
standardAttributes: {
email: {
required: true,
mutable: true
}
}
});
const userPoolClient = new UserPoolClient(this, "UserPoolClient", {
userPool
});
new cdk.CfnOutput(this, "UserPoolId", {
value: userPool.userPoolId
});
new cdk.CfnOutput(this, "UserPoolClientId", {
value: userPoolClient.userPoolClientId
});
const messageTable = new Table(this, 'CDKMessageTable', {
billingMode: BillingMode.PAY_PER_REQUEST,
partitionKey: {
name: 'id',
type: AttributeType.STRING,
},
});
const roomTable = new Table(this, 'CDKRoomTable', {
billingMode: BillingMode.PAY_PER_REQUEST,
partitionKey: {
name: 'id',
type: AttributeType.STRING,
},
});
messageTable.addGlobalSecondaryIndex({
indexName: 'messages-by-room-id',
partitionKey: {
name: 'roomId',
type: AttributeType.STRING
},
sortKey: {
name: 'createdAt',
type: AttributeType.STRING
}
})
const messageTableServiceRole = new Role(this, 'MessageTableServiceRole', {
assumedBy: new ServicePrincipal('dynamodb.amazonaws.com')
});
messageTableServiceRole.addToPolicy(
new PolicyStatement({
effect: Effect.ALLOW,
resources: [`${messageTable.tableArn}/index/messages-by-room-id`],
actions: [
'dymamodb:Query'
]
})
);
const api = new GraphQLApi(this, 'cdk-chat-app-api', {
name: "cdk-chat-app",
logConfig: {
fieldLogLevel: FieldLogLevel.ALL,
},
schemaDefinition: SchemaDefinition.FILE,
schemaDefinitionFile: './graphql/schema.graphql',
authorizationConfig: {
defaultAuthorization: {
authorizationType: AuthorizationType.USER_POOL,
userPoolConfig: {
userPool
}
},
},
});
new cdk.CfnOutput(this, "GraphQLAPIURL", {
value: api.graphQlUrl
});
const messageTableDs = api.addDynamoDbDataSource('Message', messageTable);
const roomTableDs = api.addDynamoDbDataSource('Room', roomTable);
messageTableDs.createResolver({
typeName: 'Query',
fieldName: 'listMessagesForRoom',
requestMappingTemplate: MappingTemplate.fromString(`
{
"version" : "2017-02-28",
"operation" : "Query",
"index" : "messages-by-room-id",
"query" : {
"expression": "roomId = :roomId",
"expressionValues" : {
":roomId" : $util.dynamodb.toDynamoDBJson($context.arguments.roomId)
}
}
#if( !$util.isNull($ctx.arguments.sortDirection)
&& $ctx.arguments.sortDirection == "DESC" )
,"scanIndexForward": false
#else
,"scanIndexForward": true
#end
#if($context.arguments.nextToken)
,"nextToken": "$context.arguments.nextToken"
#end
}
`),
responseMappingTemplate: MappingTemplate.fromString(`
#if( $ctx.error )
$util.error($ctx.error.message, $ctx.error.type)
#else
$util.toJson($ctx.result)
#end`
)
})
messageTableDs.createResolver({
typeName: 'Mutation',
fieldName: 'createMessage',
requestMappingTemplate: MappingTemplate.fromString(`
## Automatically set the id if it's not passed in.
$util.qr($context.args.input.put("id", $util.defaultIfNull($ctx.args.input.id, $util.autoId())))
## Automatically set the createdAt timestamp.
#set( $createdAt = $util.time.nowISO8601() )
$util.qr($context.args.input.put("createdAt", $util.defaultIfNull($ctx.args.input.createdAt, $createdAt)))
## Automatically set the user's username on owner field.
$util.qr($ctx.args.input.put("owner", $context.identity.username))
## Create a condition that will error if the id already exists
#set( $condition = {
"expression": "attribute_not_exists(#id)",
"expressionNames": {
"#id": "id"
}
} )
{
"version": "2018-05-29",
"operation": "PutItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id)
},
"attributeValues": $util.dynamodb.toMapValuesJson($context.args.input),
"condition": $util.toJson($condition)
}
`),
responseMappingTemplate: MappingTemplate.dynamoDbResultItem()
})
roomTableDs.createResolver({
typeName: 'Query',
fieldName: 'listRooms',
requestMappingTemplate: MappingTemplate.fromString(`
#set( $limit = $util.defaultIfNull($context.args.limit, 1000) )
#set( $ListRequest = {
"version": "2018-05-29",
"limit": $limit
} )
#if( $context.args.nextToken )
#set( $ListRequest.nextToken = $context.args.nextToken )
#end
$util.qr($ListRequest.put("operation", "Scan"))
$util.toJson($ListRequest)
`),
responseMappingTemplate: MappingTemplate.fromString(`
#if( $ctx.error)
$util.error($ctx.error.message, $ctx.error.type)
#else
$util.toJson($ctx.result)
#end
`)
})
roomTableDs.createResolver({
typeName: 'Mutation',
fieldName: 'createRoom',
requestMappingTemplate: MappingTemplate.fromString(`
$util.qr($context.args.input.put("id", $util.defaultIfNull($ctx.args.input.id, $util.autoId())))
{
"version": "2018-05-29",
"operation": "PutItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.input.id)
},
"attributeValues": $util.dynamodb.toMapValuesJson($context.args.input),
"condition": $util.toJson($condition)
}
`),
responseMappingTemplate: MappingTemplate.dynamoDbResultItem()
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment