Skip to content

Commit 1d5c163

Browse files
feat: add proxy support
1 parent c7a558e commit 1d5c163

File tree

7 files changed

+92
-2
lines changed

7 files changed

+92
-2
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77

8+
## [Unreleased]
9+
### Added
10+
* Add proxy support.
11+
12+
813
## [1.4.0] - 2022-08-09
914
### Added
1015
* Add `createGlossaryWithCsv()` allowing glossaries downloaded from website to
@@ -99,6 +104,7 @@ client library took over this package name. Thanks to
99104
ownership.
100105

101106

107+
[Unreleased]: https://github.com/DeepLcom/deepl-node/compare/v1.4.0...HEAD
102108
[1.4.0]: https://github.com/DeepLcom/deepl-node/compare/v1.3.2...v1.4.0
103109
[1.3.2]: https://github.com/DeepLcom/deepl-node/compare/v1.3.1...v1.3.2
104110
[1.3.1]: https://github.com/DeepLcom/deepl-node/compare/v1.2.2...v1.3.1

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,9 @@ The available options are:
385385
- `headers`: extra HTTP headers attached to every HTTP request. By default, no
386386
extra headers are used. Note that Authorization and User-Agent headers are
387387
added automatically but may be overridden by this option.
388+
- `proxy`: define the hostname, and port of the proxy server, and optionally
389+
the protocol, and authorization (as an auth object with username and
390+
password fields).
388391

389392
#### Logging
390393

