Skip to content

Commit

Permalink
add filter selection
Browse files Browse the repository at this point in the history
  • Loading branch information
MGJamJam committed Apr 25, 2024
1 parent 45a1405 commit a928c87
Show file tree
Hide file tree
Showing 9 changed files with 329 additions and 12 deletions.
66 changes: 66 additions & 0 deletions bitmovin-analytics-datasource/src/components/FilterInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as React from 'react';
import { QueryFilterOperator, SELECTABLE_QUERY_FILTER_OPERATORS, SelectableQueryFilter } from '../types/queryFilter';
import { QueryAttribute, SELECTABLE_QUERY_ATTRIBUTES } from '../types/queryAttributes';
import { QueryAdAttribute, SELECTABLE_QUERY_AD_ATTRIBUTES } from '../types/queryAdAttributes';
import { Field, HorizontalGroup, IconButton, Input, Select } from '@grafana/ui';
import { Space } from '@grafana/plugin-ui';
import { convertFilterValueToProperType } from '../utils/filterUtils';
import { useState } from 'react';

type Props = {
readonly isAdAnalytics: boolean;
readonly filter: SelectableQueryFilter;
readonly onAttributeChange: (newValue: QueryAttribute | QueryAdAttribute) => void;
readonly onOperatorChange: (newValue: QueryFilterOperator) => void;
readonly onFilterValueChange: (newValue: boolean | number | string | Array<string>) => void;
readonly onDelete: () => void;
};
export const FilterInput = (props: Props) => {
const [filterValueError, setFilterValueError] = useState(null);

//TODOMY delay check here, only on enter? Small timeout or check button?
const onFilterValueChange = (value: string, filter: SelectableQueryFilter) => {
try {
const convertedValue = convertFilterValueToProperType(value, filter, props.isAdAnalytics);
setFilterValueError(null);
props.onFilterValueChange(convertedValue);
} catch (e) {
setFilterValueError(e.message);
}
};

return (
<HorizontalGroup spacing="xs">
{
//TODOMY fix autospaching ui issue
}

<Field label="Dimension">
<Select
value={props.filter.name}
onChange={(value) => props.onAttributeChange(value.value as QueryAttribute | QueryAdAttribute)}
options={props.isAdAnalytics ? SELECTABLE_QUERY_AD_ATTRIBUTES : SELECTABLE_QUERY_ATTRIBUTES}
/>
</Field>

<Field label="Operator">
<Select
value={props.filter.operator}
onChange={(value) => props.onOperatorChange(value.value as QueryFilterOperator)}
options={SELECTABLE_QUERY_FILTER_OPERATORS}
/>
</Field>
<Field label="Value" invalid={filterValueError !== null} error={filterValueError}>
<Input onChange={(value) => onFilterValueChange(value.currentTarget.value, props.filter)} />
</Field>
<Space h={1} />
<IconButton
name="trash-alt"
size="xl"
tooltip="Delete Order By"
onClick={() => props.onDelete()}
variant="destructive"
/>
</HorizontalGroup>
);
};
84 changes: 84 additions & 0 deletions bitmovin-analytics-datasource/src/components/FilterRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import * as React from 'react';
import { useState } from 'react';
import { QueryAttribute } from '../types/queryAttributes';
import { QueryAdAttribute } from '../types/queryAdAttributes';
import { FieldSet, IconButton } from '@grafana/ui';
import { QueryFilter, QueryFilterOperator, SelectableQueryFilter } from '../types/queryFilter';
import { FilterInput } from './FilterInput';

