Skip to content

Commit 42464a6

Browse files
authored
feat(axios-client-adapter): add support for configurable proxy (#257)
1 parent e1f4d2e commit 42464a6

22 files changed

Lines changed: 455 additions & 39 deletions

.github/workflows/main.yml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,3 @@ jobs:
3434

3535
- name: Code formatting
3636
run: yarn check-style
37-
38-
- name: SonarQube Scan
39-
if: ${{ github.actor != 'dependabot[bot]' }}
40-
uses: SonarSource/sonarqube-scan-action@v5.2.0
41-
env:
42-
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
43-
with:
44-
projectBaseDir: .
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: SonarQube Scan
2+
on:
3+
push:
4+
branches: [master]
5+
pull_request:
6+
branches: [master]
7+
8+
jobs:
9+
check:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v2
13+
with:
14+
fetch-depth: 0
15+
16+
- uses: actions/setup-node@v1
17+
with:
18+
node-version: "18.x"
19+
20+
- name: Install deps
21+
run: yarn
22+
23+
- name: Build
24+
run: yarn build
25+
26+
- name: Test
27+
run: yarn test
28+
29+
- name: SonarQube Scan
30+
if: ${{ github.actor != 'dependabot[bot]' }}
31+
uses: SonarSource/sonarqube-scan-action@v5.2.0
32+
env:
33+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
34+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

README.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,22 @@ They provide common runtime utilities needed by SDKs to make API calls and handl
1111

1212
## List of Packages
1313

14-
| Name | Version | Description |
15-
|-----------------------------------------------------------------------| --- | --- |
16-
| [@apimatic/schema](packages/schema) | [![npm shield](https://img.shields.io/npm/v/@apimatic/schema)](https://www.npmjs.com/package/@apimatic/schema) | Validate and transform data using schema definitions. |
14+
| Name | Version | Description |
15+
|-----------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| --- |
16+
| [@apimatic/schema](packages/schema) | [![npm shield](https://img.shields.io/npm/v/@apimatic/schema)](https://www.npmjs.com/package/@apimatic/schema) | Validate and transform data using schema definitions. |
1717
| [@apimatic/authentication-adapters](packages/authentication-adapters) | [![npm shield](https://img.shields.io/npm/v/@apimatic/authentication-adapters)](https://www.npmjs.com/package/@apimatic/authentication-adapters) | Provides pluggable adapters for different authentication schemes. |
18-
| [@apimatic/axios-client-adapter](packages/axios-client-adapter) | [![npm shield](https://img.shields.io/npm/v/@apimatic/axios-client-adapter)](https://www.npmjs.com/package/@apimatic/axios-client-adapter) | Axios HTTP Client adapter which can be plugged into @apimatic/core package. |
19-
| [@apimatic/convert-to-stream](packages/convert-to-stream) | [![npm shield](https://img.shields.io/npm/v/@apimatic/convert-to-stream)](https://www.npmjs.com/package/@apimatic/convert-to-stream) | Converts data into streams/blobs. |
20-
| [@apimatic/core](packages/core) | [![npm shield](https://img.shields.io/npm/v/@apimatic/core)](https://www.npmjs.com/package/@apimatic/core) | Provides core http logic of request building, response handling and validation and using api error classes |
21-
| [@apimatic/core-interfaces](packages/core-interfaces) | [![npm shield](https://img.shields.io/npm/v/@apimatic/core-interfaces)](https://www.npmjs.com/package/@apimatic/core-interfaces) | Abstraction layer for @apimatic/core. |
22-
| [@apimatic/file-wrapper](packages/file-wrapper) | [![npm shield](https://img.shields.io/npm/v/@apimatic/file-wrapper)](https://www.npmjs.com/package/@apimatic/file-wrapper) | Wrapper around the file types and their utilities. |
23-
| [@apimatic/http-headers](packages/http-headers) | [![npm shield](https://img.shields.io/npm/v/@apimatic/http-headers)](https://www.npmjs.com/package/@apimatic/http-headers) | HTTP Headers utilities for apimatic-js-runtime libraries. |
24-
| [@apimatic/http-query](packages/http-query) | [![npm shield](https://img.shields.io/npm/v/@apimatic/http-query)](https://www.npmjs.com/package/@apimatic/http-query) | HTTP Query utilities for apimatic-js-runtime libraries |
25-
| [@apimatic/oauth-adapters](packages/oauth-adapters) | [![npm shield](https://img.shields.io/npm/v/@apimatic/oauth-adapters)](https://www.npmjs.com/package/@apimatic/oauth-adapters) | Provides pluggable adapters for OAuth 2.0 authentication schemes. |
26-
| [@apimatic/xml-adapter](packages/xml-adapter) | [![npm shield](https://img.shields.io/npm/v/@apimatic/xml-adapter)](https://www.npmjs.com/package/@apimatic/xml-adapter) | Provides XML serialization and deserialization utilities for apimatic-js-runtime libraries. |
27-
| [@apimatic/test-utilities](packages/test-utilities) | [![npm shield](https://img.shields.io/npm/v/@apimatic/test-utilities)](https://www.npmjs.com/package/@apimatic/test-utilities) | Provides assertion utilities for testing api calls. It can be plugged in as dev dependency to any library. |
28-
| [@apimatic/pagination](packages/pagination) | [![npm shield](https://img.shields.io/npm/v/@apimatic/pagination)](https://www.npmjs.com/package/@apimatic/pagination) | Provides utilities to handle paginated API responses, including support for asynchronous iteration over pages or items. |
18+
| [@apimatic/axios-client-adapter](packages/axios-client-adapter) | [![npm shield](https://img.shields.io/npm/v/@apimatic/axios-client-adapter)](https://www.npmjs.com/package/@apimatic/axios-client-adapter) | Axios HTTP Client adapter which can be plugged into @apimatic/core package. |
19+
| [@apimatic/convert-to-stream](packages/convert-to-stream) | [![npm shield](https://img.shields.io/npm/v/@apimatic/convert-to-stream)](https://www.npmjs.com/package/@apimatic/convert-to-stream) | Converts data into streams/blobs. |
20+
| [@apimatic/core](packages/core) | [![npm shield](https://img.shields.io/npm/v/@apimatic/core)](https://www.npmjs.com/package/@apimatic/core) | Provides core http logic of request building, response handling and validation and using api error classes |
21+
| [@apimatic/core-interfaces](packages/core-interfaces) | [![npm shield](https://img.shields.io/npm/v/@apimatic/core-interfaces)](https://www.npmjs.com/package/@apimatic/core-interfaces) | Abstraction layer for @apimatic/core. |
22+
| [@apimatic/file-wrapper](packages/file-wrapper) | [![npm shield](https://img.shields.io/npm/v/@apimatic/file-wrapper)](https://www.npmjs.com/package/@apimatic/file-wrapper) | Wrapper around the file types and their utilities. |
23+
| [@apimatic/http-headers](packages/http-headers) | [![npm shield](https://img.shields.io/npm/v/@apimatic/http-headers)](https://www.npmjs.com/package/@apimatic/http-headers) | HTTP Headers utilities for apimatic-js-runtime libraries. |
24+
| [@apimatic/http-query](packages/http-query) | [![npm shield](https://img.shields.io/npm/v/@apimatic/http-query)](https://www.npmjs.com/package/@apimatic/http-query) | HTTP Query utilities for apimatic-js-runtime libraries |
25+
| [@apimatic/oauth-adapters](packages/oauth-adapters) | [![npm shield](https://img.shields.io/npm/v/@apimatic/oauth-adapters)](https://www.npmjs.com/package/@apimatic/oauth-adapters) | Provides pluggable adapters for OAuth 2.0 authentication schemes. |
26+
| [@apimatic/xml-adapter](packages/xml-adapter) | [![npm shield](https://img.shields.io/npm/v/@apimatic/xml-adapter)](https://www.npmjs.com/package/@apimatic/xml-adapter) | Provides XML serialization and deserialization utilities for apimatic-js-runtime libraries. |
27+
| [@apimatic/test-utilities](packages/test-utilities) | [![npm shield](https://img.shields.io/npm/v/@apimatic/test-utilities)](https://www.npmjs.com/package/@apimatic/test-utilities) | Provides assertion utilities for testing api calls. It can be plugged in as dev dependency to any library. |
28+
| [@apimatic/pagination](packages/pagination) | [![npm shield](https://img.shields.io/npm/v/@apimatic/pagination)](https://www.npmjs.com/package/@apimatic/pagination) | Provides utilities to handle paginated API responses, including support for asynchronous iteration over pages or items. |
29+
| [@apimatic/proxy](packages/proxy) | [![npm shield](https://img.shields.io/npm/v/@apimatic/proxy)](https://www.npmjs.com/package/@apimatic/proxy) | Provides proxy configuration utilities for HTTP clients. |
2930

3031

3132
[ci-badge]: https://github.com/apimatic/apimatic-js-runtime/actions/workflows/main.yml/badge.svg

packages/axios-client-adapter/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"@apimatic/http-headers": "^0.3.6",
6565
"@apimatic/http-query": "^0.3.6",
6666
"@apimatic/json-bigint": "^1.2.0",
67+
"@apimatic/proxy": "^0.1.0",
6768
"@apimatic/schema": "^0.7.17",
6869
"axios": "^1.8.4",
6970
"detect-browser": "^5.3.0",

packages/axios-client-adapter/src/httpClient.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
} from '@apimatic/core-interfaces';
2121
import { urlEncodeKeyValuePairs } from '@apimatic/http-query';
2222
import { isFileWrapper } from '@apimatic/file-wrapper';
23+
import { createProxyAgents } from '@apimatic/proxy';
24+
import { ProxySettings } from '.';
2325

2426
export const DEFAULT_AXIOS_CONFIG_OVERRIDES: AxiosRequestConfig = {
2527
transformResponse: [],
@@ -36,6 +38,7 @@ export class HttpClient {
3638
private _axiosInstance: AxiosInstance;
3739
private _timeout: number;
3840
private _abortErrorFactory: AbortErrorConstructor;
41+
private readonly _proxySettings?: ProxySettings;
3942

4043
constructor(
4144
abortErrorFactory: AbortErrorConstructor,
@@ -44,13 +47,16 @@ export class HttpClient {
4447
timeout = DEFAULT_TIMEOUT,
4548
httpAgent,
4649
httpsAgent,
50+
proxySettings,
4751
}: {
4852
clientConfigOverrides?: AxiosRequestConfig;
4953
timeout?: number;
5054
httpAgent?: any;
5155
httpsAgent?: any;
56+
proxySettings?: ProxySettings;
5257
} = {}
5358
) {
59+
this._proxySettings = proxySettings;
5460
this._timeout = timeout;
5561
this._axiosInstance = axios.create({
5662
...DEFAULT_AXIOS_CONFIG_OVERRIDES,
@@ -143,6 +149,8 @@ export class HttpClient {
143149
// set headers
144150
newRequest.headers = headers;
145151

152+
this.setProxyAgent(newRequest);
153+
146154
return newRequest;
147155
}
148156

@@ -213,6 +221,21 @@ export class HttpClient {
213221
}
214222
}
215223

224+
private setProxyAgent(axiosRequest: AxiosRequestConfig): void {
225+
if (!this._proxySettings || !axiosRequest.url) {
226+
return;
227+
}
228+
const proxyAgents = createProxyAgents(this._proxySettings);
229+
230+
const protocol = new URL(axiosRequest.url).protocol;
231+
232+
if (protocol === 'https:') {
233+
axiosRequest.httpsAgent = proxyAgents?.httpsAgent;
234+
} else if (protocol === 'http:') {
235+
axiosRequest.httpAgent = proxyAgents?.httpAgent;
236+
}
237+
}
238+
216239
private abortError() {
217240
return new this._abortErrorFactory('The HTTP call was aborted.');
218241
}
@@ -226,6 +249,8 @@ export interface HttpClientOptions {
226249
httpAgent?: any;
227250
/** Custom https agent to be used when performing https requests. */
228251
httpsAgent?: any;
252+
/** Proxy configuration to route requests through a proxy server. */
253+
proxySettings?: ProxySettings;
229254
/** Configurations to retry requests */
230255
retryConfig: Partial<RetryConfiguration>;
231256
}

packages/axios-client-adapter/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
/// <reference path='./shim/index.ts' />
33

44
export * from './httpClient';
5+
export type { ProxySettings } from '@apimatic/proxy';

packages/axios-client-adapter/test/abortError.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.

packages/axios-client-adapter/test/httpClient.test.ts

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,19 @@ import {
1616
import { FileWrapper } from '@apimatic/file-wrapper';
1717
import FormData from 'form-data';
1818
import fs from 'fs';
19-
import { AbortError } from './abortError';
19+
20+
/**
21+
* Thrown when the API call is aborted by the caller.
22+
*
23+
* Note that when an AbortError is thrown, it is not a guarantee that the API call
24+
* did not go through.
25+
*/
26+
class AbortError extends Error {
27+
constructor(message?: string) {
28+
super(message);
29+
Object.setPrototypeOf(this, new.target.prototype);
30+
}
31+
}
2032

2133
describe('HTTP Client', () => {
2234
it('converts request with http text body and http get method', () => {
@@ -239,6 +251,65 @@ describe('HTTP Client', () => {
239251
const httpResponse = httpClient.convertHttpResponse(response);
240252
expect(httpResponse).toMatchObject(expectedHttpResponse);
241253
});
254+
255+
const proxySettings = {
256+
address: 'http://proxy.example.com',
257+
port: 8080,
258+
auth: {
259+
username: 'user',
260+
password: 'pass',
261+
},
262+
};
263+
const httpRequest: HttpRequest = {
264+
method: 'GET',
265+
headers: { 'test-header': 'test-value' },
266+
body: {
267+
content: 'testBody',
268+
type: 'text',
269+
},
270+
url: '',
271+
responseType: 'text',
272+
auth: { username: 'test-username', password: 'test-password' },
273+
};
274+
const expectedProxyConfig = {
275+
protocol: 'http:',
276+
username: 'user',
277+
password: 'pass',
278+
host: 'proxy.example.com:8080',
279+
port: '8080',
280+
};
281+
282+
it('adds httpAgent to axios request config for http URLs when proxySettings are provided', () => {
283+
const httpClient = new HttpClient(AbortError, { proxySettings });
284+
httpRequest.url = 'http://apimatic.hopto.org:3000/test/requestBuilder';
285+
const axiosRequestConfig = httpClient.convertHttpRequest(httpRequest);
286+
expect(axiosRequestConfig.httpAgent.proxy).toMatchObject(
287+
expectedProxyConfig
288+
);
289+
});
290+
291+
it('adds httpsAgent to axios request config for https URLs when proxySettings are provided', () => {
292+
const httpClient = new HttpClient(AbortError, { proxySettings });
293+
httpRequest.url = 'https://apimatic.hopto.org:3000/test/requestBuilder';
294+
const axiosRequestConfig = httpClient.convertHttpRequest(httpRequest);
295+
expect(axiosRequestConfig.httpsAgent.proxy).toMatchObject(
296+
expectedProxyConfig
297+
);
298+
});
299+
300+
it('httpsAgent should be undefined when proxySettings are not provided', () => {
301+
const httpClient = new HttpClient(AbortError);
302+
httpRequest.url = 'https://apimatic.hopto.org:3000/test/requestBuilder';
303+
const axiosRequestConfig = httpClient.convertHttpRequest(httpRequest);
304+
expect(axiosRequestConfig.httpsAgent).toBeUndefined();
305+
});
306+
307+
it('httpsAgent and httpAgent should be undefined when requestUrl is not set', () => {
308+
const httpClient = new HttpClient(AbortError);
309+
const axiosRequestConfig = httpClient.convertHttpRequest(httpRequest);
310+
expect(axiosRequestConfig.httpsAgent).toBeUndefined();
311+
expect(axiosRequestConfig.httpAgent).toBeUndefined();
312+
});
242313
});
243314

244315
describe('test blob type', () => {

packages/pagination/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This library is used by JavaScript SDKs generated by the [APIMatic Code Generato
1818

1919
The following environments are supported:
2020

21-
1. Node.js v10+
21+
1. Node.js v14+ and v16+
2222
1. Bundlers like Rollup or Webpack
2323
1. Web browsers
2424

packages/proxy/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# APIMatic Proxy Libary for JavaScript
2+
3+
> This library is currently in preview.
4+
5+
Provides utility functions to generate HTTP and HTTPS proxy agents based on configuration. It supports conditional behavior for both Node.js and browser environments.
6+
7+
The exported helper functions and interfaces include:
8+
9+
1. **createProxyAgents**: Creates and returns HTTP and HTTPS proxy agents using provided proxy settings. In browser environments, this function returns undefined with a warning, as proxy agents are not supported in browsers.
10+
2. **ProxySettings**: Defines the proxy configuration, including a required address and optional port and authentication credentials.
11+
12+
This library is used by JavaScript SDKs generated by the [APIMatic Code Generator](http://www.apimatic.io).
13+
14+
## Builds
15+
16+
The following environments are supported:
17+
18+
1. Node.js v14+ and v16+
19+
1. Bundlers like Rollup or Webpack
20+
1. Web browsers
21+
22+
To support multiple environments, we export various builds:
23+
24+
| Environment | Usage |
25+
| --- |----------------------------------------------------------------------------|
26+
| Common.js | Import like this: `require('@apimatic/proxy')`. |
27+
| ES Module | Import like this: `import { /* your imports */ } from '@apimatic/proxy'`. |
28+
| Browsers | *Use script: `https://unpkg.com/@apimatic/proxy@VERSION/umd/schema.js` |
29+
| Modern Browsers (supports ESM and uses modern JS) | *Use script: `https://unpkg.com/@apimatic/proxy@VERSION/umd/schema.esm.js` |
30+
31+
_* Don't forget to replace VERSION with the version number._
32+
33+
**Note**: We discourage importing files or modules directly from the package. These are likely to change in the future and should not be considered stable.

0 commit comments

Comments
 (0)