Skip to content

Commit

Permalink
feat: enable proxy support in the cli (#1608)
Browse files Browse the repository at this point in the history
  • Loading branch information
AayushSaini101 authored Jan 5, 2025
1 parent 492119c commit 430898d
Show file tree
Hide file tree
Showing 11 changed files with 3,110 additions and 2,833 deletions.
5,782 changes: 2,976 additions & 2,806 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -172,4 +173,4 @@
"action:test": "cd github-action && make test"
},
"types": "lib/index.d.ts"
}
}
18 changes: 15 additions & 3 deletions src/commands/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;

Expand Down
35 changes: 25 additions & 10 deletions src/commands/optimize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +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;

Expand Down Expand Up @@ -42,27 +43,41 @@ 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) {
this.error(
new ValidationError({
type: 'invalid-file',
filepath: filePath,
})
);
} catch (err:any) {
if (err.message.includes('Failed to download')) {
throw new Error('Proxy Connection Error: Unable to establish a connection to the proxy check hostName or PortNumber.');
} else {
this.error(
new ValidationError({
type: 'invalid-file',
filepath: filePath,
})
);
}
}

let optimizer: Optimizer;
Expand Down
21 changes: 15 additions & 6 deletions src/commands/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
15 changes: 15 additions & 0 deletions src/core/flags/proxy.flags.ts
Original file line number Diff line number Diff line change
@@ -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
})
};
};

40 changes: 34 additions & 6 deletions src/core/models/SpecificationFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -87,15 +87,43 @@ 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' });
if (!response.ok) {
throw new ErrorLoadingSpec('url', URLpath);
// 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;
response = await fetch(targetUrl,fetchOptions);
} catch (err: any) {
throw new Error('Proxy Connection Error: Unable to establish a connection to the proxy check hostName or PortNumber');
}
} else {
response = await fetch(targetUrl);
if (!response.ok) {
throw new ErrorLoadingSpec('url', targetUrl);
}
}
} catch (error) {
throw new ErrorLoadingSpec('url', URLpath);
console.log(error);
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 });
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/newtemplate/__transpiled/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions test/integration/convert.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ describe('convert', () => {
expect(ctx.stderr).to.equal('');
done();
});
test
.stderr()
.stdout()
.command(['convert', 'http://localhost:8080/dummySpec.yml --proxyHost=host --proxyPort=8080'])
.it('should throw error when url is passed with proxyHost and proxyPort with invalid host ', (ctx, done) => {
expect(ctx.stdout).to.contain('');
expect(ctx.stderr).to.equal('error loading AsyncAPI document from url: Failed to download http://localhost:8080/dummySpec.yml --proxyHost=host --proxyPort=8080.\n');
done();
});
});

describe('with no arguments', () => {
Expand Down
9 changes: 9 additions & 0 deletions test/integration/optimize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ describe('optimize', () => {
expect(ctx.stderr).to.equal('');
done();
});
test
.stderr()
.stdout()
.command(['optimize', 'http://localhost:8080/dummySpec.yml --proxyHost=host --proxyPort=8080'])
.it('should throw error when url is passed with proxyHost and proxyPort with invalid host ', (ctx, done) => {
expect(ctx.stdout).to.contain('');
expect(ctx.stderr).to.equal('Error: Proxy Connection Error: Unable to establish a connection to the proxy check hostName or PortNumber.\n');
done();
});
});

describe('with no arguments', () => {
Expand Down
9 changes: 9 additions & 0 deletions test/integration/validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ describe('validate', () => {
expect(ctx.stderr).to.equal('');
done();
});
test
.stderr()
.stdout()
.command(['validate', 'http://localhost:8080/dummySpec.yml --proxyHost=host --proxyPort=8080'])
.it('should throw error when url is passed with proxyHost and proxyPort with invalid host ', (ctx, done) => {
expect(ctx.stdout).to.contain('');
expect(ctx.stderr).to.equal('error loading AsyncAPI document from url: Failed to download http://localhost:8080/dummySpec.yml --proxyHost=host --proxyPort=8080.\n');
done();
});

test
.stderr()
Expand Down

0 comments on commit 430898d

Please sign in to comment.