@mondaydotcomorg/monday-authorization 3.3.0-feat-add-graph-api-routing-support-cb899c0 → 3.3.0-feat-add-graph-api-routing-support-c8d1d84

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 (50) hide show
  1. package/README.md +4 -0
  2. package/dist/authorization-service.d.ts +5 -0
  3. package/dist/authorization-service.d.ts.map +1 -1
  4. package/dist/authorization-service.js +25 -5
  5. package/dist/clients/graph-api.d.ts +28 -0
  6. package/dist/clients/graph-api.d.ts.map +1 -0
  7. package/dist/clients/{graph-api.client.js → graph-api.js} +42 -31
  8. package/dist/clients/platform-api.d.ts +26 -0
  9. package/dist/clients/platform-api.d.ts.map +1 -0
  10. package/dist/clients/{platform-api.client.js → platform-api.js} +19 -13
  11. package/dist/esm/authorization-service.d.ts +5 -0
  12. package/dist/esm/authorization-service.d.ts.map +1 -1
  13. package/dist/esm/authorization-service.mjs +26 -6
  14. package/dist/esm/clients/graph-api.d.ts +28 -0
  15. package/dist/esm/clients/graph-api.d.ts.map +1 -0
  16. package/dist/esm/clients/{graph-api.client.mjs → graph-api.mjs} +42 -31
  17. package/dist/esm/clients/platform-api.d.ts +26 -0
  18. package/dist/esm/clients/platform-api.d.ts.map +1 -0
  19. package/dist/esm/clients/{platform-api.client.mjs → platform-api.mjs} +20 -14
  20. package/dist/esm/index.d.ts +6 -0
  21. package/dist/esm/index.d.ts.map +1 -1
  22. package/dist/esm/index.mjs +8 -0
  23. package/dist/esm/metrics-service.d.ts +13 -0
  24. package/dist/esm/metrics-service.d.ts.map +1 -0
  25. package/dist/esm/metrics-service.mjs +65 -0
  26. package/dist/esm/prometheus-service.d.ts +0 -2
  27. package/dist/esm/prometheus-service.d.ts.map +1 -1
  28. package/dist/esm/prometheus-service.mjs +4 -50
  29. package/dist/esm/types/graph-api.types.d.ts +8 -7
  30. package/dist/esm/types/graph-api.types.d.ts.map +1 -1
  31. package/dist/index.d.ts +6 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +8 -0
  34. package/dist/metrics-service.d.ts +13 -0
  35. package/dist/metrics-service.d.ts.map +1 -0
  36. package/dist/metrics-service.js +70 -0
  37. package/dist/prometheus-service.d.ts +0 -2
  38. package/dist/prometheus-service.d.ts.map +1 -1
  39. package/dist/prometheus-service.js +3 -51
  40. package/dist/types/graph-api.types.d.ts +8 -7
  41. package/dist/types/graph-api.types.d.ts.map +1 -1
  42. package/package.json +2 -1
  43. package/dist/clients/graph-api.client.d.ts +0 -24
  44. package/dist/clients/graph-api.client.d.ts.map +0 -1
  45. package/dist/clients/platform-api.client.d.ts +0 -31
  46. package/dist/clients/platform-api.client.d.ts.map +0 -1
  47. package/dist/esm/clients/graph-api.client.d.ts +0 -24
  48. package/dist/esm/clients/graph-api.client.d.ts.map +0 -1
  49. package/dist/esm/clients/platform-api.client.d.ts +0 -31
  50. package/dist/esm/clients/platform-api.client.d.ts.map +0 -1
package/README.md CHANGED
@@ -27,6 +27,9 @@ import * as MondayAuthorization from '@mondaydotcomorg/monday-authorization';
27
27
 
28
28
  MondayAuthorization.init({
29
29
  prometheus: getPrometheus(),
30
+ metrics: {
31
+ serviceName: process.env.APP_NAME,
32
+ },
30
33
  redisClient: redisClient,
31
34
  grantedFeatureRedisExpirationInSeconds: 10 * 60
32
35
  });
@@ -36,6 +39,7 @@ startServer(...)
36
39
  **Recommended** - optionally init authorization with redisClient so the granted feature results will be cached and reduce http calls.
37
40
 
