@forge/migrations 0.0.0
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/README.md +22 -0
- package/out/__test__/errors.test.d.ts +2 -0
- package/out/__test__/errors.test.d.ts.map +1 -0
- package/out/__test__/errors.test.js +16 -0
- package/out/__test__/migration.test.d.ts +2 -0
- package/out/__test__/migration.test.d.ts.map +1 -0
- package/out/__test__/migration.test.js +220 -0
- package/out/index.d.ts +3 -0
- package/out/index.d.ts.map +1 -0
- package/out/index.js +5 -0
- package/out/migration/errors.d.ts +6 -0
- package/out/migration/errors.d.ts.map +1 -0
- package/out/migration/errors.js +15 -0
- package/out/migration/index.d.ts +17 -0
- package/out/migration/index.d.ts.map +1 -0
- package/out/migration/index.js +22 -0
- package/out/migration/migration-adaptor.d.ts +27 -0
- package/out/migration/migration-adaptor.d.ts.map +1 -0
- package/out/migration/migration-adaptor.js +2 -0
- package/out/migration/migration.d.ts +11 -0
- package/out/migration/migration.d.ts.map +1 -0
- package/out/migration/migration.js +33 -0
- package/out/migration/utils.d.ts +5 -0
- package/out/migration/utils.d.ts.map +1 -0
- package/out/migration/utils.js +33 -0
- package/package.json +21 -0
- package/src/__test__/errors.test.ts +15 -0
- package/src/__test__/migration.test.ts +242 -0
- package/src/index.ts +3 -0
- package/src/migration/errors.ts +13 -0
- package/src/migration/index.ts +26 -0
- package/src/migration/migration-adaptor.ts +32 -0
- package/src/migration/migration.ts +41 -0
- package/src/migration/utils.ts +31 -0
- package/tsconfig.json +13 -0
- package/tsconfig.tsbuildinfo +1453 -0
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Forge Migrations
|
|
2
|
+
|
|
3
|
+
API methods exposed to perform app migrations in Forge apps. These include methods to simplify calls to app migration platform.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
See [Set up Forge](https://developer.atlassian.com/platform/forge/set-up-forge/) for details of the software required to develop Forge apps.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
In your Forge app, include the following:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
import migration from '@forge/migrations';
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Support
|
|
21
|
+
|
|
22
|
+
See [Get help](https://developer.atlassian.com/platform/forge/get-help/) for how to get help and provide feedback.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.test.d.ts","sourceRoot":"","sources":["../../src/__test__/errors.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const migration_1 = require("../migration");
|
|
4
|
+
describe('APIError', () => {
|
|
5
|
+
describe('forStatus', () => {
|
|
6
|
+
it.each([
|
|
7
|
+
[400, 'Bad request'],
|
|
8
|
+
[401, 'Unauthorized'],
|
|
9
|
+
[403, 'Forbidden'],
|
|
10
|
+
[404, 'Not Found'],
|
|
11
|
+
[500, 'Internal server error']
|
|
12
|
+
])('should contain the correct error message for a code of %s', (status, message) => {
|
|
13
|
+
expect(migration_1.APIError.forStatus(status, message).message).toEqual(message);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.test.d.ts","sourceRoot":"","sources":["../../src/__test__/migration.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const migration_1 = require("../migration");
|
|
5
|
+
const jest_when_1 = require("jest-when");
|
|
6
|
+
const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
7
|
+
const utils_1 = require("ts-jest/utils");
|
|
8
|
+
let transferId;
|
|
9
|
+
let invalidTransferId;
|
|
10
|
+
let progress;
|
|
11
|
+
let keys;
|
|
12
|
+
let invalidKeys;
|
|
13
|
+
let namespace;
|
|
14
|
+
let mappingByKeyResponseWithKeys;
|
|
15
|
+
let mappingByKeyResponseWithInvalidKeys;
|
|
16
|
+
let s3Key;
|
|
17
|
+
let invalidS3Key;
|
|
18
|
+
let feedbackPayload;
|
|
19
|
+
let appDataListResponse;
|
|
20
|
+
let appDataPayloadResponse;
|
|
21
|
+
jest.mock('node-fetch');
|
|
22
|
+
describe('migration', () => {
|
|
23
|
+
beforeAll(() => {
|
|
24
|
+
global.api = {
|
|
25
|
+
asApp: jest.fn().mockReturnValue({
|
|
26
|
+
__requestAtlassian: jest.fn()
|
|
27
|
+
})
|
|
28
|
+
};
|
|
29
|
+
transferId = '521ee36a-b673-3dc6-b60b-12460332c200';
|
|
30
|
+
invalidTransferId = '521ee36a-b673-3dc6-b60b-12460332c001';
|
|
31
|
+
});
|
|
32
|
+
describe('sendMigrationProgress', () => {
|
|
33
|
+
beforeAll(() => {
|
|
34
|
+
progress = { status: 'IN_PROGRESS', percent: 10, message: 'reporting' };
|
|
35
|
+
jest_when_1.when(global.api.asApp().__requestAtlassian)
|
|
36
|
+
.calledWith(`/app/migration/forge/v1/progress/${transferId}`, {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: {
|
|
39
|
+
'content-type': 'application/json'
|
|
40
|
+
},
|
|
41
|
+
body: JSON.stringify(progress)
|
|
42
|
+
})
|
|
43
|
+
.mockReturnValue({
|
|
44
|
+
status: 200,
|
|
45
|
+
text: jest.fn().mockImplementationOnce(() => Promise.resolve()),
|
|
46
|
+
json: jest.fn().mockImplementationOnce(() => Promise.resolve())
|
|
47
|
+
})
|
|
48
|
+
.calledWith(`/app/migration/forge/v1/progress/${invalidTransferId}`, {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
headers: {
|
|
51
|
+
'content-type': 'application/json'
|
|
52
|
+
},
|
|
53
|
+
body: JSON.stringify(progress)
|
|
54
|
+
})
|
|
55
|
+
.mockReturnValue({
|
|
56
|
+
status: 403,
|
|
57
|
+
statusText: 'Forbidden'
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
it('should update progress when sendProgress method called with valid transferId', async () => {
|
|
61
|
+
const returnedValue = migration_1.migration.sendProgress(transferId, progress);
|
|
62
|
+
await expect(returnedValue).resolves.toBeUndefined();
|
|
63
|
+
});
|
|
64
|
+
it('should return 403 when sendProgress method called with invalid transferId', async () => {
|
|
65
|
+
const returnedValue = migration_1.migration.sendProgress(invalidTransferId, progress);
|
|
66
|
+
await expect(returnedValue).rejects.toThrow(migration_1.APIError.forStatus(403, 'Forbidden'));
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe('getMappingById', () => {
|
|
70
|
+
beforeAll(() => {
|
|
71
|
+
keys = ['email/abc@example.com', 'email/admin@example.com'];
|
|
72
|
+
invalidKeys = ['email/abc@example.com', 'email/admin@example'];
|
|
73
|
+
namespace = 'identity:user';
|
|
74
|
+
mappingByKeyResponseWithKeys =
|
|
75
|
+
'{"email/abc@example.com":"5a20a4d7c6bd4a32df3a711","email/admin@example.com":"5a29c4ae20cfc31b0dc0e112"}';
|
|
76
|
+
mappingByKeyResponseWithInvalidKeys = '{"email/abc@example.com":"5a20a4d7c6bd4a32df3a711"}';
|
|
77
|
+
jest_when_1.when(global.api.asApp().__requestAtlassian)
|
|
78
|
+
.calledWith(`/app/migration/forge/v1/mapping/${transferId}/find?namespace=${namespace}`, {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: {
|
|
81
|
+
'content-type': 'application/json'
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify(keys)
|
|
84
|
+
})
|
|
85
|
+
.mockReturnValue({
|
|
86
|
+
status: 200,
|
|
87
|
+
text: jest.fn().mockResolvedValue(mappingByKeyResponseWithKeys),
|
|
88
|
+
json: jest.fn().mockResolvedValue(JSON.parse(mappingByKeyResponseWithKeys))
|
|
89
|
+
})
|
|
90
|
+
.calledWith(`/app/migration/forge/v1/mapping/${transferId}/find?namespace=${namespace}`, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: {
|
|
93
|
+
'content-type': 'application/json'
|
|
94
|
+
},
|
|
95
|
+
body: JSON.stringify(invalidKeys)
|
|
96
|
+
})
|
|
97
|
+
.mockReturnValue({
|
|
98
|
+
status: 200,
|
|
99
|
+
text: jest.fn().mockResolvedValue(mappingByKeyResponseWithInvalidKeys),
|
|
100
|
+
json: jest.fn().mockResolvedValue(JSON.parse(mappingByKeyResponseWithInvalidKeys))
|
|
101
|
+
})
|
|
102
|
+
.calledWith(`/app/migration/forge/v1/mapping/${invalidTransferId}/find?namespace=${namespace}`, {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
headers: {
|
|
105
|
+
'content-type': 'application/json'
|
|
106
|
+
},
|
|
107
|
+
body: JSON.stringify(keys)
|
|
108
|
+
})
|
|
109
|
+
.mockReturnValue({
|
|
110
|
+
status: 403,
|
|
111
|
+
statusText: 'Forbidden'
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
it('should return MappingResponse when getMappingById method called with valid transferId', async () => {
|
|
115
|
+
const returnedValue = await migration_1.migration.getMappingById(transferId, namespace, keys);
|
|
116
|
+
expect(JSON.stringify(returnedValue)).toEqual(mappingByKeyResponseWithKeys);
|
|
117
|
+
});
|
|
118
|
+
it('should return 403 when getMappingById method called with invalid transferId', async () => {
|
|
119
|
+
const returnedValue = migration_1.migration.getMappingById(invalidTransferId, namespace, keys);
|
|
120
|
+
await expect(returnedValue).rejects.toThrow(migration_1.APIError.forStatus(403, 'Forbidden'));
|
|
121
|
+
});
|
|
122
|
+
it('should return MappingResponse except for invalid keys when getMappingById method called with valid transferId', async () => {
|
|
123
|
+
const returnedValue = await migration_1.migration.getMappingById(transferId, namespace, invalidKeys);
|
|
124
|
+
expect(JSON.stringify(returnedValue)).toEqual(mappingByKeyResponseWithInvalidKeys);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
describe('getAppDataList', () => {
|
|
128
|
+
beforeAll(() => {
|
|
129
|
+
appDataListResponse =
|
|
130
|
+
'[{"s3Key":"2ea231e3-ab0d-4236-97f7-26f951df1c11","label":null},{"s3Key":"317a3631-2fef-4940-a418-7096e971eb11","label":null}]';
|
|
131
|
+
jest_when_1.when(global.api.asApp().__requestAtlassian)
|
|
132
|
+
.calledWith(`/app/migration/forge/v1/data/${transferId}/all`)
|
|
133
|
+
.mockReturnValue({
|
|
134
|
+
status: 200,
|
|
135
|
+
text: jest.fn().mockResolvedValue(appDataListResponse),
|
|
136
|
+
json: jest.fn().mockResolvedValue(JSON.parse(appDataListResponse))
|
|
137
|
+
})
|
|
138
|
+
.calledWith(`/app/migration/forge/v1/data/${invalidTransferId}/all`)
|
|
139
|
+
.mockReturnValue({
|
|
140
|
+
status: 403,
|
|
141
|
+
statusText: 'Forbidden'
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
it('should return AppDataListResponse when getAppDataList method called with valid transferId', async () => {
|
|
145
|
+
const returnedValue = await migration_1.migration.getAppDataList(transferId);
|
|
146
|
+
expect(returnedValue).toEqual(JSON.parse(appDataListResponse));
|
|
147
|
+
});
|
|
148
|
+
it('should return 403 when getAppDataList method called with invalid transferId', async () => {
|
|
149
|
+
const returnedValue = migration_1.migration.getAppDataList(invalidTransferId);
|
|
150
|
+
await expect(returnedValue).rejects.toThrow(migration_1.APIError.forStatus(403, 'Forbidden'));
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
describe('getAppDataPayload', () => {
|
|
154
|
+
beforeAll(() => {
|
|
155
|
+
s3Key = '7941b65d-094b-4305-97f2-e2b412c6db0a';
|
|
156
|
+
invalidS3Key = '7941b65d-094b-4305-97f2-e2b412c000';
|
|
157
|
+
appDataPayloadResponse = '{ "url": "https://rps--stg-east--app-migration-service--ams.s3.amazonaws.com/test"}';
|
|
158
|
+
utils_1.mocked(node_fetch_1.default).mockImplementation(() => Promise.resolve({
|
|
159
|
+
text: () => Promise.resolve('Sample app data')
|
|
160
|
+
}));
|
|
161
|
+
jest_when_1.when(global.api.asApp().__requestAtlassian)
|
|
162
|
+
.calledWith(`/app/migration/forge/v1/data/${s3Key}`)
|
|
163
|
+
.mockReturnValue({
|
|
164
|
+
status: 200,
|
|
165
|
+
text: jest.fn().mockResolvedValue(JSON.stringify(appDataPayloadResponse)),
|
|
166
|
+
json: jest.fn().mockResolvedValue(JSON.parse(appDataPayloadResponse))
|
|
167
|
+
})
|
|
168
|
+
.calledWith(`/app/migration/forge/v1/data/${invalidS3Key}`)
|
|
169
|
+
.mockReturnValue({
|
|
170
|
+
status: 403,
|
|
171
|
+
statusText: 'Forbidden'
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
it('should return Response when getAppDataPayload method called with valid S3Key', async () => {
|
|
175
|
+
const returnedValue = await migration_1.migration.getAppDataPayload(s3Key);
|
|
176
|
+
await expect(returnedValue.text()).resolves.toBe('Sample app data');
|
|
177
|
+
});
|
|
178
|
+
it('should return 403 when getAppDataPayload method called with invalid S3Key', async () => {
|
|
179
|
+
const returnedValue = migration_1.migration.getAppDataPayload(invalidS3Key);
|
|
180
|
+
await expect(returnedValue).rejects.toThrow(migration_1.APIError.forStatus(403, 'Forbidden'));
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe('updateFeedback', () => {
|
|
184
|
+
beforeAll(() => {
|
|
185
|
+
feedbackPayload = { transferId: transferId, msg: 'Current Data processed, send next' };
|
|
186
|
+
jest_when_1.when(global.api.asApp().__requestAtlassian)
|
|
187
|
+
.calledWith(`/app/migration/forge/v1/feedback/${transferId}`, {
|
|
188
|
+
method: 'POST',
|
|
189
|
+
headers: {
|
|
190
|
+
'content-type': 'application/json'
|
|
191
|
+
},
|
|
192
|
+
body: JSON.stringify(feedbackPayload)
|
|
193
|
+
})
|
|
194
|
+
.mockReturnValue({
|
|
195
|
+
status: 200,
|
|
196
|
+
text: jest.fn().mockImplementationOnce(() => Promise.resolve()),
|
|
197
|
+
json: jest.fn().mockImplementationOnce(() => Promise.resolve())
|
|
198
|
+
})
|
|
199
|
+
.calledWith(`/app/migration/forge/v1/feedback/${invalidTransferId}`, {
|
|
200
|
+
method: 'POST',
|
|
201
|
+
headers: {
|
|
202
|
+
'content-type': 'application/json'
|
|
203
|
+
},
|
|
204
|
+
body: JSON.stringify(feedbackPayload)
|
|
205
|
+
})
|
|
206
|
+
.mockReturnValue({
|
|
207
|
+
status: 403,
|
|
208
|
+
statusText: 'Forbidden'
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
it('should send feedback when updateFeedback method called with valid transferId', async () => {
|
|
212
|
+
const returnedValue = migration_1.migration.updateFeedback(transferId, feedbackPayload);
|
|
213
|
+
await expect(returnedValue).resolves.toBeUndefined();
|
|
214
|
+
});
|
|
215
|
+
it('should return 403 when updateFeedback method called with invalid transferId', async () => {
|
|
216
|
+
const returnedValue = migration_1.migration.updateFeedback(invalidTransferId, feedbackPayload);
|
|
217
|
+
await expect(returnedValue).rejects.toThrow(migration_1.APIError.forStatus(403, 'Forbidden'));
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
});
|
package/out/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
package/out/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.migration = void 0;
|
|
4
|
+
const migration_1 = require("./migration");
|
|
5
|
+
Object.defineProperty(exports, "migration", { enumerable: true, get: function () { return migration_1.migration; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/migration/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,QAAS,SAAQ,KAAK;IACjC,OAAO;WAIO,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ;WAIpD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ;CAGvD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.APIError = void 0;
|
|
4
|
+
class APIError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
}
|
|
8
|
+
static forStatus(status, message) {
|
|
9
|
+
return new APIError(message);
|
|
10
|
+
}
|
|
11
|
+
static forUnexpected(message) {
|
|
12
|
+
return new APIError(message);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.APIError = APIError;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { MigrationAdapter, Progress } from './migration-adaptor';
|
|
2
|
+
export { APIError } from './errors';
|
|
3
|
+
export declare const getMigrationInstance: (adapter: MigrationAdapter) => {
|
|
4
|
+
sendProgress: (transferId: string, progress: Progress) => Promise<void>;
|
|
5
|
+
getMappingById: (transferId: string, namespace: string, keys: Array<string>) => Promise<import("./migration-adaptor").MappingResponse>;
|
|
6
|
+
updateFeedback: (transferId: string, feedback: object) => Promise<void>;
|
|
7
|
+
getAppDataList: (transferId: string) => Promise<import("./migration-adaptor").AppDataListResponse>;
|
|
8
|
+
getAppDataPayload: (s3Key: string) => Promise<import("node-fetch").Response>;
|
|
9
|
+
};
|
|
10
|
+
export declare const migration: {
|
|
11
|
+
sendProgress: (transferId: string, progress: Progress) => Promise<void>;
|
|
12
|
+
getMappingById: (transferId: string, namespace: string, keys: Array<string>) => Promise<import("./migration-adaptor").MappingResponse>;
|
|
13
|
+
updateFeedback: (transferId: string, feedback: object) => Promise<void>;
|
|
14
|
+
getAppDataList: (transferId: string) => Promise<import("./migration-adaptor").AppDataListResponse>;
|
|
15
|
+
getAppDataPayload: (s3Key: string) => Promise<import("node-fetch").Response>;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migration/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,eAAO,MAAM,oBAAoB,YAAa,gBAAgB;+BAE/B,MAAM,YAAY,QAAQ;iCACxB,MAAM,aAAa,MAAM,QAAQ,KAAK,CAAC,MAAM,CAAC;iCAE9C,MAAM,YAAY,MAAM;iCACxB,MAAM;+BACR,MAAM;CAEpC,CAAC;AAEF,eAAO,MAAM,SAAS;+BACO,MAAM,YAAY,QAAQ;iCAExB,MAAM,aAAa,MAAM,QAAQ,KAAK,CAAC,MAAM,CAAC;iCAE9C,MAAM,YAAY,MAAM;iCAExB,MAAM;+BACR,MAAM;CAClC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.migration = exports.getMigrationInstance = void 0;
|
|
4
|
+
const migration_1 = require("./migration");
|
|
5
|
+
var errors_1 = require("./errors");
|
|
6
|
+
Object.defineProperty(exports, "APIError", { enumerable: true, get: function () { return errors_1.APIError; } });
|
|
7
|
+
exports.getMigrationInstance = (adapter) => {
|
|
8
|
+
return {
|
|
9
|
+
sendProgress: (transferId, progress) => adapter.sendProgress(transferId, progress),
|
|
10
|
+
getMappingById: (transferId, namespace, keys) => adapter.getMappingById(transferId, namespace, keys),
|
|
11
|
+
updateFeedback: (transferId, feedback) => adapter.updateFeedback(transferId, feedback),
|
|
12
|
+
getAppDataList: (transferId) => adapter.getAppDataList(transferId),
|
|
13
|
+
getAppDataPayload: (s3Key) => adapter.getAppDataPayload(s3Key)
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
exports.migration = {
|
|
17
|
+
sendProgress: (transferId, progress) => exports.getMigrationInstance(new migration_1.Migration()).sendProgress(transferId, progress),
|
|
18
|
+
getMappingById: (transferId, namespace, keys) => exports.getMigrationInstance(new migration_1.Migration()).getMappingById(transferId, namespace, keys),
|
|
19
|
+
updateFeedback: (transferId, feedback) => exports.getMigrationInstance(new migration_1.Migration()).updateFeedback(transferId, feedback),
|
|
20
|
+
getAppDataList: (transferId) => exports.getMigrationInstance(new migration_1.Migration()).getAppDataList(transferId),
|
|
21
|
+
getAppDataPayload: (s3Key) => exports.getMigrationInstance(new migration_1.Migration()).getAppDataPayload(s3Key)
|
|
22
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Response } from 'node-fetch';
|
|
2
|
+
export interface MigrationAdapter {
|
|
3
|
+
sendProgress(transferId: string, progress: Progress): Promise<void>;
|
|
4
|
+
getMappingById(transferId: string, namespace: string, keys: Array<string>): Promise<MappingResponse>;
|
|
5
|
+
getAppDataList(transferId: string): Promise<AppDataListResponse>;
|
|
6
|
+
getAppDataPayload(s3Key: string): Promise<Response>;
|
|
7
|
+
updateFeedback(transferId: string, feedback: object): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export interface MappingResponse {
|
|
10
|
+
result: Map<string, string>;
|
|
11
|
+
}
|
|
12
|
+
export interface AppData {
|
|
13
|
+
s3Key: string;
|
|
14
|
+
label: string;
|
|
15
|
+
}
|
|
16
|
+
export interface AppDataListResponse {
|
|
17
|
+
result: Set<AppData>;
|
|
18
|
+
}
|
|
19
|
+
export interface AppDataPayloadResponse {
|
|
20
|
+
url: string;
|
|
21
|
+
}
|
|
22
|
+
export interface Progress {
|
|
23
|
+
status: string;
|
|
24
|
+
percent: number;
|
|
25
|
+
message: string;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=migration-adaptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-adaptor.d.ts","sourceRoot":"","sources":["../../src/migration/migration-adaptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACrG,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACjE,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpD,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrE;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Response } from 'node-fetch';
|
|
2
|
+
import { AppDataListResponse, MappingResponse, MigrationAdapter, Progress } from './migration-adaptor';
|
|
3
|
+
export declare class Migration implements MigrationAdapter {
|
|
4
|
+
private readonly basePath;
|
|
5
|
+
sendProgress: (transferId: string, progress: Progress) => Promise<void>;
|
|
6
|
+
getMappingById: (transferId: string, namespace: string, keys: Array<string>) => Promise<MappingResponse>;
|
|
7
|
+
getAppDataList: (transferId: string) => Promise<AppDataListResponse>;
|
|
8
|
+
getAppDataPayload: (s3Key: string) => Promise<Response>;
|
|
9
|
+
updateFeedback: (transferId: string, feedback: object) => Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../../src/migration/migration.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EACL,mBAAmB,EAEnB,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACT,MAAM,qBAAqB,CAAC;AAI7B,qBAAa,SAAU,YAAW,gBAAgB;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IAEtD,YAAY,eAAsB,MAAM,YAAY,QAAQ,KAAG,OAAO,CAAC,IAAI,CAAC,CAG1E;IAEF,cAAc,eAAsB,MAAM,aAAa,MAAM,QAAQ,KAAK,CAAC,MAAM,CAAC,KAAG,OAAO,CAAC,eAAe,CAAC,CAG3G;IAEF,cAAc,eAAsB,MAAM,KAAG,OAAO,CAAC,mBAAmB,CAAC,CAGvE;IAEF,iBAAiB,UAAiB,MAAM,KAAG,OAAO,CAAC,QAAQ,CAAC,CAI1D;IAEF,cAAc,eAAsB,MAAM,YAAY,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAG1E;CACH"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Migration = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
|
|
6
|
+
const utils_1 = require("./utils");
|
|
7
|
+
class Migration {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.basePath = '/app/migration/forge/v1';
|
|
10
|
+
this.sendProgress = async (transferId, progress) => {
|
|
11
|
+
const result = await utils_1.invokePOSTApi(`${this.basePath}/progress/${transferId}`, progress);
|
|
12
|
+
return utils_1.getResponseBody(result);
|
|
13
|
+
};
|
|
14
|
+
this.getMappingById = async (transferId, namespace, keys) => {
|
|
15
|
+
const result = await utils_1.invokePOSTApi(`${this.basePath}/mapping/${transferId}/find?namespace=${namespace}`, keys);
|
|
16
|
+
return utils_1.getResponseBody(result);
|
|
17
|
+
};
|
|
18
|
+
this.getAppDataList = async (transferId) => {
|
|
19
|
+
const result = await utils_1.invokeGETApi(`${this.basePath}/data/${transferId}/all`);
|
|
20
|
+
return utils_1.getResponseBody(result);
|
|
21
|
+
};
|
|
22
|
+
this.getAppDataPayload = async (s3Key) => {
|
|
23
|
+
const result = await utils_1.invokeGETApi(`${this.basePath}/data/${s3Key}`);
|
|
24
|
+
const responseBody = await utils_1.getResponseBody(result);
|
|
25
|
+
return node_fetch_1.default(responseBody.url);
|
|
26
|
+
};
|
|
27
|
+
this.updateFeedback = async (transferId, feedback) => {
|
|
28
|
+
const result = await utils_1.invokePOSTApi(`${this.basePath}/feedback/${transferId}`, feedback);
|
|
29
|
+
return utils_1.getResponseBody(result);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.Migration = Migration;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { APIResponse } from '@forge/api';
|
|
2
|
+
export declare function getResponseBody(response: APIResponse): Promise<any>;
|
|
3
|
+
export declare function invokePOSTApi(url: string, payload: any): Promise<APIResponse>;
|
|
4
|
+
export declare function invokeGETApi(url: string): Promise<APIResponse>;
|
|
5
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/migration/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC,wBAAsB,eAAe,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAazE;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAQnF;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAEpE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.invokeGETApi = exports.invokePOSTApi = exports.getResponseBody = void 0;
|
|
4
|
+
const errors_1 = require("./errors");
|
|
5
|
+
async function getResponseBody(response) {
|
|
6
|
+
if (response.status !== 200) {
|
|
7
|
+
throw errors_1.APIError.forStatus(response.status, response.statusText);
|
|
8
|
+
}
|
|
9
|
+
const responseText = await response.text();
|
|
10
|
+
try {
|
|
11
|
+
if (responseText !== '' && responseText !== undefined) {
|
|
12
|
+
return JSON.parse(responseText);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
throw errors_1.APIError.forUnexpected(`Response text was not a valid JSON: ${responseText}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.getResponseBody = getResponseBody;
|
|
20
|
+
async function invokePOSTApi(url, payload) {
|
|
21
|
+
return global.api.asApp().__requestAtlassian(url, {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
headers: {
|
|
24
|
+
'content-type': 'application/json'
|
|
25
|
+
},
|
|
26
|
+
body: JSON.stringify(payload)
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
exports.invokePOSTApi = invokePOSTApi;
|
|
30
|
+
async function invokeGETApi(url) {
|
|
31
|
+
return global.api.asApp().__requestAtlassian(url);
|
|
32
|
+
}
|
|
33
|
+
exports.invokeGETApi = invokeGETApi;
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@forge/migrations",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "App migration methods for Forge app",
|
|
5
|
+
"main": "out/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "yarn run clean && yarn run compile",
|
|
8
|
+
"clean": "rm -rf ./out && rm -f tsconfig.tsbuildinfo",
|
|
9
|
+
"compile": "tsc -b -v"
|
|
10
|
+
},
|
|
11
|
+
"author": "Atlassian",
|
|
12
|
+
"license": "UNLICENSED",
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@types/node": "^12.12.63",
|
|
15
|
+
"@types/node-fetch": "^2.5.7",
|
|
16
|
+
"node-fetch": "2.6.1"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@forge/api": "^2.6.1"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { APIError } from '../migration';
|
|
2
|
+
|
|
3
|
+
describe('APIError', () => {
|
|
4
|
+
describe('forStatus', () => {
|
|
5
|
+
it.each([
|
|
6
|
+
[400, 'Bad request'],
|
|
7
|
+
[401, 'Unauthorized'],
|
|
8
|
+
[403, 'Forbidden'],
|
|
9
|
+
[404, 'Not Found'],
|
|
10
|
+
[500, 'Internal server error']
|
|
11
|
+
] as const)('should contain the correct error message for a code of %s', (status, message) => {
|
|
12
|
+
expect(APIError.forStatus(status, message).message).toEqual(message);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
});
|