diff --git a/packages/amplify-app/src/framework-config-mapping.js b/packages/amplify-app/src/framework-config-mapping.js index 9dc0b3672a9..4f1fa982c42 100644 --- a/packages/amplify-app/src/framework-config-mapping.js +++ b/packages/amplify-app/src/framework-config-mapping.js @@ -36,7 +36,7 @@ const vueConfig = { }; const emberConfig = { - SourceDir: '/', + SourceDir: './', DistributionDir: 'dist', BuildCommand: `${npm} run-script build -- -e production`, StartCommand: `${npm} run-script start`, diff --git a/packages/amplify-codegen/src/callbacks/postPushCallback.js b/packages/amplify-codegen/src/callbacks/postPushCallback.js index 4b0c5078b88..1b344ac14b8 100644 --- a/packages/amplify-codegen/src/callbacks/postPushCallback.js +++ b/packages/amplify-codegen/src/callbacks/postPushCallback.js @@ -1,3 +1,6 @@ +const path = require('path'); +const { pathManager } = require('amplify-cli-core'); + const loadConfig = require('../codegen-config'); const generateStatements = require('../commands/statements'); const generateTypes = require('../commands/types'); @@ -10,25 +13,32 @@ async function postPushCallback(context, graphQLConfig) { return; } - if (!graphQLConfig.gqlConfig.schema) { - const config = loadConfig(context); - const schemaLocation = getSchemaDownloadLocation(context); + try { + if (!graphQLConfig.gqlConfig.schema) { + const config = loadConfig(context); - const newProject = graphQLConfig.gqlConfig; - newProject.schema = schemaLocation; - config.addProject(newProject); - config.save(); - } - const apis = getAppSyncAPIDetails(context); + const projectPath = pathManager.findProjectRoot() || process.cwd(); + const schemaLocation = path.join(projectPath, getSchemaDownloadLocation(context)); - await downloadIntrospectionSchema(context, apis[0].id, graphQLConfig.gqlConfig.schema); - if (graphQLConfig.shouldGenerateDocs) { - await generateStatements(context); - } - if (graphQLConfig.shouldGenerateModels) { - await generateModels(context); + const newProject = graphQLConfig.gqlConfig; + newProject.schema = schemaLocation; + config.addProject(newProject); + config.save(); + } + const apis = getAppSyncAPIDetails(context); + + await downloadIntrospectionSchema(context, apis[0].id, graphQLConfig.gqlConfig.schema); + if (graphQLConfig.shouldGenerateDocs) { + await generateStatements(context); + } + if (graphQLConfig.shouldGenerateModels) { + await generateModels(context); + } + await generateTypes(context); + } catch (error) { + // Code Generation failure should not result in actual push failure + context.print.warning(`Code generation failed with the following error \n${error.message}.`); } - await generateTypes(context); } module.exports = postPushCallback; diff --git a/packages/amplify-codegen/src/utils/getSchemaDownloadLocation.js b/packages/amplify-codegen/src/utils/getSchemaDownloadLocation.js index d62e77d819b..056e20f5783 100644 --- a/packages/amplify-codegen/src/utils/getSchemaDownloadLocation.js +++ b/packages/amplify-codegen/src/utils/getSchemaDownloadLocation.js @@ -1,8 +1,13 @@ const path = require('path'); +const { pathManager } = require('amplify-cli-core'); const getAndroidResDir = require('./getAndroidResDir'); const getFrontEndHandler = require('./getFrontEndHandler'); +function isSubDirectory(parent, pathToCheck) { + const relative = path.relative(parent, pathToCheck); + return relative && !relative.startsWith('..') && !path.isAbsolute(relative); +} function getSchemaDownloadLocation(context) { let downloadDir; try { @@ -18,6 +23,10 @@ function getSchemaDownloadLocation(context) { const outputPath = frontEnd === 'javascript' ? sourceDir : ''; downloadDir = path.join(outputPath, 'graphql'); } + + const projectRoot = pathManager.findProjectRoot(); + // Downloaded schema should always be inside the project dir so the project is self contained + downloadDir = isSubDirectory(projectRoot, path.resolve(downloadDir)) ? downloadDir : path.join(projectRoot, downloadDir); return path.join(downloadDir, 'schema.json'); } diff --git a/packages/amplify-codegen/tests/callbacks/postPushCallback.test.js b/packages/amplify-codegen/tests/callbacks/postPushCallback.test.js index 12dbdfeebcb..c7c21abe319 100644 --- a/packages/amplify-codegen/tests/callbacks/postPushCallback.test.js +++ b/packages/amplify-codegen/tests/callbacks/postPushCallback.test.js @@ -1,3 +1,5 @@ +const path = require('path'); +const { pathManager } = require('amplify-cli-core'); const loadConfig = require('../../src/codegen-config'); const { downloadIntrospectionSchema, getAppSyncAPIDetails, getSchemaDownloadLocation } = require('../../src/utils'); const generateStatements = require('../../src/commands/statements'); @@ -10,6 +12,7 @@ const MOCK_CONTEXT = { }, }; +jest.mock('amplify-cli-core'); jest.mock('../../src/codegen-config'); jest.mock('../../src/utils'); jest.mock('../../src/commands/statements'); @@ -19,6 +22,7 @@ const MOCK_PROJECT_NAME = 'MOCK_PROJECT'; const MOCK_API_ID = 'MOCK_API_ID'; const MOCK_API_ENDPOINT = 'MOCK_API_ENDPOINT'; +const MOCK_PATH_MANAGER_PROJECT_ROOT = '/project'; const MOCK_SELECTED_PROJECT = { projectName: MOCK_PROJECT_NAME, id: MOCK_API_ID, @@ -42,6 +46,7 @@ const LOAD_CONFIG_METHODS = { describe('Callback - Post Push update AppSync API', () => { beforeEach(() => { jest.resetAllMocks(); + pathManager.findProjectRoot.mockReturnValue(MOCK_PATH_MANAGER_PROJECT_ROOT); loadConfig.mockReturnValue(LOAD_CONFIG_METHODS); getAppSyncAPIDetails.mockReturnValue(MOCK_PROJECTS); getSchemaDownloadLocation.mockReturnValue(MOCK_SCHEMA_DOWNLOAD_LOCATION); @@ -52,7 +57,11 @@ describe('Callback - Post Push update AppSync API', () => { expect(loadConfig).toHaveBeenCalledWith(MOCK_CONTEXT); expect(getAppSyncAPIDetails).toHaveBeenCalledWith(MOCK_CONTEXT); expect(getSchemaDownloadLocation).toHaveBeenCalledWith(MOCK_CONTEXT); - expect(downloadIntrospectionSchema).toHaveBeenCalledWith(MOCK_CONTEXT, MOCK_API_ID, MOCK_SCHEMA_DOWNLOAD_LOCATION); + expect(downloadIntrospectionSchema).toHaveBeenCalledWith( + MOCK_CONTEXT, + MOCK_API_ID, + path.join(MOCK_PATH_MANAGER_PROJECT_ROOT, MOCK_SCHEMA_DOWNLOAD_LOCATION), + ); expect(LOAD_CONFIG_METHODS.addProject).toHaveBeenCalled(); const newProject = LOAD_CONFIG_METHODS.addProject.mock.calls[0][0]; expect(newProject).toEqual({ @@ -60,7 +69,7 @@ describe('Callback - Post Push update AppSync API', () => { amplifyExtension: { ...MOCK_GRAPHQL_CONFIG.gqlConfig.amplifyExtension, }, - schema: MOCK_SCHEMA_DOWNLOAD_LOCATION, + schema: path.join(MOCK_PATH_MANAGER_PROJECT_ROOT, MOCK_SCHEMA_DOWNLOAD_LOCATION), }); expect(generateTypes).toHaveBeenCalledTimes(1); expect(generateStatements).toHaveBeenCalledTimes(1); diff --git a/packages/amplify-codegen/tests/utils/getSchemaDownloadLocation.test.js b/packages/amplify-codegen/tests/utils/getSchemaDownloadLocation.test.js index b8a906853d8..bc30f2dc896 100644 --- a/packages/amplify-codegen/tests/utils/getSchemaDownloadLocation.test.js +++ b/packages/amplify-codegen/tests/utils/getSchemaDownloadLocation.test.js @@ -1,4 +1,5 @@ const { join, dirname } = require('path'); +const { pathManager } = require('amplify-cli-core'); const getSchemaDownloadLocation = require('../../src/utils/getSchemaDownloadLocation'); const getAndroidResDir = require('../../src/utils/getAndroidResDir'); @@ -6,6 +7,7 @@ const getFrontendHandler = require('../../src/utils/getFrontEndHandler'); jest.mock('../../src/utils/getAndroidResDir'); jest.mock('../../src/utils/getFrontEndHandler'); +jest.mock('amplify-cli-core'); let mockContext; const mockProjectConfigDefault = 'MOCK_PROJECT_CONFIG'; @@ -19,12 +21,14 @@ const mockProjectConfig = { }; const mockResDir = 'MOCK_RES_DIR/Res'; const mockAPIName = 'FooAPI'; +const mockProjectRoot = '/home/user/project/proj1'; const mockGetProjectConfigDefault = jest.fn(); const mockGetProjectConfig = jest.fn(); describe('getSchemaDownloadLocation', () => { beforeEach(() => { jest.resetAllMocks(); + pathManager.findProjectRoot.mockReturnValue(mockProjectRoot); mockGetProjectConfigDefault.mockReturnValue(mockProjectConfigDefault); mockGetProjectConfig.mockReturnValue(mockProjectConfig); getAndroidResDir.mockImplementation(() => { @@ -40,7 +44,7 @@ describe('getSchemaDownloadLocation', () => { }, }; const downloadLocation = getSchemaDownloadLocation(mockContext); - expect(downloadLocation).toEqual(join('src', 'graphql', 'schema.json')); + expect(downloadLocation).toEqual(join(mockProjectRoot, 'src', 'graphql', 'schema.json')); }); it('should use the defined project config directory when used in JS frontend', () => { @@ -50,7 +54,7 @@ describe('getSchemaDownloadLocation', () => { }, }; const downloadLocation = getSchemaDownloadLocation(mockContext); - expect(downloadLocation).toEqual(join('web-client', 'src', 'graphql', 'schema.json')); + expect(downloadLocation).toEqual(join(mockProjectRoot, 'web-client', 'src', 'graphql', 'schema.json')); }); it('should use the graphql directory when used in iOS frontend', () => { @@ -61,7 +65,7 @@ describe('getSchemaDownloadLocation', () => { }; getFrontendHandler.mockReturnValue('iOS'); const downloadLocation = getSchemaDownloadLocation(mockContext); - expect(downloadLocation).toEqual(join('graphql', 'schema.json')); + expect(downloadLocation).toEqual(join(mockProjectRoot, 'graphql', 'schema.json')); }); it('should use main directory in Android', () => { @@ -72,6 +76,17 @@ describe('getSchemaDownloadLocation', () => { }; getAndroidResDir.mockReturnValue(mockResDir); const downloadLocation = getSchemaDownloadLocation(mockContext); - expect(downloadLocation).toEqual(join(dirname(mockResDir), 'graphql', 'schema.json')); + expect(downloadLocation).toEqual(join(mockProjectRoot, dirname(mockResDir), 'graphql', 'schema.json')); + }); + + it('should return the download location inside the project root', () => { + mockContext = { + amplify: { + getProjectConfig: mockGetProjectConfig, + }, + }; + getAndroidResDir.mockReturnValue(mockResDir); + const downloadLocation = getSchemaDownloadLocation(mockContext); + expect(downloadLocation).toContain(mockProjectRoot); }); }); diff --git a/packages/amplify-frontend-javascript/lib/framework-config-mapping.js b/packages/amplify-frontend-javascript/lib/framework-config-mapping.js index cf42a27cfc4..4c57dfb0e2a 100644 --- a/packages/amplify-frontend-javascript/lib/framework-config-mapping.js +++ b/packages/amplify-frontend-javascript/lib/framework-config-mapping.js @@ -40,7 +40,7 @@ const vueConfig = { }; const emberConfig = { - SourceDir: '/', + SourceDir: './', DistributionDir: 'dist', BuildCommand: `${npm} run-script build -- -e production`, StartCommand: `${npm} run-script start`,