Skip to content

Commit

Permalink
feat: add null/NaN support
Browse files Browse the repository at this point in the history
  • Loading branch information
jmbuss committed Jan 16, 2025
1 parent 4959c13 commit 450fc70
Show file tree
Hide file tree
Showing 26 changed files with 99 additions and 80 deletions.
1 change: 0 additions & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ jobs:
data-mocked,
dev-env,
doc-site,
helpers,
react-components,
scene-composer,
source-iotsitewise,
Expand Down
1 change: 0 additions & 1 deletion apps/dev-env/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"@iot-app-kit/core-util": "*",
"@iot-app-kit/dashboard": "*",
"@iot-app-kit/data-mocked": "*",
"@iot-app-kit/helpers": "*",
"@iot-app-kit/ts-config": "*",
"@playwright/test": "^1.48.2",
"@storybook/addon-essentials": "^8.4.5",
Expand Down
7 changes: 3 additions & 4 deletions apps/dev-env/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import {
MINUTE_IN_MS,
SECOND_IN_MS,
} from '@iot-app-kit/helpers/constants/time';
import { defineConfig, devices } from '@playwright/test';

const SECOND_IN_MS = 1000;
const MINUTE_IN_MS = 60_000;

/**
* See https://playwright.dev/docs/test-configuration.
*/
Expand Down
2 changes: 1 addition & 1 deletion configuration/eslint-config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = {
'plugin:import/recommended',
'plugin:import/typescript',
],
plugins: ['prettier', 'react', 'jest', 'import', 'unused-imports'],
plugins: ['prettier', 'react', "react-hooks", 'jest', 'import', 'unused-imports'],
globals: {
module: true,
process: true,
Expand Down
14 changes: 0 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/core/src/data-module/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface DataPoint<T extends Primitive = Primitive>

export type Resolution = number;

export type Primitive = string | number | boolean;
export type Primitive = string | number | boolean | null;

export type DataStreamId = string;

Expand Down
1 change: 0 additions & 1 deletion packages/helpers/.eslintignore

This file was deleted.

4 changes: 0 additions & 4 deletions packages/helpers/.eslintrc.cjs

This file was deleted.

20 changes: 0 additions & 20 deletions packages/helpers/package.json

This file was deleted.

2 changes: 0 additions & 2 deletions packages/helpers/src/constants/time.ts

This file was deleted.

5 changes: 0 additions & 5 deletions packages/helpers/tsconfig.json

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { isNumeric, round } from '@iot-app-kit/core-util';
export const formatValue =
(significantDigits = 4) =>
(value: Primitive) =>
isNumeric(value) ? `${round(value, significantDigits)}` : value.toString();
isNumeric(value) ? `${round(value, significantDigits)}` : value?.toString();

export type XYPlotTooltipValueOptions = {
value?: Primitive;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { useHighlightedDataStreams } from '../hooks/useHighlightedDataStreams';
import './yAxisMenu.css';

const getValue = (value: Primitive, significantDigits = 4) =>
isNumeric(value) ? `${round(value, significantDigits)}` : value.toString();
isNumeric(value) ? `${round(value, significantDigits)}` : value?.toString();

const MENU_OFFSET = 5;
const MENU_FONT_SIZE = 14;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { BatchGetAssetPropertyValueSuccessEntry } from '@aws-sdk/client-iotsitewise';
import type { DataStreamRequestEntry, DataStreamResource } from './types';
import type { DataStreamResourceWithLatestValue } from '../../types/resources';
import { toValue } from './toValue';

type SuccessEntry = BatchGetAssetPropertyValueSuccessEntry;

Expand Down Expand Up @@ -41,11 +42,10 @@ function createDataStreamWithLatestValue<DataStream extends DataStreamResource>(
requestEntry: DataStreamRequestEntry<DataStream>,
successEntry?: SuccessEntry
): DataStreamResourceWithLatestValue<DataStream> {
const variant = successEntry?.assetPropertyValue?.value;
const dataStreamWithLatestValue = {
...requestEntry.dataStream,
latestValue: Object.values(
successEntry?.assetPropertyValue?.value ?? {}
).at(0),
latestValue: variant && toValue(variant),
latestValueTimestamp:
successEntry?.assetPropertyValue?.timestamp?.timeInSeconds,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { Primitive } from '@iot-app-kit/core';
import type { Variant } from '@aws-sdk/client-iotsitewise';

export const toValue = (variant: Variant | undefined): Primitive => {
if (variant == null) {
throw new Error('variant is undefined');
}

const { doubleValue, integerValue, stringValue, booleanValue } = variant;

if (doubleValue != null) {
return doubleValue;
}

if (integerValue != null) {
return integerValue;
}

if (stringValue != null) {
return stringValue;
}

if (booleanValue != null) {
return booleanValue.toString();
}

if ('nullValue' in variant && variant.nullValue != null) {
/**
* nullValue is not a nullish value
* it's an object with the string type of
* what the value should be.
*/
return null;
}

/**
* A variant with no properties is treated
* as null data so that datastreams do not
* break when the sdk updates
*/
return null;
// throw new Error(
// 'Expected value to have at least one property value, but instead it has none!'
// );
};
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,6 @@ export type TimeSeriesResourceWithLatestValue =

export type DataStreamResourceWithLatestValue<DataStreamResource> =
DataStreamResource & {
latestValue?: number | string | boolean;
latestValue?: number | string | boolean | null;
latestValueTimestamp?: number;
};
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const createTableItems: (
const alarmItemsWithData = alarms.map((alarm) => {
const isLoading = alarm.isLoading;
return {
id: alarm.id as Primitive,
id: alarm.id as string,
assetId: createCellItem(
{
value: alarm.assetId,
Expand Down Expand Up @@ -189,7 +189,7 @@ export const createTableItems: (

const [first] = keyDataPairs;
return {
id: first.data.value as Primitive,
id: first.data.value as string | number | boolean,
...keyDataPairs.reduce(
(previous, { key, data }) => ({
...previous,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
type GetAssetPropertyValueHistoryRequest,
type GetAssetPropertyValueHistoryResponse,
} from '@aws-sdk/client-iotsitewise';
import { type Primitive } from '@iot-app-kit/charts-core';
import {
type RequestFunction,
type RequestParameters,
Expand Down Expand Up @@ -84,7 +85,7 @@ export type AssetPropertyValuesRequest =
| AssetPropertyValueHistoryRequest
| AssetPropertyAggregatesRequest;

export type AssetPropertyValuesData = DataPoint[];
export type AssetPropertyValuesData = DataPoint<Primitive>[];

export type AssetPropertyValuesRequestFunctions = {
getAssetPropertyValueHistory?: GetAssetPropertyValueHistoryRequestFunction;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ describe('toDataPoint', () => {
});
});

it('throws error when no property values passed in', () => {
it('does not throw error when no property values passed in', () => {
expect(() =>
toDataPoint({
timestamp: { timeInSeconds: SECONDS },
value: {},
})
).toThrowError();
).not.toThrowError();
});

it('converts correctly for a string value', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { NANO_SECOND_IN_MS, SECOND_IN_MS } from './timeConstants';
import type { DataPoint, Primitive } from '@iot-app-kit/core';
import type { DataPoint } from '@iot-app-kit/core';
import type {
AssetPropertyValue,
TimeInNanos,
Variant,
Aggregates,
AggregatedValue,
} from '@aws-sdk/client-iotsitewise';
import { type Primitive } from '@iot-app-kit/charts-core';

/** converts the TimeInNanos to milliseconds */
export const toTimestamp = (time: TimeInNanos | undefined): number =>
Expand All @@ -24,7 +25,7 @@ export const toTimestamp = (time: TimeInNanos | undefined): number =>
*
* NOTE: Currently we treat booleans as strings.
*/
export const toValue = (variant: Variant | undefined): Primitive => {
export const toValue = (variant: Variant | undefined): Primitive | null => {
if (variant == null) {
throw new Error('variant is undefined');
}
Expand All @@ -47,17 +48,15 @@ export const toValue = (variant: Variant | undefined): Primitive => {
return booleanValue.toString();
}

throw new Error(
'Expected value to have at least one property value, but instead it has none!'
);
return null;
};

/**
* Converts a SiteWise response for data into a data point understood by IoT App Kit.
*/
export const toDataPoint = (
assetPropertyValue: AssetPropertyValue | undefined
): DataPoint | undefined => {
): DataPoint<Primitive> | undefined => {
if (assetPropertyValue == null) {
return undefined;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/react-components/src/utils/getPreciseValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import { type Primitive } from '@iot-app-kit/core';
import { isNumeric, round } from '@iot-app-kit/core-util';

export const getPreciseValue = (value: Primitive, significantDigits = 4) =>
isNumeric(value) ? `${round(value, significantDigits)}` : value.toString();
isNumeric(value) ? `${round(value, significantDigits)}` : value?.toString();
6 changes: 5 additions & 1 deletion packages/react-components/src/utils/thresholdUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,11 @@ export const getBreachedThreshold = (
return undefined;
}

if (typeof value === 'string' || typeof value === 'boolean') {
if (
typeof value === 'string' ||
typeof value === 'boolean' ||
value === null
) {
return (
thresholds.find((threshold) => isThresholdBreached(value, threshold)) ||
undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ export class SiteWiseAlarmModule {
inputPropertyId,
thresholdPropertyId,
comparisonOperator,
threshold,
// old synchro charts type does not support null
threshold: threshold === null ? '' : threshold,
severity,
rule,
state,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const completeAlarmStream = ({
if (!assetModel) {
if (
isIoTEventsAlarmStateProperty(
dataStream.data[dataStream.data?.length - 1]?.y
dataStream.data[dataStream.data?.length - 1]?.y ?? undefined
)
) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,16 @@ describe('toDataPoint', () => {
});
});

it('throws error when no property values passed in', () => {
it('does not throw error when no property values passed in', () => {
/**
* changing this functionality to patch support null / nan data
*/
expect(() =>
toDataPoint({
timestamp: { timeInSeconds: SECONDS },
value: {},
})
).toThrowError();
).not.toThrowError();
});

it('converts correctly for a string value', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,24 @@ export const toValue = (variant: Variant | undefined): Primitive => {
return booleanValue.toString();
}

throw new Error(
'Expected value to have at least one property value, but instead it has none!'
);
if ('nullValue' in variant && variant.nullValue != null) {
/**
* nullValue is not a nullish value
* it's an object with the string type of
* what the value should be.
*/
return null;
}

/**
* A variant with no properties is treated
* as null data so that datastreams do not
* break when the sdk updates
*/
return null;
// throw new Error(
// 'Expected value to have at least one property value, but instead it has none!'
// );
};

/**
Expand Down

0 comments on commit 450fc70

Please sign in to comment.