diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index e562a0b267342..0290e1e3627b0 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -334,6 +334,16 @@ new route53.CnameRecord(this, `CnameApiRecord`, { }); ``` +## Log Group + +AppSync automatically create a log group with the name `/aws/appsync/apis/` upon deployment with +log data set to never expire. + +The `logConfig.retention` property can be used to set a different expiration period. + +It is possible to obtain the GraphQL API's log group as a `logs.ILogGroup` by calling the `logGroup` property of the +`GraphqlApi` construct. + ## Schema Every GraphQL Api needs a schema to define the Api. CDK offers `appsync.Schema` @@ -427,7 +437,7 @@ new appsync.GraphqlApi(this, 'api', { defaultAuthorization: { authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { - handler: authFunction, + handler: authFunction, // can also specify `resultsCacheTtl` and `validationRegex`. }, }, diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts index ab740aac4cfb2..3a3eeb6b6e41a 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts @@ -2,6 +2,7 @@ import { ICertificate } from '@aws-cdk/aws-certificatemanager'; import { IUserPool } from '@aws-cdk/aws-cognito'; import { ManagedPolicy, Role, IRole, ServicePrincipal, Grant, IGrantable } from '@aws-cdk/aws-iam'; import { IFunction } from '@aws-cdk/aws-lambda'; +import { ILogGroup, LogGroup, LogRetention, RetentionDays } from '@aws-cdk/aws-logs'; import { ArnFormat, CfnResource, Duration, Expiration, IResolvable, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnApiKey, CfnGraphQLApi, CfnGraphQLSchema, CfnDomainName, CfnDomainNameApiAssociation } from './appsync.generated'; @@ -248,6 +249,13 @@ export interface LogConfig { * @default - None */ readonly role?: IRole; + + /** + * log retention period + * + * @default - Use AppSync default + */ + readonly retention?: RetentionDays } /** @@ -459,6 +467,11 @@ export class GraphqlApi extends GraphqlApiBase { */ public readonly apiKey?: string; + /** + * the CloudWatch Log Group for this API + */ + public readonly logGroup: ILogGroup; + private schemaResource: CfnGraphQLSchema; private api: CfnGraphQLApi; private apiKeyResource?: CfnApiKey; @@ -527,6 +540,16 @@ export class GraphqlApi extends GraphqlApiBase { }); } + const logGroupName = `/aws/appsync/apis/${this.apiId}`; + + this.logGroup = LogGroup.fromLogGroupName(this, 'LogGroup', logGroupName); + + if (props.logConfig?.retention) { + new LogRetention(this, 'LogRetention', { + logGroupName: this.logGroup.logGroupName, + retention: props.logConfig.retention, + }); + }; } /** diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json index 480e781af2bbc..b0e9b8c1320d8 100644 --- a/packages/@aws-cdk/aws-appsync/package.json +++ b/packages/@aws-cdk/aws-appsync/package.json @@ -84,6 +84,7 @@ "@aws-cdk/aws-stepfunctions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", + "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", @@ -97,6 +98,7 @@ "@aws-cdk/aws-elasticsearch": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", + "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/aws-opensearchservice": "0.0.0", "@aws-cdk/aws-rds": "0.0.0", "@aws-cdk/aws-s3-assets": "0.0.0", @@ -113,6 +115,7 @@ "@aws-cdk/aws-elasticsearch": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", + "@aws-cdk/aws-logs": "0.0.0", "@aws-cdk/aws-opensearchservice": "0.0.0", "@aws-cdk/aws-rds": "0.0.0", "@aws-cdk/aws-s3-assets": "0.0.0", diff --git a/packages/@aws-cdk/aws-appsync/test/appsync.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync.test.ts index 350fbff6229db..cf2572ebf37f9 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync.test.ts @@ -2,6 +2,7 @@ import * as path from 'path'; import { Template } from '@aws-cdk/assertions'; import { Certificate } from '@aws-cdk/aws-certificatemanager'; import * as iam from '@aws-cdk/aws-iam'; +import * as logs from '@aws-cdk/aws-logs'; import * as cdk from '@aws-cdk/core'; import * as appsync from '../lib'; @@ -191,3 +192,37 @@ test('appsync GraphqlApi should be configured with custom domain when specified' DomainName: domainName, }); }); + +test('log retention should be configured with given retention time when specified', () => { + // GIVEN + const retentionTime = logs.RetentionDays.ONE_WEEK; + + // WHEN + new appsync.GraphqlApi(stack, 'log-retention', { + authorizationConfig: {}, + name: 'log-retention', + schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), + logConfig: { + retention: retentionTime, + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', { + LogGroupName: { + 'Fn::Join': [ + '', + [ + '/aws/appsync/apis/', + { + 'Fn::GetAtt': [ + 'logretentionB69DFB48', + 'ApiId', + ], + }, + ], + ], + }, + RetentionInDays: 7, + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-appsync/test/integ.log-retention.ts b/packages/@aws-cdk/aws-appsync/test/integ.log-retention.ts new file mode 100644 index 0000000000000..bb3c2431cde55 --- /dev/null +++ b/packages/@aws-cdk/aws-appsync/test/integ.log-retention.ts @@ -0,0 +1,23 @@ +import { join } from 'path'; +import { RetentionDays } from '@aws-cdk/aws-logs'; +import { App, Stack } from '@aws-cdk/core'; +import { IntegTest } from '@aws-cdk/integ-tests'; +import { GraphqlApi, LogConfig, Schema } from '../lib'; + +const app = new App(); +const stack = new Stack(app, 'AppSyncIntegLogRetention'); + +const logConfig: LogConfig = { + retention: RetentionDays.ONE_WEEK, +}; + +new GraphqlApi(stack, 'GraphqlApi', { + authorizationConfig: {}, + name: 'IntegLogRetention', + schema: Schema.fromAsset(join(__dirname, 'appsync.test.graphql')), + logConfig, +}); + +new IntegTest(app, 'Integ', { testCases: [stack] }); + +app.synth(); \ No newline at end of file