Skip to content

Commit

Permalink
feat(core): add support for annotations and url params (apache#812)
Browse files Browse the repository at this point in the history
* feat(core): add support for annotations and url params

* add type guards
  • Loading branch information
villebro authored and zhaoyongjie committed Nov 24, 2021
1 parent af4be3a commit a84522f
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const DTTM_ALIAS = '__timestamp';
*/
export default function buildQueryObject<T extends QueryFormData>(formData: T): QueryObject {
const {
annotation_layers = [],
time_range,
since,
until,
Expand All @@ -28,6 +29,7 @@ export default function buildQueryObject<T extends QueryFormData>(formData: T):
timeseries_limit_metric,
queryFields,
granularity,
url_params = {},
...residualFormData
} = formData;

Expand All @@ -49,6 +51,7 @@ export default function buildQueryObject<T extends QueryFormData>(formData: T):
granularity,
...extras,
...extrasAndfilters,
annotation_layers,
groupby: processGroupby(Array.from(groupbySet)),
is_timeseries: groupbySet.has(DTTM_ALIAS),
metrics: metrics.map(convertMetric),
Expand All @@ -60,5 +63,6 @@ export default function buildQueryObject<T extends QueryFormData>(formData: T):
timeseries_limit_metric: timeseries_limit_metric
? convertMetric(timeseries_limit_metric)
: null,
url_params,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export { default as convertFilter } from './convertFilter';
export { default as convertMetric } from './convertMetric';
export { default as DatasourceKey } from './DatasourceKey';

export * from './types/AnnotationLayer';
export * from './types/QueryFormData';
export * from './types/Column';
export * from './types/Datasource';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/* eslint camelcase: 0 */
type BaseAnnotationLayer = {
color?: string | null;
name: string;
opacity?: '' | 'opacityLow' | 'opacityMedium' | 'opacityHigh';
show: boolean;
style: 'dashed' | 'dotted' | 'solid' | 'longDashed';
width?: number;
};

type AnnotationOverrides = {
granularity?: string | null;
time_grain_sqla?: string | null;
time_range?: string | null;
time_shift?: string | null;
};

type LineSourceAnnotationLayer = {
hideLine?: boolean;
overrides?: AnnotationOverrides;
sourceType: 'line';
titleColumn: string;
// viz id
value: number;
};

type NativeSourceAnnotationLayer = {
sourceType: 'NATIVE';
// annotation id
value: number;
};

type TableSourceAnnotationLayer = {
descriptionColumns?: string[];
timeColumn?: string;
intervalEndColumn?: string;
overrides?: AnnotationOverrides;
sourceType: 'table';
titleColumn?: string;
// viz id
value: number;
};

export type EventAnnotationLayer = BaseAnnotationLayer &
(TableSourceAnnotationLayer | NativeSourceAnnotationLayer) & {
annotationType: 'EVENT';
};

export type IntervalAnnotationLayer = BaseAnnotationLayer &
(TableSourceAnnotationLayer | NativeSourceAnnotationLayer) & {
annotationType: 'INTERVAL';
};

export type FormulaAnnotationLayer = BaseAnnotationLayer & {
annotationType: 'FORMULA';
// the mathjs parseable formula
sourceType?: '';
value: string;
};

export type TimeseriesAnnotationLayer = BaseAnnotationLayer &
LineSourceAnnotationLayer & {
annotationType: 'TIME_SERIES';
showMarkers?: boolean;
value: number;
};

export type AnnotationLayer =
| EventAnnotationLayer
| IntervalAnnotationLayer
| FormulaAnnotationLayer
| TimeseriesAnnotationLayer;

export function isFormulaAnnotationLayer(layer: AnnotationLayer): layer is FormulaAnnotationLayer {
return layer.annotationType === 'FORMULA';
}

export function isEventAnnotationLayer(layer: EventAnnotationLayer): layer is EventAnnotationLayer {
return layer.annotationType === 'EVENT';
}

export function isIntervalAnnotationLayer(
layer: IntervalAnnotationLayer,
): layer is IntervalAnnotationLayer {
return layer.annotationType === 'INTERVAL';
}

export function isTimeseriesAnnotationLayer(
layer: AnnotationLayer,
): layer is TimeseriesAnnotationLayer {
return layer.annotationType === 'TIME_SERIES';
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DatasourceType } from './Datasource';
import { AdhocMetric } from './Metric';
import { BinaryOperator, SetOperator, UnaryOperator } from './Operator';
import { AppliedTimeExtras, TimeRange } from './Time';
import { AnnotationLayer } from './AnnotationLayer';
import { QueryFormDataMetric, QueryFormResidualDataValue } from './QueryFormData';

export type QueryObjectFilterClause = {
Expand Down Expand Up @@ -46,6 +47,7 @@ export type ResidualQueryObjectData = {
};

export type QueryObject = {
annotation_layers?: AnnotationLayer[];
/** Time filters that have been applied to the query object */
applied_time_extras?: AppliedTimeExtras;
/** Columns to group by */
Expand Down Expand Up @@ -82,6 +84,7 @@ export type QueryObject = {

/** If set, will group by timestamp */
is_timeseries?: boolean;
url_params?: Record<string, string>;
} & TimeRange &
ResidualQueryObjectData;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AdhocMetric } from './Metric';
import { TimeRange } from './Time';
import { AdhocFilter } from './Filter';
import { BinaryOperator, SetOperator } from './Operator';
import { AnnotationLayer } from './AnnotationLayer';

export type QueryFormDataMetric = string | AdhocMetric;
export type QueryFormResidualDataValue = string | AdhocMetric;
Expand Down Expand Up @@ -72,6 +73,8 @@ export type BaseFormData = {
result_type?: string;
queryFields?: QueryFields;
time_range_endpoints?: TimeRangeEndpoints;
annotation_layers?: AnnotationLayer[];
url_params?: Record<string, string>;
} & TimeRange &
QueryFormResidualData;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { buildQueryObject, QueryObject } from '@superset-ui/core/src/query';
import { AnnotationLayer, buildQueryObject, QueryObject } from '@superset-ui/core/src/query';

describe('buildQueryObject', () => {
let query: QueryObject;
Expand Down Expand Up @@ -120,4 +120,64 @@ describe('buildQueryObject', () => {
expect(query.row_limit).toBeUndefined();
expect(query.row_offset).toBeUndefined();
});

it('should populate annotation_layers', () => {
const annotationLayers: AnnotationLayer[] = [
{
annotationType: 'FORMULA',
color: '#ff7f44',
name: 'My Formula',
opacity: 'opacityLow',
show: true,
style: 'solid',
value: '10*sin(x)',
width: 1,
},
{
annotationType: 'INTERVAL',
color: null,
descriptionColumns: [],
name: 'My Interval',
overrides: { time_range: null },
sourceType: 'NATIVE',
style: 'dashed',
value: 1,
width: 100,
},
{
annotationType: 'EVENT',
color: null,
descriptionColumns: [],
name: 'My Interval',
overrides: {
granularity: null,
time_grain_sqla: null,
time_range: null,
},
sourceType: 'table',
timeColumn: 'ds',
style: 'dashed',
value: 'asdf',
width: 100,
},
];
query = buildQueryObject({
datasource: '5__table',
granularity_sqla: 'ds',
viz_type: 'table',
annotation_layers: annotationLayers,
});
expect(query.annotation_layers).toEqual(annotationLayers);
});

it('should populate url_params', () => {
const urlParams = { abc: '123' };
query = buildQueryObject({
datasource: '5__table',
granularity_sqla: 'ds',
viz_type: 'table',
url_params: urlParams,
});
expect(query.url_params).toEqual(urlParams);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {
isEventAnnotationLayer,
isFormulaAnnotationLayer,
isIntervalAnnotationLayer,
isTimeseriesAnnotationLayer,
} from '@superset-ui/core/src/query/types/AnnotationLayer';

describe('AnnotationLayer type guards', () => {
describe('isFormulaAnnotationLayer', () => {
it('should return true when it is the correct type', () => {
expect(
isFormulaAnnotationLayer({
annotationType: 'FORMULA',
name: 'My Formula',
value: 'sin(2*x)',
style: 'solid',
show: true,
}),
).toEqual(true);
});
it('should return false otherwise', () => {
expect(
isFormulaAnnotationLayer({
annotationType: 'EVENT',
name: 'My Event',
value: 1,
style: 'solid',
show: true,
}),
).toEqual(false);
});
});

describe('isEventAnnotationLayer', () => {
it('should return true when it is the correct type', () => {
expect(
isEventAnnotationLayer({
annotationType: 'EVENT',
name: 'My Event',
value: 1,
style: 'solid',
show: true,
}),
).toEqual(true);
});
it('should return false otherwise', () => {
expect(
isEventAnnotationLayer({
annotationType: 'FORMULA',
name: 'My Formula',
value: 'sin(2*x)',
style: 'solid',
show: true,
}),
).toEqual(false);
});
});

describe('isIntervalAnnotationLayer', () => {
it('should return true when it is the correct type', () => {
expect(
isIntervalAnnotationLayer({
annotationType: 'INTERVAL',
name: 'My Event',
value: 1,
style: 'solid',
show: true,
}),
).toEqual(true);
});
it('should return false otherwise', () => {
expect(
isEventAnnotationLayer({
annotationType: 'FORMULA',
name: 'My Formula',
value: 'sin(2*x)',
style: 'solid',
show: true,
}),
).toEqual(false);
});
});

describe('isTimeseriesAnnotationLayer', () => {
it('should return true when it is the correct type', () => {
expect(
isTimeseriesAnnotationLayer({
annotationType: 'TIME_SERIES',
name: 'My Event',
value: 1,
style: 'solid',
show: true,
}),
).toEqual(true);
});
it('should return false otherwise', () => {
expect(
isTimeseriesAnnotationLayer({
annotationType: 'FORMULA',
name: 'My Formula',
value: 'sin(2*x)',
style: 'solid',
show: true,
}),
).toEqual(false);
});
});
});

0 comments on commit a84522f

Please sign in to comment.