2020-07-02 20:46:54 -04:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-12-07 19:56:57 -05:00
|
|
|
import fetchMock from 'fetch-mock';
|
2021-12-14 03:19:55 -05:00
|
|
|
import { SupersetClientClass, ClientConfig, CallApi } from '@superset-ui/core';
|
2018-12-07 19:56:57 -05:00
|
|
|
import { LOGIN_GLOB } from './fixtures/constants';
|
|
|
|
|
|
|
|
describe('SupersetClientClass', () => {
|
|
|
|
beforeAll(() => {
|
2021-03-01 16:46:25 -05:00
|
|
|
fetchMock.get(LOGIN_GLOB, { result: '' });
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
afterAll(fetchMock.restore);
|
|
|
|
|
2020-07-07 20:44:36 -04:00
|
|
|
describe('new SupersetClientClass()', () => {
|
|
|
|
it('fallback protocol to https when setting only host', () => {
|
|
|
|
const client = new SupersetClientClass({ host: 'TEST-HOST' });
|
|
|
|
expect(client.baseUrl).toEqual('https://test-host');
|
|
|
|
});
|
2020-07-02 20:46:54 -04:00
|
|
|
});
|
|
|
|
|
2018-12-07 19:56:57 -05:00
|
|
|
describe('.getUrl()', () => {
|
2018-12-13 20:15:23 -05:00
|
|
|
let client = new SupersetClientClass();
|
|
|
|
|
2018-12-07 19:56:57 -05:00
|
|
|
beforeEach(() => {
|
2021-11-09 07:42:28 -05:00
|
|
|
client = new SupersetClientClass({
|
|
|
|
protocol: 'https:',
|
|
|
|
host: 'CONFIG_HOST',
|
|
|
|
});
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
it('uses url if passed', () => {
|
2021-11-09 07:42:28 -05:00
|
|
|
expect(
|
|
|
|
client.getUrl({ url: 'myUrl', endpoint: 'blah', host: 'blah' }),
|
|
|
|
).toBe('myUrl');
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
it('constructs a valid url from config.protocol + host + endpoint if passed', () => {
|
2021-11-09 07:42:28 -05:00
|
|
|
expect(client.getUrl({ endpoint: '/test', host: 'myhost' })).toBe(
|
|
|
|
'https://myhost/test',
|
|
|
|
);
|
|
|
|
expect(client.getUrl({ endpoint: '/test', host: 'myhost/' })).toBe(
|
|
|
|
'https://myhost/test',
|
|
|
|
);
|
|
|
|
expect(client.getUrl({ endpoint: 'test', host: 'myhost' })).toBe(
|
|
|
|
'https://myhost/test',
|
|
|
|
);
|
2018-12-07 19:56:57 -05:00
|
|
|
expect(client.getUrl({ endpoint: '/test/test//', host: 'myhost/' })).toBe(
|
|
|
|
'https://myhost/test/test//',
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('constructs a valid url from config.host + endpoint if host is omitted', () => {
|
2021-11-09 07:42:28 -05:00
|
|
|
expect(client.getUrl({ endpoint: '/test' })).toBe(
|
|
|
|
'https://config_host/test',
|
|
|
|
);
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
it('does not throw if url, endpoint, and host are all empty', () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
client = new SupersetClientClass({ protocol: 'https:', host: '' });
|
2020-07-02 20:46:54 -04:00
|
|
|
expect(client.getUrl()).toBe('https://localhost/');
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('.init()', () => {
|
|
|
|
afterEach(() => {
|
|
|
|
fetchMock.reset();
|
|
|
|
// reset
|
2021-03-01 16:46:25 -05:00
|
|
|
fetchMock.get(LOGIN_GLOB, { result: 1234 }, { overwriteRoutes: true });
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2021-03-01 16:46:25 -05:00
|
|
|
it('calls api/v1/security/csrf_token/ when init() is called if no CSRF token is passed', async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(1);
|
2020-07-02 20:46:54 -04:00
|
|
|
await new SupersetClientClass().init();
|
|
|
|
expect(fetchMock.calls(LOGIN_GLOB)).toHaveLength(1);
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2021-03-01 16:46:25 -05:00
|
|
|
it('does NOT call api/v1/security/csrf_token/ when init() is called if a CSRF token is passed', async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(1);
|
2020-07-02 20:46:54 -04:00
|
|
|
await new SupersetClientClass({ csrfToken: 'abc' }).init();
|
|
|
|
expect(fetchMock.calls(LOGIN_GLOB)).toHaveLength(0);
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2021-03-01 16:46:25 -05:00
|
|
|
it('calls api/v1/security/csrf_token/ when init(force=true) is called even if a CSRF token is passed', async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(4);
|
|
|
|
const initialToken = 'initial_token';
|
|
|
|
const client = new SupersetClientClass({ csrfToken: initialToken });
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init();
|
|
|
|
expect(fetchMock.calls(LOGIN_GLOB)).toHaveLength(0);
|
|
|
|
expect(client.csrfToken).toBe(initialToken);
|
2018-12-07 19:56:57 -05:00
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init(true);
|
|
|
|
expect(fetchMock.calls(LOGIN_GLOB)).toHaveLength(1);
|
|
|
|
expect(client.csrfToken).not.toBe(initialToken);
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2021-03-01 16:46:25 -05:00
|
|
|
it('throws if api/v1/security/csrf_token/ returns an error', async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(1);
|
2020-07-02 20:46:54 -04:00
|
|
|
const rejectError = { status: 403 };
|
|
|
|
fetchMock.get(LOGIN_GLOB, () => Promise.reject(rejectError), {
|
2018-12-07 19:56:57 -05:00
|
|
|
overwriteRoutes: true,
|
|
|
|
});
|
2021-01-13 01:09:58 -05:00
|
|
|
|
|
|
|
let error;
|
2020-07-02 20:46:54 -04:00
|
|
|
try {
|
|
|
|
await new SupersetClientClass({}).init();
|
2021-01-13 01:09:58 -05:00
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
} finally {
|
2020-07-02 20:46:54 -04:00
|
|
|
expect(error as typeof rejectError).toEqual(rejectError);
|
|
|
|
}
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
const invalidCsrfTokenError = { error: 'Failed to fetch CSRF token' };
|
|
|
|
|
2021-03-01 16:46:25 -05:00
|
|
|
it('throws if api/v1/security/csrf_token/ does not return a token', async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(1);
|
|
|
|
fetchMock.get(LOGIN_GLOB, {}, { overwriteRoutes: true });
|
2021-01-13 01:09:58 -05:00
|
|
|
|
|
|
|
let error;
|
2020-07-02 20:46:54 -04:00
|
|
|
try {
|
|
|
|
await new SupersetClientClass({}).init();
|
2021-01-13 01:09:58 -05:00
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
} finally {
|
2021-11-09 07:42:28 -05:00
|
|
|
expect(error as typeof invalidCsrfTokenError).toEqual(
|
|
|
|
invalidCsrfTokenError,
|
|
|
|
);
|
2020-07-02 20:46:54 -04:00
|
|
|
}
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
it('does not set csrfToken if response is not json', async () => {
|
|
|
|
expect.assertions(1);
|
2018-12-07 19:56:57 -05:00
|
|
|
fetchMock.get(LOGIN_GLOB, '123', {
|
|
|
|
overwriteRoutes: true,
|
|
|
|
});
|
2021-01-13 01:09:58 -05:00
|
|
|
|
|
|
|
let error;
|
2020-07-02 20:46:54 -04:00
|
|
|
try {
|
|
|
|
await new SupersetClientClass({}).init();
|
2021-01-13 01:09:58 -05:00
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
} finally {
|
2021-11-09 07:42:28 -05:00
|
|
|
expect(error as typeof invalidCsrfTokenError).toEqual(
|
|
|
|
invalidCsrfTokenError,
|
|
|
|
);
|
2020-07-02 20:46:54 -04:00
|
|
|
}
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('.isAuthenticated()', () => {
|
|
|
|
afterEach(fetchMock.reset);
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
it('returns true if there is a token and false if not', async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(2);
|
|
|
|
const client = new SupersetClientClass({});
|
|
|
|
expect(client.isAuthenticated()).toBe(false);
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init();
|
|
|
|
expect(client.isAuthenticated()).toBe(true);
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
it('returns true if a token is passed at configuration', () => {
|
|
|
|
expect.assertions(2);
|
2021-11-09 07:42:28 -05:00
|
|
|
const clientWithoutToken = new SupersetClientClass({
|
|
|
|
csrfToken: undefined,
|
|
|
|
});
|
2018-12-07 19:56:57 -05:00
|
|
|
const clientWithToken = new SupersetClientClass({ csrfToken: 'token' });
|
|
|
|
expect(clientWithoutToken.isAuthenticated()).toBe(false);
|
|
|
|
expect(clientWithToken.isAuthenticated()).toBe(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('.ensureAuth()', () => {
|
2020-07-02 20:46:54 -04:00
|
|
|
it(`returns a promise that rejects if .init() has not been called`, async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(2);
|
|
|
|
|
|
|
|
const client = new SupersetClientClass({});
|
2021-01-13 01:09:58 -05:00
|
|
|
let error;
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
try {
|
|
|
|
await client.ensureAuth();
|
2021-01-13 01:09:58 -05:00
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
} finally {
|
2020-07-02 20:46:54 -04:00
|
|
|
expect(error).toEqual({ error: expect.any(String) });
|
|
|
|
}
|
|
|
|
expect(client.isAuthenticated()).toBe(false);
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
it('returns a promise that resolves if .init() resolves successfully', async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(1);
|
|
|
|
|
|
|
|
const client = new SupersetClientClass({});
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init();
|
|
|
|
await client.ensureAuth();
|
2018-12-07 19:56:57 -05:00
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
expect(client.isAuthenticated()).toBe(true);
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
it(`returns a promise that rejects if .init() is unsuccessful`, async () => {
|
|
|
|
expect.assertions(4);
|
|
|
|
|
2018-12-07 19:56:57 -05:00
|
|
|
const rejectValue = { status: 403 };
|
|
|
|
fetchMock.get(LOGIN_GLOB, () => Promise.reject(rejectValue), {
|
|
|
|
overwriteRoutes: true,
|
|
|
|
});
|
|
|
|
|
|
|
|
const client = new SupersetClientClass({});
|
2021-01-13 01:09:58 -05:00
|
|
|
let error;
|
|
|
|
let error2;
|
2018-12-07 19:56:57 -05:00
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
try {
|
|
|
|
await client.init();
|
2021-01-13 01:09:58 -05:00
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
} finally {
|
2020-07-02 20:46:54 -04:00
|
|
|
expect(error).toEqual(expect.objectContaining(rejectValue));
|
|
|
|
expect(client.isAuthenticated()).toBe(false);
|
|
|
|
try {
|
|
|
|
await client.ensureAuth();
|
2021-01-13 01:09:58 -05:00
|
|
|
} catch (err) {
|
|
|
|
error2 = err;
|
|
|
|
} finally {
|
2020-07-02 20:46:54 -04:00
|
|
|
expect(error2).toEqual(expect.objectContaining(rejectValue));
|
|
|
|
expect(client.isAuthenticated()).toBe(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// reset
|
|
|
|
fetchMock.get(
|
|
|
|
LOGIN_GLOB,
|
2021-03-01 16:46:25 -05:00
|
|
|
{ result: 1234 },
|
2020-07-02 20:46:54 -04:00
|
|
|
{
|
|
|
|
overwriteRoutes: true,
|
|
|
|
},
|
|
|
|
);
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('requests', () => {
|
|
|
|
afterEach(fetchMock.reset);
|
|
|
|
const protocol = 'https:';
|
2020-07-02 20:46:54 -04:00
|
|
|
const host = 'host';
|
2018-12-07 19:56:57 -05:00
|
|
|
const mockGetEndpoint = '/get/url';
|
2019-01-30 16:56:40 -05:00
|
|
|
const mockRequestEndpoint = '/request/url';
|
2018-12-07 19:56:57 -05:00
|
|
|
const mockPostEndpoint = '/post/url';
|
2019-01-30 16:56:40 -05:00
|
|
|
const mockPutEndpoint = '/put/url';
|
|
|
|
const mockDeleteEndpoint = '/delete/url';
|
2018-12-07 19:56:57 -05:00
|
|
|
const mockTextEndpoint = '/text/endpoint';
|
|
|
|
const mockGetUrl = `${protocol}//${host}${mockGetEndpoint}`;
|
2019-01-30 16:56:40 -05:00
|
|
|
const mockRequestUrl = `${protocol}//${host}${mockRequestEndpoint}`;
|
2018-12-07 19:56:57 -05:00
|
|
|
const mockPostUrl = `${protocol}//${host}${mockPostEndpoint}`;
|
|
|
|
const mockTextUrl = `${protocol}//${host}${mockTextEndpoint}`;
|
2019-01-30 16:56:40 -05:00
|
|
|
const mockPutUrl = `${protocol}//${host}${mockPutEndpoint}`;
|
|
|
|
const mockDeleteUrl = `${protocol}//${host}${mockDeleteEndpoint}`;
|
2019-04-02 17:32:15 -04:00
|
|
|
const mockTextJsonResponse = '{ "value": 9223372036854775807 }';
|
2019-06-10 19:46:52 -04:00
|
|
|
const mockPayload = { json: () => Promise.resolve('payload') };
|
2018-12-07 19:56:57 -05:00
|
|
|
|
2019-06-10 19:46:52 -04:00
|
|
|
fetchMock.get(mockGetUrl, mockPayload);
|
|
|
|
fetchMock.post(mockPostUrl, mockPayload);
|
|
|
|
fetchMock.put(mockPutUrl, mockPayload);
|
|
|
|
fetchMock.delete(mockDeleteUrl, mockPayload);
|
|
|
|
fetchMock.delete(mockRequestUrl, mockPayload);
|
2018-12-07 19:56:57 -05:00
|
|
|
fetchMock.get(mockTextUrl, mockTextJsonResponse);
|
|
|
|
fetchMock.post(mockTextUrl, mockTextJsonResponse);
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
it('checks for authentication before every get and post request', async () => {
|
2019-01-30 16:56:40 -05:00
|
|
|
expect.assertions(6);
|
2020-07-02 20:46:54 -04:00
|
|
|
|
2018-12-07 19:56:57 -05:00
|
|
|
const authSpy = jest.spyOn(SupersetClientClass.prototype, 'ensureAuth');
|
|
|
|
const client = new SupersetClientClass({ protocol, host });
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init();
|
|
|
|
await client.get({ url: mockGetUrl });
|
|
|
|
await client.post({ url: mockPostUrl });
|
|
|
|
await client.put({ url: mockPutUrl });
|
|
|
|
await client.delete({ url: mockDeleteUrl });
|
|
|
|
await client.request({ url: mockRequestUrl, method: 'DELETE' });
|
|
|
|
|
|
|
|
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();
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
it('sets protocol, host, headers, mode, and credentials from config', async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(3);
|
2020-07-02 20:46:54 -04:00
|
|
|
|
2018-12-07 19:56:57 -05:00
|
|
|
const clientConfig: ClientConfig = {
|
|
|
|
host,
|
|
|
|
protocol,
|
|
|
|
mode: 'cors',
|
|
|
|
credentials: 'include',
|
|
|
|
headers: { my: 'header' },
|
|
|
|
};
|
|
|
|
|
|
|
|
const client = new SupersetClientClass(clientConfig);
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init();
|
|
|
|
await client.get({ url: mockGetUrl });
|
|
|
|
|
2021-12-14 03:19:55 -05:00
|
|
|
const fetchRequest = fetchMock.calls(mockGetUrl)[0][1] as CallApi;
|
2020-07-02 20:46:54 -04:00
|
|
|
expect(fetchRequest.mode).toBe(clientConfig.mode);
|
|
|
|
expect(fetchRequest.credentials).toBe(clientConfig.credentials);
|
|
|
|
expect(fetchRequest.headers).toEqual(
|
2021-11-09 07:42:28 -05:00
|
|
|
expect.objectContaining(
|
|
|
|
clientConfig.headers,
|
|
|
|
) as typeof fetchRequest.headers,
|
2018-12-07 19:56:57 -05:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2022-01-25 19:41:32 -05:00
|
|
|
it('uses a guest token when provided', async () => {
|
|
|
|
expect.assertions(1);
|
|
|
|
|
|
|
|
const client = new SupersetClientClass({
|
|
|
|
protocol,
|
|
|
|
host,
|
|
|
|
guestToken: 'abc123',
|
|
|
|
guestTokenHeaderName: 'guestTokenHeader',
|
|
|
|
});
|
|
|
|
|
|
|
|
await client.init();
|
|
|
|
await client.get({ url: mockGetUrl });
|
|
|
|
const fetchRequest = fetchMock.calls(mockGetUrl)[0][1] as CallApi;
|
|
|
|
expect(fetchRequest.headers).toEqual(
|
|
|
|
expect.objectContaining({
|
|
|
|
guestTokenHeader: 'abc123',
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2018-12-07 19:56:57 -05:00
|
|
|
describe('.get()', () => {
|
2020-07-02 20:46:54 -04:00
|
|
|
it('makes a request using url or endpoint', async () => {
|
|
|
|
expect.assertions(2);
|
|
|
|
|
2018-12-07 19:56:57 -05:00
|
|
|
const client = new SupersetClientClass({ protocol, host });
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init();
|
2018-12-07 19:56:57 -05:00
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.get({ url: mockGetUrl });
|
|
|
|
expect(fetchMock.calls(mockGetUrl)).toHaveLength(1);
|
2018-12-07 19:56:57 -05:00
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.get({ endpoint: mockGetEndpoint });
|
|
|
|
expect(fetchMock.calls(mockGetUrl)).toHaveLength(2);
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
it('supports parsing a response as text', async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(2);
|
|
|
|
const client = new SupersetClientClass({ protocol, host });
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init();
|
2021-11-09 07:42:28 -05:00
|
|
|
const { text } = await client.get({
|
|
|
|
url: mockTextUrl,
|
|
|
|
parseMethod: 'text',
|
|
|
|
});
|
2020-07-02 20:46:54 -04:00
|
|
|
expect(fetchMock.calls(mockTextUrl)).toHaveLength(1);
|
|
|
|
expect(text).toBe(mockTextJsonResponse);
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
it('allows overriding host, headers, mode, and credentials per-request', async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(3);
|
2020-07-02 20:46:54 -04:00
|
|
|
|
2018-12-07 19:56:57 -05:00
|
|
|
const clientConfig: ClientConfig = {
|
|
|
|
host,
|
|
|
|
protocol,
|
|
|
|
mode: 'cors',
|
|
|
|
credentials: 'include',
|
|
|
|
headers: { my: 'header' },
|
|
|
|
};
|
|
|
|
const overrideConfig: ClientConfig = {
|
|
|
|
host: 'override_host',
|
|
|
|
mode: 'no-cors',
|
|
|
|
credentials: 'omit',
|
|
|
|
headers: { my: 'override', another: 'header' },
|
|
|
|
};
|
|
|
|
|
|
|
|
const client = new SupersetClientClass(clientConfig);
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init();
|
|
|
|
await client.get({ url: mockGetUrl, ...overrideConfig });
|
|
|
|
|
2021-12-14 03:19:55 -05:00
|
|
|
const fetchRequest = fetchMock.calls(mockGetUrl)[0][1] as CallApi;
|
2020-07-02 20:46:54 -04:00
|
|
|
expect(fetchRequest.mode).toBe(overrideConfig.mode);
|
|
|
|
expect(fetchRequest.credentials).toBe(overrideConfig.credentials);
|
|
|
|
expect(fetchRequest.headers).toEqual(
|
2021-11-09 07:42:28 -05:00
|
|
|
expect.objectContaining(
|
|
|
|
overrideConfig.headers,
|
|
|
|
) as typeof fetchRequest.headers,
|
2020-07-02 20:46:54 -04:00
|
|
|
);
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('.post()', () => {
|
2020-07-02 20:46:54 -04:00
|
|
|
it('makes a request using url or endpoint', async () => {
|
|
|
|
expect.assertions(2);
|
|
|
|
|
2018-12-07 19:56:57 -05:00
|
|
|
const client = new SupersetClientClass({ protocol, host });
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init();
|
2018-12-07 19:56:57 -05:00
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.post({ url: mockPostUrl });
|
|
|
|
expect(fetchMock.calls(mockPostUrl)).toHaveLength(1);
|
2018-12-07 19:56:57 -05:00
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.post({ endpoint: mockPostEndpoint });
|
|
|
|
expect(fetchMock.calls(mockPostUrl)).toHaveLength(2);
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
it('allows overriding host, headers, mode, and credentials per-request', async () => {
|
|
|
|
expect.assertions(3);
|
2018-12-07 19:56:57 -05:00
|
|
|
const clientConfig: ClientConfig = {
|
|
|
|
host,
|
|
|
|
protocol,
|
|
|
|
mode: 'cors',
|
|
|
|
credentials: 'include',
|
|
|
|
headers: { my: 'header' },
|
|
|
|
};
|
|
|
|
const overrideConfig: ClientConfig = {
|
|
|
|
host: 'override_host',
|
|
|
|
mode: 'no-cors',
|
|
|
|
credentials: 'omit',
|
|
|
|
headers: { my: 'override', another: 'header' },
|
|
|
|
};
|
|
|
|
|
|
|
|
const client = new SupersetClientClass(clientConfig);
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init();
|
|
|
|
await client.post({ url: mockPostUrl, ...overrideConfig });
|
|
|
|
|
2021-12-14 03:19:55 -05:00
|
|
|
const fetchRequest = fetchMock.calls(mockPostUrl)[0][1] as CallApi;
|
2018-12-07 19:56:57 -05:00
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
expect(fetchRequest.mode).toBe(overrideConfig.mode);
|
|
|
|
expect(fetchRequest.credentials).toBe(overrideConfig.credentials);
|
|
|
|
expect(fetchRequest.headers).toEqual(
|
2021-11-09 07:42:28 -05:00
|
|
|
expect.objectContaining(
|
|
|
|
overrideConfig.headers,
|
|
|
|
) as typeof fetchRequest.headers,
|
2018-12-07 19:56:57 -05:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
it('supports parsing a response as text', async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(2);
|
|
|
|
const client = new SupersetClientClass({ protocol, host });
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init();
|
2021-11-09 07:42:28 -05:00
|
|
|
const { text } = await client.post({
|
|
|
|
url: mockTextUrl,
|
|
|
|
parseMethod: 'text',
|
|
|
|
});
|
2020-07-02 20:46:54 -04:00
|
|
|
expect(fetchMock.calls(mockTextUrl)).toHaveLength(1);
|
|
|
|
expect(text).toBe(mockTextJsonResponse);
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
it('passes postPayload key,values in the body', async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(3);
|
|
|
|
|
2020-05-07 15:53:36 -04:00
|
|
|
const postPayload = { number: 123, array: [1, 2, 3] };
|
2018-12-07 19:56:57 -05:00
|
|
|
const client = new SupersetClientClass({ protocol, host });
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init();
|
|
|
|
await client.post({ url: mockPostUrl, postPayload });
|
2018-12-07 19:56:57 -05:00
|
|
|
|
2021-12-14 03:19:55 -05:00
|
|
|
const fetchRequest = fetchMock.calls(mockPostUrl)[0][1] as CallApi;
|
|
|
|
const formData = fetchRequest.body as FormData;
|
2018-12-07 19:56:57 -05:00
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
expect(fetchMock.calls(mockPostUrl)).toHaveLength(1);
|
|
|
|
Object.entries(postPayload).forEach(([key, value]) => {
|
|
|
|
expect(formData.get(key)).toBe(JSON.stringify(value));
|
|
|
|
});
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
it('respects the stringify parameter for postPayload key,values', async () => {
|
2018-12-07 19:56:57 -05:00
|
|
|
expect.assertions(3);
|
2020-07-02 20:46:54 -04:00
|
|
|
|
2020-05-07 15:53:36 -04:00
|
|
|
const postPayload = { number: 123, array: [1, 2, 3] };
|
2018-12-07 19:56:57 -05:00
|
|
|
const client = new SupersetClientClass({ protocol, host });
|
2020-07-02 20:46:54 -04:00
|
|
|
await client.init();
|
|
|
|
await client.post({ url: mockPostUrl, postPayload, stringify: false });
|
2018-12-07 19:56:57 -05:00
|
|
|
|
2021-12-14 03:19:55 -05:00
|
|
|
const fetchRequest = fetchMock.calls(mockPostUrl)[0][1] as CallApi;
|
|
|
|
const formData = fetchRequest.body as FormData;
|
2018-12-07 19:56:57 -05:00
|
|
|
|
2020-07-02 20:46:54 -04:00
|
|
|
expect(fetchMock.calls(mockPostUrl)).toHaveLength(1);
|
|
|
|
Object.entries(postPayload).forEach(([key, value]) => {
|
|
|
|
expect(formData.get(key)).toBe(String(value));
|
|
|
|
});
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2021-12-14 03:19:55 -05:00
|
|
|
|
2022-03-21 13:19:21 -04:00
|
|
|
describe('when unauthorized', () => {
|
|
|
|
let originalLocation: any;
|
|
|
|
let authSpy: jest.SpyInstance;
|
2021-12-14 03:19:55 -05:00
|
|
|
const mockRequestUrl = 'https://host/get/url';
|
2022-02-15 09:59:06 -05:00
|
|
|
const mockRequestPath = '/get/url';
|
|
|
|
const mockRequestSearch = '?param=1¶m=2';
|
2022-03-21 13:19:21 -04:00
|
|
|
const mockHref = `http://localhost${mockRequestPath + mockRequestSearch}`;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
originalLocation = window.location;
|
|
|
|
// @ts-ignore
|
|
|
|
delete window.location;
|
|
|
|
// @ts-ignore
|
|
|
|
window.location = {
|
|
|
|
pathname: mockRequestPath,
|
|
|
|
search: mockRequestSearch,
|
|
|
|
href: mockHref,
|
|
|
|
};
|
|
|
|
authSpy = jest
|
|
|
|
.spyOn(SupersetClientClass.prototype, 'ensureAuth')
|
|
|
|
.mockImplementation();
|
|
|
|
const rejectValue = { status: 401 };
|
|
|
|
fetchMock.get(mockRequestUrl, () => Promise.reject(rejectValue), {
|
|
|
|
overwriteRoutes: true,
|
|
|
|
});
|
2021-12-14 03:19:55 -05:00
|
|
|
});
|
|
|
|
|
2022-03-21 13:19:21 -04:00
|
|
|
afterEach(() => {
|
|
|
|
authSpy.mockReset();
|
|
|
|
window.location = originalLocation;
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should redirect', async () => {
|
|
|
|
const client = new SupersetClientClass({});
|
2021-12-14 03:19:55 -05:00
|
|
|
|
2022-03-21 13:19:21 -04:00
|
|
|
let error;
|
|
|
|
try {
|
|
|
|
await client.request({ url: mockRequestUrl, method: 'GET' });
|
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
} finally {
|
|
|
|
const redirectURL = window.location.href;
|
|
|
|
expect(redirectURL).toBe(
|
|
|
|
`/login?next=${mockRequestPath + mockRequestSearch}`,
|
|
|
|
);
|
|
|
|
expect(error.status).toBe(401);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does nothing if instructed to ignoreUnauthorized', async () => {
|
|
|
|
const client = new SupersetClientClass({});
|
|
|
|
|
|
|
|
let error;
|
|
|
|
try {
|
|
|
|
await client.request({
|
|
|
|
url: mockRequestUrl,
|
|
|
|
method: 'GET',
|
|
|
|
ignoreUnauthorized: true,
|
|
|
|
});
|
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
} finally {
|
|
|
|
// unchanged href, no redirect
|
|
|
|
expect(window.location.href).toBe(mockHref);
|
|
|
|
expect(error.status).toBe(401);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
it('accepts an unauthorizedHandler to override redirect behavior', async () => {
|
|
|
|
const unauthorizedHandler = jest.fn();
|
|
|
|
const client = new SupersetClientClass({ unauthorizedHandler });
|
|
|
|
|
|
|
|
let error;
|
|
|
|
try {
|
|
|
|
await client.request({
|
|
|
|
url: mockRequestUrl,
|
|
|
|
method: 'GET',
|
|
|
|
});
|
|
|
|
} catch (err) {
|
|
|
|
error = err;
|
|
|
|
} finally {
|
|
|
|
// unchanged href, no redirect
|
|
|
|
expect(window.location.href).toBe(mockHref);
|
|
|
|
expect(error.status).toBe(401);
|
|
|
|
expect(unauthorizedHandler).toHaveBeenCalledTimes(1);
|
|
|
|
}
|
|
|
|
});
|
2021-12-14 03:19:55 -05:00
|
|
|
});
|
2018-12-07 19:56:57 -05:00
|
|
|
});
|