@forge/migrations 0.0.0-experimental-416047f
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/CHANGELOG.md +13 -0
- 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 +221 -0
- package/out/__test__/query-api.test.d.ts +2 -0
- package/out/__test__/query-api.test.d.ts.map +1 -0
- package/out/__test__/query-api.test.js +144 -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 +22 -0
- package/out/migration/index.d.ts.map +1 -0
- package/out/migration/index.js +25 -0
- package/out/migration/migration-adaptor.d.ts +60 -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 +14 -0
- package/out/migration/migration.d.ts.map +1 -0
- package/out/migration/migration.js +81 -0
- package/out/migration/query-api.d.ts +11 -0
- package/out/migration/query-api.d.ts.map +1 -0
- package/out/migration/query-api.js +22 -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 +243 -0
- package/src/__test__/query-api.test.ts +177 -0
- package/src/index.ts +3 -0
- package/src/migration/errors.ts +13 -0
- package/src/migration/index.ts +33 -0
- package/src/migration/migration-adaptor.ts +71 -0
- package/src/migration/migration.ts +111 -0
- package/src/migration/query-api.ts +24 -0
- package/src/migration/utils.ts +31 -0
- package/tsconfig.json +13 -0
- package/tsconfig.tsbuildinfo +1504 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Migration } from './migration';
|
|
2
|
+
import { Progress } from './migration-adaptor';
|
|
3
|
+
import { DefaultQueryBuilder } from './query-api';
|
|
4
|
+
|
|
5
|
+
export const getMigrationInstance = (adapter: Migration) => {
|
|
6
|
+
return {
|
|
7
|
+
sendProgress: (transferId: string, progress: Progress) => adapter.sendProgress(transferId, progress),
|
|
8
|
+
getMappingById: (transferId: string, namespace: string, keys: Array<string>) =>
|
|
9
|
+
adapter.getMappingById(transferId, namespace, keys),
|
|
10
|
+
updateFeedback: (transferId: string, feedback: object) => adapter.updateFeedback(transferId, feedback),
|
|
11
|
+
getAppDataList: (transferId: string) => adapter.getAppDataList(transferId),
|
|
12
|
+
getAppDataPayload: (s3Key: string) => adapter.getAppDataPayload(s3Key),
|
|
13
|
+
getMappings: (transferId: string, namespace: string) =>
|
|
14
|
+
new DefaultQueryBuilder(adapter, { transferId: transferId, namespace: namespace }),
|
|
15
|
+
getContainers: (transferId: string, containerType: string) =>
|
|
16
|
+
new DefaultQueryBuilder(adapter, { transferId: transferId, containerType: containerType })
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const migration = {
|
|
21
|
+
sendProgress: (transferId: string, progress: Progress) =>
|
|
22
|
+
getMigrationInstance(new Migration()).sendProgress(transferId, progress),
|
|
23
|
+
getMappingById: (transferId: string, namespace: string, keys: Array<string>) =>
|
|
24
|
+
getMigrationInstance(new Migration()).getMappingById(transferId, namespace, keys),
|
|
25
|
+
updateFeedback: (transferId: string, feedback: object) =>
|
|
26
|
+
getMigrationInstance(new Migration()).updateFeedback(transferId, feedback),
|
|
27
|
+
getAppDataList: (transferId: string) => getMigrationInstance(new Migration()).getAppDataList(transferId),
|
|
28
|
+
getAppDataPayload: (s3Key: string) => getMigrationInstance(new Migration()).getAppDataPayload(s3Key),
|
|
29
|
+
getMappings: (transferId: string, namespace: string) =>
|
|
30
|
+
getMigrationInstance(new Migration()).getMappings(transferId, namespace),
|
|
31
|
+
getContainers: (transferId: string, containerType: string) =>
|
|
32
|
+
getMigrationInstance(new Migration()).getContainers(transferId, containerType)
|
|
33
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Response } from 'node-fetch';
|
|
2
|
+
|
|
3
|
+
export interface MigrationAdapter {
|
|
4
|
+
sendProgress(transferId: string, progress: Progress): Promise<void>;
|
|
5
|
+
getMappingById(transferId: string, namespace: string, keys: Array<string>): Promise<MappingResponse>;
|
|
6
|
+
getAppDataList(transferId: string): Promise<AppDataListResponse>;
|
|
7
|
+
getAppDataPayload(s3Key: string): Promise<Response>;
|
|
8
|
+
updateFeedback(transferId: string, feedback: object): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface MappingResponse {
|
|
12
|
+
result: Map<string, string>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface AppData {
|
|
16
|
+
s3Key: string;
|
|
17
|
+
label: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface AppDataListResponse {
|
|
21
|
+
result: Set<AppData>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface AppDataPayloadResponse {
|
|
25
|
+
url: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface Progress {
|
|
29
|
+
status: string;
|
|
30
|
+
percent: number;
|
|
31
|
+
message: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface PaginationMappingResponse {
|
|
35
|
+
meta: PaginationResponseMeta;
|
|
36
|
+
items: Map<string, string>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface PaginationResponseMeta {
|
|
40
|
+
pageSize: number;
|
|
41
|
+
hasNext: boolean;
|
|
42
|
+
lastEntity?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface PaginationContainerResponse {
|
|
46
|
+
containers: Array<object>;
|
|
47
|
+
meta: PaginationResponseMeta;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface ListOptions {
|
|
51
|
+
transferId: string;
|
|
52
|
+
namespace?: string;
|
|
53
|
+
containerType?: string;
|
|
54
|
+
cursor?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface Result {
|
|
58
|
+
key: string;
|
|
59
|
+
value: object;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ListResults {
|
|
63
|
+
results: Result[];
|
|
64
|
+
nextCursor?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface QueryBuilder {
|
|
68
|
+
cursor(cursor: string): QueryBuilder;
|
|
69
|
+
getMany(): Promise<ListResults>;
|
|
70
|
+
getOne(): Promise<Result | undefined>;
|
|
71
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import fetch, { Response } from 'node-fetch';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
AppDataListResponse,
|
|
5
|
+
AppDataPayloadResponse,
|
|
6
|
+
MappingResponse,
|
|
7
|
+
MigrationAdapter,
|
|
8
|
+
Progress,
|
|
9
|
+
ListOptions,
|
|
10
|
+
ListResults,
|
|
11
|
+
Result,
|
|
12
|
+
PaginationContainerResponse,
|
|
13
|
+
PaginationMappingResponse
|
|
14
|
+
} from './migration-adaptor';
|
|
15
|
+
|
|
16
|
+
import { getResponseBody, invokeGETApi, invokePOSTApi } from './utils';
|
|
17
|
+
|
|
18
|
+
export class Migration implements MigrationAdapter {
|
|
19
|
+
private readonly basePath = '/app/migration/forge/v1';
|
|
20
|
+
|
|
21
|
+
sendProgress = async (transferId: string, progress: Progress): Promise<void> => {
|
|
22
|
+
const result = await invokePOSTApi(`${this.basePath}/progress/${transferId}`, progress);
|
|
23
|
+
return getResponseBody(result);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
getMappingById = async (transferId: string, namespace: string, keys: Array<string>): Promise<MappingResponse> => {
|
|
27
|
+
const result = await invokePOSTApi(`${this.basePath}/mapping/${transferId}/find?namespace=${namespace}`, keys);
|
|
28
|
+
return getResponseBody(result);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
getAppDataList = async (transferId: string): Promise<AppDataListResponse> => {
|
|
32
|
+
const result = await invokeGETApi(`${this.basePath}/data/${transferId}/all`);
|
|
33
|
+
return getResponseBody(result);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
getAppDataPayload = async (s3Key: string): Promise<Response> => {
|
|
37
|
+
const result = await invokeGETApi(`${this.basePath}/data/${s3Key}`);
|
|
38
|
+
const responseBody: AppDataPayloadResponse = await getResponseBody(result);
|
|
39
|
+
return fetch(responseBody.url);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
updateFeedback = async (transferId: string, feedback: object): Promise<void> => {
|
|
43
|
+
const result = await invokePOSTApi(`${this.basePath}/feedback/${transferId}`, feedback);
|
|
44
|
+
return getResponseBody(result);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
getMappings = async (
|
|
48
|
+
transferId: string,
|
|
49
|
+
namespace?: string,
|
|
50
|
+
lastEntity?: string,
|
|
51
|
+
pageSize?: number
|
|
52
|
+
): Promise<ListResults> => {
|
|
53
|
+
let queryParams = `namespace=${namespace}`;
|
|
54
|
+
if (pageSize != undefined) {
|
|
55
|
+
queryParams = queryParams + `&pageSize=${pageSize}`;
|
|
56
|
+
}
|
|
57
|
+
if (lastEntity != undefined) {
|
|
58
|
+
queryParams = queryParams + `&lastEntity=${lastEntity}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const result = await invokeGETApi(`${this.basePath}/mapping/${transferId}/page?${queryParams}`);
|
|
62
|
+
const mappingResponse: PaginationMappingResponse = await getResponseBody(result);
|
|
63
|
+
|
|
64
|
+
let results: Result[] = [];
|
|
65
|
+
if (mappingResponse.items != null) {
|
|
66
|
+
results = Object.entries(mappingResponse.items).map((item) => {
|
|
67
|
+
return { key: item[0], value: item[1] as object };
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
results: results,
|
|
73
|
+
nextCursor: mappingResponse.meta.lastEntity != null ? mappingResponse.meta.lastEntity : undefined
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
getContainers = async (
|
|
78
|
+
transferId: string,
|
|
79
|
+
containerType?: string,
|
|
80
|
+
lastEntity?: string,
|
|
81
|
+
pageSize?: number
|
|
82
|
+
): Promise<ListResults> => {
|
|
83
|
+
let queryParams = `containerType=${containerType}`;
|
|
84
|
+
if (pageSize != undefined) {
|
|
85
|
+
queryParams = queryParams + `&pageSize=${pageSize}`;
|
|
86
|
+
}
|
|
87
|
+
if (lastEntity != undefined) {
|
|
88
|
+
queryParams = queryParams + `&lastEntity=${lastEntity}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const result = await invokeGETApi(`${this.basePath}/container/${transferId}/page?${queryParams}`);
|
|
92
|
+
const containerResponse: PaginationContainerResponse = await getResponseBody(result);
|
|
93
|
+
let results: Result[] = [];
|
|
94
|
+
if (containerResponse.containers != null) {
|
|
95
|
+
results = Object.entries(containerResponse.containers).map((item) => {
|
|
96
|
+
return { key: item[0], value: item[1] };
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
results: results,
|
|
102
|
+
nextCursor: containerResponse.meta.lastEntity != null ? containerResponse.meta.lastEntity : undefined
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
list = async (options: ListOptions, pageSize?: number): Promise<ListResults> => {
|
|
107
|
+
if (options.namespace != undefined)
|
|
108
|
+
return this.getMappings(options.transferId, options.namespace, options.cursor, pageSize);
|
|
109
|
+
else return this.getContainers(options.transferId, options.containerType, options.cursor, pageSize);
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { QueryBuilder, Result, ListOptions, ListResults } from './migration-adaptor';
|
|
2
|
+
import { Migration } from './migration';
|
|
3
|
+
|
|
4
|
+
export class DefaultQueryBuilder implements QueryBuilder {
|
|
5
|
+
constructor(private migration: Pick<Migration, 'list'>, private queryOptions: ListOptions) {}
|
|
6
|
+
|
|
7
|
+
cursor(cursor: string): QueryBuilder {
|
|
8
|
+
return new DefaultQueryBuilder(this.migration, {
|
|
9
|
+
...this.queryOptions,
|
|
10
|
+
cursor
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async getOne(): Promise<Result | undefined> {
|
|
15
|
+
const listResults = await this.migration.list(this.queryOptions, 1);
|
|
16
|
+
if (listResults.results && listResults.results.length > 0) {
|
|
17
|
+
return listResults.results[0];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async getMany(): Promise<ListResults> {
|
|
22
|
+
return this.migration.list(this.queryOptions);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { APIResponse } from '@forge/api';
|
|
2
|
+
import { APIError } from './errors';
|
|
3
|
+
|
|
4
|
+
export async function getResponseBody(response: APIResponse): Promise<any> {
|
|
5
|
+
if (response.status !== 200) {
|
|
6
|
+
throw APIError.forStatus(response.status, response.statusText);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const responseText = await response.text();
|
|
10
|
+
try {
|
|
11
|
+
if (responseText !== '' && responseText !== undefined) {
|
|
12
|
+
return JSON.parse(responseText);
|
|
13
|
+
}
|
|
14
|
+
} catch (error) {
|
|
15
|
+
throw APIError.forUnexpected(`Response text was not a valid JSON: ${responseText}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function invokePOSTApi(url: string, payload: any): Promise<APIResponse> {
|
|
20
|
+
return (global as any).api.asApp().__requestAtlassian(url, {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
headers: {
|
|
23
|
+
'content-type': 'application/json'
|
|
24
|
+
},
|
|
25
|
+
body: JSON.stringify(payload)
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function invokeGETApi(url: string): Promise<APIResponse> {
|
|
30
|
+
return (global as any).api.asApp().__requestAtlassian(url);
|
|
31
|
+
}
|