@forge/kvs 1.5.0 → 1.6.0-next.1
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.
- package/out/__test__/index.test.js +70 -15
- package/out/errors.d.ts +5 -1
- package/out/errors.d.ts.map +1 -1
- package/out/errors.js +6 -3
- package/out/storage-api.d.ts.map +1 -1
- package/out/storage-api.js +6 -6
- package/out/utils/__test__/error-handling.test.js +81 -10
- package/out/utils/error-handling.d.ts +7 -2
- package/out/utils/error-handling.d.ts.map +1 -1
- package/out/utils/error-handling.js +14 -7
- package/out/version.d.ts +3 -0
- package/out/version.d.ts.map +1 -0
- package/out/version.js +6 -0
- package/package.json +2 -2
|
@@ -102,25 +102,41 @@ describe('KVS', () => {
|
|
|
102
102
|
}));
|
|
103
103
|
});
|
|
104
104
|
it('should handle unexpected metadata fields', async () => {
|
|
105
|
-
const
|
|
105
|
+
const body = JSON.stringify({
|
|
106
106
|
code: 'BAD_REQUEST',
|
|
107
107
|
message: 'Provided request body is invalid'
|
|
108
|
-
})
|
|
108
|
+
});
|
|
109
|
+
const response = new Response(body, {
|
|
109
110
|
status: 400,
|
|
110
111
|
statusText: 'Bad Request',
|
|
111
112
|
headers: { 'x-trace-id': traceId }
|
|
112
113
|
});
|
|
113
114
|
const { sut } = prepare(response);
|
|
114
|
-
await expect(sut.get('foo', { metadataFields: ['INVALID_METADATA_FIELD'] })).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
115
|
+
await expect(sut.get('foo', { metadataFields: ['INVALID_METADATA_FIELD'] })).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
116
|
+
status: 400,
|
|
117
|
+
statusText: 'Bad Request',
|
|
118
|
+
traceId,
|
|
119
|
+
httpMethod: 'POST',
|
|
120
|
+
httpPath: '/api/v1/get',
|
|
121
|
+
responseBodyLength: body.length
|
|
122
|
+
}, { code: 'BAD_REQUEST', message: 'Provided request body is invalid' }));
|
|
115
123
|
});
|
|
116
124
|
it('should handle unexpected response', async () => {
|
|
117
|
-
const
|
|
125
|
+
const body = JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' });
|
|
126
|
+
const response = new Response(body, {
|
|
118
127
|
status: 500,
|
|
119
128
|
statusText: 'Internal Server Error',
|
|
120
129
|
headers: { 'x-trace-id': traceId }
|
|
121
130
|
});
|
|
122
131
|
const { sut } = prepare(response);
|
|
123
|
-
await expect(sut.get('foo')).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
132
|
+
await expect(sut.get('foo')).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
133
|
+
status: 500,
|
|
134
|
+
statusText: 'Internal Server Error',
|
|
135
|
+
traceId,
|
|
136
|
+
httpMethod: 'POST',
|
|
137
|
+
httpPath: '/api/v1/get',
|
|
138
|
+
responseBodyLength: body.length
|
|
139
|
+
}, { code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }));
|
|
124
140
|
});
|
|
125
141
|
it('should handle non-json response even though status is ok', async () => {
|
|
126
142
|
const responseText = 'Text that will fail to parse';
|
|
@@ -130,7 +146,14 @@ describe('KVS', () => {
|
|
|
130
146
|
headers: { 'x-trace-id': traceId, 'content-length': responseText.length.toString() }
|
|
131
147
|
});
|
|
132
148
|
const { sut } = prepare(response);
|
|
133
|
-
await expect(sut.get('foo')).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
149
|
+
await expect(sut.get('foo')).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
150
|
+
status: 200,
|
|
151
|
+
statusText: 'OK',
|
|
152
|
+
traceId,
|
|
153
|
+
httpMethod: 'POST',
|
|
154
|
+
httpPath: '/api/v1/get',
|
|
155
|
+
responseBodyLength: responseText.length
|
|
156
|
+
}, {
|
|
134
157
|
code: 'UNKNOWN_ERROR',
|
|
135
158
|
message: 'Unexpected error in Forge KVS API. Response was not valid JSON',
|
|
136
159
|
context: { contentLength: responseText.length.toString() }
|
|
@@ -1178,14 +1201,22 @@ describe('KVS', () => {
|
|
|
1178
1201
|
}));
|
|
1179
1202
|
});
|
|
1180
1203
|
it('should handle batchSet API error response', async () => {
|
|
1181
|
-
const
|
|
1204
|
+
const body = JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' });
|
|
1205
|
+
const response = new Response(body, {
|
|
1182
1206
|
status: 500,
|
|
1183
1207
|
statusText: 'Internal Server Error',
|
|
1184
1208
|
headers: { 'x-trace-id': traceId }
|
|
1185
1209
|
});
|
|
1186
1210
|
const { sut } = prepare(response);
|
|
1187
1211
|
const items = [{ key: 'foo', value: 'bar' }];
|
|
1188
|
-
await expect(sut.batchSet(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
1212
|
+
await expect(sut.batchSet(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
1213
|
+
status: 500,
|
|
1214
|
+
statusText: 'Internal Server Error',
|
|
1215
|
+
traceId,
|
|
1216
|
+
httpMethod: 'POST',
|
|
1217
|
+
httpPath: '/api/v1/batch/set',
|
|
1218
|
+
responseBodyLength: body.length
|
|
1219
|
+
}, { code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }));
|
|
1189
1220
|
});
|
|
1190
1221
|
it('should handle batchDelete correctly with mixed entity and non-entity items', async () => {
|
|
1191
1222
|
const response = new Response(JSON.stringify({
|
|
@@ -1277,14 +1308,22 @@ describe('KVS', () => {
|
|
|
1277
1308
|
}));
|
|
1278
1309
|
});
|
|
1279
1310
|
it('should handle batchDelete API error response', async () => {
|
|
1280
|
-
const
|
|
1311
|
+
const body = JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' });
|
|
1312
|
+
const response = new Response(body, {
|
|
1281
1313
|
status: 500,
|
|
1282
1314
|
statusText: 'Internal Server Error',
|
|
1283
1315
|
headers: { 'x-trace-id': traceId }
|
|
1284
1316
|
});
|
|
1285
1317
|
const { sut } = prepare(response);
|
|
1286
1318
|
const items = [{ key: 'foo' }];
|
|
1287
|
-
await expect(sut.batchDelete(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
1319
|
+
await expect(sut.batchDelete(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
1320
|
+
status: 500,
|
|
1321
|
+
statusText: 'Internal Server Error',
|
|
1322
|
+
traceId,
|
|
1323
|
+
httpMethod: 'POST',
|
|
1324
|
+
httpPath: '/api/v1/batch/delete',
|
|
1325
|
+
responseBodyLength: body.length
|
|
1326
|
+
}, { code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }));
|
|
1288
1327
|
});
|
|
1289
1328
|
it('should handle batchGet with mixed entity and non-entity items', async () => {
|
|
1290
1329
|
const response = new Response(JSON.stringify({
|
|
@@ -1451,20 +1490,29 @@ describe('KVS', () => {
|
|
|
1451
1490
|
}));
|
|
1452
1491
|
});
|
|
1453
1492
|
it('should handle batchGet API error response', async () => {
|
|
1454
|
-
const
|
|
1493
|
+
const body = JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' });
|
|
1494
|
+
const response = new Response(body, {
|
|
1455
1495
|
status: 500,
|
|
1456
1496
|
statusText: 'Internal Server Error',
|
|
1457
1497
|
headers: { 'x-trace-id': traceId }
|
|
1458
1498
|
});
|
|
1459
1499
|
const { sut } = prepare(response);
|
|
1460
1500
|
const items = [{ key: 'foo' }];
|
|
1461
|
-
await expect(sut.batchGet(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
1501
|
+
await expect(sut.batchGet(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
1502
|
+
status: 500,
|
|
1503
|
+
statusText: 'Internal Server Error',
|
|
1504
|
+
traceId,
|
|
1505
|
+
httpMethod: 'POST',
|
|
1506
|
+
httpPath: '/api/v1/batch/get',
|
|
1507
|
+
responseBodyLength: body.length
|
|
1508
|
+
}, { code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }));
|
|
1462
1509
|
});
|
|
1463
1510
|
it('should handle batchGet with invalid metadata fields', async () => {
|
|
1464
|
-
const
|
|
1511
|
+
const body = JSON.stringify({
|
|
1465
1512
|
code: 'BAD_REQUEST',
|
|
1466
1513
|
message: 'Provided request body is invalid'
|
|
1467
|
-
})
|
|
1514
|
+
});
|
|
1515
|
+
const response = new Response(body, {
|
|
1468
1516
|
status: 400,
|
|
1469
1517
|
statusText: 'Bad Request',
|
|
1470
1518
|
headers: { 'x-trace-id': traceId }
|
|
@@ -1476,6 +1524,13 @@ describe('KVS', () => {
|
|
|
1476
1524
|
options: { metadataFields: ['INVALID_METADATA_FIELD'] }
|
|
1477
1525
|
}
|
|
1478
1526
|
];
|
|
1479
|
-
await expect(sut.batchGet(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
1527
|
+
await expect(sut.batchGet(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
1528
|
+
status: 400,
|
|
1529
|
+
statusText: 'Bad Request',
|
|
1530
|
+
traceId,
|
|
1531
|
+
httpMethod: 'POST',
|
|
1532
|
+
httpPath: '/api/v1/batch/get',
|
|
1533
|
+
responseBodyLength: body.length
|
|
1534
|
+
}, { code: 'BAD_REQUEST', message: 'Provided request body is invalid' }));
|
|
1480
1535
|
});
|
|
1481
1536
|
});
|
package/out/errors.d.ts
CHANGED
|
@@ -7,9 +7,13 @@ export interface APIErrorResponseDetails {
|
|
|
7
7
|
status: number;
|
|
8
8
|
statusText: string;
|
|
9
9
|
traceId?: string | null;
|
|
10
|
+
httpMethod?: string;
|
|
11
|
+
httpPath?: string;
|
|
12
|
+
responseBodyLength?: number;
|
|
10
13
|
}
|
|
11
14
|
export declare class ForgeKvsError extends Error {
|
|
12
|
-
|
|
15
|
+
packageVersion: string;
|
|
16
|
+
constructor(message: string, packageVersion?: string);
|
|
13
17
|
}
|
|
14
18
|
export declare class ForgeKvsAPIError extends ForgeKvsError {
|
|
15
19
|
responseDetails: APIErrorResponseDetails;
|
package/out/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,qBAAa,aAAc,SAAQ,KAAK;IACtC,cAAc,EAAE,MAAM,CAAC;gBAEX,OAAO,EAAE,MAAM,EAAE,cAAc,GAAE,MAAwB;CAKtE;AAED,qBAAa,gBAAiB,SAAQ,aAAa;IACjD,eAAe,EAAE,uBAAuB,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAErB,eAAe,EAAE,uBAAuB,EAAE,UAAU,EAAE,UAAU;CAU7E"}
|
package/out/errors.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ForgeKvsAPIError = exports.ForgeKvsError = void 0;
|
|
4
|
+
const version_1 = require("./version");
|
|
4
5
|
class ForgeKvsError extends Error {
|
|
5
|
-
|
|
6
|
+
packageVersion;
|
|
7
|
+
constructor(message, packageVersion = version_1.PACKAGE_VERSION) {
|
|
6
8
|
super(message);
|
|
7
9
|
this.name = 'ForgeKvsError';
|
|
10
|
+
this.packageVersion = packageVersion;
|
|
8
11
|
}
|
|
9
12
|
}
|
|
10
13
|
exports.ForgeKvsError = ForgeKvsError;
|
|
@@ -15,8 +18,8 @@ class ForgeKvsAPIError extends ForgeKvsError {
|
|
|
15
18
|
context;
|
|
16
19
|
constructor(responseDetails, forgeError) {
|
|
17
20
|
super(forgeError.message);
|
|
18
|
-
const { status, statusText, traceId } = responseDetails;
|
|
19
|
-
this.responseDetails = { status, statusText, traceId };
|
|
21
|
+
const { status, statusText, traceId, httpMethod, httpPath, responseBodyLength } = responseDetails;
|
|
22
|
+
this.responseDetails = { status, statusText, traceId, httpMethod, httpPath, responseBodyLength };
|
|
20
23
|
const { code, message, context, ...bodyData } = forgeError;
|
|
21
24
|
this.code = code;
|
|
22
25
|
this.message = message;
|
package/out/storage-api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-api.d.ts","sourceRoot":"","sources":["../src/storage-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EACL,SAAS,EACT,UAAU,EACV,WAAW,EAGX,SAAS,EAGT,cAAc,EACf,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"storage-api.d.ts","sourceRoot":"","sources":["../src/storage-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EACL,SAAS,EACT,UAAU,EACV,WAAW,EAGX,SAAS,EAGT,cAAc,EACf,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,EACL,kBAAkB,EAElB,eAAe,EACf,eAAe,EAEf,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAEhB,kBAAkB,EAElB,gBAAgB,EAChB,UAAU,EAEV,YAAY,EAEZ,mBAAmB,EACnB,gBAAgB,EAEhB,gBAAgB,EAChB,UAAU,EAEV,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAQ9B,qBAAa,UAAU;IACT,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,WAAW;IAEpC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAO/D,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAO3E,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAO3E,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAKrE,SAAS,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAKjF,SAAS,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAKjF,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtD,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAQpD,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAQhE,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC;IAQ3D,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC;IAQ3D,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IAQ9D,QAAQ,CAAC,CAAC,EAAE,kBAAkB,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;YAI7D,iBAAiB;YAWjB,OAAO;IA8CrB,OAAO,CAAC,kBAAkB;IAyB1B,OAAO,CAAC,kBAAkB;CAgB3B"}
|
package/out/storage-api.js
CHANGED
|
@@ -118,7 +118,11 @@ class StorageApi {
|
|
|
118
118
|
}
|
|
119
119
|
};
|
|
120
120
|
const response = await this.apiClient(path, requestBody);
|
|
121
|
-
|
|
121
|
+
const requestContext = {
|
|
122
|
+
httpMethod: requestBody.method,
|
|
123
|
+
httpPath: path
|
|
124
|
+
};
|
|
125
|
+
await (0, error_handling_1.checkResponseError)(response, requestContext);
|
|
122
126
|
if (responseType === ResponseType.NONE) {
|
|
123
127
|
return;
|
|
124
128
|
}
|
|
@@ -128,11 +132,7 @@ class StorageApi {
|
|
|
128
132
|
}
|
|
129
133
|
const parsedBody = (0, error_handling_1.safeGetParsedBody)(responseText);
|
|
130
134
|
if (parsedBody === undefined) {
|
|
131
|
-
const details =
|
|
132
|
-
status: response.status,
|
|
133
|
-
statusText: response.statusText,
|
|
134
|
-
traceId: (0, error_handling_1.extractTraceId)(response)
|
|
135
|
-
};
|
|
135
|
+
const details = (0, error_handling_1.getAPIErrorResponseDetails)(response, responseText, requestContext);
|
|
136
136
|
throw new errors_1.ForgeKvsAPIError(details, {
|
|
137
137
|
code: 'UNKNOWN_ERROR',
|
|
138
138
|
message: 'Unexpected error in Forge KVS API. Response was not valid JSON',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const errors_1 = require("../../errors");
|
|
4
|
+
const version_1 = require("../../version");
|
|
4
5
|
const error_handling_1 = require("../error-handling");
|
|
5
6
|
describe('error-handling', () => {
|
|
6
7
|
it('isForgeError', () => {
|
|
@@ -30,7 +31,8 @@ describe('error-handling', () => {
|
|
|
30
31
|
const message = 'A test error has occurred';
|
|
31
32
|
const code = 'ERROR_CODE';
|
|
32
33
|
it('should return a ForgeKvsAPIError when response body is a Forge error', async () => {
|
|
33
|
-
const
|
|
34
|
+
const body = JSON.stringify({ code, message });
|
|
35
|
+
const mockResponse = new Response(body, {
|
|
34
36
|
status: 400,
|
|
35
37
|
statusText: 'Bad Request',
|
|
36
38
|
headers: { 'x-trace-id': traceId }
|
|
@@ -38,12 +40,14 @@ describe('error-handling', () => {
|
|
|
38
40
|
await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
39
41
|
status: 400,
|
|
40
42
|
statusText: 'Bad Request',
|
|
41
|
-
traceId
|
|
43
|
+
traceId,
|
|
44
|
+
responseBodyLength: body.length
|
|
42
45
|
}, { code, message }));
|
|
43
46
|
});
|
|
44
47
|
it('should include context if present in the Forge error', async () => {
|
|
45
48
|
const context = { key: 'value' };
|
|
46
|
-
const
|
|
49
|
+
const body = JSON.stringify({ code, message, context });
|
|
50
|
+
const mockResponse = new Response(body, {
|
|
47
51
|
status: 400,
|
|
48
52
|
statusText: 'Bad Request',
|
|
49
53
|
headers: { 'x-trace-id': traceId }
|
|
@@ -51,12 +55,14 @@ describe('error-handling', () => {
|
|
|
51
55
|
await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
52
56
|
status: 400,
|
|
53
57
|
statusText: 'Bad Request',
|
|
54
|
-
traceId
|
|
58
|
+
traceId,
|
|
59
|
+
responseBodyLength: body.length
|
|
55
60
|
}, { code, message, context }));
|
|
56
61
|
});
|
|
57
62
|
it('should include top level additional fields if present in the Forge error', async () => {
|
|
58
63
|
const extraFields = { extraValue: 'value', debug: true };
|
|
59
|
-
const
|
|
64
|
+
const body = JSON.stringify({ code, message, ...extraFields });
|
|
65
|
+
const mockResponse = new Response(body, {
|
|
60
66
|
status: 400,
|
|
61
67
|
statusText: 'Bad Request',
|
|
62
68
|
headers: { 'x-trace-id': traceId }
|
|
@@ -64,13 +70,15 @@ describe('error-handling', () => {
|
|
|
64
70
|
await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
65
71
|
status: 400,
|
|
66
72
|
statusText: 'Bad Request',
|
|
67
|
-
traceId
|
|
73
|
+
traceId,
|
|
74
|
+
responseBodyLength: body.length
|
|
68
75
|
}, { code, message, context: extraFields }));
|
|
69
76
|
});
|
|
70
77
|
it('should merge context and additional top level fields if both present in the Forge error', async () => {
|
|
71
78
|
const context = { key: 'value' };
|
|
72
79
|
const extraFields = { extraValue: 'value', debug: true };
|
|
73
|
-
const
|
|
80
|
+
const body = JSON.stringify({ code, message, context, ...extraFields });
|
|
81
|
+
const mockResponse = new Response(body, {
|
|
74
82
|
status: 400,
|
|
75
83
|
statusText: 'Bad Request',
|
|
76
84
|
headers: { 'x-trace-id': traceId }
|
|
@@ -78,7 +86,8 @@ describe('error-handling', () => {
|
|
|
78
86
|
await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toThrowError(new errors_1.ForgeKvsAPIError({
|
|
79
87
|
status: 400,
|
|
80
88
|
statusText: 'Bad Request',
|
|
81
|
-
traceId
|
|
89
|
+
traceId,
|
|
90
|
+
responseBodyLength: body.length
|
|
82
91
|
}, { code, message, context: { ...context, ...extraFields } }));
|
|
83
92
|
});
|
|
84
93
|
describe('Handle non forge errors', () => {
|
|
@@ -91,7 +100,8 @@ describe('error-handling', () => {
|
|
|
91
100
|
await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
92
101
|
status: 404,
|
|
93
102
|
statusText: 'Not Found',
|
|
94
|
-
traceId
|
|
103
|
+
traceId,
|
|
104
|
+
responseBodyLength: 0
|
|
95
105
|
}, {
|
|
96
106
|
code: 'UNKNOWN_ERROR',
|
|
97
107
|
context: { responseText: '' },
|
|
@@ -108,7 +118,8 @@ describe('error-handling', () => {
|
|
|
108
118
|
await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
109
119
|
status: 500,
|
|
110
120
|
statusText: 'Internal Server Error',
|
|
111
|
-
traceId
|
|
121
|
+
traceId,
|
|
122
|
+
responseBodyLength: body.length
|
|
112
123
|
}, {
|
|
113
124
|
code: 'UNKNOWN_ERROR',
|
|
114
125
|
context: { responseText: body },
|
|
@@ -117,5 +128,65 @@ describe('error-handling', () => {
|
|
|
117
128
|
});
|
|
118
129
|
});
|
|
119
130
|
});
|
|
131
|
+
describe('with request context', () => {
|
|
132
|
+
const requestContext = {
|
|
133
|
+
httpMethod: 'POST',
|
|
134
|
+
httpPath: '/api/v1/get'
|
|
135
|
+
};
|
|
136
|
+
it('should include request context in ForgeKvsAPIError when provided', async () => {
|
|
137
|
+
const body = JSON.stringify({ code: 'ERROR', message: 'Test error' });
|
|
138
|
+
const mockResponse = new Response(body, {
|
|
139
|
+
status: 400,
|
|
140
|
+
statusText: 'Bad Request',
|
|
141
|
+
headers: { 'x-trace-id': traceId }
|
|
142
|
+
});
|
|
143
|
+
await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse, requestContext)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
144
|
+
status: 400,
|
|
145
|
+
statusText: 'Bad Request',
|
|
146
|
+
traceId,
|
|
147
|
+
httpMethod: 'POST',
|
|
148
|
+
httpPath: '/api/v1/get',
|
|
149
|
+
responseBodyLength: body.length
|
|
150
|
+
}, { code: 'ERROR', message: 'Test error' }));
|
|
151
|
+
});
|
|
152
|
+
it('should include request context in UNKNOWN_ERROR when provided', async () => {
|
|
153
|
+
const body = 'not valid json';
|
|
154
|
+
const mockResponse = new Response(body, {
|
|
155
|
+
status: 500,
|
|
156
|
+
statusText: 'Internal Server Error',
|
|
157
|
+
headers: { 'x-trace-id': traceId }
|
|
158
|
+
});
|
|
159
|
+
await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse, requestContext)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
|
|
160
|
+
status: 500,
|
|
161
|
+
statusText: 'Internal Server Error',
|
|
162
|
+
traceId,
|
|
163
|
+
httpMethod: 'POST',
|
|
164
|
+
httpPath: '/api/v1/get',
|
|
165
|
+
responseBodyLength: body.length
|
|
166
|
+
}, {
|
|
167
|
+
code: 'UNKNOWN_ERROR',
|
|
168
|
+
context: { responseText: body },
|
|
169
|
+
message: 'Unexpected error in Forge KVS API'
|
|
170
|
+
}));
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
describe('packageVersion', () => {
|
|
175
|
+
it('should auto-populate packageVersion on ForgeKvsError', () => {
|
|
176
|
+
const error = new errors_1.ForgeKvsError('Test error');
|
|
177
|
+
expect(error.packageVersion).toBeDefined();
|
|
178
|
+
expect(typeof error.packageVersion).toBe('string');
|
|
179
|
+
expect(error.packageVersion).toMatch(/^\d+\.\d+\.\d+/);
|
|
180
|
+
});
|
|
181
|
+
it('should auto-populate packageVersion on ForgeKvsAPIError', () => {
|
|
182
|
+
const error = new errors_1.ForgeKvsAPIError({ status: 500, statusText: 'Internal Server Error' }, { code: 'ERROR', message: 'Test error' });
|
|
183
|
+
expect(error.packageVersion).toBeDefined();
|
|
184
|
+
expect(typeof error.packageVersion).toBe('string');
|
|
185
|
+
expect(error.packageVersion).toMatch(/^\d+\.\d+\.\d+/);
|
|
186
|
+
});
|
|
187
|
+
it('should match the version in package.json', () => {
|
|
188
|
+
const error = new errors_1.ForgeKvsError('Test error');
|
|
189
|
+
expect(error.packageVersion).toBe(version_1.PACKAGE_VERSION);
|
|
190
|
+
});
|
|
120
191
|
});
|
|
121
192
|
});
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { APIResponse } from '@forge/api';
|
|
2
|
-
import { ForgeError } from '../errors';
|
|
2
|
+
import { APIErrorResponseDetails, ForgeError } from '../errors';
|
|
3
|
+
export interface RequestContext {
|
|
4
|
+
httpMethod: string;
|
|
5
|
+
httpPath: string;
|
|
6
|
+
}
|
|
3
7
|
export declare function isForgeError(body: unknown): body is ForgeError;
|
|
4
8
|
export declare function safeGetParsedBody(text: string): unknown | undefined;
|
|
9
|
+
export declare function getAPIErrorResponseDetails(response: APIResponse, responseText: string, requestContext?: RequestContext): APIErrorResponseDetails;
|
|
5
10
|
export declare function extractTraceId(response: APIResponse): string | null;
|
|
6
|
-
export declare function checkResponseError(response: APIResponse): Promise<void>;
|
|
11
|
+
export declare function checkResponseError(response: APIResponse, requestContext?: RequestContext): Promise<void>;
|
|
7
12
|
//# sourceMappingURL=error-handling.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-handling.d.ts","sourceRoot":"","sources":["../../src/utils/error-handling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,
|
|
1
|
+
{"version":3,"file":"error-handling.d.ts","sourceRoot":"","sources":["../../src/utils/error-handling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAoB,MAAM,WAAW,CAAC;AAElF,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,UAAU,CAE9D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAMnE;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,WAAW,EACrB,YAAY,EAAE,MAAM,EACpB,cAAc,CAAC,EAAE,cAAc,GAC9B,uBAAuB,CASzB;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAEnE;AAED,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE,cAAc,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB9G"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.checkResponseError = exports.extractTraceId = exports.safeGetParsedBody = exports.isForgeError = void 0;
|
|
3
|
+
exports.checkResponseError = exports.extractTraceId = exports.getAPIErrorResponseDetails = exports.safeGetParsedBody = exports.isForgeError = void 0;
|
|
4
4
|
const errors_1 = require("../errors");
|
|
5
5
|
function isForgeError(body) {
|
|
6
6
|
return typeof body === 'object' && body !== null && 'code' in body && 'message' in body;
|
|
@@ -15,20 +15,27 @@ function safeGetParsedBody(text) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
exports.safeGetParsedBody = safeGetParsedBody;
|
|
18
|
+
function getAPIErrorResponseDetails(response, responseText, requestContext) {
|
|
19
|
+
return {
|
|
20
|
+
status: response.status,
|
|
21
|
+
statusText: response.statusText,
|
|
22
|
+
traceId: extractTraceId(response),
|
|
23
|
+
httpMethod: requestContext?.httpMethod,
|
|
24
|
+
httpPath: requestContext?.httpPath,
|
|
25
|
+
responseBodyLength: responseText.length
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
exports.getAPIErrorResponseDetails = getAPIErrorResponseDetails;
|
|
18
29
|
function extractTraceId(response) {
|
|
19
30
|
return response.headers.get('x-b3-traceid') || response.headers.get('x-trace-id');
|
|
20
31
|
}
|
|
21
32
|
exports.extractTraceId = extractTraceId;
|
|
22
|
-
async function checkResponseError(response) {
|
|
33
|
+
async function checkResponseError(response, requestContext) {
|
|
23
34
|
if (response.ok) {
|
|
24
35
|
return;
|
|
25
36
|
}
|
|
26
37
|
const responseText = await response.text();
|
|
27
|
-
const details =
|
|
28
|
-
status: response.status,
|
|
29
|
-
statusText: response.statusText,
|
|
30
|
-
traceId: extractTraceId(response)
|
|
31
|
-
};
|
|
38
|
+
const details = getAPIErrorResponseDetails(response, responseText, requestContext);
|
|
32
39
|
const parsedBody = safeGetParsedBody(responseText);
|
|
33
40
|
if (parsedBody && isForgeError(parsedBody)) {
|
|
34
41
|
throw new errors_1.ForgeKvsAPIError(details, parsedBody);
|
package/out/version.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,YAAY,EAAE,MAAyB,CAAC;AACrD,eAAO,MAAM,eAAe,EAAE,MAA4B,CAAC"}
|
package/out/version.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PACKAGE_VERSION = exports.PACKAGE_NAME = void 0;
|
|
4
|
+
const packageInfo = require('../package.json');
|
|
5
|
+
exports.PACKAGE_NAME = packageInfo.name;
|
|
6
|
+
exports.PACKAGE_VERSION = packageInfo.version;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forge/kvs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0-next.1",
|
|
4
4
|
"description": "Forge Key Value Store SDK",
|
|
5
5
|
"author": "Atlassian",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"@types/node": "20.19.1"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@forge/api": "^7.1.
|
|
22
|
+
"@forge/api": "^7.1.2-next.0"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"registry": "https://packages.atlassian.com/api/npm/npm-public/"
|