|
| 1 | +import * as AWS from 'aws-sdk'; |
| 2 | +import { ISDK } from '../aws-auth'; |
| 3 | +import { EvaluateCloudFormationTemplate } from '../evaluate-cloudformation-template'; |
| 4 | +import { ChangeHotswapImpact, ChangeHotswapResult, HotswapOperation, HotswappableChangeCandidate, lowerCaseFirstCharacter, transformObjectKeys } from './common'; |
| 5 | + |
| 6 | +export async function isHotswappableAppSyncChange( |
| 7 | + logicalId: string, change: HotswappableChangeCandidate, evaluateCfnTemplate: EvaluateCloudFormationTemplate, |
| 8 | +): Promise<ChangeHotswapResult> { |
| 9 | + const isResolver = change.newValue.Type === 'AWS::AppSync::Resolver'; |
| 10 | + const isFunction = change.newValue.Type === 'AWS::AppSync::FunctionConfiguration'; |
| 11 | + |
| 12 | + if (!isResolver && !isFunction) { |
| 13 | + return ChangeHotswapImpact.REQUIRES_FULL_DEPLOYMENT; |
| 14 | + } |
| 15 | + |
| 16 | + for (const updatedPropName in change.propertyUpdates) { |
| 17 | + if (updatedPropName !== 'RequestMappingTemplate' && updatedPropName !== 'ResponseMappingTemplate') { |
| 18 | + return ChangeHotswapImpact.REQUIRES_FULL_DEPLOYMENT; |
| 19 | + } |
| 20 | + } |
| 21 | + |
| 22 | + const resourceProperties = change.newValue.Properties; |
| 23 | + if (isResolver && resourceProperties?.Kind === 'PIPELINE') { |
| 24 | + // Pipeline resolvers can't be hotswapped as they reference |
| 25 | + // the FunctionId of the underlying functions, which can't be resolved. |
| 26 | + return ChangeHotswapImpact.REQUIRES_FULL_DEPLOYMENT; |
| 27 | + } |
| 28 | + |
| 29 | + const resourcePhysicalName = await evaluateCfnTemplate.establishResourcePhysicalName(logicalId, isFunction ? resourceProperties?.Name : undefined); |
| 30 | + if (!resourcePhysicalName) { |
| 31 | + return ChangeHotswapImpact.REQUIRES_FULL_DEPLOYMENT; |
| 32 | + } |
| 33 | + |
| 34 | + const evaluatedResourceProperties = await evaluateCfnTemplate.evaluateCfnExpression(resourceProperties); |
| 35 | + const sdkCompatibleResourceProperties = transformObjectKeys(evaluatedResourceProperties, lowerCaseFirstCharacter); |
| 36 | + |
| 37 | + if (isResolver) { |
| 38 | + // Resolver physical name is the ARN in the format: |
| 39 | + // arn:aws:appsync:us-east-1:111111111111:apis/<apiId>/types/<type>/resolvers/<field>. |
| 40 | + // We'll use `<type>.<field>` as the resolver name. |
| 41 | + const arnParts = resourcePhysicalName.split('/'); |
| 42 | + const resolverName = `${arnParts[3]}.${arnParts[5]}`; |
| 43 | + return new ResolverHotswapOperation(resolverName, sdkCompatibleResourceProperties); |
| 44 | + } else { |
| 45 | + return new FunctionHotswapOperation(resourcePhysicalName, sdkCompatibleResourceProperties); |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +class ResolverHotswapOperation implements HotswapOperation { |
| 50 | + public readonly service = 'appsync' |
| 51 | + public readonly resourceNames: string[]; |
| 52 | + |
| 53 | + constructor(resolverName: string, private readonly updateResolverRequest: AWS.AppSync.UpdateResolverRequest) { |
| 54 | + this.resourceNames = [`AppSync resolver '${resolverName}'`]; |
| 55 | + } |
| 56 | + |
| 57 | + public async apply(sdk: ISDK): Promise<any> { |
| 58 | + return sdk.appsync().updateResolver(this.updateResolverRequest).promise(); |
| 59 | + } |
| 60 | +} |
| 61 | + |
| 62 | +class FunctionHotswapOperation implements HotswapOperation { |
| 63 | + public readonly service = 'appsync' |
| 64 | + public readonly resourceNames: string[]; |
| 65 | + |
| 66 | + constructor( |
| 67 | + private readonly functionName: string, |
| 68 | + private readonly updateFunctionRequest: Omit<AWS.AppSync.UpdateFunctionRequest, 'functionId'>, |
| 69 | + ) { |
| 70 | + this.resourceNames = [`AppSync function '${functionName}'`]; |
| 71 | + } |
| 72 | + |
| 73 | + public async apply(sdk: ISDK): Promise<any> { |
| 74 | + const { functions } = await sdk.appsync().listFunctions({ apiId: this.updateFunctionRequest.apiId }).promise(); |
| 75 | + const { functionId } = functions?.find(fn => fn.name === this.functionName) ?? {}; |
| 76 | + const request = { |
| 77 | + ...this.updateFunctionRequest, |
| 78 | + functionId: functionId!, |
| 79 | + }; |
| 80 | + return sdk.appsync().updateFunction(request).promise(); |
| 81 | + } |
| 82 | +} |
0 commit comments