diff --git a/packages/aws-cdk/lib/api/hotswap/appsync-mapping-templates.ts b/packages/aws-cdk/lib/api/hotswap/appsync-mapping-templates.ts index 7d85525e0e50f..1240490b0a8ad 100644 --- a/packages/aws-cdk/lib/api/hotswap/appsync-mapping-templates.ts +++ b/packages/aws-cdk/lib/api/hotswap/appsync-mapping-templates.ts @@ -22,6 +22,8 @@ export async function isHotswappableAppSyncChange( 'RequestMappingTemplateS3Location', 'ResponseMappingTemplate', 'ResponseMappingTemplateS3Location', + 'Code', + 'CodeS3Location', 'Definition', 'DefinitionS3Location', ]); @@ -56,6 +58,8 @@ export async function isHotswappableAppSyncChange( requestMappingTemplateS3Location: change.newValue.Properties?.RequestMappingTemplateS3Location, responseMappingTemplate: change.newValue.Properties?.ResponseMappingTemplate, responseMappingTemplateS3Location: change.newValue.Properties?.ResponseMappingTemplateS3Location, + code: change.newValue.Properties?.Code, + codeS3Location: change.newValue.Properties?.CodeS3Location, }; const evaluatedResourceProperties = await evaluateCfnTemplate.evaluateCfnExpression(sdkProperties); const sdkRequestObject = transformObjectKeys(evaluatedResourceProperties, lowerCaseFirstCharacter); @@ -73,6 +77,10 @@ export async function isHotswappableAppSyncChange( sdkRequestObject.definition = await fetchFileFromS3(sdkRequestObject.definitionS3Location, sdk); delete sdkRequestObject.definitionS3Location; } + if (sdkRequestObject.codeS3Location) { + sdkRequestObject.code = await fetchFileFromS3(sdkRequestObject.codeS3Location, sdk); + delete sdkRequestObject.codeS3Location; + } if (isResolver) { await sdk.appsync().updateResolver(sdkRequestObject).promise(); diff --git a/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts b/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts index 5264b2662751f..4882859656a47 100644 --- a/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts +++ b/packages/aws-cdk/test/api/hotswap/appsync-mapping-templates-hotswap-deployments.test.ts @@ -193,6 +193,141 @@ describe.each([HotswapMode.FALL_BACK, HotswapMode.HOTSWAP_ONLY])('%p mode', (hot }); }); + test('calls the updateResolver() API when it receives only a code s3 location in a Pipeline Resolver', async () => { + // GIVEN + mockS3GetObject = jest.fn().mockImplementation(async () => { + return { Body: 'code defined in s3' }; + }); + hotswapMockSdkProvider.stubS3({ getObject: mockS3GetObject }); + setup.setCurrentCfnStackTemplate({ + Resources: { + AppSyncResolver: { + Type: 'AWS::AppSync::Resolver', + Properties: { + ApiId: 'apiId', + FieldName: 'myField', + TypeName: 'Query', + DataSourceName: 'my-datasource', + PipelineConfig: ['function1'], + CodeS3Location: 's3://test-bucket/old_location', + }, + Metadata: { + 'aws:asset:path': 'old-path', + }, + }, + }, + }); + setup.pushStackResourceSummaries( + setup.stackSummaryOf( + 'AppSyncResolver', + 'AWS::AppSync::Resolver', + 'arn:aws:appsync:us-east-1:111111111111:apis/apiId/types/Query/resolvers/myField', + ), + ); + const cdkStackArtifact = setup.cdkStackArtifactOf({ + template: { + Resources: { + AppSyncResolver: { + Type: 'AWS::AppSync::Resolver', + Properties: { + ApiId: 'apiId', + FieldName: 'myField', + TypeName: 'Query', + DataSourceName: 'my-datasource', + PipelineConfig: ['function1'], + CodeS3Location: 's3://test-bucket/path/to/key', + }, + Metadata: { + 'aws:asset:path': 'new-path', + }, + }, + }, + }, + }); + + // WHEN + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + + // THEN + expect(deployStackResult).not.toBeUndefined(); + expect(mockUpdateResolver).toHaveBeenCalledWith({ + apiId: 'apiId', + dataSourceName: 'my-datasource', + typeName: 'Query', + fieldName: 'myField', + pipelineConfig: ['function1'], + code: 'code defined in s3', + }); + expect(mockS3GetObject).toHaveBeenCalledWith({ + Bucket: 'test-bucket', + Key: 'path/to/key', + }); + }); + + test('calls the updateResolver() API when it receives only a code difference in a Pipeline Resolver', async () => { + // GIVEN + hotswapMockSdkProvider.stubS3({ getObject: mockS3GetObject }); + setup.setCurrentCfnStackTemplate({ + Resources: { + AppSyncResolver: { + Type: 'AWS::AppSync::Resolver', + Properties: { + ApiId: 'apiId', + FieldName: 'myField', + TypeName: 'Query', + DataSourceName: 'my-datasource', + PipelineConfig: ['function1'], + Code: 'old code', + }, + Metadata: { + 'aws:asset:path': 'old-path', + }, + }, + }, + }); + setup.pushStackResourceSummaries( + setup.stackSummaryOf( + 'AppSyncResolver', + 'AWS::AppSync::Resolver', + 'arn:aws:appsync:us-east-1:111111111111:apis/apiId/types/Query/resolvers/myField', + ), + ); + const cdkStackArtifact = setup.cdkStackArtifactOf({ + template: { + Resources: { + AppSyncResolver: { + Type: 'AWS::AppSync::Resolver', + Properties: { + ApiId: 'apiId', + FieldName: 'myField', + TypeName: 'Query', + DataSourceName: 'my-datasource', + PipelineConfig: ['function1'], + Code: 'new code', + }, + Metadata: { + 'aws:asset:path': 'new-path', + }, + }, + }, + }, + }); + + // WHEN + const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact); + + // THEN + expect(deployStackResult).not.toBeUndefined(); + expect(mockUpdateResolver).toHaveBeenCalledWith({ + apiId: 'apiId', + dataSourceName: 'my-datasource', + typeName: 'Query', + fieldName: 'myField', + pipelineConfig: ['function1'], + code: 'new code', + }); + }); + test('calls the updateResolver() API when it receives only a mapping template difference in a Pipeline Resolver', async () => { // GIVEN setup.setCurrentCfnStackTemplate({