@@ -401,6 +404,19 @@ log.getLogger('deepl').setLevel('debug'); // Or 'info'
401404
The `loglevel` package also supports plugins, see
402405
[the documentation](https://www.npmjs.com/package/loglevel#plugins).
403406

407+
#### Proxy configuration
408+
409+
You can configure a proxy by specifying the `proxy` argument when constructing a
410+
`deepl.Translator`:
411+
412+
```javascript
413+
const proxy = {host: 'localhost', port: 3000};
414+
const deepl = new deepl.Translator('YOUR_AUTH_KEY', options);
415+
```
416+
417+
The proxy argument is passed to the underlying `axios` request, see the
418+
[documentation for axios][axios-proxy-docs].
419+
404420
### Request retries
405421

406422
Requests to the DeepL API that fail due to transient conditions (for example,
@@ -440,6 +456,7 @@ environment variables defined referring to the mock-server.
440456

441457
[api-docs]: https://www.deepl.com/docs-api?utm_source=github&utm_medium=github-nodejs-readme
442458
[api-docs-csv-format]: https://www.deepl.com/docs-api/managing-glossaries/supported-glossary-formats/?utm_source=github&utm_medium=github-nodejs-readme
459+
[axios-proxy-docs]: https://axios-http.com/docs/req_config
443460
[create-account]: https://www.deepl.com/pro?utm_source=github&utm_medium=github-nodejs-readme#developer
444461
[deepl-mock]: https://www.github.com/DeepLcom/deepl-mock
445462
[issues]: https://www.github.com/DeepLcom/deepl-node/issues

src/client.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import axios, { AxiosError, AxiosRequestConfig } from 'axios';
99
import { URLSearchParams } from 'url';
1010
import FormData from 'form-data';
1111
import { IncomingMessage } from 'http';
12+
import { ProxyConfig } from './types';
1213

1314
type HttpMethod = 'GET' | 'DELETE' | 'POST';
1415

@@ -84,17 +85,20 @@ export class HttpClient {
8485
private readonly headers: Record<string, string>;
8586
private readonly minTimeout: number;
8687
private readonly maxRetries: number;
88+
private readonly proxy?: ProxyConfig;
8789

8890
constructor(
8991
serverUrl: string,
9092
headers: Record<string, string>,
9193
maxRetries: number,
9294
minTimeout: number,
95+
proxy?: ProxyConfig,
9396
) {
9497
this.serverUrl = serverUrl;
9598
this.headers = headers;
9699
this.maxRetries = maxRetries;
97100
this.minTimeout = minTimeout;
101+
this.proxy = proxy;
98102
}
99103

100104
prepareRequest(
@@ -133,6 +137,7 @@ export class HttpClient {
133137
axiosRequestConfig.data = options.data;
134138
}
135139
}
140+
axiosRequestConfig.proxy = this.proxy;
136141
return axiosRequestConfig;
137142
}
138143

src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,13 @@ export class Translator {
478478

479479
const maxRetries = options?.maxRetries !== undefined ? options.maxRetries : 5;
480480
const minTimeout = options?.minTimeout !== undefined ? options.minTimeout : 5000;
481-
this.httpClient = new HttpClient(serverUrl, headers, maxRetries, minTimeout);
481+
this.httpClient = new HttpClient(
482+
serverUrl,
483+
headers,
484+
maxRetries,
485+
minTimeout,
486+
options?.proxy,
487+
);
482488
}
483489

484490
/**

src/types.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@
22
// Use of this source code is governed by an MIT
33
// license that can be found in the LICENSE file.
44

5+
/**
6+
* Optional proxy configuration, may be specified as proxy in TranslatorOptions.
7+
* @see TranslatorOptions.proxy
8+
*/
9+
export interface ProxyConfig {
10+
host: string;
11+
port: number;
12+
auth?: {
13+
username: string;
14+
password: string;
15+
};
16+
protocol?: string;
17+
}
18+
519
/**
620
* Options that can be specified when constructing a Translator.
721
*/
@@ -30,6 +44,11 @@ export interface TranslatorOptions {
3044
* if this value is unspecified is 10 seconds (10000).
3145
*/
3246
minTimeout?: number;
47+
48+
/**
49+
* Define the host, port and protocol of the proxy server.
50+
*/
51+
proxy?: ProxyConfig;
3352
}
3453

3554
export type Formality = 'less' | 'more' | 'default';

tests/core.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ const internalExampleText: Record<string, string> = {
4444
};
4545

4646
const usingMockServer = process.env.DEEPL_MOCK_SERVER_PORT !== undefined;
47+
const usingMockProxyServer =
48+
usingMockServer && process.env.DEEPL_MOCK_PROXY_SERVER_PORT !== undefined;
4749

4850
/**
4951
* Creates a random authKey for testing purposes. Only valid if using mock-server.
@@ -84,6 +86,7 @@ export interface TestTranslatorOptions {
8486
randomAuthKey?: boolean;
8587
maxRetries?: number;
8688
minTimeout?: number;
89+
proxy?: deepl.ProxyConfig;
8790

8891
mockServerNoResponseTimes?: number;
8992
mockServer429ResponseTimes?: number;
@@ -161,14 +164,24 @@ export function makeTranslator(options?: TestTranslatorOptions) {
161164
headers: sessionHeaders,
162165
minTimeout: options?.minTimeout,
163166
maxRetries: options?.maxRetries,
167+
proxy: options?.proxy,
164168
});
165169
}
166170

167171
// Use instead of it(...) for tests that require a mock-server
168172
export const withMockServer = usingMockServer ? it : it.skip;
173+
// Use instead of it(...) for tests that require a mock-server with proxy
174+
export const withMockProxyServer = usingMockProxyServer ? it : it.skip;
169175
// Use instead of it(...) for tests that cannot run using mock-server
170176
export const withRealServer = usingMockServer ? it.skip : it;
171177

178+
const proxyUrlString = process.env.DEEPL_PROXY_URL;
179+
const proxyUrl = proxyUrlString ? new URL(proxyUrlString) : undefined;
180+
const proxyConfigHost = proxyUrl ? proxyUrl.hostname : '';
181+
const proxyConfigPort = parseInt(process.env.DEEPL_MOCK_PROXY_SERVER_PORT || '');
182+
183+
export const proxyConfig: deepl.ProxyConfig = { host: proxyConfigHost, port: proxyConfigPort };
184+
172185
// Wrap setTimeout() with Promise
173186
export const timeout = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
174187

@@ -180,7 +193,9 @@ module.exports = {
180193
exampleLargeDocumentOutput,
181194
tempFiles,
182195
withMockServer,
196+
withMockProxyServer,
183197
withRealServer,
184198
makeTranslator,
185199
timeout,
200+
proxyConfig,
186201
};

tests/general.test.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@ import * as deepl from 'deepl-node';
66

77
import fs from 'fs';
88

9-
import { exampleText, tempFiles, withMockServer, makeTranslator } from './core';
9+
import {
10+
exampleText,
11+
tempFiles,
12+
withMockServer,
13+
withMockProxyServer,
14+
makeTranslator,
15+
proxyConfig,
16+
} from './core';
1017

1118
const serverUrl = process.env.DEEPL_SERVER_URL;
1219

@@ -94,6 +101,21 @@ describe('general', () => {
94101
expect(deepl.isFreeAccountAuthKey('0000')).toBe(false);
95102
});
96103

104+
withMockProxyServer('should support proxy usage', async () => {
105+
const translator = makeTranslator({
106+
mockServerExpectProxy: true,
107+
randomAuthKey: true,
108+
});
109+
const translatorWithProxy = makeTranslator({
110+
mockServerExpectProxy: true,
111+
randomAuthKey: true,
112+
proxy: proxyConfig,
113+
});
114+
115+
await expect(translator.getUsage()).rejects.toThrowError(deepl.DeepLError);
116+
await translatorWithProxy.getUsage();
117+
});
118+
97119
withMockServer('should throw ConnectionError with timed-out responses', async () => {
98120
const translator = makeTranslator({
99121
mockServerNoResponseTimes: 2,

0 commit comments

Comments
 (0)