| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| import fetchMock from 'fetch-mock'; |
| import { JsonValue, SupersetClientClass } from '@superset-ui/core/src/connection'; |
| import { makeApi, SupersetApiError } from '@superset-ui/core/src/query'; |
| import setupClientForTest from '../setupClientForTest'; |
| |
| describe('makeApi()', () => { |
| beforeAll(setupClientForTest); |
| afterEach(fetchMock.restore); |
| |
| it('should expose method and endpoint', () => { |
| const api = makeApi({ |
| method: 'GET', |
| endpoint: '/test', |
| }); |
| expect(api.method).toEqual('GET'); |
| expect(api.endpoint).toEqual('/test'); |
| expect(api.requestType).toEqual('search'); |
| }); |
| |
| it('should allow custom client', async () => { |
| expect.assertions(2); |
| const api = makeApi({ |
| method: 'GET', |
| endpoint: '/test-custom-client', |
| }); |
| const client = new SupersetClientClass({ baseUrl: 'http://foo/' }); |
| const mockResponse = { yes: 'ok' }; |
| const mockRequest = jest.fn(() => |
| Promise.resolve( |
| new Response(JSON.stringify(mockResponse), { |
| headers: { 'Content-Type': 'application/json' }, |
| }), |
| ), |
| ); |
| Object.assign(client, { |
| request: mockRequest, |
| }); |
| const result = await api(null, { client }); |
| expect(result).toEqual(mockResponse); |
| expect(mockRequest).toHaveBeenCalledTimes(1); |
| }); |
| |
| it('should obtain json response by default', async () => { |
| expect.assertions(1); |
| const api = makeApi({ |
| method: 'GET', |
| endpoint: '/test', |
| }); |
| fetchMock.get('glob:*/test', { yes: 'ok' }); |
| expect(await api({})).toEqual({ yes: 'ok' }); |
| }); |
| |
| it('should allow custom parseResponse', async () => { |
| expect.assertions(2); |
| const responseJson = { items: [1, 2, 3] }; |
| fetchMock.post('glob:*/test', responseJson); |
| const api = makeApi({ |
| method: 'POST', |
| endpoint: '/test', |
| processResponse: (json: typeof responseJson) => |
| json.items.reduce((a: number, b: number) => a + b), |
| }); |
| expect(api.method).toEqual('POST'); |
| expect(await api({})).toBe(6); |
| }); |
| |
| it('should post FormData when requestType=form', async () => { |
| expect.assertions(3); |
| const api = makeApi({ |
| method: 'POST', |
| endpoint: '/test-formdata', |
| requestType: 'form', |
| }); |
| fetchMock.post('glob:*/test-formdata', { test: 'ok' }); |
| |
| expect(await api({ request: 'test' })).toEqual({ test: 'ok' }); |
| |
| const expected = new FormData(); |
| expected.append('request', JSON.stringify('test')); |
| const received = fetchMock.lastOptions().body as FormData; |
| |
| expect(received).toBeInstanceOf(FormData); |
| expect(received.get('request')).toEqual(expected.get('request')); |
| }); |
| |
| it('should use searchParams for method=GET (`requestType=search` implied)', async () => { |
| expect.assertions(1); |
| const api = makeApi({ |
| method: 'GET', |
| endpoint: '/test-get-search', |
| }); |
| fetchMock.get('glob:*/test-get-search*', { search: 'get' }); |
| await api({ p1: 1, p2: 2, p3: [1, 2] }); |
| expect(fetchMock.lastUrl()).toContain('/test-get-search?p1=1&p2=2&p3=1%2C2'); |
| }); |
| |
| it('should serialize rison for method=GET, requestType=rison', async () => { |
| expect.assertions(1); |
| const api = makeApi({ |
| method: 'GET', |
| endpoint: '/test-post-search', |
| requestType: 'rison', |
| }); |
| fetchMock.get('glob:*/test-post-search*', { rison: 'get' }); |
| await api({ p1: 1, p3: [1, 2] }); |
| expect(fetchMock.lastUrl()).toContain('/test-post-search?q=(p1:1,p3:!(1,2))'); |
| }); |
| |
| it('should use searchParams for method=POST, requestType=search', async () => { |
| expect.assertions(1); |
| const api = makeApi({ |
| method: 'POST', |
| endpoint: '/test-post-search', |
| requestType: 'search', |
| }); |
| fetchMock.post('glob:*/test-post-search*', { search: 'post' }); |
| await api({ p1: 1, p3: [1, 2] }); |
| expect(fetchMock.lastUrl()).toContain('/test-post-search?p1=1&p3=1%2C2'); |
| }); |
| |
| it('should throw when requestType is invalid', () => { |
| expect(() => { |
| makeApi({ |
| method: 'POST', |
| endpoint: '/test-formdata', |
| // @ts-ignore |
| requestType: 'text', |
| }); |
| }).toThrow('Invalid request payload type'); |
| }); |
| |
| it('should handle errors', async () => { |
| expect.assertions(1); |
| const api = makeApi({ |
| method: 'POST', |
| endpoint: '/test-formdata', |
| requestType: 'form', |
| }); |
| let error; |
| |
| fetchMock.post('glob:*/test-formdata', { test: 'ok' }); |
| |
| try { |
| await api('<This is an invalid JSON string>'); |
| } catch (err) { |
| error = err; |
| } finally { |
| expect((error as SupersetApiError).message).toContain('Invalid payload'); |
| } |
| }); |
| |
| it('should handle error on 200 response', async () => { |
| expect.assertions(1); |
| const api = makeApi({ |
| method: 'POST', |
| endpoint: '/test-200-error', |
| requestType: 'json', |
| }); |
| fetchMock.post('glob:*/test-200-error', { error: 'not ok' }); |
| |
| let error; |
| try { |
| await api({}); |
| } catch (err) { |
| error = err; |
| } finally { |
| expect((error as SupersetApiError).message).toContain('not ok'); |
| } |
| }); |
| |
| it('should parse text response when responseType=text', async () => { |
| expect.assertions(1); |
| const api = makeApi<JsonValue, string, 'text'>({ |
| method: 'PUT', |
| endpoint: '/test-parse-text', |
| requestType: 'form', |
| responseType: 'text', |
| processResponse: text => `${text}?`, |
| }); |
| fetchMock.put('glob:*/test-parse-text', 'ok'); |
| const result = await api({ field1: 11 }); |
| expect(result).toBe('ok?'); |
| }); |
| |
| it('should return raw resposnse when responseType=raw', async () => { |
| expect.assertions(2); |
| const api = makeApi<JsonValue, number, 'raw'>({ |
| method: 'DELETE', |
| endpoint: '/test-raw-response', |
| responseType: 'raw', |
| processResponse: response => response.status, |
| }); |
| fetchMock.delete('glob:*/test-raw-response?*', 'ok'); |
| const result = await api({ field1: 11 }, {}); |
| expect(result).toEqual(200); |
| expect(fetchMock.lastUrl()).toContain('/test-raw-response?field1=11'); |
| }); |
| }); |