@mondaydotcomorg/monday-authorization 3.3.1 → 3.4.0-feature-bashanye-add-membership-create-delete-api-9ba3b16

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 (45) hide show
  1. package/README.md +127 -2
  2. package/dist/authorization-service.js +1 -1
  3. package/dist/clients/graph-api.d.ts.map +1 -1
  4. package/dist/clients/graph-api.js +1 -1
  5. package/dist/esm/authorization-service.mjs +1 -1
  6. package/dist/esm/clients/graph-api.d.ts.map +1 -1
  7. package/dist/esm/clients/graph-api.mjs +1 -1
  8. package/dist/esm/index.d.ts +10 -6
  9. package/dist/esm/index.d.ts.map +1 -1
  10. package/dist/esm/index.mjs +15 -7
  11. package/dist/esm/memberships.d.ts +30 -0
  12. package/dist/esm/memberships.d.ts.map +1 -0
  13. package/dist/esm/memberships.mjs +98 -0
  14. package/dist/esm/metrics-service.d.ts +7 -4
  15. package/dist/esm/metrics-service.d.ts.map +1 -1
  16. package/dist/esm/metrics-service.mjs +34 -16
  17. package/dist/esm/types/memberships.d.ts +42 -0
  18. package/dist/esm/types/memberships.d.ts.map +1 -0
  19. package/dist/esm/types/memberships.mjs +1 -0
  20. package/dist/esm/utils/api-error-handler.d.ts +1 -1
  21. package/dist/esm/utils/api-error-handler.d.ts.map +1 -1
  22. package/dist/esm/utils/api-error-handler.mjs +4 -4
  23. package/dist/index.d.ts +10 -6
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +16 -7
  26. package/dist/memberships.d.ts +30 -0
  27. package/dist/memberships.d.ts.map +1 -0
  28. package/dist/memberships.js +100 -0
  29. package/dist/metrics-service.d.ts +7 -4
  30. package/dist/metrics-service.d.ts.map +1 -1
  31. package/dist/metrics-service.js +34 -16
  32. package/dist/types/memberships.d.ts +42 -0
  33. package/dist/types/memberships.d.ts.map +1 -0
  34. package/dist/types/memberships.js +1 -0
  35. package/dist/utils/api-error-handler.d.ts +1 -1
  36. package/dist/utils/api-error-handler.d.ts.map +1 -1
  37. package/dist/utils/api-error-handler.js +4 -4
  38. package/package.json +1 -1
  39. package/src/authorization-service.ts +1 -1
  40. package/src/clients/graph-api.ts +1 -1
  41. package/src/index.ts +25 -15
  42. package/src/memberships.ts +111 -0
  43. package/src/metrics-service.ts +50 -18
  44. package/src/types/memberships.ts +47 -0
  45. package/src/utils/api-error-handler.ts +9 -5
package/README.md CHANGED
@@ -25,7 +25,7 @@ import * as MondayAuthorization from '@mondaydotcomorg/monday-authorization';
25
25
 
26
26
  ...
27
27
 
