Skip to content

Commit

Permalink
Added feature to pick from multiple sample backends. (#2853)
Browse files Browse the repository at this point in the history
* Initial work

* Added magento backend validation.

* Updated intercept to fetch backends.

* Fetching sample backends while creating a pwa app.

* Minor.

* Added try catches.

* Updated docs.

* Minor.

* Added node-fetch peer dep.

* Minor pretty print stuff.

* Added lodash and node-fetch deps.

* Updated extension desc.

* Updated tests.

* Minor.

* Updated remaining tests.

* Added runEnvValidators.js tests.

* Fixed linter issues.

* Minor.

* Added intercept tests.

* Using debug instead of console.error.

* Moving backend related code to run after configureWebpack.

* Updated the skipped test.

* Update ENV error reporting message.

* Minor.

* Minor.

* Updated error snapshot test.

* Minor.

* Minor.

* Added or condition for path variable in tests.

* Minor.

* Updated tests to use snapshots.

* Mock everything, lets get this working.

* Removed unecessary mocks.

* Updated tests.

* Using try catch to avoid prj creations.

* Reporting different message if otherBackends is empty.

* Updated tests.

* Prettier fix.

* Added console warning for production deployment.

* Updated production launch checklist docs.

Co-authored-by: Devagouda <40405790+dpatil-magento@users.noreply.github.com>
  • Loading branch information
revanth0212 and dpatil-magento authored Dec 2, 2020
1 parent c59de80 commit 936bd9e
Show file tree
Hide file tree
Showing 28 changed files with 586 additions and 107 deletions.
31 changes: 30 additions & 1 deletion packages/create-pwa/lib/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
const { basename, resolve } = require('path');
const os = require('os');
const fetch = require('node-fetch');
const changeCase = require('change-case');
const inquirer = require('inquirer');
const execa = require('execa');
const chalk = require('chalk');
const gitUserInfo = require('git-user-info');
const isInvalidPath = require('is-invalid-path');
const isValidNpmName = require('is-valid-npm-name');
const { uniqBy } = require('lodash');

const pkg = require('../package.json');
const {
sampleBackends
sampleBackends: defaultSampleBackends
} = require('@magento/pwa-buildpack/lib/cli/create-project');

const removeDuplicateBackends = backendEnvironments =>
uniqBy(backendEnvironments, 'url');

const fetchSampleBackends = async () => {
try {
const res = await fetch(
'https://fvp0esmt8f.execute-api.us-east-1.amazonaws.com/default/getSampleBackends'
);
const { sampleBackends } = await res.json();

return sampleBackends.environments;
} catch {
return [];
}
};

module.exports = async () => {
console.log(chalk.greenBright(`${pkg.name} v${pkg.version}`));
console.log(
Expand All @@ -20,6 +39,16 @@ module.exports = async () => {
const userAgent = process.env.npm_config_user_agent || '';
const isYarn = userAgent.includes('yarn');

const sampleBackendEnvironments = await fetchSampleBackends();
const filteredBackendEnvironments = removeDuplicateBackends([
...sampleBackendEnvironments,
...defaultSampleBackends.environments
]);
const sampleBackends = {
...defaultSampleBackends,
environments: filteredBackendEnvironments
};

const questions = [
{
name: 'directory',
Expand Down
2 changes: 2 additions & 0 deletions packages/create-pwa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
"inquirer": "^6.3.1",
"is-invalid-path": "^1.0.2",
"is-valid-npm-name": "^0.0.4",
"lodash": "~4.17.11",
"node-fetch": "~2.3.0",
"webpack": "^4.29.5"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should call onFail if backend is inactive 1`] = `
"https://www.magento-backend-2.3.4.com/ is inactive. Please consider using one of these other backends:
[{\\"name\\":\\"2.3.3-venia-cloud\\",\\"description\\":\\"Magento 2.3.3 with Venia sample data installed\\",\\"url\\":\\"https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/\\"}]
"
`;

exports[`should call onFail with a different error message if environments is empty 1`] = `
"https://www.magento-backend-2.3.4.com/ is inactive. Please consider using one of these other backends:
[{\\"name\\":\\"2.3.3-venia-cloud\\",\\"description\\":\\"Magento 2.3.3 with Venia sample data installed\\",\\"url\\":\\"https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/\\"}]
"
`;

exports[`should log warning message in the console 1`] = `
"
venia-sample-backends is a Dev only extension, please remove it from the project's package.json before going to production.
"
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
jest.mock('node-fetch');
const fetch = require('node-fetch');

const { validateSampleBackend } = require('../intercept');

const env = {
MAGENTO_BACKEND_URL: 'https://www.magento-backend-2.3.4.com/'
};
const onFail = jest.fn().mockName('onFail');
const debug = jest.fn().mockName('debug');

const args = { env, onFail, debug };

beforeAll(() => {
console.warn = jest.fn();
});

test('should not call onFail if backend is active', async () => {
fetch.mockResolvedValueOnce({ ok: true });

await validateSampleBackend(args);

expect(onFail).not.toHaveBeenCalled();
});

test('should call onFail if backend is inactive', async () => {
fetch.mockResolvedValueOnce({ ok: false }).mockResolvedValueOnce({
json: jest.fn().mockResolvedValue({
sampleBackends: {
environments: [
{
name: '2.3.3-venia-cloud',
description:
'Magento 2.3.3 with Venia sample data installed',
url:
'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/'
},
{
name: '2.3.4-venia-cloud',
description:
'Magento 2.3.4 with Venia sample data installed',
url: 'https://www.magento-backend-2.3.4.com/'
}
]
}
})
});

await validateSampleBackend(args);

expect(onFail).toHaveBeenCalled();
expect(onFail.mock.calls[0][0]).toMatchSnapshot();
});

test('should call onFail with a different error message if environments is empty', async () => {
fetch.mockResolvedValueOnce({ ok: false }).mockResolvedValueOnce({
json: jest.fn().mockResolvedValue({
sampleBackends: {
environments: []
}
})
});

await validateSampleBackend(args);

expect(onFail).toHaveBeenCalled();
expect(onFail.mock.calls[0][0]).toMatchSnapshot();
});

test('should log warning message in the console', async () => {
await validateSampleBackend(args);

expect(console.warn).toHaveBeenCalled();
expect(console.warn.mock.calls[0][0]).toMatchSnapshot();
});
91 changes: 91 additions & 0 deletions packages/extensions/venia-sample-backends/intercept.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const fetch = require('node-fetch');

const isBackendActive = async (env, debug) => {
try {
const magentoBackend = env.MAGENTO_BACKEND_URL;
const res = await fetch(magentoBackend);

return res.ok;
} catch (err) {
debug(err);

return false;
}
};

const fetchBackends = async debug => {
try {
const res = await fetch(
'https://fvp0esmt8f.execute-api.us-east-1.amazonaws.com/default/getSampleBackends'
);
const { sampleBackends } = await res.json();

return sampleBackends.environments;
} catch (err) {
debug(err);

return [];
}
};

/**
* Validation function to check if the backend being used is one of the sample backends provided
* by PWA Studio. If yes, the function validates if the backend is active. If not, it reports an
* error by calling the onFail function. In the error being reported, it sends the other sample
* backends that the developers can use.
*
* @param {Object} config.env - The ENV provided to the app, usually avaialable through process.ENV
* @param {Function} config.onFail - callback function to call on validation fail
* @param {Function} config.debug - function to log debug messages in console in debug mode
*
* To watch the debug messages, run the command with DEBUG=*runEnvValidators*
*/
const validateSampleBackend = async config => {
console.warn(
"\n venia-sample-backends is a Dev only extension, please remove it from the project's package.json before going to production. \n"
);

const { env, onFail, debug } = config;

const backendIsActive = await isBackendActive(env, debug);

if (!backendIsActive) {
debug(`${env.MAGENTO_BACKEND_URL} is inactive`);

debug('Fetching other backends');

const sampleBackends = await fetchBackends(debug);
const otherBackends = sampleBackends.filter(
({ url }) => url !== env.MAGENTO_BACKEND_URL
);

debug('PWA Studio supports the following backends', sampleBackends);

debug('Reporting backend URL validation failure');
if (otherBackends.length) {
onFail(
`${
env.MAGENTO_BACKEND_URL
} is inactive. Please consider using one of these other backends: \n\n ${JSON.stringify(
otherBackends
)} \n`
);
} else {
onFail(
`${
env.MAGENTO_BACKEND_URL
} is inactive. Please consider using an active backend \n`
);
}
} else {
debug(`${env.MAGENTO_BACKEND_URL} is active`);
}
};

module.exports = targets => {
targets
.of('@magento/pwa-buildpack')
.validateEnv.tapPromise(validateSampleBackend);
};

module.exports.validateSampleBackend = validateSampleBackend;
24 changes: 24 additions & 0 deletions packages/extensions/venia-sample-backends/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@magento/venia-sample-backends",
"version": "0.0.1",
"publishConfig": {
"access": "public"
},
"description": "Provides demo backends and backend validation utils for PWA Studio.",
"main": "./intercept.js",
"scripts": {
"clean": " ",
"test": "jest"
},
"repository": "github:magento/pwa-studio",
"license": "(OSL-3.0 OR AFL-3.0)",
"peerDependencies": {
"@magento/pwa-buildpack": "~7.0.0",
"node-fetch": "~2.3.0"
},
"pwa-studio": {
"targets": {
"intercept": "./intercept"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@
"intercept": "./i18n-intercept"
}
}
}
}
47 changes: 46 additions & 1 deletion packages/pwa-buildpack/lib/BuildBus/declare-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,26 @@ module.exports = targets => {
* @member {tapable.AsyncSeriesHook}
* @param {transformUpwardIntercept} interceptor
*/
transformUpward: new targets.types.AsyncSeries(['definitions'])
transformUpward: new targets.types.AsyncSeries(['definitions']),

/**
* Collect all ENV validation functions that will run against the
* project's ENV. The functions can be async and they will run in
* parallel. If a validation function wants to stop the whole process
* for instance in case of a serious security issue, it can do so
* by throwing an error. If it wants to report an error, it can do so
* by using the onFail callback provided as an argument. A validation
* function can submit multiple errors by calling the onFail function
* multiple times. All the errors will be queued into an array and
* displayed on the console at the end of the process.
*
* @example
* targets.of('@magento/pwa-buildpack').validateEnv.tapPromise(validateBackendUrl);
*
* @member {tapable.AsyncParallelHook}
* @param {envValidationInterceptor} validator
*/
validateEnv: new targets.types.AsyncParallel(['validator'])
};

/**
Expand Down Expand Up @@ -282,3 +301,29 @@ module.exports = targets => {
* @param {object} definition - Parsed UPWARD definition object.
* @returns {Promise}
*/

/** Type definitions related to: validateEnv */

/**
* Intercept function signature for the validateEnv target.
*
* Interceptors of the `validateEnv` target receive a config object.
* The config object contains the project env, an onFail callback and
* the debug function to be used in case of the debug mode to log more
* inforamtion to the console.
*
* This Target can be used asynchronously in the parallel mode. If a
* validator needs to stop the process immediately, it can throw an error.
* If it needs to report an error but not stop the whole process, it can do
* so by calling the onFail function with the error message it wants to report.
* It can call the onFail multiple times if it wants to report multiple errors.
*
* All the errors will be queued and printed into the console at the end of the
* validation process and the build process will be stopeed.
*
* @callback envValidationInterceptor
* @param {Object} config.env - Project ENV
* @param {Function} config.onFail - On fail callback
* @param {Function} config.debug - Debug function to be used for additional reporting in debug mode
* @returns {Boolean}
*/
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@ Array [
],
]
`;

exports[`throws on load if variable defs are invalid 1`] = `
"Bad environment variable definition. Section inscrutable variable {
\\"type\\": \\"ineffable\\"
} declares an unknown type ineffable"
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should throw error if there are validation errors reported by interceptors 1`] = `
"Environment has 2 validation errors:
(1) Danger,
(2) Another error"
`;
Loading

0 comments on commit 936bd9e

Please sign in to comment.