@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
|
@@ -10,13 +10,118 @@
|
|
|
10
10
|
import { describe, expect, it, test } from "vitest";
|
|
11
11
|
|
|
12
12
|
import { ontology } from "@/ontology";
|
|
13
|
-
import {
|
|
13
|
+
import { createTestClient } from "@/testutil/client";
|
|
14
14
|
|
|
15
|
-
const client =
|
|
15
|
+
const client = createTestClient();
|
|
16
16
|
|
|
17
17
|
const randomName = (): string => `group-${Math.random()}`;
|
|
18
18
|
|
|
19
19
|
describe("Ontology", () => {
|
|
20
|
+
describe("parseIDs", () => {
|
|
21
|
+
it("should parse a single ID object", () => {
|
|
22
|
+
const id: ontology.ID = { type: "group", key: "test-key" };
|
|
23
|
+
const result = ontology.parseIDs(id);
|
|
24
|
+
expect(result).toEqual([id]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should parse an array of ID objects", () => {
|
|
28
|
+
const ids: ontology.ID[] = [
|
|
29
|
+
{ type: "group", key: "test-key-1" },
|
|
30
|
+
{ type: "channel", key: "test-key-2" },
|
|
31
|
+
];
|
|
32
|
+
const result = ontology.parseIDs(ids);
|
|
33
|
+
expect(result).toEqual(ids);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should parse a single string ID", () => {
|
|
37
|
+
const stringId = "group:test-key";
|
|
38
|
+
const result = ontology.parseIDs(stringId);
|
|
39
|
+
expect(result).toEqual([{ type: "group", key: "test-key" }]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should parse an array of string IDs", () => {
|
|
43
|
+
const stringIds = ["group:test-key-1", "channel:test-key-2"];
|
|
44
|
+
const result = ontology.parseIDs(stringIds);
|
|
45
|
+
expect(result).toEqual([
|
|
46
|
+
{ type: "group", key: "test-key-1" },
|
|
47
|
+
{ type: "channel", key: "test-key-2" },
|
|
48
|
+
]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should extract ID from a single Resource object", () => {
|
|
52
|
+
const resource: ontology.Resource = {
|
|
53
|
+
id: { type: "group", key: "test-key" },
|
|
54
|
+
name: "Test Resource",
|
|
55
|
+
key: "group:test-key",
|
|
56
|
+
};
|
|
57
|
+
const result = ontology.parseIDs(resource);
|
|
58
|
+
expect(result).toEqual([{ type: "group", key: "test-key" }]);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("should extract IDs from an array of Resource objects", () => {
|
|
62
|
+
const resources: ontology.Resource[] = [
|
|
63
|
+
{
|
|
64
|
+
id: { type: "group", key: "test-key-1" },
|
|
65
|
+
name: "Test Resource 1",
|
|
66
|
+
key: "group:test-key-1",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: { type: "channel", key: "test-key-2" },
|
|
70
|
+
name: "Test Resource 2",
|
|
71
|
+
key: "channel:test-key-2",
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
const result = ontology.parseIDs(resources);
|
|
75
|
+
expect(result).toEqual([
|
|
76
|
+
{ type: "group", key: "test-key-1" },
|
|
77
|
+
{ type: "channel", key: "test-key-2" },
|
|
78
|
+
]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("should return empty array for empty input", () => {
|
|
82
|
+
const result = ontology.parseIDs([]);
|
|
83
|
+
expect(result).toEqual([]);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should handle string IDs with colons in the key", () => {
|
|
87
|
+
const stringId = "group:test:key:with:colons";
|
|
88
|
+
const result = ontology.parseIDs(stringId);
|
|
89
|
+
expect(result).toEqual([{ type: "group", key: "test" }]);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should handle mixed Resource objects with different data types", () => {
|
|
93
|
+
const resources: ontology.Resource[] = [
|
|
94
|
+
{
|
|
95
|
+
id: { type: "group", key: "test-key-1" },
|
|
96
|
+
name: "Test Resource 1",
|
|
97
|
+
key: "group:test-key-1",
|
|
98
|
+
data: { customField: "value" },
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: { type: "channel", key: "test-key-2" },
|
|
102
|
+
name: "Test Resource 2",
|
|
103
|
+
key: "channel:test-key-2",
|
|
104
|
+
data: null,
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
const result = ontology.parseIDs(resources);
|
|
108
|
+
expect(result).toEqual([
|
|
109
|
+
{ type: "group", key: "test-key-1" },
|
|
110
|
+
{ type: "channel", key: "test-key-2" },
|
|
111
|
+
]);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should throw an error for invalid string ID format", () => {
|
|
115
|
+
const invalidStringId = "invalid-format";
|
|
116
|
+
expect(() => ontology.parseIDs(invalidStringId)).toThrow();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should throw an error for invalid resource type in string ID", () => {
|
|
120
|
+
const invalidStringId = "invalid-type:test-key";
|
|
121
|
+
expect(() => ontology.parseIDs(invalidStringId)).toThrow();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
20
125
|
describe("retrieve", () => {
|
|
21
126
|
test("retrieve", async () => {
|
|
22
127
|
const name = randomName();
|
|
@@ -43,20 +148,6 @@ describe("Ontology", () => {
|
|
|
43
148
|
expect(parents[0].name).toEqual(name);
|
|
44
149
|
});
|
|
45
150
|
});
|
|
46
|
-
describe("page", () => {
|
|
47
|
-
it("should return a page of resources", async () => {
|
|
48
|
-
for (let i = 0; i < 10; i++)
|
|
49
|
-
await client.ontology.groups.create(ontology.ROOT_ID, randomName());
|
|
50
|
-
const page = await client.ontology.page(0, 5);
|
|
51
|
-
expect(page.length).toEqual(5);
|
|
52
|
-
const page2 = await client.ontology.page(5, 5);
|
|
53
|
-
expect(page2.length).toEqual(5);
|
|
54
|
-
const page1Keys = page.map((r) => r.key);
|
|
55
|
-
const page2Keys = page2.map((r) => r.key);
|
|
56
|
-
const intersection = page1Keys.filter((key) => page2Keys.includes(key));
|
|
57
|
-
expect(intersection.length).toEqual(0);
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
151
|
describe("write", () => {
|
|
61
152
|
test("add children", async () => {
|
|
62
153
|
const name = randomName();
|
|
@@ -93,36 +184,259 @@ describe("Ontology", () => {
|
|
|
93
184
|
expect(newRootLength).toEqual(oldRootLength - 1);
|
|
94
185
|
});
|
|
95
186
|
});
|
|
187
|
+
|
|
96
188
|
describe("signals", async () => {
|
|
97
189
|
it("should correctly decode a set of relationships from a string", () => {
|
|
98
|
-
const rel = ontology.
|
|
99
|
-
expect(rel.type).toEqual(
|
|
100
|
-
expect(rel.from.type).toEqual("
|
|
190
|
+
const rel = ontology.relationshipZ.parse("table:keyA->parent->schematic:keyB");
|
|
191
|
+
expect(rel.type).toEqual(ontology.PARENT_OF_RELATIONSHIP_TYPE);
|
|
192
|
+
expect(rel.from.type).toEqual("table");
|
|
101
193
|
expect(rel.from.key).toEqual("keyA");
|
|
102
|
-
expect(rel.to.type).toEqual("
|
|
194
|
+
expect(rel.to.type).toEqual("schematic");
|
|
103
195
|
expect(rel.to.key).toEqual("keyB");
|
|
104
196
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("matchRelationship", () => {
|
|
200
|
+
const sampleRelationship: ontology.Relationship = {
|
|
201
|
+
from: { type: "group", key: "test-group" },
|
|
202
|
+
type: "parent",
|
|
203
|
+
to: { type: "channel", key: "test-channel" },
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
describe("type matching", () => {
|
|
207
|
+
it("should return true when types match", () => {
|
|
208
|
+
const match: ontology.MatchRelationshipArgs = { type: "parent" };
|
|
209
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
210
|
+
expect(result).toBe(true);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("should return false when types don't match", () => {
|
|
214
|
+
const match: ontology.MatchRelationshipArgs = { type: "child" };
|
|
215
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
216
|
+
expect(result).toBe(false);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe("from ID matching", () => {
|
|
221
|
+
it("should return true when from type matches", () => {
|
|
222
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
223
|
+
type: "parent",
|
|
224
|
+
from: { type: "group" },
|
|
225
|
+
};
|
|
226
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
227
|
+
expect(result).toBe(true);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("should return true when from key matches", () => {
|
|
231
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
232
|
+
type: "parent",
|
|
233
|
+
from: { key: "test-group" },
|
|
234
|
+
};
|
|
235
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
236
|
+
expect(result).toBe(true);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("should return true when both from type and key match", () => {
|
|
240
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
241
|
+
type: "parent",
|
|
242
|
+
from: { type: "group", key: "test-group" },
|
|
243
|
+
};
|
|
244
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
245
|
+
expect(result).toBe(true);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("should return false when from type doesn't match", () => {
|
|
249
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
250
|
+
type: "parent",
|
|
251
|
+
from: { type: "channel" },
|
|
252
|
+
};
|
|
253
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
254
|
+
expect(result).toBe(false);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("should return false when from key doesn't match", () => {
|
|
258
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
259
|
+
type: "parent",
|
|
260
|
+
from: { key: "wrong-key" },
|
|
261
|
+
};
|
|
262
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
263
|
+
expect(result).toBe(false);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("should return false when from type matches but key doesn't", () => {
|
|
267
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
268
|
+
type: "parent",
|
|
269
|
+
from: { type: "group", key: "wrong-key" },
|
|
270
|
+
};
|
|
271
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
272
|
+
expect(result).toBe(false);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe("to ID matching", () => {
|
|
277
|
+
it("should return true when to type matches", () => {
|
|
278
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
279
|
+
type: "parent",
|
|
280
|
+
to: { type: "channel" },
|
|
281
|
+
};
|
|
282
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
283
|
+
expect(result).toBe(true);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("should return true when to key matches", () => {
|
|
287
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
288
|
+
type: "parent",
|
|
289
|
+
to: { key: "test-channel" },
|
|
290
|
+
};
|
|
291
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
292
|
+
expect(result).toBe(true);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("should return true when both to type and key match", () => {
|
|
296
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
297
|
+
type: "parent",
|
|
298
|
+
to: { type: "channel", key: "test-channel" },
|
|
299
|
+
};
|
|
300
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
301
|
+
expect(result).toBe(true);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("should return false when to type doesn't match", () => {
|
|
305
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
306
|
+
type: "parent",
|
|
307
|
+
to: { type: "group" },
|
|
308
|
+
};
|
|
309
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
310
|
+
expect(result).toBe(false);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it("should return false when to key doesn't match", () => {
|
|
314
|
+
const match = { type: "parent", to: { key: "wrong-key" } };
|
|
315
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
316
|
+
expect(result).toBe(false);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("should return false when to type matches but key doesn't", () => {
|
|
320
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
321
|
+
type: "parent",
|
|
322
|
+
to: { type: "channel", key: "wrong-key" },
|
|
323
|
+
};
|
|
324
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
325
|
+
expect(result).toBe(false);
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
describe("combined matching", () => {
|
|
330
|
+
it("should return true when all specified criteria match", () => {
|
|
331
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
332
|
+
type: "parent",
|
|
333
|
+
from: { type: "group", key: "test-group" },
|
|
334
|
+
to: { type: "channel", key: "test-channel" },
|
|
335
|
+
};
|
|
336
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
337
|
+
expect(result).toBe(true);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it("should return false when type matches but from doesn't", () => {
|
|
341
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
342
|
+
type: "parent",
|
|
343
|
+
from: { type: "channel" },
|
|
344
|
+
to: { type: "channel", key: "test-channel" },
|
|
345
|
+
};
|
|
346
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
347
|
+
expect(result).toBe(false);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it("should return false when type matches but to doesn't", () => {
|
|
351
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
352
|
+
type: "parent",
|
|
353
|
+
from: { type: "group", key: "test-group" },
|
|
354
|
+
to: { type: "group" },
|
|
355
|
+
};
|
|
356
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
357
|
+
expect(result).toBe(false);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it("should return false when from and to match but type doesn't", () => {
|
|
361
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
362
|
+
type: "child",
|
|
363
|
+
from: { type: "group", key: "test-group" },
|
|
364
|
+
to: { type: "channel", key: "test-channel" },
|
|
365
|
+
};
|
|
366
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
367
|
+
expect(result).toBe(false);
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
describe("partial matching", () => {
|
|
372
|
+
it("should return true when only type is specified and matches", () => {
|
|
373
|
+
const match: ontology.MatchRelationshipArgs = { type: "parent" };
|
|
374
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
375
|
+
expect(result).toBe(true);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it("should return true when only from type is specified and matches", () => {
|
|
379
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
380
|
+
type: "parent",
|
|
381
|
+
from: { type: "group" },
|
|
382
|
+
};
|
|
383
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
384
|
+
expect(result).toBe(true);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it("should return true when only to type is specified and matches", () => {
|
|
388
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
389
|
+
type: "parent",
|
|
390
|
+
to: { type: "channel" },
|
|
391
|
+
};
|
|
392
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
393
|
+
expect(result).toBe(true);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it("should return true when only from key is specified and matches", () => {
|
|
397
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
398
|
+
type: "parent",
|
|
399
|
+
from: { key: "test-group" },
|
|
400
|
+
};
|
|
401
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
402
|
+
expect(result).toBe(true);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it("should return true when only to key is specified and matches", () => {
|
|
406
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
407
|
+
type: "parent",
|
|
408
|
+
to: { key: "test-channel" },
|
|
409
|
+
};
|
|
410
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
411
|
+
expect(result).toBe(true);
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
describe("edge cases", () => {
|
|
416
|
+
it("should handle empty from and to objects", () => {
|
|
417
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
418
|
+
type: "parent",
|
|
419
|
+
from: {},
|
|
420
|
+
to: {},
|
|
421
|
+
};
|
|
422
|
+
const result = ontology.matchRelationship(sampleRelationship, match);
|
|
423
|
+
expect(result).toBe(true);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it("should handle relationships with empty keys", () => {
|
|
427
|
+
const relationship: ontology.Relationship = {
|
|
428
|
+
from: { type: "group", key: "" },
|
|
429
|
+
type: "parent",
|
|
430
|
+
to: { type: "channel", key: "" },
|
|
431
|
+
};
|
|
432
|
+
const match: ontology.MatchRelationshipArgs = {
|
|
433
|
+
type: "parent",
|
|
434
|
+
from: { key: "" },
|
|
435
|
+
to: { key: "" },
|
|
436
|
+
};
|
|
437
|
+
const result = ontology.matchRelationship(relationship, match);
|
|
438
|
+
expect(result).toBe(true);
|
|
439
|
+
});
|
|
126
440
|
});
|
|
127
441
|
});
|
|
128
442
|
});
|
package/src/ontology/payload.ts
CHANGED
|
@@ -7,30 +7,8 @@
|
|
|
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
|
-
import { z } from "zod
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
ALLOW_ALL_ONTOLOGY_TYPE as ALLOW_ALL_TYPE,
|
|
15
|
-
ONTOLOGY_TYPE as POLICY_TYPE,
|
|
16
|
-
} from "@/access/policy/ontology";
|
|
17
|
-
import { ONTOLOGY_TYPE as CHANNEL_TYPE } from "@/channel/payload";
|
|
18
|
-
import { ONTOLOGY_TYPE as FRAMER_TYPE } from "@/framer/frame";
|
|
19
|
-
import { ONTOLOGY_TYPE as DEVICE_TYPE } from "@/hardware/device/payload";
|
|
20
|
-
import { ONTOLOGY_TYPE as RACK_TYPE } from "@/hardware/rack/payload";
|
|
21
|
-
import { ONTOLOGY_TYPE as TASK_TYPE } from "@/hardware/task/payload";
|
|
22
|
-
import { ONTOLOGY_TYPE as LABEL_TYPE } from "@/label/payload";
|
|
23
|
-
import { ONTOLOGY_TYPE as GROUP_TYPE } from "@/ontology/group/payload";
|
|
24
|
-
import {
|
|
25
|
-
ALIAS_ONTOLOGY_TYPE as RANGE_ALIAS_TYPE,
|
|
26
|
-
ONTOLOGY_TYPE as RANGE_TYPE,
|
|
27
|
-
} from "@/ranger/payload";
|
|
28
|
-
import { ONTOLOGY_TYPE as USER_TYPE } from "@/user/payload";
|
|
29
|
-
import { ONTOLOGY_TYPE as LINE_PLOT_TYPE } from "@/workspace/lineplot/payload";
|
|
30
|
-
import { ONTOLOGY_TYPE as LOG_TYPE } from "@/workspace/log/payload";
|
|
31
|
-
import { ONTOLOGY_TYPE as WORKSPACE_TYPE } from "@/workspace/payload";
|
|
32
|
-
import { ONTOLOGY_TYPE as SCHEMATIC_TYPE } from "@/workspace/schematic/payload";
|
|
33
|
-
import { ONTOLOGY_TYPE as TABLE_TYPE } from "@/workspace/table/payload";
|
|
10
|
+
import { array, type change, record } from "@synnaxlabs/x";
|
|
11
|
+
import { z } from "zod";
|
|
34
12
|
|
|
35
13
|
export type ResourceChange = change.Change<ID, Resource>;
|
|
36
14
|
export interface ResourceSet extends change.Set<ID, Resource> {}
|
|
@@ -44,113 +22,100 @@ export const CLUSTER_TYPE = "cluster";
|
|
|
44
22
|
export const NODE_TYPE = "node";
|
|
45
23
|
|
|
46
24
|
export const resourceTypeZ = z.enum([
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
25
|
+
"label",
|
|
26
|
+
"log",
|
|
27
|
+
"allow_all",
|
|
28
|
+
"builtin",
|
|
29
|
+
"cluster",
|
|
30
|
+
"channel",
|
|
31
|
+
"node",
|
|
32
|
+
"group",
|
|
33
|
+
"range",
|
|
34
|
+
"framer",
|
|
35
|
+
"range-alias",
|
|
36
|
+
"user",
|
|
37
|
+
"workspace",
|
|
38
|
+
"schematic",
|
|
39
|
+
"lineplot",
|
|
40
|
+
"rack",
|
|
41
|
+
"device",
|
|
42
|
+
"task",
|
|
43
|
+
"policy",
|
|
44
|
+
"table",
|
|
67
45
|
]);
|
|
68
46
|
export type ResourceType = z.infer<typeof resourceTypeZ>;
|
|
69
47
|
|
|
70
|
-
|
|
71
|
-
export interface IDPayload extends z.infer<typeof idZ> {}
|
|
72
|
-
|
|
73
|
-
export const stringIDZ = z.string().transform((v) => {
|
|
48
|
+
const stringIDZ = z.string().transform((v) => {
|
|
74
49
|
const [type, key] = v.split(":");
|
|
75
50
|
return { type: resourceTypeZ.parse(type), key: key ?? "" };
|
|
76
51
|
});
|
|
77
52
|
|
|
78
|
-
export const
|
|
79
|
-
export type CrudeID = z.input<typeof crudeIDZ>;
|
|
80
|
-
|
|
81
|
-
export class ID {
|
|
82
|
-
type: ResourceType;
|
|
83
|
-
key: string;
|
|
84
|
-
|
|
85
|
-
constructor(args: z.input<typeof crudeIDZ> | ID) {
|
|
86
|
-
if (args instanceof ID) {
|
|
87
|
-
this.type = args.type;
|
|
88
|
-
this.key = args.key;
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (typeof args === "string") {
|
|
92
|
-
const [type, key] = args.split(":");
|
|
93
|
-
this.type = type as ResourceType;
|
|
94
|
-
this.key = key ?? "";
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
this.type = args.type;
|
|
98
|
-
this.key = args.key;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
toString(): string {
|
|
102
|
-
return `${this.type}:${this.key}`;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
isType(): boolean {
|
|
106
|
-
return this.key === "";
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
matchesType(type: ResourceType): boolean {
|
|
110
|
-
return this.type === type && this.isType();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
get payload(): IDPayload {
|
|
114
|
-
return { type: this.type, key: this.key };
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
static readonly z = z.union([z.instanceof(ID), crudeIDZ.transform((v) => new ID(v))]);
|
|
118
|
-
}
|
|
53
|
+
export const idZ = z.object({ type: resourceTypeZ, key: z.string() }).or(stringIDZ);
|
|
119
54
|
|
|
120
|
-
export
|
|
55
|
+
export type ID = z.infer<typeof idZ>;
|
|
121
56
|
|
|
122
|
-
export const
|
|
123
|
-
export interface SchemaField extends z.infer<typeof schemaFieldZ> {}
|
|
57
|
+
export const ROOT_ID: ID = { type: BUILTIN_TYPE, key: "root" };
|
|
124
58
|
|
|
125
|
-
export const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
export
|
|
59
|
+
export const idToString = (id: ID) => `${id.type}:${id.key}`;
|
|
60
|
+
|
|
61
|
+
export const idsEqual = (a: ID, b: ID) => a.type === b.type && a.key === b.key;
|
|
62
|
+
|
|
63
|
+
export const parseIDs = (
|
|
64
|
+
ids: ID | ID[] | string | string[] | Resource | Resource[],
|
|
65
|
+
): ID[] => {
|
|
66
|
+
const arr = array.toArray(ids);
|
|
67
|
+
if (arr.length === 0) return [];
|
|
68
|
+
if (typeof arr[0] === "object" && "id" in arr[0])
|
|
69
|
+
return (arr as Resource[]).map(({ id }) => id);
|
|
70
|
+
return arr.map((id) => idZ.parse(id));
|
|
71
|
+
};
|
|
130
72
|
|
|
131
73
|
export const resourceZ = z
|
|
132
74
|
.object({
|
|
133
|
-
id:
|
|
75
|
+
id: idZ,
|
|
134
76
|
name: z.string(),
|
|
135
|
-
|
|
136
|
-
data: unknownRecordZ.optional().nullable(),
|
|
77
|
+
data: record.unknownZ.optional().nullable(),
|
|
137
78
|
})
|
|
138
|
-
.transform((resource) => ({ key: resource.id
|
|
139
|
-
export interface Resource<T extends
|
|
79
|
+
.transform((resource) => ({ key: idToString(resource.id), ...resource }));
|
|
80
|
+
export interface Resource<T extends record.Unknown = record.Unknown>
|
|
140
81
|
extends Omit<z.infer<typeof resourceZ>, "data"> {
|
|
141
82
|
data?: T | null;
|
|
142
83
|
}
|
|
143
84
|
|
|
144
|
-
export
|
|
145
|
-
export const FROM_RELATIONSHIP_DIRECTION = "from";
|
|
146
|
-
export type RelationshipDirection =
|
|
147
|
-
| typeof TO_RELATIONSHIP_DIRECTION
|
|
148
|
-
| typeof FROM_RELATIONSHIP_DIRECTION;
|
|
85
|
+
export type RelationshipDirection = "to" | "from";
|
|
149
86
|
|
|
150
|
-
export const
|
|
151
|
-
|
|
87
|
+
export const oppositeRelationshipDirection = (
|
|
88
|
+
direction: RelationshipDirection,
|
|
89
|
+
): RelationshipDirection => (direction === "to" ? "from" : "to");
|
|
90
|
+
|
|
91
|
+
export const relationshipZ = z.object({ from: idZ, type: z.string(), to: idZ }).or(
|
|
92
|
+
z.string().transform((v) => {
|
|
93
|
+
const [from, type, to] = v.split("->");
|
|
94
|
+
return { from: idZ.parse(from), type, to: idZ.parse(to) };
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
export type Relationship = z.infer<typeof relationshipZ>;
|
|
98
|
+
|
|
99
|
+
export const relationshipToString = (relationship: Relationship) =>
|
|
100
|
+
`${idToString(relationship.from)}->${relationship.type}->${idToString(relationship.to)}`;
|
|
101
|
+
|
|
102
|
+
export const PARENT_OF_RELATIONSHIP_TYPE = "parent";
|
|
103
|
+
|
|
104
|
+
export interface MatchRelationshipArgs {
|
|
105
|
+
from?: Partial<ID>;
|
|
106
|
+
type: string;
|
|
107
|
+
to?: Partial<ID>;
|
|
108
|
+
}
|
|
152
109
|
|
|
153
|
-
export const
|
|
154
|
-
|
|
155
|
-
|
|
110
|
+
export const matchRelationship = (
|
|
111
|
+
relationship: Relationship,
|
|
112
|
+
match: MatchRelationshipArgs,
|
|
113
|
+
) => {
|
|
114
|
+
if (match.type != null && match.type !== relationship.type) return false;
|
|
115
|
+
if (match.from?.type != null && match.from.type !== relationship.from.type)
|
|
116
|
+
return false;
|
|
117
|
+
if (match.to?.type != null && match.to.type !== relationship.to.type) return false;
|
|
118
|
+
if (match.from?.key != null && match.from.key !== relationship.from.key) return false;
|
|
119
|
+
if (match.to?.key != null && match.to.key !== relationship.to.key) return false;
|
|
120
|
+
return true;
|
|
156
121
|
};
|