28
- MondayAuthorization.init({
28
+ await MondayAuthorization.init({
29
29
  prometheus: getPrometheus(),
30
30
  metrics: {
31
31
  serviceName: process.env.APP_NAME,
@@ -39,7 +39,9 @@ startServer(...)
39
39
  **Recommended** - optionally init authorization with redisClient so the granted feature results will be cached and reduce http calls.
40
40
 
41
41
  - grantedFeatureRedisExpirationInSeconds - (optional), redis TTL for cached granted features, default set to 5 minutes
42
- - metrics - (optional), configure internal DataDog/observability integration. Defaults to `process.env.APP_NAME` as the service name, uses the standard StatsD endpoint (`localhost:8125`) when host/port are not provided, and disables emission automatically in test/development environments (override with `disabled`).
42
+ - metrics - (optional), configure internal DataDog/observability integration. Provide either:
43
+ - `metrics.client` with a pre-initialized StatsD client that exposes `distribution` and `increment`.
44
+ - Or the config fields (`serviceName`, `host`, `port`, `disabled`) to let the SDK initialize `@mondaydotcomorg/monday-observability-kit` for you. Defaults to `process.env.APP_NAME` as the service name, uses the standard StatsD endpoint (`localhost:8125`) when host/port are not provided, and disables emission automatically in test/development environments (override with `disabled`).
43
45
 
44
46
  ### Metrics & Observability
45
47
 
@@ -402,6 +404,129 @@ interface RolesResponse {
402
404
  }
403
405
  ```
404
406
 
407
+ ### Memberships API
408
+
409
+ The Memberships API allows you to manage memberships (role assignments) for entities on resources. Use `MembershipsService` to create/update and delete memberships synchronously.
410
+
411
+ Important note: there is no validations for the user that create/delete memberships on authorization side.
412
+ It's on the caller responsibility to validate the user have the right permission to change these memberships.
413
+
414
+ #### Create/Update Memberships
415
+
416
+ Use `MembershipsService.upsertMemberships` to create or update memberships synchronously:
417
+
418
+ ```ts
419
+ import { MembershipsService, MembershipForCreate } from '@mondaydotcomorg/monday-authorization';
420
+
421
+ const membershipsService = new MembershipsService();
422
+ const accountId = 739630;
423
+ const memberships: MembershipForCreate[] = [
424
+ {
425
+ entityId: 123,
426
+ entityType: 'user',
427
+ resourceId: 456,
428
+ resourceType: 'workspace',
429
+ roleId: 5,
430
+ roleType: 'basic',
431
+ addedById: 789,
432
+ },
433
+ ];
434
+
435
+ const response = await membershipsService.upsertMemberships(accountId, memberships);
436
+ // Returns: { memberships: Membership[] }
437
+ ```
438
+
439
+ **Parameters:**
440
+
441
+ - `accountId` - The account ID
442
+ - `memberships` - Array of `MembershipForCreate` objects
443
+
444
+ #### Delete Memberships
445
+
446
+ Use `MembershipsService.deleteMemberships` to delete memberships synchronously:
447
+
448
+ ```ts
449
+ import { MembershipsService, MembershipForDelete } from '@mondaydotcomorg/monday-authorization';
450
+
451
+ const membershipsService = new MembershipsService();
452
+ const accountId = 739630;
453
+ const memberships: MembershipForDelete[] = [
454
+ {
455
+ entityId: 123,
456
+ entityType: 'user',
457
+ resourceId: 456,
458
+ resourceType: 'workspace',
459
+ },
460
+ ];
461
+
462
+ const response = await membershipsService.deleteMemberships(accountId, memberships);
463
+ // Returns: { memberships: Membership[] }
464
+ ```
465
+
466
+ **Parameters:**
467
+
468
+ - `accountId` - The account ID
469
+ - `memberships` - Array of `MembershipForDelete` objects
470
+
471
+ #### Types
472
+
473
+ The following types are available for working with memberships:
474
+
475
+ ```ts
476
+ import {
477
+ MembershipForCreate,
478
+ MembershipForDelete,
479
+ Membership,
480
+ MembershipCreateResponse,
481
+ MembershipDeleteResponse,
482
+ } from '@mondaydotcomorg/monday-authorization';
483
+
484
+ // MembershipForCreate interface
485
+ interface MembershipForCreate {
486
+ entityId: number;
487
+ entityType: string;
488
+ resourceId: number;
489
+ resourceType: string;
490
+ roleId: number;
491
+ roleType?: string;
492
+ addedById: number;
493
+ }
494
+
495
+ // MembershipForDelete interface
496
+ interface MembershipForDelete {
497
+ entityId?: number;
498
+ entityType: string;
499
+ resourceId?: number;
500
+ resourceType: string;
501
+ }
502
+
503
+ // Membership interface
504
+ interface Membership {
505
+ id: number;
506
+ entityId: number;
507
+ entityType: string;
508
+ resourceId: number;
509
+ resourceType: string;
510
+ roleId: number;
511
+ roleType: string;
512
+ addedById: null | number | undefined;
513
+ hops: number;
514
+ isNewRecord: boolean;
515
+ previousValues: Partial<Membership>;
516
+ walVersion: number | null | undefined;
517
+ }
518
+
519
+ // MembershipCreateResponse interface
520
+ interface MembershipCreateResponse {
521
+ memberships: Membership[];
522
+ }
523
+
524
+ // MembershipDeleteResponse interface
525
+ interface MembershipDeleteResponse {
526
+ memberships: Membership[];
527
+ }
528
+ ```
529
+
405
530
  ## Development
406
531
 
407
532
  ### Local Development and Testing
@@ -133,7 +133,7 @@ class AuthorizationService {
133
133
  const { resourceType } = utils_authorization_utils.scopeToResource(scope);
134
134
  const isAuthorized = obj.permit.can;
135
135
  prometheusService.sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthorized, 200, time);
136
- metricsService.recordAuthorizationTiming(apiType, time);
136
+ metricsService.recordAuthorizationTiming(apiType, time, 'canActionInScopeMultiple');
137
137
  }
138
138
  return scopedActionResponseObjects;
139
139
  }
@@ -1 +1 @@
1
- {"version":3,"file":"graph-api.d.ts","sourceRoot":"","sources":["../../src/clients/graph-api.ts"],"names":[],"mappings":"AACA,OAAO,EACL,YAAY,EACZ,0BAA0B,EAG3B,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAEL,sBAAsB,EAMvB,MAAM,0BAA0B,CAAC;AASlC;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;;IAezC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAyB/B;;OAEG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAgCzG;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAiC1B;;OAEG;IACG,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;IAMxC,OAAO,CAAC,MAAM,CAAC,iBAAiB;CAWjC"}
1
+ {"version":3,"file":"graph-api.d.ts","sourceRoot":"","sources":["../../src/clients/graph-api.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,YAAY,EACZ,0BAA0B,EAG3B,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAEL,sBAAsB,EAMvB,MAAM,0BAA0B,CAAC;AAQlC;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;;IAezC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAyB/B;;OAEG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAgCzG;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAiC1B;;OAEG;IACG,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;IAMxC,OAAO,CAAC,MAAM,CAAC,iBAAiB;CAWjC"}
@@ -1,11 +1,11 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  const tridentBackendApi = require('@mondaydotcomorg/trident-backend-api');
4
+ const mondayJwt = require('@mondaydotcomorg/monday-jwt');
4
5
  const types_scopedActionsContracts = require('../types/scoped-actions-contracts.js');
5
6
  const authorizationInternalService = require('../authorization-internal-service.js');
6
7
  const attributionsService = require('../attributions-service.js');
7
8
  const utils_authorization_utils = require('../utils/authorization.utils.js');
8
- const mondayJwt = require('@mondaydotcomorg/monday-jwt');
9
9
  const constants = require('../constants.js');
10
10
  const utils_apiErrorHandler = require('../utils/api-error-handler.js');
11
11
 
@@ -131,7 +131,7 @@ class AuthorizationService {
131
131
  const { resourceType } = scopeToResource(scope);
132
132
  const isAuthorized = obj.permit.can;
133
133
  sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthorized, 200, time);
134
- recordAuthorizationTiming(apiType, time);
134
+ recordAuthorizationTiming(apiType, time, 'canActionInScopeMultiple');
135
135
  }
136
136
  return scopedActionResponseObjects;
137
137
  }
@@ -1 +1 @@
1
- {"version":3,"file":"graph-api.d.ts","sourceRoot":"","sources":["../../../src/clients/graph-api.ts"],"names":[],"mappings":"AACA,OAAO,EACL,YAAY,EACZ,0BAA0B,EAG3B,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAEL,sBAAsB,EAMvB,MAAM,0BAA0B,CAAC;AASlC;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;;IAezC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAyB/B;;OAEG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAgCzG;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAiC1B;;OAEG;IACG,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;IAMxC,OAAO,CAAC,MAAM,CAAC,iBAAiB;CAWjC"}
1
+ {"version":3,"file":"graph-api.d.ts","sourceRoot":"","sources":["../../../src/clients/graph-api.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,YAAY,EACZ,0BAA0B,EAG3B,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAEL,sBAAsB,EAMvB,MAAM,0BAA0B,CAAC;AAQlC;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;;IAezC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAyB/B;;OAEG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAgCzG;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAiC1B;;OAEG;IACG,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;IAMxC,OAAO,CAAC,MAAM,CAAC,iBAAiB;CAWjC"}
@@ -1,9 +1,9 @@
1
1
  import { Api } from '@mondaydotcomorg/trident-backend-api';
2
+ import { signAuthorizationHeader } from '@mondaydotcomorg/monday-jwt';
2
3
  import { PermitTechnicalReason } from '../types/scoped-actions-contracts.mjs';
3
4
  import { AuthorizationInternalService } from '../authorization-internal-service.mjs';
4
5
  import { getAttributionsFromApi } from '../attributions-service.mjs';
5
6
  import { scopeToResource } from '../utils/authorization.utils.mjs';
6
- import { signAuthorizationHeader } from '@mondaydotcomorg/monday-jwt';
7
7
  import { GRAPH_APP_NAME } from '../constants.mjs';
8
8
  import { handleApiError } from '../utils/api-error-handler.mjs';
9
9
 
@@ -1,22 +1,26 @@
1
1
  import { MondayFetchOptions } from '@mondaydotcomorg/monday-fetch';
2
+ import { MetricsClient } from './metrics-service';
2
3
  import * as TestKit from './testKit';
4
+ interface MetricsInitOptions {
5
+ client?: MetricsClient;
6
+ serviceName?: string;
7
+ host?: string;
8
+ port?: number;
9
+ disabled?: boolean;
10
+ }
3
11
  export interface InitOptions {
4
12
  prometheus?: any;
5
13
  mondayFetchOptions?: MondayFetchOptions;
6
14
  redisClient?: any;
7
15
  grantedFeatureRedisExpirationInSeconds?: number;
8
- metrics?: {
9
- serviceName?: string;
10
- host?: string;
11
- port?: number;
12
- disabled?: boolean;
13
- };
16
+ metrics?: MetricsInitOptions;
14
17
  }
15
18
  export declare function init(options?: InitOptions): Promise<void>;
16
19
  export { authorizationCheckMiddleware, getAuthorizationMiddleware, skipAuthorizationMiddleware, } from './authorization-middleware';
17
20
  export { AuthorizationService, AuthorizeResponse } from './authorization-service';
18
21
  export { AuthorizationAttributesService } from './authorization-attributes-service';
19
22
  export { RolesService } from './roles-service';
23
+ export { MembershipsService } from './memberships';
20
24
  export { AuthorizationObject, Resource, BaseRequest, ResourceGetter, ContextGetter } from './types/general';
21
25
  export { Translation, ScopedAction, ScopedActionResponseObject, ScopedActionPermit, } from './types/scoped-actions-contracts';
22
26
  export { CustomRole, BasicRole, RoleType, RoleCreateRequest, RoleUpdateRequest, RolesResponse } from './types/roles';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAInE,OAAO,KAAK,OAAO,MAAM,WAAW,CAAC;AAErC,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,WAAW,CAAC,EAAE,GAAG,CAAC;IAClB,sCAAsC,CAAC,EAAE,MAAM,CAAC;IAChD,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;CACH;AAED,wBAAsB,IAAI,CAAC,OAAO,GAAE,WAAgB,iBAuBnD;AAED,OAAO,EACL,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,8BAA8B,EAAE,MAAM,oCAAoC,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC5G,OAAO,EACL,WAAW,EACX,YAAY,EACZ,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAErH,OAAO,EAAE,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAGnE,OAAO,EAAqB,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,OAAO,MAAM,WAAW,CAAC;AAErC,UAAU,kBAAkB;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,WAAW,CAAC,EAAE,GAAG,CAAC;IAClB,sCAAsC,CAAC,EAAE,MAAM,CAAC;IAChD,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AAED,wBAAsB,IAAI,CAAC,OAAO,GAAE,WAAgB,iBA6BnD;AAED,OAAO,EACL,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,8BAA8B,EAAE,MAAM,oCAAoC,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC5G,OAAO,EACL,WAAW,EACX,YAAY,EACZ,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAErH,OAAO,EAAE,OAAO,EAAE,CAAC"}
@@ -7,19 +7,27 @@ export { testKit_index as TestKit };
7
7
  export { authorizationCheckMiddleware, getAuthorizationMiddleware, skipAuthorizationMiddleware } from './authorization-middleware.mjs';
8
8
  export { AuthorizationAttributesService } from './authorization-attributes-service.mjs';
9
9
  export { RolesService } from './roles-service.mjs';
10
+ export { MembershipsService } from './memberships.mjs';
10
11
  export { RoleType } from './types/roles.mjs';
11
12
 
12
13
  async function init(options = {}) {
13
14
  if (options.prometheus) {
14
15
  setPrometheus(options.prometheus);
15
16
  }
16
- const resolvedDisabled = options.metrics?.disabled ?? ['test', 'development'].includes((process.env.NODE_ENV ?? '').toLowerCase());
17
- initializeMetrics({
18
- serviceName: options.metrics?.serviceName ?? process.env.APP_NAME ?? 'authorization-sdk',
19
- host: options.metrics?.host,
20
- port: options.metrics?.port,
21
- disabled: resolvedDisabled,
22
- });
17
+ if (options.metrics) {
18
+ if (options.metrics.client) {
19
+ initializeMetrics({ client: options.metrics.client });
20
+ }
21
+ else {
22
+ const resolvedDisabled = options.metrics.disabled ?? ['test', 'development'].includes((process.env.NODE_ENV ?? '').toLowerCase());
23
+ initializeMetrics({
24
+ serviceName: options.metrics.serviceName ?? process.env.APP_NAME ?? 'authorization-sdk',
25
+ host: options.metrics.host,
26
+ port: options.metrics.port,
27
+ disabled: resolvedDisabled,
28
+ });
29
+ }
30
+ }
23
31
  if (options.mondayFetchOptions) {
24
32
  setRequestFetchOptions(options.mondayFetchOptions);
25
33
  }
@@ -0,0 +1,30 @@
1
+ import { FetcherConfig, HttpClient } from '@mondaydotcomorg/trident-backend-api';
2
+ import { RecursivePartial } from '@mondaydotcomorg/monday-fetch-api';
3
+ import { MembershipCreateResponse, MembershipDeleteResponse, MembershipForCreate, MembershipForDelete } from './types/memberships';
4
+ export declare class MembershipsService {
5
+ private static API_PATHS;
6
+ private httpClient;
7
+ private fetchOptions;
8
+ /**
9
+ * Public constructor to create the AuthorizationAttributesService instance.
10
+ * @param httpClient The HTTP client to use for API requests, if not provided, the default HTTP client from Api will be used.
11
+ * @param fetchOptions The fetch options to use for API requests, if not provided, the default fetch options will be used.
12
+ */
13
+ constructor(httpClient?: HttpClient, fetchOptions?: RecursivePartial<FetcherConfig>);
14
+ /**
15
+ * Upsert memberships synchronously, performing http call to the authorization MS to assign the given memberships.
16
+ * @param accountId
17
+ * @param memberships - Array of memberships to upsert
18
+ * @returns MembershipCreateResponse - The affected (created and updated) memberships.
19
+ */
20
+ upsertMemberships(accountId: number, memberships: MembershipForCreate[]): Promise<MembershipCreateResponse>;
21
+ /**
22
+ * Delete memberships synchronously, performing http call to the authorization MS to delete the given memberships.
23
+ * @param accountId
24
+ * @param resource - The resource (resourceType, resourceId) to delete the attributes for.
25
+ * @param attributeKeys - Array of attribute keys to delete for the resource.
26
+ * @returns ResourceAttributeResponse - The affected (deleted) resource attributes assignments in the `attributes` field.
27
+ */
28
+ deleteMemberships(accountId: number, memberships: MembershipForDelete[]): Promise<MembershipDeleteResponse>;
29
+ }
30
+ //# sourceMappingURL=memberships.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memberships.d.ts","sourceRoot":"","sources":["../../src/memberships.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,aAAa,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAM3B,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAC,SAAS,CAGb;IACX,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,YAAY,CAAkC;IAEtD;;;;OAIG;gBACS,UAAU,CAAC,EAAE,UAAU,EAAE,YAAY,CAAC,EAAE,gBAAgB,CAAC,aAAa,CAAC;IAoBnF;;;;;OAKG;IACG,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC,wBAAwB,CAAC;IA0BjH;;;;;;OAMG;IACG,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC,wBAAwB,CAAC;CAyBlH"}
@@ -0,0 +1,98 @@
1
+ import { Api } from '@mondaydotcomorg/trident-backend-api';
2
+ import { handleApiError } from './utils/api-error-handler.mjs';
3
+ import { getAttributionsFromApi } from './attributions-service.mjs';
4
+ import { ERROR_MESSAGES, DEFAULT_FETCH_OPTIONS, APP_NAME } from './constants.mjs';
5
+
6
+ class MembershipsService {
7
+ static API_PATHS = {
8
+ UPSERT_RESOURCE_ATTRIBUTES: '/memberships/{accountId}',
9
+ DELETE_RESOURCE_ATTRIBUTES: '/memberships/{accountId}',
10
+ };
11
+ httpClient;
12
+ fetchOptions;
13
+ /**
14
+ * Public constructor to create the AuthorizationAttributesService instance.
15
+ * @param httpClient The HTTP client to use for API requests, if not provided, the default HTTP client from Api will be used.
16
+ * @param fetchOptions The fetch options to use for API requests, if not provided, the default fetch options will be used.
17
+ */
18
+ constructor(httpClient, fetchOptions) {
19
+ if (!httpClient) {
20
+ httpClient = Api.getPart('httpClient');
21
+ if (!httpClient) {
22
+ throw new Error(ERROR_MESSAGES.HTTP_CLIENT_NOT_INITIALIZED);
23
+ }
24
+ }
25
+ if (!fetchOptions) {
26
+ fetchOptions = DEFAULT_FETCH_OPTIONS;
27
+ }
28
+ else {
29
+ fetchOptions = {
30
+ ...DEFAULT_FETCH_OPTIONS,
31
+ ...fetchOptions,
32
+ };
33
+ }
34
+ this.httpClient = httpClient;
35
+ this.fetchOptions = fetchOptions;
36
+ }
37
+ /**
38
+ * Upsert memberships synchronously, performing http call to the authorization MS to assign the given memberships.
39
+ * @param accountId
40
+ * @param memberships - Array of memberships to upsert
41
+ * @returns MembershipCreateResponse - The affected (created and updated) memberships.
42
+ */
43
+ async upsertMemberships(accountId, memberships) {
44
+ const attributionHeaders = getAttributionsFromApi();
45
+ try {
46
+ return await this.httpClient.fetch({
47
+ url: {
48
+ appName: APP_NAME,
49
+ path: MembershipsService.API_PATHS.UPSERT_RESOURCE_ATTRIBUTES.replace('{accountId}', accountId.toString()),
50
+ },
51
+ method: 'PUT',
52
+ query: {
53
+ useAStyleRoleId: 'true',
54
+ },
55
+ headers: {
56
+ 'Content-Type': 'application/json',
57
+ ...attributionHeaders,
58
+ },
59
+ body: JSON.stringify({ memberships }),
60
+ }, this.fetchOptions);
61
+ }
62
+ catch (err) {
63
+ return handleApiError(err, 'authorization', 'upsertMemberships');
64
+ }
65
+ }
66
+ /**
67
+ * Delete memberships synchronously, performing http call to the authorization MS to delete the given memberships.
68
+ * @param accountId
69
+ * @param resource - The resource (resourceType, resourceId) to delete the attributes for.
70
+ * @param attributeKeys - Array of attribute keys to delete for the resource.
71
+ * @returns ResourceAttributeResponse - The affected (deleted) resource attributes assignments in the `attributes` field.
72
+ */
73
+ async deleteMemberships(accountId, memberships) {
74
+ const attributionHeaders = getAttributionsFromApi();
75
+ try {
76
+ return await this.httpClient.fetch({
77
+ url: {
78
+ appName: APP_NAME,
79
+ path: MembershipsService.API_PATHS.DELETE_RESOURCE_ATTRIBUTES.replace('{accountId}', accountId.toString()),
80
+ },
81
+ method: 'DELETE',
82
+ query: {
83
+ useAStyleRoleId: 'true',
84
+ },
85
+ headers: {
86
+ 'Content-Type': 'application/json',
87
+ ...attributionHeaders,
88
+ },
89
+ body: JSON.stringify({ memberships }),
90
+ }, this.fetchOptions);
91
+ }
92
+ catch (err) {
93
+ return handleApiError(err, 'authorization', 'deleteMemberships');
94
+ }
95
+ }
96
+ }
97
+
98
+ export { MembershipsService };
@@ -1,12 +1,15 @@
1
- type ApiType = 'platform' | 'graph';
1
+ import { Metric } from '@mondaydotcomorg/monday-observability-kit';
2
+ type ApiType = 'platform' | 'graph' | 'authorization';
3
+ export type MetricsClient = Pick<typeof Metric, 'distribution' | 'increment'>;
2
4
  interface InitializeMetricsOptions {
3
- serviceName: string;
5
+ client?: MetricsClient;
6
+ serviceName?: string;
4
7
  host?: string;
5
8
  port?: number;
6
9
  disabled?: boolean;
7
10
  }
8
11
  export declare function initializeMetrics(options: InitializeMetricsOptions): void;
9
- export declare function recordAuthorizationTiming(apiType: ApiType, duration: number): void;
10
- export declare function recordAuthorizationError(apiType: ApiType, statusCode: number): void;
12
+ export declare function recordAuthorizationTiming(apiType: ApiType, duration: number, placement: string): void;
13
+ export declare function recordAuthorizationError(apiType: ApiType, statusCode: number, placement: string): void;
11
14
  export {};
12
15
  //# sourceMappingURL=metrics-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"metrics-service.d.ts","sourceRoot":"","sources":["../../src/metrics-service.ts"],"names":[],"mappings":"AAGA,KAAK,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;AAEpC,UAAU,wBAAwB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAID,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI,CA4BzE;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAUlF;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAUnF"}
1
+ {"version":3,"file":"metrics-service.d.ts","sourceRoot":"","sources":["../../src/metrics-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAGnE,KAAK,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG,eAAe,CAAC;AAEtD,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,MAAM,EAAE,cAAc,GAAG,WAAW,CAAC,CAAC;AAE9E,UAAU,wBAAwB;IAChC,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAID,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI,CAiCzE;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAoBrG;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAwBtG"}
@@ -1,17 +1,21 @@
1
1
  import { Metric } from '@mondaydotcomorg/monday-observability-kit';
2
2
  import { logger } from './authorization-internal-service.mjs';
3
3
 
4
- let initialized = false;
4
+ let metricsClient = null;
5
5
  function initializeMetrics(options) {
6
- if (initialized) {
6
+ if (metricsClient) {
7
+ return;
8
+ }
9
+ if (options.client) {
10
+ metricsClient = options.client;
7
11
  return;
8
12
  }
9
13
  const { serviceName } = options;
10
14
  if (!serviceName) {
11
- logger.warn({ tag: 'metrics-service' }, 'Metrics initialization skipped: serviceName is missing');
15
+ logger.warn({ tag: 'monday-authorization-sdk' }, 'Metrics initialization skipped: serviceName is missing');
12
16
  return;
13
17
  }
14
- const resolvedHost = options.host ?? process.env.DOGSTATSD_HOST ?? 'localhost';
18
+ const resolvedHost = options.host ?? process.env.HOST_IP ?? 'localhost';
15
19
  const envPort = process.env.DOGSTATSD_PORT ? Number(process.env.DOGSTATSD_PORT) : undefined;
16
20
  const resolvedPort = options.port ?? (Number.isFinite(envPort ?? NaN) ? envPort : undefined) ?? 8125;
17
21
  const resolvedDisabled = options.disabled ?? ['test', 'development'].includes((process.env.NODE_ENV ?? '').toLowerCase());
@@ -22,32 +26,46 @@ function initializeMetrics(options) {
22
26
  port: resolvedPort,
23
27
  disabled: resolvedDisabled,
24
28
  });
25
- initialized = true;
29
+ metricsClient = Metric;
26
30
  }
27
31
  catch (error) {
28
- logger.warn({ tag: 'metrics-service', error }, 'Failed to initialize metrics');
32
+ logger.warn({ tag: 'monday-authorization-sdk', error }, 'Failed to initialize metrics');
29
33
  }
30
34
  }
31
- function recordAuthorizationTiming(apiType, duration) {
32
- if (!initialized) {
35
+ function recordAuthorizationTiming(apiType, duration, placement) {
36
+ if (!metricsClient) {
33
37
  return;
34
38
  }
35
39
  try {
36
- Metric.distribution(`authorization.authorizationCheck.${apiType}.duration`, duration);
40
+ metricsClient.distribution(`authorization.authorizationCheck.${apiType}.${placement}.duration`, duration);
37
41
  }
38
- catch {
39
- // ignore metric emission failures
42
+ catch (error) {
43
+ logger.warn({
44
+ tag: 'monday-authorization-sdk',
45
+ metric: 'authorizationCheckDuration',
46
+ apiType,
47
+ placement,
48
+ duration,
49
+ error,
50
+ }, 'Failed to emit authorization timing metric');
40
51
  }
41
52
  }
42
- function recordAuthorizationError(apiType, statusCode) {
43
- if (!initialized) {
53
+ function recordAuthorizationError(apiType, statusCode, placement) {
54
+ if (!metricsClient) {
44
55
  return;
45
56
  }
46
57
  try {
47
- Metric.increment(`authorization.authorizationCheck.${apiType}.error`, { statusCode: String(statusCode) }, 1);
58
+ metricsClient.increment(`authorization.authorizationCheck.${apiType}.${placement}.error`, { statusCode: String(statusCode) }, 1);
48
59
  }
49
- catch {
50
- // ignore metric emission failures
60
+ catch (error) {
61
+ logger.warn({
62
+ tag: 'monday-authorization-sdk',
63
+ metric: 'authorizationCheckError',
64
+ apiType,
65
+ placement,
66
+ statusCode,
67
+ error,
68
+ }, 'Failed to emit authorization error metric');
51
69
  }
52
70
  }
53
71
 
@@ -0,0 +1,42 @@
1
+ export interface MembershipForCreate {
2
+ entityId: number;
3
+ entityType: string;
4
+ resourceId: number;
5
+ resourceType: string;
6
+ roleId: number;
7
+ roleType?: string;
8
+ addedById: number;
9
+ }
10
+ export interface MembershipForDelete {
11
+ entityId?: number;
12
+ entityType: string;
13
+ resourceId?: number;
14
+ resourceType: string;
15
+ }
16
+ export interface Membership {
17
+ id: number;
18
+ entityId: number;
19
+ entityType: string;
20
+ resourceId: number;
21
+ resourceType: string;
22
+ roleId: number;
23
+ roleType: string;
24
+ addedById: null | number | undefined;
25
+ hops: number;
26
+ isNewRecord: boolean;
27
+ previousValues: Partial<Membership>;
28
+ walVersion: number | null | undefined;
29
+ }
30
+ export interface MembershipCreateResponse {
31
+ memberships: Membership[];
32
+ }
33
+ export interface MembershipDeleteResponse {
34
+ memberships: Membership[];
35
+ }
36
+ export interface MembershipCreateRequest {
37
+ memberships: MembershipForCreate[];
38
+ }
39
+ export interface MembershipDeleteRequest {
40
+ memberships: MembershipForDelete[];
41
+ }
42
+ //# sourceMappingURL=memberships.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memberships.d.ts","sourceRoot":"","sources":["../../../src/types/memberships.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACpC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,mBAAmB,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,mBAAmB,EAAE,CAAC;CACpC"}
@@ -0,0 +1 @@
1
+
@@ -1,2 +1,2 @@
1
- export declare function handleApiError(err: unknown, apiType: 'platform' | 'graph', placement: string): never;
1
+ export declare function handleApiError(err: unknown, apiType: 'platform' | 'graph' | 'authorization', placement: string): never;
2
2
  //# sourceMappingURL=api-error-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-error-handler.d.ts","sourceRoot":"","sources":["../../../src/utils/api-error-handler.ts"],"names":[],"mappings":"AAIA,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,CAgBpG"}
1
+ {"version":3,"file":"api-error-handler.d.ts","sourceRoot":"","sources":["../../../src/utils/api-error-handler.ts"],"names":[],"mappings":"AAIA,wBAAgB,cAAc,CAC5B,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,UAAU,GAAG,OAAO,GAAG,eAAe,EAC/C,SAAS,EAAE,MAAM,GAChB,KAAK,CAgBP"}
@@ -4,13 +4,13 @@ import { recordAuthorizationError } from '../metrics-service.mjs';
4
4
 
5
5
  function handleApiError(err, apiType, placement) {
6
6
  if (err instanceof HttpFetcherError) {
7
- logger.error({ tag: `${apiType}-api`, status: err.status, error: err.message }, `${apiType.charAt(0).toUpperCase() + apiType.slice(1)} API authorization request failed`);
8
- recordAuthorizationError(apiType, err.status);
7
+ logger.error({ tag: `${apiType}-api`, status: err.status, error: err.message }, `${apiType.charAt(0).toUpperCase() + apiType.slice(1)} API ${placement} request failed`);
8
+ recordAuthorizationError(apiType, err.status, placement);
9
9
  AuthorizationInternalService.throwOnHttpError(err.status, placement);
10
10
  }
11
11
  else {
12
- logger.error({ tag: `${apiType}-api`, error: err instanceof Error ? err.message : String(err) }, `${apiType.charAt(0).toUpperCase() + apiType.slice(1)} API authorization request failed`);
13
- recordAuthorizationError(apiType, 500);
12
+ logger.error({ tag: `${apiType}-api`, error: err instanceof Error ? err.message : String(err) }, `${apiType.charAt(0).toUpperCase() + apiType.slice(1)} API ${placement} request failed`);
13
+ recordAuthorizationError(apiType, 500, placement);
14
14
  throw err;
15
15
  }
16
16
  }
package/dist/index.d.ts CHANGED
@@ -1,22 +1,26 @@
1
1
  import { MondayFetchOptions } from '@mondaydotcomorg/monday-fetch';
2
+ import { MetricsClient } from './metrics-service';
2
3
  import * as TestKit from './testKit';
4
+ interface MetricsInitOptions {
5
+ client?: MetricsClient;
6
+ serviceName?: string;
7
+ host?: string;
8
+ port?: number;
9
+ disabled?: boolean;
10
+ }
3
11
  export interface InitOptions {
4
12
  prometheus?: any;
5
13
  mondayFetchOptions?: MondayFetchOptions;
6
14
  redisClient?: any;
7
15
  grantedFeatureRedisExpirationInSeconds?: number;
8
- metrics?: {
9
- serviceName?: string;
10
- host?: string;
11
- port?: number;
12
- disabled?: boolean;
13
- };
16
+ metrics?: MetricsInitOptions;
14
17
  }
15
18
  export declare function init(options?: InitOptions): Promise<void>;
16
19
  export { authorizationCheckMiddleware, getAuthorizationMiddleware, skipAuthorizationMiddleware, } from './authorization-middleware';
17
20
  export { AuthorizationService, AuthorizeResponse } from './authorization-service';
18
21
  export { AuthorizationAttributesService } from './authorization-attributes-service';
19
22
  export { RolesService } from './roles-service';
23
+ export { MembershipsService } from './memberships';
20
24
  export { AuthorizationObject, Resource, BaseRequest, ResourceGetter, ContextGetter } from './types/general';
21
25
  export { Translation, ScopedAction, ScopedActionResponseObject, ScopedActionPermit, } from './types/scoped-actions-contracts';
22
26
  export { CustomRole, BasicRole, RoleType, RoleCreateRequest, RoleUpdateRequest, RolesResponse } from './types/roles';