Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Add proxy configuration to the client #402

Merged
merged 3 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,29 @@ Primary accounts can make API calls on behalf of their subaccounts. [API documen
// then, if you need to reset it back to the primary account:
mg.resetSubaccount();
```

### Proxy configuration
By leveraging client configuration options, users can effortlessly establish proxy connections that align with their network requirements.
Ex:
```js
import * as FormData from 'form-data';
import Mailgun from 'mailgun.js';
const mailgun = new Mailgun(FormData);

const mg = mailgun.client({
username: 'api',
key: process.env.MAILGUN_API_KEY || 'key-yourkeyhere',
proxy: {
protocol: 'https' // 'http' ,
host: '127.0.0.1', // use your proxy host here
port: 9000, // use your proxy port here
auth: { // may be omitted if proxy doesn't require authentication
username: 'user_name', // provide username
password: 'user_password' // provide password
}
},
});
```
### Types imports
Starting from version **9.0.0.** Types can be includes as named import:
```TS
Expand Down
1 change: 1 addition & 0 deletions dist/Classes/common/Request.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ declare class Request {
private headers;
private formDataBuilder;
private maxBodyLength;
private proxy;
constructor(options: RequestOptions, formData: InputFormData);
request(method: string, url: string, onCallOptions?: Record<string, unknown | Record<string, unknown>>): Promise<APIResponse>;
private getResponseBody;
Expand Down
2 changes: 2 additions & 0 deletions dist/Types/MailgunClient/MailgunClientOptions.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { AxiosProxyConfig } from 'axios';
export type MailgunClientOptions = {
username: string;
key: string;
url?: string;
public_key?: string;
timeout?: number;
proxy?: AxiosProxyConfig;
};
14,564 changes: 14,561 additions & 3 deletions dist/mailgun.node.js

Large diffs are not rendered by default.

9,807 changes: 9,804 additions & 3 deletions dist/mailgun.web.js

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions lib/Classes/common/Request.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import * as base64 from 'base-64';
import urljoin from 'url-join';
import axios, {
AxiosError, AxiosResponse, AxiosHeaders, RawAxiosRequestHeaders
AxiosError,
AxiosResponse,
AxiosHeaders,
RawAxiosRequestHeaders,
AxiosProxyConfig,
} from 'axios';
import * as NodeFormData from 'form-data';
import APIError from './Error';
Expand All @@ -25,6 +29,7 @@ class Request {
private headers: AxiosHeaders;
private formDataBuilder: FormDataBuilder;
private maxBodyLength: number;
private proxy: AxiosProxyConfig | undefined;

constructor(options: RequestOptions, formData: InputFormData) {
this.username = options.username;
Expand All @@ -34,6 +39,7 @@ class Request {
this.headers = this.makeHeadersFromObject(options.headers);
this.formDataBuilder = new FormDataBuilder(formData);
this.maxBodyLength = 52428800; // 50 MB
this.proxy = options?.proxy;
}

async request(
Expand Down Expand Up @@ -66,7 +72,8 @@ class Request {
url: urlValue,
headers: requestHeaders,
...params,
maxBodyLength: this.maxBodyLength
maxBodyLength: this.maxBodyLength,
proxy: this.proxy,
});
} catch (err: unknown) {
const errorResponse = err as AxiosError;
Expand Down
2 changes: 2 additions & 0 deletions lib/Types/MailgunClient/MailgunClientOptions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { AxiosProxyConfig } from 'axios';
/* eslint-disable camelcase */
export type MailgunClientOptions = {
username: string;
key: string;
url?: string;
public_key?: string;
timeout?: number;
proxy?: AxiosProxyConfig;
}
112 changes: 78 additions & 34 deletions test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ import DomainTemplatesClient from '../lib/Classes/Domains/domainsTemplates';
import MultipleValidationClient from '../lib/Classes/Validations/multipleValidation';
import MailListsMembers from '../lib/Classes/MailingLists/mailListMembers';

describe('Client', function () {
describe('Client', () => {
let client: IMailgunClient;

beforeEach(function () {
beforeEach(() => {
client = new Client({
username: 'username',
key: 'key',
Expand All @@ -37,27 +37,11 @@ describe('Client', function () {
}, formData as InputFormData);
});

it('raises error when username is not provided', function () {
expect(
function () {
return new Client({ key: 'key' } as MailgunClientOptions, formData as InputFormData);
}
).to.throw('Parameter "username" is required');
});

it('raises error when key is not provided', function () {
expect(
function () {
return new Client({ username: 'username' } as MailgunClientOptions, formData as InputFormData);
}
).to.throw('Parameter "key" is required');
});

it('exposes raw request client', function () {
it('exposes raw request client', () => {
client.should.have.property('request').to.be.instanceOf(Request);
});

it('sets and resets subaccount header for requests', function () {
it('sets and resets subaccount header for requests', () => {
client.setSubaccount('XYZ');
client.should.have.property('request').to.be.instanceOf(Request);
client.should.have.property('request').to.haveOwnProperty('headers')
Expand All @@ -67,7 +51,7 @@ describe('Client', function () {
.to.not.haveOwnProperty(SubaccountsClient.SUBACCOUNT_HEADER);
});

it('creates domain client', function () {
it('creates domain client', () => {
client.domains.should.be.instanceOf(DomainsClient);
});

Expand All @@ -83,55 +67,115 @@ describe('Client', function () {
client.domains.domainTemplates.should.be.instanceOf(DomainTemplatesClient);
});

it('creates event client', function () {
it('creates event client', () => {
client.events.should.be.instanceOf(EventsClient);
});

it('creates webhook client', function () {
it('creates webhook client', () => {
client.webhooks.should.be.instanceOf(WebhooksClient);
});

it('creates suppressions client', function () {
it('creates suppressions client', () => {
client.suppressions.should.be.instanceOf(SuppressionsClient);
});

it('creates stats client', function () {
it('creates stats client', () => {
client.stats.should.be.instanceOf(StatsClient);
});

it('creates messages client', function () {
it('creates messages client', () => {
client.messages.should.be.instanceOf(MessagesClient);
});

it('creates routes client', function () {
it('creates routes client', () => {
client.routes.should.be.instanceOf(RoutesClient);
});

it('creates ips client', function () {
it('creates ips client', () => {
client.ips.should.be.instanceOf(IpsClient);
});

it('creates ip_pools client', function () {
it('creates ip_pools client', () => {
client.ip_pools.should.be.instanceOf(IpPoolsClient);
});

it('creates lists client', function () {
it('creates lists client', () => {
client.lists.should.be.instanceOf(ListsClient);
});

it('creates mail lists members client', function () {
it('creates mail lists members client', () => {
client.lists.members.should.be.instanceOf(MailListsMembers);
});

it('creates address validate client', function () {
it('creates address validate client', () => {
client.validate.should.be.instanceOf(ValidateClient);
});

it('creates multiple validation client', function () {
it('creates multiple validation client', () => {
client.validate.multipleValidation.should.be.instanceOf(MultipleValidationClient);
});

it('creates subaccounts client', function () {
it('creates subaccounts client', () => {
client.subaccounts.should.be.instanceOf(SubaccountsClient);
});

describe('User configuration', () => {
it('respects proxy settings', () => {
const mgClient = new Client({
username: 'username',
key: 'key',
public_key: 'key',
proxy: {
protocol: 'https',
host: '127.0.0.1',
port: 9000,
auth: {
username: 'test',
password: 'test-pass'
}
}
} as MailgunClientOptions, formData as InputFormData);
mgClient.should.have.property('request');
mgClient.should.have.property('request').to.have.property('proxy').to.eql({
protocol: 'https',
host: '127.0.0.1',
port: 9000,
auth: {
username: 'test',
password: 'test-pass'
}
});
});

it('respects timeout and url settings', () => {
const mgClient = new Client({
username: 'username',
key: 'key',
public_key: 'public_key',
timeout: 1000,
url: 'test_url'
} as MailgunClientOptions, formData as InputFormData);
mgClient.should.have.property('request');
mgClient.should.have.property('request').to.have.property('timeout').to.equal(1000);
mgClient.should.have.property('request').to.have.property('url').to.eql('test_url');
});

it('raises error when username is not provided', () => {
expect(
() => new Client(
{ key: 'key' } as MailgunClientOptions,
formData as InputFormData
)
).to.throw('Parameter "username" is required');
});

it('raises error when key is not provided', () => {
expect(
() => new Client(
{ username: 'username' } as MailgunClientOptions,
formData as InputFormData
)
).to.throw('Parameter "key" is required');
});
});
});
Loading