@xube/kit-aws-hooks 0.0.45 → 0.0.47
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.
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -1
- package/dist/generate.js +7 -2
- package/dist/schemas/webhook.d.ts +12 -0
- package/dist/schemas/webhook.js +2 -2
- package/dist/send.d.ts +1 -1
- package/dist/send.js +10 -6
- package/dist/transform.d.ts +18 -0
- package/dist/transform.js +93 -0
- package/package.json +8 -5
- package/src/constants.ts +2 -0
- package/src/generate.ts +9 -2
- package/src/schemas/webhook.ts +1 -1
- package/src/send.ts +13 -5
- package/src/transform.ts +107 -0
- package/tsconfig.json +10 -1
package/dist/constants.d.ts
CHANGED
package/dist/constants.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WEBHOOK_TABLE_INVERTED_INDEX_NAME_ENV_VAR = exports.WEBHOOK_TABLE_NAME_ENV_VAR = void 0;
|
|
3
|
+
exports.WEBHOOK_TYPE = exports.WEBHOOK_TABLE_INVERTED_INDEX_NAME_ENV_VAR = exports.WEBHOOK_TABLE_NAME_ENV_VAR = void 0;
|
|
4
4
|
exports.WEBHOOK_TABLE_NAME_ENV_VAR = "WEBHOOK_TABLE_NAME";
|
|
5
5
|
exports.WEBHOOK_TABLE_INVERTED_INDEX_NAME_ENV_VAR = "WEBHOOK_TABLE_INVERTED_INDEX_NAME";
|
|
6
|
+
exports.WEBHOOK_TYPE = "webhook";
|
package/dist/generate.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateWebhookItems = void 0;
|
|
4
|
+
const kit_aws_schema_1 = require("@xube/kit-aws-schema");
|
|
4
5
|
const keys_1 = require("./keys");
|
|
5
6
|
const types_1 = require("./types");
|
|
6
|
-
const
|
|
7
|
+
const constants_1 = require("./constants");
|
|
7
8
|
const generateWebhookItems = (request) => {
|
|
8
9
|
const webhookItems = {};
|
|
9
10
|
for (const endpoint of request.endpoints) {
|
|
@@ -12,9 +13,13 @@ const generateWebhookItems = (request) => {
|
|
|
12
13
|
PK: (0, keys_1.getWebhookPartitionKey)(request.account),
|
|
13
14
|
SK: (0, keys_1.getWebhookSortKey)(id, request.type ?? types_1.WebhookTypes.data),
|
|
14
15
|
};
|
|
15
|
-
const aggregateKey = (0,
|
|
16
|
+
const aggregateKey = (0, kit_aws_schema_1.getAggregateKeyFromTableKey)(key);
|
|
17
|
+
const time = new Date().getTime();
|
|
16
18
|
if (!webhookItems[aggregateKey]) {
|
|
17
19
|
webhookItems[aggregateKey] = {
|
|
20
|
+
s: time / 1000,
|
|
21
|
+
us: time % 1000,
|
|
22
|
+
type: constants_1.WEBHOOK_TYPE,
|
|
18
23
|
account: request.account,
|
|
19
24
|
endpoints: [endpoint],
|
|
20
25
|
id,
|
|
@@ -49,35 +49,47 @@ export declare const WebhookEndpointItemSchema: z.ZodObject<{
|
|
|
49
49
|
headers?: Record<string, string> | undefined;
|
|
50
50
|
}>, "many">;
|
|
51
51
|
id: z.ZodString;
|
|
52
|
+
type: z.ZodString;
|
|
53
|
+
s: z.ZodNumber;
|
|
54
|
+
us: z.ZodOptional<z.ZodNumber>;
|
|
52
55
|
PK: z.ZodString;
|
|
53
56
|
SK: z.ZodString;
|
|
54
57
|
}, "strip", z.ZodTypeAny, {
|
|
58
|
+
type: string;
|
|
55
59
|
account: string;
|
|
56
60
|
endpoints: {
|
|
57
61
|
url: string;
|
|
58
62
|
headers?: Record<string, string> | undefined;
|
|
59
63
|
}[];
|
|
60
64
|
id: string;
|
|
65
|
+
s: number;
|
|
61
66
|
PK: string;
|
|
62
67
|
SK: string;
|
|
68
|
+
us?: number | undefined;
|
|
63
69
|
}, {
|
|
70
|
+
type: string;
|
|
64
71
|
account: string;
|
|
65
72
|
endpoints: {
|
|
66
73
|
url: string;
|
|
67
74
|
headers?: Record<string, string> | undefined;
|
|
68
75
|
}[];
|
|
69
76
|
id: string;
|
|
77
|
+
s: number;
|
|
70
78
|
PK: string;
|
|
71
79
|
SK: string;
|
|
80
|
+
us?: number | undefined;
|
|
72
81
|
}>;
|
|
73
82
|
export type WebhookEndpointItem = z.infer<typeof WebhookEndpointItemSchema>;
|
|
74
83
|
export declare const isWebhookEndpointItem: (obj: unknown) => obj is {
|
|
84
|
+
type: string;
|
|
75
85
|
account: string;
|
|
76
86
|
endpoints: {
|
|
77
87
|
url: string;
|
|
78
88
|
headers?: Record<string, string> | undefined;
|
|
79
89
|
}[];
|
|
80
90
|
id: string;
|
|
91
|
+
s: number;
|
|
81
92
|
PK: string;
|
|
82
93
|
SK: string;
|
|
94
|
+
us?: number | undefined;
|
|
83
95
|
};
|
package/dist/schemas/webhook.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isWebhookEndpointItem = exports.WebhookEndpointItemSchema = exports.isWebhookEndpoint = exports.WebhookEndpointSchema = void 0;
|
|
4
|
-
const kit_aws_1 = require("@xube/kit-aws");
|
|
5
4
|
const zod_1 = require("zod");
|
|
6
5
|
const endpoint_1 = require("./endpoint");
|
|
6
|
+
const kit_aws_schema_1 = require("@xube/kit-aws-schema");
|
|
7
7
|
exports.WebhookEndpointSchema = zod_1.z.object({
|
|
8
8
|
endpoints: zod_1.z.array(endpoint_1.HookEndpointSchema),
|
|
9
9
|
id: zod_1.z.string(),
|
|
@@ -11,6 +11,6 @@ exports.WebhookEndpointSchema = zod_1.z.object({
|
|
|
11
11
|
});
|
|
12
12
|
const isWebhookEndpoint = (obj) => exports.WebhookEndpointSchema.safeParse(obj).success;
|
|
13
13
|
exports.isWebhookEndpoint = isWebhookEndpoint;
|
|
14
|
-
exports.WebhookEndpointItemSchema = exports.WebhookEndpointSchema.merge(
|
|
14
|
+
exports.WebhookEndpointItemSchema = exports.WebhookEndpointSchema.merge(kit_aws_schema_1.TableItemSchema);
|
|
15
15
|
const isWebhookEndpointItem = (obj) => exports.WebhookEndpointItemSchema.safeParse(obj).success;
|
|
16
16
|
exports.isWebhookEndpointItem = isWebhookEndpointItem;
|
package/dist/send.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TableItem } from "@xube/kit-aws";
|
|
2
1
|
import { XubeResponse } from "@xube/kit-request";
|
|
3
2
|
import { XubeLog } from "@xube/kit-log";
|
|
3
|
+
import { TableItem } from "@xube/kit-aws-schema";
|
|
4
4
|
export declare const sendDataToEndpoints: (webhookTableName: string, indexName: string, items: TableItem[], log?: XubeLog) => Promise<XubeResponse<boolean>>;
|
package/dist/send.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.sendDataToEndpoints = void 0;
|
|
4
|
-
const kit_aws_1 = require("@xube/kit-aws");
|
|
5
4
|
const keys_1 = require("./keys");
|
|
6
5
|
const types_1 = require("./types");
|
|
7
6
|
const kit_request_1 = require("@xube/kit-request");
|
|
8
7
|
const webhook_1 = require("./schemas/webhook");
|
|
9
8
|
const kit_constants_1 = require("@xube/kit-constants");
|
|
10
9
|
const kit_log_1 = require("@xube/kit-log");
|
|
10
|
+
const kit_aws_1 = require("@xube/kit-aws");
|
|
11
|
+
const kit_aws_schema_1 = require("@xube/kit-aws-schema");
|
|
12
|
+
const transform_1 = require("./transform");
|
|
11
13
|
const sendDataToEndpoints = async (webhookTableName, indexName, items, log = kit_log_1.XubeLog.getInstance()) => {
|
|
12
14
|
const endpointSends = [];
|
|
13
15
|
log.info(`Sending data to endpoints based on ${items.length} items.}`);
|
|
@@ -17,7 +19,7 @@ const sendDataToEndpoints = async (webhookTableName, indexName, items, log = kit
|
|
|
17
19
|
};
|
|
18
20
|
log.info(`Searching for webhooks with key: ${JSON.stringify(invertedWebhookKey)}`);
|
|
19
21
|
const getWebhookEndpointsResponse = await (0, kit_aws_1.queryItemsFromTable)(webhookTableName, invertedWebhookKey, indexName, {
|
|
20
|
-
partitionKey:
|
|
22
|
+
partitionKey: kit_aws_schema_1.SORT_KEY,
|
|
21
23
|
});
|
|
22
24
|
if (getWebhookEndpointsResponse.hasFailed()) {
|
|
23
25
|
log.error(`Failed to get webhook endpoints for webhook endpoint ${item.id} and type ${types_1.WebhookTypes.data}`);
|
|
@@ -41,9 +43,10 @@ const sendDataToEndpoints = async (webhookTableName, indexName, items, log = kit
|
|
|
41
43
|
continue;
|
|
42
44
|
}
|
|
43
45
|
log.info(`Preparing to send data to endpoints: ${JSON.stringify(webhookEndpoint.endpoints, null, 2)}`);
|
|
46
|
+
const timeSeries = (0, transform_1.getTimeSeriesDataFromData)(items);
|
|
44
47
|
for (const endpoint of webhookEndpoint.endpoints) {
|
|
45
48
|
endpointSends.push(fetch(endpoint.url, {
|
|
46
|
-
body: JSON.stringify(
|
|
49
|
+
body: JSON.stringify(timeSeries),
|
|
47
50
|
headers: endpoint.headers,
|
|
48
51
|
method: "POST",
|
|
49
52
|
}));
|
|
@@ -62,18 +65,19 @@ const sendDataToEndpoints = async (webhookTableName, indexName, items, log = kit
|
|
|
62
65
|
}
|
|
63
66
|
if (failedResponseCount == endpointSends.length) {
|
|
64
67
|
return new kit_request_1.XubeResponse({
|
|
65
|
-
statusCode:
|
|
68
|
+
statusCode: kit_constants_1.StatusCode.InternalError,
|
|
66
69
|
error: `Failed to send data to all endpoints: ${JSON.stringify(failedEndpointSendUrls, null, 2)}`,
|
|
67
70
|
});
|
|
68
71
|
}
|
|
69
72
|
if (failedResponseCount) {
|
|
70
73
|
return new kit_request_1.XubeResponse({
|
|
71
|
-
statusCode:
|
|
74
|
+
statusCode: kit_constants_1.StatusCode.PartialSuccess,
|
|
72
75
|
error: `Failed to send data to ${failedResponseCount} of ${endpointSends.length} endpoints: ${JSON.stringify(failedEndpointSendUrls, null, 2)}`,
|
|
73
76
|
});
|
|
74
77
|
}
|
|
78
|
+
log.info(`Successfully sent data to all endpoints.`);
|
|
75
79
|
return new kit_request_1.XubeResponse({
|
|
76
|
-
statusCode:
|
|
80
|
+
statusCode: kit_constants_1.StatusCode.OK,
|
|
77
81
|
data: true,
|
|
78
82
|
});
|
|
79
83
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { TableItem } from "@xube/kit-aws-schema";
|
|
2
|
+
export declare const stageDataForSend: (items: TableItem[]) => void;
|
|
3
|
+
export declare const getTimeSeriesDataFromData: (items: TableItem[]) => {
|
|
4
|
+
data: (string | number | boolean | import("zod").objectOutputType<{}, import("zod").ZodTypeAny, "passthrough">)[];
|
|
5
|
+
m: {
|
|
6
|
+
id: string;
|
|
7
|
+
trigger: "data" | "event";
|
|
8
|
+
source: "device" | "component" | "platform";
|
|
9
|
+
};
|
|
10
|
+
v: string;
|
|
11
|
+
t0: string;
|
|
12
|
+
dtt: "s" | "us" | "ms" | "m" | "h" | "d";
|
|
13
|
+
dt: number;
|
|
14
|
+
dataT?: import("zod").objectOutputType<{
|
|
15
|
+
i: import("zod").ZodNumber;
|
|
16
|
+
dt: import("zod").ZodNumber;
|
|
17
|
+
}, import("zod").ZodTypeAny, "passthrough">[] | undefined;
|
|
18
|
+
}[];
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTimeSeriesDataFromData = exports.stageDataForSend = void 0;
|
|
4
|
+
const kit_data_schema_1 = require("@xube/kit-data-schema");
|
|
5
|
+
const kit_data_schema_2 = require("@xube/kit-data-schema");
|
|
6
|
+
const stageDataForSend = (items) => {
|
|
7
|
+
const timeSeries = (0, exports.getTimeSeriesDataFromData)(items);
|
|
8
|
+
};
|
|
9
|
+
exports.stageDataForSend = stageDataForSend;
|
|
10
|
+
const getTimeSeriesDataFromData = (items) => {
|
|
11
|
+
const timeSeriesEntriesById = {};
|
|
12
|
+
const previousMicroSecondsSinceEpochById = {};
|
|
13
|
+
for (const item of items) {
|
|
14
|
+
if (!(0, kit_data_schema_1.isReadingV1)(item)) {
|
|
15
|
+
console.log(`${item.type} is not a currently supported type.`);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const microsSinceEpoch = microsecondsSinceEpoch(item.s, item?.us ?? 0);
|
|
19
|
+
if (!timeSeriesEntriesById[item.id]) {
|
|
20
|
+
timeSeriesEntriesById[item.id] = {
|
|
21
|
+
v: "1",
|
|
22
|
+
m: {
|
|
23
|
+
id: item.id,
|
|
24
|
+
trigger: kit_data_schema_2.DATA_TYPE,
|
|
25
|
+
source: "device",
|
|
26
|
+
},
|
|
27
|
+
t0: isoDateTimeWithMicroseconds(microsSinceEpoch),
|
|
28
|
+
dtt: "ms",
|
|
29
|
+
dt: 0,
|
|
30
|
+
dataT: [],
|
|
31
|
+
data: [item.data],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const timeSeriesEntry = timeSeriesEntriesById[item.id];
|
|
36
|
+
timeSeriesEntry.data.push(item.data);
|
|
37
|
+
const timeDifference = Math.floor((microsSinceEpoch - previousMicroSecondsSinceEpochById?.[item.id]) /
|
|
38
|
+
getDivisorByTimeUnit(timeSeriesEntry.dtt));
|
|
39
|
+
if (timeDifference !== timeSeriesEntry.dt) {
|
|
40
|
+
if (!timeSeriesEntry.dataT) {
|
|
41
|
+
timeSeriesEntry.dataT = [];
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
timeSeriesEntry.dataT.push({
|
|
45
|
+
i: timeSeriesEntry.data.length - 1,
|
|
46
|
+
dt: microsSinceEpoch - previousMicroSecondsSinceEpochById?.[item.id],
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
previousMicroSecondsSinceEpochById[item.id] = microsSinceEpoch;
|
|
52
|
+
}
|
|
53
|
+
return Object.values(timeSeriesEntriesById);
|
|
54
|
+
};
|
|
55
|
+
exports.getTimeSeriesDataFromData = getTimeSeriesDataFromData;
|
|
56
|
+
function getDivisorByTimeUnit(timeUnit) {
|
|
57
|
+
switch (timeUnit) {
|
|
58
|
+
case "us":
|
|
59
|
+
return 1;
|
|
60
|
+
case "ms":
|
|
61
|
+
return 1000;
|
|
62
|
+
case "s":
|
|
63
|
+
return 1000000;
|
|
64
|
+
case "m":
|
|
65
|
+
return 60000000;
|
|
66
|
+
case "h":
|
|
67
|
+
return 3600000000;
|
|
68
|
+
case "d":
|
|
69
|
+
return 86400000000;
|
|
70
|
+
default:
|
|
71
|
+
throw new Error("Invalid time unit");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function microsecondsSinceEpoch(s, us) {
|
|
75
|
+
return s * 1000000 + us;
|
|
76
|
+
}
|
|
77
|
+
function isoDateTimeWithMicroseconds(microsecondsSinceEpoch) {
|
|
78
|
+
const milliseconds = Math.floor(microsecondsSinceEpoch / 1000);
|
|
79
|
+
const microseconds = microsecondsSinceEpoch % 1000;
|
|
80
|
+
const date = new Date(milliseconds);
|
|
81
|
+
const year = date.getUTCFullYear();
|
|
82
|
+
const month = (date.getUTCMonth() + 1).toString().padStart(2, "0");
|
|
83
|
+
const day = date.getUTCDate().toString().padStart(2, "0");
|
|
84
|
+
const hours = date.getUTCHours().toString().padStart(2, "0");
|
|
85
|
+
const minutes = date.getUTCMinutes().toString().padStart(2, "0");
|
|
86
|
+
const seconds = date.getUTCSeconds().toString().padStart(2, "0");
|
|
87
|
+
const millisecondsStr = date
|
|
88
|
+
.getUTCMilliseconds()
|
|
89
|
+
.toString()
|
|
90
|
+
.padStart(3, "0");
|
|
91
|
+
const microsecondsStr = microseconds.toString().padStart(3, "0");
|
|
92
|
+
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${millisecondsStr}${microsecondsStr}Z`;
|
|
93
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xube/kit-aws-hooks",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.47",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -17,12 +17,15 @@
|
|
|
17
17
|
},
|
|
18
18
|
"homepage": "https://github.com/XubeLtd/dev-kit#readme",
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@xube/kit-build": "^0.0.
|
|
20
|
+
"@xube/kit-build": "^0.0.47"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@xube/kit-aws": "^0.0.
|
|
24
|
-
"@xube/kit-
|
|
25
|
-
"@xube/kit-
|
|
23
|
+
"@xube/kit-aws": "^0.0.47",
|
|
24
|
+
"@xube/kit-aws-schema": "^0.0.47",
|
|
25
|
+
"@xube/kit-data-schema": "^0.0.47",
|
|
26
|
+
"@xube/kit-log": "^0.0.47",
|
|
27
|
+
"@xube/kit-request": "^0.0.47",
|
|
28
|
+
"@xube/kit-schema": "^0.0.47",
|
|
26
29
|
"zod": "^3.22.4"
|
|
27
30
|
}
|
|
28
31
|
}
|
package/src/constants.ts
CHANGED
package/src/generate.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { TableKey, getAggregateKeyFromTableKey } from "@xube/kit-aws-schema";
|
|
1
2
|
import { getWebhookPartitionKey, getWebhookSortKey } from "./keys";
|
|
2
3
|
import { CreateWebhookForAccountRequest } from "./schemas/create";
|
|
3
|
-
import {
|
|
4
|
+
import { WebhookEndpointItem } from "./schemas/webhook";
|
|
4
5
|
import { WebhookTypes } from "./types";
|
|
5
|
-
import {
|
|
6
|
+
import { WEBHOOK_TYPE } from "./constants";
|
|
6
7
|
|
|
7
8
|
export const generateWebhookItems = (
|
|
8
9
|
request: CreateWebhookForAccountRequest
|
|
@@ -18,11 +19,17 @@ export const generateWebhookItems = (
|
|
|
18
19
|
|
|
19
20
|
const aggregateKey = getAggregateKeyFromTableKey(key);
|
|
20
21
|
|
|
22
|
+
const time = new Date().getTime();
|
|
23
|
+
|
|
21
24
|
if (!webhookItems[aggregateKey]) {
|
|
22
25
|
webhookItems[aggregateKey] = {
|
|
26
|
+
s: time / 1000,
|
|
27
|
+
us: time % 1000,
|
|
28
|
+
type: WEBHOOK_TYPE,
|
|
23
29
|
account: request.account,
|
|
24
30
|
endpoints: [endpoint],
|
|
25
31
|
id,
|
|
32
|
+
|
|
26
33
|
...key,
|
|
27
34
|
};
|
|
28
35
|
} else {
|
package/src/schemas/webhook.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { TableItemSchema } from "@xube/kit-aws";
|
|
2
1
|
import { z } from "zod";
|
|
3
2
|
import { HookEndpointSchema } from "./endpoint";
|
|
3
|
+
import { TableItemSchema } from "@xube/kit-aws-schema";
|
|
4
4
|
|
|
5
5
|
export const WebhookEndpointSchema = z.object({
|
|
6
6
|
endpoints: z.array(HookEndpointSchema),
|
package/src/send.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { PARTITION_KEY, PartialTableKey, SORT_KEY, TableItem, queryItemsFromTable } from "@xube/kit-aws";
|
|
2
1
|
import { getWebhookSortKey } from "./keys";
|
|
3
2
|
import { WebhookTypes } from "./types";
|
|
4
3
|
import { XubeResponse } from "@xube/kit-request";
|
|
5
4
|
import { isWebhookEndpoint } from "./schemas/webhook";
|
|
6
5
|
import { StatusCode } from "@xube/kit-constants";
|
|
7
6
|
import { XubeLog } from "@xube/kit-log";
|
|
7
|
+
import { TableItem } from "@xube/kit-aws-schema";
|
|
8
|
+
import { PartialTableKey } from "@xube/kit-aws-schema";
|
|
9
|
+
import { queryItemsFromTable } from "@xube/kit-aws";
|
|
10
|
+
import { SORT_KEY } from "@xube/kit-aws-schema";
|
|
11
|
+
import { getTimeSeriesDataFromData } from "./transform";
|
|
8
12
|
|
|
9
13
|
export const sendDataToEndpoints = async (
|
|
10
14
|
webhookTableName: string,
|
|
@@ -83,10 +87,12 @@ export const sendDataToEndpoints = async (
|
|
|
83
87
|
)}`
|
|
84
88
|
);
|
|
85
89
|
|
|
90
|
+
const timeSeries = getTimeSeriesDataFromData(items);
|
|
91
|
+
|
|
86
92
|
for (const endpoint of webhookEndpoint.endpoints) {
|
|
87
93
|
endpointSends.push(
|
|
88
94
|
fetch(endpoint.url, {
|
|
89
|
-
body: JSON.stringify(
|
|
95
|
+
body: JSON.stringify(timeSeries),
|
|
90
96
|
headers: endpoint.headers,
|
|
91
97
|
method: "POST",
|
|
92
98
|
})
|
|
@@ -110,7 +116,7 @@ export const sendDataToEndpoints = async (
|
|
|
110
116
|
|
|
111
117
|
if (failedResponseCount == endpointSends.length) {
|
|
112
118
|
return new XubeResponse({
|
|
113
|
-
statusCode:
|
|
119
|
+
statusCode: StatusCode.InternalError,
|
|
114
120
|
error: `Failed to send data to all endpoints: ${JSON.stringify(
|
|
115
121
|
failedEndpointSendUrls,
|
|
116
122
|
null,
|
|
@@ -121,15 +127,17 @@ export const sendDataToEndpoints = async (
|
|
|
121
127
|
|
|
122
128
|
if (failedResponseCount) {
|
|
123
129
|
return new XubeResponse({
|
|
124
|
-
statusCode:
|
|
130
|
+
statusCode: StatusCode.PartialSuccess,
|
|
125
131
|
error: `Failed to send data to ${failedResponseCount} of ${
|
|
126
132
|
endpointSends.length
|
|
127
133
|
} endpoints: ${JSON.stringify(failedEndpointSendUrls, null, 2)}`,
|
|
128
134
|
});
|
|
129
135
|
}
|
|
130
136
|
|
|
137
|
+
log.info(`Successfully sent data to all endpoints.`);
|
|
138
|
+
|
|
131
139
|
return new XubeResponse({
|
|
132
|
-
statusCode:
|
|
140
|
+
statusCode: StatusCode.OK,
|
|
133
141
|
data: true,
|
|
134
142
|
});
|
|
135
143
|
} catch (e) {
|
package/src/transform.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { TableItem } from "@xube/kit-aws-schema";
|
|
2
|
+
import { isReadingV1 } from "@xube/kit-data-schema";
|
|
3
|
+
import { DATA_TYPE } from "@xube/kit-data-schema";
|
|
4
|
+
import { TimeSeries, TimeSeriesEntry, TimeUnit } from "@xube/kit-schema";
|
|
5
|
+
|
|
6
|
+
export const stageDataForSend = (items: TableItem[]) => {
|
|
7
|
+
const timeSeries = getTimeSeriesDataFromData(items);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const getTimeSeriesDataFromData = (items: TableItem[]): TimeSeries => {
|
|
11
|
+
const timeSeriesEntriesById: Record<string, TimeSeriesEntry> = {};
|
|
12
|
+
const previousMicroSecondsSinceEpochById: Record<string, number> = {};
|
|
13
|
+
|
|
14
|
+
for (const item of items) {
|
|
15
|
+
if (!isReadingV1(item)) {
|
|
16
|
+
console.log(`${item.type} is not a currently supported type.`);
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const microsSinceEpoch = microsecondsSinceEpoch(item.s, item?.us ?? 0);
|
|
21
|
+
|
|
22
|
+
if (!timeSeriesEntriesById[item.id]) {
|
|
23
|
+
timeSeriesEntriesById[item.id] = {
|
|
24
|
+
v: "1",
|
|
25
|
+
m: {
|
|
26
|
+
id: item.id,
|
|
27
|
+
trigger: DATA_TYPE,
|
|
28
|
+
source: "device",
|
|
29
|
+
},
|
|
30
|
+
t0: isoDateTimeWithMicroseconds(microsSinceEpoch),
|
|
31
|
+
dtt: "ms",
|
|
32
|
+
dt: 0,
|
|
33
|
+
dataT: [],
|
|
34
|
+
data: [item.data],
|
|
35
|
+
};
|
|
36
|
+
} else {
|
|
37
|
+
const timeSeriesEntry = timeSeriesEntriesById[item.id];
|
|
38
|
+
|
|
39
|
+
timeSeriesEntry.data.push(item.data);
|
|
40
|
+
|
|
41
|
+
const timeDifference = Math.floor(
|
|
42
|
+
(microsSinceEpoch - previousMicroSecondsSinceEpochById?.[item.id]) /
|
|
43
|
+
getDivisorByTimeUnit(timeSeriesEntry.dtt)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
if (timeDifference !== timeSeriesEntry.dt) {
|
|
47
|
+
if (!timeSeriesEntry.dataT) {
|
|
48
|
+
timeSeriesEntry.dataT = [];
|
|
49
|
+
} else {
|
|
50
|
+
timeSeriesEntry.dataT.push({
|
|
51
|
+
i: timeSeriesEntry.data.length - 1,
|
|
52
|
+
dt:
|
|
53
|
+
microsSinceEpoch - previousMicroSecondsSinceEpochById?.[item.id],
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
previousMicroSecondsSinceEpochById[item.id] = microsSinceEpoch;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return Object.values(timeSeriesEntriesById);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function getDivisorByTimeUnit(timeUnit: TimeUnit): number {
|
|
66
|
+
switch (timeUnit) {
|
|
67
|
+
case "us":
|
|
68
|
+
return 1;
|
|
69
|
+
case "ms":
|
|
70
|
+
return 1000;
|
|
71
|
+
case "s":
|
|
72
|
+
return 1000000;
|
|
73
|
+
case "m":
|
|
74
|
+
return 60000000;
|
|
75
|
+
case "h":
|
|
76
|
+
return 3600000000;
|
|
77
|
+
case "d":
|
|
78
|
+
return 86400000000;
|
|
79
|
+
default:
|
|
80
|
+
throw new Error("Invalid time unit");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function microsecondsSinceEpoch(s: number, us: number): number {
|
|
85
|
+
return s * 1000000 + us;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isoDateTimeWithMicroseconds(microsecondsSinceEpoch: number): string {
|
|
89
|
+
const milliseconds = Math.floor(microsecondsSinceEpoch / 1000);
|
|
90
|
+
const microseconds = microsecondsSinceEpoch % 1000;
|
|
91
|
+
|
|
92
|
+
const date = new Date(milliseconds);
|
|
93
|
+
|
|
94
|
+
const year: number = date.getUTCFullYear();
|
|
95
|
+
const month: string = (date.getUTCMonth() + 1).toString().padStart(2, "0");
|
|
96
|
+
const day: string = date.getUTCDate().toString().padStart(2, "0");
|
|
97
|
+
const hours: string = date.getUTCHours().toString().padStart(2, "0");
|
|
98
|
+
const minutes: string = date.getUTCMinutes().toString().padStart(2, "0");
|
|
99
|
+
const seconds: string = date.getUTCSeconds().toString().padStart(2, "0");
|
|
100
|
+
const millisecondsStr: string = date
|
|
101
|
+
.getUTCMilliseconds()
|
|
102
|
+
.toString()
|
|
103
|
+
.padStart(3, "0");
|
|
104
|
+
const microsecondsStr: string = microseconds.toString().padStart(3, "0");
|
|
105
|
+
|
|
106
|
+
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${millisecondsStr}${microsecondsStr}Z`;
|
|
107
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -13,10 +13,19 @@
|
|
|
13
13
|
"path": "../kit-log"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
|
-
"path": "../kit-
|
|
16
|
+
"path": "../kit-schema"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "../kit-aws-schema"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"path": "../kit-data-schema"
|
|
17
23
|
},
|
|
18
24
|
{
|
|
19
25
|
"path": "../kit-aws"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"path": "../kit-request"
|
|
20
29
|
}
|
|
21
30
|
]
|
|
22
31
|
}
|