@synnaxlabs/client 0.29.0 → 0.31.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.
Files changed (115) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/api/client.api.md +615 -261
  3. package/dist/access/client.d.ts +2 -7
  4. package/dist/access/client.d.ts.map +1 -1
  5. package/dist/access/payload.d.ts +7 -102
  6. package/dist/access/payload.d.ts.map +1 -1
  7. package/dist/access/policy/client.d.ts +17 -0
  8. package/dist/access/policy/client.d.ts.map +1 -0
  9. package/dist/access/policy/external.d.ts +3 -0
  10. package/dist/access/policy/external.d.ts.map +1 -0
  11. package/dist/access/policy/index.d.ts +2 -0
  12. package/dist/access/policy/index.d.ts.map +1 -0
  13. package/dist/access/policy/payload.d.ts +163 -0
  14. package/dist/access/policy/payload.d.ts.map +1 -0
  15. package/dist/access/policy/policy.spec.d.ts +2 -0
  16. package/dist/access/policy/policy.spec.d.ts.map +1 -0
  17. package/dist/access/policy/retriever.d.ts +36 -0
  18. package/dist/access/policy/retriever.d.ts.map +1 -0
  19. package/dist/access/policy/writer.d.ts +9 -0
  20. package/dist/access/policy/writer.d.ts.map +1 -0
  21. package/dist/auth/auth.d.ts +6 -30
  22. package/dist/auth/auth.d.ts.map +1 -1
  23. package/dist/channel/payload.d.ts +17 -17
  24. package/dist/channel/payload.d.ts.map +1 -1
  25. package/dist/channel/retriever.d.ts +8 -8
  26. package/dist/client.cjs +31 -21
  27. package/dist/client.js +2962 -2230
  28. package/dist/framer/client.d.ts +4 -1
  29. package/dist/framer/client.d.ts.map +1 -1
  30. package/dist/framer/frame.d.ts +27 -80
  31. package/dist/framer/frame.d.ts.map +1 -1
  32. package/dist/framer/streamer.d.ts +3 -1
  33. package/dist/framer/streamer.d.ts.map +1 -1
  34. package/dist/framer/writer.d.ts +24 -16
  35. package/dist/framer/writer.d.ts.map +1 -1
  36. package/dist/hardware/device/client.d.ts +2 -2
  37. package/dist/hardware/device/payload.d.ts +1 -1
  38. package/dist/hardware/device/payload.d.ts.map +1 -1
  39. package/dist/hardware/rack/payload.d.ts +1 -1
  40. package/dist/hardware/rack/payload.d.ts.map +1 -1
  41. package/dist/hardware/task/client.d.ts +2 -2
  42. package/dist/hardware/task/ni/types.d.ts +16 -16
  43. package/dist/hardware/task/payload.d.ts +13 -13
  44. package/dist/hardware/task/payload.d.ts.map +1 -1
  45. package/dist/index.d.ts +6 -2
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/label/payload.d.ts +1 -1
  48. package/dist/label/payload.d.ts.map +1 -1
  49. package/dist/label/writer.d.ts +5 -5
  50. package/dist/ontology/client.d.ts +32 -30
  51. package/dist/ontology/client.d.ts.map +1 -1
  52. package/dist/ontology/payload.d.ts +62 -63
  53. package/dist/ontology/payload.d.ts.map +1 -1
  54. package/dist/ranger/payload.d.ts +2 -2
  55. package/dist/ranger/payload.d.ts.map +1 -1
  56. package/dist/ranger/writer.d.ts +5 -5
  57. package/dist/user/client.d.ts +13 -3
  58. package/dist/user/client.d.ts.map +1 -1
  59. package/dist/user/payload.d.ts +34 -3
  60. package/dist/user/payload.d.ts.map +1 -1
  61. package/dist/user/retriever.d.ts +21 -0
  62. package/dist/user/retriever.d.ts.map +1 -0
  63. package/dist/user/user.spec.d.ts +2 -0
  64. package/dist/user/user.spec.d.ts.map +1 -0
  65. package/dist/user/writer.d.ts +11 -0
  66. package/dist/user/writer.d.ts.map +1 -0
  67. package/dist/workspace/lineplot/payload.d.ts +1 -1
  68. package/dist/workspace/lineplot/payload.d.ts.map +1 -1
  69. package/dist/workspace/payload.d.ts +1 -1
  70. package/dist/workspace/payload.d.ts.map +1 -1
  71. package/dist/workspace/schematic/client.d.ts.map +1 -1
  72. package/dist/workspace/schematic/payload.d.ts +1 -1
  73. package/dist/workspace/schematic/payload.d.ts.map +1 -1
  74. package/examples/node/package-lock.json +963 -134
  75. package/examples/node/package.json +1 -1
  76. package/package.json +3 -3
  77. package/src/access/client.ts +4 -70
  78. package/src/access/payload.ts +14 -24
  79. package/src/access/policy/client.ts +65 -0
  80. package/src/access/policy/external.ts +11 -0
  81. package/src/access/policy/index.ts +10 -0
  82. package/src/access/policy/payload.ts +45 -0
  83. package/src/access/policy/policy.spec.ts +331 -0
  84. package/src/access/policy/retriever.ts +43 -0
  85. package/src/access/policy/writer.ts +65 -0
  86. package/src/auth/auth.ts +32 -10
  87. package/src/channel/payload.ts +2 -2
  88. package/src/framer/client.ts +7 -1
  89. package/src/framer/frame.spec.ts +21 -12
  90. package/src/framer/frame.ts +9 -24
  91. package/src/framer/streamer.spec.ts +51 -0
  92. package/src/framer/streamer.ts +9 -4
  93. package/src/framer/writer.ts +0 -2
  94. package/src/hardware/device/payload.ts +2 -2
  95. package/src/hardware/rack/payload.ts +2 -2
  96. package/src/hardware/task/payload.ts +2 -2
  97. package/src/index.ts +16 -13
  98. package/src/label/payload.ts +2 -2
  99. package/src/ontology/client.ts +35 -34
  100. package/src/ontology/payload.ts +28 -35
  101. package/src/ranger/payload.ts +5 -7
  102. package/src/setupspecs.ts +2 -2
  103. package/src/user/client.ts +63 -19
  104. package/src/user/payload.ts +14 -7
  105. package/src/user/retriever.ts +41 -0
  106. package/src/user/user.spec.ts +289 -0
  107. package/src/user/writer.ts +91 -0
  108. package/src/workspace/lineplot/payload.ts +2 -2
  109. package/src/workspace/payload.ts +2 -2
  110. package/src/workspace/schematic/client.ts +1 -1
  111. package/src/workspace/schematic/payload.ts +2 -2
  112. package/src/workspace/workspace.spec.ts +1 -1
  113. package/dist/access/access.spec.d.ts +0 -2
  114. package/dist/access/access.spec.d.ts.map +0 -1
  115. package/src/access/access.spec.ts +0 -276
