Skip to content

Commit

Permalink
feat(ReactComponents): support alarms via thresholds ingested from us…
Browse files Browse the repository at this point in the history
…eTimeSeriesData hook
  • Loading branch information
diehbria committed Mar 17, 2023
1 parent dc5661e commit 4df9ff0
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 49 deletions.
14 changes: 9 additions & 5 deletions packages/react-components/src/components/kpi/kpi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@ import { useTimeSeriesData } from '../../hooks/useTimeSeriesData';
import { useViewport } from '../../hooks/useViewport';
import { widgetPropertiesFromInputs } from '../../common/widgetPropertiesFromInputs';
import { DEFAULT_VIEWPORT } from '../../common/constants';
import type { Annotations, StyleSettingsMap, Viewport, TimeSeriesDataQuery } from '@iot-app-kit/core';
import type { Threshold, StyleSettingsMap, Viewport, TimeSeriesDataQuery } from '@iot-app-kit/core';
import type { KPISettings } from './types';

export const Kpi = ({
query,
viewport: passedInViewport,
annotations,
thresholds = [],
styles,
settings,
}: {
query: TimeSeriesDataQuery;
viewport?: Viewport;
annotations?: Annotations;
thresholds?: Threshold[];
styles?: StyleSettingsMap;
settings?: KPISettings;
}) => {
const { dataStreams } = useTimeSeriesData({
const { dataStreams, thresholds: queryThresholds } = useTimeSeriesData({
viewport: passedInViewport,
queries: [query],
settings: { fetchMostRecentBeforeEnd: true },
Expand All @@ -31,7 +31,11 @@ export const Kpi = ({
const utilizedViewport = passedInViewport || viewport || DEFAULT_VIEWPORT; // explicitly passed in viewport overrides viewport group

const { propertyPoint, alarmPoint, alarmThreshold, propertyThreshold, alarmStream, propertyStream } =
widgetPropertiesFromInputs({ dataStreams, annotations, viewport: utilizedViewport });
widgetPropertiesFromInputs({
dataStreams,
annotations: { y: [...queryThresholds, ...thresholds] },
viewport: utilizedViewport,
});

const name = propertyStream?.name || alarmStream?.name;
const unit = propertyStream?.unit || alarmStream?.unit;
Expand Down
14 changes: 9 additions & 5 deletions packages/react-components/src/components/status/status.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useTimeSeriesData } from '../../hooks/useTimeSeriesData';
import { widgetPropertiesFromInputs } from '../../common/widgetPropertiesFromInputs';
import { useViewport } from '../../hooks/useViewport';
import type {
Annotations,
Threshold,
TimeQuery,
TimeSeriesData,
TimeSeriesDataRequest,
Expand All @@ -18,16 +18,16 @@ export const Status = ({
query,
viewport: passedInViewport,
styles,
annotations,
thresholds = [],
settings,
}: {
query: TimeQuery<TimeSeriesData[], TimeSeriesDataRequest>;
viewport?: Viewport;
annotations?: Annotations;
thresholds?: Threshold[];
styles?: StyleSettingsMap;
settings?: StatusSettings;
}) => {
const { dataStreams } = useTimeSeriesData({
const { dataStreams, thresholds: queryThresholds } = useTimeSeriesData({
viewport: passedInViewport,
queries: [query],
settings: { fetchMostRecentBeforeEnd: true },
Expand All @@ -38,7 +38,11 @@ export const Status = ({
const utilizedViewport = passedInViewport || viewport || DEFAULT_VIEWPORT; // explicitly passed in viewport overrides viewport group

const { propertyPoint, alarmPoint, alarmThreshold, propertyThreshold, alarmStream, propertyStream } =
widgetPropertiesFromInputs({ dataStreams, annotations, viewport: utilizedViewport });
widgetPropertiesFromInputs({
dataStreams,
annotations: { y: [...queryThresholds, ...thresholds] },
viewport: utilizedViewport,
});

const name = alarmStream?.name || propertyStream?.name;
const unit = alarmStream?.unit || (alarmStream == null && propertyStream?.unit) || undefined;
Expand Down
7 changes: 4 additions & 3 deletions packages/react-components/src/components/table/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const DEFAULT_VIEWPORT: Viewport = { duration: '10m' };
export const Table = ({
queries,
viewport: passedInViewport,
thresholds,
thresholds = [],
columnDefinitions,
propertyFiltering,
items,
Expand All @@ -31,21 +31,22 @@ export const Table = ({
viewport?: Viewport;
styles?: StyleSettingsMap;
} & Pick<TableBaseProps, 'resizableColumns'>) => {
const { dataStreams } = useTimeSeriesData({
const { dataStreams, thresholds: queryThresholds } = useTimeSeriesData({
viewport: passedInViewport,
queries,
settings: { fetchMostRecentBeforeEnd: true },
styles,
});
const { viewport } = useViewport();
const allThresholds = [...queryThresholds, ...thresholds];

const utilizedViewport = passedInViewport || viewport || DEFAULT_VIEWPORT; // explicitly passed in viewport overrides viewport group
const itemsWithData = createTableItems(
{
dataStreams,
items,
viewport: utilizedViewport,
thresholds,
thresholds: allThresholds,
},
DEFAULT_TABLE_MESSAGES
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,10 @@
import { renderHook } from '@testing-library/react';
import { mockTimeSeriesDataQuery } from '@iot-app-kit/testing-util';
import { useTimeSeriesData } from './useTimeSeriesData';
import type { DataStream, TimeQuery, TimeSeriesData, TimeSeriesDataRequest, Viewport } from '@iot-app-kit/core';

const noop = () => {};

const queryCreator = (
timeSeriesData: TimeSeriesData[],
overrides?: { updateViewport?: (viewport: Viewport) => void; unsubscribe?: () => void }
): TimeQuery<TimeSeriesData[], TimeSeriesDataRequest> => {
const { updateViewport = noop, unsubscribe = noop } = overrides || {};

return {
toQueryString: () =>
JSON.stringify({
source: 'mock',
query: timeSeriesData,
}),
build: () => ({
subscribe: ({ next }) => {
next(timeSeriesData);
},
unsubscribe,
updateViewport,
}),
};
};
import type { DataStream, TimeSeriesData } from '@iot-app-kit/core';

it('returns no time series data when query returns no time series data', () => {
const queries = [queryCreator([])];
const queries = [mockTimeSeriesDataQuery([])];
const {
result: { current: timeSeriesData },
} = renderHook(() =>
Expand All @@ -42,7 +19,7 @@ it('returns no time series data when query returns no time series data', () => {

it('provides time series data returned from query', () => {
const QUERY_RESPONSE: TimeSeriesData[] = [{ dataStreams: [], viewport: { duration: '5m' }, annotations: { y: [] } }];
const queries = [queryCreator(QUERY_RESPONSE)];
const queries = [mockTimeSeriesDataQuery(QUERY_RESPONSE)];
const viewport = { duration: '5m' };

const {
Expand All @@ -64,7 +41,7 @@ it('binds style settings color to the data stream color', () => {
viewport: { duration: '5m' },
annotations: {},
};
const queries = [queryCreator([TIME_SERIES_DATA])];
const queries = [mockTimeSeriesDataQuery([TIME_SERIES_DATA])];
const color = 'red';

const {
Expand All @@ -81,14 +58,16 @@ it('binds style settings color to the data stream color', () => {
});

it('combines multiple time series data results into a single time series data', () => {
const THRESHOLD_1 = { comparisonOperator: 'GT', value: 10, color: 'black' };
const THRESHOLD_2 = { comparisonOperator: 'GT', value: 100, color: 'black' };
const DATA_STREAM_1: DataStream = { refId: 'red', id: 'abc-1', data: [], resolution: 0, name: 'my-name' };
const DATA_STREAM_2: DataStream = { refId: 'red', id: 'abc-2', data: [], resolution: 0, name: 'my-name' };

const QUERY_RESPONSE: TimeSeriesData[] = [
{ dataStreams: [DATA_STREAM_1], viewport: { duration: '5m' }, annotations: { y: [] } },
{ dataStreams: [DATA_STREAM_2], viewport: { duration: '5m' }, annotations: { y: [] } },
{ dataStreams: [DATA_STREAM_1], viewport: { duration: '5m' }, annotations: { y: [THRESHOLD_1] } },
{ dataStreams: [DATA_STREAM_2], viewport: { duration: '5m' }, annotations: { y: [THRESHOLD_2] } },
];
const queries = [queryCreator(QUERY_RESPONSE)];
const queries = [mockTimeSeriesDataQuery(QUERY_RESPONSE)];
const viewport = { duration: '5m' };

const {
Expand All @@ -102,6 +81,7 @@ it('combines multiple time series data results into a single time series data',

expect(timeSeriesData).toEqual({
dataStreams: [DATA_STREAM_1, DATA_STREAM_2],
thresholds: [THRESHOLD_1, THRESHOLD_2],
});
});

Expand All @@ -116,7 +96,7 @@ it('returns data streams from multiple queries', () => {
{ dataStreams: [DATA_STREAM_2], viewport: { duration: '5m' }, annotations: { y: [] } },
];

const queries = [queryCreator(QUERY_RESPONSE_1), queryCreator(QUERY_RESPONSE_2)];
const queries = [mockTimeSeriesDataQuery(QUERY_RESPONSE_1), mockTimeSeriesDataQuery(QUERY_RESPONSE_2)];

const {
result: {
Expand All @@ -142,7 +122,7 @@ it('providers updated viewport to query', () => {
annotations: {},
};

const queries = [queryCreator([TIME_SERIES_DATA], { updateViewport })];
const queries = [mockTimeSeriesDataQuery([TIME_SERIES_DATA], { updateViewport })];
const color = 'red';

const { rerender } = renderHook(() =>
Expand All @@ -168,10 +148,30 @@ it('does not attempt to re-create the subscription when provided a new reference
result: { current: timeSeriesData },
} = renderHook(() =>
useTimeSeriesData({
queries: [queryCreator([])],
queries: [mockTimeSeriesDataQuery([])],
viewport: { duration: '5m' },
})
);

expect(timeSeriesData.dataStreams).toEqual([]);
});

it('returns thresholds from time series data provider', () => {
const THRESHOLD = { comparisonOperator: 'GT', value: 10, color: 'black' };
const TIME_SERIES_DATA: TimeSeriesData = {
dataStreams: [],
viewport: { duration: '5m' },
annotations: { y: [THRESHOLD, { value: 100, color: 'red' }] },
};
const queries = [mockTimeSeriesDataQuery([TIME_SERIES_DATA])];
const {
result: { current: timeSeriesData },
} = renderHook(() =>
useTimeSeriesData({
queries,
viewport: { duration: '5m' },
})
);

expect(timeSeriesData.thresholds).toEqual([THRESHOLD]);
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import { combineTimeSeriesData } from '../utils/combineTimeSeriesData';
import { useViewport } from '../useViewport';
import type {
Viewport,
DataStream,
Threshold,
TimeSeriesData,
TimeSeriesDataRequest,
TimeQuery,
TimeSeriesDataRequestSettings,
ProviderWithViewport,
StyleSettingsMap,
} from '@iot-app-kit/core';
import { getThresholds } from '../../utils/thresholdUtils';

const DEFAULT_SETTINGS: TimeSeriesDataRequestSettings = {
resolution: '0',
Expand All @@ -32,7 +35,7 @@ export const useTimeSeriesData = ({
viewport?: Viewport;
settings?: TimeSeriesDataRequestSettings;
styles?: StyleSettingsMap;
}) => {
}): { dataStreams: DataStream[]; thresholds: Threshold[] } => {
const [timeSeriesData, setTimeSeriesData] = useState<TimeSeriesData | undefined>(undefined);

const { viewport: injectedViewport } = useViewport();
Expand Down Expand Up @@ -91,5 +94,5 @@ export const useTimeSeriesData = ({
prevViewport.current = viewport;
}, [viewport]);

return { dataStreams: timeSeriesData?.dataStreams || [] };
return { dataStreams: timeSeriesData?.dataStreams || [], thresholds: getThresholds(timeSeriesData?.annotations) };
};

0 comments on commit 4df9ff0

Please sign in to comment.