Skip to content

Commit

Permalink
feat: codegen add --region (#683)
Browse files Browse the repository at this point in the history
  • Loading branch information
dpilch authored Sep 12, 2023
1 parent dbe12ab commit 4d44c6c
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 11 deletions.
10 changes: 6 additions & 4 deletions packages/amplify-codegen/commands/codegen/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ module.exports = {
try {
const { options = {} } = context.parameters;
const keys = Object.keys(options);
if (keys.length && !keys.includes('apiId')) {
const paramMsg = keys.length > 1 ? 'Invalid parameters ' : 'Invalid parameter ';
// frontend and framework are undocumented, but are read when apiId is also supplied
const { apiId = null, region, yes, frontend, framework, ...rest } = options;
const extraOptions = Object.keys(rest);
if (extraOptions.length) {
const paramMsg = extraOptions.length > 1 ? 'Invalid parameters' : 'Invalid parameter';
context.print.info(`${paramMsg} ${keys.join(', ')}`);
context.print.info(constants.INFO_MESSAGE_ADD_ERROR);
return;
}
const apiId = context.parameters.options.apiId || null;
await codeGen.add(context, apiId);
await codeGen.add(context, apiId, region);
} catch (ex) {
context.print.error(ex.message);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/amplify-codegen/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"coverageThreshold": {
"global": {
"branches": 54,
"functions": 65,
"functions": 64,
"lines": 72
}
},
Expand Down
6 changes: 3 additions & 3 deletions packages/amplify-codegen/src/commands/add.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const Ora = require('ora');
const process = require('process');
const { loadConfig } = require('../codegen-config');
const constants = require('../constants');
const generateStatements = require('./statements');
Expand All @@ -23,11 +24,11 @@ const askForFramework = require('../walkthrough/questions/selectFramework');
const frontends = ['android', 'ios', 'javascript'];
const frameworks = ['angular', 'ember', 'ionic', 'react', 'react-native', 'vue', 'none'];

async function add(context, apiId = null) {
async function add(context, apiId = null, region = 'us-east-1') {
let withoutInit = false;
// Determine if working in an amplify project
try {
context.amplify.getProjectMeta();
await context.amplify.getProjectMeta();
} catch (e) {
withoutInit = true;
const config = loadConfig(context, withoutInit);
Expand Down Expand Up @@ -69,7 +70,6 @@ async function add(context, apiId = null) {
}
}

let region = 'us-east-1';
if (!withoutInit) {
region = getProjectAwsRegion(context);
}
Expand Down
9 changes: 6 additions & 3 deletions packages/amplify-codegen/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ module.exports = {
PROMPT_MSG_SELECT_PROJECT: 'Choose the AppSync API',
PROMPT_MSG_SELECT_REGION: 'Choose AWS Region',
ERROR_CODEGEN_TARGET_NOT_SUPPORTED: 'is not supported by codegen plugin',
ERROR_FLUTTER_CODEGEN_NOT_SUPPORTED: 'Flutter only supports the command $amplify codegen models. All the other codegen commands are not supported.',
ERROR_FLUTTER_CODEGEN_NOT_SUPPORTED:
'Flutter only supports the command $amplify codegen models. All the other codegen commands are not supported.',
ERROR_CODEGEN_FRONTEND_NOT_SUPPORTED: 'The project frontend is not supported by codegen',
ERROR_MSG_MAX_DEPTH: 'Depth should be a integer greater than 0',
ERROR_CODEGEN_NO_API_AVAILABLE: 'There are no GraphQL APIs available.\nAdd by running $amplify api add',
Expand All @@ -37,7 +38,8 @@ module.exports = {
CMD_DESCRIPTION_CONFIGURE: 'Change/Update codegen configuration',
ERROR_CODEGEN_NO_API_CONFIGURED: 'code generation is not configured. Configure it by running \n$amplify codegen add',
ERROR_CODEGEN_PENDING_API_PUSH: 'AppSync API is not pushed to the cloud. Did you forget to do \n$amplify api push',
ERROR_CODEGEN_NO_API_META: 'Cannot find API metadata. Please reset codegen by running $amplify codegen remove && amplify codegen add --apiId YOUR_API_ID',
ERROR_CODEGEN_NO_API_META:
'Cannot find API metadata. Please reset codegen by running $amplify codegen remove && amplify codegen add --apiId YOUR_API_ID',
WARNING_CODEGEN_PENDING_API_PUSH: 'The APIs listed below are not pushed to the cloud. Run amplify api push',
ERROR_APPSYNC_API_NOT_FOUND:
'Could not find the AppSync API. If you have removed the AppSync API in the console run amplify codegen remove',
Expand All @@ -55,5 +57,6 @@ module.exports = {
INFO_MESSAGE_DOWNLOAD_ERROR: 'Downloading schema failed',
INFO_MESSAGE_OPS_GEN: 'Generating GraphQL operations',
INFO_MESSAGE_OPS_GEN_SUCCESS: 'Generated GraphQL operations successfully and saved at ',
INFO_MESSAGE_ADD_ERROR: 'amplify codegen add takes only apiId as parameter. \n$ amplify codegen add [--apiId <API_ID>]',
INFO_MESSAGE_ADD_ERROR:
'amplify codegen add takes only apiId and region as parameters. \n$ amplify codegen add [--apiId <API_ID>] [--region <region>]',
};
144 changes: 144 additions & 0 deletions packages/amplify-codegen/tests/cli/add.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
const add = require('../../commands/codegen/add');
const codeGen = require('../../src/index');

jest.mock('../../src/index');

describe('cli - add', () => {
beforeEach(() => {
jest.resetAllMocks();
});

test('feature name', () => {
expect(add.name).toEqual('add');
});

describe('run', () => {
test('executes codegen add', async () => {
const context = {
parameters: {},
};
await add.run(context);
expect(codeGen.add).toHaveBeenCalledWith(context, null, undefined);
});

test('catches error in codegen add', async () => {
const error = jest.fn();
const context = {
parameters: {},
print: {
error,
},
};

const codegenError = new Error('failed to read file');
codeGen.add.mockRejectedValueOnce(codegenError);
await add.run(context);
expect(error).toHaveBeenCalledWith(codegenError.message);
});

test('passes apiId', async () => {
const apiId = 'apiid';
const context = {
parameters: {
options: {
apiId,
},
},
};
await add.run(context);
expect(codeGen.add).toHaveBeenCalledWith(context, apiId, undefined);
});

test('passes region', async () => {
const region = 'region';
const context = {
parameters: {
options: {
region,
},
},
};
await add.run(context);
expect(codeGen.add).toHaveBeenCalledWith(context, null, region);
});

test('throws error on invalid arg', async () => {
const badArg = 'badArg';
const info = jest.fn();
const context = {
parameters: {
options: {
badArg,
},
},
print: {
info,
},
};
await add.run(context);
expect(info).toHaveBeenCalledWith('Invalid parameter badArg');

expect(info).toHaveBeenCalledWith(
'amplify codegen add takes only apiId and region as parameters. \n$ amplify codegen add [--apiId <API_ID>] [--region <region>]',
);
});

test('throws error on invalid args', async () => {
const badArgOne = 'badArgOne';
const badArgTwo = 'badArgTwo';
const info = jest.fn();
const context = {
parameters: {
options: {
badArgOne,
badArgTwo,
},
},
print: {
info,
},
};
await add.run(context);
expect(info).toHaveBeenCalledWith('Invalid parameters badArgOne, badArgTwo');

expect(info).toHaveBeenCalledWith(
'amplify codegen add takes only apiId and region as parameters. \n$ amplify codegen add [--apiId <API_ID>] [--region <region>]',
);
});

test('allows undocummented frontend and framework', async () => {
const frontend = 'frontend';
const framework = 'framework';
const info = jest.fn();
const context = {
parameters: {
options: {
frontend,
framework,
},
},
print: {
info,
},
};
await add.run(context);
expect(info).not.toHaveBeenCalled();
});

test('ignores yes arg', async () => {
const yes = true;
const info = jest.fn();
const context = {
parameters: {
options: {
yes,
},
},
print: {
info,
},
};
await add.run(context);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`command - add without init should read frontend and framework from options 1`] = `
Object {
"amplifyExtension": Object {
"apiId": "MOCK_API_ID",
"codeGenTarget": "TYPE_SCRIPT_OR_FLOW_OR_ANY_OTHER_LANGUAGE",
"docsFilePath": "MOCK_DOCS_FILE_PATH",
"framework": "vue",
"frontend": "javascript",
"generatedFileName": "API.TS",
"region": "us-east-1",
},
"excludes": "MOCK_EXCLUDE",
"includes": "MOCK_INCLUDE",
"projectName": "Codegen Project",
"schema": "/user/foo/project/schema.graphql",
}
`;

exports[`command - add without init should use region supplied when without init 1`] = `
Object {
"amplifyExtension": Object {
"apiId": "MOCK_API_ID",
"codeGenTarget": "TYPE_SCRIPT_OR_FLOW_OR_ANY_OTHER_LANGUAGE",
"docsFilePath": "MOCK_DOCS_FILE_PATH",
"framework": "react",
"frontend": "javascript",
"generatedFileName": "API.TS",
"region": "us-west-2",
},
"excludes": "MOCK_EXCLUDE",
"includes": "MOCK_INCLUDE",
"projectName": "Codegen Project",
"schema": "/user/foo/project/schema.graphql",
}
`;
83 changes: 83 additions & 0 deletions packages/amplify-codegen/tests/commands/add.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const fs = require('fs');
const { loadConfig } = require('../../src/codegen-config');
const generateStatements = require('../../src/commands/statements');
const generateTypes = require('../../src/commands/types');
const addWalkthrough = require('../../src/walkthrough/add');
const askForFrontend = require('../../src/walkthrough/questions/selectFrontend');
const askForFramework = require('../../src/walkthrough/questions/selectFramework');
const changeAppSyncRegions = require('../../src/walkthrough/changeAppSyncRegions');
const { AmplifyCodeGenAPINotFoundError } = require('../../src/errors');

Expand All @@ -16,13 +19,22 @@ const MOCK_CONTEXT = {
amplify: {
getProjectMeta: jest.fn(),
},
parameters: {
options: {},
},
};
jest.mock('fs');
jest.mock('../../src/walkthrough/add');
jest.mock('../../src/walkthrough/questions/selectFrontend');
jest.mock('../../src/walkthrough/questions/selectFramework');
jest.mock('../../src/walkthrough/changeAppSyncRegions');
jest.mock('../../src/commands/types');
jest.mock('../../src/commands/statements');
jest.mock('../../src/codegen-config');
jest.mock('../../src/utils');
jest.mock('process', () => ({
cwd: () => '/user/foo/project',
}));

const MOCK_INCLUDE_PATTERN = 'MOCK_INCLUDE';
const MOCK_EXCLUDE_PATTERN = 'MOCK_EXCLUDE';
Expand Down Expand Up @@ -148,4 +160,75 @@ describe('command - add', () => {
await add(MOCK_CONTEXT);
expect(generateTypes).not.toHaveBeenCalled();
});

it('should ignore region supplied when with init', async () => {
const region = 'us-west-2';
await add(MOCK_CONTEXT, MOCK_API_ID, region);
expect(getProjectAwsRegion).toHaveBeenCalled();
expect(getAppSyncAPIInfo).toHaveBeenCalledWith(MOCK_CONTEXT, MOCK_API_ID, MOCK_AWS_REGION);
});

describe('without init', () => {
const getProjectMeta = jest.fn();
beforeEach(() => {
loadConfig.mockReturnValue({ ...LOAD_CONFIG_METHODS, getProjects: jest.fn().mockReturnValue([]) });
askForFrontend.mockReturnValue('javascript');
askForFramework.mockReturnValue('react');
getProjectMeta.mockRejectedValue('no init');
fs.existsSync.mockReturnValue(true);
});

afterEach(() => {
loadConfig.mockReset();
askForFrontend.mockReset();
askForFramework.mockReset();
getProjectMeta.mockReset();
fs.existsSync.mockReset();
});

it('should read frontend and framework from options', async () => {
const parameters = {
options: {
frontend: 'javascript',
framework: 'vue',
},
};
await add({ ...MOCK_CONTEXT, amplify: { getProjectMeta }, parameters }, MOCK_API_ID);
expect(askForFrontend).not.toHaveBeenCalled();
expect(askForFramework).not.toHaveBeenCalled();
expect(LOAD_CONFIG_METHODS.addProject).toHaveBeenCalled();
expect(LOAD_CONFIG_METHODS.addProject.mock.calls[0][0]).toMatchSnapshot();
});

it('should use region supplied when without init', async () => {
const region = 'us-west-2';
await add({ ...MOCK_CONTEXT, amplify: { getProjectMeta } }, MOCK_API_ID, region);
expect(getProjectAwsRegion).not.toHaveBeenCalled();
expect(LOAD_CONFIG_METHODS.addProject).toHaveBeenCalled();
expect(LOAD_CONFIG_METHODS.addProject.mock.calls[0][0]).toMatchSnapshot();
});

it('should error on invalid frontend', () => {
const parameters = {
options: {
frontend: 'foo',
},
};
expect(add({ ...MOCK_CONTEXT, amplify: { getProjectMeta }, parameters }, MOCK_API_ID)).rejects.toThrowError(
'Invalid frontend provided',
);
});

it('should error on invalid framework', () => {
const parameters = {
options: {
frontend: 'javascript',
framework: 'foo',
},
};
expect(add({ ...MOCK_CONTEXT, amplify: { getProjectMeta }, parameters }, MOCK_API_ID)).rejects.toThrowError(
'Invalid framework provided',
);
});
});
});

0 comments on commit 4d44c6c

Please sign in to comment.