@axinom/mosaic-id-guard 0.26.0-rc.11 → 0.26.0-rc.13

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.
@@ -2,6 +2,7 @@ import { MessagingSettings } from '@axinom/mosaic-message-bus-abstractions';
2
2
  import { Logger } from '@axinom/mosaic-service-common';
3
3
  import { DbConfig, TransactionalInboxMessage, TransactionalInboxMessageHandler } from '@axinom/mosaic-transactional-inbox-outbox';
4
4
  import { ClientBase } from 'pg';
5
+ import { InboxMessage } from 'pg-transactional-outbox';
5
6
  import { AuthenticatedManagementSubject, AuthenticationConfig } from '../common';
6
7
  export interface GuardedContext {
7
8
  subject: AuthenticatedManagementSubject;
@@ -36,5 +37,8 @@ export declare abstract class GuardedTransactionalInboxMessageHandler<TMessage,
36
37
  */
37
38
  protected abstract setPgSettings(envOwnerClient: ClientBase, subject: AuthenticatedManagementSubject): Promise<void>;
38
39
  mapError(error: Error): Error;
40
+ protected updateErrorDetails(error: Error & {
41
+ details?: Record<string, unknown>;
42
+ }, message: InboxMessage, context?: GuardedContext): Error;
39
43
  }
40
44
  //# sourceMappingURL=guarded-transactional-inbox-message-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"guarded-transactional-inbox-message-handler.d.ts","sourceRoot":"","sources":["../../src/message-bus/guarded-transactional-inbox-message-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,EACL,MAAM,EAGP,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,QAAQ,EACR,yBAAyB,EACzB,gCAAgC,EACjC,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EACL,8BAA8B,EAC9B,oBAAoB,EAGrB,MAAM,WAAW,CAAC;AAGnB,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,8BAA8B,CAAC;IACxC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG;IAAE,oBAAoB,EAAE,MAAM,CAAA;CAAE,CAAC;AAExE,8BAAsB,uCAAuC,CAC3D,QAAQ,EACR,OAAO,SAAS,aAAa,CAC7B,SAAQ,gCAAgC,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,CAAC;IAYzE,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE;IAG/B,OAAO,CAAC,UAAU;IAdpB;;;;;;;;OAQG;gBAED,iBAAiB,EAAE,iBAAiB,EAC1B,WAAW,EAAE,MAAM,EAAE,EAC/B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,EACP,UAAU,EAAE,MAAM,GAAG,oBAAoB;IAWnD,SAAS,CAAC,wBAAwB,+EAEhB,UAAU,KACzB,QAAQ,cAAc,CAAC,CAYxB;IAEF;;;;;;;;;OASG;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa,CAC9B,cAAc,EAAE,UAAU,EAC1B,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,IAAI,CAAC;IAEP,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK;CAGvC"}