type Props = {
readonly isAdAnalytics: boolean;
readonly onChange: (newFilters: QueryFilter[]) => void;
};
export const FilterRow = (props: Props) => {
const [filters, setFilters] = useState<SelectableQueryFilter[]>([]);

const deleteFilter = (index: number) => {
const newFilter = [...filters];
newFilter.splice(index, 1);

setFilters(newFilter);
props.onChange(newFilter as QueryFilter[]);
};

const onAttributesChange = (index: number, newAttribute: QueryAttribute | QueryAdAttribute) => {
const newFilters = [...filters];
const newValue = {
name: newAttribute,
operator: newFilters[index].operator,
value: newFilters[index].value,
} as QueryFilter;
newFilters.splice(index, 1, newValue);
setFilters(newFilters);
props.onChange(newFilters as QueryFilter[]);
};

const onOperatorChange = (index: number, newOperator: QueryFilterOperator) => {
const newFilters = [...filters];
const newValue = {
name: newFilters[index].name,
operator: newOperator,
value: newFilters[index].value,
} as QueryFilter;
newFilters.splice(index, 1, newValue);
setFilters(newFilters);
props.onChange(newFilters as QueryFilter[]);
};

const onFilterValueChange = (index: number, newFilterValue: string | number) => {
const newFilters = [...filters];
const newValue = {
name: newFilters[index].name,
operator: newFilters[index].operator,
value: newFilterValue,
} as QueryFilter;
newFilters.splice(index, 1, newValue);
setFilters(newFilters);
props.onChange(newFilters as QueryFilter[]);
};

const addFilterInput = () => {
//TODOMY what to use as the default value
setFilters((prevState) => [...prevState, { name: '', operator: '', value: '' }]);
};

return (
<FieldSet>
{filters.map((item, index) => (
<FilterInput
isAdAnalytics={props.isAdAnalytics}
filter={filters[index]}
onAttributeChange={(newValue: QueryAttribute | QueryAdAttribute) => onAttributesChange(index, newValue)}
onOperatorChange={(newValue: QueryFilterOperator) => onOperatorChange(index, newValue)}
onFilterValueChange={(newValue: string | number) => onFilterValueChange(index, newValue)}
onDelete={() => deleteFilter(index)}
/>
))}

{
//TODOMY fix position of the IconButton
}
<IconButton name="plus-square" tooltip="Add Order By" size="xl" onClick={() => addFilterInput()} />
</FieldSet>
);
};
11 changes: 11 additions & 0 deletions bitmovin-analytics-datasource/src/components/QueryEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { SELECTABLE_QUERY_ATTRIBUTES } from '../types/queryAttributes';
import { SELECTABLE_QUERY_AD_ATTRIBUTES } from '../types/queryAdAttributes';
import { OrderByRow } from './OrderByRow';
import { QueryOrderBy } from '../types/queryOrderBy';
import { FilterRow } from './FilterRow';
import { QueryFilter } from '../types/queryFilter';

type Props = QueryEditorProps<DataSource, MyQuery, MyDataSourceOptions>;

Expand Down Expand Up @@ -54,6 +56,12 @@ export function QueryEditor({ query, onChange, onRunQuery, datasource }: Props)
onRunQuery();
};

const onFiltersChange = (newFilters: QueryFilter[]) => {
console.log('filters in QueryEditor', newFilters);
onChange({ ...query, filters: newFilters });
onRunQuery();
};

