UNPKG

@mcdevsl/superset-ui

Version:
167 lines (146 loc) 5.39 kB
import { isDefined, SupersetClient, SupersetClientInterface, RequestConfig, SupersetClientClass, QueryFormData, Datasource, } from '../..'; import getChartBuildQueryRegistry from '../registries/ChartBuildQueryRegistrySingleton'; import getChartMetadataRegistry from '../registries/ChartMetadataRegistrySingleton'; import { QueryData } from '../types/QueryResponse'; import { AnnotationLayerMetadata } from '../types/Annotation'; import { PlainObject } from '../types/Base'; // This expands to Partial<All> & (union of all possible single-property types) type AtLeastOne<All, Each = { [K in keyof All]: Pick<All, K> }> = Partial<All> & Each[keyof Each]; export type SliceIdAndOrFormData = AtLeastOne<{ sliceId: number; formData: Partial<QueryFormData>; }>; interface AnnotationData { [key: string]: PlainObject; } export interface ChartData { annotationData: AnnotationData; datasource: PlainObject; formData: QueryFormData; queriesData: QueryData[]; } export default class ChartClient { readonly client: SupersetClientInterface | SupersetClientClass; constructor( config: { client?: SupersetClientInterface | SupersetClientClass; } = {}, ) { const { client = SupersetClient } = config; this.client = client; } loadFormData( input: SliceIdAndOrFormData, options?: Partial<RequestConfig>, ): Promise<QueryFormData> { /* If sliceId is provided, use it to fetch stored formData from API */ if ('sliceId' in input) { const promise = this.client .get({ endpoint: `/api/v1/form_data/?slice_id=${input.sliceId}`, ...options, } as RequestConfig) .then(response => response.json as QueryFormData); /* * If formData is also specified, override API result * with user-specified formData */ return promise.then((dbFormData: QueryFormData) => ({ ...dbFormData, ...input.formData, })); } /* If sliceId is not provided, returned formData wrapped in a Promise */ return input.formData ? Promise.resolve(input.formData as QueryFormData) : Promise.reject(new Error('At least one of sliceId or formData must be specified')); } async loadQueryData( formData: QueryFormData, options?: Partial<RequestConfig>, ): Promise<QueryData[]> { const { viz_type: visType } = formData; const metaDataRegistry = getChartMetadataRegistry(); const buildQueryRegistry = getChartBuildQueryRegistry(); if (metaDataRegistry.has(visType)) { const { useLegacyApi } = metaDataRegistry.get(visType)!; const buildQuery = (await buildQueryRegistry.get(visType)) ?? (() => formData); const requestConfig: RequestConfig = useLegacyApi ? { endpoint: '/superset/explore_json/', postPayload: { form_data: buildQuery(formData), }, ...options, } : { endpoint: '/api/v1/chart/data', jsonPayload: { query_context: buildQuery(formData), }, ...options, }; return this.client .post(requestConfig) .then(response => (Array.isArray(response.json) ? response.json : [response.json])); } return Promise.reject(new Error(`Unknown chart type: ${visType}`)); } loadDatasource(datasourceKey: string, options?: Partial<RequestConfig>): Promise<Datasource> { return this.client .get({ endpoint: `/superset/fetch_datasource_metadata?datasourceKey=${datasourceKey}`, ...options, } as RequestConfig) .then(response => response.json as Datasource); } // eslint-disable-next-line class-methods-use-this loadAnnotation(annotationLayer: AnnotationLayerMetadata): Promise<AnnotationData> { /* When annotation does not require query */ if (!isDefined(annotationLayer.sourceType)) { return Promise.resolve({} as AnnotationData); } // TODO: Implement return Promise.reject(new Error('This feature is not implemented yet.')); } loadAnnotations(annotationLayers?: AnnotationLayerMetadata[]): Promise<AnnotationData> { if (Array.isArray(annotationLayers) && annotationLayers.length > 0) { return Promise.all(annotationLayers.map(layer => this.loadAnnotation(layer))).then(results => annotationLayers.reduce((prev, layer, i) => { const output: AnnotationData = prev; output[layer.name] = results[i]; return output; }, {}), ); } return Promise.resolve({}); } loadChartData(input: SliceIdAndOrFormData): Promise<ChartData> { return this.loadFormData(input).then( ( formData: QueryFormData & { // eslint-disable-next-line camelcase annotation_layers?: AnnotationLayerMetadata[]; }, ) => Promise.all([ this.loadAnnotations(formData.annotation_layers), this.loadDatasource(formData.datasource), this.loadQueryData(formData), ]).then(([annotationData, datasource, queriesData]) => ({ annotationData, datasource, formData, queriesData, })), ); } }