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

Feat/middleware #189

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
32 changes: 27 additions & 5 deletions sdk/src/platform/Platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import Cache from '../core/Cache';
import Client, {ApiError} from '../http/Client';
import Externals from '../core/Externals';
import {delay} from './utils';
import {
parseMiddlewares,
executePreMiddlewaresInSerial,
executePostMiddlewaresInSerial,
executeErrorMiddlewaresInSerial,
Middleware,
} from './middleware';

declare const screen: any; //FIXME TS Crap

Expand Down Expand Up @@ -77,6 +84,8 @@ export default class Platform extends EventEmitter {

private _handleRateLimit: boolean | number;

private _middlewares: Middleware[];

private _codeVerifier: string;

private _discovery?: Discovery;
Expand Down Expand Up @@ -110,6 +119,7 @@ export default class Platform extends EventEmitter {
authProxy = false,
urlPrefix = '',
handleRateLimit,
middlewares = [],
}: PlatformOptionsConstructor) {
super();

Expand Down Expand Up @@ -140,6 +150,7 @@ export default class Platform extends EventEmitter {
this._revokeEndpoint = revokeEndpoint;
this._authorizeEndpoint = authorizeEndpoint;
this._handleRateLimit = handleRateLimit;
this._middlewares = middlewares;
this._codeVerifier = '';
if (enableDiscovery) {
const initialEndpoint = discoveryServer
Expand Down Expand Up @@ -702,20 +713,27 @@ export default class Platform extends EventEmitter {
}

public async sendRequest(request: Request, options: SendOptions = {}): Promise<Response> {
const middlewares = [...this._middlewares, ...(options.middlewares || [])];
const {preMiddlewares, postMiddlewares, errorMiddlewares} = parseMiddlewares(middlewares);
try {
request = await this.inflateRequest(request, options);
return await this._client.sendRequest(request);
request = await executePreMiddlewaresInSerial(preMiddlewares, request);
const response = await this._client.sendRequest(request);
return await executePostMiddlewaresInSerial(postMiddlewares, response);
} catch (e) {
let {retry, handleRateLimit} = options;

// Guard is for errors that come from polling
if (!e.response || retry) throw e;
if (!e.response || retry) {
return executeErrorMiddlewaresInSerial(errorMiddlewares, e);
}

const {response} = e;
const {status} = response;

if ((status !== Client._unauthorizedStatus && status !== Client._rateLimitStatus) || this._authProxy)
throw e;
if ((status !== Client._unauthorizedStatus && status !== Client._rateLimitStatus) || this._authProxy) {
return executeErrorMiddlewaresInSerial(errorMiddlewares, e);
}

options.retry = true;

Expand All @@ -738,7 +756,9 @@ export default class Platform extends EventEmitter {

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

if (!handleRateLimit) throw e;
if (!handleRateLimit) {
return executeErrorMiddlewaresInSerial(errorMiddlewares, e);
}
}

await delay(retryAfter);
Expand Down Expand Up @@ -859,6 +879,7 @@ export interface PlatformOptions extends AuthOptions {
discoveryAuthorizedEndpoint?: string;
discoveryAutoInit?: boolean;
brandId?: string;
middlewares?: Middleware[];
}

export interface PlatformOptionsConstructor extends PlatformOptions {
Expand All @@ -878,6 +899,7 @@ export interface SendOptions {
skipDiscoveryCheck?: boolean;
handleRateLimit?: boolean | number;
retry?: boolean; // Will be set by this method if SDK makes second request
middlewares?: Middleware[];
}

export interface LoginOptions {
Expand Down
287 changes: 287 additions & 0 deletions sdk/src/platform/middleware-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
import {apiCall, asyncTest, expect} from '../test/test';
import {PreMiddleware, PostMiddleware, ErrorMiddleware} from './middleware';

const globalAny: any = global;
const windowAny: any = typeof window !== 'undefined' ? window : global;

describe('RingCentral.platform.middleware', () => {
describe('add middlewares in sdkOption', () => {
const preMiddleware: PreMiddleware = request => {
request.headers.append('custom-value', 'RC');
return request;
};
it(
'will enhance the request config before send the request',
asyncTest(
async sdk => {
const platform = sdk.platform();
const customValue = await platform
.send({
url: 'http://whatever/test/test',
method: 'GET',
})
.catch(error => {
return error.request.headers.get('custom-value');
});
expect(customValue).to.equal('RC');
},
{
middlewares: [{pre: preMiddleware}],
},
),
);
});

describe('PreMiddleware', () => {
it(
'will enhance the request config before send the request',
asyncTest(async sdk => {
const preMiddleware: PreMiddleware = request => {
request.headers.append('custom-value', 'RC');
return request;
};
const platform = sdk.platform();
const customValue = await platform
.send({
url: 'http://whatever/test/test',
method: 'GET',
middlewares: [{pre: preMiddleware}],
})
.catch(error => {
return error.request.headers.get('custom-value');
});
expect(customValue).to.equal('RC');
}),
);
it(
'will enhance the request config twice before send the request',
asyncTest(async sdk => {
const preMiddleware1: PreMiddleware = request => {
request.headers.append('custom-value1', 'RC1');
return request;
};
const preMiddleware2: PreMiddleware = request => {
request.headers.append('custom-value2', 'RC2');
return request;
};
const platform = sdk.platform();
const customValue = await platform
.send({
url: 'http://whatever/test/test',
method: 'GET',
middlewares: [{pre: preMiddleware1}, {pre: preMiddleware2}],
})
.catch(error => {
return `${error.request.headers.get('custom-value1')} - ${error.request.headers.get(
'custom-value2',
)}`;
});
expect(customValue).to.equal('RC1 - RC2');
}),
);
});

describe('PostMiddleware', () => {
it(
'will enhance the response after the request was success',
asyncTest(async sdk => {
apiCall('GET', '/test/test', {name: 'RC'}, 200);
const postMiddleware: PostMiddleware = response => {
return response.json() as any;
};
const platform = sdk.platform();
const response = await platform.send({
url: 'http://whatever/test/test',
method: 'GET',
middlewares: [{post: postMiddleware}],
skipAuthCheck: true,
skipDiscoveryCheck: true,
});
expect(JSON.stringify(response)).to.equal(JSON.stringify({name: 'RC'}));
}),
);
it(
'will enhance the response twice after the request was success',
asyncTest(async sdk => {
apiCall('GET', '/test/test', {name: 'RC'}, 200);
const postMiddleware1: PostMiddleware = response => {
return response.json() as any;
};
const postMiddleware2: PostMiddleware = response => {
return (response as any).name as any;
};
const platform = sdk.platform();
const response = await platform.send({
url: 'http://whatever/test/test',
method: 'GET',
middlewares: [{post: postMiddleware1}, {post: postMiddleware2}],
skipAuthCheck: true,
skipDiscoveryCheck: true,
});
expect(response).to.equal('RC');
}),
);
});

describe('ErrorMiddleware', () => {
it(
'will be treated as success after the request was failed',
asyncTest(async sdk => {
const errorMiddleware: ErrorMiddleware = error => {
error.message = 'Success';
return error;
};
const platform = sdk.platform();
const customValue = await platform
.send({
url: 'http://whatever/test/test',
method: 'GET',
middlewares: [{error: errorMiddleware}],
})
.then((res: any) => res.message)
.catch(() => {
return 'Error';
});
expect(customValue).to.equal('Success');
}),
);

it(
'will enhance the error after the request was failed',
asyncTest(async sdk => {
const errorMiddleware: ErrorMiddleware = error => {
error.message = 'Failed';
throw error;
};
const platform = sdk.platform();
const customValue = await platform
.send({
url: 'http://whatever/test/test',
method: 'GET',
middlewares: [{error: errorMiddleware}],
})
.then(() => 'Success')
.catch(error => {
return error.message;
});
expect(customValue).to.equal('Failed');
}),
);

it(
'will enhance the error twice after the request was failed 1',
asyncTest(async sdk => {
const errorMiddleware1: ErrorMiddleware = error => {
error.message = 'RC';
throw error;
};
const errorMiddleware2: ErrorMiddleware = error => {
throw error.message;
};
const platform = sdk.platform();
const customValue = await platform
.send({
url: 'http://whatever/test/test',
method: 'GET',
middlewares: [{error: errorMiddleware1}, {error: errorMiddleware2}],
})
.catch(error => error);
expect(customValue).to.equal('RC');
}),
);

it(
'will enhance the error twice after the request was failed 2',
asyncTest(async sdk => {
const errorMiddleware1: ErrorMiddleware = error => {
error.message = 'RC';
throw error;
};
const errorMiddleware2: ErrorMiddleware = error => {
return error.message;
};
const platform = sdk.platform();
const customValue = await platform
.send({
url: 'http://whatever/test/test',
method: 'GET',
middlewares: [{error: errorMiddleware1}, {error: errorMiddleware2}],
})
.catch(() => 'Error');
expect(customValue).to.equal('RC');
}),
);

it(
'will enhance the error twice after the request was failed 3',
asyncTest(async sdk => {
const errorMiddleware1: ErrorMiddleware = error => {
error.message = 'RC';
return error;
};
const errorMiddleware2: ErrorMiddleware = error => {
throw error.message;
};
const platform = sdk.platform();
const customValue: any = await platform
.send({
url: 'http://whatever/test/test',
method: 'GET',
middlewares: [{error: errorMiddleware1}, {error: errorMiddleware2}],
})
.catch(() => 'Error');
expect(customValue.message).to.equal('RC');
}),
);
});

describe('PostMiddleware & ErrorMiddleware', () => {
it(
'will goto error middleware if there are some errors in the post middlewares 1',
asyncTest(async sdk => {
apiCall('GET', '/test/test', {name: 'RC'}, 200);
const postMiddleware: PostMiddleware = _response => {
throw new Error('RC');
};
const errorMiddleware: ErrorMiddleware = error => {
return error;
};
const platform = sdk.platform();
const response = await platform
.send({
url: 'http://whatever/test/test',
method: 'GET',
middlewares: [{post: postMiddleware, error: errorMiddleware}],
skipAuthCheck: true,
skipDiscoveryCheck: true,
})
.catch(error => error);
expect(response.message).to.equal('RC');
}),
);

it(
'will goto error middleware if there are some errors in the post middlewares 2',
asyncTest(async sdk => {
apiCall('GET', '/test/test', {name: 'RC'}, 200);
const postMiddleware: PostMiddleware = _response => {
throw new Error('RC');
};
const errorMiddleware: ErrorMiddleware = error => {
throw error.message;
};
const platform = sdk.platform();
const response = await platform
.send({
url: 'http://whatever/test/test',
method: 'GET',
middlewares: [{post: postMiddleware, error: errorMiddleware}],
skipAuthCheck: true,
skipDiscoveryCheck: true,
})
.catch(error => error);
expect(response).to.equal('RC');
}),
);
});
});
Loading