Skip to content

Commit

Permalink
merge migrate-to-react into feat/AN-4127_update-readme
Browse files Browse the repository at this point in the history
  • Loading branch information
MGJamJam committed Jun 7, 2024
2 parents 83de950 + e63d6dc commit c2ffc08
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 93 deletions.
23 changes: 13 additions & 10 deletions bitmovin-analytics-datasource/src/components/FilterInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isEmpty } from 'lodash';
import { QueryAttribute, SELECTABLE_QUERY_ATTRIBUTES } from '../types/queryAttributes';
import { QueryAdAttribute, SELECTABLE_QUERY_AD_ATTRIBUTES } from '../types/queryAdAttributes';
import { QueryFilterOperator, SELECTABLE_QUERY_FILTER_OPERATORS } from '../types/queryFilter';
import type { FilterRowData } from './QueryEditor';
import { FilterRowData } from './FilterRow';

const mapAttributeToSelectableValue = (
attribute: QueryAttribute | QueryAdAttribute,
Expand All @@ -31,22 +31,25 @@ type Props = {
readonly onOperatorChange: (newValue: SelectableValue<QueryFilterOperator>) => void;
readonly onValueChange: (newValue: string) => void;
readonly onDelete: () => void;
readonly addFilterDisabled: boolean;
readonly onAddFilter: () => void;
readonly onSaveFilter: () => void;
};

export function FilterInput(props: Props) {
return (
<HorizontalGroup spacing="xs">
<Select
value={mapAttributeToSelectableValue(props.filter.attribute, props.isAdAnalytics)}
onChange={(value) => props.onAttributeChange(value)}
value={
props.filter.attribute
? mapAttributeToSelectableValue(props.filter.attribute, props.isAdAnalytics)
: undefined
}
onChange={(selectableValue) => props.onAttributeChange(selectableValue)}
options={props.selectableFilterAttributes}
width={30}
/>
<Select
value={mapOperatorToSelectableOperator(props.filter.operator)}
onChange={(value) => props.onOperatorChange(value)}
value={props.filter.operator ? mapOperatorToSelectableOperator(props.filter.operator) : undefined}
onChange={(selectableValue) => props.onOperatorChange(selectableValue)}
options={SELECTABLE_QUERY_FILTER_OPERATORS}
width={15}
/>
Expand All @@ -59,17 +62,17 @@ export function FilterInput(props: Props) {
value={props.filter.rawFilterValue}
invalid={!isEmpty(props.filter.parsingValueError)}
type="text"
onChange={(value) => props.onValueChange(value.currentTarget.value)}
onChange={(input) => props.onValueChange(input.currentTarget.value)}
width={30}
/>
</Tooltip>
<IconButton
tooltip="Add Filter"
onClick={props.onAddFilter}
onClick={props.onSaveFilter}
name="check"
size="xl"
variant="primary"
disabled={props.addFilterDisabled}
disabled={isEmpty(props.filter.attribute) || isEmpty(props.filter.operator)}
/>
<IconButton tooltip="Delete Filter" name="trash-alt" onClick={props.onDelete} size="lg" variant="destructive" />
</HorizontalGroup>
Expand Down
113 changes: 67 additions & 46 deletions bitmovin-analytics-datasource/src/components/FilterRow.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import React from 'react';
import { differenceWith, isEmpty } from 'lodash';
import React, { useEffect, useState } from 'react';
import { differenceWith } from 'lodash';
import { SelectableValue } from '@grafana/data';
import { Box, HorizontalGroup, IconButton, InlineLabel, VerticalGroup } from '@grafana/ui';

import { QueryFilter, QueryFilterOperator } from '../types/queryFilter';
import { QueryFilter, QueryFilterOperator, QueryFilterValue } from '../types/queryFilter';
import { QueryAdAttribute, SELECTABLE_QUERY_AD_ATTRIBUTES } from '../types/queryAdAttributes';
import { QueryAttribute, SELECTABLE_QUERY_ATTRIBUTES } from '../types/queryAttributes';
import { FilterInput } from './FilterInput';
import { convertFilterValueToProperType } from '../utils/filterUtils';
import type { FilterRowData } from './QueryEditor';
import { convertFilterValueToProperType, mapQueryFilterValueToRawFilterValue } from '../utils/filterUtils';

export type FilterRowData = {
attribute: QueryAdAttribute | QueryAttribute | undefined;
operator: QueryFilterOperator | undefined;
rawFilterValue: string;
convertedFilterValue: QueryFilterValue;
parsingValueError: string;
};

const mapFilterAttributesToSelectableValue = (
filters: FilterRowData[],
Expand All @@ -32,104 +39,119 @@ const mapFilterAttributesToSelectableValue = (
const mapFilterRowsToQueryFilters = (filters: FilterRowData[]): QueryFilter[] => {
return filters.map((filter) => {
return {
name: filter.attribute,
operator: filter.operator,
name: filter.attribute!,
operator: filter.operator!,
value: filter.convertedFilterValue,
} as QueryFilter;
};
});
};

type Props = {
readonly isAdAnalytics: boolean;
readonly onQueryFilterChange: (newFilters: QueryFilter[]) => void;
readonly onFilterRowChange: (newFilters: FilterRowData[]) => void;
readonly filters: FilterRowData[];
readonly filters: QueryFilter[];
};

export function FilterRow(props: Props) {
const [filterInputs, setFilterInputs] = useState<FilterRowData[]>([]);

/** Map QueryFilters to FilterRowData */
useEffect(() => {
const filterRows = props.filters.map((filter) => {
return {
attribute: filter.name,
operator: filter.operator,
rawFilterValue: mapQueryFilterValueToRawFilterValue(filter.value),
convertedFilterValue: filter.value,
parsingValueError: '',
};
});
setFilterInputs(filterRows);
}, [props.filters]);

const addFilterInput = () => {
const newFilters = [...props.filters];
newFilters.push({
attribute: {},
operator: {},
const newFilterInputs = [...filterInputs];
newFilterInputs.push({
attribute: undefined,
operator: undefined,
rawFilterValue: '',
convertedFilterValue: '',
parsingValueError: '',
} as FilterRowData);
props.onFilterRowChange(newFilters);
});
setFilterInputs(newFilterInputs);
};

const onAddFilter = (index: number) => {
const filter = props.filters[index];
const onSaveFilter = (index: number) => {
const filter = filterInputs[index];
try {
const convertedValue = convertFilterValueToProperType(
filter.rawFilterValue,
filter.attribute,
filter.attribute,
filter.operator,
filter.attribute!,
filter.attribute!,
filter.operator!,
props.isAdAnalytics
);

const newFilter = { ...filter, convertedFilterValue: convertedValue, parsingValueError: '' } as FilterRowData;
const newFilter: FilterRowData = { ...filter, convertedFilterValue: convertedValue, parsingValueError: '' };

const newFilters = [...props.filters];
const newFilters = [...filterInputs];
newFilters.splice(index, 1, newFilter);

props.onFilterRowChange(newFilters);
setFilterInputs(filterInputs);

props.onQueryFilterChange(mapFilterRowsToQueryFilters(newFilters));
} catch (e: unknown) {
if (e instanceof Error) {
const errorMessage = e.message;
const newFilter = { ...filter, parsingValueError: errorMessage } as FilterRowData;
const newFilter: FilterRowData = { ...filter, parsingValueError: errorMessage };

const newFilters = [...props.filters];
const newFilters = [...filterInputs];
newFilters.splice(index, 1, newFilter);

props.onFilterRowChange(newFilters);
setFilterInputs(newFilters);
}
}
};

const deleteFilterInput = (index: number) => {
const newFilters = [...props.filters];
const newFilters = [...filterInputs];
newFilters.splice(index, 1);

props.onFilterRowChange(newFilters);
setFilterInputs(newFilters);

props.onQueryFilterChange(mapFilterRowsToQueryFilters(newFilters));
};

const onAttributesChange = (index: number, newAttribute: SelectableValue<QueryAttribute | QueryAdAttribute>) => {
const filter = props.filters[index];
const newFilter = { ...filter, attribute: newAttribute.value } as FilterRowData;
const newFilters = [...props.filters];
const filter = filterInputs[index];
const newFilter: FilterRowData = { ...filter, attribute: newAttribute.value! };
const newFilters = [...filterInputs];
newFilters.splice(index, 1, newFilter);

props.onFilterRowChange(newFilters);
setFilterInputs(newFilters);
};

const onOperatorsChange = (index: number, newOperator: SelectableValue<QueryFilterOperator>) => {
const filter = props.filters[index];
const newFilter = { ...filter, operator: newOperator.value } as FilterRowData;
const newFilters = [...props.filters];
const filter = filterInputs[index];
const newFilter: FilterRowData = { ...filter, operator: newOperator.value! };
const newFilters = [...filterInputs];
newFilters.splice(index, 1, newFilter);

props.onFilterRowChange(newFilters);
setFilterInputs(newFilters);
};

const onValuesChange = (index: number, newValue: string) => {
const filter = props.filters[index];
const newFilter = { ...filter, rawFilterValue: newValue };
const newFilters = [...props.filters];
const filter = filterInputs[index];
const newFilter: FilterRowData = { ...filter, rawFilterValue: newValue };
const newFilters = [...filterInputs];
newFilters.splice(index, 1, newFilter);

props.onFilterRowChange(newFilters);
setFilterInputs(newFilters);
};

return (
<VerticalGroup>
{props.filters.length !== 0 && (
{filterInputs.length > 0 && (
<HorizontalGroup spacing={'none'}>
<InlineLabel width={30} tooltip="">
Dimension
Expand All @@ -142,7 +164,7 @@ export function FilterRow(props: Props) {
</InlineLabel>
</HorizontalGroup>
)}
{props.filters.map((filter, index, filtersArray) => (
{filterInputs.map((filter, index, filtersArray) => (
<FilterInput
key={index}
isAdAnalytics={props.isAdAnalytics}
Expand All @@ -154,12 +176,11 @@ export function FilterRow(props: Props) {
onOperatorChange={(newValue: SelectableValue<QueryFilterOperator>) => onOperatorsChange(index, newValue)}
onValueChange={(newValue: string) => onValuesChange(index, newValue)}
onDelete={() => deleteFilterInput(index)}
addFilterDisabled={isEmpty(filter.attribute) || isEmpty(filter.operator)}
onAddFilter={() => onAddFilter(index)}
onSaveFilter={() => onSaveFilter(index)}
/>
))}

<Box paddingTop={props.filters.length === 0 ? 0.5 : 0}>
<Box paddingTop={filterInputs.length === 0 ? 0.5 : 0}>
<IconButton name="plus-square" tooltip="Add Filter" onClick={() => addFilterInput()} size="xl" />
</Box>
</VerticalGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function GroupByInput(props: Props) {
<HorizontalGroup>
<Select
value={isEmpty(props.groupBy) ? undefined : props.groupBy}
onChange={(value) => props.onChange(value.value!)}
onChange={(selectableValue) => props.onChange(selectableValue.value!)}
options={props.selectableGroupBys}
width={30}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function OrderByInput(props: Props) {
<HorizontalGroup spacing="xs">
<Select
value={isEmpty(props.attribute) ? undefined : props.attribute}
onChange={(value) => props.onAttributeChange(value)}
onChange={(selectableValue) => props.onAttributeChange(selectableValue)}
options={props.selectableOrderByAttributes}
width={30}
/>
Expand Down
4 changes: 2 additions & 2 deletions bitmovin-analytics-datasource/src/components/OrderByRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function OrderByRow(props: Props) {

const onAttributesChange = (index: number, newAttribute: SelectableValue<QueryAttribute | QueryAdAttribute>) => {
const newOrderBys = [...props.orderBys];
const newOrderBy = { name: newAttribute.value, order: newOrderBys[index].order } as QueryOrderBy;
const newOrderBy: QueryOrderBy = { name: newAttribute.value!, order: newOrderBys[index].order };

newOrderBys.splice(index, 1, newOrderBy);

Expand All @@ -64,7 +64,7 @@ export function OrderByRow(props: Props) {

const onSortOrdersChange = (index: number, newSortOrder: QuerySortOrder) => {
const newOrderBys = [...props.orderBys];
const newOrderBy = { name: newOrderBys[index].name, order: newSortOrder } as QueryOrderBy;
const newOrderBy: QueryOrderBy = { name: newOrderBys[index].name, order: newSortOrder };

newOrderBys.splice(index, 1, newOrderBy);

Expand Down
33 changes: 3 additions & 30 deletions bitmovin-analytics-datasource/src/components/QueryEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ import { isMetric, SELECTABLE_METRICS } from '../types/metric';
import { GroupByRow } from './GroupByRow';
import { OrderByRow } from './OrderByRow';
import type { QueryOrderBy } from '../types/queryOrderBy';
import type { QueryFilter, QueryFilterOperator, QueryFilterValue } from '../types/queryFilter';
import type { QueryFilter } from '../types/queryFilter';
import { FilterRow } from './FilterRow';
import { mapFilterValueToRawFilterValue } from '../utils/filterUtils';

enum LoadingState {
Default = 'DEFAULT',
Expand All @@ -25,26 +24,18 @@ enum LoadingState {
Error = 'ERROR',
}

export type FilterRowData = {
attribute: QueryAdAttribute | QueryAttribute;
operator: QueryFilterOperator;
rawFilterValue: string;
convertedFilterValue: QueryFilterValue;
parsingValueError: string;
};

type Props = QueryEditorProps<DataSource, BitmovinAnalyticsDataQuery, BitmovinDataSourceOptions>;

export function QueryEditor(props: Props) {
const [selectableLicenses, setSelectableLicenses] = useState<SelectableValue[]>([]);
const [licenseLoadingState, setLicenseLoadingState] = useState<LoadingState>(LoadingState.Default);
const [licenseErrorMessage, setLicenseErrorMessage] = useState('');
const [isTimeSeries, setIsTimeSeries] = useState(!!props.query.interval);
const [filterRows, setFilterRows] = useState<FilterRowData[]>([]);
const isDimensionMetricSelected = useMemo(() => {
return props.query.metric !== undefined;
}, [props.query.metric]);

/** Fetch Licenses */
useEffect(() => {
setLicenseLoadingState(LoadingState.Loading);
fetchLicenses(props.datasource.apiKey, props.datasource.baseUrl)
Expand All @@ -58,19 +49,6 @@ export function QueryEditor(props: Props) {
});
}, [props.datasource.apiKey, props.datasource.baseUrl]);

useEffect(() => {
const filterRows = props.query.filters.map((filter) => {
return {
attribute: filter.name,
operator: filter.operator,
rawFilterValue: mapFilterValueToRawFilterValue(filter.value),
convertedFilterValue: filter.value,
parsingValueError: '',
} as FilterRowData;
});
setFilterRows(filterRows);
}, [props.query.filters]);

const query = defaults(props.query, DEFAULT_QUERY);

const handleLicenseChange = (item: SelectableValue) => {
Expand Down Expand Up @@ -102,10 +80,6 @@ export function QueryEditor(props: Props) {
props.onRunQuery();
};

const handleFilterRowChange = (newFilters: FilterRowData[]) => {
setFilterRows(newFilters);
};

const handleQueryFilterChange = (newFilters: QueryFilter[]) => {
props.onChange({ ...query, filters: newFilters });
props.onRunQuery();
Expand Down Expand Up @@ -199,8 +173,7 @@ export function QueryEditor(props: Props) {
<FilterRow
isAdAnalytics={props.datasource.adAnalytics ? true : false}
onQueryFilterChange={handleQueryFilterChange}
onFilterRowChange={handleFilterRowChange}
filters={filterRows}
filters={props.query.filters}
/>
</InlineField>
<InlineField label="Group By" labelWidth={20}>
Expand Down
2 changes: 1 addition & 1 deletion bitmovin-analytics-datasource/src/types/aggregations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SelectableValue } from '@grafana/data';
import type { SelectableValue } from '@grafana/data';

export type Aggregation = 'count' | 'sum' | 'avg' | 'min' | 'max' | 'stddev' | 'percentile' | 'variance' | 'median';

Expand Down
Loading

0 comments on commit c2ffc08

Please sign in to comment.