diff --git a/package-lock.json b/package-lock.json index 70f3758136c..e8aecc4268a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@asyncapi/cli", - "version": "2.13.0", + "version": "2.14.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@asyncapi/cli", - "version": "2.13.0", + "version": "2.14.0", "license": "Apache-2.0", "dependencies": { "@asyncapi/avro-schema-parser": "^3.0.23", @@ -30,6 +30,7 @@ "fast-levenshtein": "^3.0.0", "fs-extra": "^11.1.0", "generator-v2": "npm:@asyncapi/generator@^2.4.1", + "https-proxy-agent": "^7.0.6", "inquirer": "^8.2.0", "js-yaml": "^4.1.0", "lodash.template": "^4.4.0", @@ -14121,16 +14122,25 @@ } }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "dependencies": { - "agent-base": "6", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "engines": { + "node": ">= 14" } }, "node_modules/humanize-ms": { @@ -15337,6 +15347,19 @@ "node": ">= 6" } }, + "node_modules/jsdom/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/jsdom/node_modules/ws": { "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", @@ -16013,6 +16036,19 @@ "node": ">= 6" } }, + "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/make-fetch-happen/node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", diff --git a/package.json b/package.json index 133d9799deb..2e21441b03e 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "fast-levenshtein": "^3.0.0", "fs-extra": "^11.1.0", "generator-v2": "npm:@asyncapi/generator@^2.4.1", + "https-proxy-agent": "^7.0.6", "inquirer": "^8.2.0", "js-yaml": "^4.1.0", "lodash.template": "^4.4.0", @@ -172,4 +173,4 @@ "action:test": "cd github-action && make test" }, "types": "lib/index.d.ts" -} \ No newline at end of file +} diff --git a/src/commands/convert.ts b/src/commands/convert.ts index 499f594d700..765f6d2605b 100644 --- a/src/commands/convert.ts +++ b/src/commands/convert.ts @@ -8,7 +8,7 @@ import { SpecificationFileNotFound } from '../core/errors/specification-file'; import { convert, convertOpenAPI, convertPostman } from '@asyncapi/converter'; import type { AsyncAPIConvertVersion, OpenAPIConvertVersion } from '@asyncapi/converter'; import { cyan, green } from 'picocolors'; - +import { proxyFlags } from '../core/flags/proxy.flags'; // @ts-ignore import specs from '@asyncapi/specs'; import { convertFlags } from '../core/flags/convert.flags'; @@ -20,15 +20,27 @@ export default class Convert extends Command { static metricsMetadata: any = {}; static description = 'Convert asyncapi documents older to newer versions or OpenAPI/postman-collection documents to AsyncAPI'; - static flags = convertFlags(latestVersion); + static flags = { + ...convertFlags(latestVersion), + ...proxyFlags() + + }; static args = { 'spec-file': Args.string({description: 'spec path, url, or context-name', required: false}), + proxyHost: Args.string({description: 'Name of the Proxy Host', required: false}), + proxyPort: Args.string({description: 'Name of the Port of the ProxyHost', required: false}), }; async run() { const { args, flags } = await this.parse(Convert); - const filePath = args['spec-file']; + let filePath = args['spec-file']; + const proxyHost = flags['proxyHost']; + const proxyPort = flags['proxyPort']; + if (proxyHost && proxyPort) { + const proxyUrl = `http://${proxyHost}:${proxyPort}`; + filePath = `${filePath}+${proxyUrl}`; // Update filePath with proxyUrl + } let convertedFile; let convertedFileFormatted; diff --git a/src/commands/optimize.ts b/src/commands/optimize.ts index 4607ab3ec2c..9f512ae4ccc 100644 --- a/src/commands/optimize.ts +++ b/src/commands/optimize.ts @@ -8,7 +8,7 @@ import chalk from 'chalk'; import { promises } from 'fs'; import { Parser } from '@asyncapi/parser'; import { optimizeFlags } from '../core/flags/optimize.flags'; - +import { proxyFlags } from '../core/flags/proxy.flags'; const { writeFile } = promises; export enum Optimizations { @@ -42,18 +42,28 @@ export default class Optimize extends Command { 'asyncapi optimize ./asyncapi.yaml --ignore=schema' ]; - static flags = optimizeFlags(); + static flags = { + ...optimizeFlags(), + ...proxyFlags(), + }; static args = { 'spec-file': Args.string({description: 'spec path, url, or context-name', required: false}), + proxyHost: Args.string({description: 'Name of the Proxy Host', required: false}), + proxyPort: Args.string({description: 'Name of the Port of the ProxyHost', required: false}), }; parser = new Parser(); async run() { const { args, flags } = await this.parse(Optimize); //NOSONAR - const filePath = args['spec-file']; - + let filePath = args['spec-file']; + const proxyHost = flags['proxyHost']; + const proxyPort = flags['proxyPort']; + if (proxyHost && proxyPort) { + const proxyUrl = `http://${proxyHost}:${proxyPort}`; + filePath = `${filePath}+${proxyUrl}`; // Update filePath with proxyUrl + } try { this.specFile = await load(filePath); } catch (err) { diff --git a/src/commands/validate.ts b/src/commands/validate.ts index 6ee932eb162..6cd4c23be1b 100644 --- a/src/commands/validate.ts +++ b/src/commands/validate.ts @@ -4,32 +4,41 @@ import { validate, ValidateOptions, ValidationStatus, parse } from '../core/pars import { load } from '../core/models/SpecificationFile'; import { specWatcher } from '../core/globals'; import { validateFlags } from '../core/flags/validate.flags'; +import { proxyFlags } from '../core/flags/proxy.flags'; import { calculateScore } from '../core/utils/scoreCalculator'; export default class Validate extends Command { static description = 'validate asyncapi file'; - static flags = validateFlags(); + static flags = { + ...validateFlags(), + ...proxyFlags(), // Merge proxyFlags with validateFlags + }; static args = { 'spec-file': Args.string({description: 'spec path, url, or context-name', required: false}), + proxyHost: Args.string({description: 'Name of the Proxy Host', required: false}), + proxyPort: Args.string({description: 'Name of the Port of the ProxyHost', required: false}), }; async run() { const { args, flags } = await this.parse(Validate); //NOSONAR - - const filePath = args['spec-file']; + let filePath = args['spec-file']; + const proxyHost = flags['proxyHost']; + const proxyPort = flags['proxyPort']; + if (proxyHost && proxyPort) { + const proxyUrl = `http://${proxyHost}:${proxyPort}`; + filePath = `${filePath}+${proxyUrl}`; // Update filePath with proxyUrl + } + this.specFile = await load(filePath); const watchMode = flags.watch; if (flags['score']) { - this.specFile = await load(filePath); const { document } = await parse(this,this.specFile); this.log(`The score of the asyncapi document is ${await calculateScore(document)}`); } - this.specFile = await load(filePath); if (watchMode) { specWatcher({ spec: this.specFile, handler: this, handlerName: 'validate' }); } - const result = await validate(this, this.specFile, flags as ValidateOptions); this.metricsMetadata.validation_result = result; diff --git a/src/core/flags/proxy.flags.ts b/src/core/flags/proxy.flags.ts new file mode 100644 index 00000000000..6147a6d20da --- /dev/null +++ b/src/core/flags/proxy.flags.ts @@ -0,0 +1,15 @@ +import { Flags } from '@oclif/core'; + +export const proxyFlags = () => { + return { + proxyHost: Flags.string({ + description: 'Name of the ProxyHost', + required: false + }), + proxyPort: Flags.string({ + description: 'Port number number for the proxyHost.', + required: false + }) + }; +}; + diff --git a/src/core/models/SpecificationFile.ts b/src/core/models/SpecificationFile.ts index be3681e6158..f8e2b26a152 100644 --- a/src/core/models/SpecificationFile.ts +++ b/src/core/models/SpecificationFile.ts @@ -7,7 +7,7 @@ import { loadContext } from './Context'; import { ErrorLoadingSpec } from '../errors/specification-file'; import { MissingContextFileError } from '../errors/context-error'; import { fileFormat } from 'core/flags/format.flags'; - +import { HttpsProxyAgent } from 'https-proxy-agent'; const { readFile, lstat } = fs; const allowedFileNames: string[] = [ 'asyncapi.json', @@ -87,15 +87,42 @@ export class Specification { static async fromURL(URLpath: string) { let response; + const delimiter = '+'; + let targetUrl = URLpath; + let proxyUrl = ''; + + // Check if URLpath contains a proxy URL + if (URLpath.includes(delimiter)) { + [targetUrl, proxyUrl] = URLpath.split(delimiter); + } + try { - response = await fetch(URLpath, { method: 'GET' }); + // Validate the target URL + new URL(targetUrl); + + const fetchOptions: any = { method: 'GET' }; + + // If proxy URL is provided, create a proxy agent + if (proxyUrl) { + try { + new URL(proxyUrl); + const proxyAgent = new HttpsProxyAgent(proxyUrl,); + fetchOptions.agent = proxyAgent; + } catch (error) { + throw new Error(`Invalid proxy URL: ${proxyUrl}`); + } + } + + // Fetch the target URL + response = await fetch(targetUrl, fetchOptions); if (!response.ok) { - throw new ErrorLoadingSpec('url', URLpath); + throw new ErrorLoadingSpec('url', targetUrl); } } catch (error) { - throw new ErrorLoadingSpec('url', URLpath); + throw new ErrorLoadingSpec('url', targetUrl); } - return new Specification(await response?.text() as string, { fileURL: URLpath }); + + return new Specification(await response?.text() as string, { fileURL: targetUrl }); } }