@cubist-labs/cubesigner-sdk 0.1.26 → 0.1.77

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 (44) hide show
  1. package/README.md +94 -33
  2. package/dist/src/ethers/index.d.ts +25 -5
  3. package/dist/src/ethers/index.js +58 -16
  4. package/dist/src/fido.d.ts +76 -0
  5. package/dist/src/fido.js +148 -0
  6. package/dist/src/index.d.ts +148 -35
  7. package/dist/src/index.js +320 -53
  8. package/dist/src/key.d.ts +64 -8
  9. package/dist/src/key.js +91 -19
  10. package/dist/src/org.d.ts +98 -9
  11. package/dist/src/org.js +144 -29
  12. package/dist/src/paginator.d.ts +76 -0
  13. package/dist/src/paginator.js +99 -0
  14. package/dist/src/role.d.ts +20 -8
  15. package/dist/src/role.js +7 -5
  16. package/dist/src/schema.d.ts +2395 -393
  17. package/dist/src/schema.js +1 -1
  18. package/dist/src/session/cognito_manager.d.ts +59 -0
  19. package/dist/src/session/cognito_manager.js +111 -0
  20. package/dist/src/session/session_manager.d.ts +15 -0
  21. package/dist/src/session/session_manager.js +21 -2
  22. package/dist/src/session/session_storage.js +1 -1
  23. package/dist/src/session/signer_session_manager.d.ts +24 -12
  24. package/dist/src/session/signer_session_manager.js +45 -20
  25. package/dist/src/signer_session.d.ts +136 -38
  26. package/dist/src/signer_session.js +187 -80
  27. package/dist/src/util.d.ts +20 -0
  28. package/dist/src/util.js +31 -2
  29. package/package.json +12 -7
  30. package/src/ethers/index.ts +88 -16
  31. package/src/fido.ts +166 -0
  32. package/src/index.ts +366 -77
  33. package/src/key.ts +112 -16
  34. package/src/org.ts +200 -37
  35. package/src/paginator.ts +122 -0
  36. package/src/role.ts +24 -11
  37. package/src/schema.ts +2458 -449
  38. package/src/session/{management_session_manager.ts → cognito_manager.ts} +13 -15
  39. package/src/session/session_manager.ts +25 -1
  40. package/src/session/session_storage.ts +1 -1
  41. package/src/session/signer_session_manager.ts +57 -27
  42. package/src/signer_session.ts +266 -89
  43. package/src/util.ts +41 -0
  44. package/src/session/oidc_session_manager.ts +0 -193
package/src/key.ts CHANGED
@@ -7,10 +7,12 @@ export enum Secp256k1 {
7
7
  Evm = "SecpEthAddr", // eslint-disable-line no-unused-vars
8
8
  Btc = "SecpBtc", // eslint-disable-line no-unused-vars
9
9
  BtcTest = "SecpBtcTest", // eslint-disable-line no-unused-vars
10
+ Ava = "SecpAvaAddr", // eslint-disable-line no-unused-vars
11
+ AvaTest = "SecpAvaTestAddr", // eslint-disable-line no-unused-vars
10
12
  }
11
13
 
12
14
  /** BLS key type */
