@forge/kvs 0.0.2-next.0 → 0.0.2-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.
Files changed (49) hide show
  1. package/out/__test__/index.test.d.ts +2 -0
  2. package/out/__test__/index.test.d.ts.map +1 -0
  3. package/out/__test__/index.test.js +509 -0
  4. package/out/conditions.d.ts +38 -0
  5. package/out/conditions.d.ts.map +1 -0
  6. package/out/conditions.js +98 -0
  7. package/out/entity-query.d.ts +13 -0
  8. package/out/entity-query.d.ts.map +1 -0
  9. package/out/entity-query.js +100 -0
  10. package/out/entity.d.ts +13 -0
  11. package/out/entity.d.ts.map +1 -0
  12. package/out/entity.js +35 -0
  13. package/out/errors.d.ts +21 -0
  14. package/out/errors.d.ts.map +1 -0
  15. package/out/errors.js +26 -0
  16. package/out/index.d.ts +9 -0
  17. package/out/index.d.ts.map +1 -0
  18. package/out/index.js +43 -0
  19. package/out/interfaces/entity-query.d.ts +41 -0
  20. package/out/interfaces/entity-query.d.ts.map +1 -0
  21. package/out/interfaces/entity-query.js +2 -0
  22. package/out/interfaces/kvs-api.d.ts +72 -0
  23. package/out/interfaces/kvs-api.d.ts.map +1 -0
  24. package/out/interfaces/kvs-api.js +2 -0
  25. package/out/interfaces/kvs.d.ts +19 -0
  26. package/out/interfaces/kvs.d.ts.map +1 -0
  27. package/out/interfaces/kvs.js +2 -0
  28. package/out/interfaces/query.d.ts +17 -0
  29. package/out/interfaces/query.d.ts.map +1 -0
  30. package/out/interfaces/query.js +2 -0
  31. package/out/interfaces/types.d.ts +64 -0
  32. package/out/interfaces/types.d.ts.map +1 -0
  33. package/out/interfaces/types.js +8 -0
  34. package/out/kvs.d.ts +16 -0
  35. package/out/kvs.d.ts.map +1 -0
  36. package/out/kvs.js +36 -0
  37. package/out/query.d.ts +14 -0
  38. package/out/query.d.ts.map +1 -0
  39. package/out/query.js +38 -0
  40. package/out/storage-api.d.ts +20 -0
  41. package/out/storage-api.d.ts.map +1 -0
  42. package/out/storage-api.js +77 -0
  43. package/out/utils/__test__/error-handling.test.d.ts +2 -0
  44. package/out/utils/__test__/error-handling.test.d.ts.map +1 -0
  45. package/out/utils/__test__/error-handling.test.js +121 -0
  46. package/out/utils/error-handling.d.ts +7 -0
  47. package/out/utils/error-handling.d.ts.map +1 -0
  48. package/out/utils/error-handling.js +42 -0
  49. package/package.json +3 -4
