doover-js 0.5.2 → 0.5.4
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.
|
@@ -30,6 +30,14 @@ export declare class AgentsApi {
|
|
|
30
30
|
private readonly rest;
|
|
31
31
|
constructor(rest: RestClient);
|
|
32
32
|
getMultiAgentMessages(channelName: string, params: MultiAgentMessagesParams): Promise<BatchMessagesResponse>;
|
|
33
|
+
/**
|
|
34
|
+
* Batch-fetch aggregates for many agents. Auto-chunks `agent_id` so each
|
|
35
|
+
* request's URL stays under CloudFront's hard 8,192-byte URL quota — each
|
|
36
|
+
* agent contributes ~30 chars (`agent_id=<snowflake>&`), so the chunk size
|
|
37
|
+
* below caps the query string at ~7.5KB even before the base URL. Chunks
|
|
38
|
+
* are fetched in parallel and merged into one `{ results, count }` so
|
|
39
|
+
* callers never have to think about the limit.
|
|
40
|
+
*/
|
|
33
41
|
getMultiAgentAggregates(channelName: string, params: MultiAgentAggregatesParams): Promise<{
|
|
34
42
|
results: AgentAggregate[];
|
|
35
43
|
count: number;
|
package/dist/apis/agents-api.js
CHANGED
|
@@ -13,8 +13,37 @@ class AgentsApi {
|
|
|
13
13
|
results: response.results.map((message) => "timestamp" in message ? message : (0, snowflake_1.addTimestampToMessage)(message)),
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Batch-fetch aggregates for many agents. Auto-chunks `agent_id` so each
|
|
18
|
+
* request's URL stays under CloudFront's hard 8,192-byte URL quota — each
|
|
19
|
+
* agent contributes ~30 chars (`agent_id=<snowflake>&`), so the chunk size
|
|
20
|
+
* below caps the query string at ~7.5KB even before the base URL. Chunks
|
|
21
|
+
* are fetched in parallel and merged into one `{ results, count }` so
|
|
22
|
+
* callers never have to think about the limit.
|
|
23
|
+
*/
|
|
24
|
+
async getMultiAgentAggregates(channelName, params) {
|
|
25
|
+
const path = `/agents/channels/${channelName}/aggregates`;
|
|
26
|
+
const { agent_id, ...rest } = params;
|
|
27
|
+
if (agent_id.length <= MULTI_AGENT_CHUNK_SIZE) {
|
|
28
|
+
return this.rest.get(path, params);
|
|
29
|
+
}
|
|
30
|
+
const chunks = [];
|
|
31
|
+
for (let i = 0; i < agent_id.length; i += MULTI_AGENT_CHUNK_SIZE) {
|
|
32
|
+
chunks.push(agent_id.slice(i, i + MULTI_AGENT_CHUNK_SIZE));
|
|
33
|
+
}
|
|
34
|
+
const responses = await Promise.all(chunks.map((chunk) => this.rest.get(path, {
|
|
35
|
+
...rest,
|
|
36
|
+
agent_id: chunk,
|
|
37
|
+
})));
|
|
38
|
+
return {
|
|
39
|
+
results: responses.flatMap((r) => r.results),
|
|
40
|
+
count: responses.reduce((acc, r) => acc + r.count, 0),
|
|
41
|
+
};
|
|
18
42
|
}
|
|
19
43
|
}
|
|
20
44
|
exports.AgentsApi = AgentsApi;
|
|
45
|
+
// CloudFront fronts the channels-rest API with a hard 8,192-byte URL quota.
|
|
46
|
+
// Each agent adds ~30 chars (`agent_id=<19-digit snowflake>&`), so 250 IDs
|
|
47
|
+
// is ~7.5KB of query string — comfortably under the cap with room for the
|
|
48
|
+
// base URL and any `field_name` params.
|
|
49
|
+
const MULTI_AGENT_CHUNK_SIZE = 250;
|
|
@@ -16,7 +16,12 @@ export interface UseChannelAggregateResult<TData> extends Omit<UseQueryResult<Ag
|
|
|
16
16
|
data: TData | undefined;
|
|
17
17
|
/** Attachments (files/blobs) on the aggregate. */
|
|
18
18
|
attachments: Aggregate["attachments"] | undefined;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Server timestamp (epoch **milliseconds**) of the last aggregate update —
|
|
21
|
+
* matches the `Date` / `dayjs()` constructor. Use `dayjs(last_updated)`, not
|
|
22
|
+
* `dayjs.unix(last_updated)` (the latter assumes seconds and lands ~58000
|
|
23
|
+
* years in the future).
|
|
24
|
+
*/
|
|
20
25
|
last_updated: number | null | undefined;
|
|
21
26
|
}
|
|
22
27
|
export interface UseChannelAggregateOptions {
|
package/dist/test/apis.test.js
CHANGED
|
@@ -61,6 +61,27 @@ function setupRest(responseFactory) {
|
|
|
61
61
|
(0, chai_1.expect)(fetchMock.getCall(0).args[0]).to.equal("https://api.example.com/agents/channels/c1/messages?agent_id=a1&agent_id=a2&limit=5");
|
|
62
62
|
(0, chai_1.expect)(fetchMock.getCall(1).args[0]).to.equal("https://api.example.com/agents/channels/c1/aggregates?agent_id=a1");
|
|
63
63
|
});
|
|
64
|
+
(0, mocha_1.it)("chunks getMultiAgentAggregates over the per-request agent cap", async () => {
|
|
65
|
+
const { rest, fetchMock } = setupRest((url) => {
|
|
66
|
+
const ids = [...new URL(url).searchParams.getAll("agent_id")];
|
|
67
|
+
return (0, helpers_1.createJsonResponse)({
|
|
68
|
+
results: ids.map((id) => ({ agent_id: id, data: {}, attachments: [] })),
|
|
69
|
+
count: ids.length,
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
const api = new agents_api_1.AgentsApi(rest);
|
|
73
|
+
// 600 agents → 3 chunks of 250/250/100.
|
|
74
|
+
const agentIds = Array.from({ length: 600 }, (_, i) => `a${i}`);
|
|
75
|
+
const aggregates = await api.getMultiAgentAggregates("c1", {
|
|
76
|
+
agent_id: agentIds,
|
|
77
|
+
});
|
|
78
|
+
(0, chai_1.expect)(fetchMock.callCount).to.equal(3);
|
|
79
|
+
(0, chai_1.expect)(aggregates.results).to.have.length(600);
|
|
80
|
+
(0, chai_1.expect)(aggregates.count).to.equal(600);
|
|
81
|
+
// Every agent is represented exactly once across the merged chunks.
|
|
82
|
+
const returnedIds = aggregates.results.map((r) => r.agent_id).sort();
|
|
83
|
+
(0, chai_1.expect)(returnedIds).to.deep.equal([...agentIds].sort());
|
|
84
|
+
});
|
|
64
85
|
(0, mocha_1.it)("covers channels methods", async () => {
|
|
65
86
|
const { rest, fetchMock } = setupRest(() => (0, helpers_1.createJsonResponse)([]));
|
|
66
87
|
const api = new channels_api_1.ChannelsApi(rest);
|
package/dist/types/common.d.ts
CHANGED
|
@@ -51,9 +51,10 @@ export interface Alarm {
|
|
|
51
51
|
state: AlarmState;
|
|
52
52
|
expiry_mins: number | null;
|
|
53
53
|
entered_state_ts: number;
|
|
54
|
+
alarm_pending_ms: number | null;
|
|
54
55
|
}
|
|
55
56
|
export type AlarmOperator = "eq" | "ge" | "gt" | "le" | "lt";
|
|
56
|
-
export type AlarmState = "NoData" | "OK" | "Alarm";
|
|
57
|
+
export type AlarmState = "NoData" | "OK" | "Alarm" | "AlarmPending";
|
|
57
58
|
export interface ConnectionSubscription {
|
|
58
59
|
channel: ChannelRef;
|
|
59
60
|
subscribed_at: number;
|
package/dist/types/openapi.d.ts
CHANGED