13
- export enum BLS {
15
+ export enum Bls {
14
16
  Eth2Deposited = "BlsPub", // eslint-disable-line no-unused-vars
15
17
  Eth2Inactive = "BlsInactive", // eslint-disable-line no-unused-vars
16
18
  }
@@ -20,16 +22,56 @@ export enum Ed25519 {
20
22
  Solana = "Ed25519SolanaAddr", // eslint-disable-line no-unused-vars
21
23
  Sui = "Ed25519SuiAddr", // eslint-disable-line no-unused-vars
22
24
  Aptos = "Ed25519AptosAddr", // eslint-disable-line no-unused-vars
25
+ Cardano = "Ed25519CardanoAddrVk", // eslint-disable-line no-unused-vars
26
+ Stellar = "Ed25519StellarAddr", // eslint-disable-line no-unused-vars
23
27
  }
24
28
 
29
+ /** Mnemonic key type */
30
+ export const Mnemonic = "Mnemonic" as const;
31
+ export type Mnemonic = typeof Mnemonic;
32
+
33
+ /** Stark key type */
34
+ export const Stark = "Stark" as const;
35
+ export type Stark = typeof Stark;
36
+
25
37
  /** Key type */
26
- export type KeyType = Secp256k1 | BLS | Ed25519;
38
+ export type KeyType = Secp256k1 | Bls | Ed25519 | Mnemonic | Stark;
27
39
 
28
40
  /** Schema key type (i.e., key type at the API level) */
29
41
  type SchemaKeyType = components["schemas"]["KeyType"];
30
42
 
31
43
  type UpdateKeyRequest = components["schemas"]["UpdateKeyRequest"];
32
- type KeyInfo = components["schemas"]["KeyInfo"];
44
+ type KeyInfoApi = components["schemas"]["KeyInfo"];
45
+ type KeyTypeApi = components["schemas"]["KeyType"];
46
+
47
+ /** Additional properties (for backward compatibility) */
48
+ export interface KeyInfo extends KeyInfoApi {
49
+ /** Alias for key_id */
50
+ id: string;
51
+ /** Alias for key_type */
52
+ type: KeyTypeApi;
53
+ /** Alias for material_id */
54
+ materialId: string;
55
+ /** Alias for public_key */
56
+ publicKey: string;
57
+ }
58
+
59
+ /**
60
+ * Define some additional (backward compatibility) properties
61
+ * on a `KeyInfoApi` object returned from the remote end.
62
+ *
63
+ * @param {KeyInfoApi} key Key information returned from the remote end
64
+ * @return {KeyInfo} The same `key` object extended with some derived properties.
65
+ */
66
+ export function toKeyInfo(key: KeyInfoApi): KeyInfo {
67
+ return {
68
+ ...key,
69
+ id: key.key_id,
70
+ type: key.key_type,
71
+ publicKey: key.public_key,
72
+ materialId: key.material_id,
73
+ };
74
+ }
33
75
 
34
76
  /** Signing keys. */
35
77
  export class Key {
@@ -44,9 +86,6 @@ export class Key {
44
86
  * */
45
87
  readonly id: string;
46
88
 
47
- /** The type of key. */
48
- readonly type: KeyType;
49
-
50
89
  /**
51
90
  * A unique identifier specific to the type of key, such as a public key or an ethereum address
52
91
  * @example 0x8e3484687e66cdd26cf04c3647633ab4f3570148
@@ -61,6 +100,12 @@ export class Key {
61
100
  * */
62
101
  readonly publicKey: string;
63
102
 
103
+ /** The type of key. */
104
+ async type(): Promise<KeyType> {
105
+ const data = await this.fetch();
106
+ return fromSchemaKeyType(data.key_type);
107
+ }
108
+
64
109
  /** Is the key enabled? */
65
110
  async enabled(): Promise<boolean> {
66
111
  const data = await this.fetch();
@@ -119,6 +164,13 @@ export class Key {
119
164
  await this.update({ owner });
120
165
  }
121
166
 
167
+ /**
168
+ * Delete this key.
169
+ */
170
+ async delete() {
171
+ await this.#cs.deleteKey(this.orgId, this.id);
172
+ }
173
+
122
174
  // --------------------------------------------------------------------------
123
175
  // -- INTERNAL --------------------------------------------------------------
124
176
  // --------------------------------------------------------------------------
@@ -129,11 +181,10 @@ export class Key {
129
181
  * @param {KeyInfo} data The JSON response from the API server.
130
182
  * @internal
131
183
  * */
132
- constructor(cs: CubeSigner, orgId: string, data: KeyInfo) {
184
+ constructor(cs: CubeSigner, orgId: string, data: KeyInfoApi) {
133
185
  this.#cs = cs;
134
186
  this.orgId = orgId;
135
187
  this.id = data.key_id;
136
- this.type = fromSchemaKeyType(data.key_type);
137
188
  this.materialId = data.material_id;
138
189
  this.publicKey = data.public_key;
139
190
  }
@@ -150,7 +201,7 @@ export class Key {
150
201
  body: request,
151
202
  parseAs: "json",
152
203
  });
153
- return assertOk(resp);
204
+ return toKeyInfo(assertOk(resp));
154
205
  }
155
206
 
156
207
  /** Create new signing keys.
@@ -183,7 +234,42 @@ export class Key {
183
234
  parseAs: "json",
184
235
  });
185
236
  const data = assertOk(resp);
186
- return data.keys.map((k: KeyInfo) => new Key(cs, orgId, k));
237
+ return data.keys.map((k) => new Key(cs, orgId, k));
238
+ }
239
+
240
+ /**
241
+ * Derives a key of a specified type using a supplied derivation path and an existing long-lived mnemonic.
242
+ *
243
+ * The owner of the derived key will be the owner of the mnemonic.
244
+ *
245
+ * @param {CubeSigner} cs The CubeSigner instance to use for key creation.
246
+ * @param {string} orgId The id of the organization to which the key belongs.
247
+ * @param {KeyType} keyType The type of key to create.
248
+ * @param {string[]} derivationPaths Derivation paths from which to derive new keys.
249
+ * @param {string} mnemonicId materialId of mnemonic key used to derive the new key.
250
+ *
251
+ * @return {Key[]} The newly derived keys.
252
+ */
253
+ static async deriveKeys(
254
+ cs: CubeSigner,
255
+ orgId: string,
256
+ keyType: KeyType,
257
+ derivationPaths: string[],
258
+ mnemonicId: string,
259
+ ): Promise<Key[]> {
260
+ const resp = await (
261
+ await cs.management()
262
+ ).put("/v0/org/{org_id}/derive_key", {
263
+ params: { path: { org_id: orgId } },
264
+ body: {
265
+ derivation_path: derivationPaths,
266
+ mnemonic_id: mnemonicId,
267
+ key_type: keyType,
268
+ },
269
+ parseAs: "json",
270
+ });
271
+ const data = assertOk(resp);
272
+ return data.keys.map((k) => new Key(cs, orgId, k));
187
273
  }
188
274
 
189
275
  /** Get a key by id.
@@ -216,7 +302,7 @@ export class Key {
216
302
  parseAs: "json",
217
303
  });
218
304
  const data = assertOk(resp);
219
- return data;
305
+ return toKeyInfo(data);
220
306
  }
221
307
  }
222
308
 
@@ -225,7 +311,7 @@ export class Key {
225
311
  * @return {KeyType} The key type.
226
312
  * @internal
227
313
  * */
228
- function fromSchemaKeyType(ty: SchemaKeyType): KeyType {
314
+ export function fromSchemaKeyType(ty: SchemaKeyType): KeyType {
229
315
  switch (ty) {
230
316
  case "SecpEthAddr":
231
317
  return Secp256k1.Evm;
@@ -233,17 +319,27 @@ function fromSchemaKeyType(ty: SchemaKeyType): KeyType {
233
319
  return Secp256k1.Btc;
234
320
  case "SecpBtcTest":
235
321
  return Secp256k1.BtcTest;
322
+ case "SecpAvaAddr":
323
+ return Secp256k1.Ava;
324
+ case "SecpAvaTestAddr":
325
+ return Secp256k1.AvaTest;
236
326
  case "BlsPub":
237
- return BLS.Eth2Deposited;
327
+ return Bls.Eth2Deposited;
238
328
  case "BlsInactive":
239
- return BLS.Eth2Inactive;
329
+ return Bls.Eth2Inactive;
240
330
  case "Ed25519SolanaAddr":
241
331
  return Ed25519.Solana;
242
332
  case "Ed25519SuiAddr":
243
333
  return Ed25519.Sui;
244
334
  case "Ed25519AptosAddr":
245
335
  return Ed25519.Aptos;
246
- default:
247
- throw new Error(`Unknown key type: ${ty}`);
336
+ case "Ed25519CardanoAddrVk":
337
+ return Ed25519.Cardano;
338
+ case "Ed25519StellarAddr":
339
+ return Ed25519.Stellar;
340
+ case "Stark":
341
+ return Stark;
342
+ case "Mnemonic":
343
+ return Mnemonic;
248
344
  }
249
345
  }
package/src/org.ts CHANGED
@@ -1,14 +1,34 @@
1
- import { CubeSigner, KeyInfo, MfaRequestInfo } from ".";
1
+ import {
2
+ CubeSigner,
3
+ MfaRequestInfo,
4
+ IdentityProof,
5
+ PageOpts,
6
+ Page,
7
+ PageQueryArgs,
8
+ Paginator,
9
+ } from ".";
2
10
  import { components, paths } from "./client";
3
11
  import { assertOk } from "./util";
4
12
  import { KeyType, Key } from "./key";
5
- import { Role, RoleInfo } from "./role";
13
+ import { MfaPolicy, Role, RoleInfo } from "./role";
6
14
 
7
15
  /** Organization id */
8
16
  export type OrgId = string;
9
17
 
10
18
  /** Org-wide policy */
11
- export type OrgPolicy = SourceIpAllowlistPolicy | OriginAllowlistPolicy | MaxDailyUnstakePolicy;
19
+ export type OrgPolicy =
20
+ | SourceIpAllowlistPolicy
21
+ | OidcAuthSourcesPolicy
22
+ | OriginAllowlistPolicy
23
+ | MaxDailyUnstakePolicy;
24
+
25
+ /**
26
+ * Provides an allowlist of OIDC Issuers and audiences that are allowed to authenticate into this org.
27
+ * @example {"OidcAuthSources": { "https://accounts.google.com": [ "1234.apps.googleusercontent.com" ]}}
28
+ */
29
+ export interface OidcAuthSourcesPolicy {
30
+ OidcAuthSources: Record<string, string[]>;
31
+ }
12
32
 
13
33
  /**
14
34
  * Only allow requests from the specified origins.
@@ -44,6 +64,14 @@ type UpdateOrgResponse =
44
64
  export type OidcIdentity = components["schemas"]["OIDCIdentity"];
45
65
  export type MemberRole = components["schemas"]["MemberRole"];
46
66
 
67
+ /** Options for a new OIDC user */
68
+ export interface CreateOidcUserOptions {
69
+ /** The role of an OIDC user, default is "Alien" */
70
+ memberRole?: MemberRole;
71
+ /** Optional MFA policy to associate with the user account */
72
+ mfaPolicy?: MfaPolicy;
73
+ }
74
+
47
75
  /** An organization. */
48
76
  export class Org {
49
77
  readonly #cs: CubeSigner;
@@ -127,6 +155,35 @@ export class Org {
127
155
  return Key.createKeys(this.#cs, this.id, type, count, ownerId);
128
156
  }
129
157
 
158
+ /**
159
+ * Derives a key of the given type using the given derivation path and mnemonic.
160
+ * The owner of the derived key will be the owner of the mnemonic.
161
+ *
162
+ * @param {KeyType} type Type of key to derive from the mnemonic.
163
+ * @param {string} derivationPath Mnemonic derivation path used to generate new key.
164
+ * @param {string} mnemonicId materialId of mnemonic key used to derive the new key.
165
+ *
166
+ * @return {Key} newly derived key.
167
+ */
168
+ async deriveKey(type: KeyType, derivationPath: string, mnemonicId: string): Promise<Key> {
169
+ return (await Key.deriveKeys(this.#cs, this.id, type, [derivationPath], mnemonicId))[0];
170
+ }
171
+
172
+ /**
173
+ * Derives a set of keys of the given type using the given derivation paths and mnemonic.
174
+ *
175
+ * The owner of the derived keys will be the owner of the mnemonic.
176
+ *
177
+ * @param {KeyType} type Type of key to derive from the mnemonic.
178
+ * @param {string[]} derivationPaths Mnemonic derivation paths used to generate new key.
179
+ * @param {string} mnemonicId materialId of mnemonic key used to derive the new key.
180
+ *
181
+ * @return {Key[]} newly derived keys.
182
+ */
183
+ async deriveKeys(type: KeyType, derivationPaths: string[], mnemonicId: string): Promise<Key[]> {
184
+ return await Key.deriveKeys(this.#cs, this.#id, type, derivationPaths, mnemonicId);
185
+ }
186
+
130
187
  /**
131
188
  * Create a new user in the organization and sends an invitation to that user
132
189
  * @param {string} email Email of the user
@@ -150,23 +207,54 @@ export class Org {
150
207
  /**
151
208
  * Create a new OIDC user
152
209
  * @param {OidcIdentity} identity The identity of the OIDC user
153
- * @param {MemberRole} memberRole The type of membership of the new user
210
+ * @param {string} email Email of the OIDC user
211
+ * @param {CreateOidcUserOptions} opts Additional options for new OIDC users
154
212
  * @return {string} User id of the new user
155
213
  */
156
- async createOidcUser(identity: OidcIdentity, memberRole: MemberRole): Promise<string> {
214
+ async createOidcUser(
215
+ identity: OidcIdentity,
216
+ email: string,
217
+ opts: CreateOidcUserOptions = {},
218
+ ): Promise<string> {
157
219
  const resp = await (
158
220
  await this.#cs.management()
159
221
  ).post("/v0/org/{org_id}/users", {
160
222
  params: { path: { org_id: this.id } },
161
223
  body: {
162
224
  identity,
163
- role: memberRole,
225
+ role: opts.memberRole ?? "Alien",
226
+ email: email,
227
+ mfa_policy: opts.mfaPolicy ?? null,
164
228
  },
165
229
  parseAs: "json",
166
230
  });
167
231
  return assertOk(resp).user_id;
168
232
  }
169
233
 
234
+ /**
235
+ * Delete an existing OIDC user
236
+ * @param {OidcIdentity} identity The identity of the OIDC user
237
+ */
238
+ async deleteOidcUser(identity: OidcIdentity) {
239
+ const resp = await (
240
+ await this.#cs.management()
241
+ ).del("/v0/org/{org_id}/users/oidc", {
242
+ params: { path: { org_id: this.id } },
243
+ body: identity,
244
+ parseAs: "json",
245
+ });
246
+ return assertOk(resp);
247
+ }
248
+
249
+ /**
250
+ * Checks if a given proof of OIDC authentication is valid.
251
+ *
252
+ * @param {IdentityProof} proof The proof of authentication.
253
+ */
254
+ async verifyIdentity(proof: IdentityProof) {
255
+ await this.#cs.verifyIdentity(this.id, proof);
256
+ }
257
+
170
258
  /**
171
259
  * List users in the organization
172
260
  * @return {UserIdInfo[]} List of users
@@ -191,20 +279,33 @@ export class Org {
191
279
 
192
280
  /** Get all keys in the org.
193
281
  * @param {KeyType?} type Optional key type to filter list for.
282
+ * @param {PageOpts} page Pagination options. Defaults to fetching the entire result set.
194
283
  * @return {Key} The key.
195
284
  * */
196
- async keys(type?: KeyType): Promise<Key[]> {
197
- const resp = await (
198
- await this.#cs.management()
199
- ).get("/v0/org/{org_id}/keys", {
200
- params: {
201
- path: { org_id: this.id },
202
- query: type ? { key_type: type } : undefined,
203
- },
204
- parseAs: "json",
205
- });
206
- const data = assertOk(resp);
207
- return data.keys.map((k: KeyInfo) => new Key(this.#cs, this.id, k));
285
+ async keys(type?: KeyType, page?: PageOpts): Promise<Key[]> {
286
+ page ??= Page.default();
287
+ const listFn = async (query: PageQueryArgs) => {
288
+ const client = await this.#cs.management();
289
+ const resp = await client.get("/v0/org/{org_id}/keys", {
290
+ params: {
291
+ path: { org_id: this.id },
292
+ query: {
293
+ key_type: type,
294
+ ...query,
295
+ },
296
+ },
297
+ parseAs: "json",
298
+ });
299
+ return assertOk(resp);
300
+ };
301
+ const p = new Paginator(
302
+ page,
303
+ listFn,
304
+ (r) => r.keys,
305
+ (r) => r.last_evaluated_key,
306
+ );
307
+ const keys = await p.fetch();
308
+ return keys.map((k) => new Key(this.#cs, this.id, k));
208
309
  }
209
310
 
210
311
  /** Create a new role.
@@ -223,25 +324,32 @@ export class Org {
223
324
  return Role.getRole(this.#cs, this.id, roleId);
224
325
  }
225
326
 
226
- /** List all roles in the org..
327
+ /**
328
+ * List all roles in the org.
329
+ *
330
+ * @param {PageOpts} page Pagination options. Defaults to fetching the entire result set.
227
331
  * @return {Role[]} The roles.
228
332
  * */
229
- async list(): Promise<Role[]> {
230
- return Org.roles(this.#cs, this.id);
333
+ async listRoles(page?: PageOpts): Promise<Role[]> {
334
+ return Org.roles(this.#cs, this.id, page);
335
+ }
336
+
337
+ /** List all users in the org.
338
+ * @return {User[]} The users.
339
+ * */
340
+ async listUsers(): Promise<UserIdInfo[]> {
341
+ return Org.users(this.#cs, this.id);
231
342
  }
232
343
 
233
344
  /**
234
345
  * Get a pending MFA request by its id.
235
346
  * @param {string} mfaId The id of the MFA request.
236
347
  * @return {Promise<MfaRequestInfo>} The MFA request.
348
+ *
349
+ * @deprecated Use {@link getMfaInfo()} instead.
237
350
  */
238
351
  async mfaGet(mfaId: string): Promise<MfaRequestInfo> {
239
- const resp = await (
240
- await this.#cs.management()
241
- ).get("/v0/org/{org_id}/mfa/{mfa_id}", {
242
- params: { path: { org_id: this.#id, mfa_id: mfaId } },
243
- });
244
- return assertOk(resp);
352
+ return await this.getMfaInfo(mfaId);
245
353
  }
246
354
 
247
355
  /**
@@ -249,8 +357,37 @@ export class Org {
249
357
  *
250
358
  * @param {string} mfaId The id of the MFA request.
251
359
  * @return {Promise<MfaRequestInfo>} The MFA request.
360
+ *
361
+ * @deprecated Use {@link approveMfaRequest()} instead.
252
362
  */
253
363
  async mfaApprove(mfaId: string): Promise<MfaRequestInfo> {
364
+ return await this.approveMfaRequest(mfaId);
365
+ }
366
+
367
+ /**
368
+ * Get a pending MFA request by its id.
369
+ * @param {string} mfaId The id of the MFA request.
370
+ * @return {Promise<MfaRequestInfo>} The MFA request.
371
+ */
372
+ async getMfaInfo(mfaId: string): Promise<MfaRequestInfo> {
373
+ return await this.#cs.mfaGet(this.id, mfaId);
374
+ }
375
+
376
+ /**
377
+ * List pending MFA requests accessible to the current user.
378
+ * @return {Promise<MfaRequestInfo[]>} The MFA requests.
379
+ */
380
+ async listMfaInfos(): Promise<MfaRequestInfo[]> {
381
+ return await this.#cs.mfaList(this.id);
382
+ }
383
+
384
+ /**
385
+ * Approve a pending MFA request.
386
+ *
387
+ * @param {string} mfaId The id of the MFA request.
388
+ * @return {Promise<MfaRequestInfo>} The MFA request.
389
+ */
390
+ async approveMfaRequest(mfaId: string): Promise<MfaRequestInfo> {
254
391
  return Org.mfaApprove(this.#cs, this.#id, mfaId);
255
392
  }
256
393
 
@@ -277,12 +414,7 @@ export class Org {
277
414
  * @return {Promise<MfaRequestInfo>} The result of the MFA request
278
415
  */
279
416
  static async mfaApprove(cs: CubeSigner, orgId: string, mfaId: string): Promise<MfaRequestInfo> {
280
- const resp = await (
281
- await cs.management()
282
- ).patch("/v0/org/{org_id}/mfa/{mfa_id}", {
283
- params: { path: { org_id: orgId, mfa_id: mfaId } },
284
- });
285
- return assertOk(resp);
417
+ return await cs.mfaApprove(orgId, mfaId);
286
418
  }
287
419
 
288
420
  /** Fetch org info.
@@ -317,17 +449,48 @@ export class Org {
317
449
  /** List roles.
318
450
  * @param {CubeSigner} cs The CubeSigner instance to use for signing.
319
451
  * @param {string} orgId The id of the organization to which the role belongs.
320
- * @return {Role} The role.
452
+ * @param {PageOpts} page Pagination options. Defaults to fetching the entire result set.
453
+ * @return {Role[]} Org roles.
454
+ * @internal
455
+ * */
456
+ private static async roles(cs: CubeSigner, orgId: string, page?: PageOpts): Promise<Role[]> {
457
+ page ??= Page.default();
458
+ const listFn = async (query: PageQueryArgs) => {
459
+ const resp = await (
460
+ await cs.management()
461
+ ).get("/v0/org/{org_id}/roles", {
462
+ params: {
463
+ path: { org_id: orgId },
464
+ query,
465
+ },
466
+ parseAs: "json",
467
+ });
468
+ return assertOk(resp);
469
+ };
470
+ const p = new Paginator(
471
+ page,
472
+ listFn,
473
+ (u) => u.roles,
474
+ (u) => u.last_evaluated_key,
475
+ );
476
+ const roles = await p.fetch();
477
+ return roles.map((r: RoleInfo) => new Role(cs, orgId, r));
478
+ }
479
+
480
+ /** List users.
481
+ * @param {CubeSigner} cs The CubeSigner instance to use for signing.
482
+ * @param {string} orgId The id of the organization to which the role belongs.
483
+ * @return {User[]} Org users.
321
484
  * @internal
322
485
  * */
323
- private static async roles(cs: CubeSigner, orgId: string): Promise<Role[]> {
486
+ private static async users(cs: CubeSigner, orgId: string): Promise<UserIdInfo[]> {
324
487
  const resp = await (
325
488
  await cs.management()
326
- ).get("/v0/org/{org_id}/roles", {
489
+ ).get("/v0/org/{org_id}/users", {
327
490
  params: { path: { org_id: orgId } },
328
491
  parseAs: "json",
329
492
  });
330
493
  const data = assertOk(resp);
331
- return data.roles.map((r: RoleInfo) => new Role(cs, orgId, r));
494
+ return data.users;
332
495
  }
333
496
  }
@@ -0,0 +1,122 @@
1
+ /** Pagination options. */
2
+ export interface PageOpts {
3
+ /** Max number of items per page. */
4
+ size?: number;
5
+ /**
6
+ * Starting point (i.e., 'last_evaluated_key' from the previous page).
7
+ * Omit to start from the beginning.
8
+ */
9
+ start?: string;
10
+ /** Iterate until retrieving the entire result set. */
11
+ all: boolean;
12
+ }
13
+
14
+ /** Static constructors for `IPage` */
15
+ export class Page {
16
+ /**
17
+ * The default is to fetch the entire result set
18
+ * (by repeatedly calling the remote endpoint until all pages are retrieved).
19
+ *
20
+ * @return {PageOpts} Pagination options.
21
+ */
22
+ static default(): PageOpts {
23
+ return <PageOpts>{
24
+ all: true,
25
+ };
26
+ }
27
+ }
28
+
29
+ export interface PageQueryArgs {
30
+ /**
31
+ * Max number of items to return per page.
32
+ *
33
+ * The actual number of returned items may be less that this, even if there exist more
34
+ * data in the result set. To reliably determine if more data is left in the result set,
35
+ * inspect the [UnencryptedLastEvalKey] value in the response object.
36
+ */
37
+ "page.size"?: number;
38
+
39
+ /**
40
+ * The start of the page.
41
+ *
42
+ * Omit to start from the beginning; otherwise, only specify the exact
43
+ * value previously returned as 'last_evaluated_key' from the same endpoint.
44
+ */
45
+ "page.start"?: string | null;
46
+ }
47
+
48
+ export type ListFn<U> = (pageQueryArgs: PageQueryArgs) => Promise<U>;
49
+ export type ItemsFn<U, T> = (resp: U) => T[];
50
+ export type LastFn<U> = (resp: U) => string | null | undefined;
51
+
52
+ /**
53
+ * Helper class for fetching paginated results.
54
+ */
55
+ export class Paginator<U, T> {
56
+ readonly #listFn: ListFn<U>;
57
+ readonly #itemsFn: ItemsFn<U, T>;
58
+ readonly #lastFn: LastFn<U>;
59
+ #opts: PageOpts;
60
+ #last: string | null | undefined;
61
+ #done: boolean;
62
+
63
+ /**
64
+ * @param {PageOpts} pageOpts Pagination options
65
+ * @param {ListFn<U>} listFn Calls a remote endpoint that returns a paginated response
66
+ * @param {ItemsFn<U, T>} itemsFn Extracts items from the paginated response
67
+ * @param {LastFn<U>} lastFn Extracts the last evaluated key from the paginated response
68
+ */
69
+ constructor(pageOpts: PageOpts, listFn: ListFn<U>, itemsFn: ItemsFn<U, T>, lastFn: LastFn<U>) {
70
+ this.#listFn = listFn;
71
+ this.#itemsFn = itemsFn;
72
+ this.#lastFn = lastFn;
73
+ this.#opts = pageOpts;
74
+ this.#last = pageOpts.start;
75
+ this.#done = false;
76
+ }
77
+
78
+ /**
79
+ * Fetches either a single page or the entire result set, depending on
80
+ * the `all` property of the pagination options.
81
+ *
82
+ * @return {Promise<T[]>} A single page or the entire result set.
83
+ */
84
+ async fetch(): Promise<T[]> {
85
+ return this.#opts.all ? await this.fetchAll() : await this.fetchPage();
86
+ }
87
+
88
+ /**
89
+ * Fetches a single page of the result set from where it previously left off.
90
+ * Mutates self to remember where it left off.
91
+ *
92
+ * @return {Promise<T[]>} The next page of the result set.
93
+ */
94
+ async fetchPage(): Promise<T[]> {
95
+ if (this.#done) {
96
+ return [];
97
+ }
98
+
99
+ const resp = await this.#listFn({
100
+ "page.size": this.#opts.size,
101
+ "page.start": this.#last,
102
+ });
103
+ this.#last = this.#lastFn(resp);
104
+ this.#done = !this.#last;
105
+ return this.#itemsFn(resp);
106
+ }
107
+
108
+ /**
109
+ * Fetches the entire result set starting from where it previously left off
110
+ * by iterating through the pages returned by the remote end.
111
+ *
112
+ * @return {Promise<T[]>} The entire result set.
113
+ */
114
+ async fetchAll(): Promise<T[]> {
115
+ const result = [];
116
+ while (!this.#done) {
117
+ const items = await this.fetchPage();
118
+ result.push(...items);
119
+ }
120
+ return result;
121
+ }
122
+ }