@@ -0,0 +1,20 @@
1
+ import { FetchMethod } from '@forge/api';
2
+ import { ListResult } from './interfaces/types';
3
+ import { DeleteRequest, EntityDeleteRequest, EntityGetRequest, EntityQueryRequest, EntitySetRequest, GetRequest, QueryRequest, SecretDeleteRequest, SecretGetRequest, SecretSetRequest, SetRequest } from './interfaces/kvs-api';
4
+ export declare class StorageApi {
5
+ private apiClient;
6
+ constructor(apiClient: FetchMethod);
7
+ get<T>(body: GetRequest): Promise<T>;
8
+ getSecret<T>(body: SecretGetRequest): Promise<T>;
9
+ getEntity<T>(body: EntityGetRequest): Promise<T>;
10
+ set<T>(body: SetRequest<T>): Promise<void>;
11
+ setSecret<T>(body: SecretSetRequest<T>): Promise<void>;
12
+ setEntity<T>(body: EntitySetRequest<T>): Promise<void>;
13
+ delete(body: DeleteRequest): Promise<void>;
14
+ deleteSecret(body: SecretDeleteRequest): Promise<void>;
15
+ deleteEntity(body: EntityDeleteRequest): Promise<void>;
16
+ query<T>(body: QueryRequest): Promise<ListResult<T>>;
17
+ queryEntity<T>(body: EntityQueryRequest): Promise<ListResult<T>>;
18
+ private request;
19
+ }
20
+ //# sourceMappingURL=storage-api.d.ts.map
@@ -0,0 +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,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,OAAO,EACL,aAAa,EAEb,mBAAmB,EAEnB,gBAAgB,EAEhB,kBAAkB,EAElB,gBAAgB,EAEhB,UAAU,EAEV,YAAY,EAEZ,mBAAmB,EAEnB,gBAAgB,EAEhB,gBAAgB,EAEhB,UAAU,EAEX,MAAM,sBAAsB,CAAC;AAE9B,qBAAa,UAAU;IACT,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,WAAW;IAEpC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC;IAKpC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;IAKhD,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;IAKhD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1C,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1C,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,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;YAQxD,OAAO;CAyBtB"}
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StorageApi = void 0;
4
+ const error_handling_1 = require("./utils/error-handling");
5
+ const errors_1 = require("./errors");
6
+ class StorageApi {
7
+ apiClient;
8
+ constructor(apiClient) {
9
+ this.apiClient = apiClient;
10
+ }
11
+ async get(body) {
12
+ const rs = await this.request('/api/v1/get', body, true);
13
+ return rs.value;
14
+ }
15
+ async getSecret(body) {
16
+ const rs = await this.request('/api/v1/secret/get', body, true);
17
+ return rs.value;
18
+ }
19
+ async getEntity(body) {
20
+ const rs = await this.request('/api/v1/entity/get', body, true);
21
+ return rs.value;
22
+ }
23
+ async set(body) {
24
+ await this.request('/api/v1/set', body, false);
25
+ }
26
+ async setSecret(body) {
27
+ await this.request('/api/v1/secret/set', body, false);
28
+ }
29
+ async setEntity(body) {
30
+ await this.request('/api/v1/entity/set', body, false);
31
+ }
32
+ async delete(body) {
33
+ await this.request('/api/v1/delete', body, false);
34
+ }
35
+ async deleteSecret(body) {
36
+ await this.request('/api/v1/secret/delete', body, false);
37
+ }
38
+ async deleteEntity(body) {
39
+ await this.request('/api/v1/entity/delete', body, false);
40
+ }
41
+ async query(body) {
42
+ const rs = await this.request('/api/v1/query', body, true);
43
+ return {
44
+ results: rs.data,
45
+ nextCursor: rs.cursor
46
+ };
47
+ }
48
+ async queryEntity(body) {
49
+ const rs = await this.request('/api/v1/entity/query', body, true);
50
+ return {
51
+ results: rs.data,
52
+ nextCursor: rs.cursor
53
+ };
54
+ }
55
+ async request(path, body, isResponseExpected) {
56
+ const requestBody = {
57
+ method: 'POST',
58
+ body: JSON.stringify(body),
59
+ headers: {
60
+ 'content-type': 'application/json'
61
+ }
62
+ };
63
+ const response = await this.apiClient(path, requestBody);
64
+ await (0, error_handling_1.checkResponseError)(response);
65
+ if (!isResponseExpected) {
66
+ return {};
67
+ }
68
+ const responseText = await response.text();
69
+ try {
70
+ return JSON.parse(responseText);
71
+ }
72
+ catch (error) {
73
+ throw new errors_1.ForgeKvsError(`Unexpected error. Response was not valid JSON: ${responseText}`);
74
+ }
75
+ }
76
+ }
77
+ exports.StorageApi = StorageApi;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=error-handling.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handling.test.d.ts","sourceRoot":"","sources":["../../../src/utils/__test__/error-handling.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const errors_1 = require("../../errors");
4
+ const error_handling_1 = require("../error-handling");
5
+ describe('error-handling', () => {
6
+ it('isForgeError', () => {
7
+ expect((0, error_handling_1.isForgeError)({ code: 'code', message: 'message' })).toBe(true);
8
+ expect((0, error_handling_1.isForgeError)({ code: 'code' })).toBe(false);
9
+ expect((0, error_handling_1.isForgeError)({ message: 'message' })).toBe(false);
10
+ expect((0, error_handling_1.isForgeError)({})).toBe(false);
11
+ expect((0, error_handling_1.isForgeError)(null)).toBe(false);
12
+ expect((0, error_handling_1.isForgeError)(undefined)).toBe(false);
13
+ expect((0, error_handling_1.isForgeError)('string')).toBe(false);
14
+ expect((0, error_handling_1.isForgeError)(123)).toBe(false);
15
+ });
16
+ it('safeGetParsedBody', () => {
17
+ expect((0, error_handling_1.safeGetParsedBody)('{"valid": "json"}')).toEqual({ valid: 'json' });
18
+ expect((0, error_handling_1.safeGetParsedBody)('invalid json')).toBeUndefined();
19
+ });
20
+ describe('checkResponseError', () => {
21
+ const traceId = 'trace-id';
22
+ it('should do nothing if response is ok', async () => {
23
+ const mockResponse = new Response('OK', {
24
+ status: 200,
25
+ statusText: 'OK'
26
+ });
27
+ await expect((0, error_handling_1.checkResponseError)(mockResponse)).resolves.toBeUndefined();
28
+ });
29
+ describe('Forge errors - ForgeKvsAPIError', () => {
30
+ const message = 'A test error has occurred';
31
+ const code = 'ERROR_CODE';
32
+ it('should return a ForgeKvsAPIError when response body is a Forge error', async () => {
33
+ const mockResponse = new Response(JSON.stringify({ code, message }), {
34
+ status: 400,
35
+ statusText: 'Bad Request',
36
+ headers: { 'x-trace-id': traceId }
37
+ });
38
+ await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
39
+ status: 400,
40
+ statusText: 'Bad Request',
41
+ traceId
42
+ }, { code, message }));
43
+ });
44
+ it('should include context if present in the Forge error', async () => {
45
+ const context = { key: 'value' };
46
+ const mockResponse = new Response(JSON.stringify({ code, message, context }), {
47
+ status: 400,
48
+ statusText: 'Bad Request',
49
+ headers: { 'x-trace-id': traceId }
50
+ });
51
+ await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
52
+ status: 400,
53
+ statusText: 'Bad Request',
54
+ traceId
55
+ }, { code, message, context }));
56
+ });
57
+ it('should include top level additional fields if present in the Forge error', async () => {
58
+ const extraFields = { extraValue: 'value', debug: true };
59
+ const mockResponse = new Response(JSON.stringify({ code, message, ...extraFields }), {
60
+ status: 400,
61
+ statusText: 'Bad Request',
62
+ headers: { 'x-trace-id': traceId }
63
+ });
64
+ await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
65
+ status: 400,
66
+ statusText: 'Bad Request',
67
+ traceId
68
+ }, { code, message, context: extraFields }));
69
+ });
70
+ it('should merge context and additional top level fields if both present in the Forge error', async () => {
71
+ const context = { key: 'value' };
72
+ const extraFields = { extraValue: 'value', debug: true };
73
+ const mockResponse = new Response(JSON.stringify({ code, message, context, ...extraFields }), {
74
+ status: 400,
75
+ statusText: 'Bad Request',
76
+ headers: { 'x-trace-id': traceId }
77
+ });
78
+ await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toThrowError(new errors_1.ForgeKvsAPIError({
79
+ status: 400,
80
+ statusText: 'Bad Request',
81
+ traceId
82
+ }, { code, message, context: { ...context, ...extraFields } }));
83
+ });
84
+ describe('Handle non forge errors', () => {
85
+ it('returns an UNKNOWN_ERROR when no response body', async () => {
86
+ const mockResponse = new Response(undefined, {
87
+ status: 404,
88
+ statusText: 'Not Found',
89
+ headers: { 'x-trace-id': traceId }
90
+ });
91
+ await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
92
+ status: 404,
93
+ statusText: 'Not Found',
94
+ traceId
95
+ }, {
96
+ code: 'UNKNOWN_ERROR',
97
+ context: { responseText: '' },
98
+ message: 'Unexpected error in Forge KVS API'
99
+ }));
100
+ });
101
+ it("returns UNKNOWN_ERROR if there is a JSON body that isn't a forge error", async () => {
102
+ const body = JSON.stringify({ not: 'a forge error' });
103
+ const mockResponse = new Response(body, {
104
+ status: 500,
105
+ statusText: 'Internal Server Error',
106
+ headers: { 'x-trace-id': traceId }
107
+ });
108
+ await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
109
+ status: 500,
110
+ statusText: 'Internal Server Error',
111
+ traceId
112
+ }, {
113
+ code: 'UNKNOWN_ERROR',
114
+ context: { responseText: body },
115
+ message: 'Unexpected error in Forge KVS API'
116
+ }));
117
+ });
118
+ });
119
+ });
120
+ });
121
+ });
@@ -0,0 +1,7 @@
1
+ import { APIResponse } from '@forge/api';
2
+ import { ForgeError } from '../errors';
3
+ export declare function isForgeError(body: unknown): body is ForgeError;
4
+ export declare function safeGetParsedBody(text: string): unknown | undefined;
5
+ export declare function extractTraceId(response: APIResponse): string | null;
6
+ export declare function checkResponseError(response: APIResponse): Promise<void>;
7
+ //# sourceMappingURL=error-handling.d.ts.map
@@ -0,0 +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,EAA2B,UAAU,EAAoB,MAAM,WAAW,CAAC;AAElF,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,cAAc,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAEnE;AAED,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB7E"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkResponseError = exports.extractTraceId = exports.safeGetParsedBody = exports.isForgeError = void 0;
4
+ const errors_1 = require("../errors");
5
+ function isForgeError(body) {
6
+ return typeof body === 'object' && body !== null && 'code' in body && 'message' in body;
7
+ }
8
+ exports.isForgeError = isForgeError;
9
+ function safeGetParsedBody(text) {
10
+ try {
11
+ return JSON.parse(text);
12
+ }
13
+ catch (error) {
14
+ return undefined;
15
+ }
16
+ }
17
+ exports.safeGetParsedBody = safeGetParsedBody;
18
+ function extractTraceId(response) {
19
+ return response.headers.get('x-b3-traceid') || response.headers.get('x-trace-id');
20
+ }
21
+ exports.extractTraceId = extractTraceId;
22
+ async function checkResponseError(response) {
23
+ if (response.ok) {
24
+ return;
25
+ }
26
+ const responseText = await response.text();
27
+ const details = {
28
+ status: response.status,
29
+ statusText: response.statusText,
30
+ traceId: extractTraceId(response)
31
+ };
32
+ const parsedBody = safeGetParsedBody(responseText);
33
+ if (parsedBody && isForgeError(parsedBody)) {
34
+ throw new errors_1.ForgeKvsAPIError(details, parsedBody);
35
+ }
36
+ throw new errors_1.ForgeKvsAPIError(details, {
37
+ code: 'UNKNOWN_ERROR',
38
+ message: 'Unexpected error in Forge KVS API',
39
+ context: { responseText }
40
+ });
41
+ }
42
+ exports.checkResponseError = checkResponseError;
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@forge/kvs",
3
- "version": "0.0.2-next.0",
3
+ "version": "0.0.2-next.1",
4
4
  "description": "Forge Key Value Store SDK",
5
5
  "author": "Atlassian",
6
- "license": "UNLICENSED",
6
+ "license": "SEE LICENSE IN LICENSE.txt",
7
7
  "main": "out/index.js",
8
8
  "types": "out/index.d.ts",
9
9
  "files": [
@@ -12,8 +12,7 @@
12
12
  "scripts": {
13
13
  "build": "yarn run clean && yarn run compile",
14
14
  "clean": "rm -rf ./out && rm -f tsconfig.tsbuildinfo",
15
- "compile": "tsc -b -v",
16
- "postbuild": "yarn bolt w forge-scripts preserve-deprecated-tags ../forge-kvs"
15
+ "compile": "tsc -b -v"
17
16
  },
18
17
  "devDependencies": {
19
18
  "@types/node": "14.18.63"