Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support to pull templates from private npm repository #877

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/template.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Examples outputs:

A template is an independent Node.js project unrelated to the `generator` repository. AsyncAPI templates are managed, released, and published separately. You can also create templates and manage templates on your own.

The generator uses the official [Arborist](https://www.npmjs.com/package/@npmcli/arborist) NPM library. (This means templates do not have to be published to package managers to use them.) Arborist helps the generator fetch the template's source code and use it for the generation process.
The generator uses the official [Arborist](https://www.npmjs.com/package/@npmcli/arborist) NPM library. (This means templates do not have to be published to package managers to use them.) Arborist helps the generator fetch the template's source code and use it for the generation process. By default, this library pulls data from the default NPM registry, which is https://registry.npmjs.org. You can also configure the generator to fetch templates that are private or hosted in different NPM registries.

derberg marked this conversation as resolved.
Show resolved Hide resolved
You can store template projects on a local drive or as a `git` repository during the development process.

Expand Down
32 changes: 26 additions & 6 deletions lib/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ const DEFAULT_TEMPLATES_DIR = path.resolve(ROOT_DIR, 'node_modules');

const TRANSPILED_TEMPLATE_LOCATION = '__transpiled';
const TEMPLATE_CONTENT_DIRNAME = 'template';
const GENERATOR_OPTIONS = ['debug', 'disabledHooks', 'entrypoint', 'forceWrite', 'install', 'noOverwriteGlobs', 'output', 'templateParams', 'mapBaseUrlToFolder'];
const GENERATOR_OPTIONS = ['debug', 'disabledHooks', 'entrypoint', 'forceWrite', 'install', 'noOverwriteGlobs', 'output', 'templateParams', 'mapBaseUrlToFolder','registry'];
const REGISTRY_OPTIONS = ['url', 'username', 'password', 'token'];

const logMessage = require('./logMessages');

Expand Down Expand Up @@ -91,10 +92,20 @@ class Generator {
* @param {Boolean} [options.install=false] Install the template and its dependencies, even when the template has already been installed.
* @param {Boolean} [options.debug=false] Enable more specific errors in the console. At the moment it only shows specific errors about filters. Keep in mind that as a result errors about template are less descriptive.
* @param {Object<String, String>} [options.mapBaseUrlToFolder] Optional parameter to map schema references from a base url to a local base folder e.g. url=https://schema.example.com/crm/ folder=./test/docs/ .
* @param {Object} [options.registry] Optional parameter with private registry configuration
* @param {String} [options.registry.url] Parameter to pass npm registry url
* @param {String} [options.registry.username] Optional parameter to pass npm registry username
* @param {String} [options.registry.password] Optional parameter to pass npm registry base64 encoded password
* @param {String} [options.registry.token] Optional parameter to pass npm registry auth token
*/
constructor(templateName, targetDir, { templateParams = {}, entrypoint, noOverwriteGlobs, disabledHooks, output = 'fs', forceWrite = false, install = false, debug = false, mapBaseUrlToFolder = {} } = {}) {
const invalidOptions = getInvalidOptions(GENERATOR_OPTIONS, arguments[arguments.length - 1] || []);
constructor(templateName, targetDir, { templateParams = {}, entrypoint, noOverwriteGlobs, disabledHooks, output = 'fs', forceWrite = false, install = false, debug = false, mapBaseUrlToFolder = {}, registry = {url: '', username: '', password: '', token: '' }} = {}) {
const options = arguments[arguments.length - 1];
const invalidOptions = getInvalidOptions(GENERATOR_OPTIONS, options || []);
if (invalidOptions.length) throw new Error(`These options are not supported by the generator: ${invalidOptions.join(', ')}`);
if (options && options.registry) {
const invalidRegOptions = getInvalidOptions(REGISTRY_OPTIONS, options.registry || []);
if (invalidRegOptions.length) throw new Error(`There invalid parameters were specified to configure private registry: ${invalidRegOptions.join(', ')}`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (invalidRegOptions.length) throw new Error(`There invalid parameters were specified to configure private registry: ${invalidRegOptions.join(', ')}`);
if (invalidRegOptions.length) throw new Error(`These options are not supported by the generator to configure private registry: ${invalidRegOptions.join(', ')}`);

}
if (!templateName) throw new Error('No template name has been specified.');
if (!entrypoint && !targetDir) throw new Error('No target directory has been specified.');
if (!['fs', 'string'].includes(output)) throw new Error(`Invalid output type ${output}. Valid values are 'fs' and 'string'.`);
Expand Down Expand Up @@ -123,6 +134,8 @@ class Generator {
this.hooks = {};
/** @type {Object} Maps schema URL to folder. */
this.mapBaseUrlToFolder = mapBaseUrlToFolder;
/** @type {Object} Npm registry information. */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for better jsdoc experience can you please:

  • add above class type definition for the registry option like https://github.com/asyncapi/parser-js/blob/master/lib/parser.js#L46-L53
  • If you call this type RegistryOptions then /** @type {Object} Npm registry information. */ will have to be /** @type {RegistryOptions} Npm registry information. */. If I'm not mistaken as a result we will get a nice API docs generated
  • I think that once you add typedef as described above, then you can also reuse it in jsdoc for the constructor, so you do not have to repeat all these options and descriptions as they are there now

this.registry = registry;

// Load template configuration
/** @type {Object} The template parameters. The structure for this object is based on each individual template. */
Expand Down Expand Up @@ -385,9 +398,16 @@ class Generator {

if (isFileSystemPath(this.templateName)) log.debug(logMessage.NPM_INSTALL_TRIGGER);

const arb = new Arborist({
path: ROOT_DIR
});
const arbOptions = {
path: ROOT_DIR,
};
if (this.registry) {
if (this.registry.url) arbOptions.registry = this.registry.url;
if (this.registry.username) arbOptions.username = this.registry.username;
if (this.registry.password) arbOptions.password = this.registry.password;
if (this.registry.token) arbOptions.token = this.registry.token;
}
const arb = new Arborist(arbOptions);

try {
const installResult = await arb.reify({
Expand Down
22 changes: 21 additions & 1 deletion test/generator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ describe('Generator', () => {
const utils = require('../lib/utils');
const asyncapiURL = 'http://example.com/fake-asyncapi.yml';
utils.__contentOfFetchedFile = 'fake text';

const generateFromStringMock = jest.fn().mockResolvedValue();
const gen = new Generator('testTemplate', __dirname);
gen.generateFromString = generateFromStringMock;
Expand Down Expand Up @@ -410,6 +410,26 @@ describe('Generator', () => {
}, 0);
});

it('works with a path to registry', async () => {
log.debug = jest.fn();
utils.__getTemplateDetails = undefined;
const gen = new Generator('nameOfTestTemplate', __dirname, {debug: true, registry: {url: 'some.registry.com', username: 'user', password: 'password', token: 'token'}});
await gen.installTemplate();
setTimeout(() => { // This puts the call at the end of the Node.js event loop queue.
expect(arboristMock.reify).toHaveBeenCalledTimes(1);
}, 0);
});

it('throws an error indicating an unexpected param was given for registry configuration', () => {
const t = () => new Generator('testTemplate', __dirname, {
registry: {
url: 'some.url.com',
privateKey: 'some.key'
}
});
expect(t).toThrow('There invalid parameters were specified to configure private registry: privateKey');
});

it('works with a url and force = true', async () => {
const gen = new Generator('https://my-test-template.com', __dirname);
await gen.installTemplate(true);
Expand Down