Skip to content

Commit

Permalink
Handle rate limit for all send requests (ringcentral#123)
Browse files Browse the repository at this point in the history
* default handle rate limit option
* added rate limiting in SDK constructor
* default handle rate limits with tests
  • Loading branch information
Brutalbeard authored and kirill-konshin committed Sep 28, 2019
1 parent 728fa8b commit c53d114
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 3 deletions.
5 changes: 5 additions & 0 deletions sdk/src/SDK-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ describe('RingCentral.SDK', () => {
return test(this, SDK.server.production);
});

it('sets rate limit', async function rateLimitTest() {
const sdk = new SDK({handleRateLimit: 60});
expect(sdk.platform()['_handleRateLimit']).to.equal(60);
});

describe('handleLoginRedirect', () => {
const sdk = new SDK();

Expand Down
4 changes: 3 additions & 1 deletion sdk/src/SDK.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class SDK {
}

public constructor(options: SDKOptions = {}) {
const {cachePrefix, defaultRequestInit} = options;
const {cachePrefix, defaultRequestInit, handleRateLimit} = options;

this._externals = new Externals({
...defaultExternals,
Expand All @@ -79,6 +79,7 @@ export class SDK {
externals: this._externals,
client: this._client,
cache: this._cache,
handleRateLimit,
});
}

Expand Down Expand Up @@ -164,6 +165,7 @@ export class SDK {
export interface SDKOptions extends PlatformOptions, ExternalsOptions {
cachePrefix?: string;
defaultRequestInit?: CreateRequestOptions;
handleRateLimit?: boolean | number;
}

export default SDK;
28 changes: 28 additions & 0 deletions sdk/src/platform/Platform-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,34 @@ describe('RingCentral.platform.Platform', () => {
}),
);

it(
'handles default rate limit 429',
asyncTest(async sdk => {
const platform = sdk.platform();
const path = '/restapi/xxx';
const response = {foo: 'bar'};
const rateLimitSpy = spy(() => {
apiCall('GET', path, response, 200);
});

platform['_handleRateLimit'] = 0.01;

apiCall('GET', path, {message: 'expected'}, 429, 'Rate Limit Exceeded');

platform.on(platform.events.rateLimitError, rateLimitSpy);

const res = await platform.get(path);

expect(rateLimitSpy.calledOnce).to.be.true;

const e = rateLimitSpy.getCalls()[0].args[0];
expect(e.message).to.equal('expected');
expect(e.retryAfter).to.equal(10);

expect(await res.json()).to.deep.equal(response);
}),
);

it(
'emits rate limit 429 errors if they are not handled',
asyncTest(async sdk => {
Expand Down
11 changes: 9 additions & 2 deletions sdk/src/platform/Platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export default class Platform extends EventEmitter {

private _urlPrefix;

private _handleRateLimit: boolean | number;

public constructor({
server,
clientId,
Expand All @@ -87,6 +89,7 @@ export default class Platform extends EventEmitter {
authorizeEndpoint = '/restapi/oauth/authorize',
authProxy = false,
urlPrefix = '',
handleRateLimit,
}: PlatformOptionsConstructor) {
super();

Expand Down Expand Up @@ -114,6 +117,7 @@ export default class Platform extends EventEmitter {
this._tokenEndpoint = tokenEndpoint;
this._revokeEndpoint = revokeEndpoint;
this._authorizeEndpoint = authorizeEndpoint;
this._handleRateLimit = handleRateLimit;
}

public on(event: events.beforeLogin, listener: () => void);
Expand Down Expand Up @@ -443,7 +447,7 @@ export default class Platform extends EventEmitter {
request = await this.inflateRequest(request, options);
return await this._client.sendRequest(request);
} catch (e) {
const {retry, handleRateLimit} = options;
let {retry, handleRateLimit} = options;

// Guard is for errors that come from polling
if (!e.response || retry) throw e;
Expand All @@ -463,6 +467,8 @@ export default class Platform extends EventEmitter {
}

if (status === Client._rateLimitStatus) {
handleRateLimit = handleRateLimit || this._handleRateLimit;

const defaultRetryAfter =
!handleRateLimit || typeof handleRateLimit === 'boolean' ? 60 : handleRateLimit;

Expand All @@ -473,7 +479,7 @@ export default class Platform extends EventEmitter {

this.emit(this.events.rateLimitError, e);

if (!options.handleRateLimit) throw e;
if (!handleRateLimit) throw e;
}

await delay(retryAfter);
Expand Down Expand Up @@ -554,6 +560,7 @@ export interface PlatformOptions extends AuthOptions {
authorizeEndpoint?: string;
authProxy?: boolean;
urlPrefix?: string;
handleRateLimit?: boolean | number;
}

export interface PlatformOptionsConstructor extends PlatformOptions {
Expand Down
1 change: 1 addition & 0 deletions sdk/src/test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export function createSdk(options: SDKOptions = {}) {
fetch: fetchMock.fetchHandler,
refreshDelayMs: 1,
redirectUri: 'http://foo',
handleRateLimit: false,
...options,
});
}
Expand Down

0 comments on commit c53d114

Please sign in to comment.