@synnaxlabs/client 0.43.1 → 0.44.2
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/dist/access/payload.d.ts +1 -1
- package/dist/access/payload.d.ts.map +1 -1
- package/dist/access/policy/client.d.ts +263 -6
- package/dist/access/policy/client.d.ts.map +1 -1
- package/dist/access/policy/external.d.ts +0 -1
- package/dist/access/policy/external.d.ts.map +1 -1
- package/dist/access/policy/payload.d.ts +105 -93
- package/dist/access/policy/payload.d.ts.map +1 -1
- package/dist/auth/auth.d.ts +1 -1
- package/dist/auth/auth.d.ts.map +1 -1
- package/dist/channel/client.d.ts +23 -17
- package/dist/channel/client.d.ts.map +1 -1
- package/dist/channel/payload.d.ts +151 -21
- package/dist/channel/payload.d.ts.map +1 -1
- package/dist/channel/retriever.d.ts +9 -16
- package/dist/channel/retriever.d.ts.map +1 -1
- package/dist/channel/writer.d.ts +1 -1
- package/dist/channel/writer.d.ts.map +1 -1
- package/dist/client.cjs +27 -135
- package/dist/client.d.ts +3 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +8657 -28963
- package/dist/connection/checker.d.ts +1 -1
- package/dist/connection/checker.d.ts.map +1 -1
- package/dist/control/client.d.ts +1 -0
- package/dist/control/client.d.ts.map +1 -1
- package/dist/control/state.d.ts +6 -6
- package/dist/control/state.d.ts.map +1 -1
- package/dist/errors.d.ts +18 -5
- package/dist/errors.d.ts.map +1 -1
- package/dist/framer/adapter.d.ts +3 -3
- package/dist/framer/adapter.d.ts.map +1 -1
- package/dist/framer/client.d.ts +4 -13
- package/dist/framer/client.d.ts.map +1 -1
- package/dist/framer/codec.d.ts +1 -1
- package/dist/framer/codec.d.ts.map +1 -1
- package/dist/framer/deleter.d.ts +5 -5
- package/dist/framer/deleter.d.ts.map +1 -1
- package/dist/framer/frame.d.ts +5 -7
- package/dist/framer/frame.d.ts.map +1 -1
- package/dist/framer/streamProxy.d.ts +1 -1
- package/dist/framer/streamProxy.d.ts.map +1 -1
- package/dist/framer/streamer.d.ts +235 -20
- package/dist/framer/streamer.d.ts.map +1 -1
- package/dist/framer/writer.d.ts +302 -33
- package/dist/framer/writer.d.ts.map +1 -1
- package/dist/hardware/device/client.d.ts +49 -28
- package/dist/hardware/device/client.d.ts.map +1 -1
- package/dist/hardware/device/payload.d.ts +126 -46
- package/dist/hardware/device/payload.d.ts.map +1 -1
- package/dist/hardware/rack/client.d.ts +78 -22
- package/dist/hardware/rack/client.d.ts.map +1 -1
- package/dist/hardware/rack/payload.d.ts +99 -56
- package/dist/hardware/rack/payload.d.ts.map +1 -1
- package/dist/hardware/task/client.d.ts +100 -41
- package/dist/hardware/task/client.d.ts.map +1 -1
- package/dist/hardware/task/payload.d.ts +83 -61
- package/dist/hardware/task/payload.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/label/client.d.ts +138 -20
- package/dist/label/client.d.ts.map +1 -1
- package/dist/label/external.d.ts +0 -2
- package/dist/label/external.d.ts.map +1 -1
- package/dist/label/payload.d.ts +4 -5
- package/dist/label/payload.d.ts.map +1 -1
- package/dist/ontology/client.d.ts +45 -135
- package/dist/ontology/client.d.ts.map +1 -1
- package/dist/ontology/group/group.d.ts +3 -3
- package/dist/ontology/group/group.d.ts.map +1 -1
- package/dist/ontology/group/payload.d.ts +3 -27
- package/dist/ontology/group/payload.d.ts.map +1 -1
- package/dist/ontology/payload.d.ts +114 -243
- package/dist/ontology/payload.d.ts.map +1 -1
- package/dist/ontology/writer.d.ts +4 -4
- package/dist/ontology/writer.d.ts.map +1 -1
- package/dist/ranger/alias.d.ts +15 -5
- package/dist/ranger/alias.d.ts.map +1 -1
- package/dist/ranger/client.d.ts +91 -30
- package/dist/ranger/client.d.ts.map +1 -1
- package/dist/ranger/external.d.ts +1 -1
- package/dist/ranger/external.d.ts.map +1 -1
- package/dist/ranger/kv.d.ts +11 -12
- package/dist/ranger/kv.d.ts.map +1 -1
- package/dist/ranger/payload.d.ts +19 -44
- package/dist/ranger/payload.d.ts.map +1 -1
- package/dist/ranger/writer.d.ts +22 -19
- package/dist/ranger/writer.d.ts.map +1 -1
- package/dist/testutil/client.d.ts +4 -0
- package/dist/testutil/client.d.ts.map +1 -0
- package/dist/user/client.d.ts +59 -6
- package/dist/user/client.d.ts.map +1 -1
- package/dist/user/payload.d.ts +4 -6
- package/dist/user/payload.d.ts.map +1 -1
- package/dist/user/retriever.d.ts +2 -2
- package/dist/user/retriever.d.ts.map +1 -1
- package/dist/util/decodeJSONString.d.ts +2 -2
- package/dist/util/decodeJSONString.d.ts.map +1 -1
- package/dist/util/parseWithoutKeyConversion.d.ts +2 -2
- package/dist/util/parseWithoutKeyConversion.d.ts.map +1 -1
- package/dist/util/retrieve.d.ts +1 -1
- package/dist/util/retrieve.d.ts.map +1 -1
- package/dist/util/zod.d.ts +1 -1
- package/dist/util/zod.d.ts.map +1 -1
- package/dist/workspace/client.d.ts +17 -6
- package/dist/workspace/client.d.ts.map +1 -1
- package/dist/workspace/lineplot/client.d.ts +2 -2
- package/dist/workspace/lineplot/client.d.ts.map +1 -1
- package/dist/workspace/lineplot/payload.d.ts +8 -9
- package/dist/workspace/lineplot/payload.d.ts.map +1 -1
- package/dist/workspace/log/client.d.ts +2 -2
- package/dist/workspace/log/client.d.ts.map +1 -1
- package/dist/workspace/log/payload.d.ts +8 -9
- package/dist/workspace/log/payload.d.ts.map +1 -1
- package/dist/workspace/payload.d.ts +10 -11
- package/dist/workspace/payload.d.ts.map +1 -1
- package/dist/workspace/schematic/client.d.ts +2 -2
- package/dist/workspace/schematic/client.d.ts.map +1 -1
- package/dist/workspace/schematic/payload.d.ts +10 -11
- package/dist/workspace/schematic/payload.d.ts.map +1 -1
- package/dist/workspace/table/client.d.ts +2 -2
- package/dist/workspace/table/client.d.ts.map +1 -1
- package/dist/workspace/table/payload.d.ts +10 -11
- package/dist/workspace/table/payload.d.ts.map +1 -1
- package/examples/node/package-lock.json +47 -39
- package/examples/node/package.json +2 -1
- package/examples/node/streamWrite.js +5 -11
- package/package.json +14 -13
- package/src/access/payload.ts +1 -1
- package/src/access/policy/client.ts +87 -32
- package/src/access/policy/external.ts +0 -1
- package/src/access/policy/payload.ts +4 -4
- package/src/access/policy/policy.spec.ts +86 -83
- package/src/auth/auth.spec.ts +29 -18
- package/src/auth/auth.ts +1 -1
- package/src/channel/batchRetriever.spec.ts +4 -9
- package/src/channel/channel.spec.ts +24 -6
- package/src/channel/client.ts +52 -51
- package/src/channel/payload.ts +15 -16
- package/src/channel/retriever.ts +26 -41
- package/src/channel/writer.ts +7 -4
- package/src/client.ts +4 -4
- package/src/connection/checker.ts +1 -1
- package/src/connection/connection.spec.ts +31 -23
- package/src/control/client.ts +2 -2
- package/src/control/state.spec.ts +3 -3
- package/src/control/state.ts +1 -1
- package/src/errors.spec.ts +9 -5
- package/src/errors.ts +28 -15
- package/src/framer/adapter.spec.ts +118 -9
- package/src/framer/adapter.ts +24 -11
- package/src/framer/client.spec.ts +125 -2
- package/src/framer/client.ts +41 -47
- package/src/framer/codec.ts +1 -1
- package/src/framer/deleter.spec.ts +2 -2
- package/src/framer/deleter.ts +1 -1
- package/src/framer/frame.ts +1 -4
- package/src/framer/iterator.spec.ts +8 -8
- package/src/framer/iterator.ts +1 -1
- package/src/framer/streamProxy.ts +1 -1
- package/src/framer/streamer.spec.ts +185 -36
- package/src/framer/streamer.ts +28 -36
- package/src/framer/writer.spec.ts +7 -6
- package/src/framer/writer.ts +97 -111
- package/src/hardware/device/client.ts +45 -131
- package/src/hardware/device/device.spec.ts +163 -52
- package/src/hardware/device/payload.ts +10 -21
- package/src/hardware/rack/client.ts +87 -105
- package/src/hardware/rack/payload.ts +4 -13
- package/src/hardware/rack/rack.spec.ts +28 -35
- package/src/hardware/task/client.ts +335 -291
- package/src/hardware/task/payload.ts +86 -62
- package/src/hardware/task/task.spec.ts +208 -32
- package/src/index.ts +2 -1
- package/src/label/client.ts +100 -95
- package/src/label/external.ts +0 -2
- package/src/label/label.spec.ts +8 -6
- package/src/label/payload.ts +3 -4
- package/src/ontology/client.ts +41 -324
- package/src/ontology/group/group.spec.ts +2 -2
- package/src/ontology/group/group.ts +4 -5
- package/src/ontology/group/payload.ts +2 -25
- package/src/ontology/group/writer.ts +1 -1
- package/src/ontology/ontology.spec.ts +355 -41
- package/src/ontology/payload.ts +77 -112
- package/src/ontology/writer.ts +8 -17
- package/src/ranger/alias.ts +45 -37
- package/src/ranger/client.ts +144 -149
- package/src/ranger/external.ts +1 -1
- package/src/ranger/kv.ts +9 -27
- package/src/ranger/payload.ts +23 -37
- package/src/ranger/ranger.spec.ts +37 -56
- package/src/ranger/writer.ts +1 -1
- package/src/{signals/index.ts → testutil/client.ts} +11 -1
- package/src/user/client.ts +122 -47
- package/src/user/payload.ts +2 -5
- package/src/user/retriever.ts +1 -1
- package/src/user/user.spec.ts +31 -31
- package/src/user/writer.ts +1 -1
- package/src/util/decodeJSONString.ts +3 -3
- package/src/util/parseWithoutKeyConversion.ts +2 -2
- package/src/util/retrieve.ts +1 -1
- package/src/util/zod.ts +1 -1
- package/src/workspace/client.ts +20 -36
- package/src/workspace/lineplot/client.ts +5 -7
- package/src/workspace/lineplot/lineplot.spec.ts +2 -2
- package/src/workspace/lineplot/payload.ts +4 -7
- package/src/workspace/log/client.ts +5 -7
- package/src/workspace/log/log.spec.ts +2 -2
- package/src/workspace/log/payload.ts +4 -7
- package/src/workspace/payload.ts +4 -7
- package/src/workspace/schematic/client.ts +5 -7
- package/src/workspace/schematic/payload.ts +4 -7
- package/src/workspace/schematic/schematic.spec.ts +2 -2
- package/src/workspace/table/client.ts +5 -7
- package/src/workspace/table/payload.ts +4 -7
- package/src/workspace/table/table.spec.ts +2 -2
- package/src/workspace/workspace.spec.ts +2 -2
- package/dist/access/policy/ontology.d.ts +0 -5
- package/dist/access/policy/ontology.d.ts.map +0 -1
- package/dist/access/policy/retriever.d.ts +0 -40
- package/dist/access/policy/retriever.d.ts.map +0 -1
- package/dist/access/policy/writer.d.ts +0 -9
- package/dist/access/policy/writer.d.ts.map +0 -1
- package/dist/label/retriever.d.ts +0 -14
- package/dist/label/retriever.d.ts.map +0 -1
- package/dist/label/writer.d.ts +0 -54
- package/dist/label/writer.d.ts.map +0 -1
- package/dist/setupspecs.d.ts +0 -5
- package/dist/setupspecs.d.ts.map +0 -1
- package/dist/signals/external.d.ts +0 -2
- package/dist/signals/external.d.ts.map +0 -1
- package/dist/signals/index.d.ts +0 -2
- package/dist/signals/index.d.ts.map +0 -1
- package/dist/signals/observable.d.ts +0 -12
- package/dist/signals/observable.d.ts.map +0 -1
- package/src/access/policy/ontology.ts +0 -17
- package/src/access/policy/retriever.ts +0 -44
- package/src/access/policy/writer.ts +0 -65
- package/src/label/retriever.ts +0 -63
- package/src/label/writer.ts +0 -95
- package/src/setupspecs.ts +0 -27
- package/src/signals/external.ts +0 -10
- package/src/signals/observable.ts +0 -42
package/src/ranger/payload.ts
CHANGED
|
@@ -7,11 +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 { array } from "@synnaxlabs/x/array";
|
|
11
10
|
import { TimeRange } from "@synnaxlabs/x/telem";
|
|
12
|
-
import { z } from "zod
|
|
11
|
+
import { z } from "zod";
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
import { label } from "@/label";
|
|
14
|
+
|
|
15
|
+
export const keyZ = z.uuid();
|
|
15
16
|
export type Key = z.infer<typeof keyZ>;
|
|
16
17
|
export const nameZ = z.string().min(1);
|
|
17
18
|
export type Name = z.infer<typeof nameZ>;
|
|
@@ -24,40 +25,25 @@ export const payloadZ = z.object({
|
|
|
24
25
|
name: nameZ,
|
|
25
26
|
timeRange: TimeRange.z,
|
|
26
27
|
color: z.string().optional(),
|
|
28
|
+
labels: label.labelZ
|
|
29
|
+
.array()
|
|
30
|
+
.or(z.null().transform(() => undefined))
|
|
31
|
+
.optional(),
|
|
32
|
+
get parent(): z.ZodUnion<readonly [z.ZodNull, typeof payloadZ]> {
|
|
33
|
+
// Using as unknown is bad, but unfortunately resolving the output type of this
|
|
34
|
+
// transform is nearly impossible.
|
|
35
|
+
return payloadZ
|
|
36
|
+
.optional()
|
|
37
|
+
.nullable()
|
|
38
|
+
.transform((p) => (p === undefined ? null : p)) as unknown as z.ZodUnion<
|
|
39
|
+
readonly [z.ZodNull, typeof payloadZ]
|
|
40
|
+
>;
|
|
41
|
+
},
|
|
27
42
|
});
|
|
28
|
-
export interface Payload extends z.infer<typeof payloadZ> {}
|
|
29
|
-
|
|
30
|
-
export const newZ = payloadZ.partial({ key: true });
|
|
31
|
-
export interface New extends z.input<typeof newZ> {}
|
|
32
43
|
|
|
33
|
-
export type
|
|
34
|
-
| { single: true; variant: "keys"; normalized: Keys; actual: Key; empty: never }
|
|
35
|
-
| { single: true; variant: "names"; normalized: Names; actual: Name; empty: never }
|
|
36
|
-
| { single: false; variant: "keys"; normalized: Keys; actual: Keys; empty: boolean }
|
|
37
|
-
| {
|
|
38
|
-
single: false;
|
|
39
|
-
variant: "names";
|
|
40
|
-
normalized: Names;
|
|
41
|
-
actual: Names;
|
|
42
|
-
empty: boolean;
|
|
43
|
-
};
|
|
44
|
+
export type Payload = z.infer<typeof payloadZ>;
|
|
44
45
|
|
|
45
|
-
export const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (!empty) isKey = keyZ.safeParse(normal[0]).success;
|
|
50
|
-
return {
|
|
51
|
-
single: !Array.isArray(ranges),
|
|
52
|
-
variant: isKey ? "keys" : "names",
|
|
53
|
-
normalized: normal,
|
|
54
|
-
actual: ranges,
|
|
55
|
-
empty,
|
|
56
|
-
} as ParamAnalysisResult;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export const ONTOLOGY_TYPE = "range";
|
|
60
|
-
export type OntologyType = typeof ONTOLOGY_TYPE;
|
|
61
|
-
|
|
62
|
-
export const ALIAS_ONTOLOGY_TYPE = "range-alias";
|
|
63
|
-
export type AliasOntologyType = typeof ALIAS_ONTOLOGY_TYPE;
|
|
46
|
+
export const newZ = payloadZ
|
|
47
|
+
.omit({ parent: true, labels: true })
|
|
48
|
+
.partial({ key: true });
|
|
49
|
+
export interface New extends z.input<typeof newZ> {}
|
|
@@ -7,15 +7,14 @@
|
|
|
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 change } from "@synnaxlabs/x";
|
|
11
10
|
import { DataType, TimeSpan, TimeStamp } from "@synnaxlabs/x/telem";
|
|
12
11
|
import { describe, expect, it } from "vitest";
|
|
13
12
|
|
|
14
13
|
import { NotFoundError } from "@/errors";
|
|
15
14
|
import { type ranger } from "@/ranger";
|
|
16
|
-
import {
|
|
15
|
+
import { createTestClient } from "@/testutil/client";
|
|
17
16
|
|
|
18
|
-
const client =
|
|
17
|
+
const client = createTestClient();
|
|
19
18
|
|
|
20
19
|
describe("Ranger", () => {
|
|
21
20
|
describe("create", () => {
|
|
@@ -143,25 +142,6 @@ describe("Ranger", () => {
|
|
|
143
142
|
});
|
|
144
143
|
});
|
|
145
144
|
|
|
146
|
-
describe("page", () => {
|
|
147
|
-
it("should page through ranges", async () => {
|
|
148
|
-
await client.ranges.create({
|
|
149
|
-
name: "My New One Second Range",
|
|
150
|
-
timeRange: TimeStamp.now().spanRange(TimeSpan.seconds(1)),
|
|
151
|
-
});
|
|
152
|
-
await client.ranges.create({
|
|
153
|
-
name: "My New Two Second Range",
|
|
154
|
-
timeRange: TimeStamp.now().spanRange(TimeSpan.seconds(2)),
|
|
155
|
-
});
|
|
156
|
-
const ranges = await client.ranges.page(0, 1);
|
|
157
|
-
expect(ranges.length).toEqual(1);
|
|
158
|
-
const keys = ranges.map((r) => r.key);
|
|
159
|
-
const next = await client.ranges.page(1, 1);
|
|
160
|
-
expect(next.length).toEqual(1);
|
|
161
|
-
expect(next.map((r) => r.key)).not.toContain(keys[0]);
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
145
|
describe("KV", () => {
|
|
166
146
|
it("should set, get, and delete a single key", async () => {
|
|
167
147
|
const rng = await client.ranges.create({
|
|
@@ -196,43 +176,44 @@ describe("Ranger", () => {
|
|
|
196
176
|
const res = await rng.kv.list();
|
|
197
177
|
expect(res).toEqual({ foo: "bar", baz: "qux" });
|
|
198
178
|
});
|
|
179
|
+
});
|
|
199
180
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
});
|
|
206
|
-
const obs = await rng.kv.openTracker();
|
|
207
|
-
const res = new Promise<change.Change<string, ranger.KVPair>[]>((resolve) => {
|
|
208
|
-
obs.onChange((pair) => resolve(pair));
|
|
209
|
-
});
|
|
210
|
-
await rng.kv.set("foo", "bar");
|
|
211
|
-
const pair = await res;
|
|
212
|
-
expect(pair.length).toBeGreaterThan(0);
|
|
213
|
-
expect(pair[0].value?.range).toEqual(rng.key);
|
|
214
|
-
expect(pair[0].value?.key).toEqual("foo");
|
|
215
|
-
expect(pair[0].value?.value).toEqual("bar");
|
|
181
|
+
describe("label", () => {
|
|
182
|
+
it("should set and get a label for the range", async () => {
|
|
183
|
+
const rng = await client.ranges.create({
|
|
184
|
+
name: "My New One Second Range",
|
|
185
|
+
timeRange: TimeStamp.now().spanRange(TimeSpan.seconds(1)),
|
|
216
186
|
});
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
187
|
+
const label = await client.labels.create({
|
|
188
|
+
name: "My New Label",
|
|
189
|
+
color: "#E774D0",
|
|
190
|
+
});
|
|
191
|
+
await rng.addLabel(label.key);
|
|
192
|
+
const newRange = await client.ranges.retrieve({
|
|
193
|
+
keys: [rng.key],
|
|
194
|
+
includeLabels: true,
|
|
195
|
+
});
|
|
196
|
+
expect(newRange[0].labels).toHaveLength(1);
|
|
197
|
+
expect(newRange[0].labels?.[0]).toEqual(label);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe("parent", () => {
|
|
202
|
+
it("should set and get a parent for the range", async () => {
|
|
203
|
+
const parent = await client.ranges.create({
|
|
204
|
+
name: "My New One Second Range",
|
|
205
|
+
timeRange: TimeStamp.now().spanRange(TimeSpan.seconds(1)),
|
|
206
|
+
});
|
|
207
|
+
const child = await client.ranges.create({
|
|
208
|
+
name: "My New One Second Range",
|
|
209
|
+
timeRange: TimeStamp.now().spanRange(TimeSpan.seconds(1)),
|
|
210
|
+
});
|
|
211
|
+
await client.ontology.addChildren(parent.ontologyID, child.ontologyID);
|
|
212
|
+
const newParent = await client.ranges.retrieve({
|
|
213
|
+
keys: [child.key],
|
|
214
|
+
includeParent: true,
|
|
235
215
|
});
|
|
216
|
+
expect(newParent[0].parent).toEqual(parent.payload);
|
|
236
217
|
});
|
|
237
218
|
});
|
|
238
219
|
|
package/src/ranger/writer.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
10
|
import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
|
-
import { z } from "zod
|
|
11
|
+
import { z } from "zod";
|
|
12
12
|
|
|
13
13
|
import { ontology } from "@/ontology";
|
|
14
14
|
import { keyZ, nameZ, type New, newZ, type Payload, payloadZ } from "@/ranger/payload";
|
|
@@ -7,4 +7,14 @@
|
|
|
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
|
-
|
|
10
|
+
import Synnax, { type SynnaxProps } from "@/client";
|
|
11
|
+
|
|
12
|
+
export const TEST_CLIENT_PROPS: SynnaxProps = {
|
|
13
|
+
host: "localhost",
|
|
14
|
+
port: 9090,
|
|
15
|
+
username: "synnax",
|
|
16
|
+
password: "seldon",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const createTestClient = (props?: Partial<SynnaxProps>): Synnax =>
|
|
20
|
+
new Synnax({ ...TEST_CLIENT_PROPS, ...props });
|
package/src/user/client.ts
CHANGED
|
@@ -7,79 +7,154 @@
|
|
|
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 UnaryClient } from "@synnaxlabs/freighter";
|
|
10
|
+
import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
11
|
import { array } from "@synnaxlabs/x";
|
|
12
|
+
import { z } from "zod";
|
|
12
13
|
|
|
13
14
|
import { MultipleFoundError, NotFoundError } from "@/errors";
|
|
14
|
-
import { ontology } from "@/ontology";
|
|
15
|
-
import { type Key, type New,
|
|
16
|
-
import {
|
|
17
|
-
|
|
15
|
+
import { type ontology } from "@/ontology";
|
|
16
|
+
import { type Key, keyZ, type New, newZ, type User, userZ } from "@/user/payload";
|
|
17
|
+
import { nullableArrayZ } from "@/util/zod";
|
|
18
|
+
|
|
19
|
+
const retrieveRequestZ = z.object({
|
|
20
|
+
keys: keyZ.array().optional(),
|
|
21
|
+
usernames: z.string().array().optional(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const keyRetrieveRequestZ = z
|
|
25
|
+
.object({
|
|
26
|
+
key: keyZ,
|
|
27
|
+
})
|
|
28
|
+
.transform(({ key }) => ({ keys: [key] }));
|
|
29
|
+
|
|
30
|
+
const usernameRetrieveRequestZ = z
|
|
31
|
+
.object({
|
|
32
|
+
username: z.string(),
|
|
33
|
+
})
|
|
34
|
+
.transform(({ username }) => ({ usernames: [username] }));
|
|
35
|
+
|
|
36
|
+
const usernamesRetrieveRequestZ = z
|
|
37
|
+
.object({
|
|
38
|
+
usernames: z.string().array(),
|
|
39
|
+
})
|
|
40
|
+
.transform(({ usernames }) => ({ usernames }));
|
|
41
|
+
|
|
42
|
+
export type KeyRetrieveRequest = z.input<typeof keyRetrieveRequestZ>;
|
|
43
|
+
export type UsernameRetrieveRequest = z.input<typeof usernameRetrieveRequestZ>;
|
|
44
|
+
export type UsernamesRetrieveRequest = z.input<typeof usernamesRetrieveRequestZ>;
|
|
45
|
+
|
|
46
|
+
const retrieveArgsZ = z.union([
|
|
47
|
+
keyRetrieveRequestZ,
|
|
48
|
+
usernameRetrieveRequestZ,
|
|
49
|
+
usernamesRetrieveRequestZ,
|
|
50
|
+
retrieveRequestZ,
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
export type RetrieveArgs = z.input<typeof retrieveArgsZ>;
|
|
54
|
+
|
|
55
|
+
export interface RetrieveRequest extends z.infer<typeof retrieveRequestZ> {}
|
|
56
|
+
|
|
57
|
+
const retrieveResZ = z.object({ users: nullableArrayZ(userZ) });
|
|
58
|
+
|
|
59
|
+
const createReqZ = z.object({ users: newZ.array() });
|
|
60
|
+
const createResZ = z.object({ users: userZ.array() });
|
|
61
|
+
const changeUsernameReqZ = z.object({ key: keyZ, username: z.string().min(1) });
|
|
62
|
+
const changeUsernameResZ = z.object({});
|
|
63
|
+
const renameReqZ = z.object({
|
|
64
|
+
key: keyZ,
|
|
65
|
+
firstName: z.string().optional(),
|
|
66
|
+
lastName: z.string().optional(),
|
|
67
|
+
});
|
|
68
|
+
const renameResZ = z.object({});
|
|
69
|
+
const deleteReqZ = z.object({ keys: keyZ.array() });
|
|
70
|
+
const deleteResZ = z.object({});
|
|
71
|
+
|
|
72
|
+
const RETRIEVE_ENDPOINT = "/user/retrieve";
|
|
73
|
+
const CREATE_ENDPOINT = "/user/create";
|
|
74
|
+
const CHANGE_USERNAME_ENDPOINT = "/user/change-username";
|
|
75
|
+
const RENAME_ENDPOINT = "/user/rename";
|
|
76
|
+
const DELETE_ENDPOINT = "/user/delete";
|
|
18
77
|
|
|
19
78
|
export class Client {
|
|
20
|
-
private readonly
|
|
21
|
-
private readonly writer: Writer;
|
|
79
|
+
private readonly client: UnaryClient;
|
|
22
80
|
|
|
23
81
|
constructor(client: UnaryClient) {
|
|
24
|
-
this.
|
|
25
|
-
this.reader = new Retriever(client);
|
|
82
|
+
this.client = client;
|
|
26
83
|
}
|
|
27
84
|
|
|
28
85
|
async create(user: New): Promise<User>;
|
|
29
|
-
|
|
30
86
|
async create(users: New[]): Promise<User[]>;
|
|
31
|
-
|
|
32
87
|
async create(users: New | New[]): Promise<User | User[]> {
|
|
33
88
|
const isMany = Array.isArray(users);
|
|
34
|
-
const res = await
|
|
35
|
-
|
|
89
|
+
const res = await sendRequired<typeof createReqZ, typeof createResZ>(
|
|
90
|
+
this.client,
|
|
91
|
+
CREATE_ENDPOINT,
|
|
92
|
+
{ users: array.toArray(users) },
|
|
93
|
+
createReqZ,
|
|
94
|
+
createResZ,
|
|
95
|
+
);
|
|
96
|
+
return isMany ? res.users : res.users[0];
|
|
36
97
|
}
|
|
37
98
|
|
|
38
99
|
async changeUsername(key: Key, newUsername: string): Promise<void> {
|
|
39
|
-
await
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
async retrieve(keys: Key | Key[]): Promise<User | User[]> {
|
|
47
|
-
const isMany = Array.isArray(keys);
|
|
48
|
-
const res = await this.reader.retrieve({ keys: array.toArray(keys) });
|
|
49
|
-
if (isMany) return res;
|
|
50
|
-
if (res.length === 0) throw new NotFoundError(`No user with key ${keys} found`);
|
|
51
|
-
if (res.length > 1)
|
|
52
|
-
throw new MultipleFoundError(`Multiple users found with key ${keys}`);
|
|
53
|
-
return res[0];
|
|
100
|
+
await sendRequired<typeof changeUsernameReqZ, typeof changeUsernameResZ>(
|
|
101
|
+
this.client,
|
|
102
|
+
CHANGE_USERNAME_ENDPOINT,
|
|
103
|
+
{ key, username: newUsername },
|
|
104
|
+
changeUsernameReqZ,
|
|
105
|
+
changeUsernameResZ,
|
|
106
|
+
);
|
|
54
107
|
}
|
|
55
108
|
|
|
56
|
-
async
|
|
57
|
-
|
|
58
|
-
async
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
109
|
+
async retrieve(args: KeyRetrieveRequest): Promise<User>;
|
|
110
|
+
async retrieve(args: UsernameRetrieveRequest): Promise<User>;
|
|
111
|
+
async retrieve(args: RetrieveArgs): Promise<User[]>;
|
|
112
|
+
async retrieve(args: RetrieveArgs): Promise<User | User[]> {
|
|
113
|
+
const isSingle = "key" in args || "username" in args;
|
|
114
|
+
const res = await sendRequired<typeof retrieveArgsZ, typeof retrieveResZ>(
|
|
115
|
+
this.client,
|
|
116
|
+
RETRIEVE_ENDPOINT,
|
|
117
|
+
args,
|
|
118
|
+
retrieveArgsZ,
|
|
119
|
+
retrieveResZ,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if (!isSingle) return res.users;
|
|
123
|
+
|
|
124
|
+
if (res.users.length === 0) {
|
|
125
|
+
const identifier =
|
|
126
|
+
"key" in args ? `key ${args.key}` : `username ${args.username}`;
|
|
127
|
+
throw new NotFoundError(`No user with ${identifier} found`);
|
|
128
|
+
}
|
|
129
|
+
if (res.users.length > 1) {
|
|
130
|
+
const identifier =
|
|
131
|
+
"key" in args ? `key ${args.key}` : `username ${args.username}`;
|
|
132
|
+
throw new MultipleFoundError(`Multiple users found with ${identifier}`);
|
|
133
|
+
}
|
|
134
|
+
return res.users[0];
|
|
69
135
|
}
|
|
70
136
|
|
|
71
137
|
async rename(key: Key, firstName?: string, lastName?: string): Promise<void> {
|
|
72
|
-
await
|
|
138
|
+
await sendRequired<typeof renameReqZ, typeof renameResZ>(
|
|
139
|
+
this.client,
|
|
140
|
+
RENAME_ENDPOINT,
|
|
141
|
+
{ key, firstName, lastName },
|
|
142
|
+
renameReqZ,
|
|
143
|
+
renameResZ,
|
|
144
|
+
);
|
|
73
145
|
}
|
|
74
146
|
|
|
75
147
|
async delete(key: Key): Promise<void>;
|
|
76
|
-
|
|
77
148
|
async delete(keys: Key[]): Promise<void>;
|
|
78
|
-
|
|
79
149
|
async delete(keys: Key | Key[]): Promise<void> {
|
|
80
|
-
await
|
|
150
|
+
await sendRequired<typeof deleteReqZ, typeof deleteResZ>(
|
|
151
|
+
this.client,
|
|
152
|
+
DELETE_ENDPOINT,
|
|
153
|
+
{ keys: array.toArray(keys) },
|
|
154
|
+
deleteReqZ,
|
|
155
|
+
deleteResZ,
|
|
156
|
+
);
|
|
81
157
|
}
|
|
82
158
|
}
|
|
83
159
|
|
|
84
|
-
export const ontologyID = (key: Key): ontology.ID =>
|
|
85
|
-
new ontology.ID({ type: ONTOLOGY_TYPE, key });
|
|
160
|
+
export const ontologyID = (key: Key): ontology.ID => ({ type: "user", key });
|
package/src/user/payload.ts
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
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 { z } from "zod
|
|
10
|
+
import { z } from "zod";
|
|
11
11
|
|
|
12
|
-
export const keyZ = z.
|
|
12
|
+
export const keyZ = z.uuid();
|
|
13
13
|
export type Key = z.infer<typeof keyZ>;
|
|
14
14
|
|
|
15
15
|
export const userZ = z.object({
|
|
@@ -29,6 +29,3 @@ export const newZ = userZ
|
|
|
29
29
|
.omit({ rootUser: true })
|
|
30
30
|
.extend({ password: z.string().min(1) });
|
|
31
31
|
export interface New extends z.infer<typeof newZ> {}
|
|
32
|
-
|
|
33
|
-
export const ONTOLOGY_TYPE = "user";
|
|
34
|
-
export type OntologyType = typeof ONTOLOGY_TYPE;
|
package/src/user/retriever.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
10
|
import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
|
-
import { z } from "zod
|
|
11
|
+
import { z } from "zod";
|
|
12
12
|
|
|
13
13
|
import { keyZ, type User, userZ } from "@/user/payload";
|
|
14
14
|
import { nullableArrayZ } from "@/util/zod";
|
package/src/user/user.spec.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { id } from "@synnaxlabs/x";
|
|
|
11
11
|
import { describe, expect, test } from "vitest";
|
|
12
12
|
|
|
13
13
|
import { AuthError, NotFoundError } from "@/errors";
|
|
14
|
-
import {
|
|
14
|
+
import { createTestClient } from "@/testutil/client";
|
|
15
15
|
import { type user } from "@/user";
|
|
16
16
|
|
|
17
17
|
interface SortType {
|
|
@@ -20,7 +20,7 @@ interface SortType {
|
|
|
20
20
|
|
|
21
21
|
const sort = (a: SortType, b: SortType) => a.username.localeCompare(b.username);
|
|
22
22
|
|
|
23
|
-
const client =
|
|
23
|
+
const client = createTestClient();
|
|
24
24
|
|
|
25
25
|
const userOne: user.New = {
|
|
26
26
|
username: id.create(),
|
|
@@ -102,22 +102,22 @@ describe("User", () => {
|
|
|
102
102
|
describe("by name", () => {
|
|
103
103
|
describe("one", () => {
|
|
104
104
|
test("found", async () => {
|
|
105
|
-
const res = await client.user.
|
|
105
|
+
const res = await client.user.retrieve({ username: userOne.username });
|
|
106
106
|
expect(res.username).toEqual(userOne.username);
|
|
107
107
|
expect(res.key).toEqual(userOne.key);
|
|
108
108
|
expect(res.firstName).toEqual(userOne.firstName);
|
|
109
109
|
expect(res.lastName).toEqual(userOne.lastName);
|
|
110
110
|
});
|
|
111
111
|
test("not found", async () =>
|
|
112
|
-
await expect(client.user.
|
|
112
|
+
await expect(client.user.retrieve({ username: id.create() })).rejects.toThrow(
|
|
113
113
|
NotFoundError,
|
|
114
114
|
));
|
|
115
115
|
});
|
|
116
116
|
describe("many", () => {
|
|
117
117
|
test("found", async () => {
|
|
118
|
-
const res = await client.user.
|
|
119
|
-
userArray.map((u) => u.username),
|
|
120
|
-
);
|
|
118
|
+
const res = await client.user.retrieve({
|
|
119
|
+
usernames: userArray.map((u) => u.username),
|
|
120
|
+
});
|
|
121
121
|
expect(res.sort(sort)).toHaveLength(2);
|
|
122
122
|
res.forEach((u, i) => {
|
|
123
123
|
expect(u.username).toEqual(userArray[i].username);
|
|
@@ -127,14 +127,13 @@ describe("User", () => {
|
|
|
127
127
|
});
|
|
128
128
|
});
|
|
129
129
|
test("not found", async () => {
|
|
130
|
-
const res = await client.user.
|
|
130
|
+
const res = await client.user.retrieve({ usernames: [id.create()] });
|
|
131
131
|
expect(res).toEqual([]);
|
|
132
132
|
});
|
|
133
133
|
test("extra names getting deleted", async () => {
|
|
134
|
-
const res = await client.user.
|
|
135
|
-
...userArray.map((u) => u.username),
|
|
136
|
-
|
|
137
|
-
]);
|
|
134
|
+
const res = await client.user.retrieve({
|
|
135
|
+
usernames: [...userArray.map((u) => u.username), id.create()],
|
|
136
|
+
});
|
|
138
137
|
expect(res.sort(sort)).toHaveLength(2);
|
|
139
138
|
res.forEach((u, i) => {
|
|
140
139
|
expect(u.username).toEqual(userArray[i].username);
|
|
@@ -144,7 +143,7 @@ describe("User", () => {
|
|
|
144
143
|
});
|
|
145
144
|
});
|
|
146
145
|
test("calling with no names", async () => {
|
|
147
|
-
const res = await client.user.
|
|
146
|
+
const res = await client.user.retrieve({ usernames: [] });
|
|
148
147
|
const usernames = res.map((u) => u.username);
|
|
149
148
|
expect(usernames).toContain(userOne.username);
|
|
150
149
|
expect(usernames).toContain(userTwo.username);
|
|
@@ -156,7 +155,7 @@ describe("User", () => {
|
|
|
156
155
|
describe("by key", () => {
|
|
157
156
|
describe("one", () => {
|
|
158
157
|
test("found", async () => {
|
|
159
|
-
const res = await client.user.retrieve(userOne.key as string);
|
|
158
|
+
const res = await client.user.retrieve({ key: userOne.key as string });
|
|
160
159
|
expect(res.username).toEqual(userOne.username);
|
|
161
160
|
expect(res.key).toEqual(userOne.key);
|
|
162
161
|
expect(res.firstName).toEqual(userOne.firstName);
|
|
@@ -166,16 +165,18 @@ describe("User", () => {
|
|
|
166
165
|
await expect(
|
|
167
166
|
client.user.delete(userOne.key as string),
|
|
168
167
|
).resolves.toBeUndefined();
|
|
169
|
-
await expect(
|
|
170
|
-
|
|
171
|
-
);
|
|
168
|
+
await expect(
|
|
169
|
+
client.user.retrieve({ key: userOne.key as string }),
|
|
170
|
+
).rejects.toThrow(NotFoundError);
|
|
172
171
|
const u = await client.user.create(userOne);
|
|
173
172
|
userOne.key = u.key;
|
|
174
173
|
});
|
|
175
174
|
});
|
|
176
175
|
describe("many", () => {
|
|
177
176
|
test("found", async () => {
|
|
178
|
-
const res = await client.user.retrieve(
|
|
177
|
+
const res = await client.user.retrieve({
|
|
178
|
+
keys: userArray.map((u) => u.key as string),
|
|
179
|
+
});
|
|
179
180
|
expect(res.sort(sort)).toHaveLength(2);
|
|
180
181
|
res.forEach((u, i) => {
|
|
181
182
|
expect(u.username).toEqual(userArray[i].username);
|
|
@@ -188,14 +189,13 @@ describe("User", () => {
|
|
|
188
189
|
for (const u of userArray)
|
|
189
190
|
await expect(client.user.delete(u.key as string)).resolves.toBeUndefined();
|
|
190
191
|
await expect(
|
|
191
|
-
client.user.retrieve(userArray.map((u) => u.key as string)),
|
|
192
|
+
client.user.retrieve({ keys: userArray.map((u) => u.key as string) }),
|
|
192
193
|
).rejects.toThrow(NotFoundError);
|
|
193
|
-
// cleanup
|
|
194
194
|
const users = await client.user.create(userArray);
|
|
195
195
|
users.forEach((u, i) => (userArray[i].key = u.key));
|
|
196
196
|
});
|
|
197
197
|
test("all", async () => {
|
|
198
|
-
const res = await client.user.retrieve([]);
|
|
198
|
+
const res = await client.user.retrieve({ keys: [] });
|
|
199
199
|
const usernames = res.map((u) => u.username);
|
|
200
200
|
expect(usernames).toContain(userOne.username);
|
|
201
201
|
expect(usernames).toContain(userTwo.username);
|
|
@@ -211,7 +211,7 @@ describe("User", () => {
|
|
|
211
211
|
await expect(
|
|
212
212
|
client.user.changeUsername(userOne.key as string, newUsername),
|
|
213
213
|
).resolves.toBeUndefined();
|
|
214
|
-
const res = await client.user.
|
|
214
|
+
const res = await client.user.retrieve({ username: newUsername });
|
|
215
215
|
expect(res.username).toEqual(newUsername);
|
|
216
216
|
expect(res.key).not.toEqual("");
|
|
217
217
|
expect(res.firstName).toEqual(userOne.firstName);
|
|
@@ -240,7 +240,7 @@ describe("User", () => {
|
|
|
240
240
|
await expect(
|
|
241
241
|
client.user.rename(userOne.key as string, "Thomas", "Jefferson"),
|
|
242
242
|
).resolves.toBeUndefined();
|
|
243
|
-
const res = await client.user.retrieve(userOne.key as string);
|
|
243
|
+
const res = await client.user.retrieve({ key: userOne.key as string });
|
|
244
244
|
expect(res.username).toEqual(userOne.username);
|
|
245
245
|
expect(res.key).toEqual(userOne.key);
|
|
246
246
|
expect(res.firstName).toEqual("Thomas");
|
|
@@ -252,7 +252,7 @@ describe("User", () => {
|
|
|
252
252
|
await expect(
|
|
253
253
|
client.user.rename(userOne.key as string, "James"),
|
|
254
254
|
).resolves.toBeUndefined();
|
|
255
|
-
const res = await client.user.retrieve(userOne.key as string);
|
|
255
|
+
const res = await client.user.retrieve({ key: userOne.key as string });
|
|
256
256
|
expect(res.username).toEqual(userOne.username);
|
|
257
257
|
expect(res.key).toEqual(userOne.key);
|
|
258
258
|
expect(res.firstName).toEqual("James");
|
|
@@ -263,16 +263,16 @@ describe("User", () => {
|
|
|
263
263
|
describe("Delete", () => {
|
|
264
264
|
test("one that exists", async () => {
|
|
265
265
|
await expect(client.user.delete(userOne.key as string)).resolves.toBeUndefined();
|
|
266
|
-
await expect(
|
|
267
|
-
|
|
268
|
-
);
|
|
266
|
+
await expect(
|
|
267
|
+
client.user.retrieve({ key: userOne.key as string }),
|
|
268
|
+
).rejects.toThrow(NotFoundError);
|
|
269
269
|
});
|
|
270
270
|
test("many that exist", async () => {
|
|
271
271
|
await expect(
|
|
272
272
|
client.user.delete(userArray.map((u) => u.key as string)),
|
|
273
273
|
).resolves.toBeUndefined();
|
|
274
274
|
await expect(
|
|
275
|
-
client.user.retrieve(userArray.map((u) => u.key as string)),
|
|
275
|
+
client.user.retrieve({ keys: userArray.map((u) => u.key as string) }),
|
|
276
276
|
).rejects.toThrow(NotFoundError);
|
|
277
277
|
});
|
|
278
278
|
test("one that doesn't exist", async () => {
|
|
@@ -282,9 +282,9 @@ describe("User", () => {
|
|
|
282
282
|
await expect(
|
|
283
283
|
client.user.delete([userOne.key as string, userTwo.key as string]),
|
|
284
284
|
).resolves.toBeUndefined();
|
|
285
|
-
await expect(
|
|
286
|
-
|
|
287
|
-
);
|
|
285
|
+
await expect(
|
|
286
|
+
client.user.retrieve({ key: userTwo.key as string }),
|
|
287
|
+
).rejects.toThrow(NotFoundError);
|
|
288
288
|
});
|
|
289
289
|
});
|
|
290
290
|
});
|
package/src/user/writer.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
|
|
11
11
|
import { array } from "@synnaxlabs/x/array";
|
|
12
|
-
import { z } from "zod
|
|
12
|
+
import { z } from "zod";
|
|
13
13
|
|
|
14
14
|
import { type Key, keyZ, type New, newZ, type User, userZ } from "@/user/payload";
|
|
15
15
|
|
|
@@ -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 { binary,
|
|
10
|
+
import { binary, record } from "@synnaxlabs/x";
|
|
11
11
|
|
|
12
|
-
export const decodeJSONString = (s: string):
|
|
13
|
-
s ? binary.JSON_CODEC.decodeString(s,
|
|
12
|
+
export const decodeJSONString = (s: string): record.Unknown =>
|
|
13
|
+
s ? binary.JSON_CODEC.decodeString(s, record.unknownZ) : {};
|