Skip to content
This repository has been archived by the owner on Dec 10, 2021. It is now read-only.

Commit

Permalink
refactor(plugin-chart-table): migrate to API v1
Browse files Browse the repository at this point in the history
  • Loading branch information
ktmud committed Jan 11, 2021
1 parent 950f236 commit 63a9943
Show file tree
Hide file tree
Showing 47 changed files with 13,829 additions and 20,615 deletions.
4 changes: 4 additions & 0 deletions packages/superset-ui-chart-controls/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ export * from './components/MetricOption';
// React control components
export * from './components/RadioButtonControl';
export * from './types';

// re-export * from pure types file may cause
// `module undefined` error in babel/webpack,
export { default as __hack__ } from './types';
2 changes: 2 additions & 0 deletions packages/superset-ui-chart-controls/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,3 +325,5 @@ export type ControlOverrides = {
export type SectionOverrides = {
[P in SharedSectionAlias]?: Partial<ControlPanelSectionConfig>;
};

export default {};
2 changes: 1 addition & 1 deletion packages/superset-ui-core/src/chart/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ export { default as getChartTransformPropsRegistry } from './registries/ChartTra

export { default as ChartDataProvider } from './components/ChartDataProvider';

export { SetExtraFormDataHook } from './types/Base';
export * from './types/Base';
export * from './types/TransformFunction';
export * from './types/QueryResponse';
51 changes: 28 additions & 23 deletions packages/superset-ui-core/src/chart/models/ChartPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import getChartControlPanelRegistry from '../registries/ChartControlPanelRegistr
import getChartTransformPropsRegistry from '../registries/ChartTransformPropsRegistrySingleton';
import { BuildQueryFunction, TransformProps } from '../types/TransformFunction';
import { ChartControlPanel } from './ChartControlPanel';
import { ChartProps } from '..';

function IDENTITY<T>(x: T) {
return x;
Expand All @@ -20,16 +21,19 @@ export type PromiseOrValueLoader<T> = () => PromiseOrValue<T>;
export type ChartType = ComponentType<any>;
type ValueOrModuleWithValue<T> = T | { default: T };

interface ChartPluginConfig<T extends QueryFormData> {
interface ChartPluginConfig<
FormData extends QueryFormData = QueryFormData,
Props extends ChartProps = ChartProps
> {
metadata: ChartMetadata;
/** Use buildQuery for immediate value. For lazy-loading, use loadBuildQuery. */
buildQuery?: BuildQueryFunction<T>;
buildQuery?: BuildQueryFunction<FormData>;
/** Use loadBuildQuery for dynamic import (lazy-loading) */
loadBuildQuery?: PromiseOrValueLoader<ValueOrModuleWithValue<BuildQueryFunction<T>>>;
loadBuildQuery?: PromiseOrValueLoader<ValueOrModuleWithValue<BuildQueryFunction<FormData>>>;
/** Use transformProps for immediate value. For lazy-loading, use loadTransformProps. */
transformProps?: TransformProps;
transformProps?: TransformProps<Props>;
/** Use loadTransformProps for dynamic import (lazy-loading) */
loadTransformProps?: PromiseOrValueLoader<ValueOrModuleWithValue<TransformProps>>;
loadTransformProps?: PromiseOrValueLoader<ValueOrModuleWithValue<TransformProps<Props>>>;
/** Use Chart for immediate value. For lazy-loading, use loadChart. */
Chart?: ChartType;
/** Use loadChart for dynamic import (lazy-loading) */
Expand All @@ -54,18 +58,21 @@ function sanitizeLoader<T>(
};
}

export default class ChartPlugin<T extends QueryFormData = QueryFormData> extends Plugin {
export default class ChartPlugin<
FormData extends QueryFormData = QueryFormData,
Props extends ChartProps = ChartProps
> extends Plugin {
controlPanel: ChartControlPanel;

metadata: ChartMetadata;

loadBuildQuery?: PromiseOrValueLoader<BuildQueryFunction<T>>;
loadBuildQuery?: PromiseOrValueLoader<BuildQueryFunction<FormData>>;

loadTransformProps: PromiseOrValueLoader<TransformProps>;
loadTransformProps: PromiseOrValueLoader<TransformProps<Props>>;

loadChart: PromiseOrValueLoader<ChartType>;

constructor(config: ChartPluginConfig<T>) {
constructor(config: ChartPluginConfig<FormData, Props>) {
super();
const {
metadata,
Expand Down Expand Up @@ -95,26 +102,24 @@ export default class ChartPlugin<T extends QueryFormData = QueryFormData> extend
}

register() {
const { key = isRequired('config.key') } = this.config;
getChartMetadataRegistry().registerValue(key as string, this.metadata);
getChartComponentRegistry().registerLoader(key as string, this.loadChart);
getChartControlPanelRegistry().registerValue(key as string, this.controlPanel);
getChartTransformPropsRegistry().registerLoader(key as string, this.loadTransformProps);
const key: string = this.config.key || isRequired('config.key');
getChartMetadataRegistry().registerValue(key, this.metadata);
getChartComponentRegistry().registerLoader(key, this.loadChart);
getChartControlPanelRegistry().registerValue(key, this.controlPanel);
getChartTransformPropsRegistry().registerLoader(key, this.loadTransformProps);
if (this.loadBuildQuery) {
getChartBuildQueryRegistry().registerLoader(key as string, this.loadBuildQuery);
getChartBuildQueryRegistry().registerLoader(key, this.loadBuildQuery);
}

return this;
}

unregister() {
const { key = isRequired('config.key') } = this.config;
getChartMetadataRegistry().remove(key as string);
getChartComponentRegistry().remove(key as string);
getChartControlPanelRegistry().remove(key as string);
getChartTransformPropsRegistry().remove(key as string);
getChartBuildQueryRegistry().remove(key as string);

const key: string = this.config.key || isRequired('config.key');
getChartMetadataRegistry().remove(key);
getChartComponentRegistry().remove(key);
getChartControlPanelRegistry().remove(key);
getChartTransformPropsRegistry().remove(key);
getChartBuildQueryRegistry().remove(key);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Registry, makeSingleton, OverwritePolicy } from '../..';
import { TransformProps } from '../types/TransformFunction';

class ChartTransformPropsRegistry extends Registry<TransformProps> {
class ChartTransformPropsRegistry extends Registry<TransformProps<any>> {
constructor() {
super({ name: 'ChartTransformProps', overwritePolicy: OverwritePolicy.WARN });
}
Expand Down
16 changes: 13 additions & 3 deletions packages/superset-ui-core/src/chart/types/QueryResponse.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Types for query response
*/
import { DataRecordValue, DataRecord } from '../../types';
import { DataRecordValue, DataRecord, ChartDataResponseResult } from '../../types';
import { PlainObject } from './Base';

export interface TimeseriesDataRecord extends DataRecord {
Expand All @@ -13,5 +13,15 @@ export interface DataRecordFilters {
[key: string]: DataRecordValue[];
}

// the response json from query API
export type QueryData = PlainObject;
/**
* Legacy queried data for charts. List of arbitrary dictionaries generated
* by `viz.py`.
* TODO: clean this up when all charts have been migrated to v1 API.
*/
export type LegacyQueryData = PlainObject;

/**
* Ambiguous query data type. Reserved for the generic QueryFormData.
* Don't use this for a specific chart (since you know which API it uses already).
*/
export type QueryData = LegacyQueryData | ChartDataResponseResult;
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { QueryFormData, QueryContext } from '../..';
import ChartProps from '../models/ChartProps';
import { PlainObject } from './Base';

export interface PlainProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any;
}
export type PlainProps = PlainObject;

type TransformFunction<Input = PlainProps, Output = PlainProps> = (x: Input) => Output;

export type PreTransformProps = TransformFunction<ChartProps, ChartProps>;
export type TransformProps = TransformFunction<ChartProps>;
export type TransformProps<Props extends ChartProps = ChartProps> = TransformFunction<Props>;
export type PostTransformProps = TransformFunction;

export type BuildQueryFunction<T extends QueryFormData> = (formData: T) => QueryContext;
3 changes: 1 addition & 2 deletions packages/superset-ui-core/src/query/buildQueryObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default function buildQueryObject<T extends QueryFormData>(

const numericRowLimit = Number(row_limit);
const numericRowOffset = Number(row_offset);
const { metrics, groupby, columns } = extractQueryFields(residualFormData, queryFields);
const { metrics, columns } = extractQueryFields(residualFormData, queryFields);

const extras = extractExtras(formData);
const extrasAndfilters = processFilters({
Expand All @@ -54,7 +54,6 @@ export default function buildQueryObject<T extends QueryFormData>(
...extras,
...extrasAndfilters,
annotation_layers,
groupby,
columns,
metrics,
order_desc: typeof order_desc === 'undefined' ? true : order_desc,
Expand Down
48 changes: 33 additions & 15 deletions packages/superset-ui-core/src/query/extractQueryFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/
import { DTTM_ALIAS } from './buildQueryObject';
import { QueryFields, QueryFieldAliases, FormDataResidual } from './types/QueryFormData';
import { QueryFields, QueryFieldAliases, FormDataResidual, QueryMode } from './types/QueryFormData';

/**
* Extra SQL query related fields from chart form data.
Expand All @@ -34,7 +34,6 @@ export default function extractQueryFields(
const queryFieldAliases: QueryFieldAliases = {
/** These are predefined for backward compatibility */
metric: 'metrics',
percent_metrics: 'metrics',
metric_2: 'metrics',
secondary_metric: 'metrics',
x: 'metrics',
Expand All @@ -46,26 +45,45 @@ export default function extractQueryFields(
};
const finalQueryFields: QueryFields = {
columns: [],
groupby: [],
metrics: [],
};
const { query_mode: queryMode, include_time: includeTime, ...restFormData } = formData;

Object.entries(formData).forEach(([key, value]) => {
const normalizedKey = queryFieldAliases[key] || key;
if (normalizedKey in finalQueryFields) {
if (normalizedKey === 'metrics') {
finalQueryFields[normalizedKey] = finalQueryFields[normalizedKey].concat(value);
} else {
// currently the groupby and columns field only accept pre-defined columns (string shortcut)
finalQueryFields[normalizedKey] = finalQueryFields[normalizedKey]
.concat(value)
.filter(x => typeof x === 'string' && x);
Object.entries(restFormData).forEach(([key, value]) => {
// ignore `null` or `undefined` value
if (value == null) {
return;
}

let normalizedKey: string = queryFieldAliases[key] || key;

// ignore groupby when in raw records mode
if (queryMode === QueryMode.raw && normalizedKey === 'groupby') {
return;
}
if (queryMode === QueryMode.aggregate) {
// ignore columns when (specifically) in aggregate mode.
if (normalizedKey === 'columns') {
return;
}
}
// groupby has been deprecated: https://github.com/apache/superset/pull/9366
if (normalizedKey === 'groupby') {
normalizedKey = 'columns';
}
if (normalizedKey === 'metrics') {
finalQueryFields[normalizedKey] = finalQueryFields[normalizedKey].concat(value);
}
if (normalizedKey === 'columns') {
// currently the columns field only accept pre-defined columns (string shortcut)
finalQueryFields[normalizedKey] = finalQueryFields[normalizedKey]
.concat(value)
.filter(x => typeof x === 'string' && x);
}
});

if (formData.include_time && !finalQueryFields.groupby.includes(DTTM_ALIAS)) {
finalQueryFields.groupby.unshift(DTTM_ALIAS);
if (includeTime && !finalQueryFields.columns.includes(DTTM_ALIAS)) {
finalQueryFields.columns.unshift(DTTM_ALIAS);
}

return finalQueryFields;
Expand Down
2 changes: 1 addition & 1 deletion packages/superset-ui-core/src/query/types/Metric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export type AdhocMetric = AdhocMetricSimple | AdhocMetricSQL;
/**
* Select a predefined metric by its `metric_name`.
*/
export type PredefinedMetric = string;
export type SavedMetric = string;

/**
* Metric definition stored in datasource metadata.
Expand Down
118 changes: 118 additions & 0 deletions packages/superset-ui-core/src/query/types/PostProcessing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { JsonObject } from '../../connection';
import { TimeGranularity } from '../../time-format';

/**
* 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.
*/
export type NumpyFunction =
| 'average'
| 'argmin'
| 'argmax'
| 'count'
| 'count_nonzero'
| 'cumsum'
| 'cumprod'
| 'max'
| 'mean'
| 'median'
| 'nansum'
| 'nanmin'
| 'nanmax'
| 'nanmean'
| 'nanmedian'
| 'nanpercentile'
| 'min'
| 'percentile'
| 'prod'
| 'product'
| 'std'
| 'sum'
| 'var';

interface Aggregates {
/**
* The name of the generated aggregate column.
*/
[colname: string]: {
operator: NumpyFunction;
/**
* the name of the column to generate aggrates from.
*/
column?: string;
options?: JsonObject;
};
}

export interface PostProcessingAggregation {
operation: 'aggregation';
options: {
groupby: string[];
aggregates: Aggregates;
};
}

export interface PostProcessingBoxplot {
operation: 'boxplot';
options: {
groupby: string[];
metrics: string[];
whisker_type: 'tukey' | 'min/max' | 'percentile';
percentiles?: [number, number];
};
}

export interface PostProcessingContribution {
operation: 'contribution';
options?: {
orientation?: 'row' | 'column';
columns?: string[];
rename_columns?: string[];
};
}

export interface PostProcessingPivot {
operation: 'pivot';
options: {
index: string[];
columns: string[];
aggregates: Aggregates;
};
}

export interface PostProcessingProphet {
operation: 'prophet';
options: {
time_grain: TimeGranularity;
periods: number;
confidence_interval: number;
yearly_seasonality?: boolean | number;
weekly_seasonality?: boolean | number;
daily_seasonality?: boolean | number;
};
}

/**
* Parameters for chart data postprocessing.
* See superset/utils/pandas_processing.py.
*/
export type PostProcessingRule =
| PostProcessingAggregation
| PostProcessingBoxplot
| PostProcessingContribution
| PostProcessingPivot
| PostProcessingProphet;
Loading

0 comments on commit 63a9943

Please sign in to comment.