@synnaxlabs/client 0.21.0 → 0.22.0
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/.turbo/turbo-build.log +7 -7
- package/.vscode/settings.json +3 -0
- package/dist/auth/auth.d.ts +7 -6
- package/dist/auth/auth.d.ts.map +1 -1
- package/dist/channel/client.d.ts +14 -11
- package/dist/channel/client.d.ts.map +1 -1
- package/dist/channel/creator.d.ts +10 -0
- package/dist/channel/creator.d.ts.map +1 -0
- package/dist/channel/payload.d.ts +6 -2
- package/dist/channel/payload.d.ts.map +1 -1
- package/dist/channel/retriever.d.ts +65 -35
- package/dist/channel/retriever.d.ts.map +1 -1
- package/dist/channel/writer.d.ts +4 -3
- package/dist/client.cjs +18 -21
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.ts +12 -10
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +6546 -6457
- package/dist/client.js.map +1 -1
- package/dist/connection/checker.d.ts +4 -3
- package/dist/connection/checker.d.ts.map +1 -1
- package/dist/control/authority.d.ts +2 -1
- package/dist/control/state.d.ts +6 -4
- package/dist/control/state.d.ts.map +1 -1
- package/dist/errors.d.ts +24 -20
- package/dist/errors.d.ts.map +1 -1
- package/dist/framer/adapter.d.ts +5 -4
- package/dist/framer/adapter.d.ts.map +1 -1
- package/dist/framer/client.d.ts +10 -9
- package/dist/framer/client.d.ts.map +1 -1
- package/dist/framer/frame.d.ts +13 -12
- package/dist/framer/frame.d.ts.map +1 -1
- package/dist/framer/iterator.d.ts +5 -4
- package/dist/framer/iterator.d.ts.map +1 -1
- package/dist/framer/streamProxy.d.ts +3 -2
- package/dist/framer/streamProxy.d.ts.map +1 -1
- package/dist/framer/streamer.d.ts +13 -4
- package/dist/framer/streamer.d.ts.map +1 -1
- package/dist/framer/writer.d.ts +42 -33
- package/dist/framer/writer.d.ts.map +1 -1
- package/dist/hardware/client.d.ts +4 -3
- package/dist/hardware/device/client.d.ts +107 -14
- package/dist/hardware/device/client.d.ts.map +1 -1
- package/dist/hardware/device/external.d.ts +0 -2
- package/dist/hardware/device/external.d.ts.map +1 -1
- package/dist/hardware/device/index.d.ts +1 -1
- package/dist/hardware/device/index.d.ts.map +1 -1
- package/dist/hardware/rack/client.d.ts +43 -20
- package/dist/hardware/rack/client.d.ts.map +1 -1
- package/dist/hardware/rack/external.d.ts +0 -2
- package/dist/hardware/rack/external.d.ts.map +1 -1
- package/dist/hardware/task/client.d.ts +197 -14
- package/dist/hardware/task/client.d.ts.map +1 -1
- package/dist/hardware/task/index.d.ts +1 -1
- package/dist/hardware/task/index.d.ts.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/label/client.d.ts +8 -6
- package/dist/label/client.d.ts.map +1 -1
- package/dist/label/payload.d.ts +2 -1
- package/dist/label/retriever.d.ts +3 -2
- package/dist/label/writer.d.ts +4 -3
- package/dist/ontology/client.d.ts +82 -14
- package/dist/ontology/client.d.ts.map +1 -1
- package/dist/ontology/external.d.ts +0 -1
- package/dist/ontology/external.d.ts.map +1 -1
- package/dist/ontology/group/client.d.ts +3 -2
- package/dist/ontology/group/client.d.ts.map +1 -1
- package/dist/ontology/group/group.d.ts +1 -0
- package/dist/ontology/group/payload.d.ts +2 -1
- package/dist/ontology/group/writer.d.ts +4 -3
- package/dist/ontology/payload.d.ts +71 -60
- package/dist/ontology/payload.d.ts.map +1 -1
- package/dist/ontology/writer.d.ts +3 -2
- package/dist/ranger/active.d.ts +3 -2
- package/dist/ranger/alias.d.ts +7 -6
- package/dist/ranger/alias.d.ts.map +1 -1
- package/dist/ranger/client.d.ts +162 -11
- package/dist/ranger/client.d.ts.map +1 -1
- package/dist/ranger/external.d.ts +0 -1
- package/dist/ranger/external.d.ts.map +1 -1
- package/dist/ranger/kv.d.ts +5 -3
- package/dist/ranger/kv.d.ts.map +1 -1
- package/dist/ranger/payload.d.ts +57 -50
- package/dist/ranger/payload.d.ts.map +1 -1
- package/dist/ranger/range.d.ts +12 -10
- package/dist/ranger/range.d.ts.map +1 -1
- package/dist/ranger/writer.d.ts +3 -2
- package/dist/setupspecs.d.ts +2 -1
- package/dist/signals/observable.d.ts +8 -15
- package/dist/signals/observable.d.ts.map +1 -1
- package/dist/transport.d.ts +3 -2
- package/dist/transport.d.ts.map +1 -1
- package/dist/user/payload.d.ts +2 -1
- package/dist/util/retrieve.d.ts +24 -0
- package/dist/util/retrieve.d.ts.map +1 -0
- package/dist/util/retrieve.spec.d.ts +2 -0
- package/dist/util/retrieve.spec.d.ts.map +1 -0
- package/dist/util/telem.d.ts +2 -1
- package/dist/util/telem.d.ts.map +1 -1
- package/dist/util/zod.d.ts +4 -0
- package/dist/util/zod.d.ts.map +1 -0
- package/dist/workspace/client.d.ts +9 -6
- package/dist/workspace/client.d.ts.map +1 -1
- package/dist/workspace/lineplot/client.d.ts +6 -5
- package/dist/workspace/lineplot/client.d.ts.map +1 -1
- package/dist/workspace/lineplot/payload.d.ts +3 -2
- package/dist/workspace/lineplot/payload.d.ts.map +1 -1
- package/dist/workspace/lineplot/retriever.d.ts +3 -2
- package/dist/workspace/lineplot/retriever.d.ts.map +1 -1
- package/dist/workspace/lineplot/writer.d.ts +7 -6
- package/dist/workspace/lineplot/writer.d.ts.map +1 -1
- package/dist/workspace/payload.d.ts +3 -2
- package/dist/workspace/payload.d.ts.map +1 -1
- package/dist/workspace/retriever.d.ts +3 -2
- package/dist/workspace/schematic/client.d.ts +18 -0
- package/dist/workspace/schematic/client.d.ts.map +1 -0
- package/dist/workspace/schematic/external.d.ts.map +1 -0
- package/dist/workspace/schematic/index.d.ts +2 -0
- package/dist/workspace/schematic/index.d.ts.map +1 -0
- package/dist/workspace/{pid → schematic}/payload.d.ts +6 -5
- package/dist/workspace/schematic/payload.d.ts.map +1 -0
- package/dist/workspace/schematic/retriever.d.ts +10 -0
- package/dist/workspace/schematic/retriever.d.ts.map +1 -0
- package/dist/workspace/schematic/schematic.spec.d.ts +2 -0
- package/dist/workspace/schematic/schematic.spec.d.ts.map +1 -0
- package/dist/workspace/{pid → schematic}/writer.d.ts +11 -10
- package/dist/workspace/schematic/writer.d.ts.map +1 -0
- package/dist/workspace/writer.d.ts +5 -4
- package/dist/workspace/writer.d.ts.map +1 -1
- package/package.json +9 -8
- package/src/auth/auth.spec.ts +55 -15
- package/src/auth/auth.ts +41 -42
- package/src/channel/batchRetriever.spec.ts +37 -40
- package/src/channel/channel.spec.ts +4 -4
- package/src/channel/client.ts +42 -49
- package/src/channel/creator.ts +37 -0
- package/src/channel/payload.ts +2 -1
- package/src/channel/retriever.ts +55 -71
- package/src/client.ts +23 -20
- package/src/connection/checker.ts +1 -1
- package/src/connection/connection.spec.ts +1 -6
- package/src/control/state.ts +3 -1
- package/src/errors.ts +71 -67
- package/src/framer/adapter.spec.ts +33 -1
- package/src/framer/adapter.ts +10 -6
- package/src/framer/client.ts +11 -9
- package/src/framer/frame.spec.ts +1 -1
- package/src/framer/frame.ts +9 -6
- package/src/framer/iterator.spec.ts +1 -1
- package/src/framer/iterator.ts +1 -1
- package/src/framer/streamProxy.ts +12 -13
- package/src/framer/streamer.spec.ts +1 -1
- package/src/framer/streamer.ts +25 -1
- package/src/framer/writer.spec.ts +23 -11
- package/src/framer/writer.ts +6 -6
- package/src/hardware/device/client.ts +155 -28
- package/src/hardware/device/device.spec.ts +2 -2
- package/src/hardware/device/external.ts +0 -2
- package/src/hardware/device/index.ts +2 -2
- package/src/hardware/rack/client.ts +139 -56
- package/src/hardware/rack/external.ts +0 -2
- package/src/hardware/rack/rack.spec.ts +20 -1
- package/src/hardware/task/client.ts +324 -31
- package/src/hardware/task/index.ts +1 -1
- package/src/hardware/task/task.spec.ts +41 -0
- package/src/index.ts +3 -4
- package/src/label/client.ts +3 -2
- package/src/label/retriever.ts +1 -1
- package/src/label/writer.ts +1 -1
- package/src/ontology/client.ts +195 -41
- package/src/ontology/external.ts +0 -1
- package/src/ontology/group/client.ts +1 -2
- package/src/ontology/group/payload.ts +1 -1
- package/src/ontology/ontology.spec.ts +16 -0
- package/src/ontology/payload.ts +22 -13
- package/src/ranger/active.ts +5 -5
- package/src/ranger/alias.ts +2 -2
- package/src/ranger/client.ts +68 -17
- package/src/ranger/external.ts +0 -1
- package/src/ranger/kv.ts +6 -1
- package/src/ranger/payload.ts +6 -4
- package/src/ranger/range.ts +4 -1
- package/src/ranger/ranger.spec.ts +24 -2
- package/src/signals/observable.ts +24 -63
- package/src/transport.ts +2 -1
- package/src/util/retrieve.spec.ts +56 -0
- package/src/util/retrieve.ts +103 -0
- package/src/util/telem.ts +1 -1
- package/src/util/zod.ts +4 -0
- package/src/workspace/client.ts +6 -4
- package/src/workspace/lineplot/client.ts +3 -3
- package/src/workspace/lineplot/linePlot.spec.ts +11 -11
- package/src/workspace/lineplot/payload.ts +1 -1
- package/src/workspace/lineplot/retriever.ts +5 -13
- package/src/workspace/lineplot/writer.ts +8 -7
- package/src/workspace/payload.ts +6 -3
- package/src/workspace/retriever.ts +1 -1
- package/src/workspace/{pid → schematic}/client.ts +10 -10
- package/src/workspace/{pid → schematic}/external.ts +2 -2
- package/src/workspace/{pid → schematic}/index.ts +1 -1
- package/src/workspace/{pid → schematic}/payload.ts +4 -4
- package/src/workspace/{pid → schematic}/retriever.ts +10 -10
- package/src/workspace/{pid/pid.spec.ts → schematic/schematic.spec.ts} +35 -35
- package/src/workspace/{pid → schematic}/writer.ts +26 -25
- package/src/workspace/workspace.spec.ts +7 -7
- package/src/workspace/writer.ts +8 -2
- package/dist/hardware/device/payload.d.ts +0 -30
- package/dist/hardware/device/payload.d.ts.map +0 -1
- package/dist/hardware/device/retriever.d.ts +0 -10
- package/dist/hardware/device/retriever.d.ts.map +0 -1
- package/dist/hardware/device/writer.d.ts +0 -9
- package/dist/hardware/device/writer.d.ts.map +0 -1
- package/dist/hardware/rack/payload.d.ts +0 -26
- package/dist/hardware/rack/payload.d.ts.map +0 -1
- package/dist/hardware/rack/retriever.d.ts +0 -10
- package/dist/hardware/rack/retriever.d.ts.map +0 -1
- package/dist/hardware/rack/writer.d.ts +0 -9
- package/dist/hardware/rack/writer.d.ts.map +0 -1
- package/dist/hardware/task/external.d.ts +0 -4
- package/dist/hardware/task/external.d.ts.map +0 -1
- package/dist/hardware/task/payload.d.ts +0 -42
- package/dist/hardware/task/payload.d.ts.map +0 -1
- package/dist/hardware/task/retriever.d.ts +0 -29
- package/dist/hardware/task/retriever.d.ts.map +0 -1
- package/dist/hardware/task/writer.d.ts +0 -9
- package/dist/hardware/task/writer.d.ts.map +0 -1
- package/dist/ontology/retriever.d.ts +0 -13
- package/dist/ontology/retriever.d.ts.map +0 -1
- package/dist/ontology/signals.d.ts +0 -30
- package/dist/ontology/signals.d.ts.map +0 -1
- package/dist/ranger/retriever.d.ts +0 -11
- package/dist/ranger/retriever.d.ts.map +0 -1
- package/dist/workspace/pid/client.d.ts +0 -17
- package/dist/workspace/pid/client.d.ts.map +0 -1
- package/dist/workspace/pid/external.d.ts.map +0 -1
- package/dist/workspace/pid/index.d.ts +0 -2
- package/dist/workspace/pid/index.d.ts.map +0 -1
- package/dist/workspace/pid/payload.d.ts.map +0 -1
- package/dist/workspace/pid/pid.spec.d.ts +0 -2
- package/dist/workspace/pid/pid.spec.d.ts.map +0 -1
- package/dist/workspace/pid/retriever.d.ts +0 -9
- package/dist/workspace/pid/retriever.d.ts.map +0 -1
- package/dist/workspace/pid/writer.d.ts.map +0 -1
- package/src/hardware/device/payload.ts +0 -27
- package/src/hardware/device/retriever.ts +0 -71
- package/src/hardware/device/writer.ts +0 -59
- package/src/hardware/rack/payload.ts +0 -26
- package/src/hardware/rack/retriever.ts +0 -68
- package/src/hardware/rack/writer.ts +0 -59
- package/src/hardware/task/external.ts +0 -12
- package/src/hardware/task/payload.ts +0 -40
- package/src/hardware/task/retriever.ts +0 -70
- package/src/hardware/task/writer.ts +0 -65
- package/src/ontology/retriever.ts +0 -91
- package/src/ontology/signals.ts +0 -136
- package/src/ranger/retriever.ts +0 -50
- /package/dist/workspace/{pid → schematic}/external.d.ts +0 -0
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import { DataType, Rate, TimeRange, TimeSpan, TimeStamp } from "@synnaxlabs/x";
|
|
10
|
+
import { DataType, Rate, TimeRange, TimeSpan, TimeStamp } from "@synnaxlabs/x/telem";
|
|
11
11
|
import { describe, expect, test } from "vitest";
|
|
12
12
|
|
|
13
13
|
import { type channel } from "@/channel";
|
|
@@ -68,7 +68,11 @@ describe("Writer", () => {
|
|
|
68
68
|
});
|
|
69
69
|
test("write with auto commit on", async () => {
|
|
70
70
|
const ch = await newChannel();
|
|
71
|
-
const writer = await client.openWriter({
|
|
71
|
+
const writer = await client.openWriter({
|
|
72
|
+
start: 0,
|
|
73
|
+
channels: ch.key,
|
|
74
|
+
enableAutoCommit: true,
|
|
75
|
+
});
|
|
72
76
|
try {
|
|
73
77
|
await writer.write(ch.key, randomSeries(10, ch.dataType));
|
|
74
78
|
} finally {
|
|
@@ -76,31 +80,39 @@ describe("Writer", () => {
|
|
|
76
80
|
}
|
|
77
81
|
expect(true).toBeTruthy();
|
|
78
82
|
|
|
79
|
-
const f = await client.read(new TimeRange(0, TimeStamp.seconds(10)), ch.key)
|
|
80
|
-
expect(f.length).toEqual(10)
|
|
81
|
-
})
|
|
83
|
+
const f = await client.read(new TimeRange(0, TimeStamp.seconds(10)), ch.key);
|
|
84
|
+
expect(f.length).toEqual(10);
|
|
85
|
+
});
|
|
82
86
|
test("write with auto commit and alwaysPersist", async () => {
|
|
83
87
|
const ch = await newChannel();
|
|
84
|
-
const writer = await client.openWriter({
|
|
85
|
-
|
|
88
|
+
const writer = await client.openWriter({
|
|
89
|
+
start: 0,
|
|
90
|
+
channels: ch.key,
|
|
91
|
+
enableAutoCommit: true,
|
|
92
|
+
autoIndexPersistInterval: ALWAYS_INDEX_PERSIST_ON_AUTO_COMMIT,
|
|
93
|
+
});
|
|
86
94
|
try {
|
|
87
95
|
await writer.write(ch.key, randomSeries(10, ch.dataType));
|
|
88
96
|
} finally {
|
|
89
97
|
await writer.close();
|
|
90
98
|
}
|
|
91
99
|
expect(true).toBeTruthy();
|
|
92
|
-
})
|
|
100
|
+
});
|
|
93
101
|
test("write with auto commit and a set interval", async () => {
|
|
94
102
|
const ch = await newChannel();
|
|
95
|
-
const writer = await client.openWriter({
|
|
96
|
-
|
|
103
|
+
const writer = await client.openWriter({
|
|
104
|
+
start: 0,
|
|
105
|
+
channels: ch.key,
|
|
106
|
+
enableAutoCommit: true,
|
|
107
|
+
autoIndexPersistInterval: TimeSpan.milliseconds(100),
|
|
108
|
+
});
|
|
97
109
|
try {
|
|
98
110
|
await writer.write(ch.key, randomSeries(10, ch.dataType));
|
|
99
111
|
} finally {
|
|
100
112
|
await writer.close();
|
|
101
113
|
}
|
|
102
114
|
expect(true).toBeTruthy();
|
|
103
|
-
})
|
|
115
|
+
});
|
|
104
116
|
});
|
|
105
117
|
describe("Client", () => {
|
|
106
118
|
test("Client - basic write", async () => {
|
package/src/framer/writer.ts
CHANGED
|
@@ -13,10 +13,10 @@ import { decodeError, errorZ } from "@synnaxlabs/freighter";
|
|
|
13
13
|
import {
|
|
14
14
|
TimeStamp,
|
|
15
15
|
type CrudeTimeStamp,
|
|
16
|
-
toArray,
|
|
17
16
|
type CrudeSeries,
|
|
18
17
|
TimeSpan,
|
|
19
|
-
} from "@synnaxlabs/x";
|
|
18
|
+
} from "@synnaxlabs/x/telem";
|
|
19
|
+
import { toArray } from "@synnaxlabs/x/toArray";
|
|
20
20
|
import { z } from "zod";
|
|
21
21
|
|
|
22
22
|
import {
|
|
@@ -50,7 +50,7 @@ export enum WriterMode {
|
|
|
50
50
|
StreamOnly = 3,
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
export const ALWAYS_INDEX_PERSIST_ON_AUTO_COMMIT
|
|
53
|
+
export const ALWAYS_INDEX_PERSIST_ON_AUTO_COMMIT: TimeSpan = new TimeSpan(-1);
|
|
54
54
|
|
|
55
55
|
const netConfigZ = z.object({
|
|
56
56
|
start: TimeStamp.z.optional(),
|
|
@@ -95,11 +95,11 @@ export interface WriterConfig {
|
|
|
95
95
|
// enableAutoCommit determines whether the writer will automatically commit.
|
|
96
96
|
// If enableAutoCommit is true, then the writer will commit after each write, and
|
|
97
97
|
// will flush that commit to index after the specified autoIndexPersistInterval.
|
|
98
|
-
enableAutoCommit?: boolean
|
|
98
|
+
enableAutoCommit?: boolean;
|
|
99
99
|
// autoIndexPersistInterval sets the interval at which commits to the index will be
|
|
100
100
|
// persisted. To persist every commit to guarantee minimal loss of data, set
|
|
101
101
|
// auto_index_persist_interval to AlwaysAutoIndexPersist.
|
|
102
|
-
autoIndexPersistInterval?: TimeSpan
|
|
102
|
+
autoIndexPersistInterval?: TimeSpan;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
/**
|
|
@@ -163,7 +163,7 @@ export class Writer {
|
|
|
163
163
|
controlSubject: subject,
|
|
164
164
|
mode = WriterMode.PersistStream,
|
|
165
165
|
enableAutoCommit = false,
|
|
166
|
-
autoIndexPersistInterval = TimeSpan.SECOND
|
|
166
|
+
autoIndexPersistInterval = TimeSpan.SECOND,
|
|
167
167
|
}: WriterConfig,
|
|
168
168
|
): Promise<Writer> {
|
|
169
169
|
const adapter = await WriteFrameAdapter.open(retriever, channels);
|
|
@@ -7,63 +7,190 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { type UnaryClient, sendRequired } from "@synnaxlabs/freighter";
|
|
11
|
+
import { type UnknownRecord, toArray } from "@synnaxlabs/x";
|
|
12
|
+
import { binary } from "@synnaxlabs/x/binary";
|
|
13
|
+
import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
|
|
14
|
+
import { z } from "zod";
|
|
11
15
|
|
|
12
16
|
import { type framer } from "@/framer";
|
|
13
|
-
import {
|
|
14
|
-
import { type Retriever } from "@/hardware/device/retriever";
|
|
15
|
-
import { type Writer } from "@/hardware/device/writer";
|
|
17
|
+
import { rackKeyZ } from "@/hardware/rack/client";
|
|
16
18
|
import { signals } from "@/signals";
|
|
19
|
+
import { checkForMultipleOrNoResults } from "@/util/retrieve";
|
|
20
|
+
import { nullableArrayZ } from "@/util/zod";
|
|
17
21
|
|
|
18
22
|
const DEVICE_SET_NAME = "sy_device_set";
|
|
19
23
|
const DEVICE_DELETE_NAME = "sy_device_delete";
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
const RETRIEVE_ENDPOINT = "/hardware/device/retrieve";
|
|
26
|
+
const CREATE_ENDPOINT = "/hardware/device/create";
|
|
27
|
+
const DELETE_ENDPOINT = "/hardware/device/delete";
|
|
28
|
+
|
|
29
|
+
export const deviceKeyZ = z.string();
|
|
30
|
+
|
|
31
|
+
export const deviceZ = z.object({
|
|
32
|
+
key: deviceKeyZ,
|
|
33
|
+
rack: rackKeyZ,
|
|
34
|
+
name: z.string(),
|
|
35
|
+
make: z.string(),
|
|
36
|
+
model: z.string(),
|
|
37
|
+
location: z.string(),
|
|
38
|
+
configured: z.boolean().optional(),
|
|
39
|
+
properties: z.record(z.unknown()).or(
|
|
40
|
+
z.string().transform((c) => {
|
|
41
|
+
if (c === "") return {};
|
|
42
|
+
return binary.JSON_ECD.decodeString(c);
|
|
43
|
+
}),
|
|
44
|
+
) as z.ZodType<UnknownRecord>,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export type Device<P extends UnknownRecord = UnknownRecord> = Omit<
|
|
48
|
+
z.output<typeof deviceZ>,
|
|
49
|
+
"properties"
|
|
50
|
+
> & { properties: P };
|
|
51
|
+
|
|
52
|
+
export type DeviceKey = z.infer<typeof deviceKeyZ>;
|
|
53
|
+
|
|
54
|
+
export const newDeviceZ = deviceZ.extend({
|
|
55
|
+
properties: z.unknown().transform((c) => binary.JSON_ECD.encodeString(c)),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export type NewDevice<P extends UnknownRecord = UnknownRecord> = Omit<
|
|
59
|
+
z.input<typeof newDeviceZ>,
|
|
60
|
+
"properties"
|
|
61
|
+
> & { properties: P };
|
|
62
|
+
|
|
63
|
+
const createReqZ = z.object({ devices: newDeviceZ.array() });
|
|
64
|
+
|
|
65
|
+
const createResZ = z.object({ devices: deviceZ.array() });
|
|
66
|
+
|
|
67
|
+
const deleteReqZ = z.object({ keys: deviceKeyZ.array() });
|
|
68
|
+
|
|
69
|
+
const deleteResZ = z.object({});
|
|
70
|
+
|
|
71
|
+
const retrieveReqZ = z.object({
|
|
72
|
+
search: z.string().optional(),
|
|
73
|
+
limit: z.number().optional(),
|
|
74
|
+
offset: z.number().optional(),
|
|
75
|
+
keys: deviceKeyZ.array().optional(),
|
|
76
|
+
names: z.string().array().optional(),
|
|
77
|
+
makes: z.string().array().optional(),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
type RetrieveRequest = z.input<typeof retrieveReqZ>;
|
|
81
|
+
|
|
82
|
+
export type RetrieveOptions = Pick<RetrieveRequest, "limit" | "offset" | "makes">;
|
|
83
|
+
|
|
84
|
+
type PageOptions = Pick<RetrieveOptions, "makes">;
|
|
85
|
+
|
|
86
|
+
const retrieveResZ = z.object({ devices: nullableArrayZ(deviceZ) });
|
|
87
|
+
|
|
88
|
+
export class Client implements AsyncTermSearcher<string, DeviceKey, Device> {
|
|
89
|
+
readonly type = "device";
|
|
90
|
+
private readonly client: UnaryClient;
|
|
24
91
|
private readonly frameClient: framer.Client;
|
|
25
92
|
|
|
26
|
-
constructor(
|
|
27
|
-
this.
|
|
28
|
-
this.writer = writer;
|
|
93
|
+
constructor(client: UnaryClient, frameClient: framer.Client) {
|
|
94
|
+
this.client = client;
|
|
29
95
|
this.frameClient = frameClient;
|
|
30
96
|
}
|
|
31
97
|
|
|
32
|
-
async
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
98
|
+
async retrieve<P extends UnknownRecord = UnknownRecord>(
|
|
99
|
+
key: string,
|
|
100
|
+
options?: RetrieveOptions,
|
|
101
|
+
): Promise<Device<P>>;
|
|
36
102
|
|
|
37
|
-
async retrieve
|
|
38
|
-
|
|
103
|
+
async retrieve<P extends UnknownRecord = UnknownRecord>(
|
|
104
|
+
keys: string[],
|
|
105
|
+
options?: RetrieveOptions,
|
|
106
|
+
): Promise<Array<Device<P>>>;
|
|
39
107
|
|
|
40
|
-
async retrieve
|
|
41
|
-
|
|
42
|
-
|
|
108
|
+
async retrieve<P extends UnknownRecord = UnknownRecord>(
|
|
109
|
+
keys: string | string[],
|
|
110
|
+
options?: RetrieveOptions,
|
|
111
|
+
): Promise<Device<P> | Array<Device<P>>> {
|
|
112
|
+
const isSingle = !Array.isArray(keys);
|
|
113
|
+
const res = await sendRequired(
|
|
114
|
+
this.client,
|
|
115
|
+
RETRIEVE_ENDPOINT,
|
|
116
|
+
{ keys: toArray(keys), ...options },
|
|
117
|
+
retrieveReqZ,
|
|
118
|
+
retrieveResZ,
|
|
119
|
+
);
|
|
120
|
+
checkForMultipleOrNoResults("Device", keys, res.devices, isSingle);
|
|
121
|
+
return (isSingle ? res.devices[0] : res.devices) as Device<P> | Array<Device<P>>;
|
|
43
122
|
}
|
|
44
123
|
|
|
45
|
-
async search(term: string): Promise<Device[]> {
|
|
46
|
-
|
|
47
|
-
|
|
124
|
+
async search(term: string, options?: RetrieveOptions): Promise<Device[]> {
|
|
125
|
+
return (
|
|
126
|
+
await sendRequired(
|
|
127
|
+
this.client,
|
|
128
|
+
RETRIEVE_ENDPOINT,
|
|
129
|
+
{ search: term, ...options },
|
|
130
|
+
retrieveReqZ,
|
|
131
|
+
retrieveResZ,
|
|
132
|
+
)
|
|
133
|
+
).devices;
|
|
48
134
|
}
|
|
49
135
|
|
|
50
|
-
async page(offset: number, limit: number): Promise<Device[]> {
|
|
51
|
-
|
|
52
|
-
|
|
136
|
+
async page(offset: number, limit: number, options?: PageOptions): Promise<Device[]> {
|
|
137
|
+
return (
|
|
138
|
+
await sendRequired(
|
|
139
|
+
this.client,
|
|
140
|
+
RETRIEVE_ENDPOINT,
|
|
141
|
+
{ offset, limit, ...options },
|
|
142
|
+
retrieveReqZ,
|
|
143
|
+
retrieveResZ,
|
|
144
|
+
)
|
|
145
|
+
).devices;
|
|
53
146
|
}
|
|
54
147
|
|
|
55
|
-
async
|
|
56
|
-
|
|
148
|
+
async create(device: NewDevice): Promise<Device>;
|
|
149
|
+
|
|
150
|
+
async create(devices: NewDevice[]): Promise<Device[]>;
|
|
151
|
+
|
|
152
|
+
async create(devices: NewDevice | NewDevice[]): Promise<Device | Device[]> {
|
|
153
|
+
const isSingle = !Array.isArray(devices);
|
|
154
|
+
const res = await sendRequired(
|
|
155
|
+
this.client,
|
|
156
|
+
CREATE_ENDPOINT,
|
|
157
|
+
{ devices: toArray(devices) },
|
|
158
|
+
createReqZ,
|
|
159
|
+
createResZ,
|
|
160
|
+
);
|
|
161
|
+
return isSingle ? res.devices[0] : res.devices;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async delete(keys: string | string[]): Promise<void> {
|
|
165
|
+
await sendRequired(
|
|
166
|
+
this.client,
|
|
167
|
+
DELETE_ENDPOINT,
|
|
168
|
+
{ keys: toArray(keys) },
|
|
169
|
+
deleteReqZ,
|
|
170
|
+
deleteResZ,
|
|
171
|
+
);
|
|
57
172
|
}
|
|
58
173
|
|
|
59
174
|
async openDeviceTracker(): Promise<signals.Observable<string, Device>> {
|
|
60
|
-
return await signals.
|
|
175
|
+
return await signals.openObservable<string, Device>(
|
|
61
176
|
this.frameClient,
|
|
62
177
|
DEVICE_SET_NAME,
|
|
63
178
|
DEVICE_DELETE_NAME,
|
|
64
179
|
decodeDeviceChanges,
|
|
65
180
|
);
|
|
66
181
|
}
|
|
182
|
+
|
|
183
|
+
newSearcherWithOptions(
|
|
184
|
+
options: RetrieveOptions,
|
|
185
|
+
): AsyncTermSearcher<string, DeviceKey, Device> {
|
|
186
|
+
return {
|
|
187
|
+
type: this.type,
|
|
188
|
+
search: async (term: string) => await this.search(term, options),
|
|
189
|
+
retrieve: async (keys: string[]) => await this.retrieve(keys, options),
|
|
190
|
+
page: async (offset: number, limit: number) =>
|
|
191
|
+
await this.page(offset, limit, options),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
67
194
|
}
|
|
68
195
|
|
|
69
196
|
const decodeDeviceChanges: signals.Decoder<string, Device> = (variant, data) => {
|
|
@@ -25,7 +25,7 @@ describe("Device", () => {
|
|
|
25
25
|
name: "test",
|
|
26
26
|
make: "ni",
|
|
27
27
|
model: "dog",
|
|
28
|
-
properties: "dog",
|
|
28
|
+
properties: { cat: "dog" },
|
|
29
29
|
});
|
|
30
30
|
expect(d.key).toEqual("SN222");
|
|
31
31
|
expect(d.name).toBe("test");
|
|
@@ -42,7 +42,7 @@ describe("Device", () => {
|
|
|
42
42
|
name: "test",
|
|
43
43
|
make: "ni",
|
|
44
44
|
model: "dog",
|
|
45
|
-
properties: "dog",
|
|
45
|
+
properties: { cat: "dog" },
|
|
46
46
|
});
|
|
47
47
|
const retrieved = await client.hardware.devices.retrieve(d.key);
|
|
48
48
|
expect(retrieved.key).toBe(d.key);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright
|
|
1
|
+
// Copyright 2023 Synnax Labs, Inc.
|
|
2
2
|
//
|
|
3
3
|
// Use of this software is governed by the Business Source License included in the file
|
|
4
4
|
// licenses/BSL.txt.
|
|
@@ -7,4 +7,4 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
export * as device from "@/hardware/device/
|
|
10
|
+
export * as device from "@/hardware/device/client";
|
|
@@ -7,105 +7,188 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
+
import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
|
+
import { type UnknownRecord } from "@synnaxlabs/x";
|
|
12
|
+
import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
|
|
13
|
+
import { toArray } from "@synnaxlabs/x/toArray";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
|
|
10
16
|
import { type framer } from "@/framer";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
import { type task } from "@/hardware/task";
|
|
18
|
+
import { analyzeParams, checkForMultipleOrNoResults } from "@/util/retrieve";
|
|
19
|
+
import { nullableArrayZ } from "@/util/zod";
|
|
20
|
+
|
|
21
|
+
export const rackKeyZ = z.number();
|
|
22
|
+
|
|
23
|
+
export type RackKey = z.infer<typeof rackKeyZ>;
|
|
24
|
+
|
|
25
|
+
export const rackZ = z.object({
|
|
26
|
+
key: rackKeyZ,
|
|
27
|
+
name: z.string(),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export type RackPayload = z.infer<typeof rackZ>;
|
|
31
|
+
|
|
32
|
+
export const newRackZ = rackZ.partial({ key: true });
|
|
33
|
+
|
|
34
|
+
export type NewRack = z.input<typeof newRackZ>;
|
|
35
|
+
|
|
36
|
+
const RETRIEVE_ENDPOINT = "/hardware/rack/retrieve";
|
|
37
|
+
const CREATE_RACK_ENDPOINT = "/hardware/rack/create";
|
|
38
|
+
const DELETE_RACK_ENDPOINT = "/hardware/rack/delete";
|
|
39
|
+
|
|
40
|
+
const retrieveRackReqZ = z.object({
|
|
41
|
+
keys: rackKeyZ.array().optional(),
|
|
42
|
+
names: z.string().array().optional(),
|
|
43
|
+
search: z.string().optional(),
|
|
44
|
+
offset: z.number().optional(),
|
|
45
|
+
limit: z.number().optional(),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const retrieveRackResZ = z.object({
|
|
49
|
+
racks: nullableArrayZ(rackZ),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const createReqZ = z.object({
|
|
53
|
+
racks: newRackZ.array(),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const createResZ = z.object({
|
|
57
|
+
racks: rackZ.array(),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const deleteReqZ = z.object({
|
|
61
|
+
keys: rackKeyZ.array(),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const deleteResZ = z.object({});
|
|
18
65
|
|
|
19
66
|
export class Client implements AsyncTermSearcher<string, RackKey, Rack> {
|
|
20
|
-
|
|
21
|
-
private readonly
|
|
67
|
+
readonly type: string = "rack";
|
|
68
|
+
private readonly client: UnaryClient;
|
|
22
69
|
private readonly frameClient: framer.Client;
|
|
23
|
-
private readonly
|
|
24
|
-
private readonly taskRetriever: TaskRetriever;
|
|
70
|
+
private readonly tasks: task.Client;
|
|
25
71
|
|
|
26
72
|
constructor(
|
|
27
|
-
|
|
28
|
-
writer: Writer,
|
|
73
|
+
client: UnaryClient,
|
|
29
74
|
frameClient: framer.Client,
|
|
30
|
-
|
|
31
|
-
taskRetriever: TaskRetriever,
|
|
75
|
+
taskClient: task.Client,
|
|
32
76
|
) {
|
|
33
|
-
this.
|
|
34
|
-
this.writer = writer;
|
|
77
|
+
this.client = client;
|
|
35
78
|
this.frameClient = frameClient;
|
|
36
|
-
this.
|
|
37
|
-
this.taskRetriever = taskRetriever;
|
|
79
|
+
this.tasks = taskClient;
|
|
38
80
|
}
|
|
39
81
|
|
|
40
|
-
async
|
|
41
|
-
|
|
42
|
-
|
|
82
|
+
async delete(keys: RackKey | RackKey[]): Promise<void> {
|
|
83
|
+
await sendRequired<typeof deleteReqZ, typeof deleteResZ>(
|
|
84
|
+
this.client,
|
|
85
|
+
DELETE_RACK_ENDPOINT,
|
|
86
|
+
{ keys: toArray(keys) },
|
|
87
|
+
deleteReqZ,
|
|
88
|
+
deleteResZ,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async create(rack: NewRack): Promise<Rack>;
|
|
93
|
+
|
|
94
|
+
async create(racks: NewRack[]): Promise<Rack[]>;
|
|
95
|
+
|
|
96
|
+
async create(rack: NewRack | NewRack[]): Promise<Rack | Rack[]> {
|
|
97
|
+
const isSingle = !Array.isArray(rack);
|
|
98
|
+
const res = await sendRequired<typeof createReqZ, typeof createResZ>(
|
|
99
|
+
this.client,
|
|
100
|
+
CREATE_RACK_ENDPOINT,
|
|
101
|
+
{ racks: toArray(rack) },
|
|
102
|
+
createReqZ,
|
|
103
|
+
createResZ,
|
|
104
|
+
);
|
|
105
|
+
const sugared = this.sugar(res.racks);
|
|
106
|
+
if (isSingle) return sugared[0];
|
|
107
|
+
return sugared;
|
|
43
108
|
}
|
|
44
109
|
|
|
45
110
|
async search(term: string): Promise<Rack[]> {
|
|
46
|
-
const res = await
|
|
47
|
-
|
|
111
|
+
const res = await sendRequired<typeof retrieveRackReqZ, typeof retrieveRackResZ>(
|
|
112
|
+
this.client,
|
|
113
|
+
RETRIEVE_ENDPOINT,
|
|
114
|
+
{ search: term },
|
|
115
|
+
retrieveRackReqZ,
|
|
116
|
+
retrieveRackResZ,
|
|
117
|
+
);
|
|
118
|
+
return this.sugar(res.racks);
|
|
48
119
|
}
|
|
49
120
|
|
|
50
121
|
async page(offset: number, limit: number): Promise<Rack[]> {
|
|
51
|
-
const res = await
|
|
52
|
-
|
|
122
|
+
const res = await sendRequired<typeof retrieveRackReqZ, typeof retrieveRackResZ>(
|
|
123
|
+
this.client,
|
|
124
|
+
RETRIEVE_ENDPOINT,
|
|
125
|
+
{ offset, limit },
|
|
126
|
+
retrieveRackReqZ,
|
|
127
|
+
retrieveRackResZ,
|
|
128
|
+
);
|
|
129
|
+
return this.sugar(res.racks);
|
|
53
130
|
}
|
|
54
131
|
|
|
55
|
-
async retrieve(key: RackKey): Promise<Rack>;
|
|
56
|
-
|
|
57
|
-
async retrieve(keys: RackKey[]): Promise<Rack[]>;
|
|
58
|
-
|
|
59
|
-
async retrieve(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
132
|
+
async retrieve(key: string | RackKey): Promise<Rack>;
|
|
133
|
+
|
|
134
|
+
async retrieve(keys: number[] | RackKey[]): Promise<Rack[]>;
|
|
135
|
+
|
|
136
|
+
async retrieve(
|
|
137
|
+
params: string | RackKey | string[] | RackKey[],
|
|
138
|
+
): Promise<Rack | Rack[]> {
|
|
139
|
+
const { variant, normalized, single } = analyzeParams(params, {
|
|
140
|
+
string: "names",
|
|
141
|
+
number: "keys",
|
|
142
|
+
});
|
|
143
|
+
const res = await sendRequired<typeof retrieveRackReqZ, typeof retrieveRackResZ>(
|
|
144
|
+
this.client,
|
|
145
|
+
RETRIEVE_ENDPOINT,
|
|
146
|
+
{ [variant]: normalized },
|
|
147
|
+
retrieveRackReqZ,
|
|
148
|
+
retrieveRackResZ,
|
|
149
|
+
);
|
|
150
|
+
const sugared = this.sugar(res.racks);
|
|
151
|
+
checkForMultipleOrNoResults("Rack", params, sugared, single);
|
|
152
|
+
return single ? sugared[0] : sugared;
|
|
63
153
|
}
|
|
64
154
|
|
|
65
155
|
private sugar(payloads: RackPayload[]): Rack[] {
|
|
66
|
-
return payloads.map(
|
|
67
|
-
(payload) =>
|
|
68
|
-
new Rack(payload.key, payload.name, this.taskWriter, this.taskRetriever),
|
|
69
|
-
);
|
|
156
|
+
return payloads.map(({ key, name }) => new Rack(key, name, this.tasks));
|
|
70
157
|
}
|
|
71
158
|
}
|
|
72
159
|
|
|
73
160
|
export class Rack {
|
|
74
161
|
key: number;
|
|
75
162
|
name: string;
|
|
76
|
-
private readonly
|
|
77
|
-
private readonly tasks: TaskRetriever;
|
|
163
|
+
private readonly tasks: task.Client;
|
|
78
164
|
|
|
79
|
-
constructor(
|
|
80
|
-
key: number,
|
|
81
|
-
name: string,
|
|
82
|
-
_writer: TaskWriter,
|
|
83
|
-
_retriever: TaskRetriever,
|
|
84
|
-
) {
|
|
165
|
+
constructor(key: number, name: string, client: task.Client) {
|
|
85
166
|
this.key = key;
|
|
86
167
|
this.name = name;
|
|
87
|
-
this.
|
|
88
|
-
this.tasks = _retriever;
|
|
168
|
+
this.tasks = client;
|
|
89
169
|
}
|
|
90
170
|
|
|
91
|
-
async listTasks(): Promise<Task[]> {
|
|
92
|
-
return await this.tasks.retrieve(
|
|
171
|
+
async listTasks(): Promise<task.Task[]> {
|
|
172
|
+
return await this.tasks.retrieve(this.key);
|
|
93
173
|
}
|
|
94
174
|
|
|
95
|
-
async
|
|
96
|
-
return
|
|
175
|
+
async retrieveTaskByName(name: string): Promise<task.Task> {
|
|
176
|
+
return await this.tasks.retrieveByName(name, this.key);
|
|
97
177
|
}
|
|
98
178
|
|
|
99
|
-
async createTask
|
|
179
|
+
async createTask<
|
|
180
|
+
C extends UnknownRecord,
|
|
181
|
+
D extends {} = UnknownRecord,
|
|
182
|
+
T extends string = string,
|
|
183
|
+
>(task: task.NewTask<C, T>): Promise<task.Task<C, D, T>> {
|
|
100
184
|
task.key = (
|
|
101
185
|
(BigInt(this.key) << 32n) +
|
|
102
186
|
(BigInt(task.key ?? 0) & 0xffffffffn)
|
|
103
187
|
).toString();
|
|
104
|
-
|
|
105
|
-
return res[0];
|
|
188
|
+
return await this.tasks.create<C, D, T>(task);
|
|
106
189
|
}
|
|
107
190
|
|
|
108
191
|
async deleteTask(task: bigint): Promise<void> {
|
|
109
|
-
await this.
|
|
192
|
+
await this.tasks.delete([task]);
|
|
110
193
|
}
|
|
111
194
|
}
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
10
|
import { describe, expect, it } from "vitest";
|
|
11
|
+
import { ZodError } from "zod";
|
|
11
12
|
|
|
13
|
+
import { NotFoundError } from "@/errors";
|
|
12
14
|
import { newClient } from "@/setupspecs";
|
|
13
15
|
|
|
14
16
|
const client = newClient();
|
|
@@ -19,6 +21,10 @@ describe("Rack", () => {
|
|
|
19
21
|
const r = await client.hardware.racks.create({ name: "test" });
|
|
20
22
|
expect(r.key).toBeGreaterThan(0n);
|
|
21
23
|
});
|
|
24
|
+
it("should return an error if the rack doesn't have a name", async () => {
|
|
25
|
+
// @ts-expect-error
|
|
26
|
+
await expect(client.hardware.racks.create({})).rejects.toThrow(ZodError);
|
|
27
|
+
});
|
|
22
28
|
});
|
|
23
29
|
describe("retrieve", () => {
|
|
24
30
|
it("should retrieve a rack by its key", async () => {
|
|
@@ -27,6 +33,13 @@ describe("Rack", () => {
|
|
|
27
33
|
expect(retrieved.key).toBe(r.key);
|
|
28
34
|
expect(retrieved.name).toBe("test");
|
|
29
35
|
});
|
|
36
|
+
it("should retrieve a rack by its name", async () => {
|
|
37
|
+
const name = `TimeStamp.now().toString()}-${Math.random()}`;
|
|
38
|
+
const r = await client.hardware.racks.create({ name });
|
|
39
|
+
const retrieved = await client.hardware.racks.retrieve(name);
|
|
40
|
+
expect(retrieved.key).toBe(r.key);
|
|
41
|
+
expect(retrieved.name).toEqual(name);
|
|
42
|
+
});
|
|
30
43
|
});
|
|
31
44
|
describe("tasks", () => {
|
|
32
45
|
it("should list the tasks on a rack", async () => {
|
|
@@ -34,5 +47,11 @@ describe("Rack", () => {
|
|
|
34
47
|
const tasks = await r.listTasks();
|
|
35
48
|
expect(tasks).toHaveLength(0);
|
|
36
49
|
});
|
|
37
|
-
|
|
50
|
+
it("should throw an error if a task cannot be found by name", async () => {
|
|
51
|
+
const r = await client.hardware.racks.create({ name: "test" });
|
|
52
|
+
await expect(
|
|
53
|
+
async () => await r.retrieveTaskByName("nonexistent"),
|
|
54
|
+
).rejects.toThrow(NotFoundError);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
38
57
|
});
|