From f4f8e9092d6623bc1c9a9673f2e0d31838c51af3 Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Wed, 30 Jan 2019 13:56:40 -0800 Subject: [PATCH] [SupersetClient] Implement put and delete HTTP methods (#85) * feat: Allow any http METHOD * Add method to RequestConfig * linting * fix tests --- .../src/SupersetClient.ts | 6 +++ .../src/SupersetClientClass.ts | 46 ++++++++----------- .../superset-ui-connection/src/types.ts | 1 + .../test/SupersetClient.test.ts | 19 +++++++- .../test/SupersetClientClass.test.ts | 36 +++++++++++---- 5 files changed, 70 insertions(+), 38 deletions(-) diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/src/SupersetClient.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/src/SupersetClient.ts index 5a180855f4b8b..94b4ceda3851c 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/src/SupersetClient.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/src/SupersetClient.ts @@ -13,12 +13,15 @@ function getInstance(maybeClient: SupersetClientClass | undefined): SupersetClie export interface SupersetClientInterface { configure: (config?: ClientConfig) => SupersetClientClass; + delete: (request: RequestConfig) => Promise; get: (request: RequestConfig) => Promise; getInstance: (maybeClient?: SupersetClientClass) => SupersetClientClass; init: (force?: boolean) => Promise; isAuthenticated: () => boolean; post: (request: RequestConfig) => Promise; + put: (request: RequestConfig) => Promise; reAuthenticate: () => Promise; + request: (request: RequestConfig) => Promise; reset: () => void; } @@ -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; }, diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/src/SupersetClientClass.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/src/SupersetClientClass.ts index 38147c36c4101..b1e2cc406083b 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/src/SupersetClientClass.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/src/SupersetClientClass.ts @@ -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 { - 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 { + return this.request({ ...requestConfig, method: 'GET' }); } - async post({ + async delete(requestConfig: RequestConfig): Promise { + return this.request({ ...requestConfig, method: 'DELETE' }); + } + + async put(requestConfig: RequestConfig): Promise { + return this.request({ ...requestConfig, method: 'PUT' }); + } + + async post(requestConfig: RequestConfig): Promise { + return this.request({ ...requestConfig, method: 'POST' }); + } + + async request({ + body, credentials, endpoint, headers, host, + method, mode, parseMethod, postPayload, @@ -112,9 +103,10 @@ export class SupersetClientClass { }: RequestConfig): Promise { return this.ensureAuth().then(() => callApi({ + body, credentials: credentials || this.credentials, headers: { ...this.headers, ...headers }, - method: 'POST', + method, mode: mode || this.mode, parseMethod, postPayload, diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/src/types.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/src/types.ts index 19c1e82b56550..07de12a479929 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/src/types.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/src/types.ts @@ -35,6 +35,7 @@ export interface RequestBase { headers?: Headers; host?: Host; mode?: Mode; + method?: Method; parseMethod?: ParseMethod; postPayload?: PostPayload; signal?: Signal; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/test/SupersetClient.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/test/SupersetClient.test.ts index 34e40bb8dd99d..13030d0934cda 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/test/SupersetClient.test.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/test/SupersetClient.test.ts @@ -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)); }); @@ -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(); }); @@ -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); @@ -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(); @@ -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(); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/test/SupersetClientClass.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/test/SupersetClientClass.test.ts index 97edf21124a04..717a0671b788f 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/test/SupersetClientClass.test.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-connection/test/SupersetClientClass.test.ts @@ -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(); + }), ); });