@synnaxlabs/client 0.48.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +6 -6
- package/dist/client.cjs +31 -31
- package/dist/client.js +4836 -4644
- package/dist/src/access/client.d.ts +3 -1
- package/dist/src/access/client.d.ts.map +1 -1
- package/dist/src/access/enforce.d.ts +35 -0
- package/dist/src/access/enforce.d.ts.map +1 -0
- package/dist/src/access/enforce.spec.d.ts +2 -0
- package/dist/src/access/enforce.spec.d.ts.map +1 -0
- package/dist/src/access/external.d.ts +3 -0
- package/dist/src/access/external.d.ts.map +1 -1
- package/dist/src/access/payload.d.ts +0 -6
- package/dist/src/access/payload.d.ts.map +1 -1
- package/dist/src/access/policy/access.spec.d.ts +2 -0
- package/dist/src/access/policy/access.spec.d.ts.map +1 -0
- package/dist/src/access/policy/client.d.ts +485 -31
- package/dist/src/access/policy/client.d.ts.map +1 -1
- package/dist/src/access/policy/payload.d.ts +36 -113
- package/dist/src/access/policy/payload.d.ts.map +1 -1
- package/dist/src/access/role/client.d.ts +135 -0
- package/dist/src/access/role/client.d.ts.map +1 -0
- package/dist/src/access/role/external.d.ts.map +1 -0
- package/dist/src/access/role/index.d.ts +2 -0
- package/dist/src/access/role/index.d.ts.map +1 -0
- package/dist/src/access/role/payload.d.ts +27 -0
- package/dist/src/access/role/payload.d.ts.map +1 -0
- package/dist/src/access/role/role.spec.d.ts +2 -0
- package/dist/src/access/role/role.spec.d.ts.map +1 -0
- package/dist/src/arc/access.spec.d.ts +2 -0
- package/dist/src/arc/access.spec.d.ts.map +1 -0
- package/dist/src/arc/client.d.ts +5 -14
- package/dist/src/arc/client.d.ts.map +1 -1
- package/dist/src/arc/payload.d.ts +11 -2
- package/dist/src/arc/payload.d.ts.map +1 -1
- package/dist/src/auth/auth.d.ts +5 -3
- package/dist/src/auth/auth.d.ts.map +1 -1
- package/dist/src/channel/access.spec.d.ts +2 -0
- package/dist/src/channel/access.spec.d.ts.map +1 -0
- package/dist/src/channel/client.d.ts +0 -1
- package/dist/src/channel/client.d.ts.map +1 -1
- package/dist/src/channel/payload.d.ts +18 -8
- package/dist/src/channel/payload.d.ts.map +1 -1
- package/dist/src/channel/payload.spec.d.ts +2 -0
- package/dist/src/channel/payload.spec.d.ts.map +1 -0
- package/dist/src/channel/retriever.d.ts +4 -6
- package/dist/src/channel/retriever.d.ts.map +1 -1
- package/dist/src/channel/writer.d.ts.map +1 -1
- package/dist/src/client.d.ts +9 -5
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/device/access.spec.d.ts +2 -0
- package/dist/src/device/access.spec.d.ts.map +1 -0
- package/dist/src/{hardware/device → device}/client.d.ts +14 -7
- package/dist/src/device/client.d.ts.map +1 -0
- package/dist/src/device/device.spec.d.ts.map +1 -0
- package/dist/src/device/external.d.ts.map +1 -0
- package/dist/src/device/index.d.ts.map +1 -0
- package/dist/src/{hardware/device → device}/payload.d.ts +1 -1
- package/dist/src/device/payload.d.ts.map +1 -0
- package/dist/src/errors.d.ts +3 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/framer/client.d.ts +8 -1
- package/dist/src/framer/client.d.ts.map +1 -1
- package/dist/src/framer/frame.d.ts +10 -5
- package/dist/src/framer/frame.d.ts.map +1 -1
- package/dist/src/framer/iterator.d.ts +3 -3
- package/dist/src/framer/streamer.d.ts +24 -21
- package/dist/src/framer/streamer.d.ts.map +1 -1
- package/dist/src/framer/writer.d.ts +13 -13
- package/dist/src/index.d.ts +4 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/label/access.spec.d.ts +2 -0
- package/dist/src/label/access.spec.d.ts.map +1 -0
- package/dist/src/label/client.d.ts +20 -11
- package/dist/src/label/client.d.ts.map +1 -1
- package/dist/src/ontology/client.d.ts +6 -6
- package/dist/src/ontology/client.d.ts.map +1 -1
- package/dist/src/ontology/group/access.spec.d.ts +2 -0
- package/dist/src/ontology/group/access.spec.d.ts.map +1 -0
- package/dist/src/ontology/group/client.d.ts +2 -2
- package/dist/src/ontology/group/client.d.ts.map +1 -1
- package/dist/src/ontology/group/payload.d.ts +1 -2
- package/dist/src/ontology/group/payload.d.ts.map +1 -1
- package/dist/src/ontology/payload.d.ts +23 -17
- package/dist/src/ontology/payload.d.ts.map +1 -1
- package/dist/src/ontology/writer.d.ts +10 -10
- package/dist/src/ontology/writer.d.ts.map +1 -1
- package/dist/src/rack/access.spec.d.ts +2 -0
- package/dist/src/rack/access.spec.d.ts.map +1 -0
- package/dist/src/{hardware/rack → rack}/client.d.ts +15 -8
- package/dist/src/rack/client.d.ts.map +1 -0
- package/dist/src/rack/external.d.ts.map +1 -0
- package/dist/src/rack/index.d.ts.map +1 -0
- package/dist/src/{hardware/rack → rack}/payload.d.ts +1 -1
- package/dist/src/rack/payload.d.ts.map +1 -0
- package/dist/src/rack/rack.spec.d.ts.map +1 -0
- package/dist/src/ranger/access.spec.d.ts +2 -0
- package/dist/src/ranger/access.spec.d.ts.map +1 -0
- package/dist/src/ranger/alias.d.ts +1 -8
- package/dist/src/ranger/alias.d.ts.map +1 -1
- package/dist/src/ranger/client.d.ts +12 -5
- package/dist/src/ranger/client.d.ts.map +1 -1
- package/dist/src/ranger/kv.d.ts +0 -3
- package/dist/src/ranger/kv.d.ts.map +1 -1
- package/dist/src/ranger/writer.d.ts +2 -2
- package/dist/src/ranger/writer.d.ts.map +1 -1
- package/dist/src/status/access.spec.d.ts +2 -0
- package/dist/src/status/access.spec.d.ts.map +1 -0
- package/dist/src/status/client.d.ts +4 -4
- package/dist/src/status/client.d.ts.map +1 -1
- package/dist/src/status/payload.d.ts +9 -2
- package/dist/src/status/payload.d.ts.map +1 -1
- package/dist/src/task/access.spec.d.ts +2 -0
- package/dist/src/task/access.spec.d.ts.map +1 -0
- package/dist/src/{hardware/task → task}/client.d.ts +26 -15
- package/dist/src/task/client.d.ts.map +1 -0
- package/dist/src/task/external.d.ts +3 -0
- package/dist/src/task/external.d.ts.map +1 -0
- package/dist/src/task/index.d.ts.map +1 -0
- package/dist/src/{hardware/task → task}/payload.d.ts +45 -6
- package/dist/src/task/payload.d.ts.map +1 -0
- package/dist/src/task/task.spec.d.ts.map +1 -0
- package/dist/src/testutil/access.d.ts +4 -0
- package/dist/src/testutil/access.d.ts.map +1 -0
- package/dist/src/transport.d.ts.map +1 -1
- package/dist/src/user/access.spec.d.ts +2 -0
- package/dist/src/user/access.spec.d.ts.map +1 -0
- package/dist/src/user/client.d.ts +10 -1
- package/dist/src/user/client.d.ts.map +1 -1
- package/dist/src/user/external.d.ts +1 -1
- package/dist/src/user/external.d.ts.map +1 -1
- package/dist/src/user/payload.d.ts.map +1 -1
- package/dist/src/workspace/access.spec.d.ts +2 -0
- package/dist/src/workspace/access.spec.d.ts.map +1 -0
- package/dist/src/workspace/client.d.ts +10 -5
- package/dist/src/workspace/client.d.ts.map +1 -1
- package/dist/src/workspace/lineplot/access.spec.d.ts +2 -0
- package/dist/src/workspace/lineplot/access.spec.d.ts.map +1 -0
- package/dist/src/workspace/lineplot/client.d.ts +8 -1
- package/dist/src/workspace/lineplot/client.d.ts.map +1 -1
- package/dist/src/workspace/log/access.spec.d.ts +2 -0
- package/dist/src/workspace/log/access.spec.d.ts.map +1 -0
- package/dist/src/workspace/log/client.d.ts +8 -1
- package/dist/src/workspace/log/client.d.ts.map +1 -1
- package/dist/src/workspace/schematic/access.spec.d.ts +2 -0
- package/dist/src/workspace/schematic/access.spec.d.ts.map +1 -0
- package/dist/src/workspace/schematic/client.d.ts +8 -1
- package/dist/src/workspace/schematic/client.d.ts.map +1 -1
- package/dist/src/workspace/schematic/symbol/access.spec.d.ts +2 -0
- package/dist/src/workspace/schematic/symbol/access.spec.d.ts.map +1 -0
- package/dist/src/workspace/schematic/symbol/client.d.ts +1 -5
- package/dist/src/workspace/schematic/symbol/client.d.ts.map +1 -1
- package/dist/src/workspace/schematic/symbol/payload.d.ts +2 -2
- package/dist/src/workspace/table/access.spec.d.ts +2 -0
- package/dist/src/workspace/table/access.spec.d.ts.map +1 -0
- package/dist/src/workspace/table/client.d.ts +8 -1
- package/dist/src/workspace/table/client.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/access/client.ts +5 -2
- package/src/access/enforce.spec.ts +189 -0
- package/src/access/enforce.ts +84 -0
- package/src/access/external.ts +3 -0
- package/src/access/payload.ts +1 -13
- package/src/access/policy/access.spec.ts +147 -0
- package/src/access/policy/client.ts +21 -25
- package/src/access/policy/payload.ts +9 -5
- package/src/access/role/client.ts +135 -0
- package/src/access/role/external.ts +11 -0
- package/src/{hardware → access/role}/index.ts +1 -1
- package/src/access/role/payload.ts +32 -0
- package/src/access/role/role.spec.ts +95 -0
- package/src/arc/access.spec.ts +143 -0
- package/src/arc/client.ts +7 -31
- package/src/arc/payload.ts +4 -0
- package/src/auth/auth.ts +33 -11
- package/src/channel/access.spec.ts +116 -0
- package/src/channel/channel.spec.ts +63 -73
- package/src/channel/client.ts +2 -8
- package/src/channel/payload.spec.ts +171 -0
- package/src/channel/payload.ts +35 -7
- package/src/channel/retriever.ts +10 -11
- package/src/channel/writer.ts +3 -7
- package/src/client.ts +14 -18
- package/src/device/access.spec.ts +159 -0
- package/src/{hardware/device → device}/client.ts +12 -21
- package/src/{hardware/device → device}/device.spec.ts +70 -34
- package/src/device/external.ts +11 -0
- package/src/{hardware/rack → device}/index.ts +1 -1
- package/src/{hardware/device → device}/payload.ts +3 -3
- package/src/errors.ts +2 -0
- package/src/framer/adapter.spec.ts +14 -14
- package/src/framer/client.spec.ts +14 -20
- package/src/framer/client.ts +3 -5
- package/src/framer/deleter.spec.ts +1 -1
- package/src/framer/frame.spec.ts +131 -0
- package/src/framer/frame.ts +10 -2
- package/src/framer/iterator.ts +3 -3
- package/src/framer/streamer.spec.ts +100 -12
- package/src/framer/streamer.ts +29 -9
- package/src/framer/writer.spec.ts +5 -5
- package/src/index.ts +4 -5
- package/src/label/access.spec.ts +109 -0
- package/src/label/client.ts +10 -14
- package/src/ontology/client.ts +4 -6
- package/src/ontology/group/access.spec.ts +77 -0
- package/src/ontology/group/client.ts +3 -7
- package/src/ontology/group/group.spec.ts +18 -0
- package/src/ontology/group/payload.ts +2 -2
- package/src/ontology/ontology.spec.ts +2 -0
- package/src/ontology/payload.ts +18 -2
- package/src/ontology/writer.ts +3 -7
- package/src/rack/access.spec.ts +102 -0
- package/src/{hardware/rack → rack}/client.ts +14 -19
- package/src/{hardware/device/index.ts → rack/external.ts} +2 -1
- package/src/{hardware/external.ts → rack/index.ts} +1 -1
- package/src/{hardware/rack → rack}/payload.ts +2 -2
- package/src/{hardware/rack → rack}/rack.spec.ts +43 -17
- package/src/ranger/access.spec.ts +115 -0
- package/src/ranger/alias.ts +6 -14
- package/src/ranger/client.ts +13 -14
- package/src/ranger/kv.ts +7 -9
- package/src/ranger/ranger.spec.ts +4 -4
- package/src/ranger/writer.ts +3 -7
- package/src/status/access.spec.ts +129 -0
- package/src/status/client.ts +5 -9
- package/src/status/payload.ts +3 -2
- package/src/task/access.spec.ts +131 -0
- package/src/{hardware/task → task}/client.ts +50 -25
- package/src/task/external.ts +11 -0
- package/src/{hardware/task → task}/index.ts +1 -1
- package/src/{hardware/task → task}/payload.ts +22 -3
- package/src/{hardware/task → task}/task.spec.ts +197 -34
- package/src/testutil/access.ts +34 -0
- package/src/testutil/channels.ts +3 -3
- package/src/transport.ts +1 -3
- package/src/user/access.spec.ts +107 -0
- package/src/user/client.ts +10 -12
- package/src/user/external.ts +12 -1
- package/src/user/payload.ts +3 -5
- package/src/workspace/access.spec.ts +108 -0
- package/src/workspace/client.ts +11 -27
- package/src/workspace/lineplot/access.spec.ts +134 -0
- package/src/workspace/lineplot/client.ts +8 -13
- package/src/workspace/log/access.spec.ts +134 -0
- package/src/workspace/log/client.ts +8 -13
- package/src/workspace/schematic/access.spec.ts +134 -0
- package/src/workspace/schematic/client.ts +9 -18
- package/src/workspace/schematic/symbol/access.spec.ts +172 -0
- package/src/workspace/schematic/symbol/client.ts +6 -17
- package/src/workspace/schematic/symbol/payload.ts +1 -1
- package/src/workspace/table/access.spec.ts +134 -0
- package/src/workspace/table/client.ts +8 -13
- package/dist/src/access/policy/policy.spec.d.ts +0 -2
- package/dist/src/access/policy/policy.spec.d.ts.map +0 -1
- package/dist/src/hardware/client.d.ts +0 -10
- package/dist/src/hardware/client.d.ts.map +0 -1
- package/dist/src/hardware/device/client.d.ts.map +0 -1
- package/dist/src/hardware/device/device.spec.d.ts.map +0 -1
- package/dist/src/hardware/device/external.d.ts.map +0 -1
- package/dist/src/hardware/device/index.d.ts.map +0 -1
- package/dist/src/hardware/device/payload.d.ts.map +0 -1
- package/dist/src/hardware/external.d.ts +0 -2
- package/dist/src/hardware/external.d.ts.map +0 -1
- package/dist/src/hardware/index.d.ts +0 -2
- package/dist/src/hardware/index.d.ts.map +0 -1
- package/dist/src/hardware/rack/client.d.ts.map +0 -1
- package/dist/src/hardware/rack/external.d.ts.map +0 -1
- package/dist/src/hardware/rack/index.d.ts.map +0 -1
- package/dist/src/hardware/rack/payload.d.ts.map +0 -1
- package/dist/src/hardware/rack/rack.spec.d.ts.map +0 -1
- package/dist/src/hardware/task/client.d.ts.map +0 -1
- package/dist/src/hardware/task/external.d.ts.map +0 -1
- package/dist/src/hardware/task/index.d.ts.map +0 -1
- package/dist/src/hardware/task/payload.d.ts.map +0 -1
- package/dist/src/hardware/task/task.spec.d.ts.map +0 -1
- package/dist/src/user/retriever.d.ts +0 -16
- package/dist/src/user/retriever.d.ts.map +0 -1
- package/dist/src/user/writer.d.ts +0 -11
- package/dist/src/user/writer.d.ts.map +0 -1
- package/src/access/policy/policy.spec.ts +0 -329
- package/src/hardware/client.ts +0 -24
- package/src/hardware/device/external.ts +0 -11
- package/src/hardware/rack/external.ts +0 -11
- package/src/hardware/task/external.ts +0 -11
- package/src/user/retriever.ts +0 -41
- package/src/user/writer.ts +0 -84
- /package/dist/src/{hardware/device → access/role}/external.d.ts +0 -0
- /package/dist/src/{hardware/device → device}/device.spec.d.ts +0 -0
- /package/dist/src/{hardware/rack → device}/external.d.ts +0 -0
- /package/dist/src/{hardware/device → device}/index.d.ts +0 -0
- /package/dist/src/{hardware/task → rack}/external.d.ts +0 -0
- /package/dist/src/{hardware/rack → rack}/index.d.ts +0 -0
- /package/dist/src/{hardware/rack → rack}/rack.spec.d.ts +0 -0
- /package/dist/src/{hardware/task → task}/index.d.ts +0 -0
- /package/dist/src/{hardware/task → task}/task.spec.d.ts +0 -0
package/src/framer/frame.spec.ts
CHANGED
|
@@ -544,6 +544,137 @@ describe("framer.Frame", () => {
|
|
|
544
544
|
});
|
|
545
545
|
});
|
|
546
546
|
|
|
547
|
+
describe("forEachUnique", () => {
|
|
548
|
+
it("should iterate over all unique columns in the frame", () => {
|
|
549
|
+
const f = new framer.Frame(
|
|
550
|
+
new Map([
|
|
551
|
+
[
|
|
552
|
+
12,
|
|
553
|
+
[
|
|
554
|
+
new Series({
|
|
555
|
+
data: new Float32Array([1, 2, 3]),
|
|
556
|
+
timeRange: new TimeRange(1, 2),
|
|
557
|
+
}),
|
|
558
|
+
],
|
|
559
|
+
],
|
|
560
|
+
[
|
|
561
|
+
13,
|
|
562
|
+
[
|
|
563
|
+
new Series({
|
|
564
|
+
data: new Float32Array([4, 5, 6]),
|
|
565
|
+
timeRange: new TimeRange(4, 6),
|
|
566
|
+
}),
|
|
567
|
+
],
|
|
568
|
+
],
|
|
569
|
+
[
|
|
570
|
+
14,
|
|
571
|
+
[
|
|
572
|
+
new Series({
|
|
573
|
+
data: new Float32Array([7, 8, 9]),
|
|
574
|
+
timeRange: new TimeRange(7, 9),
|
|
575
|
+
}),
|
|
576
|
+
],
|
|
577
|
+
],
|
|
578
|
+
]),
|
|
579
|
+
);
|
|
580
|
+
f.push(
|
|
581
|
+
12,
|
|
582
|
+
new Series({
|
|
583
|
+
data: new Float32Array([10, 11, 12]),
|
|
584
|
+
timeRange: new TimeRange(10, 12),
|
|
585
|
+
}),
|
|
586
|
+
);
|
|
587
|
+
f.push(
|
|
588
|
+
13,
|
|
589
|
+
new Series({
|
|
590
|
+
data: new Float32Array([13, 14, 15]),
|
|
591
|
+
timeRange: new TimeRange(13, 15),
|
|
592
|
+
}),
|
|
593
|
+
);
|
|
594
|
+
f.push(
|
|
595
|
+
14,
|
|
596
|
+
new Series({
|
|
597
|
+
data: new Float32Array([7, 8, 9]),
|
|
598
|
+
timeRange: new TimeRange(7, 9),
|
|
599
|
+
}),
|
|
600
|
+
);
|
|
601
|
+
const firstValues = new Set<number>();
|
|
602
|
+
f.forEachUnique((_, ms) => {
|
|
603
|
+
firstValues.add(ms.at(0) as number);
|
|
604
|
+
});
|
|
605
|
+
expect(firstValues).toEqual(new Set([1, 4, 7]));
|
|
606
|
+
});
|
|
607
|
+
it("should get all data from the frame", () => {
|
|
608
|
+
const f = new framer.Frame(
|
|
609
|
+
new Map([
|
|
610
|
+
[
|
|
611
|
+
12,
|
|
612
|
+
[
|
|
613
|
+
new Series({
|
|
614
|
+
data: new Float32Array([1, 2, 3]),
|
|
615
|
+
timeRange: new TimeRange(1, 2),
|
|
616
|
+
}),
|
|
617
|
+
],
|
|
618
|
+
],
|
|
619
|
+
]),
|
|
620
|
+
);
|
|
621
|
+
f.push(
|
|
622
|
+
12,
|
|
623
|
+
new Series({
|
|
624
|
+
data: new Float32Array([10, 11, 12]),
|
|
625
|
+
timeRange: new TimeRange(10, 12),
|
|
626
|
+
}),
|
|
627
|
+
);
|
|
628
|
+
f.push(
|
|
629
|
+
13,
|
|
630
|
+
new Series({
|
|
631
|
+
data: new Float32Array([13, 14, 15]),
|
|
632
|
+
timeRange: new TimeRange(13, 15),
|
|
633
|
+
}),
|
|
634
|
+
);
|
|
635
|
+
const data: number[] = [];
|
|
636
|
+
f.forEachUnique((key, ms) => {
|
|
637
|
+
if (key !== 12) return;
|
|
638
|
+
for (let i = 0; i < ms.length; i++) data.push(ms.at(i, true) as number);
|
|
639
|
+
});
|
|
640
|
+
expect(data).toEqual([1, 2, 3, 10, 11, 12]);
|
|
641
|
+
});
|
|
642
|
+
it("should allow use of an index parameter", () => {
|
|
643
|
+
const f = new framer.Frame(
|
|
644
|
+
new Map([
|
|
645
|
+
[
|
|
646
|
+
12,
|
|
647
|
+
[
|
|
648
|
+
new Series({
|
|
649
|
+
data: new Float32Array([1, 2, 3]),
|
|
650
|
+
timeRange: new TimeRange(1, 2),
|
|
651
|
+
}),
|
|
652
|
+
],
|
|
653
|
+
],
|
|
654
|
+
]),
|
|
655
|
+
);
|
|
656
|
+
f.push(
|
|
657
|
+
12,
|
|
658
|
+
new Series({
|
|
659
|
+
data: new Float32Array([10, 11, 12]),
|
|
660
|
+
timeRange: new TimeRange(10, 12),
|
|
661
|
+
}),
|
|
662
|
+
);
|
|
663
|
+
f.push(
|
|
664
|
+
13,
|
|
665
|
+
new Series({
|
|
666
|
+
data: new Float32Array([13, 14, 15]),
|
|
667
|
+
timeRange: new TimeRange(13, 15),
|
|
668
|
+
}),
|
|
669
|
+
);
|
|
670
|
+
let currentIndex = 0;
|
|
671
|
+
f.forEachUnique((_, __, i) => {
|
|
672
|
+
expect(i).toEqual(currentIndex);
|
|
673
|
+
currentIndex++;
|
|
674
|
+
});
|
|
675
|
+
expect(currentIndex).toEqual(2);
|
|
676
|
+
});
|
|
677
|
+
});
|
|
547
678
|
describe("mapFilter", () => {
|
|
548
679
|
it("should filter out items based on the keep boolean", () => {
|
|
549
680
|
const f = new framer.Frame(
|
package/src/framer/frame.ts
CHANGED
|
@@ -386,6 +386,14 @@ export class Frame {
|
|
|
386
386
|
});
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
+
/**
|
|
390
|
+
* Iterates over all unique columns in the frame.
|
|
391
|
+
* @param fn a function that takes a channel key, multi-series, and index.
|
|
392
|
+
*/
|
|
393
|
+
forEachUnique(fn: (k: channel.KeyOrName, ms: MultiSeries, i: number) => void): void {
|
|
394
|
+
this.uniqueColumns.forEach((k, i) => fn(k, this.get(k), i));
|
|
395
|
+
}
|
|
396
|
+
|
|
389
397
|
at(index: number, required: true): Record<channel.KeyOrName, TelemValue>;
|
|
390
398
|
|
|
391
399
|
at(
|
|
@@ -457,11 +465,11 @@ export class Frame {
|
|
|
457
465
|
export const frameZ = z.object({
|
|
458
466
|
keys: z.union([
|
|
459
467
|
z.null().transform<number[]>(() => []),
|
|
460
|
-
z.number().array().
|
|
468
|
+
z.number().array().default([]),
|
|
461
469
|
]),
|
|
462
470
|
series: z.union([
|
|
463
471
|
z.null().transform<z.infer<typeof Series.crudeZ>[]>(() => []),
|
|
464
|
-
Series.crudeZ.array().
|
|
472
|
+
Series.crudeZ.array().default([]),
|
|
465
473
|
]),
|
|
466
474
|
});
|
|
467
475
|
|
package/src/framer/iterator.ts
CHANGED
|
@@ -196,9 +196,9 @@ export class Iterator {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
|
-
* @returns true if the iterator value contains a valid segment, and
|
|
200
|
-
* valid most commonly returns false when the iterator is exhausted or has
|
|
201
|
-
*
|
|
199
|
+
* @returns true if the iterator value contains a valid segment, and false otherwise.
|
|
200
|
+
* valid most commonly returns false when the iterator is exhausted or has accumulated
|
|
201
|
+
* an error.
|
|
202
202
|
*/
|
|
203
203
|
async valid(): Promise<boolean> {
|
|
204
204
|
return await this.execute({ command: Command.Valid });
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
10
|
import { EOF, Unreachable } from "@synnaxlabs/freighter";
|
|
11
|
-
import { id, sleep } from "@synnaxlabs/x";
|
|
12
|
-
import { DataType, Series, TimeSpan, TimeStamp } from "@synnaxlabs/x/telem";
|
|
11
|
+
import { DataType, id, Rate, Series, sleep, TimeSpan, TimeStamp } from "@synnaxlabs/x";
|
|
13
12
|
import { describe, expect, it, test, vi } from "vitest";
|
|
14
13
|
|
|
15
14
|
import { type channel } from "@/channel";
|
|
@@ -114,11 +113,98 @@ describe("Streamer", () => {
|
|
|
114
113
|
});
|
|
115
114
|
});
|
|
116
115
|
|
|
116
|
+
describe("throttling", () => {
|
|
117
|
+
test("throttle at 60Hz", async () => {
|
|
118
|
+
const ch = await newVirtualChannel(client);
|
|
119
|
+
const streamer = await client.openStreamer({
|
|
120
|
+
channels: ch.key,
|
|
121
|
+
throttleRate: 60,
|
|
122
|
+
});
|
|
123
|
+
const writer = await client.openWriter({
|
|
124
|
+
start: TimeStamp.now(),
|
|
125
|
+
channels: ch.key,
|
|
126
|
+
});
|
|
127
|
+
try {
|
|
128
|
+
const startTime = Date.now();
|
|
129
|
+
// Write data rapidly
|
|
130
|
+
for (let i = 0; i < 10; i++) {
|
|
131
|
+
await writer.write(ch.key, new Float64Array([i]));
|
|
132
|
+
await sleep.sleep(TimeSpan.milliseconds(5));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Read frames - should be throttled
|
|
136
|
+
const receivedFrames: Frame[] = [];
|
|
137
|
+
const timeout = Date.now() + 500;
|
|
138
|
+
while (Date.now() < timeout)
|
|
139
|
+
try {
|
|
140
|
+
const frame = await Promise.race([
|
|
141
|
+
streamer.read(),
|
|
142
|
+
sleep.sleep(TimeSpan.milliseconds(100)).then(() => null),
|
|
143
|
+
]);
|
|
144
|
+
if (frame) receivedFrames.push(frame);
|
|
145
|
+
else break;
|
|
146
|
+
} catch {
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
expect(receivedFrames.length).toBeGreaterThan(0);
|
|
151
|
+
const elapsed = Date.now() - startTime;
|
|
152
|
+
// Should take at least the throttle period
|
|
153
|
+
expect(elapsed).toBeGreaterThanOrEqual(16); // ~1/60Hz
|
|
154
|
+
} finally {
|
|
155
|
+
await writer.close();
|
|
156
|
+
streamer.close();
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("no throttling with rate of 0", async () => {
|
|
161
|
+
const ch = await newVirtualChannel(client);
|
|
162
|
+
const streamer = await client.openStreamer({
|
|
163
|
+
channels: ch.key,
|
|
164
|
+
throttleRate: 0,
|
|
165
|
+
});
|
|
166
|
+
const writer = await client.openWriter({
|
|
167
|
+
start: TimeStamp.now(),
|
|
168
|
+
channels: ch.key,
|
|
169
|
+
});
|
|
170
|
+
try {
|
|
171
|
+
await writer.write(ch.key, new Float64Array([1, 2, 3]));
|
|
172
|
+
const d = await streamer.read();
|
|
173
|
+
expect(Array.from(d.get(ch.key))).toEqual([1, 2, 3]);
|
|
174
|
+
} finally {
|
|
175
|
+
await writer.close();
|
|
176
|
+
streamer.close();
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("combine throttling and downsampling", async () => {
|
|
181
|
+
const ch = await newVirtualChannel(client);
|
|
182
|
+
const streamer = await client.openStreamer({
|
|
183
|
+
channels: ch.key,
|
|
184
|
+
downsampleFactor: 2,
|
|
185
|
+
throttleRate: 10,
|
|
186
|
+
});
|
|
187
|
+
const writer = await client.openWriter({
|
|
188
|
+
start: TimeStamp.now(),
|
|
189
|
+
channels: ch.key,
|
|
190
|
+
});
|
|
191
|
+
try {
|
|
192
|
+
await writer.write(ch.key, new Float64Array([1, 2, 3, 4, 5, 6]));
|
|
193
|
+
const d = await streamer.read();
|
|
194
|
+
// Should be downsampled to [1, 3, 5] and throttled
|
|
195
|
+
expect(Array.from(d.get(ch.key))).toEqual([1, 3, 5]);
|
|
196
|
+
} finally {
|
|
197
|
+
await writer.close();
|
|
198
|
+
streamer.close();
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
117
203
|
describe("calculations", () => {
|
|
118
204
|
test("basic calculated channel streaming", async () => {
|
|
119
205
|
// Create a timestamp index channel
|
|
120
206
|
const timeChannel = await client.channels.create({
|
|
121
|
-
name:
|
|
207
|
+
name: id.create(),
|
|
122
208
|
isIndex: true,
|
|
123
209
|
dataType: DataType.TIMESTAMP,
|
|
124
210
|
});
|
|
@@ -139,7 +225,7 @@ describe("Streamer", () => {
|
|
|
139
225
|
|
|
140
226
|
// Create calculated channel that adds the two source channels
|
|
141
227
|
const calcChannel = await client.channels.create({
|
|
142
|
-
name:
|
|
228
|
+
name: id.create(),
|
|
143
229
|
dataType: DataType.FLOAT64,
|
|
144
230
|
virtual: true,
|
|
145
231
|
expression: `return ${channelA.name} + ${channelB.name}`,
|
|
@@ -193,7 +279,7 @@ describe("Streamer", () => {
|
|
|
193
279
|
|
|
194
280
|
// Create calculated channel that adds 5
|
|
195
281
|
const calcChannel = await client.channels.create({
|
|
196
|
-
name:
|
|
282
|
+
name: id.create(),
|
|
197
283
|
dataType: DataType.FLOAT64,
|
|
198
284
|
virtual: true,
|
|
199
285
|
expression: `return ${baseChannel.name} + 5`,
|
|
@@ -232,23 +318,24 @@ describe("Streamer", () => {
|
|
|
232
318
|
test("calculated channel with multiple operations", async () => {
|
|
233
319
|
// Create timestamp channel
|
|
234
320
|
const timeChannel = await client.channels.create({
|
|
235
|
-
name:
|
|
321
|
+
name: id.create(),
|
|
236
322
|
isIndex: true,
|
|
237
323
|
dataType: DataType.TIMESTAMP,
|
|
238
324
|
});
|
|
239
325
|
|
|
240
326
|
// Create source channels
|
|
327
|
+
const names = [id.create(), id.create()];
|
|
241
328
|
const [channelA, channelB] = await client.channels.create([
|
|
242
|
-
{ name:
|
|
243
|
-
{ name:
|
|
329
|
+
{ name: names[0], dataType: DataType.FLOAT64, index: timeChannel.key },
|
|
330
|
+
{ name: names[1], dataType: DataType.FLOAT64, index: timeChannel.key },
|
|
244
331
|
]);
|
|
245
332
|
|
|
246
333
|
// Create calculated channel with multiple operations
|
|
247
334
|
const calcChannel = await client.channels.create({
|
|
248
|
-
name:
|
|
335
|
+
name: id.create(),
|
|
249
336
|
dataType: DataType.FLOAT64,
|
|
250
337
|
virtual: true,
|
|
251
|
-
expression:
|
|
338
|
+
expression: `return (${names[0]} * 2) + (${names[1]} / 2)`,
|
|
252
339
|
});
|
|
253
340
|
|
|
254
341
|
const streamer = await client.openStreamer(calcChannel.key);
|
|
@@ -279,7 +366,7 @@ describe("Streamer", () => {
|
|
|
279
366
|
describe("legacy calculations", async () => {
|
|
280
367
|
it("should correctly execute a calculation with a requires field", async () => {
|
|
281
368
|
const timeChannel = await client.channels.create({
|
|
282
|
-
name:
|
|
369
|
+
name: id.create(),
|
|
283
370
|
isIndex: true,
|
|
284
371
|
dataType: DataType.TIMESTAMP,
|
|
285
372
|
});
|
|
@@ -298,7 +385,7 @@ describe("Streamer", () => {
|
|
|
298
385
|
]);
|
|
299
386
|
|
|
300
387
|
const calcChannel = await client.channels.create({
|
|
301
|
-
name:
|
|
388
|
+
name: id.create(),
|
|
302
389
|
dataType: DataType.FLOAT64,
|
|
303
390
|
virtual: true,
|
|
304
391
|
expression: `return ${channelA.name} + ${channelB.name}`,
|
|
@@ -398,6 +485,7 @@ describe("Streamer", () => {
|
|
|
398
485
|
expect(openMock).toHaveBeenCalledWith({
|
|
399
486
|
...config,
|
|
400
487
|
downsampleFactor: 1,
|
|
488
|
+
throttleRate: new Rate(0),
|
|
401
489
|
});
|
|
402
490
|
await hardened.update([1, 2, 3]);
|
|
403
491
|
expect(streamer.updateMock).toHaveBeenCalledWith([1, 2, 3]);
|
package/src/framer/streamer.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
10
|
import { EOF, type Stream, type WebSocketClient } from "@synnaxlabs/freighter";
|
|
11
|
-
import { breaker, observe, TimeSpan } from "@synnaxlabs/x";
|
|
11
|
+
import { breaker, observe, Rate, TimeSpan } from "@synnaxlabs/x";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
|
|
14
14
|
import { type channel } from "@/channel";
|
|
@@ -18,7 +18,11 @@ import { WSStreamerCodec } from "@/framer/codec";
|
|
|
18
18
|
import { Frame, frameZ } from "@/framer/frame";
|
|
19
19
|
import { StreamProxy } from "@/framer/streamProxy";
|
|
20
20
|
|
|
21
|
-
const reqZ = z.object({
|
|
21
|
+
const reqZ = z.object({
|
|
22
|
+
keys: z.number().array(),
|
|
23
|
+
downsampleFactor: z.int(),
|
|
24
|
+
throttleRate: Rate.z.optional(),
|
|
25
|
+
});
|
|
22
26
|
|
|
23
27
|
/**
|
|
24
28
|
* Request interface for streaming frames from a Synnax cluster.
|
|
@@ -38,10 +42,12 @@ const intermediateStreamerConfigZ = z.object({
|
|
|
38
42
|
/** The channels to stream data from. Can be channel keys, names, or payloads. */
|
|
39
43
|
channels: paramsZ,
|
|
40
44
|
/** Optional factor to downsample the data by. Defaults to 1 (no downsampling). */
|
|
41
|
-
downsampleFactor: z.
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
useHighPerformanceCodec
|
|
45
|
+
downsampleFactor: z.int().default(1),
|
|
46
|
+
/** Optional throttle rate in Hz to limit the rate of frames sent to the client. Defaults to 0 (no throttling). */
|
|
47
|
+
throttleRate: Rate.z.default(new Rate(0)),
|
|
48
|
+
/** useHighPerformanceCodec sets whether the writer will use the Synnax frame encoder
|
|
49
|
+
as opposed to the standard JSON encoding mechanisms for frames. */
|
|
50
|
+
useHighPerformanceCodec: z.boolean().default(true),
|
|
45
51
|
});
|
|
46
52
|
|
|
47
53
|
export const streamerConfigZ = intermediateStreamerConfigZ.or(
|
|
@@ -105,10 +111,16 @@ export const createStreamOpener =
|
|
|
105
111
|
if (cfg.useHighPerformanceCodec)
|
|
106
112
|
client = client.withCodec(new WSStreamerCodec(adapter.codec));
|
|
107
113
|
const stream = await client.stream("/frame/stream", reqZ, resZ);
|
|
108
|
-
const streamer = new CoreStreamer(
|
|
114
|
+
const streamer = new CoreStreamer(
|
|
115
|
+
stream,
|
|
116
|
+
adapter,
|
|
117
|
+
cfg.downsampleFactor,
|
|
118
|
+
cfg.throttleRate,
|
|
119
|
+
);
|
|
109
120
|
stream.send({
|
|
110
121
|
keys: Array.from(adapter.keys),
|
|
111
122
|
downsampleFactor: cfg.downsampleFactor,
|
|
123
|
+
throttleRate: cfg.throttleRate,
|
|
112
124
|
});
|
|
113
125
|
const [, err] = await stream.receive();
|
|
114
126
|
if (err != null) throw err;
|
|
@@ -132,11 +144,18 @@ class CoreStreamer implements Streamer {
|
|
|
132
144
|
private readonly stream: StreamProxy<typeof reqZ, typeof resZ>;
|
|
133
145
|
private readonly adapter: ReadAdapter;
|
|
134
146
|
private readonly downsampleFactor: number;
|
|
147
|
+
private readonly throttleRate: Rate;
|
|
135
148
|
|
|
136
|
-
constructor(
|
|
149
|
+
constructor(
|
|
150
|
+
stream: Stream<typeof reqZ, typeof resZ>,
|
|
151
|
+
adapter: ReadAdapter,
|
|
152
|
+
downsampleFactor: number = 1,
|
|
153
|
+
throttleRate: Rate = new Rate(0),
|
|
154
|
+
) {
|
|
137
155
|
this.stream = new StreamProxy("Streamer", stream);
|
|
138
156
|
this.adapter = adapter;
|
|
139
|
-
this.downsampleFactor =
|
|
157
|
+
this.downsampleFactor = downsampleFactor;
|
|
158
|
+
this.throttleRate = throttleRate;
|
|
140
159
|
}
|
|
141
160
|
|
|
142
161
|
get keys(): channel.Key[] {
|
|
@@ -163,6 +182,7 @@ class CoreStreamer implements Streamer {
|
|
|
163
182
|
this.stream.send({
|
|
164
183
|
keys: Array.from(this.adapter.keys),
|
|
165
184
|
downsampleFactor: this.downsampleFactor,
|
|
185
|
+
throttleRate: this.throttleRate,
|
|
166
186
|
});
|
|
167
187
|
}
|
|
168
188
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import { DataType, TimeRange, TimeSpan, TimeStamp } from "@synnaxlabs/x";
|
|
10
|
+
import { DataType, id, TimeRange, TimeSpan, TimeStamp } from "@synnaxlabs/x";
|
|
11
11
|
import { describe, expect, it, test } from "vitest";
|
|
12
12
|
|
|
13
13
|
import { UnauthorizedError, ValidationError } from "@/errors";
|
|
@@ -42,8 +42,8 @@ describe("Writer", () => {
|
|
|
42
42
|
const channels = await newIndexedPair(client);
|
|
43
43
|
const writer = await client.openWriter({ start: TimeStamp.now(), channels });
|
|
44
44
|
await expect(
|
|
45
|
-
writer.write("
|
|
46
|
-
).rejects.toThrow('Channel "
|
|
45
|
+
writer.write("nonexistent_channel", randomSeries(10, DataType.FLOAT64)),
|
|
46
|
+
).rejects.toThrow('Channel "nonexistent_channel" not found');
|
|
47
47
|
await writer.close();
|
|
48
48
|
});
|
|
49
49
|
|
|
@@ -152,13 +152,13 @@ describe("Writer", () => {
|
|
|
152
152
|
|
|
153
153
|
test("write with out of order timestamp", async () => {
|
|
154
154
|
const indexCh = await client.channels.create({
|
|
155
|
-
name:
|
|
155
|
+
name: id.create(),
|
|
156
156
|
dataType: DataType.TIMESTAMP,
|
|
157
157
|
isIndex: true,
|
|
158
158
|
});
|
|
159
159
|
|
|
160
160
|
const dataCh = await client.channels.create({
|
|
161
|
-
name:
|
|
161
|
+
name: id.create(),
|
|
162
162
|
dataType: DataType.FLOAT64,
|
|
163
163
|
index: indexCh.key,
|
|
164
164
|
});
|
package/src/index.ts
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
10
|
export { access } from "@/access";
|
|
11
|
-
export { policy } from "@/access/policy";
|
|
12
11
|
export { arc } from "@/arc";
|
|
13
12
|
export { channel } from "@/channel";
|
|
14
13
|
export { Channel, isCalculated } from "@/channel/client";
|
|
@@ -22,6 +21,7 @@ export {
|
|
|
22
21
|
} from "@/client";
|
|
23
22
|
export * from "@/connection";
|
|
24
23
|
export { control } from "@/control";
|
|
24
|
+
export { device } from "@/device";
|
|
25
25
|
export {
|
|
26
26
|
AuthError,
|
|
27
27
|
ContiguityError,
|
|
@@ -35,15 +35,14 @@ export {
|
|
|
35
35
|
} from "@/errors";
|
|
36
36
|
export { framer } from "@/framer";
|
|
37
37
|
export { Frame } from "@/framer/frame";
|
|
38
|
-
export { hardware } from "@/hardware";
|
|
39
|
-
export { device } from "@/hardware/device";
|
|
40
|
-
export { rack } from "@/hardware/rack";
|
|
41
|
-
export { task } from "@/hardware/task";
|
|
42
38
|
export { label } from "@/label";
|
|
43
39
|
export { ontology } from "@/ontology";
|
|
44
40
|
export { group } from "@/ontology/group";
|
|
41
|
+
export { rack } from "@/rack";
|
|
45
42
|
export { ranger } from "@/ranger";
|
|
46
43
|
export { status } from "@/status";
|
|
44
|
+
export { task } from "@/task";
|
|
45
|
+
export { createTestClientWithPolicy } from "@/testutil/access";
|
|
47
46
|
export { createTestClient, TEST_CLIENT_PARAMS } from "@/testutil/client";
|
|
48
47
|
export { user } from "@/user";
|
|
49
48
|
export { workspace } from "@/workspace";
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Copyright 2025 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 { describe, expect, it } from "vitest";
|
|
11
|
+
|
|
12
|
+
import { AuthError, NotFoundError } from "@/errors";
|
|
13
|
+
import { label } from "@/label";
|
|
14
|
+
import { createTestClientWithPolicy } from "@/testutil/access";
|
|
15
|
+
import { createTestClient } from "@/testutil/client";
|
|
16
|
+
|
|
17
|
+
const client = createTestClient();
|
|
18
|
+
|
|
19
|
+
describe("label", () => {
|
|
20
|
+
describe("access control", () => {
|
|
21
|
+
it("should deny access when no retrieve policy exists", async () => {
|
|
22
|
+
const userClient = await createTestClientWithPolicy(client, {
|
|
23
|
+
name: "test",
|
|
24
|
+
objects: [],
|
|
25
|
+
actions: [],
|
|
26
|
+
});
|
|
27
|
+
const randomLabel = await client.labels.create({
|
|
28
|
+
name: "test",
|
|
29
|
+
color: "#E774D0",
|
|
30
|
+
});
|
|
31
|
+
await expect(
|
|
32
|
+
userClient.labels.retrieve({ key: randomLabel.key }),
|
|
33
|
+
).rejects.toThrow(AuthError);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should allow the caller to retrieve labels with the correct policy", async () => {
|
|
37
|
+
const userClient = await createTestClientWithPolicy(client, {
|
|
38
|
+
name: "test",
|
|
39
|
+
objects: [label.ontologyID("")],
|
|
40
|
+
actions: ["retrieve"],
|
|
41
|
+
});
|
|
42
|
+
const randomLabel = await client.labels.create({
|
|
43
|
+
name: "test",
|
|
44
|
+
color: "#E774D0",
|
|
45
|
+
});
|
|
46
|
+
const retrieved = await userClient.labels.retrieve({ key: randomLabel.key });
|
|
47
|
+
expect(retrieved.key).toBe(randomLabel.key);
|
|
48
|
+
expect(retrieved.name).toBe(randomLabel.name);
|
|
49
|
+
expect(retrieved.color).toBe(randomLabel.color);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should allow the caller to create labels with the correct policy", async () => {
|
|
53
|
+
const userClient = await createTestClientWithPolicy(client, {
|
|
54
|
+
name: "test",
|
|
55
|
+
objects: [label.ontologyID("")],
|
|
56
|
+
actions: ["create"],
|
|
57
|
+
});
|
|
58
|
+
await userClient.labels.create({
|
|
59
|
+
name: "test",
|
|
60
|
+
color: "#E774D0",
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should deny access when no create policy exists", async () => {
|
|
65
|
+
const userClient = await createTestClientWithPolicy(client, {
|
|
66
|
+
name: "test",
|
|
67
|
+
objects: [label.ontologyID("")],
|
|
68
|
+
actions: [],
|
|
69
|
+
});
|
|
70
|
+
await expect(
|
|
71
|
+
userClient.labels.create({
|
|
72
|
+
name: "test",
|
|
73
|
+
color: "#E774D0",
|
|
74
|
+
}),
|
|
75
|
+
).rejects.toThrow(AuthError);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("should allow the caller to delete labels with the correct policy", async () => {
|
|
79
|
+
const userClient = await createTestClientWithPolicy(client, {
|
|
80
|
+
name: "test",
|
|
81
|
+
objects: [label.ontologyID("")],
|
|
82
|
+
actions: ["delete"],
|
|
83
|
+
});
|
|
84
|
+
const randomLabel = await client.labels.create({
|
|
85
|
+
name: "test",
|
|
86
|
+
color: "#E774D0",
|
|
87
|
+
});
|
|
88
|
+
await userClient.labels.delete(randomLabel.key);
|
|
89
|
+
await expect(
|
|
90
|
+
userClient.labels.retrieve({ key: randomLabel.key }),
|
|
91
|
+
).rejects.toThrow(NotFoundError);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should deny access when no delete policy exists", async () => {
|
|
95
|
+
const userClient = await createTestClientWithPolicy(client, {
|
|
96
|
+
name: "test",
|
|
97
|
+
objects: [label.ontologyID("")],
|
|
98
|
+
actions: [],
|
|
99
|
+
});
|
|
100
|
+
const randomLabel = await client.labels.create({
|
|
101
|
+
name: "test",
|
|
102
|
+
color: "#E774D0",
|
|
103
|
+
});
|
|
104
|
+
await expect(userClient.labels.delete(randomLabel.key)).rejects.toThrow(
|
|
105
|
+
AuthError,
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|