@synnaxlabs/client 0.20.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/README.md +13 -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 +19 -22
- 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 +6781 -6675
- 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 +12 -12
- 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 +67 -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/examples/node/package-lock.json +29 -12
- package/examples/node/package.json +2 -2
- package/examples/node/streamWrite.js +8 -11
- package/package.json +10 -9
- 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 +13 -12
- 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 +49 -2
- package/src/framer/writer.ts +27 -2
- 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 -60
- 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 -139
- package/src/ranger/retriever.ts +0 -50
- /package/dist/workspace/{pid → schematic}/external.d.ts +0 -0
|
@@ -7,11 +7,11 @@
|
|
|
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, 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";
|
|
14
|
-
import { WriterMode } from "@/framer/writer";
|
|
14
|
+
import { ALWAYS_INDEX_PERSIST_ON_AUTO_COMMIT, WriterMode } from "@/framer/writer";
|
|
15
15
|
import { newClient } from "@/setupspecs";
|
|
16
16
|
import { randomSeries } from "@/util/telem";
|
|
17
17
|
|
|
@@ -66,6 +66,53 @@ describe("Writer", () => {
|
|
|
66
66
|
]);
|
|
67
67
|
expect(v).toEqual(123);
|
|
68
68
|
});
|
|
69
|
+
test("write with auto commit on", async () => {
|
|
70
|
+
const ch = await newChannel();
|
|
71
|
+
const writer = await client.openWriter({
|
|
72
|
+
start: 0,
|
|
73
|
+
channels: ch.key,
|
|
74
|
+
enableAutoCommit: true,
|
|
75
|
+
});
|
|
76
|
+
try {
|
|
77
|
+
await writer.write(ch.key, randomSeries(10, ch.dataType));
|
|
78
|
+
} finally {
|
|
79
|
+
await writer.close();
|
|
80
|
+
}
|
|
81
|
+
expect(true).toBeTruthy();
|
|
82
|
+
|
|
83
|
+
const f = await client.read(new TimeRange(0, TimeStamp.seconds(10)), ch.key);
|
|
84
|
+
expect(f.length).toEqual(10);
|
|
85
|
+
});
|
|
86
|
+
test("write with auto commit and alwaysPersist", async () => {
|
|
87
|
+
const ch = await newChannel();
|
|
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
|
+
});
|
|
94
|
+
try {
|
|
95
|
+
await writer.write(ch.key, randomSeries(10, ch.dataType));
|
|
96
|
+
} finally {
|
|
97
|
+
await writer.close();
|
|
98
|
+
}
|
|
99
|
+
expect(true).toBeTruthy();
|
|
100
|
+
});
|
|
101
|
+
test("write with auto commit and a set interval", async () => {
|
|
102
|
+
const ch = await newChannel();
|
|
103
|
+
const writer = await client.openWriter({
|
|
104
|
+
start: 0,
|
|
105
|
+
channels: ch.key,
|
|
106
|
+
enableAutoCommit: true,
|
|
107
|
+
autoIndexPersistInterval: TimeSpan.milliseconds(100),
|
|
108
|
+
});
|
|
109
|
+
try {
|
|
110
|
+
await writer.write(ch.key, randomSeries(10, ch.dataType));
|
|
111
|
+
} finally {
|
|
112
|
+
await writer.close();
|
|
113
|
+
}
|
|
114
|
+
expect(true).toBeTruthy();
|
|
115
|
+
});
|
|
69
116
|
});
|
|
70
117
|
describe("Client", () => {
|
|
71
118
|
test("Client - basic write", async () => {
|
package/src/framer/writer.ts
CHANGED
|
@@ -13,9 +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,
|
|
18
|
+
} from "@synnaxlabs/x/telem";
|
|
19
|
+
import { toArray } from "@synnaxlabs/x/toArray";
|
|
19
20
|
import { z } from "zod";
|
|
20
21
|
|
|
21
22
|
import {
|
|
@@ -49,12 +50,16 @@ export enum WriterMode {
|
|
|
49
50
|
StreamOnly = 3,
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
export const ALWAYS_INDEX_PERSIST_ON_AUTO_COMMIT: TimeSpan = new TimeSpan(-1);
|
|
54
|
+
|
|
52
55
|
const netConfigZ = z.object({
|
|
53
56
|
start: TimeStamp.z.optional(),
|
|
54
57
|
controlSubject: controlSubjectZ.optional(),
|
|
55
58
|
keys: z.number().array().optional(),
|
|
56
59
|
authorities: Authority.z.array().optional(),
|
|
57
60
|
mode: z.nativeEnum(WriterMode).optional(),
|
|
61
|
+
enableAutoCommit: z.boolean().optional(),
|
|
62
|
+
autoIndexPersistInterval: TimeSpan.z.optional(),
|
|
58
63
|
});
|
|
59
64
|
|
|
60
65
|
const reqZ = z.object({
|
|
@@ -74,11 +79,27 @@ const resZ = z.object({
|
|
|
74
79
|
type Response = z.infer<typeof resZ>;
|
|
75
80
|
|
|
76
81
|
export interface WriterConfig {
|
|
82
|
+
// channels denote the channels to write to.
|
|
77
83
|
channels: Params;
|
|
84
|
+
// start sets the starting timestamp for the first sample in the writer.
|
|
78
85
|
start?: CrudeTimeStamp;
|
|
86
|
+
// controlSubject sets the control subject of the writer.
|
|
79
87
|
controlSubject?: ControlSubject;
|
|
88
|
+
// authorities set the control authority to set for each channel on the writer.
|
|
89
|
+
// Defaults to absolute authority. If not working with concurrent control,
|
|
90
|
+
// it's best to leave this as the default.
|
|
80
91
|
authorities?: Authority | Authority[];
|
|
92
|
+
// mode sets the persistence and streaming mode of the writer. The default
|
|
93
|
+
// mode is WriterModePersistStream.
|
|
81
94
|
mode?: WriterMode;
|
|
95
|
+
// enableAutoCommit determines whether the writer will automatically commit.
|
|
96
|
+
// If enableAutoCommit is true, then the writer will commit after each write, and
|
|
97
|
+
// will flush that commit to index after the specified autoIndexPersistInterval.
|
|
98
|
+
enableAutoCommit?: boolean;
|
|
99
|
+
// autoIndexPersistInterval sets the interval at which commits to the index will be
|
|
100
|
+
// persisted. To persist every commit to guarantee minimal loss of data, set
|
|
101
|
+
// auto_index_persist_interval to AlwaysAutoIndexPersist.
|
|
102
|
+
autoIndexPersistInterval?: TimeSpan;
|
|
82
103
|
}
|
|
83
104
|
|
|
84
105
|
/**
|
|
@@ -141,6 +162,8 @@ export class Writer {
|
|
|
141
162
|
authorities = Authority.Absolute,
|
|
142
163
|
controlSubject: subject,
|
|
143
164
|
mode = WriterMode.PersistStream,
|
|
165
|
+
enableAutoCommit = false,
|
|
166
|
+
autoIndexPersistInterval = TimeSpan.SECOND,
|
|
144
167
|
}: WriterConfig,
|
|
145
168
|
): Promise<Writer> {
|
|
146
169
|
const adapter = await WriteFrameAdapter.open(retriever, channels);
|
|
@@ -154,6 +177,8 @@ export class Writer {
|
|
|
154
177
|
controlSubject: subject,
|
|
155
178
|
authorities: toArray(authorities),
|
|
156
179
|
mode,
|
|
180
|
+
enableAutoCommit,
|
|
181
|
+
autoIndexPersistInterval,
|
|
157
182
|
},
|
|
158
183
|
});
|
|
159
184
|
return writer;
|
|
@@ -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
|
}
|