@mondaydotcomorg/monday-authorization 3.5.0-feat-use-profile-for-graph-can-action-in-scope-f1451ab → 3.5.0-feat-shaime-support-entity-attributes-in-authorization-sdk-c9e4cfc

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 (65) hide show
  1. package/dist/attributions-service.d.ts.map +1 -1
  2. package/dist/attributions-service.js +1 -0
  3. package/dist/authorization-attributes-ms-service.d.ts +37 -0
  4. package/dist/authorization-attributes-ms-service.d.ts.map +1 -0
  5. package/dist/authorization-attributes-ms-service.js +232 -0
  6. package/dist/authorization-attributes-service.d.ts +45 -1
  7. package/dist/authorization-attributes-service.d.ts.map +1 -1
  8. package/dist/authorization-attributes-service.js +262 -0
  9. package/dist/clients/graph-api.d.ts.map +1 -1
  10. package/dist/clients/graph-api.js +0 -1
  11. package/dist/constants.d.ts +0 -3
  12. package/dist/constants.d.ts.map +1 -1
  13. package/dist/constants.js +0 -4
  14. package/dist/errors/argument-error.d.ts +4 -0
  15. package/dist/errors/argument-error.d.ts.map +1 -0
  16. package/dist/errors/argument-error.js +11 -0
  17. package/dist/esm/attributions-service.d.ts.map +1 -1
  18. package/dist/esm/attributions-service.mjs +1 -0
  19. package/dist/esm/authorization-attributes-ms-service.d.ts +37 -0
  20. package/dist/esm/authorization-attributes-ms-service.d.ts.map +1 -0
  21. package/dist/esm/authorization-attributes-ms-service.mjs +230 -0
  22. package/dist/esm/authorization-attributes-service.d.ts +45 -1
  23. package/dist/esm/authorization-attributes-service.d.ts.map +1 -1
  24. package/dist/esm/authorization-attributes-service.mjs +263 -1
  25. package/dist/esm/clients/graph-api.d.ts.map +1 -1
  26. package/dist/esm/clients/graph-api.mjs +1 -2
  27. package/dist/esm/constants.d.ts +0 -3
  28. package/dist/esm/constants.d.ts.map +1 -1
  29. package/dist/esm/constants.mjs +1 -5
  30. package/dist/esm/errors/argument-error.d.ts +4 -0
  31. package/dist/esm/errors/argument-error.d.ts.map +1 -0
  32. package/dist/esm/errors/argument-error.mjs +9 -0
  33. package/dist/esm/index.d.ts +5 -0
  34. package/dist/esm/index.d.ts.map +1 -1
  35. package/dist/esm/index.mjs +4 -0
  36. package/dist/esm/resource-attribute-assignment.d.ts +25 -0
  37. package/dist/esm/resource-attribute-assignment.d.ts.map +1 -0
  38. package/dist/esm/resource-attribute-assignment.mjs +60 -0
  39. package/dist/esm/resource-attributes-constants.d.ts +25 -0
  40. package/dist/esm/resource-attributes-constants.d.ts.map +1 -0
  41. package/dist/esm/resource-attributes-constants.mjs +25 -0
  42. package/dist/esm/types/authorization-attributes-contracts.d.ts +19 -3
  43. package/dist/esm/types/authorization-attributes-contracts.d.ts.map +1 -1
  44. package/dist/index.d.ts +5 -0
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +9 -0
  47. package/dist/resource-attribute-assignment.d.ts +25 -0
  48. package/dist/resource-attribute-assignment.d.ts.map +1 -0
  49. package/dist/resource-attribute-assignment.js +62 -0
  50. package/dist/resource-attributes-constants.d.ts +25 -0
  51. package/dist/resource-attributes-constants.d.ts.map +1 -0
  52. package/dist/resource-attributes-constants.js +28 -0
  53. package/dist/types/authorization-attributes-contracts.d.ts +19 -3
  54. package/dist/types/authorization-attributes-contracts.d.ts.map +1 -1
  55. package/package.json +1 -1
  56. package/src/attributions-service.ts +4 -0
  57. package/src/authorization-attributes-ms-service.ts +327 -0
  58. package/src/authorization-attributes-service.ts +325 -0
  59. package/src/clients/graph-api.ts +1 -2
  60. package/src/constants.ts +0 -4
  61. package/src/errors/argument-error.ts +8 -0
  62. package/src/index.ts +16 -0
  63. package/src/resource-attribute-assignment.ts +70 -0
  64. package/src/resource-attributes-constants.ts +27 -0
  65. package/src/types/authorization-attributes-contracts.ts +49 -3
