From 5c99587f9bd0981054a712d6c3477d413321ebeb Mon Sep 17 00:00:00 2001 From: Zack_Aayush <60972989+AayushSaini101@users.noreply.github.com> Date: Mon, 13 May 2024 20:05:45 +0530 Subject: [PATCH] chore: support cli to fetch private templates (#1399) Co-authored-by: asyncapi-bot --- docs/usage.md | 3 +++ package-lock.json | 36 +++++++++++++++++++++++++- src/commands/generate/fromTemplate.ts | 37 ++++++++++++++++++++++++--- 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 69d456c5d90..d0f24dc7447 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -422,6 +422,9 @@ FLAGS unstaged files or not empty dir (defaults to false) --map-base-url= Maps all schema references from base url to local folder --no-interactive Disable interactive mode and run with the provided flags. + --registry-url Specifies the URL of the private registry for fetching templates and dependencies + --registry-auth The registry username and password encoded with base64, formatted as username:password + --registry-token The npm registry authentication token, that can be passed instead of base64 encoded username and password DESCRIPTION Generates whatever you want using templates compatible with AsyncAPI Generator. diff --git a/package-lock.json b/package-lock.json index d538fe61469..6b393eb3154 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "@smoya/multi-parser": "^5.0.7", "@stoplight/spectral-cli": "6.9.0", "ajv": "^8.12.0", + "axios": "^1.6.8", "chalk": "^4.1.0", "chokidar": "^3.5.2", "fast-levenshtein": "^3.0.0", @@ -38757,6 +38758,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -42696,6 +42707,25 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -42758,7 +42788,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -65134,6 +65163,11 @@ "undici-types": "~5.26.4" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", diff --git a/src/commands/generate/fromTemplate.ts b/src/commands/generate/fromTemplate.ts index 80eb592745e..eb216bcad73 100644 --- a/src/commands/generate/fromTemplate.ts +++ b/src/commands/generate/fromTemplate.ts @@ -15,6 +15,7 @@ import { Parser } from '@asyncapi/parser'; import type { Example } from '@oclif/core/lib/interfaces'; import { intro, isCancel, spinner, text } from '@clack/prompts'; import { inverse, yellow, magenta, green, red } from 'picocolors'; +import fetch from 'node-fetch'; interface IMapBaseUrlToFlag { url: string, @@ -100,8 +101,17 @@ export default class Template extends Command { 'map-base-url': Flags.string({ description: 'Maps all schema references from base url to local folder' }), + 'registry-url': Flags.string({ + default: 'https://registry.npmjs.org', + description: 'Specifies the URL of the private registry for fetching templates and dependencies' + }), + 'registry-auth': Flags.string({ + description: 'The registry username and password encoded with base64, formatted as username:password' + }), + 'registry-token': Flags.string({ + description: 'The npm registry authentication token, that can be passed instead of base64 encoded username and password' + }) }; - static args = [ { name: 'asyncapi', description: '- Local path, url or context-name pointing to AsyncAPI file' }, { name: 'template', description: '- Name of the generator template like for example @asyncapi/html-template or https://github.com/asyncapi/html-template' }, @@ -124,7 +134,7 @@ export default class Template extends Command { output = parsedArgs.output; } - const parsedFlags = this.parseFlags(flags['disable-hook'], flags['param'], flags['map-base-url']); + const parsedFlags = this.parseFlags(flags['disable-hook'], flags['param'], flags['map-base-url'],flags['registry.url'],flags['registry.auth'],flags['registry.token']); const options = { forceWrite: flags['force-write'], install: flags.install, @@ -133,6 +143,11 @@ export default class Template extends Command { noOverwriteGlobs: flags['no-overwrite'], mapBaseUrlToFolder: parsedFlags.mapBaseUrlToFolder, disabledHooks: parsedFlags.disableHooks, + registry: { + url: flags['registry-url'], + auth: flags['registry-auth'], + token: flags['registry-token'] + } }; const asyncapiInput = (await load(asyncapi)) || (await load()); @@ -211,14 +226,30 @@ export default class Template extends Command { return { asyncapi, template, output }; } - private parseFlags(disableHooks?: string[], params?: string[], mapBaseUrl?: string): ParsedFlags { + private parseFlags(disableHooks?: string[], params?: string[], mapBaseUrl?: string, registryUrl?: string, registryAuth?:string, registryToken?:string): ParsedFlags { return { params: this.paramParser(params), disableHooks: this.disableHooksParser(disableHooks), mapBaseUrlToFolder: this.mapBaseURLParser(mapBaseUrl), + registryURLValidation: this.registryURLParser(registryUrl), + registryAuthentication: this.registryValidation(registryUrl, registryAuth, registryToken) + } as ParsedFlags; } + private registryURLParser(input?:string) { + if (!input) { return; } + const isURL = /^https?:/; + if (!isURL.test(input.toLowerCase())) { + throw new Error('Invalid --registry-url flag. The param requires a valid http/https url.'); + } + } + private async registryValidation(registryUrl?:string, registryAuth?:string, registryToken?:string) { + const response= await fetch(registryUrl as string); + if (response.status === 401&&!registryAuth&&!registryToken) { + throw new Error('Need to pass either registryAuth in username:password encoded in Base64 or need to pass registryToken'); + } + } private paramParser(inputs?: string[]) { if (!inputs) { return {}; } const params: Record = {};