1
+ {"version":3,"file":"guarded-transactional-inbox-message-handler.d.ts","sourceRoot":"","sources":["../../src/message-bus/guarded-transactional-inbox-message-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAC5E,OAAO,EACL,MAAM,EAGP,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,QAAQ,EACR,yBAAyB,EACzB,gCAAgC,EACjC,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EACL,8BAA8B,EAC9B,oBAAoB,EAGrB,MAAM,WAAW,CAAC;AAGnB,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,8BAA8B,CAAC;IACxC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG;IAAE,oBAAoB,EAAE,MAAM,CAAA;CAAE,CAAC;AAExE,8BAAsB,uCAAuC,CAC3D,QAAQ,EACR,OAAO,SAAS,aAAa,CAC7B,SAAQ,gCAAgC,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,CAAC;IAYzE,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE;IAG/B,OAAO,CAAC,UAAU;IAdpB;;;;;;;;OAQG;gBAED,iBAAiB,EAAE,iBAAiB,EAC1B,WAAW,EAAE,MAAM,EAAE,EAC/B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,EACP,UAAU,EAAE,MAAM,GAAG,oBAAoB;IAWnD,SAAS,CAAC,wBAAwB,+EAEhB,UAAU,KACzB,QAAQ,cAAc,CAAC,CAYxB;IAEF;;;;;;;;;OASG;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa,CAC9B,cAAc,EAAE,UAAU,EAC1B,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,IAAI,CAAC;IAEP,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK;cAInB,kBAAkB,CACnC,KAAK,EAAE,KAAK,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EACpD,OAAO,EAAE,YAAY,EACrB,OAAO,CAAC,EAAE,cAAc,GACvB,KAAK;CAmBT"}
@@ -36,6 +36,18 @@ class GuardedTransactionalInboxMessageHandler extends mosaic_transactional_inbox
36
36
  mapError(error) {
37
37
  return (0, mosaic_service_common_1.getMappedError)(error);
38
38
  }
39
+ updateErrorDetails(error, message, context) {
40
+ var _a, _b, _c, _d, _e;
41
+ const detailedError = super.updateErrorDetails(error, message, context);
42
+ if (((_a = detailedError.details) === null || _a === void 0 ? void 0 : _a.tenantId) &&
43
+ ((_b = detailedError.details) === null || _b === void 0 ? void 0 : _b.environmentId)) {
44
+ return detailedError;
45
+ }
46
+ if (((_c = context === null || context === void 0 ? void 0 : context.subject) === null || _c === void 0 ? void 0 : _c.tenantId) && ((_d = context === null || context === void 0 ? void 0 : context.subject) === null || _d === void 0 ? void 0 : _d.environmentId)) {
47
+ detailedError.details = Object.assign({ tenantId: context.subject.tenantId, environmentId: context.subject.environmentId }, ((_e = detailedError.details) !== null && _e !== void 0 ? _e : {}));
48
+ }
49
+ return detailedError;
50
+ }
39
51
  }
40
52
  exports.GuardedTransactionalInboxMessageHandler = GuardedTransactionalInboxMessageHandler;
41
53
  //# sourceMappingURL=guarded-transactional-inbox-message-handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"guarded-transactional-inbox-message-handler.js","sourceRoot":"","sources":["../../src/message-bus/guarded-transactional-inbox-message-handler.ts"],"names":[],"mappings":";;;AACA,yEAIuC;AACvC,iGAImD;AAEnD,sCAKmB;AACnB,+EAAiE;AASjE,MAAsB,uCAGpB,SAAQ,oEAAmE;IAC3E;;;;;;;;OAQG;IACH,YACE,iBAAoC,EAC1B,WAAqB,EAC/B,MAAc,EACd,MAAe,EACP,UAAyC;QAEjD,MAAM,OAAO,GAAG,KAAK,EACnB,OAA4C,EAC5C,cAA0B,EACD,EAAE;YAC3B,OAAO,IAAI,CAAC,wBAAwB,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QAChE,CAAC,CAAC;QACF,KAAK,CAAC,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAXxC,gBAAW,GAAX,WAAW,CAAU;QAGvB,eAAU,GAAV,UAAU,CAA+B;QAWzC,6BAAwB,GAAG,KAAK,EACxC,OAA4C,EAC5C,cAA0B,EACD,EAAE;YAC3B,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;YACzC,IAAI,KAAK,KAAK,SAAS,EAAE;gBACvB,MAAM,IAAI,mCAAW,CAAC,sBAAa,CAAC,mBAAmB,CAAC,CAAC;aAC1D;YACD,MAAM,OAAO,GAAG,MAAM,IAAA,0CAAiC,EACrD,KAAK,EACL,IAAI,CAAC,UAAU,CAChB,CAAC;YACF,MAAM,IAAA,8CAAgB,EAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACzE,IAAI,CAAC,aAAa,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;YAC5C,OAAO,EAAE,OAAO,EAAE,CAAC;QACrB,CAAC,CAAC;IAjBF,CAAC;IAkCQ,QAAQ,CAAC,KAAY;QAC5B,OAAO,IAAA,sCAAc,EAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;CACF;AAhED,0FAgEC"}