@@ -6,7 +6,14 @@ import {
6
6
  ResourceAttributeAssignment,
7
7
  ResourceAttributeResponse,
8
8
  ResourceAttributesOperation,
9
+ EntityAttributeAssignment,
10
+ EntityAttributeResponse,
11
+ ResourceType,
12
+ EntityType,
13
+ ResourceAttributeKeyType,
14
+ EntityAttributeKeyType,
9
15
  } from './types/authorization-attributes-contracts';
16
+ import { AuthorizationInternalService } from './authorization-internal-service';
10
17
  import { Resource } from './types/general';
11
18
  import { logger } from './authorization-internal-service';
12
19
  import { getAttributionsFromApi } from './attributions-service';
@@ -24,6 +31,8 @@ export class AuthorizationAttributesService {
24
31
  private static API_PATHS = {
25
32
  UPSERT_RESOURCE_ATTRIBUTES: '/attributes/{accountId}/resource',
26
33
  DELETE_RESOURCE_ATTRIBUTES: '/attributes/{accountId}/resource/{resourceType}/{resourceId}',
34
+ UPSERT_ENTITY_ATTRIBUTES: '/attributes/{accountId}/entity',
35
+ DELETE_ENTITY_ATTRIBUTES: '/attributes/{accountId}/entity/{entityType}/{entityId}',
27
36
  } as const;
28
37
  private httpClient: HttpClient;
29
38
  private fetchOptions: RecursivePartial<FetcherConfig>;
@@ -231,4 +240,320 @@ export class AuthorizationAttributesService {
231
240
  return false;
232
241
  }
233
242
  }
