Skip to content

Commit

Permalink
feat: append whitespace to cli-inputs.json for updated build dir (#11024
Browse files Browse the repository at this point in the history
)

* feat: detect when attrs is added to cfn template and append whitespace for amplify push

* test: add unit test to cover appending whitespace to cli-inputs.json file
  • Loading branch information
danielleadams authored Sep 21, 2022
1 parent 262276a commit 92536d7
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import fs from 'fs-extra';
import { updateCognitoTrackedFiles } from '../../../extensions/amplify-helpers/update-tracked-files';

const cloudBackendCfnTemplatePath = '/amplify/#cloud-backend/auth/cognito/build/cognito-cloudformation-template.json';
const backendCfnTemplatePath = '/amplify/backend/auth/cognito/build/cognito-cloudformation-template.json';
const cliInputsFile = '/amplify/backend/auth/cognito/cli-inputs.json';

let cloudBackendExists: boolean;
let setInCloudBackendDir: boolean;

jest.mock('amplify-cli-core', () => {
const { stateManager } = jest.requireActual('amplify-cli-core');

return {
JSONUtilities: {
readJson: jest.fn().mockImplementation(path => {
const cfnTemplate = {
Resources: {
UserPool: {
Properties: {},
},
},
};

if ((path === cloudBackendCfnTemplatePath && setInCloudBackendDir) || path === backendCfnTemplatePath) {
cfnTemplate.Resources.UserPool.Properties = {
UserAttributeUpdateSettings: {
AttributesRequireVerificationBeforeUpdate: [
'email',
],
},
};
}

return cfnTemplate;
}),
},
pathManager: {
getBackendDirPath: jest.fn().mockReturnValue('/amplify/backend'),
getCurrentCloudBackendDirPath: jest.fn().mockReturnValue('/amplify/#cloud-backend'),
},
stateManager: {
...stateManager,
getMeta: jest.fn().mockReturnValue({
auth: {
cognito: {
service: 'Cognito',
},
},
}),
},
};
});

describe('updateCognitoTrackedFiles', () => {
const fsMock = fs as jest.Mocked<typeof fs>;

beforeEach(() => {
fsMock.existsSync = jest.fn().mockImplementation(path => {
if (path === '/amplify/#cloud-backend') {
return cloudBackendExists;
}
return true;
});

fsMock.appendFile = jest.fn();
});

afterEach(() => {
fsMock.existsSync.mockReset();
fsMock.appendFile.mockReset();

cloudBackendExists = false;
setInCloudBackendDir = false;
});

describe('when backend and cloud backend do not match', () => {
beforeEach(() => {
cloudBackendExists = true;
setInCloudBackendDir = false;
});

it('appends white space', async () => {
await updateCognitoTrackedFiles();
expect(fsMock.appendFile).toHaveBeenCalledTimes(1);
expect(fsMock.appendFile).toBeCalledWith(cliInputsFile, ' ');
});
});

describe('when backend and cloud backend do match', () => {
beforeEach(() => {
cloudBackendExists = true;
setInCloudBackendDir = true;
});

it('does not append white space', async () => {
await updateCognitoTrackedFiles();
expect(fsMock.appendFile).toHaveBeenCalledTimes(0);
});
});

describe('when cloud backend does not exist', () => {
beforeEach(() => {
cloudBackendExists = false;
});

it('does not append white space', async () => {
await updateCognitoTrackedFiles();
expect(fsMock.appendFile).toHaveBeenCalledTimes(0);
});
});
});
9 changes: 9 additions & 0 deletions packages/amplify-cli/src/commands/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import {
getProviderPlugins,
} from '../extensions/amplify-helpers/get-provider-plugins';
import { updateCognitoTrackedFiles } from '../extensions/amplify-helpers/update-tracked-files';

