Skip to content

Commit

Permalink
[SupersetClient] Implement put and delete HTTP methods (#85)
Browse files Browse the repository at this point in the history
* feat: Allow any http METHOD

* Add method to RequestConfig

* linting

* fix tests
  • Loading branch information
mistercrunch authored and zhaoyongjie committed Nov 17, 2021
1 parent d862598 commit f4f8e90
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ function getInstance(maybeClient: SupersetClientClass | undefined): SupersetClie

export interface SupersetClientInterface {
configure: (config?: ClientConfig) => SupersetClientClass;
delete: (request: RequestConfig) => Promise<SupersetClientResponse>;
get: (request: RequestConfig) => Promise<SupersetClientResponse>;
getInstance: (maybeClient?: SupersetClientClass) => SupersetClientClass;
init: (force?: boolean) => Promise<string | undefined>;
isAuthenticated: () => boolean;
post: (request: RequestConfig) => Promise<SupersetClientResponse>;
put: (request: RequestConfig) => Promise<SupersetClientResponse>;
reAuthenticate: () => Promise<string | undefined>;
request: (request: RequestConfig) => Promise<SupersetClientResponse>;
reset: () => void;
}

Expand All @@ -28,12 +31,15 @@ const SupersetClient: SupersetClientInterface = {

return singletonClient;
},
delete: (request: RequestConfig) => getInstance(singletonClient).delete(request),
get: (request: RequestConfig) => getInstance(singletonClient).get(request),
getInstance,
init: (force?: boolean) => getInstance(singletonClient).init(force),
isAuthenticated: () => getInstance(singletonClient).isAuthenticated(),
post: (request: RequestConfig) => getInstance(singletonClient).post(request),
put: (request: RequestConfig) => getInstance(singletonClient).put(request),
reAuthenticate: () => getInstance(singletonClient).init(/* force = */ true),
request: (request: RequestConfig) => getInstance(singletonClient).get(request),
reset: () => {
singletonClient = undefined;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,38 +70,29 @@ export class SupersetClientClass {
return this.csrfToken !== null && this.csrfToken !== undefined;
}

async get({
body,
credentials,
headers,
host,
endpoint,
mode,
parseMethod,
signal,
timeout,
url,
}: RequestConfig): Promise<SupersetClientResponse> {
return this.ensureAuth().then(() =>
callApi({
body,
credentials: credentials || this.credentials,
headers: { ...this.headers, ...headers },
method: 'GET',
mode: mode || this.mode,
parseMethod,
signal,
timeout: timeout || this.timeout,
url: this.getUrl({ endpoint, host, url }),
}),
);
async get(requestConfig: RequestConfig): Promise<SupersetClientResponse> {
return this.request({ ...requestConfig, method: 'GET' });
}

async post({
async delete(requestConfig: RequestConfig): Promise<SupersetClientResponse> {
return this.request({ ...requestConfig, method: 'DELETE' });
}

async put(requestConfig: RequestConfig): Promise<SupersetClientResponse> {
return this.request({ ...requestConfig, method: 'PUT' });
}

async post(requestConfig: RequestConfig): Promise<SupersetClientResponse> {
return this.request({ ...requestConfig, method: 'POST' });
}

async request({
body,
credentials,
endpoint,
headers,
host,
method,
mode,
parseMethod,
postPayload,
Expand All @@ -112,9 +103,10 @@ export class SupersetClientClass {
}: RequestConfig): Promise<SupersetClientResponse> {
return this.ensureAuth().then(() =>
callApi({
body,
credentials: credentials || this.credentials,
headers: { ...this.headers, ...headers },
method: 'POST',
method,
mode: mode || this.mode,
parseMethod,
postPayload,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface RequestBase {
headers?: Headers;
host?: Host;
mode?: Mode;
method?: Method;
parseMethod?: ParseMethod;
postPayload?: PostPayload;
signal?: Signal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe('SupersetClient', () => {
expect(SupersetClient.post).toEqual(expect.any(Function));
expect(SupersetClient.isAuthenticated).toEqual(expect.any(Function));
expect(SupersetClient.reAuthenticate).toEqual(expect.any(Function));
expect(SupersetClient.request).toEqual(expect.any(Function));
expect(SupersetClient.reset).toEqual(expect.any(Function));
});

Expand All @@ -28,6 +29,7 @@ describe('SupersetClient', () => {
expect(SupersetClient.post).toThrow();
expect(SupersetClient.isAuthenticated).toThrow();
expect(SupersetClient.reAuthenticate).toThrow();
expect(SupersetClient.request).toThrow();

expect(SupersetClient.configure).not.toThrow();
});
Expand All @@ -36,6 +38,9 @@ describe('SupersetClient', () => {
it('calls appropriate SupersetClient methods when configured', () => {
const mockGetUrl = '/mock/get/url';
const mockPostUrl = '/mock/post/url';
const mockRequestUrl = '/mock/request/url';
const mockPutUrl = '/mock/put/url';
const mockDeleteUrl = '/mock/delete/url';
const mockGetPayload = { get: 'payload' };
const mockPostPayload = { post: 'payload' };
fetchMock.get(mockGetUrl, mockGetPayload);
Expand All @@ -44,8 +49,11 @@ describe('SupersetClient', () => {
const initSpy = jest.spyOn(SupersetClientClass.prototype, 'init');
const getSpy = jest.spyOn(SupersetClientClass.prototype, 'get');
const postSpy = jest.spyOn(SupersetClientClass.prototype, 'post');
const putSpy = jest.spyOn(SupersetClientClass.prototype, 'put');
const deleteSpy = jest.spyOn(SupersetClientClass.prototype, 'delete');
const authenticatedSpy = jest.spyOn(SupersetClientClass.prototype, 'isAuthenticated');
const csrfSpy = jest.spyOn(SupersetClientClass.prototype, 'getCSRFToken');
const requestSpy = jest.spyOn(SupersetClientClass.prototype, 'request');

SupersetClient.configure({});
SupersetClient.init();
Expand All @@ -56,16 +64,25 @@ describe('SupersetClient', () => {

SupersetClient.get({ url: mockGetUrl });
SupersetClient.post({ url: mockPostUrl });
SupersetClient.delete({ url: mockDeleteUrl });
SupersetClient.put({ url: mockPutUrl });
SupersetClient.request({ url: mockRequestUrl });
SupersetClient.isAuthenticated();
SupersetClient.reAuthenticate();

expect(initSpy).toHaveBeenCalledTimes(2);
expect(getSpy).toHaveBeenCalledTimes(1);
expect(deleteSpy).toHaveBeenCalledTimes(1);
expect(putSpy).toHaveBeenCalledTimes(1);
expect(getSpy).toHaveBeenCalledTimes(2);
expect(postSpy).toHaveBeenCalledTimes(1);
expect(requestSpy).toHaveBeenCalledTimes(5); // request rewires to get
expect(csrfSpy).toHaveBeenCalledTimes(2); // from init() + reAuthenticate()

initSpy.mockRestore();
getSpy.mockRestore();
putSpy.mockRestore();
deleteSpy.mockRestore();
requestSpy.mockRestore();
postSpy.mockRestore();
authenticatedSpy.mockRestore();
csrfSpy.mockRestore();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,35 +256,51 @@ describe('SupersetClientClass', () => {
const protocol = 'https:';
const host = 'HOST';
const mockGetEndpoint = '/get/url';
const mockRequestEndpoint = '/request/url';
const mockPostEndpoint = '/post/url';
const mockPutEndpoint = '/put/url';
const mockDeleteEndpoint = '/delete/url';
const mockTextEndpoint = '/text/endpoint';
const mockGetUrl = `${protocol}//${host}${mockGetEndpoint}`;
const mockRequestUrl = `${protocol}//${host}${mockRequestEndpoint}`;
const mockPostUrl = `${protocol}//${host}${mockPostEndpoint}`;
const mockTextUrl = `${protocol}//${host}${mockTextEndpoint}`;
const mockPutUrl = `${protocol}//${host}${mockPutEndpoint}`;
const mockDeleteUrl = `${protocol}//${host}${mockDeleteEndpoint}`;
const mockBigNumber = '9223372036854775807';
const mockTextJsonResponse = `{ "value": ${mockBigNumber} }`;

fetchMock.get(mockGetUrl, { json: 'payload' });
fetchMock.post(mockPostUrl, { json: 'payload' });
fetchMock.put(mockPutUrl, { json: 'payload' });
fetchMock.delete(mockDeleteUrl, { json: 'payload' });
fetchMock.delete(mockRequestUrl, { json: 'payload' });
fetchMock.get(mockTextUrl, mockTextJsonResponse);
fetchMock.post(mockTextUrl, mockTextJsonResponse);

it('checks for authentication before every get and post request', () => {
expect.assertions(3);
expect.assertions(6);
const authSpy = jest.spyOn(SupersetClientClass.prototype, 'ensureAuth');
const client = new SupersetClientClass({ protocol, host });

return client.init().then(() =>
Promise.all([client.get({ url: mockGetUrl }), client.post({ url: mockPostUrl })]).then(
() => {
expect(fetchMock.calls(mockGetUrl)).toHaveLength(1);
expect(fetchMock.calls(mockPostUrl)).toHaveLength(1);
expect(authSpy).toHaveBeenCalledTimes(2);
authSpy.mockRestore();
Promise.all([
client.get({ url: mockGetUrl }),
client.post({ url: mockPostUrl }),
client.put({ url: mockPutUrl }),
client.delete({ url: mockDeleteUrl }),
client.request({ url: mockRequestUrl, method: 'DELETE' }),
]).then(() => {
expect(fetchMock.calls(mockGetUrl)).toHaveLength(1);
expect(fetchMock.calls(mockPostUrl)).toHaveLength(1);
expect(fetchMock.calls(mockDeleteUrl)).toHaveLength(1);
expect(fetchMock.calls(mockPutUrl)).toHaveLength(1);
expect(fetchMock.calls(mockRequestUrl)).toHaveLength(1);
expect(authSpy).toHaveBeenCalledTimes(5);
authSpy.mockRestore();

return Promise.resolve();
},
),
return Promise.resolve();
}),
);
});

Expand Down

0 comments on commit f4f8e90

Please sign in to comment.