UNPKG

@superset-ui/core

Version:
279 lines (213 loc) 7.42 kB
/** * 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 callApiAndParseWithTimeout from './callApi/callApiAndParseWithTimeout'; import { DEFAULT_FETCH_RETRY_OPTIONS, DEFAULT_BASE_URL } from './constants'; const defaultUnauthorizedHandler = () => { if (!window.location.pathname.startsWith('/login')) { window.location.href = `/login?next=${window.location.href}`; } }; export default class SupersetClientClass { constructor({ baseUrl = DEFAULT_BASE_URL, host, protocol, headers = {}, fetchRetryOptions = {}, mode = 'same-origin', timeout, credentials = undefined, csrfToken = undefined, guestToken = undefined, guestTokenHeaderName = 'X-GuestToken', unauthorizedHandler = defaultUnauthorizedHandler } = {}) {this.credentials = void 0;this.csrfToken = void 0;this.csrfPromise = void 0;this.guestToken = void 0;this.guestTokenHeaderName = void 0;this.fetchRetryOptions = void 0;this.baseUrl = void 0;this.protocol = void 0;this.host = void 0;this.headers = void 0;this.mode = void 0;this.timeout = void 0;this.handleUnauthorized = void 0; const url = new URL( host || protocol ? `${protocol || 'https:'}//${host || 'localhost'}` : baseUrl, // baseUrl for API could also be relative, so we provide current location.href // as the base of baseUrl window.location.href ); this.baseUrl = url.href.replace(/\/+$/, ''); // always strip trailing slash this.host = url.host; this.protocol = url.protocol; this.headers = { Accept: 'application/json', ...headers }; // defaulting accept to json this.mode = mode; this.timeout = timeout; this.credentials = credentials; this.csrfToken = csrfToken; this.guestToken = guestToken; this.guestTokenHeaderName = guestTokenHeaderName; this.fetchRetryOptions = { ...DEFAULT_FETCH_RETRY_OPTIONS, ...fetchRetryOptions }; if (typeof this.csrfToken === 'string') { this.headers = { ...this.headers, 'X-CSRFToken': this.csrfToken }; this.csrfPromise = Promise.resolve(this.csrfToken); } if (guestToken) { this.headers[guestTokenHeaderName] = guestToken; } this.handleUnauthorized = unauthorizedHandler; } async init(force = false) { if (this.isAuthenticated() && !force) { return this.csrfPromise; } return this.getCSRFToken(); } async postForm(url, payload, target = '_blank') { if (url) { await this.ensureAuth(); const hiddenForm = document.createElement('form'); hiddenForm.action = url; hiddenForm.method = 'POST'; hiddenForm.target = target; const payloadWithToken = { ...payload, csrf_token: this.csrfToken }; if (this.guestToken) { payloadWithToken.guest_token = this.guestToken; } Object.entries(payloadWithToken).forEach(([key, value]) => { const data = document.createElement('input'); data.type = 'hidden'; data.name = key; data.value = value; hiddenForm.appendChild(data); }); document.body.appendChild(hiddenForm); hiddenForm.submit(); document.body.removeChild(hiddenForm); } } async reAuthenticate() { return this.init(true); } isAuthenticated() { // if CSRF protection is disabled in the Superset app, the token may be an empty string return this.csrfToken !== null && this.csrfToken !== undefined; } getGuestToken() { return this.guestToken; } async get( requestConfig) { return this.request({ ...requestConfig, method: 'GET' }); } async delete( requestConfig) { return this.request({ ...requestConfig, method: 'DELETE' }); } async put( requestConfig) { return this.request({ ...requestConfig, method: 'PUT' }); } async post( requestConfig) { return this.request({ ...requestConfig, method: 'POST' }); } async request({ credentials, mode, endpoint, host, url, headers, timeout, fetchRetryOptions, ignoreUnauthorized = false, ...rest }) { await this.ensureAuth(); return callApiAndParseWithTimeout({ ...rest, credentials: credentials != null ? credentials : this.credentials, mode: mode != null ? mode : this.mode, url: this.getUrl({ endpoint, host, url }), headers: { ...this.headers, ...headers }, timeout: timeout != null ? timeout : this.timeout, fetchRetryOptions: fetchRetryOptions != null ? fetchRetryOptions : this.fetchRetryOptions }).catch((res) => { if ((res == null ? void 0 : res.status) === 401 && !ignoreUnauthorized) { this.handleUnauthorized(); } return Promise.reject(res); }); } async ensureAuth() {var _this$csrfPromise; return (_this$csrfPromise = this.csrfPromise) != null ? _this$csrfPromise : // eslint-disable-next-line prefer-promise-reject-errors Promise.reject({ error: `SupersetClient has not been provided a CSRF token, ensure it is initialized with \`client.getCSRFToken()\` or try logging in at ${this.getUrl({ endpoint: '/login' })}` }); } async getCSRFToken() { this.csrfToken = undefined; // If we can request this resource successfully, it means that the user has // authenticated. If not we throw an error prompting to authenticate. this.csrfPromise = callApiAndParseWithTimeout({ credentials: this.credentials, headers: { ...this.headers }, method: 'GET', mode: this.mode, timeout: this.timeout, url: this.getUrl({ endpoint: 'api/v1/security/csrf_token/' }), parseMethod: 'json' }).then(({ json }) => { if (typeof json === 'object') { this.csrfToken = json.result; if (typeof this.csrfToken === 'string') { this.headers = { ...this.headers, 'X-CSRFToken': this.csrfToken }; } } if (this.isAuthenticated()) { return this.csrfToken; } // eslint-disable-next-line prefer-promise-reject-errors return Promise.reject({ error: 'Failed to fetch CSRF token' }); }); return this.csrfPromise; } getUrl({ host: inputHost, endpoint = '', url } = {}) { if (typeof url === 'string') return url; const host = inputHost != null ? inputHost : this.host; const cleanHost = host.slice(-1) === '/' ? host.slice(0, -1) : host; // no backslash return `${this.protocol}//${cleanHost}/${ endpoint[0] === '/' ? endpoint.slice(1) : endpoint}`; } } //# sourceMappingURL=SupersetClientClass.js.map