/**
* Download and unzip deployment bucket contents to #current-cloud-backend so amplify status shows correct state
Expand Down Expand Up @@ -52,6 +53,13 @@ const syncCurrentCloudBackend = async (context: $TSContext): Promise<void> => {
}
};

/**
* Updates tracked files for auto updates in the build directory that will not be detected for 'amplify push'
*/
const updateTrackedFiles = async (): Promise<void> => {
await updateCognitoTrackedFiles();
};

/**
* Runs push command
*/
Expand All @@ -64,5 +72,6 @@ export const run = async (context: $TSContext): Promise<$TSAny> => {
context.exeInfo.forcePush = true;
}
await syncCurrentCloudBackend(context);
await updateTrackedFiles();
return context.amplify.pushResources(context);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import * as path from 'path';
import fs from 'fs-extra';
import {
$TSAny, JSONUtilities, pathManager, stateManager,
} from 'amplify-cli-core';

const {
readJson,
} = JSONUtilities;

/**
* Updates Cognito files that are tracked so that the diff is detected for an `amplify push`
*/
export const updateCognitoTrackedFiles = async (): Promise<void> => {
const currentCloudBackendDir = pathManager.getCurrentCloudBackendDirPath();
const localBackendDir = pathManager.getBackendDirPath();
const amplifyMeta = stateManager.getMeta();
const cognitoResource = stateManager.getResourceFromMeta(amplifyMeta, 'auth', 'Cognito', undefined, false);

if (!fs.existsSync(currentCloudBackendDir) || !cognitoResource) {
return;
}

const { resourceName } = cognitoResource;

if (await detectCognitoDiff(currentCloudBackendDir, localBackendDir, resourceName)) {
await addExtraLineToCliInputsJson(localBackendDir, resourceName);
}
};

const detectCognitoDiff = async (
currentCloudBackendDir: string,
localBackendDir: string,
resourceName: string,
): Promise<boolean> => detectCognitoAttributesRequireVerificationBeforeUpdateDiff(
currentCloudBackendDir,
localBackendDir,
resourceName,
);

const detectCognitoAttributesRequireVerificationBeforeUpdateDiff = async (
currentCloudBackendDir: string,
localBackendDir: string,
resourceName: string,
): Promise<boolean> => {
const cloudBackendUserAttrUpdateSettings = await readCfnTemplateUserAttributeSettings(currentCloudBackendDir, resourceName);
const backendUserAttrUpdateSettings = await readCfnTemplateUserAttributeSettings(localBackendDir, resourceName);
const updateNotInCloudBackend: boolean = !cloudBackendUserAttrUpdateSettings?.AttributesRequireVerificationBeforeUpdate
|| cloudBackendUserAttrUpdateSettings?.AttributesRequireVerificationBeforeUpdate[0] !== 'email';
const updateInLocalBackend: boolean = backendUserAttrUpdateSettings?.AttributesRequireVerificationBeforeUpdate.length === 1
&& backendUserAttrUpdateSettings?.AttributesRequireVerificationBeforeUpdate[0] === 'email';

return updateNotInCloudBackend && updateInLocalBackend;
};

type UserAttributeUpdateSettings = {
AttributesRequireVerificationBeforeUpdate: string[]
}

const readCfnTemplateUserAttributeSettings = async (
backendDir: string,
resourceName: string,
): Promise<UserAttributeUpdateSettings | undefined> => {
const cfnTemplatePath = path.join(backendDir, 'auth', resourceName, 'build', `${resourceName}-cloudformation-template.json`);
const cfnTemplate: $TSAny = readJson(cfnTemplatePath, { throwIfNotExist: false });

if (!cfnTemplate) {
return undefined;
}

return cfnTemplate.Resources.UserPool?.Properties?.UserAttributeUpdateSettings;
};

const addExtraLineToCliInputsJson = async (backendDir: string, resourceName: string): Promise<void> => {
const cliInputsFile = path.join(backendDir, 'auth', resourceName, 'cli-inputs.json');

if (fs.existsSync(cliInputsFile)) {
fs.appendFile(cliInputsFile, ' ');
}
};

0 comments on commit 92536d7

Please sign in to comment.