const onFormatAsTimeSeriesChange = (event: ChangeEvent<HTMLInputElement>) => {
setIsTimeSeries(event.currentTarget.checked);
if (event.currentTarget.checked) {
Expand Down Expand Up @@ -117,6 +125,9 @@ export function QueryEditor({ query, onChange, onRunQuery, datasource }: Props)
<InlineField label="Order By" labelWidth={20}>
<OrderByRow isAdAnalytics={datasource.adAnalytics ? true : false} onChange={onOrderByChange} />
</InlineField>
<InlineField label="Filters" labelWidth={20}>
<FilterRow isAdAnalytics={datasource.adAnalytics ? true : false} onChange={onFiltersChange} />
</InlineField>
<InlineField label="Format as time series" labelWidth={20}>
<InlineSwitch value={isTimeSeries} onChange={onFormatAsTimeSeriesChange}></InlineSwitch>
</InlineField>
Expand Down
14 changes: 5 additions & 9 deletions bitmovin-analytics-datasource/src/datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import { catchError, lastValueFrom, map, Observable, of } from 'rxjs';
import { MixedDataRowList, MyDataSourceOptions, MyQuery, NumberDataRowList } from './types';
import { transformGroupedTimeSeriesData, transformSimpleTimeSeries, transformTableData } from './utils/dataUtils';
import { calculateQueryInterval, QueryInterval } from './utils/intervalUtils';
import { QueryFilter } from './types/queryFilter';
import { QueryOrderBy } from './types/queryOrderBy';

type AnalyticsQuery = {
filters: Array<{ name: string; operator: string; value: number }>;
filters: QueryFilter[];
groupBy: string[];
orderBy: Array<{ name: string; order: string }>;
orderBy: QueryOrderBy[];
dimension: string;
start: Date;
end: Date;
Expand Down Expand Up @@ -61,13 +63,7 @@ export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
: undefined;

const query: AnalyticsQuery = {
filters: [
{
name: 'VIDEO_STARTUPTIME',
operator: 'GT',
value: 0,
},
],
filters: target.filters,
groupBy: target.groupBy,
orderBy: target.orderBy,
dimension: target.dimension,
Expand Down
4 changes: 3 additions & 1 deletion bitmovin-analytics-datasource/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import { Aggregation } from './types/aggregations';
import { QueryAttribute } from './types/queryAttributes';
import { QueryAdAttribute } from './types/queryAdAttributes';
import { QueryOrderBy } from './types/queryOrderBy';
import { QueryFilter } from './types/queryFilter';

export interface MyQuery extends DataQuery {
interval?: QueryInterval | 'AUTO';
timeSeries: boolean;
limit: number;
aggregation: Aggregation;
licenseKey: string;
dimension: QueryAttribute | QueryAdAttribute;
groupBy: QueryAttribute[] | QueryAdAttribute[];
orderBy: QueryOrderBy[];
filters: QueryFilter[];
}

export const DEFAULT_QUERY: Partial<MyQuery> = {
Expand All @@ -25,6 +26,7 @@ export const DEFAULT_QUERY: Partial<MyQuery> = {
dimension: 'IMPRESSION_ID',
groupBy: [],
orderBy: [],
filters: [],
};

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SelectableValue } from '@grafana/data';

enum QUERY_AD_ATTRIBUTES {
export enum QUERY_AD_ATTRIBUTES {
ADVERTISER_NAME = 'ADVERTISER_NAME',
AD_CLICKTHROUGH_URL = 'AD_CLICKTHROUGH_URL',
AD_DESCRIPTION = 'AD_DESCRIPTION',
Expand Down
2 changes: 1 addition & 1 deletion bitmovin-analytics-datasource/src/types/queryAttributes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SelectableValue } from '@grafana/data';

enum QUERY_ATTRIBUTES {
export enum QUERY_ATTRIBUTES {
AD = 'AD',
ANALYTICS_VERSION = 'ANALYTICS_VERSION',
AUDIO_BITRATE = 'AUDIO_BITRATE',
Expand Down
43 changes: 43 additions & 0 deletions bitmovin-analytics-datasource/src/types/queryFilter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { QueryAdAttribute } from './queryAdAttributes';
import { SelectableValue } from '@grafana/data';
import { QueryAttribute } from './queryAttributes';

enum QUERY_FILTER_OPERATORS {
GT = 'GT',
GTE = 'GTE',
LT = 'LT',
LTE = 'LTE',
EQ = 'EQ',
NE = 'NE',
CONTAINS = 'CONTAINS',
NOTCONTAINS = 'NOTCONTAINS',
IN = 'IN',
}

export type QueryFilterOperator = keyof typeof QUERY_FILTER_OPERATORS;

export const SELECTABLE_QUERY_FILTER_OPERATORS: SelectableValue<QueryFilterOperator>[] = [
{ value: QUERY_FILTER_OPERATORS.GT, label: QUERY_FILTER_OPERATORS.GT },
{ value: QUERY_FILTER_OPERATORS.GTE, label: QUERY_FILTER_OPERATORS.GTE },
{ value: QUERY_FILTER_OPERATORS.LT, label: QUERY_FILTER_OPERATORS.LT },
{ value: QUERY_FILTER_OPERATORS.LTE, label: QUERY_FILTER_OPERATORS.LTE },
{ value: QUERY_FILTER_OPERATORS.EQ, label: QUERY_FILTER_OPERATORS.EQ },
{ value: QUERY_FILTER_OPERATORS.NE, label: QUERY_FILTER_OPERATORS.NE },
{ value: QUERY_FILTER_OPERATORS.CONTAINS, label: QUERY_FILTER_OPERATORS.CONTAINS },
{ value: QUERY_FILTER_OPERATORS.NOTCONTAINS, label: QUERY_FILTER_OPERATORS.NOTCONTAINS },
{ value: QUERY_FILTER_OPERATORS.IN, label: QUERY_FILTER_OPERATORS.IN },
];

export type QueryFilter = {
name: QueryAdAttribute | QueryAttribute;
operator: QueryFilterOperator;
value: boolean | number | string | Array<string>;
};

export type SelectableQueryFilter = {
name: QueryAdAttribute | QueryAttribute | '';
operator: QueryFilterOperator | '';
value: boolean | number | string | Array<string>;
};

//TODOMY add specific filter attributes
Loading

0 comments on commit a928c87

Please sign in to comment.