@synnaxlabs/client 0.25.0 → 0.26.1
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 +6 -6
- package/dist/access/access.spec.d.ts +2 -0
- package/dist/access/access.spec.d.ts.map +1 -0
- package/dist/access/client.d.ts +13 -0
- package/dist/access/client.d.ts.map +1 -0
- package/dist/access/external.d.ts +3 -0
- package/dist/access/external.d.ts.map +1 -0
- package/dist/access/index.d.ts +2 -0
- package/dist/access/index.d.ts.map +1 -0
- package/dist/access/payload.d.ts +105 -0
- package/dist/access/payload.d.ts.map +1 -0
- package/dist/auth/auth.d.ts.map +1 -1
- package/dist/channel/payload.d.ts +17 -14
- package/dist/channel/payload.d.ts.map +1 -1
- package/dist/channel/retriever.d.ts +7 -7
- package/dist/client.cjs +18 -18
- package/dist/client.d.ts +4 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2223 -2126
- package/dist/control/state.d.ts +1 -1
- package/dist/control/state.d.ts.map +1 -1
- package/dist/framer/adapter.d.ts +2 -0
- package/dist/framer/adapter.d.ts.map +1 -1
- package/dist/framer/client.d.ts.map +1 -1
- package/dist/framer/deleter.d.ts +1 -1
- package/dist/framer/deleter.d.ts.map +1 -1
- package/dist/framer/writer.d.ts +9 -6
- package/dist/framer/writer.d.ts.map +1 -1
- package/dist/hardware/device/client.d.ts +1 -66
- package/dist/hardware/device/client.d.ts.map +1 -1
- package/dist/hardware/device/external.d.ts +1 -0
- 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/device/payload.d.ts +73 -0
- package/dist/hardware/device/payload.d.ts.map +1 -0
- package/dist/hardware/rack/client.d.ts +2 -25
- package/dist/hardware/rack/client.d.ts.map +1 -1
- package/dist/hardware/rack/external.d.ts +1 -0
- package/dist/hardware/rack/external.d.ts.map +1 -1
- package/dist/hardware/rack/payload.d.ts +30 -0
- package/dist/hardware/rack/payload.d.ts.map +1 -0
- package/dist/hardware/task/client.d.ts +1 -145
- package/dist/hardware/task/client.d.ts.map +1 -1
- package/dist/hardware/task/external.d.ts +3 -0
- package/dist/hardware/task/external.d.ts.map +1 -0
- package/dist/hardware/task/index.d.ts +1 -1
- package/dist/hardware/task/index.d.ts.map +1 -1
- package/dist/hardware/task/payload.d.ts +151 -0
- package/dist/hardware/task/payload.d.ts.map +1 -0
- package/dist/label/payload.d.ts +1 -0
- package/dist/label/payload.d.ts.map +1 -1
- package/dist/ontology/client.d.ts +5 -5
- package/dist/ontology/payload.d.ts +40 -37
- package/dist/ontology/payload.d.ts.map +1 -1
- package/dist/ranger/payload.d.ts +5 -0
- package/dist/ranger/payload.d.ts.map +1 -1
- package/dist/setupspecs.d.ts.map +1 -1
- package/dist/signals/observable.d.ts +1 -1
- package/dist/signals/observable.d.ts.map +1 -1
- package/dist/user/client.d.ts +9 -0
- package/dist/user/client.d.ts.map +1 -0
- package/dist/user/external.d.ts +3 -0
- package/dist/user/external.d.ts.map +1 -0
- package/dist/user/index.d.ts +1 -1
- package/dist/user/index.d.ts.map +1 -1
- package/dist/user/payload.d.ts +5 -0
- package/dist/user/payload.d.ts.map +1 -1
- package/dist/workspace/lineplot/payload.d.ts +3 -0
- package/dist/workspace/lineplot/payload.d.ts.map +1 -1
- package/dist/workspace/payload.d.ts +3 -0
- package/dist/workspace/payload.d.ts.map +1 -1
- package/dist/workspace/schematic/payload.d.ts +3 -0
- package/dist/workspace/schematic/payload.d.ts.map +1 -1
- package/examples/node/basicReadWrite.js +28 -18
- package/examples/node/liveStream.js +7 -8
- package/examples/node/package-lock.json +2165 -2365
- package/examples/node/package.json +1 -1
- package/examples/node/seriesAndFrames.js +48 -47
- package/examples/node/streamWrite.js +34 -33
- package/package.json +5 -5
- package/src/access/access.spec.ts +276 -0
- package/src/access/client.ts +86 -0
- package/src/access/external.ts +11 -0
- package/src/access/index.ts +10 -0
- package/src/access/payload.ts +35 -0
- package/src/auth/auth.ts +1 -1
- package/src/channel/payload.ts +7 -0
- package/src/client.ts +7 -1
- package/src/control/state.ts +3 -3
- package/src/framer/adapter.spec.ts +9 -0
- package/src/framer/adapter.ts +12 -0
- package/src/framer/client.ts +3 -2
- package/src/framer/deleter.ts +2 -4
- package/src/framer/writer.spec.ts +99 -14
- package/src/framer/writer.ts +56 -23
- package/src/hardware/device/client.ts +8 -36
- package/src/hardware/device/external.ts +1 -0
- package/src/hardware/device/index.ts +1 -1
- package/src/hardware/device/payload.ts +44 -0
- package/src/hardware/rack/client.ts +10 -15
- package/src/hardware/rack/external.ts +1 -0
- package/src/hardware/rack/payload.ts +23 -0
- package/src/hardware/task/client.ts +1 -84
- package/src/hardware/task/external.ts +11 -0
- package/src/hardware/task/index.ts +1 -1
- package/src/hardware/task/payload.ts +92 -0
- package/src/label/payload.ts +3 -1
- package/src/ontology/payload.ts +6 -1
- package/src/ranger/payload.ts +11 -0
- package/src/setupspecs.ts +4 -2
- package/src/signals/observable.ts +5 -3
- package/src/transport.ts +3 -3
- package/src/user/client.ts +37 -0
- package/src/user/external.ts +11 -0
- package/src/user/index.ts +1 -1
- package/src/user/payload.ts +11 -0
- package/src/workspace/lineplot/payload.ts +7 -0
- package/src/workspace/payload.ts +7 -0
- package/src/workspace/schematic/payload.ts +7 -0
- package/tsconfig.json +4 -2
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Copyright 2024 Synnax Labs, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Use of this software is governed by the Business Source License included in the file
|
|
4
|
+
// licenses/BSL.txt.
|
|
5
|
+
//
|
|
6
|
+
// As of the Change Date specified in that file, in accordance with the Business Source
|
|
7
|
+
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
|
+
// included in the file licenses/APL.txt.
|
|
9
|
+
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
import { ontology } from "@/ontology";
|
|
13
|
+
import { idZ } from "@/ontology/payload";
|
|
14
|
+
|
|
15
|
+
export const keyZ = z.string().uuid();
|
|
16
|
+
|
|
17
|
+
export type Key = z.infer<typeof keyZ>;
|
|
18
|
+
|
|
19
|
+
export type Params = Key | Key[];
|
|
20
|
+
|
|
21
|
+
export const policyZ = z.object({
|
|
22
|
+
key: keyZ,
|
|
23
|
+
subjects: idZ.array(),
|
|
24
|
+
objects: idZ.array(),
|
|
25
|
+
actions: z.string().array(),
|
|
26
|
+
});
|
|
27
|
+
export type Policy = z.infer<typeof policyZ>;
|
|
28
|
+
|
|
29
|
+
export const newPolicyPayloadZ = policyZ.extend({ key: keyZ.optional() });
|
|
30
|
+
export type NewPolicyPayload = z.infer<typeof newPolicyPayloadZ>;
|
|
31
|
+
|
|
32
|
+
export const PolicyOntologyType = "policy" as ontology.ResourceType;
|
|
33
|
+
|
|
34
|
+
export const ontologyID = (key: Key): ontology.ID =>
|
|
35
|
+
new ontology.ID({ type: PolicyOntologyType, key });
|
package/src/auth/auth.ts
CHANGED
|
@@ -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 type
|
|
10
|
+
import { type Middleware, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
|
|
13
13
|
import { InvalidTokenError } from "@/errors";
|
package/src/channel/payload.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
import { DataType, Rate } from "@synnaxlabs/x/telem";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
|
|
13
|
+
import { ontology } from "@/ontology";
|
|
14
|
+
|
|
13
15
|
export const keyZ = z.number();
|
|
14
16
|
export type Key = number;
|
|
15
17
|
export type Keys = number[];
|
|
@@ -44,3 +46,8 @@ export const newPayload = payload.extend({
|
|
|
44
46
|
});
|
|
45
47
|
|
|
46
48
|
export type NewPayload = z.input<typeof newPayload>;
|
|
49
|
+
|
|
50
|
+
export const ChannelOntologyType = "channel" as ontology.ResourceType;
|
|
51
|
+
|
|
52
|
+
export const ontologyID = (key: Key): ontology.ID =>
|
|
53
|
+
new ontology.ID({ type: ChannelOntologyType, key: key.toString() });
|
package/src/client.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { TimeSpan, TimeStamp } from "@synnaxlabs/x/telem";
|
|
|
11
11
|
import { URL } from "@synnaxlabs/x/url";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
|
|
14
|
+
import { access } from "@/access";
|
|
14
15
|
import { auth } from "@/auth";
|
|
15
16
|
import { channel } from "@/channel";
|
|
16
17
|
import { connection } from "@/connection";
|
|
@@ -25,6 +26,7 @@ import { label } from "@/label";
|
|
|
25
26
|
import { ontology } from "@/ontology";
|
|
26
27
|
import { ranger } from "@/ranger";
|
|
27
28
|
import { Transport } from "@/transport";
|
|
29
|
+
import { user } from "@/user";
|
|
28
30
|
import { workspace } from "@/workspace";
|
|
29
31
|
|
|
30
32
|
export const synnaxPropsZ = z.object({
|
|
@@ -66,6 +68,8 @@ export default class Synnax extends framer.Client {
|
|
|
66
68
|
readonly ranges: ranger.Client;
|
|
67
69
|
readonly channels: channel.Client;
|
|
68
70
|
readonly auth: auth.Client | undefined;
|
|
71
|
+
readonly user: user.Client;
|
|
72
|
+
readonly access: access.Client;
|
|
69
73
|
readonly connectivity: connection.Checker;
|
|
70
74
|
readonly ontology: ontology.Client;
|
|
71
75
|
readonly workspaces: workspace.Client;
|
|
@@ -97,7 +101,7 @@ export default class Synnax extends framer.Client {
|
|
|
97
101
|
transport.use(errorsMiddleware);
|
|
98
102
|
let auth_: auth.Client | undefined;
|
|
99
103
|
if (username != null && password != null) {
|
|
100
|
-
|
|
104
|
+
auth_ = new auth.Client(transport.unary, {
|
|
101
105
|
username,
|
|
102
106
|
password,
|
|
103
107
|
});
|
|
@@ -129,6 +133,8 @@ export default class Synnax extends framer.Client {
|
|
|
129
133
|
chRetriever,
|
|
130
134
|
this.labels,
|
|
131
135
|
);
|
|
136
|
+
this.access = new access.Client(this.transport.unary);
|
|
137
|
+
this.user = new user.Client(this.transport.unary);
|
|
132
138
|
this.workspaces = new workspace.Client(this.transport.unary);
|
|
133
139
|
const devices = new device.Client(this.transport.unary, this);
|
|
134
140
|
const tasks = new task.Client(this.transport.unary, this);
|
package/src/control/state.ts
CHANGED
|
@@ -43,16 +43,16 @@ export class StateTracker
|
|
|
43
43
|
implements observe.ObservableAsyncCloseable<Transfer[]>
|
|
44
44
|
{
|
|
45
45
|
readonly states: Map<ChannelKey, State>;
|
|
46
|
-
private readonly
|
|
46
|
+
private readonly codec: binary.Codec;
|
|
47
47
|
|
|
48
48
|
constructor(streamer: FrameStreamer) {
|
|
49
49
|
super(streamer, (frame) => {
|
|
50
|
-
const update: Update = this.
|
|
50
|
+
const update: Update = this.codec.decode(frame.series[0].buffer);
|
|
51
51
|
this.merge(update);
|
|
52
52
|
return [update.transfers, true];
|
|
53
53
|
});
|
|
54
54
|
this.states = new Map();
|
|
55
|
-
this.
|
|
55
|
+
this.codec = new binary.JSONCodec();
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
subjects(): Subject[] {
|
|
@@ -156,4 +156,13 @@ describe("WriteFrameAdapter", () => {
|
|
|
156
156
|
expect(res.get(jsonChannel.key)).toHaveLength(1);
|
|
157
157
|
expect(res.get(jsonChannel.key).at(0)).toEqual({ dog: "blue" });
|
|
158
158
|
});
|
|
159
|
+
|
|
160
|
+
it("should correctly adapt generic object keys", async () => {
|
|
161
|
+
const res = await adapter.adaptObjectKeys({
|
|
162
|
+
[timeCh.name]: 532,
|
|
163
|
+
[dataCh.name]: 123,
|
|
164
|
+
});
|
|
165
|
+
expect(res).toHaveProperty(timeCh.key.toString());
|
|
166
|
+
expect(res).toHaveProperty(dataCh.key.toString());
|
|
167
|
+
});
|
|
159
168
|
});
|
package/src/framer/adapter.ts
CHANGED
|
@@ -93,6 +93,12 @@ export class WriteFrameAdapter {
|
|
|
93
93
|
return adapter;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
async adaptObjectKeys<V>(data: Record<KeyOrName, V>): Promise<Record<Key, V>> {
|
|
97
|
+
const out: Record<Key, V> = {};
|
|
98
|
+
for (const [k, v] of Object.entries(data)) out[await this.adaptToKey(k)] = v;
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
|
|
96
102
|
async update(channels: Params): Promise<void> {
|
|
97
103
|
const results = await retrieveRequired(this.retriever, channels);
|
|
98
104
|
this.adapter = new Map<Name, Key>(results.map((c) => [c.name, c.key]));
|
|
@@ -105,6 +111,12 @@ export class WriteFrameAdapter {
|
|
|
105
111
|
return res[0];
|
|
106
112
|
}
|
|
107
113
|
|
|
114
|
+
private async adaptToKey(k: KeyOrName): Promise<Key> {
|
|
115
|
+
if (typeof k === "number") return k;
|
|
116
|
+
const res = await this.fetchChannel(k);
|
|
117
|
+
return res.key;
|
|
118
|
+
}
|
|
119
|
+
|
|
108
120
|
async adapt(
|
|
109
121
|
columnsOrData: Params | Record<KeyOrName, CrudeSeries> | CrudeFrame,
|
|
110
122
|
series?: CrudeSeries | CrudeSeries[],
|
package/src/framer/client.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
type MultiSeries,
|
|
16
16
|
TimeRange,
|
|
17
17
|
TimeSpan,
|
|
18
|
+
toArray,
|
|
18
19
|
} from "@synnaxlabs/x";
|
|
19
20
|
|
|
20
21
|
import { type Key, type KeyOrName, KeysOrNames, type Params } from "@/channel/payload";
|
|
@@ -133,7 +134,7 @@ export class Client {
|
|
|
133
134
|
const w = await this.openWriter({
|
|
134
135
|
start,
|
|
135
136
|
channels: Object.keys(data_),
|
|
136
|
-
mode: WriterMode.
|
|
137
|
+
mode: WriterMode.Persist,
|
|
137
138
|
});
|
|
138
139
|
try {
|
|
139
140
|
await w.write(data_);
|
|
@@ -146,7 +147,7 @@ export class Client {
|
|
|
146
147
|
const w = await this.openWriter({
|
|
147
148
|
start,
|
|
148
149
|
channels: channels as Params,
|
|
149
|
-
mode: WriterMode.
|
|
150
|
+
mode: WriterMode.Persist,
|
|
150
151
|
errOnUnauthorized: true,
|
|
151
152
|
enableAutoCommit: true,
|
|
152
153
|
autoIndexPersistInterval: TimeSpan.MAX,
|
package/src/framer/deleter.ts
CHANGED
|
@@ -15,8 +15,8 @@ import { keyZ } from "@/channel/payload";
|
|
|
15
15
|
|
|
16
16
|
const reqZ = z.object({
|
|
17
17
|
keys: keyZ.array().optional(),
|
|
18
|
-
names: z.string().array().optional(),
|
|
19
18
|
bounds: TimeRange.z,
|
|
19
|
+
names: z.string().array().optional(),
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
type RequestProps = z.infer<typeof reqZ>;
|
|
@@ -31,9 +31,7 @@ export class Deleter {
|
|
|
31
31
|
*/
|
|
32
32
|
private readonly client: UnaryClient;
|
|
33
33
|
|
|
34
|
-
constructor(
|
|
35
|
-
client: UnaryClient,
|
|
36
|
-
) {
|
|
34
|
+
constructor(client: UnaryClient) {
|
|
37
35
|
this.client = client;
|
|
38
36
|
}
|
|
39
37
|
|
|
@@ -7,6 +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 { id } from "@synnaxlabs/x";
|
|
10
11
|
import { DataType, Rate, TimeRange, TimeSpan, TimeStamp } from "@synnaxlabs/x/telem";
|
|
11
12
|
import { describe, expect, test } from "vitest";
|
|
12
13
|
|
|
@@ -20,8 +21,8 @@ const client = newClient();
|
|
|
20
21
|
|
|
21
22
|
const newChannel = async (): Promise<channel.Channel> =>
|
|
22
23
|
await client.channels.create({
|
|
23
|
-
name: "test",
|
|
24
24
|
leaseholder: 1,
|
|
25
|
+
name: `test-${id.id()}`,
|
|
25
26
|
rate: Rate.hz(1),
|
|
26
27
|
dataType: DataType.FLOAT64,
|
|
27
28
|
});
|
|
@@ -53,7 +54,7 @@ describe("Writer", () => {
|
|
|
53
54
|
const writer = await client.openWriter({
|
|
54
55
|
start: 0,
|
|
55
56
|
channels: ch.key,
|
|
56
|
-
mode: WriterMode.
|
|
57
|
+
mode: WriterMode.Persist,
|
|
57
58
|
});
|
|
58
59
|
try {
|
|
59
60
|
await writer.write(ch.key, randomSeries(10, ch.dataType));
|
|
@@ -137,27 +138,111 @@ describe("Writer", () => {
|
|
|
137
138
|
channels: ch.key,
|
|
138
139
|
authorities: 10,
|
|
139
140
|
enableAutoCommit: true,
|
|
140
|
-
})
|
|
141
|
+
});
|
|
141
142
|
const w2 = await client.openWriter({
|
|
142
143
|
start: 0,
|
|
143
144
|
channels: ch.key,
|
|
144
145
|
authorities: 20,
|
|
145
146
|
enableAutoCommit: true,
|
|
146
|
-
})
|
|
147
|
+
});
|
|
147
148
|
|
|
148
|
-
await w1.write(ch.key, randomSeries(10, ch.dataType))
|
|
149
|
-
let f = await ch.read(TimeRange.MAX)
|
|
150
|
-
expect(f.length).toEqual(0)
|
|
149
|
+
await w1.write(ch.key, randomSeries(10, ch.dataType));
|
|
150
|
+
let f = await ch.read(TimeRange.MAX);
|
|
151
|
+
expect(f.length).toEqual(0);
|
|
151
152
|
|
|
152
|
-
await w1.setAuthority({[ch.key]: 100});
|
|
153
|
-
await w1.write(ch.key, randomSeries(10, ch.dataType))
|
|
153
|
+
await w1.setAuthority({ [ch.key]: 100 });
|
|
154
|
+
await w1.write(ch.key, randomSeries(10, ch.dataType));
|
|
154
155
|
|
|
155
|
-
f = await ch.read(TimeRange.MAX)
|
|
156
|
-
expect(f.length).toEqual(10)
|
|
156
|
+
f = await ch.read(TimeRange.MAX);
|
|
157
|
+
expect(f.length).toEqual(10);
|
|
157
158
|
|
|
158
|
-
await w1.close()
|
|
159
|
-
await w2.close()
|
|
160
|
-
})
|
|
159
|
+
await w1.close();
|
|
160
|
+
await w2.close();
|
|
161
|
+
});
|
|
162
|
+
test("setAuthority with name keys", async () => {
|
|
163
|
+
const ch = await newChannel();
|
|
164
|
+
const w1 = await client.openWriter({
|
|
165
|
+
start: 0,
|
|
166
|
+
channels: ch.key,
|
|
167
|
+
authorities: 10,
|
|
168
|
+
enableAutoCommit: true,
|
|
169
|
+
});
|
|
170
|
+
const w2 = await client.openWriter({
|
|
171
|
+
start: 0,
|
|
172
|
+
channels: ch.key,
|
|
173
|
+
authorities: 20,
|
|
174
|
+
enableAutoCommit: true,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
await w1.write(ch.key, randomSeries(10, ch.dataType));
|
|
178
|
+
let f = await ch.read(TimeRange.MAX);
|
|
179
|
+
expect(f.length).toEqual(0);
|
|
180
|
+
|
|
181
|
+
await w1.setAuthority({ [ch.name]: 100 });
|
|
182
|
+
await w1.write(ch.key, randomSeries(10, ch.dataType));
|
|
183
|
+
|
|
184
|
+
f = await ch.read(TimeRange.MAX);
|
|
185
|
+
expect(f.length).toEqual(10);
|
|
186
|
+
|
|
187
|
+
await w1.close();
|
|
188
|
+
await w2.close();
|
|
189
|
+
});
|
|
190
|
+
test("setAuthority with name-value pair", async () => {
|
|
191
|
+
const ch = await newChannel();
|
|
192
|
+
const w1 = await client.openWriter({
|
|
193
|
+
start: 0,
|
|
194
|
+
channels: ch.key,
|
|
195
|
+
authorities: 10,
|
|
196
|
+
enableAutoCommit: true,
|
|
197
|
+
});
|
|
198
|
+
const w2 = await client.openWriter({
|
|
199
|
+
start: 0,
|
|
200
|
+
channels: ch.key,
|
|
201
|
+
authorities: 20,
|
|
202
|
+
enableAutoCommit: true,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
await w1.write(ch.key, randomSeries(10, ch.dataType));
|
|
206
|
+
let f = await ch.read(TimeRange.MAX);
|
|
207
|
+
expect(f.length).toEqual(0);
|
|
208
|
+
|
|
209
|
+
await w1.setAuthority(ch.name, 100);
|
|
210
|
+
await w1.write(ch.key, randomSeries(10, ch.dataType));
|
|
211
|
+
|
|
212
|
+
f = await ch.read(TimeRange.MAX);
|
|
213
|
+
expect(f.length).toEqual(10);
|
|
214
|
+
|
|
215
|
+
await w1.close();
|
|
216
|
+
await w2.close();
|
|
217
|
+
});
|
|
218
|
+
test("setAuthority on all channels", async () => {
|
|
219
|
+
const ch = await newChannel();
|
|
220
|
+
const w1 = await client.openWriter({
|
|
221
|
+
start: 0,
|
|
222
|
+
channels: ch.key,
|
|
223
|
+
authorities: 10,
|
|
224
|
+
enableAutoCommit: true,
|
|
225
|
+
});
|
|
226
|
+
const w2 = await client.openWriter({
|
|
227
|
+
start: 0,
|
|
228
|
+
channels: ch.key,
|
|
229
|
+
authorities: 20,
|
|
230
|
+
enableAutoCommit: true,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
await w1.write(ch.key, randomSeries(10, ch.dataType));
|
|
234
|
+
let f = await ch.read(TimeRange.MAX);
|
|
235
|
+
expect(f.length).toEqual(0);
|
|
236
|
+
|
|
237
|
+
await w1.setAuthority(ch.name, 255);
|
|
238
|
+
await w1.write(ch.key, randomSeries(10, ch.dataType));
|
|
239
|
+
|
|
240
|
+
f = await ch.read(TimeRange.MAX);
|
|
241
|
+
expect(f.length).toEqual(10);
|
|
242
|
+
|
|
243
|
+
await w1.close();
|
|
244
|
+
await w2.close();
|
|
245
|
+
});
|
|
161
246
|
});
|
|
162
247
|
describe("Client", () => {
|
|
163
248
|
test("Client - basic write", async () => {
|
package/src/framer/writer.ts
CHANGED
|
@@ -7,8 +7,12 @@
|
|
|
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
|
|
11
|
-
|
|
10
|
+
import {
|
|
11
|
+
decodeError,
|
|
12
|
+
errorZ,
|
|
13
|
+
type Stream,
|
|
14
|
+
type StreamClient,
|
|
15
|
+
} from "@synnaxlabs/freighter";
|
|
12
16
|
import { control } from "@synnaxlabs/x";
|
|
13
17
|
import {
|
|
14
18
|
type CrudeSeries,
|
|
@@ -19,12 +23,7 @@ import {
|
|
|
19
23
|
import { toArray } from "@synnaxlabs/x/toArray";
|
|
20
24
|
import { z } from "zod";
|
|
21
25
|
|
|
22
|
-
import {
|
|
23
|
-
type Key,
|
|
24
|
-
type KeyOrName,
|
|
25
|
-
type KeysOrNames,
|
|
26
|
-
type Params,
|
|
27
|
-
} from "@/channel/payload";
|
|
26
|
+
import { type KeyOrName, type KeysOrNames, type Params } from "@/channel/payload";
|
|
28
27
|
import { type Retriever } from "@/channel/retriever";
|
|
29
28
|
import { WriteFrameAdapter } from "@/framer/adapter";
|
|
30
29
|
import { type CrudeFrame, frameZ } from "@/framer/frame";
|
|
@@ -40,10 +39,25 @@ enum Command {
|
|
|
40
39
|
|
|
41
40
|
export enum WriterMode {
|
|
42
41
|
PersistStream = 1,
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
Persist = 2,
|
|
43
|
+
Stream = 3,
|
|
45
44
|
}
|
|
46
45
|
|
|
46
|
+
export type CrudeWriterMode = "persist" | "stream" | "persistStream" | WriterMode;
|
|
47
|
+
|
|
48
|
+
const constructWriterMode = (mode: CrudeWriterMode): WriterMode => {
|
|
49
|
+
switch (mode) {
|
|
50
|
+
case "persist":
|
|
51
|
+
return WriterMode.Persist;
|
|
52
|
+
case "stream":
|
|
53
|
+
return WriterMode.Stream;
|
|
54
|
+
case "persistStream":
|
|
55
|
+
return WriterMode.PersistStream;
|
|
56
|
+
default:
|
|
57
|
+
return mode;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
47
61
|
export const ALWAYS_INDEX_PERSIST_ON_AUTO_COMMIT: TimeSpan = new TimeSpan(-1);
|
|
48
62
|
|
|
49
63
|
const netConfigZ = z.object({
|
|
@@ -57,6 +71,8 @@ const netConfigZ = z.object({
|
|
|
57
71
|
autoIndexPersistInterval: TimeSpan.z.optional(),
|
|
58
72
|
});
|
|
59
73
|
|
|
74
|
+
type Config = z.infer<typeof netConfigZ>;
|
|
75
|
+
|
|
60
76
|
const reqZ = z.object({
|
|
61
77
|
command: z.nativeEnum(Command),
|
|
62
78
|
config: netConfigZ.optional(),
|
|
@@ -86,10 +102,10 @@ export interface WriterConfig {
|
|
|
86
102
|
authorities?: control.Authority | control.Authority[];
|
|
87
103
|
// mode sets the persistence and streaming mode of the writer. The default
|
|
88
104
|
// mode is WriterModePersistStream.
|
|
89
|
-
mode?:
|
|
105
|
+
mode?: CrudeWriterMode;
|
|
90
106
|
// errOnUnauthorized sets whether the writer raises an error when it attempts to write
|
|
91
107
|
// to a channel without permission.
|
|
92
|
-
errOnUnauthorized?: boolean
|
|
108
|
+
errOnUnauthorized?: boolean;
|
|
93
109
|
// enableAutoCommit determines whether the writer will automatically commit.
|
|
94
110
|
// If enableAutoCommit is true, then the writer will commit after each write, and
|
|
95
111
|
// will flush that commit to index after the specified autoIndexPersistInterval.
|
|
@@ -175,7 +191,7 @@ export class Writer {
|
|
|
175
191
|
keys: adapter.keys,
|
|
176
192
|
controlSubject: subject,
|
|
177
193
|
authorities: toArray(authorities),
|
|
178
|
-
mode,
|
|
194
|
+
mode: constructWriterMode(mode),
|
|
179
195
|
errOnUnauthorized,
|
|
180
196
|
enableAutoCommit,
|
|
181
197
|
autoIndexPersistInterval,
|
|
@@ -219,20 +235,37 @@ export class Writer {
|
|
|
219
235
|
return true;
|
|
220
236
|
}
|
|
221
237
|
|
|
222
|
-
async setAuthority(value:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
238
|
+
async setAuthority(value: number): Promise<boolean>;
|
|
239
|
+
|
|
240
|
+
async setAuthority(key: KeyOrName, authority: control.Authority): Promise<boolean>;
|
|
241
|
+
|
|
242
|
+
async setAuthority(value: Record<KeyOrName, control.Authority>): Promise<boolean>;
|
|
243
|
+
|
|
244
|
+
async setAuthority(
|
|
245
|
+
value: Record<KeyOrName, control.Authority> | KeyOrName | number,
|
|
246
|
+
authority?: control.Authority,
|
|
247
|
+
): Promise<boolean> {
|
|
248
|
+
let config: Config = { keys: [], authorities: [] };
|
|
249
|
+
if (typeof value === "number" && authority == null)
|
|
250
|
+
config = { keys: [], authorities: [value] };
|
|
251
|
+
else {
|
|
252
|
+
let oValue: Record<KeyOrName, control.Authority>;
|
|
253
|
+
if (typeof value === "string" || typeof value === "number")
|
|
254
|
+
oValue = { [value]: authority } as Record<KeyOrName, control.Authority>;
|
|
255
|
+
else oValue = value;
|
|
256
|
+
oValue = await this.adapter.adaptObjectKeys(oValue);
|
|
257
|
+
config = {
|
|
258
|
+
keys: Object.keys(oValue).map((k) => Number(k)),
|
|
259
|
+
authorities: Object.values(oValue),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
const response = await this.execute({ command: Command.SetAuthority, config });
|
|
263
|
+
return response.ack;
|
|
231
264
|
}
|
|
232
265
|
|
|
233
266
|
/**
|
|
234
267
|
* Commits the written frames to the database. Commit is synchronous, meaning that it
|
|
235
|
-
* will not return until all frames have been
|
|
268
|
+
* will not return until all frames have been committed to the database.
|
|
236
269
|
*
|
|
237
270
|
* @returns false if the commit failed due to an error. In this case, the caller
|
|
238
271
|
* should acknowledge the error by calling the error method or closing the writer.
|
|
@@ -9,12 +9,18 @@
|
|
|
9
9
|
|
|
10
10
|
import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
11
|
import { toArray, type UnknownRecord } from "@synnaxlabs/x";
|
|
12
|
-
import { binary } from "@synnaxlabs/x/binary";
|
|
13
12
|
import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
|
|
14
13
|
import { z } from "zod";
|
|
15
14
|
|
|
16
15
|
import { type framer } from "@/framer";
|
|
17
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
Device,
|
|
18
|
+
DeviceKey,
|
|
19
|
+
deviceKeyZ,
|
|
20
|
+
deviceZ,
|
|
21
|
+
NewDevice,
|
|
22
|
+
newDeviceZ,
|
|
23
|
+
} from "@/hardware/device/payload";
|
|
18
24
|
import { signals } from "@/signals";
|
|
19
25
|
import { checkForMultipleOrNoResults } from "@/util/retrieve";
|
|
20
26
|
import { nullableArrayZ } from "@/util/zod";
|
|
@@ -26,40 +32,6 @@ const RETRIEVE_ENDPOINT = "/hardware/device/retrieve";
|
|
|
26
32
|
const CREATE_ENDPOINT = "/hardware/device/create";
|
|
27
33
|
const DELETE_ENDPOINT = "/hardware/device/delete";
|
|
28
34
|
|
|
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
35
|
const createReqZ = z.object({ devices: newDeviceZ.array() });
|
|
64
36
|
|
|
65
37
|
const createResZ = z.object({ devices: deviceZ.array() });
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { binary, UnknownRecord } from "@synnaxlabs/x";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import { rackKeyZ } from "@/hardware/rack/payload";
|
|
5
|
+
import { ontology } from "@/ontology";
|
|
6
|
+
|
|
7
|
+
export const deviceKeyZ = z.string();
|
|
8
|
+
|
|
9
|
+
export const deviceZ = z.object({
|
|
10
|
+
key: deviceKeyZ,
|
|
11
|
+
rack: rackKeyZ,
|
|
12
|
+
name: z.string(),
|
|
13
|
+
make: z.string(),
|
|
14
|
+
model: z.string(),
|
|
15
|
+
location: z.string(),
|
|
16
|
+
configured: z.boolean().optional(),
|
|
17
|
+
properties: z.record(z.unknown()).or(
|
|
18
|
+
z.string().transform((c) => {
|
|
19
|
+
if (c === "") return {};
|
|
20
|
+
return binary.JSON_CODEC.decodeString(c);
|
|
21
|
+
}),
|
|
22
|
+
) as z.ZodType<UnknownRecord>,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export type Device<P extends UnknownRecord = UnknownRecord> = Omit<
|
|
26
|
+
z.output<typeof deviceZ>,
|
|
27
|
+
"properties"
|
|
28
|
+
> & { properties: P };
|
|
29
|
+
|
|
30
|
+
export type DeviceKey = z.infer<typeof deviceKeyZ>;
|
|
31
|
+
|
|
32
|
+
export const newDeviceZ = deviceZ.extend({
|
|
33
|
+
properties: z.unknown().transform((c) => binary.JSON_CODEC.encodeString(c)),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export type NewDevice<P extends UnknownRecord = UnknownRecord> = Omit<
|
|
37
|
+
z.input<typeof newDeviceZ>,
|
|
38
|
+
"properties"
|
|
39
|
+
> & { properties: P };
|
|
40
|
+
|
|
41
|
+
export const DeviceOntologyType = "device" as ontology.ResourceType;
|
|
42
|
+
|
|
43
|
+
export const ontologyID = (key: DeviceKey): ontology.ID =>
|
|
44
|
+
new ontology.ID({ type: DeviceOntologyType, key: key.toString() });
|
|
@@ -14,25 +14,18 @@ import { toArray } from "@synnaxlabs/x/toArray";
|
|
|
14
14
|
import { z } from "zod";
|
|
15
15
|
|
|
16
16
|
import { type framer } from "@/framer";
|
|
17
|
+
import {
|
|
18
|
+
NewRack,
|
|
19
|
+
newRackZ,
|
|
20
|
+
RackKey,
|
|
21
|
+
rackKeyZ,
|
|
22
|
+
RackPayload,
|
|
23
|
+
rackZ,
|
|
24
|
+
} from "@/hardware/rack/payload";
|
|
17
25
|
import { type task } from "@/hardware/task";
|
|
18
26
|
import { analyzeParams, checkForMultipleOrNoResults } from "@/util/retrieve";
|
|
19
27
|
import { nullableArrayZ } from "@/util/zod";
|
|
20
28
|
|
|
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
29
|
const RETRIEVE_ENDPOINT = "/hardware/rack/retrieve";
|
|
37
30
|
const CREATE_RACK_ENDPOINT = "/hardware/rack/create";
|
|
38
31
|
const DELETE_RACK_ENDPOINT = "/hardware/rack/delete";
|
|
@@ -192,3 +185,5 @@ export class Rack {
|
|
|
192
185
|
await this.tasks.delete([task]);
|
|
193
186
|
}
|
|
194
187
|
}
|
|
188
|
+
export { rackKeyZ };
|
|
189
|
+
|