From e2cb670f98ee28eb3afc6cb8ce1cc1e4c4218bb2 Mon Sep 17 00:00:00 2001 From: jhockett Date: Sun, 10 Jan 2021 10:55:08 -0800 Subject: [PATCH 1/5] fix(amplify-category-function): use ref for S3Bucket and S3Key in CFN --- ...-function-cloudformation-template.json.ejs | 38 ++++--- .../awscloudformation/utils/storeResources.ts | 100 +++++++----------- packages/amplify-cli-core/src/index.ts | 8 +- .../extensions/amplify-helpers/copy-batch.ts | 22 ++-- .../amplify-helpers/leave-breadcrumbs.ts | 12 +-- .../amplify-helpers/read-breadcrumbs.ts | 11 +- .../src/push-resources.ts | 43 ++++---- 7 files changed, 108 insertions(+), 126 deletions(-) diff --git a/packages/amplify-category-function/resources/awscloudformation/cloudformation-templates/lambda-function-cloudformation-template.json.ejs b/packages/amplify-category-function/resources/awscloudformation/cloudformation-templates/lambda-function-cloudformation-template.json.ejs index f123dcc7235..9fcbae75336 100644 --- a/packages/amplify-category-function/resources/awscloudformation/cloudformation-templates/lambda-function-cloudformation-template.json.ejs +++ b/packages/amplify-category-function/resources/awscloudformation/cloudformation-templates/lambda-function-cloudformation-template.json.ejs @@ -7,8 +7,14 @@ "Default" : "NONE", "Description" : " Schedule Expression" }, + "deploymentBucketName": { + "Type": "String" + }, "env": { "Type": "String" + }, + "s3Key": { + "Type": "String" }<%if (props.dependsOn && props.dependsOn.length > 0) { %>,<% } %> <% if (props.dependsOn) { %> <% for(var i=0; i < props.dependsOn.length; i++) { %> @@ -45,6 +51,14 @@ "aws:asset:property": "Code" }, "Properties": { + "Code": { + "S3Bucket": { + "Ref": "deploymentBucketName" + }, + "S3Key": { + "Ref": "s3Key" + } + }, "Handler": "<%= props.functionTemplate.handler? props.functionTemplate.handler : props.runtime.defaultHandler %>", "FunctionName": { "Fn::If": [ @@ -68,7 +82,7 @@ "Environment": { "Variables" : <%- JSON.stringify(props.environmentMap) %> }, - "Role": { "Fn::GetAtt" : ["LambdaExecutionRole", "Arn"] }, + "Role": { "Fn::GetAtt": ["LambdaExecutionRole", "Arn"] }, "Runtime": "<%= props.runtime.cloudTemplateValue %>", "Layers": <%- JSON.stringify(props.lambdaLayersCFNArray) %>, "Timeout": "25" @@ -125,10 +139,10 @@ "Statement": [ { "Effect": "Allow", - "Action":["logs:CreateLogGroup", + "Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"], - "Resource": { "Fn::Sub" : [ "arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*", { "region": {"Ref": "AWS::Region"}, "account": {"Ref": "AWS::AccountId"}, "lambda": {"Ref": "LambdaFunction"}} ]} + "Resource": { "Fn::Sub": [ "arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*", { "region": {"Ref": "AWS::Region"}, "account": {"Ref": "AWS::AccountId"}, "lambda": {"Ref": "LambdaFunction"}} ]} }<% if (props.functionTemplate.parameters && props.functionTemplate.parameters.database && props.functionTemplate.parameters.database.resourceName) { %>, { "Effect": "Allow", @@ -214,17 +228,17 @@ <% } %> <% if (props.cloudwatchRule && props.cloudwatchRule != "NONE") { %> ,"CloudWatchEvent": { - "Type" : "AWS::Events::Rule", - "Properties":{ - "Description" : "Schedule rule for Lambda", - "ScheduleExpression" : { - "Ref" : "CloudWatchRule" + "Type": "AWS::Events::Rule", + "Properties": { + "Description": "Schedule rule for Lambda", + "ScheduleExpression": { + "Ref": "CloudWatchRule" }, "State": "ENABLED", "Targets": [{ "Arn": { "Fn::GetAtt": ["LambdaFunction", "Arn"] }, - "Id":{ - "Ref" : "LambdaFunction" + "Id": { + "Ref": "LambdaFunction" } }] } @@ -263,8 +277,8 @@ } <% if (props.cloudwatchRule && props.cloudwatchRule != "NONE") { %> ,"CloudWatchEventRule": { - "Value" :{ - "Ref" : "CloudWatchEvent" + "Value": { + "Ref": "CloudWatchEvent" } } <% } %> diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts index feff6f3e1f7..6e63025066e 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts @@ -1,4 +1,4 @@ -import { JSONUtilities } from 'amplify-cli-core'; +import { JSONUtilities, pathManager, stateManager, $TSAny, $TSContext, $TSObject } from 'amplify-cli-core'; import { FunctionParameters, FunctionTriggerParameters, FunctionBreadcrumbs } from 'amplify-function-plugin-interface'; import _ from 'lodash'; import fs from 'fs-extra'; @@ -12,7 +12,7 @@ import { saveLayerRuntimes } from './layerRuntimes'; // handling both FunctionParameters and FunctionTriggerParameters here is a hack // ideally we refactor the auth trigger flows to use FunctionParameters directly and get rid of FunctionTriggerParameters altogether -export function createFunctionResources(context: any, parameters: FunctionParameters | FunctionTriggerParameters) { +export function createFunctionResources(context: $TSContext, parameters: FunctionParameters | FunctionTriggerParameters) { context.amplify.updateamplifyMetaAfterResourceAdd( categoryName, parameters.resourceName || parameters.functionName, @@ -26,10 +26,10 @@ export function createFunctionResources(context: any, parameters: FunctionParame context.amplify.leaveBreadcrumbs(context, categoryName, parameters.resourceName, createBreadcrumbs(parameters)); } -export const createLayerArtifacts = (context, parameters: LayerParameters, latestVersion: number = 1): string => { - const layerDirPath = ensureLayerFolders(context, parameters); +export const createLayerArtifacts = (context: $TSContext, parameters: LayerParameters, latestVersion: number = 1): string => { + const layerDirPath = ensureLayerFolders(parameters); updateLayerState(context, parameters, layerDirPath); - createParametersFile(context, { layerVersion: latestVersion }, parameters.layerName, parametersFileName); + createParametersFile({ layerVersion: latestVersion }, parameters.layerName, parametersFileName); createLayerCfnFile(context, parameters, layerDirPath); addLayerToAmplifyMeta(context, parameters); return layerDirPath; @@ -42,19 +42,19 @@ const defaultOpts = { amplifyMeta: true, }; export const updateLayerArtifacts = ( - context, + context: $TSContext, parameters: LayerParameters, latestVersion?: number, options: Partial = {}, ): string => { options = _.assign(defaultOpts, options); - const layerDirPath = ensureLayerFolders(context, parameters); + const layerDirPath = ensureLayerFolders(parameters); if (options.layerParams) { updateLayerState(context, parameters, layerDirPath); } if (options.cfnFile) { if (latestVersion !== undefined) { - createParametersFile(context, { layerVersion: latestVersion }, parameters.layerName, parametersFileName); + createParametersFile({ layerVersion: latestVersion }, parameters.layerName, parametersFileName); } updateLayerCfnFile(context, parameters, layerDirPath); } @@ -64,7 +64,7 @@ export const updateLayerArtifacts = ( return layerDirPath; }; -export function removeLayerArtifacts(context, layerName) { +export function removeLayerArtifacts(context: $TSContext, layerName: string) { if (isMultiEnvLayer(context, layerName)) { removeLayerFromTeamProviderInfo(context, layerName); } @@ -72,22 +72,17 @@ export function removeLayerArtifacts(context, layerName) { // ideally function update should be refactored so this function does not need to be exported export function saveMutableState( - context, + context: $TSContext, parameters: | Partial> | FunctionTriggerParameters, ) { - createParametersFile( - context, - buildParametersFileObj(parameters), - parameters.resourceName || parameters.functionName, - functionParametersFileName, - ); + createParametersFile(buildParametersFileObj(parameters), parameters.resourceName || parameters.functionName, functionParametersFileName); } // ideally function update should be refactored so this function does not need to be exported export function saveCFNParameters( - context, + context: $TSContext, parameters: Partial> | FunctionTriggerParameters, ) { if ('trigger' in parameters) { @@ -95,28 +90,28 @@ export function saveCFNParameters( modules: parameters.modules.join(), resourceName: parameters.resourceName, }; - createParametersFile(context, params, parameters.resourceName, parametersFileName); + createParametersFile(params, parameters.resourceName, parametersFileName); } if ('cloudwatchRule' in parameters) { const params = { CloudWatchRule: parameters.cloudwatchRule, }; - createParametersFile(context, params, parameters.resourceName, parametersFileName); + createParametersFile(params, parameters.resourceName, parametersFileName); } } -function updateLayerState(context: any, parameters: LayerParameters, layerDirPath: string) { +function updateLayerState(context: $TSContext, parameters: LayerParameters, layerDirPath: string) { if (isMultiEnvLayer(context, parameters.layerName)) { updateLayerTeamProviderInfo(context, parameters, layerDirPath); saveLayerRuntimes(layerDirPath, parameters.layerName, parameters.runtimes); } else { - createLayerParametersFile(context, parameters, layerDirPath, isMultiEnvLayer(context, parameters.layerName)); + createLayerParametersFile(parameters, layerDirPath, isMultiEnvLayer(context, parameters.layerName)); } } -function copyTemplateFiles(context: any, parameters: FunctionParameters | FunctionTriggerParameters) { +function copyTemplateFiles(context: $TSContext, parameters: FunctionParameters | FunctionTriggerParameters) { // copy function template files - const destDir = context.amplify.pathManager.getBackendDirPath(); + const destDir = pathManager.getBackendDirPath(); const copyJobs = parameters.functionTemplate.sourceFiles.map(file => { return { dir: parameters.functionTemplate.sourceRoot, @@ -131,7 +126,7 @@ function copyTemplateFiles(context: any, parameters: FunctionParameters | Functi }); // this is a hack to reuse some old code - let templateParams: any = parameters; + let templateParams: $TSAny = parameters; if ('trigger' in parameters) { let triggerEnvs = context.amplify.loadEnvResourceParameters(context, 'function', parameters.resourceName); parameters.triggerEnvs = JSON.parse(parameters.triggerEnvs) || []; @@ -154,7 +149,7 @@ function copyTemplateFiles(context: any, parameters: FunctionParameters | Functi target: path.join(destDir, categoryName, parameters.resourceName, `${parameters.resourceName}-cloudformation-template.json`), }; - const copyJobParams: any = parameters; + const copyJobParams: $TSAny = parameters; if ('lambdaLayers' in parameters) { const layerCFNValues = convertLambdaLayerMetaToLayerCFNArray(context, parameters.lambdaLayers, context.amplify.getEnvInfo().envName); copyJobParams.lambdaLayersCFNArray = layerCFNValues; @@ -162,8 +157,8 @@ function copyTemplateFiles(context: any, parameters: FunctionParameters | Functi context.amplify.copyBatch(context, [cloudTemplateJob], copyJobParams, false); } -function ensureLayerFolders(context, parameters) { - const projectBackendDirPath = context.amplify.pathManager.getBackendDirPath(); +function ensureLayerFolders(parameters: $TSAny) { + const projectBackendDirPath = pathManager.getBackendDirPath(); const layerDirPath = path.join(projectBackendDirPath, categoryName, parameters.layerName); fs.ensureDirSync(path.join(layerDirPath, 'opt')); parameters.runtimes.forEach(runtime => ensureLayerRuntimeFolder(layerDirPath, runtime)); @@ -171,7 +166,7 @@ function ensureLayerFolders(context, parameters) { } // Default files are only created if the path does not exist -function ensureLayerRuntimeFolder(layerDirPath: string, runtime) { +function ensureLayerRuntimeFolder(layerDirPath: string, runtime: $TSAny) { const runtimeDirPath = path.join(layerDirPath, 'lib', runtime.layerExecutablePath); if (!fs.pathExistsSync(runtimeDirPath)) { fs.ensureDirSync(runtimeDirPath); @@ -182,27 +177,27 @@ function ensureLayerRuntimeFolder(layerDirPath: string, runtime) { } } -function createLayerCfnFile(context, parameters: LayerParameters, layerDirPath: string) { +function createLayerCfnFile(context: $TSContext, parameters: LayerParameters, layerDirPath: string) { JSONUtilities.writeJson( path.join(layerDirPath, parameters.layerName + '-awscloudformation-template.json'), generateLayerCfnObj(context, parameters), ); } -function updateLayerCfnFile(context, parameters: LayerParameters, layerDirPath: string) { +function updateLayerCfnFile(context: $TSContext, parameters: LayerParameters, layerDirPath: string) { JSONUtilities.writeJson( path.join(layerDirPath, parameters.layerName + '-awscloudformation-template.json'), generateLayerCfnObj(context, parameters), ); } -const writeParametersToAmplifyMeta = (context, layerName: string, parameters) => { +const writeParametersToAmplifyMeta = (context: $TSContext, layerName: string, parameters) => { const amplifyMeta = context.amplify.getProjectMeta(); _.set(amplifyMeta, ['function', layerName], parameters); - JSONUtilities.writeJson(context.amplify.pathManager.getAmplifyMetaFilePath(), amplifyMeta); + JSONUtilities.writeJson(pathManager.getAmplifyMetaFilePath(), amplifyMeta); }; -const addLayerToAmplifyMeta = (context, parameters: LayerParameters) => { +const addLayerToAmplifyMeta = (context: $TSContext, parameters: LayerParameters) => { context.amplify.updateamplifyMetaAfterResourceAdd(categoryName, parameters.layerName, amplifyMetaAndBackendParams(parameters)); writeParametersToAmplifyMeta( context, @@ -211,7 +206,7 @@ const addLayerToAmplifyMeta = (context, parameters: LayerParameters) => { ); }; -const updateLayerInAmplifyMeta = (context, parameters: LayerParameters) => { +const updateLayerInAmplifyMeta = (context: $TSContext, parameters: LayerParameters) => { writeParametersToAmplifyMeta( context, parameters.layerName, @@ -219,41 +214,28 @@ const updateLayerInAmplifyMeta = (context, parameters: LayerParameters) => { ); }; -const createLayerParametersFile = ( - context, - parameters: LayerParameters | StoredLayerParameters, - layerDirPath: string, - isMultiEnv: boolean, -) => { +const createLayerParametersFile = (parameters: LayerParameters | StoredLayerParameters, layerDirPath: string, isMultiEnv: boolean) => { fs.ensureDirSync(layerDirPath); const parametersFilePath = path.join(layerDirPath, layerParametersFileName); JSONUtilities.writeJson(parametersFilePath, layerParamsToStoredParams(parameters, isMultiEnv)); }; -const updateLayerTeamProviderInfo = (context, parameters: LayerParameters, layerDirPath: string) => { +const updateLayerTeamProviderInfo = (context: $TSContext, parameters: LayerParameters, layerDirPath: string) => { fs.ensureDirSync(layerDirPath); - const teamProviderInfoPath = context.amplify.pathManager.getProviderInfoFilePath(); const { envName } = context.amplify.getEnvInfo(); - if (!fs.existsSync(teamProviderInfoPath)) { - throw new Error(`${teamProviderInfoPath} not found.`); - } - const teamProviderInfo = context.amplify.readJsonFile(teamProviderInfoPath); + const teamProviderInfo = stateManager.getTeamProviderInfo(); _.set( teamProviderInfo, [envName, 'nonCFNdata', categoryName, parameters.layerName], layerParamsToStoredParams(parameters, isMultiEnvLayer(context, parameters.layerName)), ); - JSONUtilities.writeJson(teamProviderInfoPath, teamProviderInfo); + stateManager.setTeamProviderInfo(undefined, teamProviderInfo); }; -const removeLayerFromTeamProviderInfo = (context, layerName) => { - const teamProviderInfoPath = context.amplify.pathManager.getProviderInfoFilePath(); +const removeLayerFromTeamProviderInfo = (context: $TSContext, layerName: string) => { const { envName } = context.amplify.getEnvInfo(); - if (!fs.existsSync(teamProviderInfoPath)) { - throw new Error(`${teamProviderInfoPath} not found.`); - } - const teamProviderInfo = JSONUtilities.readJson(teamProviderInfoPath); + const teamProviderInfo = stateManager.getTeamProviderInfo(); _.unset(teamProviderInfo, [envName, 'nonCFNdata', categoryName, layerName]); if (_.isEmpty(_.get(teamProviderInfo, [envName, 'nonCFNdata', categoryName]))) { _.unset(teamProviderInfo, [envName, 'nonCFNdata', categoryName]); @@ -261,7 +243,7 @@ const removeLayerFromTeamProviderInfo = (context, layerName) => { _.unset(teamProviderInfo, [envName, 'nonCFNdata']); } } - JSONUtilities.writeJson(teamProviderInfoPath, teamProviderInfo); + stateManager.setTeamProviderInfo(undefined, teamProviderInfo); }; interface LayerMetaAndBackendConfigParams { @@ -294,9 +276,9 @@ const layerParamsToStoredParams = (parameters: LayerParameters | StoredLayerPara return storedParams; }; -function createParametersFile(context, parameters, resourceName, parametersFileName) { - const parametersFilePath = path.join(context.amplify.pathManager.getBackendDirPath(), categoryName, resourceName, parametersFileName); - const currentParameters = JSONUtilities.readJson(parametersFilePath, { throwIfNotExist: false }) || ({} as any); +function createParametersFile(parameters: $TSObject, resourceName: string, parametersFileName: string) { + const parametersFilePath = path.join(pathManager.getBackendDirPath(), categoryName, resourceName, parametersFileName); + const currentParameters: $TSAny = JSONUtilities.readJson(parametersFilePath, { throwIfNotExist: false }) || {}; delete currentParameters.mutableParametersState; // this field was written in error in a previous version of the cli JSONUtilities.writeJson(parametersFilePath, { ...currentParameters, ...parameters }); } @@ -310,8 +292,8 @@ function buildParametersFileObj( return { ...parameters.mutableParametersState, ..._.pick(parameters, ['lambdaLayers']) }; } -function translateFuncParamsToResourceOpts(params: FunctionParameters | FunctionTriggerParameters): any { - let result: any = { +function translateFuncParamsToResourceOpts(params: FunctionParameters | FunctionTriggerParameters): $TSAny { + let result: $TSObject = { build: true, providerPlugin: provider, service: ServiceName.LambdaFunction, diff --git a/packages/amplify-cli-core/src/index.ts b/packages/amplify-cli-core/src/index.ts index bc9aa3c2f0e..cae3f5b365c 100644 --- a/packages/amplify-cli-core/src/index.ts +++ b/packages/amplify-cli-core/src/index.ts @@ -123,7 +123,7 @@ interface AmplifyToolkit { confirmPrompt: (prompt: string, defaultValue?: boolean) => $TSAny; constants: $TSAny; constructExeInfo: (context: $TSContext) => $TSAny; - copyBatch: () => $TSAny; + copyBatch: (context: $TSContext, jobs: $TSAny, props: $TSAny, force: boolean, writeParams?: $TSAny[] | $TSObject) => Promise; crudFlow: () => $TSAny; deleteProject: () => $TSAny; executeProviderUtils: () => $TSAny; @@ -153,7 +153,7 @@ interface AmplifyToolkit { context: $TSContext, category?: string, resourceName?: string, - filteredResources?: { category: string, resourceName: string }[], + filteredResources?: { category: string; resourceName: string }[], ) => $TSAny; storeCurrentCloudBackend: () => $TSAny; readJsonFile: () => $TSAny; @@ -212,8 +212,8 @@ interface AmplifyToolkit { forceRemoveResource: () => $TSAny; writeObjectAsJson: () => $TSAny; hashDir: () => $TSAny; - leaveBreadcrumbs: () => $TSAny; - readBreadcrumbs: () => $TSAny; + leaveBreadcrumbs: (context: $TSContext, category: string, resourceName: string, breadcrumbs: $TSAny) => void; + readBreadcrumbs: (context: $TSContext, category: string, resourceName: string) => $TSAny; loadRuntimePlugin: () => $TSAny; getImportedAuthProperties: ( context: $TSContext, diff --git a/packages/amplify-cli/src/extensions/amplify-helpers/copy-batch.ts b/packages/amplify-cli/src/extensions/amplify-helpers/copy-batch.ts index 5e823d3e889..826feed3b99 100644 --- a/packages/amplify-cli/src/extensions/amplify-helpers/copy-batch.ts +++ b/packages/amplify-cli/src/extensions/amplify-helpers/copy-batch.ts @@ -1,28 +1,26 @@ -import { JSONUtilities } from 'amplify-cli-core'; +import { JSONUtilities, $TSAny, $TSContext, $TSObject } from 'amplify-cli-core'; /** * Runs a series of jobs through the templating system. * - * @param {any} context - The Amplify CLI context - * @param {any[]} jobs - A list of jobs to run. - * @param {any} props - The props to use for variable replacement. - * @param {boolean} force - Force CF template generation - * @param {array|object} writeParams - data for the CF template but not parameters file + * @param {$TSContext} context - The Amplify CLI context + * @param {$TSAny[]} jobs - A list of jobs to run. + * @param {$TSAny} props - The props to use for variable replacement. + * @param {boolean} force - Force CF template generation + * @param {$TSAny[]|$TSObject} writeParams - data for the CF template but not parameters file */ -export async function copyBatch(context, jobs, props, force, writeParams) { +export async function copyBatch(context: $TSContext, jobs: $TSAny, props: $TSAny, force: boolean, writeParams?: $TSAny[] | $TSObject) { // grab some features - const { template, prompt, filesystem } = context; + const { template, prompt, filesystem } = context as $TSAny; const { confirm } = prompt; // If the file exists - const shouldGenerate = async (target, force) => { + const shouldGenerate = async (target: $TSAny, force: boolean) => { if (!filesystem.exists(target) || force) return true; return confirm(`overwrite ${target}`); }; - for (let index = 0; index < jobs.length; index += 1) { - // grab the current job - const job = jobs[index]; + for (const job of jobs) { // safety check if (!job) { continue; diff --git a/packages/amplify-cli/src/extensions/amplify-helpers/leave-breadcrumbs.ts b/packages/amplify-cli/src/extensions/amplify-helpers/leave-breadcrumbs.ts index 1f7c2b42cc3..d7ae50a467f 100644 --- a/packages/amplify-cli/src/extensions/amplify-helpers/leave-breadcrumbs.ts +++ b/packages/amplify-cli/src/extensions/amplify-helpers/leave-breadcrumbs.ts @@ -1,11 +1,7 @@ +import { JSONUtilities, pathManager, $TSAny, $TSContext } from 'amplify-cli-core'; import * as path from 'path'; -export function leaveBreadcrumbs(context, category, resourceName, breadcrumbs) { - const destPath = path.join( - context.amplify.pathManager.getBackendDirPath(), - category, - resourceName, - context.amplify.constants.BreadcrumbsFileName, - ); - context.amplify.writeObjectAsJson(destPath, breadcrumbs, true); +export function leaveBreadcrumbs(context: $TSContext, category: string, resourceName: string, breadcrumbs: $TSAny) { + const destPath = path.join(pathManager.getBackendDirPath(), category, resourceName, context.amplify.constants.BreadcrumbsFileName); + JSONUtilities.writeJson(destPath, breadcrumbs); } diff --git a/packages/amplify-cli/src/extensions/amplify-helpers/read-breadcrumbs.ts b/packages/amplify-cli/src/extensions/amplify-helpers/read-breadcrumbs.ts index fe850b4b2c8..5afe864baf6 100644 --- a/packages/amplify-cli/src/extensions/amplify-helpers/read-breadcrumbs.ts +++ b/packages/amplify-cli/src/extensions/amplify-helpers/read-breadcrumbs.ts @@ -1,13 +1,8 @@ import * as path from 'path'; -import { JSONUtilities } from 'amplify-cli-core'; +import { JSONUtilities, pathManager, $TSAny, $TSContext } from 'amplify-cli-core'; -export function readBreadcrumbs(context, category, resourceName) { - const breadcrumbsPath = path.join( - context.amplify.pathManager.getBackendDirPath(), - category, - resourceName, - context.amplify.constants.BreadcrumbsFileName, - ); +export function readBreadcrumbs(context: $TSContext, category: string, resourceName: string): $TSAny { + const breadcrumbsPath = path.join(pathManager.getBackendDirPath(), category, resourceName, context.amplify.constants.BreadcrumbsFileName); let breadcrumbs = JSONUtilities.readJson(breadcrumbsPath, { throwIfNotExist: false, }); diff --git a/packages/amplify-provider-awscloudformation/src/push-resources.ts b/packages/amplify-provider-awscloudformation/src/push-resources.ts index d05a6835d69..63275db219c 100644 --- a/packages/amplify-provider-awscloudformation/src/push-resources.ts +++ b/packages/amplify-provider-awscloudformation/src/push-resources.ts @@ -482,6 +482,7 @@ async function packageResources(context: $TSContext, resources: $TSAny[]) { async function packageResource(context: $TSContext, resource: $TSAny) { let result: $TSAny; let log: $TSAny = null; + const { envName }: { envName: string } = context.amplify.getEnvInfo(); if (resource.service === FunctionServiceNameLambdaLayer) { result = await context.amplify.invokePluginMethod(context, 'function', undefined, 'packageLayer', [context, resource]); @@ -500,7 +501,7 @@ async function packageResource(context: $TSContext, resource: $TSAny) { }; log = logger('packageResources.s3.uploadFile', [{ Key: s3Key }]); log(); - let s3Bucket; + let s3Bucket: string; try { s3Bucket = await s3.uploadFile(s3Params); } catch (error) { @@ -534,26 +535,13 @@ async function packageResource(context: $TSContext, resource: $TSAny) { const cfnMeta = JSONUtilities.readJson<$TSAny>(cfnFilePath); if (resource.service === FunctionServiceNameLambdaLayer) { - const isMultiEnvLayer: boolean = await context.amplify.invokePluginMethod(context, 'function', undefined, 'isMultiEnvLayer', [ + const isMultiEnvLayer: boolean = await context.amplify.invokePluginMethod(context, category, undefined, 'isMultiEnvLayer', [ context, resourceName, ]); if (isMultiEnvLayer) { - const amplifyMeta = stateManager.getMeta(); - const teamProviderInfo = stateManager.getTeamProviderInfo(); - - _.set(teamProviderInfo, [context.amplify.getEnvInfo().envName, 'categories', 'function', resourceName], { - deploymentBucketName: s3Bucket, - s3Key, - }); - - _.set(amplifyMeta, ['function', resourceName, 's3Bucket'], { - deploymentBucketName: s3Bucket, - s3Key, - }); - stateManager.setMeta(undefined, amplifyMeta); - stateManager.setTeamProviderInfo(undefined, teamProviderInfo); + storeS3BucketInfo(category, s3Bucket, envName, resourceName, s3Key); } else { cfnMeta.Resources.LambdaLayer.Properties.Content = { S3Bucket: s3Bucket, @@ -561,9 +549,7 @@ async function packageResource(context: $TSContext, resource: $TSAny) { }; } } else if (resource.service === ApiServiceNameElasticContainer) { - const cfnParams = { - ParamZipPath: s3Key, - }; + const cfnParams = { ParamZipPath: s3Key }; const cfnParamsFilePath = path.normalize(path.join(resourceDir, 'parameters.json')); JSONUtilities.writeJson(cfnParamsFilePath, cfnParams); @@ -574,15 +560,26 @@ async function packageResource(context: $TSContext, resource: $TSAny) { Key: s3Key, }; } else { - cfnMeta.Resources.LambdaFunction.Properties.Code = { - S3Bucket: s3Bucket, - S3Key: s3Key, - }; + const paramType = { Type: 'String' }; + cfnMeta.Parameters.deploymentBucketName = paramType; + cfnMeta.Parameters.s3Key = paramType; + storeS3BucketInfo(category, s3Bucket, envName, resourceName, s3Key); } } JSONUtilities.writeJson(cfnFilePath, cfnMeta); } +function storeS3BucketInfo(category: string, deploymentBucketName: string, envName: string, resourceName: string, s3Key: string) { + const amplifyMeta = stateManager.getMeta(); + const teamProviderInfo = stateManager.getTeamProviderInfo(); + + _.set(teamProviderInfo, [envName, 'categories', category, resourceName], { deploymentBucketName, s3Key }); + + _.set(amplifyMeta, [category, resourceName, 's3Bucket'], { deploymentBucketName, s3Key }); + stateManager.setMeta(undefined, amplifyMeta); + stateManager.setTeamProviderInfo(undefined, teamProviderInfo); +} + async function updateCloudFormationNestedStack( context: $TSContext, nestedStack: $TSAny, From 1b3109ca28d7b82c8cf92e831438ea8fe69cdf30 Mon Sep 17 00:00:00 2001 From: jhockett Date: Mon, 11 Jan 2021 10:22:43 -0800 Subject: [PATCH 2/5] test: update failing unit tests --- .../utils/storeResources.test.ts | 20 +++++++++---------- .../awscloudformation/utils/storeResources.ts | 6 ++---- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/amplify-category-function/src/__tests__/provider-utils/awscloudformation/utils/storeResources.test.ts b/packages/amplify-category-function/src/__tests__/provider-utils/awscloudformation/utils/storeResources.test.ts index acefae94d12..85141d66a94 100644 --- a/packages/amplify-category-function/src/__tests__/provider-utils/awscloudformation/utils/storeResources.test.ts +++ b/packages/amplify-category-function/src/__tests__/provider-utils/awscloudformation/utils/storeResources.test.ts @@ -1,4 +1,4 @@ -import { JSONUtilities } from 'amplify-cli-core'; +import { JSONUtilities, pathManager } from 'amplify-cli-core'; import { LambdaLayer } from 'amplify-function-plugin-interface'; import { saveMutableState } from '../../../../provider-utils/awscloudformation/utils/storeResources'; @@ -7,23 +7,21 @@ jest.mock('amplify-cli-core', () => ({ readJson: jest.fn(), writeJson: jest.fn(), }, + pathManager: { + getBackendDirPath: jest.fn(), + }, })); const JSONUtilities_mock = JSONUtilities as jest.Mocked; +const pathManager_mock = pathManager as jest.Mocked; + +pathManager_mock.getBackendDirPath.mockImplementation(() => 'backendDir'); describe('save mutable state', () => { beforeEach(() => { jest.clearAllMocks(); }); - const context_stub = { - amplify: { - pathManager: { - getBackendDirPath: () => 'backendDir', - }, - }, - }; - it('destructures mutableParametersState in the stored object', () => { const mutableParametersStateContents = { permissions: { @@ -36,7 +34,7 @@ describe('save mutable state', () => { lambdaLayers: [] as LambdaLayer[], }; - saveMutableState(context_stub, input); + saveMutableState(input); expect(JSONUtilities_mock.writeJson.mock.calls[0][1]).toMatchSnapshot(); }); @@ -62,7 +60,7 @@ describe('save mutable state', () => { lambdaLayers: [], resourceName: 'testResourceName', }; - saveMutableState(context_stub, input); + saveMutableState(input); expect(JSONUtilities_mock.writeJson.mock.calls[0][1]).toMatchSnapshot(); }); }); diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts index 6e63025066e..66d6495ef06 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/storeResources.ts @@ -21,8 +21,8 @@ export function createFunctionResources(context: $TSContext, parameters: Functio // copy template, CFN and parameter files copyTemplateFiles(context, parameters); - saveMutableState(context, parameters); - saveCFNParameters(context, parameters); + saveMutableState(parameters); + saveCFNParameters(parameters); context.amplify.leaveBreadcrumbs(context, categoryName, parameters.resourceName, createBreadcrumbs(parameters)); } @@ -72,7 +72,6 @@ export function removeLayerArtifacts(context: $TSContext, layerName: string) { // ideally function update should be refactored so this function does not need to be exported export function saveMutableState( - context: $TSContext, parameters: | Partial> | FunctionTriggerParameters, @@ -82,7 +81,6 @@ export function saveMutableState( // ideally function update should be refactored so this function does not need to be exported export function saveCFNParameters( - context: $TSContext, parameters: Partial> | FunctionTriggerParameters, ) { if ('trigger' in parameters) { From 73ce93cb3bcef3de03d5b5544f5192109dd27c84 Mon Sep 17 00:00:00 2001 From: jhockett Date: Mon, 11 Jan 2021 11:23:15 -0800 Subject: [PATCH 3/5] fix: update function calls to match updated params --- .../src/provider-utils/awscloudformation/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/index.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/index.ts index 098d1541880..a05f0b8caaa 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/index.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/index.ts @@ -209,14 +209,14 @@ export async function updateFunctionResource(context, category, service, paramet parameters = _.assign(parameters, previousParameters); } } - saveMutableState(context, parameters); + saveMutableState(parameters); } else { parameters = await serviceConfig.walkthroughs.updateWalkthrough(context, parameters, resourceToUpdate); if (parameters.dependsOn) { context.amplify.updateamplifyMetaAfterResourceUpdate(category, parameters.resourceName, 'dependsOn', parameters.dependsOn); } - saveMutableState(context, parameters); - saveCFNParameters(context, parameters); + saveMutableState(parameters); + saveCFNParameters(parameters); } if (!parameters || (parameters && !parameters.skipEdit)) { From 422895801e6896f89f63900f60f0b5ba870790c8 Mon Sep 17 00:00:00 2001 From: jhockett Date: Mon, 11 Jan 2021 20:17:55 -0800 Subject: [PATCH 4/5] chore: add TODO comment --- .../amplify-provider-awscloudformation/src/push-resources.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/amplify-provider-awscloudformation/src/push-resources.ts b/packages/amplify-provider-awscloudformation/src/push-resources.ts index 63275db219c..29384f8bc91 100644 --- a/packages/amplify-provider-awscloudformation/src/push-resources.ts +++ b/packages/amplify-provider-awscloudformation/src/push-resources.ts @@ -555,6 +555,8 @@ async function packageResource(context: $TSContext, resource: $TSAny) { JSONUtilities.writeJson(cfnParamsFilePath, cfnParams); } else { if (cfnMeta.Resources.LambdaFunction.Type === 'AWS::Serverless::Function') { + // TODO: determine if code path is still relevant + // if relevant, change to use refs for Bucket/Key cfnMeta.Resources.LambdaFunction.Properties.CodeUri = { Bucket: s3Bucket, Key: s3Key, From 0624bd3105025b1fc27519f3de7530ddd797a7f0 Mon Sep 17 00:00:00 2001 From: jhockett Date: Sat, 16 Jan 2021 08:41:17 -0800 Subject: [PATCH 5/5] fix: store s3 bucket name/key in team-provider-info --- .../lambda-layer-cloudformation-template.ts | 28 ++++++++----------- .../src/push-resources.ts | 28 ++++++------------- 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/lambda-layer-cloudformation-template.ts b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/lambda-layer-cloudformation-template.ts index c4f9b6e5c92..515b35e8982 100644 --- a/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/lambda-layer-cloudformation-template.ts +++ b/packages/amplify-category-function/src/provider-utils/awscloudformation/utils/lambda-layer-cloudformation-template.ts @@ -1,9 +1,10 @@ +import { $TSContext } from 'amplify-cli-core'; import { Fn, DeletionPolicy, Refs } from 'cloudform-types'; import _ from 'lodash'; import Lambda from 'cloudform-types/types/lambda'; -import { isMultiEnvLayer, Permission, LayerParameters, getLayerMetadataFactory, LayerMetadata } from './layerParams'; +import { getLayerMetadataFactory, isMultiEnvLayer, LayerMetadata, LayerParameters, Permission } from './layerParams'; -function generateLayerCfnObjBase(multiEnvLayer: boolean) { +function generateLayerCfnObjBase() { const cfnObj = { AWSTemplateFormatVersion: '2010-09-09', Description: 'Lambda layer resource stack creation using Amplify CLI', @@ -15,6 +16,12 @@ function generateLayerCfnObjBase(multiEnvLayer: boolean) { env: { Type: 'String', }, + s3Key: { + Type: 'String', + }, + deploymentBucketName: { + Type: 'String', + }, }, Resources: {}, Conditions: { @@ -22,26 +29,13 @@ function generateLayerCfnObjBase(multiEnvLayer: boolean) { }, }; - if (multiEnvLayer) { - _.merge(cfnObj, { - Parameters: { - s3Key: { - Type: 'String', - }, - deploymentBucketName: { - Type: 'String', - }, - }, - }); - } - return cfnObj; } /** * generates CFN for Layer and Layer permissions when updating layerVersion */ -export function generateLayerCfnObj(context, parameters: LayerParameters) { +export function generateLayerCfnObj(context: $TSContext, parameters: LayerParameters) { const multiEnvLayer = isMultiEnvLayer(context, parameters.layerName); const layerData = getLayerMetadataFactory(context)(parameters.layerName); const outputObj = { @@ -52,7 +46,7 @@ export function generateLayerCfnObj(context, parameters: LayerParameters) { Region: { Value: Refs.Region }, }, }; - let cfnObj = { ...generateLayerCfnObjBase(multiEnvLayer), ...outputObj }; + let cfnObj = { ...generateLayerCfnObjBase(), ...outputObj }; const POLICY_RETAIN = DeletionPolicy.Retain; const layerName = multiEnvLayer ? Fn.Sub(`${parameters.layerName}-` + '${env}', { env: Fn.Ref('env') }) : parameters.layerName; diff --git a/packages/amplify-provider-awscloudformation/src/push-resources.ts b/packages/amplify-provider-awscloudformation/src/push-resources.ts index 29384f8bc91..730a65eb81a 100644 --- a/packages/amplify-provider-awscloudformation/src/push-resources.ts +++ b/packages/amplify-provider-awscloudformation/src/push-resources.ts @@ -35,7 +35,7 @@ import { uploadAuthTriggerFiles } from './upload-auth-trigger-files'; import archiver from './utils/archiver'; import amplifyServiceManager from './amplify-service-manager'; import { DeploymentManager } from './iterative-deployment'; -import { Template } from 'cloudform-types'; +import { Fn, Template } from 'cloudform-types'; import { getGqlUpdatedResource } from './graphql-transformer/utils'; import { DeploymentStep, DeploymentOp } from './iterative-deployment/deployment-manager'; import { DeploymentStateManager } from './iterative-deployment/deployment-state-manager'; @@ -533,21 +533,12 @@ async function packageResource(context: $TSContext, resource: $TSAny) { const cfnFile = cfnFiles[0]; const cfnFilePath = path.normalize(path.join(resourceDir, cfnFile)); const cfnMeta = JSONUtilities.readJson<$TSAny>(cfnFilePath); + const paramType = { Type: 'String' }; if (resource.service === FunctionServiceNameLambdaLayer) { - const isMultiEnvLayer: boolean = await context.amplify.invokePluginMethod(context, category, undefined, 'isMultiEnvLayer', [ - context, - resourceName, - ]); - - if (isMultiEnvLayer) { - storeS3BucketInfo(category, s3Bucket, envName, resourceName, s3Key); - } else { - cfnMeta.Resources.LambdaLayer.Properties.Content = { - S3Bucket: s3Bucket, - S3Key: s3Key, - }; - } + cfnMeta.Parameters.deploymentBucketName = paramType; + cfnMeta.Parameters.s3Key = paramType; + storeS3BucketInfo(category, s3Bucket, envName, resourceName, s3Key); } else if (resource.service === ApiServiceNameElasticContainer) { const cfnParams = { ParamZipPath: s3Key }; @@ -555,14 +546,13 @@ async function packageResource(context: $TSContext, resource: $TSAny) { JSONUtilities.writeJson(cfnParamsFilePath, cfnParams); } else { if (cfnMeta.Resources.LambdaFunction.Type === 'AWS::Serverless::Function') { - // TODO: determine if code path is still relevant - // if relevant, change to use refs for Bucket/Key + cfnMeta.Parameters.deploymentBucketName = paramType; + cfnMeta.Parameters.s3Key = paramType; cfnMeta.Resources.LambdaFunction.Properties.CodeUri = { - Bucket: s3Bucket, - Key: s3Key, + Bucket: Fn.Ref('deploymentBucketName'), + Key: Fn.Ref('s3Key'), }; } else { - const paramType = { Type: 'String' }; cfnMeta.Parameters.deploymentBucketName = paramType; cfnMeta.Parameters.s3Key = paramType; storeS3BucketInfo(category, s3Bucket, envName, resourceName, s3Key);