38
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`).
39
43
 
40
44
  ## Usage
41
45
 
@@ -9,6 +9,11 @@ export interface AuthorizeResponse {
9
9
  }
10
10
  export declare function setRequestFetchOptions(customMondayFetchOptions: MondayFetchOptions): void;
11
11
  export declare class AuthorizationService {
12
+ private static get graphApi();
13
+ private static _graphApi?;
14
+ private static get platformApi();
15
+ private static _platformApi?;
16
+ static resetApiClients(): void;
12
17
  static redisClient?: any;
13
18
  static grantedFeatureRedisExpirationInSeconds?: number;
14
19
  static igniteClient?: IgniteClient;
@@ -1 +1 @@
1
- {"version":3,"file":"authorization-service.d.ts","sourceRoot":"","sources":["../src/authorization-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAGnE,OAAO,EAAmB,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE7F,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,0BAA0B,EAC1B,YAAY,EACb,MAAM,kCAAkC,CAAC;AAe1C,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,mBAAmB,EAAE,CAAC;CAC7C;AAED,wBAAgB,sBAAsB,CAAC,wBAAwB,EAAE,kBAAkB,QAElF;AAMD,qBAAa,oBAAoB;IAC/B,MAAM,CAAC,WAAW,CAAC,MAAC;IACpB,MAAM,CAAC,sCAAsC,CAAC,EAAE,MAAM,CAAC;IACvD,MAAM,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IAEnC;;;OAGG;WACU,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,QAAQ,EAAE,EACrB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC;WAEhB,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,2BAA2B,EAAE,mBAAmB,EAAE,GACjD,OAAO,CAAC,iBAAiB,CAAC;IAY7B;;;OAGG;WACU,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAO,GAC1C,OAAO,CAAC,OAAO,CAAC;mBAkBE,6BAA6B;IAclD,OAAO,CAAC,MAAM,CAAC,gBAAgB;WAIlB,gBAAgB,CAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,kBAAkB,CAAC;IAM9B,OAAO,CAAC,MAAM,CAAC,UAAU;WAsBZ,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;mBA6DnB,oBAAoB;mBAUpB,oBAAoB;CAoF1C;AAED,wBAAgB,cAAc,CAC5B,MAAM,KAAA,EACN,sCAAsC,GAAE,MAAiD,QAY1F;AAED,wBAAsB,eAAe,kBAMpC;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAepG"}
1
+ {"version":3,"file":"authorization-service.d.ts","sourceRoot":"","sources":["../src/authorization-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAGnE,OAAO,EAAmB,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG7F,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,0BAA0B,EAC1B,YAAY,EACb,MAAM,kCAAkC,CAAC;AAe1C,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,mBAAmB,EAAE,CAAC;CAC7C;AAED,wBAAgB,sBAAsB,CAAC,wBAAwB,EAAE,kBAAkB,QAElF;AAMD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,KAAK,QAAQ,GAK1B;IACD,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAW;IAEpC,OAAO,CAAC,MAAM,KAAK,WAAW,GAK7B;IACD,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAc;IAE1C,MAAM,CAAC,eAAe,IAAI,IAAI;IAK9B,MAAM,CAAC,WAAW,CAAC,MAAC;IACpB,MAAM,CAAC,sCAAsC,CAAC,EAAE,MAAM,CAAC;IACvD,MAAM,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IAEnC;;;OAGG;WACU,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,QAAQ,EAAE,EACrB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC;WAEhB,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,2BAA2B,EAAE,mBAAmB,EAAE,GACjD,OAAO,CAAC,iBAAiB,CAAC;IAY7B;;;OAGG;WACU,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAO,GAC1C,OAAO,CAAC,OAAO,CAAC;mBAkBE,6BAA6B;IAclD,OAAO,CAAC,MAAM,CAAC,gBAAgB;WAIlB,gBAAgB,CAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,kBAAkB,CAAC;IAM9B,OAAO,CAAC,MAAM,CAAC,UAAU;WAsBZ,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;mBA8DnB,oBAAoB;mBAUpB,oBAAoB;CAoF1C;AAED,wBAAgB,cAAc,CAC5B,MAAM,KAAA,EACN,sCAAsC,GAAE,MAAiD,QAY1F;AAED,wBAAsB,eAAe,kBAMpC;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAepG"}
@@ -5,10 +5,11 @@ const tridentBackendApi = require('@mondaydotcomorg/trident-backend-api');
5
5
  const mondayFetchApi = require('@mondaydotcomorg/monday-fetch-api');
6
6
  const igniteSdk = require('@mondaydotcomorg/ignite-sdk');
7
7
  const prometheusService = require('./prometheus-service.js');
8
+ const metricsService = require('./metrics-service.js');
8
9
  const authorizationInternalService = require('./authorization-internal-service.js');
9
10
  const attributionsService = require('./attributions-service.js');
10
- const clients_graphApi_client = require('./clients/graph-api.client.js');
11
- const clients_platformApi_client = require('./clients/platform-api.client.js');
11
+ const clients_graphApi = require('./clients/graph-api.js');
12
+ const clients_platformApi = require('./clients/platform-api.js');
12
13
  const utils_authorization_utils = require('./utils/authorization.utils.js');
13
14
 
14
15
  const GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS = 5 * 60;
@@ -21,6 +22,24 @@ function setRequestFetchOptions(customMondayFetchOptions) {
21
22
  authorizationInternalService.AuthorizationInternalService.setRequestFetchOptions(customMondayFetchOptions);
22
23
  }
23
24
  class AuthorizationService {
25
+ static get graphApi() {
26
+ if (!this._graphApi) {
27
+ this._graphApi = new clients_graphApi.GraphApi();
28
+ }
29
+ return this._graphApi;
30
+ }
31
+ static _graphApi;
32
+ static get platformApi() {
33
+ if (!this._platformApi) {
34
+ this._platformApi = new clients_platformApi.PlatformApi();
35
+ }
36
+ return this._platformApi;
37
+ }
38
+ static _platformApi;
39
+ static resetApiClients() {
40
+ this._graphApi = undefined;
41
+ this._platformApi = undefined;
42
+ }
24
43
  static redisClient;
25
44
  static grantedFeatureRedisExpirationInSeconds;
26
45
  static igniteClient;
@@ -99,7 +118,7 @@ class AuthorizationService {
99
118
  let apiType;
100
119
  if (shouldNavigateToGraph) {
101
120
  try {
102
- scopedActionResponseObjects = await clients_graphApi_client.GraphApiClient.checkPermissions(internalAuthToken, scopedActions);
121
+ scopedActionResponseObjects = await this.graphApi.checkPermissions(internalAuthToken, scopedActions);
103
122
  apiType = 'graph';
104
123
  }
105
124
  catch (error) {
@@ -116,7 +135,7 @@ class AuthorizationService {
116
135
  }
117
136
  else {
118
137
  const profile = this.getProfile(accountId, userId);
119
- scopedActionResponseObjects = await clients_platformApi_client.PlatformApiClient.checkPermissions(profile, internalAuthToken, userId, scopedActions);
138
+ scopedActionResponseObjects = await this.platformApi.checkPermissions(profile, internalAuthToken, userId, scopedActions);
120
139
  apiType = 'platform';
121
140
  }
122
141
  const endTime = perf_hooks.performance.now();
@@ -127,8 +146,9 @@ class AuthorizationService {
127
146
  const { resourceType } = utils_authorization_utils.scopeToResource(scope);
128
147
  const isAuthorized = obj.permit.can;
129
148
  prometheusService.sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthorized, 200, time, apiType);
149
+ metricsService.recordAuthorizationTiming(apiType, time);
130
150
  if (obj.permit.can) {
131
- prometheusService.incrementAuthorizationSuccess(resourceType, action, apiType);
151
+ metricsService.recordAuthorizationSuccess(apiType);
132
152
  }
133
153
  }
134
154
  return scopedActionResponseObjects;
@@ -0,0 +1,28 @@
1
+ import { ScopedAction, ScopedActionResponseObject } from '../types/scoped-actions-contracts';
2
+ import { GraphIsAllowedResponse } from '../types/graph-api.types';
3
+ /**
4
+ * Client for handling Graph API authorization operations
5
+ */
6
+ export declare class GraphApi {
7
+ private readonly httpClient;
8
+ private readonly appName;
9
+ constructor();
10
+ /**
11
+ * Builds the request body for Graph API calls
12
+ */
13
+ private static buildRequestBody;
14
+ /**
15
+ * Fetches authorization data from the Graph API
16
+ */
17
+ fetchPermissions(internalAuthToken: string, scopedActions: ScopedAction[]): Promise<GraphIsAllowedResponse>;
18
+ /**
19
+ * Maps Graph API response to the expected format
20
+ */
21
+ private static mapResponse;
22
+ /**
23
+ * Performs a complete authorization check using the Graph API
24
+ */
25
+ checkPermissions(internalAuthToken: string, scopedActions: ScopedAction[]): Promise<ScopedActionResponseObject[]>;
26
+ private static ensureGraphReason;
27
+ }
28
+ //# sourceMappingURL=graph-api.d.ts.map
@@ -0,0 +1 @@
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;AAOlC;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;;IAejC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAyB/B;;OAEG;IACG,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAqCjH;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAiC1B;;OAEG;IACG,gBAAgB,CACpB,iBAAiB,EAAE,MAAM,EACzB,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;IAKxC,OAAO,CAAC,MAAM,CAAC,iBAAiB;CAWjC"}
@@ -6,13 +6,28 @@ const types_scopedActionsContracts = require('../types/scoped-actions-contracts.
6
6
  const authorizationInternalService = require('../authorization-internal-service.js');
7
7
  const attributionsService = require('../attributions-service.js');
8
8
  const utils_authorization_utils = require('../utils/authorization.utils.js');
9
- const prometheusService = require('../prometheus-service.js');
9
+ const metricsService = require('../metrics-service.js');
10
10
 
11
11
  const CAN_ACTION_IN_SCOPE_GRAPH_PATH = '/permissions/is-allowed';
12
+ const APP_NAME_REQUIRED_ERROR = 'GraphApi: APP_NAME environment variable is required for Graph API authentication';
12
13
  /**
13
14
  * Client for handling Graph API authorization operations
14
15
  */
15
- class GraphApiClient {
16
+ class GraphApi {
17
+ httpClient;
18
+ appName;
19
+ constructor() {
20
+ const httpClient = tridentBackendApi.Api.getPart('httpClient');
21
+ if (!httpClient) {
22
+ throw new Error('GraphApi: http client is not initialized');
23
+ }
24
+ const appName = process.env.APP_NAME?.trim();
25
+ if (!appName) {
26
+ throw new Error(APP_NAME_REQUIRED_ERROR);
27
+ }
28
+ this.httpClient = httpClient;
29
+ this.appName = appName;
30
+ }
16
31
  /**
17
32
  * Builds the request body for Graph API calls
18
33
  */
@@ -41,14 +56,13 @@ class GraphApiClient {
41
56
  /**
42
57
  * Fetches authorization data from the Graph API
43
58
  */
44
- static async fetchPermissions(internalAuthToken, scopedActions) {
45
- const httpClient = tridentBackendApi.Api.getPart('httpClient');
59
+ async fetchPermissions(internalAuthToken, scopedActions) {
46
60
  const attributionHeaders = attributionsService.getAttributionsFromApi();
47
- const bodyPayload = this.buildRequestBody(scopedActions);
61
+ const bodyPayload = GraphApi.buildRequestBody(scopedActions);
48
62
  try {
49
- const response = await httpClient.fetch({
63
+ const response = await this.httpClient.fetch({
50
64
  url: {
51
- appName: 'authorization-graph',
65
+ appName: this.appName,
52
66
  path: CAN_ACTION_IN_SCOPE_GRAPH_PATH,
53
67
  },
54
68
  method: 'POST',
@@ -68,7 +82,7 @@ class GraphApiClient {
68
82
  if (err instanceof mondayFetchApi.HttpFetcherError) {
69
83
  authorizationInternalService.AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
70
84
  if (scopedActions.length > 0) {
71
- prometheusService.incrementAuthorizationError(utils_authorization_utils.scopeToResource(scopedActions[0].scope).resourceType, scopedActions[0].action, err.status, 'graph');
85
+ metricsService.recordAuthorizationError('graph', err.status);
72
86
  }
73
87
  }
74
88
  throw err;
@@ -83,41 +97,38 @@ class GraphApiClient {
83
97
  const { action, scope } = scopedAction;
84
98
  const { resourceType, resourceId } = utils_authorization_utils.scopeToResource(scope);
85
99
  const permissionResult = resources?.[resourceType]?.[String(resourceId)]?.[action];
86
- const graphReason = permissionResult?.reason;
87
- let reasonKey;
88
- let additionalOptions = {};
89
- let technicalReason = types_scopedActionsContracts.PermitTechnicalReason.NO_REASON;
90
- if (typeof graphReason === 'string') {
91
- reasonKey = graphReason;
92
- }
93
- else if (graphReason && typeof graphReason === 'object') {
94
- reasonKey = graphReason.key ?? 'unknown';
95
- additionalOptions = graphReason.additionalOptions ?? {};
96
- if (graphReason.technicalReason !== undefined) {
97
- technicalReason = (graphReason.technicalReason ?? types_scopedActionsContracts.PermitTechnicalReason.NO_REASON);
98
- }
99
- }
100
- else {
101
- reasonKey = 'unknown';
102
- }
103
100
  const permit = {
104
101
  can: permissionResult?.can ?? false,
105
102
  reason: {
106
- key: reasonKey,
107
- ...additionalOptions,
103
+ key: 'unknown',
108
104
  },
109
- technicalReason,
105
+ technicalReason: types_scopedActionsContracts.PermitTechnicalReason.NO_REASON,
110
106
  };
107
+ if (permissionResult) {
108
+ const graphReason = GraphApi.ensureGraphReason(permissionResult.reason, { resourceType, resourceId, action });
109
+ permit.reason = {
110
+ key: graphReason.key,
111
+ ...(graphReason.additionalOptions ?? {}),
112
+ };
113
+ permit.technicalReason = (graphReason.technicalReason ??
114
+ types_scopedActionsContracts.PermitTechnicalReason.NO_REASON);
115
+ }
111
116
  return { scopedAction, permit };
112
117
  });
113
118
  }
114
119
  /**
115
120
  * Performs a complete authorization check using the Graph API
116
121
  */
117
- static async checkPermissions(internalAuthToken, scopedActions) {
122
+ async checkPermissions(internalAuthToken, scopedActions) {
118
123
  const response = await this.fetchPermissions(internalAuthToken, scopedActions);
119
- return this.mapResponse(scopedActions, response);
124
+ return GraphApi.mapResponse(scopedActions, response);
125
+ }
126
+ static ensureGraphReason(reason, context) {
127
+ if (!reason || typeof reason !== 'object' || typeof reason.key !== 'string') {
128
+ throw new Error(`GraphApi: unexpected reason format for ${context.resourceType}/${context.resourceId}/${context.action}`);
129
+ }
130
+ return reason;
120
131
  }
121
132
  }
122
133
 
123
- exports.GraphApiClient = GraphApiClient;
134
+ exports.GraphApi = GraphApi;
@@ -0,0 +1,26 @@
1
+ import { ScopedAction, ScopedActionResponseObject } from '../types/scoped-actions-contracts';
2
+ import { PlatformProfile } from '../attributions-service';
3
+ /**
4
+ * Client for handling Platform API authorization operations
5
+ */
6
+ export declare class PlatformApi {
7
+ private readonly httpClient;
8
+ constructor();
9
+ /**
10
+ * Builds the request payload for Platform API calls
11
+ */
12
+ private static buildRequestPayload;
13
+ /**
14
+ * Fetches authorization data from the Platform API
15
+ */
16
+ private fetchPermissions;
17
+ /**
18
+ * Maps Platform API response to the expected format
19
+ */
20
+ private static mapResponse;
21
+ /**
22
+ * Performs a complete authorization check using the Platform API
23
+ */
24
+ checkPermissions(profile: PlatformProfile, internalAuthToken: string, userId: number, scopedActions: ScopedAction[]): Promise<ScopedActionResponseObject[]>;
25
+ }
26
+ //# sourceMappingURL=platform-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-api.d.ts","sourceRoot":"","sources":["../../src/clients/platform-api.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAE7F,OAAO,EAA0B,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAelF;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;;IAUxC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAOlC;;OAEG;YACW,gBAAgB;IA0C9B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAkB1B;;OAEG;IACG,gBAAgB,CACpB,OAAO,EAAE,eAAe,EACxB,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;CAKzC"}
@@ -5,13 +5,21 @@ const mondayFetchApi = require('@mondaydotcomorg/monday-fetch-api');
5
5
  const authorizationInternalService = require('../authorization-internal-service.js');
6
6
  const attributionsService = require('../attributions-service.js');
7
7
  const utils_authorization_utils = require('../utils/authorization.utils.js');
8
- const prometheusService = require('../prometheus-service.js');
8
+ const metricsService = require('../metrics-service.js');
9
9
 
10
10
  const PLATFORM_CAN_ACTIONS_IN_SCOPES_PATH = '/internal_ms/authorization/can_actions_in_scopes';
11
11
  /**
12
12
  * Client for handling Platform API authorization operations
13
13
  */
14
- class PlatformApiClient {
14
+ class PlatformApi {
15
+ httpClient;
16
+ constructor() {
17
+ const httpClient = tridentBackendApi.Api.getPart('httpClient');
18
+ if (!httpClient) {
19
+ throw new Error('PlatformApi: http client is not initialized');
20
+ }
21
+ this.httpClient = httpClient;
22
+ }
15
23
  /**
16
24
  * Builds the request payload for Platform API calls
17
25
  */
@@ -24,11 +32,10 @@ class PlatformApiClient {
24
32
  /**
25
33
  * Fetches authorization data from the Platform API
26
34
  */
27
- static async fetchPermissions(profile, internalAuthToken, userId, scopedActionsPayload) {
35
+ async fetchPermissions(profile, internalAuthToken, userId, scopedActionsPayload) {
28
36
  const attributionHeaders = attributionsService.getAttributionsFromApi();
29
- const httpClient = tridentBackendApi.Api.getPart('httpClient');
30
37
  try {
31
- const response = await httpClient.fetch({
38
+ const response = await this.httpClient.fetch({
32
39
  url: {
33
40
  appName: 'platform',
34
41
  path: PLATFORM_CAN_ACTIONS_IN_SCOPES_PATH,
@@ -51,8 +58,7 @@ class PlatformApiClient {
51
58
  if (err instanceof mondayFetchApi.HttpFetcherError) {
52
59
  authorizationInternalService.AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
53
60
  if (scopedActionsPayload.length > 0) {
54
- const { resourceType } = utils_authorization_utils.scopeToResource(utils_authorization_utils.toCamelCase(scopedActionsPayload[0].scope));
55
- prometheusService.incrementAuthorizationError(resourceType, scopedActionsPayload[0].action, err.status, 'platform');
61
+ metricsService.recordAuthorizationError('platform', err.status);
56
62
  }
57
63
  }
58
64
  throw err;
@@ -63,8 +69,8 @@ class PlatformApiClient {
63
69
  */
64
70
  static mapResponse(response) {
65
71
  if (!response) {
66
- authorizationInternalService.logger.error({ tag: 'platform-api-client', response }, 'PlatformApiClient: missing response');
67
- throw new Error('PlatformApiClient: missing response');
72
+ authorizationInternalService.logger.error({ tag: 'platform-api', response }, 'PlatformApi: missing response');
73
+ throw new Error('PlatformApi: missing response');
68
74
  }
69
75
  return response.result.map(responseObject => {
70
76
  const { scopedAction, permit } = responseObject;
@@ -79,11 +85,11 @@ class PlatformApiClient {
79
85
  /**
80
86
  * Performs a complete authorization check using the Platform API
81
87
  */
82
- static async checkPermissions(profile, internalAuthToken, userId, scopedActions) {
83
- const scopedActionsPayload = this.buildRequestPayload(scopedActions);
88
+ async checkPermissions(profile, internalAuthToken, userId, scopedActions) {
89
+ const scopedActionsPayload = PlatformApi.buildRequestPayload(scopedActions);
84
90
  const platformResponse = await this.fetchPermissions(profile, internalAuthToken, userId, scopedActionsPayload);
85
- return this.mapResponse(platformResponse);
91
+ return PlatformApi.mapResponse(platformResponse);
86
92
  }
87
93
  }
88
94
 
89
- exports.PlatformApiClient = PlatformApiClient;
95
+ exports.PlatformApi = PlatformApi;
@@ -9,6 +9,11 @@ export interface AuthorizeResponse {
9
9
  }
10
10
  export declare function setRequestFetchOptions(customMondayFetchOptions: MondayFetchOptions): void;
11
11
  export declare class AuthorizationService {
12
+ private static get graphApi();
13
+ private static _graphApi?;
14
+ private static get platformApi();
15
+ private static _platformApi?;
16
+ static resetApiClients(): void;
12
17
  static redisClient?: any;
13
18
  static grantedFeatureRedisExpirationInSeconds?: number;
14
19
  static igniteClient?: IgniteClient;
@@ -1 +1 @@
1
- {"version":3,"file":"authorization-service.d.ts","sourceRoot":"","sources":["../../src/authorization-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAGnE,OAAO,EAAmB,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE7F,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,0BAA0B,EAC1B,YAAY,EACb,MAAM,kCAAkC,CAAC;AAe1C,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,mBAAmB,EAAE,CAAC;CAC7C;AAED,wBAAgB,sBAAsB,CAAC,wBAAwB,EAAE,kBAAkB,QAElF;AAMD,qBAAa,oBAAoB;IAC/B,MAAM,CAAC,WAAW,CAAC,MAAC;IACpB,MAAM,CAAC,sCAAsC,CAAC,EAAE,MAAM,CAAC;IACvD,MAAM,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IAEnC;;;OAGG;WACU,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,QAAQ,EAAE,EACrB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC;WAEhB,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,2BAA2B,EAAE,mBAAmB,EAAE,GACjD,OAAO,CAAC,iBAAiB,CAAC;IAY7B;;;OAGG;WACU,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAO,GAC1C,OAAO,CAAC,OAAO,CAAC;mBAkBE,6BAA6B;IAclD,OAAO,CAAC,MAAM,CAAC,gBAAgB;WAIlB,gBAAgB,CAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,kBAAkB,CAAC;IAM9B,OAAO,CAAC,MAAM,CAAC,UAAU;WAsBZ,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;mBA6DnB,oBAAoB;mBAUpB,oBAAoB;CAoF1C;AAED,wBAAgB,cAAc,CAC5B,MAAM,KAAA,EACN,sCAAsC,GAAE,MAAiD,QAY1F;AAED,wBAAsB,eAAe,kBAMpC;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAepG"}
1
+ {"version":3,"file":"authorization-service.d.ts","sourceRoot":"","sources":["../../src/authorization-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAGnE,OAAO,EAAmB,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG7F,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,0BAA0B,EAC1B,YAAY,EACb,MAAM,kCAAkC,CAAC;AAe1C,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,mBAAmB,EAAE,CAAC;CAC7C;AAED,wBAAgB,sBAAsB,CAAC,wBAAwB,EAAE,kBAAkB,QAElF;AAMD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,KAAK,QAAQ,GAK1B;IACD,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAW;IAEpC,OAAO,CAAC,MAAM,KAAK,WAAW,GAK7B;IACD,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAc;IAE1C,MAAM,CAAC,eAAe,IAAI,IAAI;IAK9B,MAAM,CAAC,WAAW,CAAC,MAAC;IACpB,MAAM,CAAC,sCAAsC,CAAC,EAAE,MAAM,CAAC;IACvD,MAAM,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IAEnC;;;OAGG;WACU,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,QAAQ,EAAE,EACrB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC;WAEhB,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,2BAA2B,EAAE,mBAAmB,EAAE,GACjD,OAAO,CAAC,iBAAiB,CAAC;IAY7B;;;OAGG;WACU,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAO,GAC1C,OAAO,CAAC,OAAO,CAAC;mBAkBE,6BAA6B;IAclD,OAAO,CAAC,MAAM,CAAC,gBAAgB;WAIlB,gBAAgB,CAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,kBAAkB,CAAC;IAM9B,OAAO,CAAC,MAAM,CAAC,UAAU;WAsBZ,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;mBA8DnB,oBAAoB;mBAUpB,oBAAoB;CAoF1C;AAED,wBAAgB,cAAc,CAC5B,MAAM,KAAA,EACN,sCAAsC,GAAE,MAAiD,QAY1F;AAED,wBAAsB,eAAe,kBAMpC;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAepG"}
@@ -2,11 +2,12 @@ import { performance } from 'perf_hooks';
2
2
  import { Api } from '@mondaydotcomorg/trident-backend-api';
3
3
  import { HttpFetcherError } from '@mondaydotcomorg/monday-fetch-api';
4
4
  import { getIgniteClient } from '@mondaydotcomorg/ignite-sdk';
5
- import { sendAuthorizationCheckResponseTimeMetric, incrementAuthorizationSuccess } from './prometheus-service.mjs';
5
+ import { sendAuthorizationCheckResponseTimeMetric } from './prometheus-service.mjs';
6
+ import { recordAuthorizationTiming, recordAuthorizationSuccess } from './metrics-service.mjs';
6
7
  import { AuthorizationInternalService, logger } from './authorization-internal-service.mjs';
7
8
  import { getProfile, PlatformProfile, getAttributionsFromApi } from './attributions-service.mjs';
8
- import { GraphApiClient } from './clients/graph-api.client.mjs';
9
- import { PlatformApiClient } from './clients/platform-api.client.mjs';
9
+ import { GraphApi } from './clients/graph-api.mjs';
10
+ import { PlatformApi } from './clients/platform-api.mjs';
10
11
  import { scopeToResource } from './utils/authorization.utils.mjs';
11
12
 
12
13
  const GRANTED_FEATURE_CACHE_EXPIRATION_SECONDS = 5 * 60;
@@ -19,6 +20,24 @@ function setRequestFetchOptions(customMondayFetchOptions) {
19
20
  AuthorizationInternalService.setRequestFetchOptions(customMondayFetchOptions);
20
21
  }
21
22
  class AuthorizationService {
23
+ static get graphApi() {
24
+ if (!this._graphApi) {
25
+ this._graphApi = new GraphApi();
26
+ }
27
+ return this._graphApi;
28
+ }
29
+ static _graphApi;
30
+ static get platformApi() {
31
+ if (!this._platformApi) {
32
+ this._platformApi = new PlatformApi();
33
+ }
34
+ return this._platformApi;
35
+ }
36
+ static _platformApi;
37
+ static resetApiClients() {
38
+ this._graphApi = undefined;
39
+ this._platformApi = undefined;
40
+ }
22
41
  static redisClient;
23
42
  static grantedFeatureRedisExpirationInSeconds;
24
43
  static igniteClient;
@@ -97,7 +116,7 @@ class AuthorizationService {
97
116
  let apiType;
98
117
  if (shouldNavigateToGraph) {
99
118
  try {
100
- scopedActionResponseObjects = await GraphApiClient.checkPermissions(internalAuthToken, scopedActions);
119
+ scopedActionResponseObjects = await this.graphApi.checkPermissions(internalAuthToken, scopedActions);
101
120
  apiType = 'graph';
102
121
  }
103
122
  catch (error) {
@@ -114,7 +133,7 @@ class AuthorizationService {
114
133
  }
115
134
  else {
116
135
  const profile = this.getProfile(accountId, userId);
117
- scopedActionResponseObjects = await PlatformApiClient.checkPermissions(profile, internalAuthToken, userId, scopedActions);
136
+ scopedActionResponseObjects = await this.platformApi.checkPermissions(profile, internalAuthToken, userId, scopedActions);
118
137
  apiType = 'platform';
119
138
  }
120
139
  const endTime = performance.now();
@@ -125,8 +144,9 @@ class AuthorizationService {
125
144
  const { resourceType } = scopeToResource(scope);
126
145
  const isAuthorized = obj.permit.can;
127
146
  sendAuthorizationCheckResponseTimeMetric(resourceType, action, isAuthorized, 200, time, apiType);
147
+ recordAuthorizationTiming(apiType, time);
128
148
  if (obj.permit.can) {
129
- incrementAuthorizationSuccess(resourceType, action, apiType);
149
+ recordAuthorizationSuccess(apiType);
130
150
  }
131
151
  }
132
152
  return scopedActionResponseObjects;
@@ -0,0 +1,28 @@
1
+ import { ScopedAction, ScopedActionResponseObject } from '../types/scoped-actions-contracts';
2
+ import { GraphIsAllowedResponse } from '../types/graph-api.types';
3
+ /**
4
+ * Client for handling Graph API authorization operations
5
+ */
6
+ export declare class GraphApi {
7
+ private readonly httpClient;
8
+ private readonly appName;
9
+ constructor();
10
+ /**
11
+ * Builds the request body for Graph API calls
12
+ */
13
+ private static buildRequestBody;
14
+ /**
15
+ * Fetches authorization data from the Graph API
16
+ */
17
+ fetchPermissions(internalAuthToken: string, scopedActions: ScopedAction[]): Promise<GraphIsAllowedResponse>;
18
+ /**
19
+ * Maps Graph API response to the expected format
20
+ */
21
+ private static mapResponse;
22
+ /**
23
+ * Performs a complete authorization check using the Graph API
24
+ */
25
+ checkPermissions(internalAuthToken: string, scopedActions: ScopedAction[]): Promise<ScopedActionResponseObject[]>;
26
+ private static ensureGraphReason;
27
+ }
28
+ //# sourceMappingURL=graph-api.d.ts.map
@@ -0,0 +1 @@
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;AAOlC;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;;IAejC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAyB/B;;OAEG;IACG,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAqCjH;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAiC1B;;OAEG;IACG,gBAAgB,CACpB,iBAAiB,EAAE,MAAM,EACzB,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;IAKxC,OAAO,CAAC,MAAM,CAAC,iBAAiB;CAWjC"}
@@ -4,13 +4,28 @@ import { PermitTechnicalReason } from '../types/scoped-actions-contracts.mjs';
4
4
  import { AuthorizationInternalService } from '../authorization-internal-service.mjs';
5
5
  import { getAttributionsFromApi } from '../attributions-service.mjs';
6
6
  import { scopeToResource } from '../utils/authorization.utils.mjs';
7
- import { incrementAuthorizationError } from '../prometheus-service.mjs';
7
+ import { recordAuthorizationError } from '../metrics-service.mjs';
8
8
 
9
9
  const CAN_ACTION_IN_SCOPE_GRAPH_PATH = '/permissions/is-allowed';
10
+ const APP_NAME_REQUIRED_ERROR = 'GraphApi: APP_NAME environment variable is required for Graph API authentication';
10
11
  /**
11
12
  * Client for handling Graph API authorization operations
12
13
  */
13
- class GraphApiClient {
14
+ class GraphApi {
15
+ httpClient;
16
+ appName;
17
+ constructor() {
18
+ const httpClient = Api.getPart('httpClient');
19
+ if (!httpClient) {
20
+ throw new Error('GraphApi: http client is not initialized');
21
+ }
22
+ const appName = process.env.APP_NAME?.trim();
23
+ if (!appName) {
24
+ throw new Error(APP_NAME_REQUIRED_ERROR);
25
+ }
26
+ this.httpClient = httpClient;
27
+ this.appName = appName;
28
+ }
14
29
  /**
15
30
  * Builds the request body for Graph API calls
16
31
  */
@@ -39,14 +54,13 @@ class GraphApiClient {
39
54
  /**
40
55
  * Fetches authorization data from the Graph API
41
56
  */
42
- static async fetchPermissions(internalAuthToken, scopedActions) {
43
- const httpClient = Api.getPart('httpClient');
57
+ async fetchPermissions(internalAuthToken, scopedActions) {
44
58
  const attributionHeaders = getAttributionsFromApi();
45
- const bodyPayload = this.buildRequestBody(scopedActions);
59
+ const bodyPayload = GraphApi.buildRequestBody(scopedActions);
46
60
  try {
47
- const response = await httpClient.fetch({
61
+ const response = await this.httpClient.fetch({
48
62
  url: {
49
- appName: 'authorization-graph',
63
+ appName: this.appName,
50
64
  path: CAN_ACTION_IN_SCOPE_GRAPH_PATH,
51
65
  },
52
66
  method: 'POST',
@@ -66,7 +80,7 @@ class GraphApiClient {
66
80
  if (err instanceof HttpFetcherError) {
67
81
  AuthorizationInternalService.throwOnHttpError(err.status, 'canActionInScopeMultiple');
68
82
  if (scopedActions.length > 0) {
69
- incrementAuthorizationError(scopeToResource(scopedActions[0].scope).resourceType, scopedActions[0].action, err.status, 'graph');
83
+ recordAuthorizationError('graph', err.status);
70
84
  }
71
85
  }
72
86
  throw err;
@@ -81,41 +95,38 @@ class GraphApiClient {
81
95
  const { action, scope } = scopedAction;
82
96
  const { resourceType, resourceId } = scopeToResource(scope);
83
97
  const permissionResult = resources?.[resourceType]?.[String(resourceId)]?.[action];
84
- const graphReason = permissionResult?.reason;
85
- let reasonKey;
86
- let additionalOptions = {};
87
- let technicalReason = PermitTechnicalReason.NO_REASON;
88
- if (typeof graphReason === 'string') {
89
- reasonKey = graphReason;
90
- }
91
- else if (graphReason && typeof graphReason === 'object') {
92
- reasonKey = graphReason.key ?? 'unknown';
93
- additionalOptions = graphReason.additionalOptions ?? {};
94
- if (graphReason.technicalReason !== undefined) {
95
- technicalReason = (graphReason.technicalReason ?? PermitTechnicalReason.NO_REASON);
96
- }
97
- }
98
- else {
99
- reasonKey = 'unknown';
100
- }
101
98
  const permit = {
102
99
  can: permissionResult?.can ?? false,
103
100
  reason: {
104
- key: reasonKey,
105
- ...additionalOptions,
101
+ key: 'unknown',
106
102
  },
107
- technicalReason,
103
+ technicalReason: PermitTechnicalReason.NO_REASON,
108
104
  };
105
+ if (permissionResult) {
106
+ const graphReason = GraphApi.ensureGraphReason(permissionResult.reason, { resourceType, resourceId, action });
107
+ permit.reason = {
108
+ key: graphReason.key,
109
+ ...(graphReason.additionalOptions ?? {}),
110
+ };
111
+ permit.technicalReason = (graphReason.technicalReason ??
112
+ PermitTechnicalReason.NO_REASON);
113
+ }
109
114
  return { scopedAction, permit };
110
115
  });
111
116
  }
112
117
  /**
113
118
  * Performs a complete authorization check using the Graph API
114
119
  */
115
- static async checkPermissions(internalAuthToken, scopedActions) {
120
+ async checkPermissions(internalAuthToken, scopedActions) {
116
121
  const response = await this.fetchPermissions(internalAuthToken, scopedActions);
117
- return this.mapResponse(scopedActions, response);
122
+ return GraphApi.mapResponse(scopedActions, response);
123
+ }
124
+ static ensureGraphReason(reason, context) {
125
+ if (!reason || typeof reason !== 'object' || typeof reason.key !== 'string') {
126
+ throw new Error(`GraphApi: unexpected reason format for ${context.resourceType}/${context.resourceId}/${context.action}`);
127
+ }
128
+ return reason;
118
129
  }
119
130
  }
120
131
 
121
- export { GraphApiClient };
132
+ export { GraphApi };
@@ -0,0 +1,26 @@
1
+ import { ScopedAction, ScopedActionResponseObject } from '../types/scoped-actions-contracts';
2
+ import { PlatformProfile } from '../attributions-service';
3
+ /**
4
+ * Client for handling Platform API authorization operations
5
+ */
6
+ export declare class PlatformApi {
7
+ private readonly httpClient;
8
+ constructor();
9
+ /**
10
+ * Builds the request payload for Platform API calls
11
+ */
12
+ private static buildRequestPayload;
13
+ /**
14
+ * Fetches authorization data from the Platform API
15
+ */
16
+ private fetchPermissions;
17
+ /**
18
+ * Maps Platform API response to the expected format
19
+ */
20
+ private static mapResponse;
21
+ /**
22
+ * Performs a complete authorization check using the Platform API
23
+ */
24
+ checkPermissions(profile: PlatformProfile, internalAuthToken: string, userId: number, scopedActions: ScopedAction[]): Promise<ScopedActionResponseObject[]>;
25
+ }
26
+ //# sourceMappingURL=platform-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-api.d.ts","sourceRoot":"","sources":["../../../src/clients/platform-api.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAE7F,OAAO,EAA0B,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAelF;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;;IAUxC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAOlC;;OAEG;YACW,gBAAgB;IA0C9B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAkB1B;;OAEG;IACG,gBAAgB,CACpB,OAAO,EAAE,eAAe,EACxB,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;CAKzC"}