@@ -13,19 +13,18 @@ import { type AsyncTermSearcher } from "@synnaxlabs/x/search";
13
13
  import { z } from "zod";
14
14
 
15
15
  import { QueryError } from "@/errors";
16
- import { framer } from "@/framer";
17
- import { Frame } from "@/framer/frame";
16
+ import { type framer } from "@/framer";
18
17
  import { group } from "@/ontology/group";
19
18
  import {
20
- CrudeID,
19
+ type CrudeID,
21
20
  ID,
22
- IDPayload,
21
+ type IDPayload,
23
22
  idZ,
24
23
  parseRelationship,
25
- RelationshipChange,
26
- RelationshipDirection,
24
+ type RelationshipChange,
25
+ type RelationshipDirection,
27
26
  type Resource,
28
- ResourceChange,
27
+ type ResourceChange,
29
28
  resourceSchemaZ,
30
29
  resourceTypeZ,
31
30
  } from "@/ontology/payload";
@@ -92,12 +91,12 @@ export class Client implements AsyncTermSearcher<string, string, Resource> {
92
91
 
93
92
  /**
94
93
  * Retrieves the resource in the ontology with the given ID.
95
- * @param id The ID of the resource to retrieve.
96
- * @param options Additional options for the retrieval.
97
- * @param options.includeSchema Whether to include the schema of the resource in the
94
+ * @param id - The ID of the resource to retrieve.
95
+ * @param options - Additional options for the retrieval.
96
+ * @param options.includeSchema - Whether to include the schema of the resource in the
98
97
  * results.
99
- * @param options.excludeFieldData Whether to exclude the field data of the resource in
100
- * the results.
98
+ * @param options.excludeFieldData - Whether to exclude the field data of the resource
99
+ * in the results.
101
100
  * @returns The resource with the given ID.
102
101
  * @throws {QueryError} If no resource is found with the given ID.
103
102
  */
@@ -105,12 +104,13 @@ export class Client implements AsyncTermSearcher<string, string, Resource> {
105
104
 
106
105
  /**
107
106
  * Retrieves the resources in the ontology with the given IDs.
108
- * @param ids The IDs of the resources to retrieve.
109
- * @param options Additional options for the retrieval.
110
- * @param options.includeSchema Whether to include the schema of the resources in the
111
- * results.
112
- * @param options.excludeFieldData Whether to exclude the field data of the resources in
107
+ *
108
+ * @param ids - The IDs of the resources to retrieve.
109
+ * @param options - Additional options for the retrieval.
110
+ * @param options.includeSchema - Whether to include the schema of the resources in
113
111
  * the results.
112
+ * @param options.excludeFieldData - Whether to exclude the field data of the
113
+ * resources in the results.
114
114
  * @returns The resources with the given IDs.
115
115
  * @throws {QueryError} If no resource is found with any of the given IDs.
116
116
  */
@@ -164,10 +164,12 @@ export class Client implements AsyncTermSearcher<string, string, Resource> {
164
164
  /**
165
165
  * Retrieves the parents of the resources with the given IDs.
166
166
  *
167
- * @param ids the IDs of the resources whose parents to retrieve
168
- * @param options additional options for the retrieval
169
- * @param options.includeSchema whether to include the schema of the parents in the results
170
- * @param options.excludeFieldData whether to exclude the field data of the parents in the results
167
+ * @param ids - the IDs of the resources whose parents to retrieve
168
+ * @param options - additional options for the retrieval
169
+ * @param options.includeSchema - whether to include the schema of the parents in the
170
+ * results
171
+ * @param options.excludeFieldData - whether to exclude the field data of the parents
172
+ * in the results
171
173
  * @returns the parents of the resources with the given IDs
172
174
  */
173
175
  async retrieveParents(
@@ -179,8 +181,8 @@ export class Client implements AsyncTermSearcher<string, string, Resource> {
179
181
 
180
182
  /**
181
183
  * Adds children to a resource in the ontology.
182
- * @param id The ID of the resource to add children to.
183
- * @param children The IDs of the children to add.
184
+ * @param id - The ID of the resource to add children to.
185
+ * @param children - The IDs of the children to add.
184
186
  */
185
187
  async addChildren(id: CrudeID, ...children: CrudeID[]): Promise<void> {
186
188
  return await this.writer.addChildren(id, ...children);
@@ -188,9 +190,8 @@ export class Client implements AsyncTermSearcher<string, string, Resource> {
188
190
 
189
191
  /**
190
192
  * Removes children from a resource in the ontology.
191
- * @param id The ID of the resource to remove children from.
192
- * @param children The IDs of the children
193
- * to remove.
193
+ * @param id - The ID of the resource to remove children from.
194
+ * @param children - The IDs of the children to remove.
194
195
  */
195
196
  async removeChildren(id: CrudeID, ...children: CrudeID[]): Promise<void> {
196
197
  return await this.writer.removeChildren(id, ...children);
@@ -198,9 +199,9 @@ export class Client implements AsyncTermSearcher<string, string, Resource> {
198
199
 
199
200
  /**
200
201
  * Moves children from one resource to another in the ontology.
201
- * @param from The ID of the resource to move children from.
202
- * @param to The ID of the resource to move children to.
203
- * @param children The IDs of the children to move.
202
+ * @param from - The ID of the resource to move children from.
203
+ * @param to - The ID of the resource to move children to.
204
+ * @param children - The IDs of the children to move.
204
205
  */
205
206
  async moveChildren(
206
207
  from: CrudeID,
@@ -294,7 +295,7 @@ export class ChangeTracker {
294
295
  }
295
296
  }
296
297
 
297
- private async update(frame: Frame): Promise<void> {
298
+ private async update(frame: framer.Frame): Promise<void> {
298
299
  const resSets = await this.parseResourceSets(frame);
299
300
  const resDeletes = this.parseResourceDeletes(frame);
300
301
  const allResources = resSets.concat(resDeletes);
@@ -306,7 +307,7 @@ export class ChangeTracker {
306
307
  this.relationshipObs.notify(relSets.concat(relDeletes));
307
308
  }
308
309
 
309
- private parseRelationshipSets(frame: Frame): RelationshipChange[] {
310
+ private parseRelationshipSets(frame: framer.Frame): RelationshipChange[] {
310
311
  const relationships = frame.get(RELATIONSHIP_SET_NAME);
311
312
  if (relationships.length === 0) return [];
312
313
  return Array.from(relationships.as("string")).map((rel) => ({
@@ -316,7 +317,7 @@ export class ChangeTracker {
316
317
  }));
317
318
  }
318
319
 
319
- private parseRelationshipDeletes(frame: Frame): RelationshipChange[] {
320
+ private parseRelationshipDeletes(frame: framer.Frame): RelationshipChange[] {
320
321
  const relationships = frame.get(RELATIONSHIP_DELETE_NAME);
321
322
  if (relationships.length === 0) return [];
322
323
  return Array.from(relationships.as("string")).map((rel) => ({
@@ -325,7 +326,7 @@ export class ChangeTracker {
325
326
  }));
326
327
  }
327
328
 
328
- private async parseResourceSets(frame: Frame): Promise<ResourceChange[]> {
329
+ private async parseResourceSets(frame: framer.Frame): Promise<ResourceChange[]> {
329
330
  const sets = frame.get(RESOURCE_SET_NAME);
330
331
  if (sets.length === 0) return [];
331
332
  // We should only ever get one series of sets
@@ -343,7 +344,7 @@ export class ChangeTracker {
343
344
  }
344
345
  }
345
346
 
346
- private parseResourceDeletes(frame: Frame): ResourceChange[] {
347
+ private parseResourceDeletes(frame: framer.Frame): ResourceChange[] {
347
348
  const deletes = frame.get(RESOURCE_DELETE_NAME);
348
349
  if (deletes.length === 0) return [];
349
350
  // We should only ever get one series of deletes
@@ -19,12 +19,14 @@ export type RelationshipDelete = change.Delete<Relationship, undefined>;
19
19
 
20
20
  export const resourceTypeZ = z.union([
21
21
  z.literal("label"),
22
+ z.literal("allow_all"),
22
23
  z.literal("builtin"),
23
24
  z.literal("cluster"),
24
25
  z.literal("channel"),
25
26
  z.literal("node"),
26
27
  z.literal("group"),
27
28
  z.literal("range"),
29
+ z.literal("framer"),
28
30
  z.literal("range-alias"),
29
31
  z.literal("user"),
30
32
  z.literal("workspace"),
@@ -35,25 +37,22 @@ export const resourceTypeZ = z.union([
35
37
  z.literal("task"),
36
38
  z.literal("policy"),
37
39
  ]);
38
-
39
40
  export type ResourceType = z.infer<typeof resourceTypeZ>;
40
41
 
41
- export const BuiltinOntologyType = "builtin" as ResourceType;
42
- export const ClusterOntologyType = "cluster" as ResourceType;
43
- export const NodeOntologyType = "node" as ResourceType;
42
+ export const BUILTIN_TYPE: ResourceType = "builtin";
43
+ export const CLUSTER_TYPE: ResourceType = "cluster";
44
+ export const NODE_TYPE: ResourceType = "node";
44
45
 
45
46
  export const idZ = z.object({ type: resourceTypeZ, key: z.string() });
46
-
47
47
  export type IDPayload = z.infer<typeof idZ>;
48
48
 
49
49
  export const stringIDZ = z.string().transform((v) => {
50
50
  const [type, key] = v.split(":");
51
- return { type: type as ResourceType, key };
51
+ return { type: resourceTypeZ.parse(type), key: key ?? "" };
52
52
  });
53
53
 
54
54
  export const crudeIDZ = z.union([stringIDZ, idZ]);
55
-
56
- export type CrudeID = { type: ResourceType; key: string } | string;
55
+ export type CrudeID = z.input<typeof crudeIDZ>;
57
56
 
58
57
  export class ID {
59
58
  type: ResourceType;
@@ -63,35 +62,40 @@ export class ID {
63
62
  if (args instanceof ID) {
64
63
  this.type = args.type;
65
64
  this.key = args.key;
66
- } else if (typeof args === "string") {
65
+ return;
66
+ }
67
+ if (typeof args === "string") {
67
68
  const [type, key] = args.split(":");
68
69
  this.type = type as ResourceType;
69
- this.key = key;
70
- } else {
71
- this.type = args.type;
72
- this.key = args.key;
70
+ this.key = key ?? "";
71
+ return;
73
72
  }
73
+ this.type = args.type;
74
+ this.key = args.key;
74
75
  }
75
76
 
76
77
  toString(): string {
77
78
  return `${this.type}:${this.key}`;
78
79
  }
79
80
 
80
- get payload(): z.infer<typeof idZ> {
81
- return {
82
- type: this.type,
83
- key: this.key,
84
- };
81
+ isType(): boolean {
82
+ return this.key === "";
83
+ }
84
+
85
+ matchesType(type: ResourceType): boolean {
86
+ return this.type === type && this.isType();
87
+ }
88
+
89
+ get payload(): IDPayload {
90
+ return { type: this.type, key: this.key };
85
91
  }
86
92
 
87
- static readonly z = z.union([crudeIDZ, z.instanceof(ID)]).transform((v) => new ID(v));
93
+ static readonly z = z.union([z.instanceof(ID), crudeIDZ.transform((v) => new ID(v))]);
88
94
  }
89
95
 
90
96
  export const Root = new ID({ type: "builtin", key: "root" });
91
97
 
92
- export const schemaFieldZ = z.object({
93
- type: z.number(),
94
- });
98
+ export const schemaFieldZ = z.object({ type: z.number() });
95
99
 
96
100
  export type SchemaField = z.infer<typeof schemaFieldZ>;
97
101
 
@@ -99,7 +103,6 @@ export const schemaZ = z.object({
99
103
  type: resourceTypeZ,
100
104
  fields: z.record(schemaFieldZ),
101
105
  });
102
-
103
106
  export type Schema = z.infer<typeof schemaZ>;
104
107
 
105
108
  export const resourceSchemaZ = z
@@ -109,12 +112,7 @@ export const resourceSchemaZ = z
109
112
  schema: schemaZ.optional().nullable(),
110
113
  data: z.record(z.unknown()).optional().nullable(),
111
114
  })
112
- .transform((resource) => {
113
- return {
114
- key: resource.id.toString(),
115
- ...resource,
116
- };
117
- });
115
+ .transform((resource) => ({ key: resource.id.toString(), ...resource }));
118
116
 
119
117
  export type Resource<T extends UnknownRecord = UnknownRecord> = Omit<
120
118
  z.output<typeof resourceSchemaZ>,
@@ -123,12 +121,7 @@ export type Resource<T extends UnknownRecord = UnknownRecord> = Omit<
123
121
 
124
122
  export type RelationshipDirection = "from" | "to";
125
123
 
126
- export const relationshipSchemaZ = z.object({
127
- from: ID.z,
128
- type: z.string(),
129
- to: ID.z,
130
- });
131
-
124
+ export const relationshipSchemaZ = z.object({ from: ID.z, type: z.string(), to: ID.z });
132
125
  export type Relationship = z.infer<typeof relationshipSchemaZ>;
133
126
 
134
127
  export const parseRelationship = (str: string): Relationship => {
@@ -28,9 +28,7 @@ export const payloadZ = z.object({
28
28
  });
29
29
  export type Payload = z.infer<typeof payloadZ>;
30
30
 
31
- export const newPayloadZ = payloadZ.extend({
32
- key: z.string().uuid().optional(),
33
- });
31
+ export const newPayloadZ = payloadZ.extend({ key: z.string().uuid().optional() });
34
32
  export type NewPayload = z.input<typeof newPayloadZ>;
35
33
 
36
34
  export type ParamAnalysisResult =
@@ -77,11 +75,11 @@ export const analyzeParams = (ranges: Params): ParamAnalysisResult => {
77
75
  } as const as ParamAnalysisResult;
78
76
  };
79
77
 
80
- export const RangeOntologyType = "range" as ontology.ResourceType;
81
- export const RangeAliasOntologyType = "range-alias" as ontology.ResourceType;
78
+ export const ONTOLOGY_TYPE: ontology.ResourceType = "range";
79
+ export const ALIAS_ONTOLOGY_TYPE: ontology.ResourceType = "range-alias";
82
80
 
83
81
  export const rangeOntologyID = (key: Key): ontology.ID =>
84
- new ontology.ID({ type: RangeOntologyType, key: key });
82
+ new ontology.ID({ type: ONTOLOGY_TYPE, key: key });
85
83
 
86
84
  export const rangeAliasOntologyID = (key: Key): ontology.ID =>
87
- new ontology.ID({ type: RangeAliasOntologyType, key: key });
85
+ new ontology.ID({ type: ALIAS_ONTOLOGY_TYPE, key: key });
package/src/setupspecs.ts CHANGED
@@ -11,8 +11,8 @@ import Synnax, { type SynnaxProps } from "@/client";
11
11
 
12
12
  export const HOST = "localhost";
13
13
  export const PORT = 9090;
14
- const USERNAME = "synnax"
15
- const PASSWORD = "seldon"
14
+ const USERNAME = "synnax";
15
+ const PASSWORD = "seldon";
16
16
 
17
17
  export const newClient = (...props: SynnaxProps[]): Synnax => {
18
18
  let _props = {};
@@ -7,31 +7,75 @@
7
7
  // License, use of this software will be governed by the Apache License, Version 2.0,
8
8
  // included in the file licenses/APL.txt.
9
9
 
10
- import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
10
+ import { type UnaryClient } from "@synnaxlabs/freighter";
11
+ import { toArray } from "@synnaxlabs/x";
11
12
 
12
- import { insecureCredentialsZ, tokenResponseZ } from "@/auth/auth";
13
- import { Payload } from "@/user/payload";
14
-
15
- const REGISTER_ENDPOINT = "/user/register";
13
+ import { MultipleFoundError, NotFoundError } from "@/errors";
14
+ import { type Key, type NewUser, type User } from "@/user/payload";
15
+ import { Retriever } from "@/user/retriever";
16
+ import { Writer } from "@/user/writer";
16
17
 
17
18
  export class Client {
18
- private readonly client: UnaryClient;
19
+ private readonly reader: Retriever;
20
+ private readonly writer: Writer;
19
21
 
20
22
  constructor(client: UnaryClient) {
21
- this.client = client;
23
+ this.writer = new Writer(client);
24
+ this.reader = new Retriever(client);
25
+ }
26
+
27
+ async create(user: NewUser): Promise<User>;
28
+
29
+ async create(users: NewUser[]): Promise<User[]>;
30
+
31
+ async create(users: NewUser | NewUser[]): Promise<User | User[]> {
32
+ const isMany = Array.isArray(users);
33
+ const res = await this.writer.create(users);
34
+ return isMany ? res : res[0];
22
35
  }
23
36
 
24
- async register(username: string, password: string): Promise<Payload> {
25
- const { user } = await sendRequired<
26
- typeof insecureCredentialsZ,
27
- typeof tokenResponseZ
28
- >(
29
- this.client,
30
- REGISTER_ENDPOINT,
31
- { username: username, password: password },
32
- insecureCredentialsZ,
33
- tokenResponseZ,
34
- );
35
- return user;
37
+ async changeUsername(key: Key, newUsername: string): Promise<void> {
38
+ await this.writer.changeUsername(key, newUsername);
39
+ }
40
+
41
+ async retrieve(key: Key): Promise<User>;
42
+
43
+ async retrieve(keys: Key[]): Promise<User[]>;
44
+
45
+ async retrieve(keys: Key | Key[]): Promise<User | User[]> {
46
+ const isMany = Array.isArray(keys);
47
+ const res = await this.reader.retrieve({ keys: toArray(keys) });
48
+ if (isMany) return res;
49
+ if (res.length === 0) throw new NotFoundError(`No user with key ${keys} found`);
50
+ if (res.length > 1)
51
+ throw new MultipleFoundError(`Multiple users found with key ${keys}`);
52
+ return res[0];
53
+ }
54
+
55
+ async retrieveByName(username: string): Promise<User>;
56
+
57
+ async retrieveByName(usernames: string[]): Promise<User[]>;
58
+
59
+ async retrieveByName(usernames: string | string[]): Promise<User | User[]> {
60
+ const isMany = Array.isArray(usernames);
61
+ const res = await this.reader.retrieve({ usernames: toArray(usernames) });
62
+ if (isMany) return res;
63
+ if (res.length === 0)
64
+ throw new NotFoundError(`No user with username ${usernames} found`);
65
+ if (res.length > 1)
66
+ throw new MultipleFoundError(`Multiple users found with username ${usernames}`);
67
+ return res[0];
68
+ }
69
+
70
+ async rename(key: Key, firstName?: string, lastName?: string): Promise<void> {
71
+ await this.writer.rename(key, firstName, lastName);
72
+ }
73
+
74
+ async delete(key: Key): Promise<void>;
75
+
76
+ async delete(keys: Key[]): Promise<void>;
77
+
78
+ async delete(keys: Key | Key[]): Promise<void> {
79
+ await this.writer.delete(keys);
36
80
  }
37
81
  }
@@ -12,17 +12,24 @@ import { z } from "zod";
12
12
  import { ontology } from "@/ontology";
13
13
 
14
14
  export const keyZ = z.string().uuid();
15
-
16
15
  export type Key = z.infer<typeof keyZ>;
17
16
 
18
- export const payloadZ = z.object({
19
- key: z.string(),
20
- username: z.string(),
17
+ export const userZ = z.object({
18
+ key: keyZ,
19
+ username: z.string().min(1),
20
+ firstName: z.string(),
21
+ lastName: z.string(),
22
+ rootUser: z.boolean(),
21
23
  });
24
+ export type User = z.infer<typeof userZ>;
22
25
 
23
- export type Payload = z.infer<typeof payloadZ>;
26
+ export const newUserZ = userZ
27
+ .partial({ key: true, firstName: true, lastName: true })
28
+ .omit({ rootUser: true })
29
+ .extend({ password: z.string().min(1) });
30
+ export type NewUser = z.infer<typeof newUserZ>;
24
31
 
25
- export const UserOntologyType = "user" as ontology.ResourceType;
32
+ export const ONTOLOGY_TYPE: ontology.ResourceType = "user";
26
33
 
27
34
  export const ontologyID = (key: Key): ontology.ID =>
28
- new ontology.ID({ type: UserOntologyType, key });
35
+ new ontology.ID({ type: ONTOLOGY_TYPE, key });
@@ -0,0 +1,41 @@
1
+ // Copyright 2024 Synnax Labs, Inc.
2
+ //
3
+ // Use of this software is governed by the Business Source License included in the file
4
+ // licenses/BSL.txt.
5
+ //
6
+ // As of the Change Date specified in that file, in accordance with the Business Source
7
+ // License, use of this software will be governed by the Apache License, Version 2.0,
8
+ // included in the file licenses/APL.txt.
9
+
10
+ import { sendRequired, type UnaryClient } from "@synnaxlabs/freighter";
11
+ import { z } from "zod";
12
+
13
+ import { keyZ, type User, userZ } from "@/user/payload";
14
+ import { nullableArrayZ } from "@/util/zod";
15
+
16
+ const reqZ = z.object({
17
+ keys: keyZ.array().optional(),
18
+ usernames: z.string().array().optional(),
19
+ });
20
+ type Request = z.infer<typeof reqZ>;
21
+ const resZ = z.object({ users: nullableArrayZ(userZ) });
22
+ const ENDPOINT = "/user/retrieve";
23
+
24
+ export class Retriever {
25
+ private readonly client: UnaryClient;
26
+
27
+ constructor(client: UnaryClient) {
28
+ this.client = client;
29
+ }
30
+
31
+ async retrieve(req: Request): Promise<User[]> {
32
+ const res = await sendRequired<typeof reqZ, typeof resZ>(
33
+ this.client,
34
+ ENDPOINT,
35
+ req,
36
+ reqZ,
37
+ resZ,
38
+ );
39
+ return res.users;
40
+ }
41
+ }