243
+
244
+ /**
245
+ * Validates resource attribute assignments array
246
+ */
247
+ private static validateResourceAttributeAssignments(
248
+ assignments: ResourceAttributeAssignment[]
249
+ ): void {
250
+ if (!Array.isArray(assignments)) {
251
+ throw new Error('resourceAttributeAssignments must be an array');
252
+ }
253
+ if (assignments.length === 0) {
254
+ throw new Error('resourceAttributeAssignments must contain at least 1 item');
255
+ }
256
+ if (assignments.length > 100) {
257
+ throw new Error('resourceAttributeAssignments must contain at most 100 items');
258
+ }
259
+ for (let i = 0; i < assignments.length; i++) {
260
+ const assignment = assignments[i];
261
+ if (!assignment.resourceId || typeof assignment.resourceId !== 'number') {
262
+ throw new Error(`resourceAttributeAssignments[${i}].resourceId is required and must be a number`);
263
+ }
264
+ if (!assignment.resourceType || typeof assignment.resourceType !== 'string') {
265
+ throw new Error(`resourceAttributeAssignments[${i}].resourceType is required and must be a string`);
266
+ }
267
+ if (!assignment.key || typeof assignment.key !== 'string') {
268
+ throw new Error(`resourceAttributeAssignments[${i}].key is required and must be a string`);
269
+ }
270
+ if (assignment.value === undefined || typeof assignment.value !== 'string') {
271
+ throw new Error(`resourceAttributeAssignments[${i}].value is required and must be a string`);
272
+ }
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Validates entity attribute assignments array
278
+ */
279
+ private static validateEntityAttributeAssignments(assignments: EntityAttributeAssignment[]): void {
280
+ if (!Array.isArray(assignments)) {
281
+ throw new Error('entityAttributeAssignments must be an array');
282
+ }
283
+ if (assignments.length === 0) {
284
+ throw new Error('entityAttributeAssignments must contain at least 1 item');
285
+ }
286
+ if (assignments.length > 100) {
287
+ throw new Error('entityAttributeAssignments must contain at most 100 items');
288
+ }
289
+ for (let i = 0; i < assignments.length; i++) {
290
+ const assignment = assignments[i];
291
+ if (!assignment.entityId || typeof assignment.entityId !== 'number') {
292
+ throw new Error(`entityAttributeAssignments[${i}].entityId is required and must be a number`);
293
+ }
294
+ if (!assignment.entityType || typeof assignment.entityType !== 'string') {
295
+ throw new Error(`entityAttributeAssignments[${i}].entityType is required and must be a string`);
296
+ }
297
+ if (!assignment.key || typeof assignment.key !== 'string') {
298
+ throw new Error(`entityAttributeAssignments[${i}].key is required and must be a string`);
299
+ }
300
+ if (assignment.value === undefined || typeof assignment.value !== 'string') {
301
+ throw new Error(`entityAttributeAssignments[${i}].value is required and must be a string`);
302
+ }
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Upsert resource attributes synchronously.
308
+ * Matches API endpoint: POST /attributes/:accountId/resource
309
+ * @param accountId The account ID
310
+ * @param resourceAttributeAssignments Array of ResourceAttributeAssignment objects (1-100 items)
311
+ * @returns Promise with response containing affected attributes
312
+ */
313
+ static async upsertResourceAttributesSync(
314
+ accountId: number,
315
+ resourceAttributeAssignments: ResourceAttributeAssignment[]
316
+ ): Promise<ResourceAttributeResponse> {
317
+ // Validate inputs
318
+ if (!Number.isInteger(accountId)) {
319
+ throw new Error(`accountId must be an integer, got: ${accountId}`);
320
+ }
321
+ AuthorizationAttributesService.validateResourceAttributeAssignments(resourceAttributeAssignments);
322
+
323
+ const httpClient = Api.getPart('httpClient');
324
+ if (!httpClient) {
325
+ throw new Error(ERROR_MESSAGES.HTTP_CLIENT_NOT_INITIALIZED);
326
+ }
327
+
328
+ const attributionHeaders = getAttributionsFromApi();
329
+ const path = AuthorizationAttributesService.API_PATHS.UPSERT_RESOURCE_ATTRIBUTES.replace(
330
+ '{accountId}',
331
+ accountId.toString()
332
+ );
333
+
334
+ try {
335
+ return await httpClient.fetch<ResourceAttributeResponse>(
336
+ {
337
+ url: {
338
+ appName: APP_NAME,
339
+ path,
340
+ },
341
+ method: 'POST',
342
+ headers: {
343
+ 'Content-Type': 'application/json',
344
+ ...attributionHeaders,
345
+ },
346
+ body: JSON.stringify({ resourceAttributeAssignments }),
347
+ },
348
+ {
349
+ timeout: AuthorizationInternalService.getRequestTimeout(),
350
+ retryPolicy: AuthorizationInternalService.getRetriesPolicy(),
351
+ }
352
+ );
353
+ } catch (err) {
354
+ if (err instanceof HttpFetcherError) {
355
+ throw new Error(ERROR_MESSAGES.REQUEST_FAILED('upsertResourceAttributesSync', err.status, err.message));
356
+ }
357
+ throw err;
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Delete resource attributes synchronously.
363
+ * Matches API endpoint: DELETE /attributes/:accountId/resource/:resourceType/:resourceId
364
+ * @param accountId The account ID
365
+ * @param resourceType The resource type
366
+ * @param resourceId The resource ID
367
+ * @param keys Array of attribute keys to delete
368
+ * @returns Promise with response containing affected attributes
369
+ */
370
+ static async deleteResourceAttributesSync(
371
+ accountId: number,
372
+ resourceType: ResourceType,
373
+ resourceId: number,
374
+ keys: ResourceAttributeKeyType[]
375
+ ): Promise<ResourceAttributeResponse> {
376
+ // Validate inputs
377
+ if (!Number.isInteger(accountId)) {
378
+ throw new Error(`accountId must be an integer, got: ${accountId}`);
379
+ }
380
+ if (!resourceType || typeof resourceType !== 'string') {
381
+ throw new Error(`resourceType must be a string, got: ${typeof resourceType}`);
382
+ }
383
+ if (!Number.isInteger(resourceId)) {
384
+ throw new Error(`resourceId must be an integer, got: ${resourceId}`);
385
+ }
386
+ if (!Array.isArray(keys)) {
387
+ throw new Error('keys must be an array');
388
+ }
389
+ if (keys.length === 0) {
390
+ throw new Error('keys must contain at least 1 item');
391
+ }
392
+
393
+ const httpClient = Api.getPart('httpClient');
394
+ if (!httpClient) {
395
+ throw new Error(ERROR_MESSAGES.HTTP_CLIENT_NOT_INITIALIZED);
396
+ }
397
+
398
+ const attributionHeaders = getAttributionsFromApi();
399
+ const path = AuthorizationAttributesService.API_PATHS.DELETE_RESOURCE_ATTRIBUTES.replace(
400
+ '{accountId}',
401
+ accountId.toString()
402
+ )
403
+ .replace('{resourceType}', resourceType)
404
+ .replace('{resourceId}', resourceId.toString());
405
+
406
+ try {
407
+ return await httpClient.fetch<ResourceAttributeResponse>(
408
+ {
409
+ url: {
410
+ appName: APP_NAME,
411
+ path,
412
+ },
413
+ method: 'DELETE',
414
+ headers: {
415
+ 'Content-Type': 'application/json',
416
+ ...attributionHeaders,
417
+ },
418
+ body: JSON.stringify({ keys }),
419
+ },
420
+ {
421
+ timeout: AuthorizationInternalService.getRequestTimeout(),
422
+ retryPolicy: AuthorizationInternalService.getRetriesPolicy(),
423
+ }
424
+ );
425
+ } catch (err) {
426
+ if (err instanceof HttpFetcherError) {
427
+ throw new Error(ERROR_MESSAGES.REQUEST_FAILED('deleteResourceAttributesSync', err.status, err.message));
428
+ }
429
+ throw err;
430
+ }
431
+ }
432
+
433
+ /**
434
+ * Upsert entity attributes synchronously.
435
+ * Matches API endpoint: POST /attributes/:accountId/entity
436
+ * @param accountId The account ID
437
+ * @param entityAttributeAssignments Array of EntityAttributeAssignment objects (1-100 items)
438
+ * @returns Promise with response containing affected attributes
439
+ */
440
+ static async upsertEntityAttributesSync(
441
+ accountId: number,
442
+ entityAttributeAssignments: EntityAttributeAssignment[]
443
+ ): Promise<EntityAttributeResponse> {
444
+ // Validate inputs
445
+ if (!Number.isInteger(accountId)) {
446
+ throw new Error(`accountId must be an integer, got: ${accountId}`);
447
+ }
448
+ AuthorizationAttributesService.validateEntityAttributeAssignments(entityAttributeAssignments);
449
+
450
+ const httpClient = Api.getPart('httpClient');
451
+ if (!httpClient) {
452
+ throw new Error(ERROR_MESSAGES.HTTP_CLIENT_NOT_INITIALIZED);
453
+ }
454
+
455
+ const attributionHeaders = getAttributionsFromApi();
456
+ const path = AuthorizationAttributesService.API_PATHS.UPSERT_ENTITY_ATTRIBUTES.replace(
457
+ '{accountId}',
458
+ accountId.toString()
459
+ );
460
+
461
+ try {
462
+ return await httpClient.fetch<EntityAttributeResponse>(
463
+ {
464
+ url: {
465
+ appName: APP_NAME,
466
+ path,
467
+ },
468
+ method: 'POST',
469
+ headers: {
470
+ 'Content-Type': 'application/json',
471
+ ...attributionHeaders,
472
+ },
473
+ body: JSON.stringify({ entityAttributeAssignments }),
474
+ },
475
+ {
476
+ timeout: AuthorizationInternalService.getRequestTimeout(),
477
+ retryPolicy: AuthorizationInternalService.getRetriesPolicy(),
478
+ }
479
+ );
480
+ } catch (err) {
481
+ if (err instanceof HttpFetcherError) {
482
+ throw new Error(ERROR_MESSAGES.REQUEST_FAILED('upsertEntityAttributesSync', err.status, err.message));
483
+ }
484
+ throw err;
485
+ }
486
+ }
487
+
488
+ /**
489
+ * Delete entity attributes synchronously.
490
+ * Matches API endpoint: DELETE /attributes/:accountId/entity/:entityType/:entityId
491
+ * @param accountId The account ID
492
+ * @param entityType The entity type
493
+ * @param entityId The entity ID
494
+ * @param keys Array of attribute keys to delete
495
+ * @returns Promise with response containing affected attributes
496
+ */
497
+ static async deleteEntityAttributesSync(
498
+ accountId: number,
499
+ entityType: EntityType,
500
+ entityId: number,
501
+ keys: EntityAttributeKeyType[]
502
+ ): Promise<EntityAttributeResponse> {
503
+ // Validate inputs
504
+ if (!Number.isInteger(accountId)) {
505
+ throw new Error(`accountId must be an integer, got: ${accountId}`);
506
+ }
507
+ if (!entityType || typeof entityType !== 'string') {
508
+ throw new Error(`entityType must be a string, got: ${typeof entityType}`);
509
+ }
510
+ if (!Number.isInteger(entityId)) {
511
+ throw new Error(`entityId must be an integer, got: ${entityId}`);
512
+ }
513
+ if (!Array.isArray(keys)) {
514
+ throw new Error('keys must be an array');
515
+ }
516
+ if (keys.length === 0) {
517
+ throw new Error('keys must contain at least 1 item');
518
+ }
519
+
520
+ const httpClient = Api.getPart('httpClient');
521
+ if (!httpClient) {
522
+ throw new Error(ERROR_MESSAGES.HTTP_CLIENT_NOT_INITIALIZED);
523
+ }
524
+
525
+ const attributionHeaders = getAttributionsFromApi();
526
+ const path = AuthorizationAttributesService.API_PATHS.DELETE_ENTITY_ATTRIBUTES.replace(
527
+ '{accountId}',
528
+ accountId.toString()
529
+ )
530
+ .replace('{entityType}', entityType)
531
+ .replace('{entityId}', entityId.toString());
532
+
533
+ try {
534
+ return await httpClient.fetch<EntityAttributeResponse>(
535
+ {
536
+ url: {
537
+ appName: APP_NAME,
538
+ path,
539
+ },
540
+ method: 'DELETE',
541
+ headers: {
542
+ 'Content-Type': 'application/json',
543
+ ...attributionHeaders,
544
+ },
545
+ body: JSON.stringify({ keys }),
546
+ },
547
+ {
548
+ timeout: AuthorizationInternalService.getRequestTimeout(),
549
+ retryPolicy: AuthorizationInternalService.getRetriesPolicy(),
550
+ }
551
+ );
552
+ } catch (err) {
553
+ if (err instanceof HttpFetcherError) {
554
+ throw new Error(ERROR_MESSAGES.REQUEST_FAILED('deleteEntityAttributesSync', err.status, err.message));
555
+ }
556
+ throw err;
557
+ }
558
+ }
234
559
  }
@@ -18,7 +18,7 @@ import {
18
18
  GraphPermissionReason,
19
19
  } from '../types/graph-api.types';
20
20
  import { scopeToResource } from '../utils/authorization.utils';
21
- import { GRAPH_APP_NAME, GraphProfile } from '../constants';
21
+ import { GRAPH_APP_NAME } from '../constants';
22
22
  import { handleApiError } from '../utils/api-error-handler';
23
23
 
24
24
  const CAN_ACTION_IN_SCOPE_GRAPH_PATH = '/permissions/is-allowed';
@@ -85,7 +85,6 @@ export class GraphApi {
85
85
  url: {
86
86
  appName: GRAPH_APP_NAME,
87
87
  path: CAN_ACTION_IN_SCOPE_GRAPH_PATH,
88
- profile: GraphProfile.PERMISSION,
89
88
  },
90
89
  method: 'POST',
91
90
  headers: {
package/src/constants.ts CHANGED
@@ -4,10 +4,6 @@ import { FetcherConfig } from '@mondaydotcomorg/trident-backend-api';
4
4
  export const APP_NAME = 'authorization';
5
5
  export const GRAPH_APP_NAME = 'authorization-graph';
6
6
 
7
- export enum GraphProfile {
8
- PERMISSION = 'authorization-graph-permission',
9
- }
10
-
11
7
  export const ERROR_MESSAGES = {
12
8
  HTTP_CLIENT_NOT_INITIALIZED: 'MondayAuthorization: HTTP client is not initialized',
13
9
  REQUEST_FAILED: (method: string, status: number, reason: string) =>
@@ -0,0 +1,8 @@
1
+ export class ArgumentError extends Error {
2
+ constructor(message: string) {
3
+ super(message);
4
+ this.name = 'ArgumentError';
5
+ Object.setPrototypeOf(this, ArgumentError.prototype);
6
+ }
7
+ }
8
+
package/src/index.ts CHANGED
@@ -58,6 +58,22 @@ export {
58
58
  } from './authorization-middleware';
59
59
  export { AuthorizationService, AuthorizeResponse } from './authorization-service';
60
60
  export { AuthorizationAttributesService } from './authorization-attributes-service';
61
+ export { AuthorizationAttributesMsService } from './authorization-attributes-ms-service';
62
+ export { ResourceAttributeAssignment } from './resource-attribute-assignment';
63
+ export { RESOURCE_TYPES, RESOURCE_ATTRIBUTES_CONSTANTS } from './resource-attributes-constants';
64
+ export { ArgumentError } from './errors/argument-error';
65
+ export {
66
+ EntityAttributeAssignment,
67
+ ResourceAttributeResponse,
68
+ EntityAttributeResponse,
69
+ ResourceType,
70
+ EntityType,
71
+ ResourceAttributeKeyType,
72
+ EntityAttributeKeyType,
73
+ CommonAttributeKey,
74
+ ResourceAttributeKey,
75
+ EntityAttributeKey,
76
+ } from './types/authorization-attributes-contracts';
61
77
  export { RolesService } from './roles-service';
62
78
  export { MembershipsService } from './memberships';
63
79
  export { AuthorizationObject, Resource, BaseRequest, ResourceGetter, ContextGetter } from './types/general';
@@ -0,0 +1,70 @@
1
+ import { RESOURCE_TYPES, ResourceType } from './resource-attributes-constants';
2
+ import { ArgumentError } from './errors/argument-error';
3
+
4
+ export class ResourceAttributeAssignment {
5
+ public readonly resourceId: number;
6
+ public readonly resourceType: ResourceType;
7
+ public readonly attributeKey: string;
8
+ public readonly attributeValue: string;
9
+
10
+ constructor(resourceId: number, resourceType: string, attributeKey: string, attributeValue: string) {
11
+ // Validate resourceId
12
+ if (!Number.isInteger(resourceId)) {
13
+ throw new ArgumentError(`resourceId must be an integer, got: ${resourceId}`);
14
+ }
15
+
16
+ // Validate resourceType
17
+ const validResourceTypes = Object.values(RESOURCE_TYPES);
18
+ if (!validResourceTypes.includes(resourceType as ResourceType)) {
19
+ throw new ArgumentError(
20
+ `resourceType must be one of [${validResourceTypes.join(', ')}], got: ${resourceType}`
21
+ );
22
+ }
23
+
24
+ // Validate attributeKey
25
+ if (typeof attributeKey !== 'string') {
26
+ throw new ArgumentError(`attributeKey must be a string, got: ${typeof attributeKey}`);
27
+ }
28
+
29
+ // Validate attributeValue
30
+ if (typeof attributeValue !== 'string') {
31
+ throw new ArgumentError(`attributeValue must be a string, got: ${typeof attributeValue}`);
32
+ }
33
+
34
+ this.resourceId = resourceId;
35
+ this.resourceType = resourceType as ResourceType;
36
+ this.attributeKey = attributeKey;
37
+ this.attributeValue = attributeValue;
38
+ }
39
+
40
+ /**
41
+ * Converts the assignment to hash format with camelCase keys
42
+ * @returns Object with camelCase keys: { resourceId, resourceType, key, value }
43
+ */
44
+ toH(): { resourceId: number; resourceType: string; key: string; value: string } {
45
+ return {
46
+ resourceId: this.resourceId,
47
+ resourceType: this.resourceType,
48
+ key: this.attributeKey,
49
+ value: this.attributeValue,
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Compares two assignments for equality
55
+ * @param other Another ResourceAttributeAssignment instance
56
+ * @returns true if all properties are equal
57
+ */
58
+ equals(other: ResourceAttributeAssignment): boolean {
59
+ if (!(other instanceof ResourceAttributeAssignment)) {
60
+ return false;
61
+ }
62
+ return (
63
+ this.resourceId === other.resourceId &&
64
+ this.resourceType === other.resourceType &&
65
+ this.attributeKey === other.attributeKey &&
66
+ this.attributeValue === other.attributeValue
67
+ );
68
+ }
69
+ }
70
+
@@ -0,0 +1,27 @@
1
+ export const RESOURCE_TYPES = {
2
+ ACCOUNT: 'account',
3
+ ACCOUNT_PRODUCT: 'account_product',
4
+ WORKSPACE: 'workspace',
5
+ BOARD: 'board',
6
+ ITEM: 'item',
7
+ TEAM: 'team',
8
+ OVERVIEW: 'overview',
9
+ DOCUMENT: 'document',
10
+ CRM: 'crm',
11
+ } as const;
12
+
13
+ export const RESOURCE_ATTRIBUTES_CONSTANTS = {
14
+ ACCOUNT_RESOURCE_ATTRIBUTES: {
15
+ ENABLE_MEMBERS_INVITE_FROM_NON_AUTH_DOMAIN: 'enable_members_invite_from_non_auth_domain',
16
+ },
17
+ WORKSPACE_RESOURCE_ATTRIBUTES: {
18
+ IS_DEFAULT_WORKSPACE: 'is_default_workspace',
19
+ },
20
+ BOARD_RESOURCE_ATTRIBUTES: {
21
+ IS_SYNCABLE_CHILD_ENTITY: 'is_syncable_child_entity',
22
+ SYSTEM_ENTITY_TYPE: 'system_entity_type',
23
+ },
24
+ } as const;
25
+
26
+ export type ResourceType = typeof RESOURCE_TYPES[keyof typeof RESOURCE_TYPES];
27
+
@@ -1,16 +1,62 @@
1
1
  import { Resource } from './general';
2
2
 
3
+ // Resource Types - matching the API
4
+ export type ResourceType =
5
+ | 'account'
6
+ | 'account_product'
7
+ | 'workspace'
8
+ | 'board'
9
+ | 'item'
10
+ | 'team'
11
+ | 'overview'
12
+ | 'document'
13
+ | 'crm';
14
+
15
+ // Entity Types - matching the API
16
+ export type EntityType =
17
+ | 'user'
18
+ | 'team'
19
+ | 'account';
20
+
21
+ // Common attribute keys that can be used for both resources and entities
22
+ export type CommonAttributeKey = string;
23
+
24
+ // Resource-specific attribute keys
25
+ export type ResourceAttributeKey = string;
26
+
27
+ // Entity-specific attribute keys
28
+ export type EntityAttributeKey = string;
29
+
30
+ // Combined types for attribute keys
31
+ export type ResourceAttributeKeyType = CommonAttributeKey | ResourceAttributeKey;
32
+ export type EntityAttributeKeyType = CommonAttributeKey | EntityAttributeKey;
33
+
34
+ // Resource Attribute Assignment - matching API contract
3
35
  export interface ResourceAttributeAssignment {
4
- resourceType: Resource['type'];
5
- resourceId: Resource['id'];
6
- key: string;
36
+ resourceId: number;
37
+ resourceType: ResourceType;
38
+ key: ResourceAttributeKeyType;
7
39
  value: string;
8
40
  }
9
41
 
42
+ // Entity Attribute Assignment - matching API contract
43
+ export interface EntityAttributeAssignment {
44
+ entityId: number;
45
+ entityType: EntityType;
46
+ key: EntityAttributeKeyType;
47
+ value: string;
48
+ }
49
+
50
+ // Response types
10
51
  export interface ResourceAttributeResponse {
11
52
  attributes: ResourceAttributeAssignment[];
12
53
  }
13
54
 
55
+ export interface EntityAttributeResponse {
56
+ attributes: EntityAttributeAssignment[];
57
+ }
58
+
59
+ // Legacy types for backward compatibility
14
60
  export interface ResourceAttributeDelete {
15
61
  resourceType: Resource['type'];
16
62
  resourceId: Resource['id'];