@xube/kit-aws-data 0.0.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ /// <reference types="node" />
2
+ import { ReadingV1, AggregateMetadataTypes, AggregateMetadataV1 } from "@xube/kit-aws-data-schema";
3
+ export declare const decodeAggregateData: (aggregate: AggregateMetadataTypes, data: Buffer, device: string, component: string) => ReadingV1[];
4
+ export declare const decodeAggregateDataV1: (aggregate: AggregateMetadataV1, data: Buffer, device: string, component: string) => ReadingV1[];
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decodeAggregateDataV1 = exports.decodeAggregateData = void 0;
4
+ const kit_aws_data_schema_1 = require("@xube/kit-aws-data-schema");
5
+ const decodeAggregateData = (aggregate, data, device, component) => {
6
+ if (aggregate.v === kit_aws_data_schema_1.AggregateMetaDataV1Version) {
7
+ return (0, exports.decodeAggregateDataV1)(aggregate, data, device, component);
8
+ }
9
+ throw new Error(`Unknown aggregate metadata schema version: ${aggregate.v}`);
10
+ };
11
+ exports.decodeAggregateData = decodeAggregateData;
12
+ const decodeAggregateDataV1 = (aggregate, data, device, component) => {
13
+ let currentPosition = 0;
14
+ return aggregate.m.map((readingMetadata, idx) => {
15
+ let length = readingMetadata.s ? readingMetadata.s : aggregate.s;
16
+ const readingData = data.subarray(currentPosition, currentPosition + length);
17
+ currentPosition += length;
18
+ return {
19
+ ...readingMetadata.m,
20
+ data: readingData.toString("ascii"),
21
+ id: device,
22
+ type: kit_aws_data_schema_1.DATA_TYPE,
23
+ component,
24
+ };
25
+ });
26
+ };
27
+ exports.decodeAggregateDataV1 = decodeAggregateDataV1;
@@ -0,0 +1,4 @@
1
+ import { GetDataByDateRangeRequest, GetDataByDateRangeResponse } from "@xube/kit-aws-data-schema";
2
+ import { XubeLog } from "@xube/kit-log";
3
+ import { XubeResponse } from "@xube/kit-request";
4
+ export declare const getDataForDateRange: (request: GetDataByDateRangeRequest, tableName: string, log?: XubeLog) => Promise<XubeResponse<GetDataByDateRangeResponse>>;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDataForDateRange = void 0;
4
+ const kit_aws_1 = require("@xube/kit-aws");
5
+ const kit_constants_1 = require("@xube/kit-constants");
6
+ const kit_aws_data_schema_1 = require("@xube/kit-aws-data-schema");
7
+ const kit_log_1 = require("@xube/kit-log");
8
+ const kit_request_1 = require("@xube/kit-request");
9
+ const fetchItemsFromDB = async (request, tableName) => {
10
+ const queryItemsPromises = [];
11
+ for (const device of request.devices) {
12
+ for (const component of device.components) {
13
+ queryItemsPromises.push((0, kit_aws_1.queryItemsFromTableBetween)(tableName, {
14
+ PK: (0, kit_aws_data_schema_1.getDataPartitionKey)(device.id),
15
+ SKstart: (0, kit_aws_data_schema_1.getDataSortKey)(component, `${new Date(request.dateRange.start).getTime() / 1000}`),
16
+ SKend: (0, kit_aws_data_schema_1.getDataSortKey)(component, `${new Date(request.dateRange.end).getTime() / 1000}`),
17
+ }));
18
+ }
19
+ }
20
+ return Promise.all(queryItemsPromises);
21
+ };
22
+ const processResponseItems = (response, log) => {
23
+ if (response.hasFailed() || !response.data) {
24
+ log.warn(`Failed to get data for date range: ${JSON.stringify(response)}`);
25
+ return { errorCount: 1 };
26
+ }
27
+ const getDataResponse = { devices: [] };
28
+ for (const item of response.data) {
29
+ if (!(0, kit_aws_data_schema_1.isReadingV1)(item)) {
30
+ log.warn(`Found item that is not a reading v1: ${JSON.stringify(item)}`);
31
+ return { errorCount: 1 };
32
+ }
33
+ let device = getDataResponse.devices.find((device) => device.id === item.id);
34
+ if (!device) {
35
+ device = { id: item.id, components: [] };
36
+ getDataResponse.devices.push(device);
37
+ }
38
+ let component = device.components.find((component) => component.id === item.component);
39
+ if (!component) {
40
+ component = { id: item.component, data: [] };
41
+ device.components.push(component);
42
+ }
43
+ component.data.push({
44
+ seconds: item.s,
45
+ microseconds: item.us,
46
+ readings: item.data,
47
+ });
48
+ }
49
+ return { data: getDataResponse, errorCount: 0 };
50
+ };
51
+ const getDataForDateRange = async (request, tableName, log = kit_log_1.XubeLog.getInstance()) => {
52
+ const queryItemsResponses = await fetchItemsFromDB(request, tableName);
53
+ let totalErrorCount = 0;
54
+ const getDataResponse = { devices: [] };
55
+ for (const response of queryItemsResponses) {
56
+ const { data, errorCount } = processResponseItems(response, log);
57
+ totalErrorCount += errorCount;
58
+ if (data) {
59
+ getDataResponse.devices.push(...data.devices);
60
+ }
61
+ }
62
+ if (totalErrorCount === queryItemsResponses.length) {
63
+ return new kit_request_1.XubeResponse({
64
+ statusCode: kit_constants_1.StatusCode.NotFound,
65
+ error: `Failed to get data for date range. No data found.`,
66
+ });
67
+ }
68
+ else if (totalErrorCount > 0) {
69
+ return new kit_request_1.XubeResponse({
70
+ statusCode: kit_constants_1.StatusCode.PartialSuccess,
71
+ data: getDataResponse,
72
+ });
73
+ }
74
+ return new kit_request_1.XubeResponse({ data: getDataResponse, statusCode: kit_constants_1.StatusCode.OK });
75
+ };
76
+ exports.getDataForDateRange = getDataForDateRange;
@@ -0,0 +1,3 @@
1
+ export * from "./decode/aggregated";
2
+ export * from "./get/get-data";
3
+ export * from "./ingest/stage";
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./decode/aggregated"), exports);
18
+ __exportStar(require("./get/get-data"), exports);
19
+ __exportStar(require("./ingest/stage"), exports);
@@ -0,0 +1,3 @@
1
+ /// <reference types="node" />
2
+ import { ReadingV1 } from "@xube/kit-aws-data-schema";
3
+ export declare const parseDeviceData: (payload: Buffer, device: string, component: string) => ReadingV1[];
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseDeviceData = void 0;
4
+ const kit_aws_data_schema_1 = require("@xube/kit-aws-data-schema");
5
+ const aggregated_1 = require("../decode/aggregated");
6
+ const parseDeviceData = (payload, device, component) => {
7
+ const splitIdx = payload.findIndex((value, idx, obj) => {
8
+ return value === 0x00;
9
+ });
10
+ if (splitIdx === -1) {
11
+ throw new Error("Invalid data format");
12
+ }
13
+ const metadataString = payload.subarray(0, splitIdx).toString();
14
+ const metadata = JSON.parse(metadataString);
15
+ if (!(0, kit_aws_data_schema_1.isAggregateMetadataTypes)(metadata)) {
16
+ throw new Error("Invalid metadata format");
17
+ }
18
+ const data = payload.subarray(splitIdx + 1);
19
+ return (0, aggregated_1.decodeAggregateData)(metadata, data, device, component);
20
+ };
21
+ exports.parseDeviceData = parseDeviceData;
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@xube/kit-aws-data",
3
+ "version": "0.0.73",
4
+ "description": "",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+ssh://git@github.com/XubeLtd/dev-kit.git"
12
+ },
13
+ "author": "Xube Pty Ltd",
14
+ "license": "MIT",
15
+ "bugs": {
16
+ "url": "https://github.com/XubeLtd/dev-kit/issues"
17
+ },
18
+ "homepage": "https://github.com/XubeLtd/dev-kit#readme",
19
+ "dependencies": {
20
+ "@types/uuid": "^9.0.5",
21
+ "@xube/kit-aws": "^0.0.73",
22
+ "@xube/kit-aws-data-schema": "^0.0.73",
23
+ "@xube/kit-aws-schema": "^0.0.73",
24
+ "@xube/kit-generator": "^0.0.73",
25
+ "@xube/kit-log": "^0.0.73",
26
+ "@xube/kit-schema": "^0.0.73",
27
+ "uuid": "^9.0.1"
28
+ },
29
+ "devDependencies": {
30
+ "@xube/kit-build": "^0.0.73"
31
+ }
32
+ }
@@ -0,0 +1,81 @@
1
+ import { decodeAggregateData } from "./aggregated";
2
+ import {
3
+ AggregateMetadataV1,
4
+ DATA_TYPE,
5
+ ReadingV1,
6
+ } from "@xube/kit-aws-data-schema";
7
+
8
+ describe("decodeAggregateData", () => {
9
+ const aggregate: AggregateMetadataV1 = {
10
+ s: 25,
11
+ m: [
12
+ { m: { s: 1697578841, us: 214981 } },
13
+ { m: { s: 1697578841, us: 413572 } },
14
+ { m: { s: 1697578841, us: 612976 } },
15
+ { m: { s: 1697578841, us: 813754 } },
16
+ { m: { s: 1697578842, us: 13019 } },
17
+ ],
18
+ v: 1,
19
+ };
20
+
21
+ const data: Buffer = Buffer.from(
22
+ `S,+000.30,+001.74,M,00,33S,+000.35,+001.84,M,00,39S,+000.26,+002.00,M,00,34S,+000.34,+001.88,M,00,34S,+000.35,+001.82,M,00,3F`
23
+ );
24
+
25
+ it("should decode the aggregate data correctly", () => {
26
+ const result = decodeAggregateData(
27
+ aggregate,
28
+ data,
29
+ "12345678",
30
+ "component"
31
+ );
32
+
33
+ const expectedReading: ReadingV1[] = [
34
+ {
35
+ data: "S,+000.30,+001.74,M,00,33",
36
+ s: 1697578841,
37
+ us: 214981,
38
+ type: DATA_TYPE,
39
+ id: "12345678",
40
+ component: "component",
41
+ },
42
+ {
43
+ data: "S,+000.35,+001.84,M,00,39",
44
+ s: 1697578841,
45
+ us: 413572,
46
+ type: DATA_TYPE,
47
+ id: "12345678",
48
+ component: "component",
49
+ },
50
+ {
51
+ data: "S,+000.26,+002.00,M,00,34",
52
+ s: 1697578841,
53
+ us: 612976,
54
+ type: DATA_TYPE,
55
+ id: "12345678",
56
+ component: "component",
57
+ },
58
+ {
59
+ data: "S,+000.34,+001.88,M,00,34",
60
+ s: 1697578841,
61
+ us: 813754,
62
+ type: DATA_TYPE,
63
+ id: "12345678",
64
+ component: "component",
65
+ },
66
+ {
67
+ data: "S,+000.35,+001.82,M,00,3F",
68
+ s: 1697578842,
69
+ us: 13019,
70
+ type: DATA_TYPE,
71
+ id: "12345678",
72
+ component: "component",
73
+ },
74
+ ];
75
+
76
+ expect(result.length).toEqual(5);
77
+ result.forEach((reading: ReadingV1, idx: number) => {
78
+ expect(reading).toEqual(expectedReading[idx]);
79
+ });
80
+ });
81
+ });
@@ -0,0 +1,48 @@
1
+ import {
2
+ AggregateMetaDataV1Version,
3
+ DATA_TYPE,
4
+ ReadingV1,
5
+ DeviceReadingV1,
6
+ AggregateMetadataTypes,
7
+ AggregateMetadataV1,
8
+ } from "@xube/kit-aws-data-schema";
9
+
10
+ export const decodeAggregateData = (
11
+ aggregate: AggregateMetadataTypes,
12
+ data: Buffer,
13
+ device: string,
14
+ component: string
15
+ ): ReadingV1[] => {
16
+ if (aggregate.v === AggregateMetaDataV1Version) {
17
+ return decodeAggregateDataV1(aggregate, data, device, component);
18
+ }
19
+
20
+ throw new Error(`Unknown aggregate metadata schema version: ${aggregate.v}`);
21
+ };
22
+
23
+ export const decodeAggregateDataV1 = (
24
+ aggregate: AggregateMetadataV1,
25
+ data: Buffer,
26
+ device: string,
27
+ component: string
28
+ ): ReadingV1[] => {
29
+ let currentPosition: number = 0;
30
+ return aggregate.m.map<ReadingV1>(
31
+ (readingMetadata: DeviceReadingV1, idx): ReadingV1 => {
32
+ let length: number = readingMetadata.s ? readingMetadata.s : aggregate.s;
33
+
34
+ const readingData: Buffer = data.subarray(
35
+ currentPosition,
36
+ currentPosition + length
37
+ );
38
+ currentPosition += length;
39
+ return {
40
+ ...readingMetadata.m,
41
+ data: readingData.toString("ascii"),
42
+ id: device,
43
+ type: DATA_TYPE,
44
+ component,
45
+ };
46
+ }
47
+ );
48
+ };
@@ -0,0 +1,116 @@
1
+ import { queryItemsFromTableBetween } from "@xube/kit-aws";
2
+ import { TableItem } from "@xube/kit-aws-schema";
3
+ import { StatusCode } from "@xube/kit-constants";
4
+ import {
5
+ GetDataByDateRangeRequest,
6
+ GetDataByDateRangeResponse,
7
+ getDataPartitionKey,
8
+ getDataSortKey,
9
+ isReadingV1,
10
+ } from "@xube/kit-aws-data-schema";
11
+ import { XubeLog } from "@xube/kit-log";
12
+ import { XubeResponse } from "@xube/kit-request";
13
+
14
+ const fetchItemsFromDB = async (
15
+ request: GetDataByDateRangeRequest,
16
+ tableName: string
17
+ ): Promise<XubeResponse<TableItem[]>[]> => {
18
+ const queryItemsPromises: Promise<XubeResponse<TableItem[]>>[] = [];
19
+
20
+ for (const device of request.devices) {
21
+ for (const component of device.components) {
22
+ queryItemsPromises.push(
23
+ queryItemsFromTableBetween(tableName, {
24
+ PK: getDataPartitionKey(device.id),
25
+ SKstart: getDataSortKey(
26
+ component,
27
+ `${new Date(request.dateRange.start).getTime() / 1000}`
28
+ ),
29
+ SKend: getDataSortKey(
30
+ component,
31
+ `${new Date(request.dateRange.end).getTime() / 1000}`
32
+ ),
33
+ })
34
+ );
35
+ }
36
+ }
37
+
38
+ return Promise.all(queryItemsPromises);
39
+ };
40
+
41
+ const processResponseItems = (
42
+ response: XubeResponse<TableItem[]>,
43
+ log: XubeLog
44
+ ): {
45
+ data?: GetDataByDateRangeResponse;
46
+ errorCount: number;
47
+ } => {
48
+ if (response.hasFailed() || !response.data) {
49
+ log.warn(`Failed to get data for date range: ${JSON.stringify(response)}`);
50
+ return { errorCount: 1 };
51
+ }
52
+
53
+ const getDataResponse: GetDataByDateRangeResponse = { devices: [] };
54
+
55
+ for (const item of response.data) {
56
+ if (!isReadingV1(item)) {
57
+ log.warn(`Found item that is not a reading v1: ${JSON.stringify(item)}`);
58
+ return { errorCount: 1 };
59
+ }
60
+
61
+ let device = getDataResponse.devices.find((device) => device.id === item.id);
62
+ if (!device) {
63
+ device = { id: item.id, components: [] };
64
+ getDataResponse.devices.push(device);
65
+ }
66
+
67
+ let component = device.components.find(
68
+ (component) => component.id === item.component
69
+ );
70
+ if (!component) {
71
+ component = { id: item.component, data: [] };
72
+ device.components.push(component);
73
+ }
74
+
75
+ component.data.push({
76
+ seconds: item.s,
77
+ microseconds: item.us,
78
+ readings: item.data,
79
+ });
80
+ }
81
+
82
+ return { data: getDataResponse, errorCount: 0 };
83
+ };
84
+
85
+ export const getDataForDateRange = async (
86
+ request: GetDataByDateRangeRequest,
87
+ tableName: string,
88
+ log: XubeLog = XubeLog.getInstance()
89
+ ): Promise<XubeResponse<GetDataByDateRangeResponse>> => {
90
+ const queryItemsResponses = await fetchItemsFromDB(request, tableName);
91
+
92
+ let totalErrorCount = 0;
93
+ const getDataResponse: GetDataByDateRangeResponse = { devices: [] };
94
+
95
+ for (const response of queryItemsResponses) {
96
+ const { data, errorCount } = processResponseItems(response, log);
97
+ totalErrorCount += errorCount;
98
+ if (data) {
99
+ getDataResponse.devices.push(...data.devices);
100
+ }
101
+ }
102
+
103
+ if (totalErrorCount === queryItemsResponses.length) {
104
+ return new XubeResponse({
105
+ statusCode: StatusCode.NotFound,
106
+ error: `Failed to get data for date range. No data found.`,
107
+ });
108
+ } else if (totalErrorCount > 0) {
109
+ return new XubeResponse({
110
+ statusCode: StatusCode.PartialSuccess,
111
+ data: getDataResponse,
112
+ });
113
+ }
114
+
115
+ return new XubeResponse({ data: getDataResponse, statusCode: StatusCode.OK });
116
+ };
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./decode/aggregated";
2
+ export * from "./get/get-data";
3
+ export * from "./ingest/stage";
@@ -0,0 +1,27 @@
1
+ import { ReadingV1, isAggregateMetadataTypes } from "@xube/kit-aws-data-schema";
2
+ import { decodeAggregateData } from "../decode/aggregated";
3
+
4
+ export const parseDeviceData = (
5
+ payload: Buffer,
6
+ device: string,
7
+ component: string
8
+ ): ReadingV1[] => {
9
+ const splitIdx: number = payload.findIndex((value, idx, obj) => {
10
+ return value === 0x00;
11
+ });
12
+
13
+ if (splitIdx === -1) {
14
+ throw new Error("Invalid data format");
15
+ }
16
+
17
+ const metadataString: string = payload.subarray(0, splitIdx).toString();
18
+ const metadata = JSON.parse(metadataString);
19
+
20
+ if (!isAggregateMetadataTypes(metadata)) {
21
+ throw new Error("Invalid metadata format");
22
+ }
23
+
24
+ const data: Buffer = payload.subarray(splitIdx + 1);
25
+
26
+ return decodeAggregateData(metadata, data, device, component);
27
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "extends": "../kit-build/tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "outDir": "./dist"
6
+ },
7
+ "exclude": ["**/*.test.ts", "**/*.mock.ts", "dist"],
8
+ "references": [
9
+ {
10
+ "path": "../kit-constants"
11
+ },
12
+ {
13
+ "path": "../kit-generator"
14
+ },
15
+ {
16
+ "path": "../kit-log"
17
+ },
18
+ {
19
+ "path": "../kit-schema"
20
+ },
21
+ {
22
+ "path": "../kit-aws"
23
+ },
24
+ {
25
+ "path": "../kit-aws-schema"
26
+ },
27
+ {
28
+ "path": "../kit-aws-data-schema"
29
+ }
30
+ ]
31
+ }