1
+ {"version":3,"file":"guarded-transactional-inbox-message-handler.js","sourceRoot":"","sources":["../../src/message-bus/guarded-transactional-inbox-message-handler.ts"],"names":[],"mappings":";;;AACA,yEAIuC;AACvC,iGAImD;AAGnD,sCAKmB;AACnB,+EAAiE;AASjE,MAAsB,uCAGpB,SAAQ,oEAAmE;IAC3E;;;;;;;;OAQG;IACH,YACE,iBAAoC,EAC1B,WAAqB,EAC/B,MAAc,EACd,MAAe,EACP,UAAyC;QAEjD,MAAM,OAAO,GAAG,KAAK,EACnB,OAA4C,EAC5C,cAA0B,EACD,EAAE;YAC3B,OAAO,IAAI,CAAC,wBAAwB,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;QAChE,CAAC,CAAC;QACF,KAAK,CAAC,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAXxC,gBAAW,GAAX,WAAW,CAAU;QAGvB,eAAU,GAAV,UAAU,CAA+B;QAWzC,6BAAwB,GAAG,KAAK,EACxC,OAA4C,EAC5C,cAA0B,EACD,EAAE;YAC3B,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;YACzC,IAAI,KAAK,KAAK,SAAS,EAAE;gBACvB,MAAM,IAAI,mCAAW,CAAC,sBAAa,CAAC,mBAAmB,CAAC,CAAC;aAC1D;YACD,MAAM,OAAO,GAAG,MAAM,IAAA,0CAAiC,EACrD,KAAK,EACL,IAAI,CAAC,UAAU,CAChB,CAAC;YACF,MAAM,IAAA,8CAAgB,EAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACzE,IAAI,CAAC,aAAa,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;YAC5C,OAAO,EAAE,OAAO,EAAE,CAAC;QACrB,CAAC,CAAC;IAjBF,CAAC;IAkCQ,QAAQ,CAAC,KAAY;QAC5B,OAAO,IAAA,sCAAc,EAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAEkB,kBAAkB,CACnC,KAAoD,EACpD,OAAqB,EACrB,OAAwB;;QAExB,MAAM,aAAa,GAAG,KAAK,CAAC,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACxE,IACE,CAAA,MAAA,aAAa,CAAC,OAAO,0CAAE,QAAQ;aAC/B,MAAA,aAAa,CAAC,OAAO,0CAAE,aAAa,CAAA,EACpC;YACA,OAAO,aAAa,CAAC;SACtB;QAED,IAAI,CAAA,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,0CAAE,QAAQ,MAAI,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,0CAAE,aAAa,CAAA,EAAE;YACjE,aAAa,CAAC,OAAO,mBACnB,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ,EAClC,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,aAAa,IACzC,CAAC,MAAA,aAAa,CAAC,OAAO,mCAAI,EAAE,CAAC,CACjC,CAAC;SACH;QAED,OAAO,aAAa,CAAC;IACvB,CAAC;CACF;AAxFD,0FAwFC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axinom/mosaic-id-guard",
3
- "version": "0.26.0-rc.11",
3
+ "version": "0.26.0-rc.13",
4
4
  "description": "Authentication and authorization helpers for Axinom Mosaic services",
5
5
  "author": "Axinom",
6
6
  "license": "PROPRIETARY",
@@ -22,16 +22,16 @@
22
22
  "dev": "tsc -w",
23
23
  "test": "jest --silent",
24
24
  "test:watch": "jest --watch --silent",
25
- "test:cov": "jest --coverage --silent",
26
- "posttest:cov": "ts-node ../../scripts/open-test-coverage.ts -- libs/ax-guard",
25
+ "test:cov": "jest --coverage --silent && yarn posttest:cov",
26
+ "posttest:cov": "ts-node ../../../scripts/open-test-coverage.ts -- services/id/lib-id-guard",
27
27
  "test:ci": "jest --reporters=default --reporters=jest-junit --coverage --coverageReporters=cobertura --coverageReporters=html",
28
28
  "lint": "eslint . --ext .ts,.tsx,.js --color --cache"
29
29
  },
30
30
  "dependencies": {
31
- "@axinom/mosaic-id-utils": "^0.15.11-rc.11",
32
- "@axinom/mosaic-message-bus": "^0.21.0-rc.11",
33
- "@axinom/mosaic-service-common": "^0.43.0-rc.11",
34
- "@axinom/mosaic-transactional-inbox-outbox": "^0.3.0-rc.11",
31
+ "@axinom/mosaic-id-utils": "^0.15.11-rc.13",
32
+ "@axinom/mosaic-message-bus": "^0.21.0-rc.13",
33
+ "@axinom/mosaic-service-common": "^0.43.0-rc.13",
34
+ "@axinom/mosaic-transactional-inbox-outbox": "^0.3.0-rc.13",
35
35
  "amqplib": "^0.10.3",
36
36
  "express": "^4.17.1",
37
37
  "express-bearer-token": "^2.4.0",
@@ -44,6 +44,7 @@
44
44
  "jwks-rsa": "^1.8.1",
45
45
  "lru-cache": "^7.18.3",
46
46
  "pg": "^8.11.3",
47
+ "pg-transactional-outbox": "^0.4.0-beta.11",
47
48
  "postgraphile": "^4.13.0",
48
49
  "rascal": "^14.0.1",
49
50
  "subscriptions-transport-ws": "^0.9.19"
@@ -63,5 +64,5 @@
63
64
  "publishConfig": {
64
65
  "access": "public"
65
66
  },
66
- "gitHead": "de408d3200890bd3463959743c6fd230c2f91b63"
67
+ "gitHead": "e8739d927ba6360e38368c41a119491931d965db"
67
68
  }
@@ -0,0 +1,170 @@
1
+ /* eslint-disable no-console */
2
+ import 'jest-extended';
3
+ import { GuardedTransactionalInboxMessageHandler } from './guarded-transactional-inbox-message-handler';
4
+
5
+ class TestTransactionalInboxMessageHandler extends GuardedTransactionalInboxMessageHandler<
6
+ any,
7
+ any
8
+ > {
9
+ override handleMessage(): Promise<void> {
10
+ throw new Error('Method not implemented.');
11
+ }
12
+
13
+ override setPgSettings(): Promise<void> {
14
+ throw new Error('Method not implemented.');
15
+ }
16
+ }
17
+
18
+ describe('GuardedTransactionalInboxMessageHandler', () => {
19
+ const handler = new TestTransactionalInboxMessageHandler(
20
+ {} as any,
21
+ [],
22
+ {} as any,
23
+ {} as any,
24
+ {} as any,
25
+ ) as any; // To access protected method for testing
26
+
27
+ describe('updateErrorDetails', () => {
28
+ it('Update error details with empty message object -> returns original error', () => {
29
+ // Arrange
30
+ const originalError = new Error('Test Message');
31
+ const message = {} as any;
32
+
33
+ // Act
34
+ const error = handler.updateErrorDetails(originalError, message);
35
+
36
+ // Assert
37
+ expect(error).toMatchObject({
38
+ message: 'Test Message',
39
+ stack: expect.toStartWith('Error: Test Message'),
40
+ });
41
+ expect((error as any).details).toBeUndefined();
42
+ });
43
+
44
+ it.each([undefined, null])(
45
+ 'Update error details with %p routingKey -> returns original error',
46
+ (falsyKey) => {
47
+ // Arrange
48
+ const originalError = new Error('Test Message');
49
+ const message = {
50
+ metadata: { fields: { routingKey: falsyKey } },
51
+ } as any;
52
+
53
+ // Act
54
+ const error = handler.updateErrorDetails(originalError, message);
55
+
56
+ // Assert
57
+ expect(error).toMatchObject({
58
+ message: 'Test Message',
59
+ stack: expect.toStartWith('Error: Test Message'),
60
+ });
61
+ expect((error as any).details).toBeUndefined();
62
+ },
63
+ );
64
+
65
+ it('Update error details with valid routingKey -> returns extended error', () => {
66
+ // Arrange
67
+ const tenantId = '00000000-0000-0000-0000-000000000001';
68
+ const environmentId = '00000000-0000-0000-0000-000000000002';
69
+ const originalError = new Error('Test Message');
70
+ const message = {
71
+ metadata: {
72
+ fields: {
73
+ routingKey: `ax-image-service.${tenantId}.${environmentId}.image_types.declare`,
74
+ },
75
+ },
76
+ } as any;
77
+
78
+ // Act
79
+ const error = handler.updateErrorDetails(originalError, message);
80
+
81
+ // Assert
82
+ expect(error).toMatchObject({
83
+ message: 'Test Message',
84
+ stack: expect.toStartWith('Error: Test Message'),
85
+ details: {
86
+ tenantId,
87
+ environmentId,
88
+ },
89
+ });
90
+ });
91
+
92
+ it('Update error details with valid routingKey and existing details -> returns extended error preserving details', () => {
93
+ // Arrange
94
+ const tenantId = '00000000-0000-0000-0000-000000000001';
95
+ const environmentId = '00000000-0000-0000-0000-000000000002';
96
+ const originalError = new Error('Test Message') as Error & {
97
+ details?: Record<string, string>;
98
+ };
99
+ originalError.details = { custom: 'custom details property' };
100
+ const message = {
101
+ metadata: {
102
+ fields: {
103
+ routingKey: `ax-image-service.${tenantId}.${environmentId}.image_types.declare`,
104
+ },
105
+ },
106
+ } as any;
107
+
108
+ // Act
109
+ const error = handler.updateErrorDetails(originalError, message);
110
+
111
+ // Assert
112
+ expect(error).toMatchObject({
113
+ message: 'Test Message',
114
+ stack: expect.toStartWith('Error: Test Message'),
115
+ details: {
116
+ tenantId,
117
+ environmentId,
118
+ custom: 'custom details property',
119
+ },
120
+ });
121
+ });
122
+
123
+ it('Update error details with empty routingKey and existing details -> returns original error preserving details', () => {
124
+ // Arrange
125
+ const originalError = new Error('Test Message') as Error & {
126
+ details?: Record<string, string>;
127
+ };
128
+ originalError.details = { custom: 'custom details property' };
129
+ const message = { metadata: { fields: {} } } as any;
130
+
131
+ // Act
132
+ const error = handler.updateErrorDetails(originalError, message);
133
+
134
+ // Assert
135
+ expect(error).toMatchObject({
136
+ message: 'Test Message',
137
+ stack: expect.toStartWith('Error: Test Message'),
138
+ details: {
139
+ custom: 'custom details property',
140
+ },
141
+ });
142
+ });
143
+
144
+ it('Update error details with empty routingKey, existing details, and context having subject -> returns extended error preserving details', () => {
145
+ // Arrange
146
+ const tenantId = '00000000-0000-0000-0000-000000000001';
147
+ const environmentId = '00000000-0000-0000-0000-000000000002';
148
+ const originalError = new Error('Test Message') as Error & {
149
+ details?: Record<string, string>;
150
+ };
151
+ originalError.details = { custom: 'custom details property' };
152
+ const message = { metadata: { fields: {} } } as any;
153
+ const context = { subject: { tenantId, environmentId } } as any;
154
+
155
+ // Act
156
+ const error = handler.updateErrorDetails(originalError, message, context);
157
+
158
+ // Assert
159
+ expect(error).toMatchObject({
160
+ message: 'Test Message',
161
+ stack: expect.toStartWith('Error: Test Message'),
162
+ details: {
163
+ tenantId,
164
+ environmentId,
165
+ custom: 'custom details property',
166
+ },
167
+ });
168
+ });
169
+ });
170
+ });
@@ -10,6 +10,7 @@ import {
10
10
  TransactionalInboxMessageHandler,
11
11
  } from '@axinom/mosaic-transactional-inbox-outbox';
12
12
  import { ClientBase } from 'pg';
13
+ import { InboxMessage } from 'pg-transactional-outbox';
13
14
  import {
14
15
  AuthenticatedManagementSubject,
15
16
  AuthenticationConfig,
@@ -89,4 +90,28 @@ export abstract class GuardedTransactionalInboxMessageHandler<
89
90
  override mapError(error: Error): Error {
90
91
  return getMappedError(error);
91
92
  }
93
+
94
+ protected override updateErrorDetails(
95
+ error: Error & { details?: Record<string, unknown> },
96
+ message: InboxMessage,
97
+ context?: GuardedContext,
98
+ ): Error {
99
+ const detailedError = super.updateErrorDetails(error, message, context);
100
+ if (
101
+ detailedError.details?.tenantId &&
102
+ detailedError.details?.environmentId
103
+ ) {
104
+ return detailedError;
105
+ }
106
+
107
+ if (context?.subject?.tenantId && context?.subject?.environmentId) {
108
+ detailedError.details = {
109
+ tenantId: context.subject.tenantId,
110
+ environmentId: context.subject.environmentId,
111
+ ...(detailedError.details ?? {}),
112
+ };
113
+ }
114
+
115
+ return detailedError;
116
+ }
92
117
  }