sb-mig 5.8.0 → 6.0.0-beta.10
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 +36 -2
- package/dist/api/assets/assets.d.ts +3 -1
- package/dist/api/assets/assets.js +60 -18
- package/dist/api/assets/assets.types.d.ts +35 -2
- package/dist/api/assets/index.d.ts +2 -1
- package/dist/api/assets/index.js +1 -1
- package/dist/api/components/components.js +8 -2
- package/dist/api/components/components.sync.js +35 -8
- package/dist/api/data-migration/component-data-migration.d.ts +8 -3
- package/dist/api/data-migration/component-data-migration.js +52 -7
- package/dist/api/data-migration/migration-run-log.d.ts +77 -0
- package/dist/api/data-migration/migration-run-log.js +130 -0
- package/dist/api/data-migration/write-summary.d.ts +7 -0
- package/dist/api/managementApi.d.ts +15 -0
- package/dist/api/stories/index.d.ts +1 -1
- package/dist/api/stories/index.js +1 -1
- package/dist/api/stories/stories.d.ts +35 -1
- package/dist/api/stories/stories.js +212 -7
- package/dist/api/stories/stories.types.d.ts +3 -0
- package/dist/api/testApi.d.ts +15 -0
- package/dist/api-v2/discover/discover.js +3 -3
- package/dist/api-v2/precompile/precompile.d.ts +5 -1
- package/dist/api-v2/precompile/precompile.js +16 -8
- package/dist/cli/cli-descriptions.d.ts +1 -1
- package/dist/cli/cli-descriptions.js +5 -0
- package/dist/cli/commands/migrate.js +23 -0
- package/dist/cli/commands/sync.js +4 -3
- package/dist/cli/index.js +7 -0
- package/dist/cli/utils/discover.js +28 -20
- package/dist/config/helper.js +6 -7
- package/dist/rollup/build-on-the-fly.d.ts +2 -1
- package/dist/rollup/build-on-the-fly.js +88 -6
- package/dist/utils/async-utils.d.ts +1 -0
- package/dist/utils/async-utils.js +16 -0
- package/dist/utils/files.d.ts +1 -0
- package/dist/utils/files.js +26 -15
- package/dist/utils/path-utils.d.ts +7 -4
- package/dist/utils/path-utils.js +11 -8
- package/dist-cjs/api/components/components.js +8 -2
- package/dist-cjs/api/components/components.sync.js +35 -8
- package/dist-cjs/api/stories/stories.js +217 -8
- package/dist-cjs/api-v2/discover/discover.js +2 -2
- package/dist-cjs/api-v2/precompile/precompile.js +16 -8
- package/dist-cjs/utils/async-utils.js +34 -0
- package/dist-cjs/utils/path-utils.js +11 -8
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ If you've found an issue or you have feature request - <a href="https://github.c
|
|
|
13
13
|
|
|
14
14
|
| | |
|
|
15
15
|
| ---- | ------------ |
|
|
16
|
-
| Node |
|
|
16
|
+
| Node | 22.x.x or >=24.x.x |
|
|
17
17
|
|
|
18
18
|
# 5.x.x version released!
|
|
19
19
|
|
|
@@ -32,7 +32,7 @@ If you've found an issue or you have feature request - <a href="https://github.c
|
|
|
32
32
|
|
|
33
33
|
## Breaking changes
|
|
34
34
|
|
|
35
|
-
- Please note that sb-mig no longer extends support for Node versions older than
|
|
35
|
+
- Please note that sb-mig no longer extends support for Node versions older than 22.x.x, as part of its adoption of native ESM support.
|
|
36
36
|
- The sb-mig backup command has now been aligned with all other commands, causing minor changes in its execution (although functionalities have been preserved).
|
|
37
37
|
|
|
38
38
|
Do not hesitate to get in touch if you encounter any issues or require further clarification on any points.
|
|
@@ -113,6 +113,40 @@ module.exports = {
|
|
|
113
113
|
};
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
+
## Programmatic asset API
|
|
117
|
+
|
|
118
|
+
The public management API exposes asset helpers for uploading a local file and updating existing asset metadata:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { managementApi } from "sb-mig/dist/api/managementApi.js";
|
|
122
|
+
|
|
123
|
+
await managementApi.assets.createAsset(
|
|
124
|
+
{
|
|
125
|
+
spaceId: "12345",
|
|
126
|
+
pathToFile: "./public/image.jpg",
|
|
127
|
+
payload: {
|
|
128
|
+
asset_folder_id: 67890,
|
|
129
|
+
validate_upload: 1,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
config,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
await managementApi.assets.updateAsset(
|
|
136
|
+
{
|
|
137
|
+
spaceId: "12345",
|
|
138
|
+
assetId: 98765,
|
|
139
|
+
payload: {
|
|
140
|
+
meta_data: {
|
|
141
|
+
alt: "Image alt text",
|
|
142
|
+
title: "Image title",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
config,
|
|
147
|
+
);
|
|
148
|
+
```
|
|
149
|
+
|
|
116
150
|
# Schema documentation:
|
|
117
151
|
|
|
118
152
|
## Basics
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { GetAllAssets, GetAssetById, GetAssetByName, MigrateAsset } from "./assets.types.js";
|
|
1
|
+
import type { CreateAsset, GetAllAssets, GetAssetById, GetAssetByName, MigrateAsset, UpdateAsset } from "./assets.types.js";
|
|
2
2
|
export declare const getAllAssets: GetAllAssets;
|
|
3
3
|
export declare const getAssetByName: GetAssetByName;
|
|
4
4
|
export declare const migrateAsset: MigrateAsset;
|
|
5
|
+
export declare const createAsset: CreateAsset;
|
|
6
|
+
export declare const updateAsset: UpdateAsset;
|
|
5
7
|
export declare const getAssetById: GetAssetById;
|
|
6
8
|
export declare const getAsset: (assetName: string | undefined) => Promise<void>;
|
|
@@ -5,6 +5,24 @@ import FormData from "form-data";
|
|
|
5
5
|
import { createDir, isDirectoryExists } from "../../utils/files.js";
|
|
6
6
|
import Logger from "../../utils/logger.js";
|
|
7
7
|
import { getFileName, getSizeFromURL } from "../../utils/string-utils.js";
|
|
8
|
+
const isStoryblokSize = (size) => Boolean(size && /^\d+x\d+$/i.test(size));
|
|
9
|
+
const prepareSignedUploadPayload = (payload) => {
|
|
10
|
+
const { filename, asset_folder_id, id, size, validate_upload } = payload;
|
|
11
|
+
const inferredSize = isStoryblokSize(size)
|
|
12
|
+
? size
|
|
13
|
+
: isStoryblokSize(getSizeFromURL(filename))
|
|
14
|
+
? getSizeFromURL(filename)
|
|
15
|
+
: undefined;
|
|
16
|
+
return {
|
|
17
|
+
filename: getFileName(filename),
|
|
18
|
+
...(asset_folder_id === undefined || asset_folder_id === null
|
|
19
|
+
? {}
|
|
20
|
+
: { asset_folder_id }),
|
|
21
|
+
...(id === undefined ? {} : { id }),
|
|
22
|
+
...(inferredSize ? { size: inferredSize } : {}),
|
|
23
|
+
...(validate_upload === undefined ? {} : { validate_upload }),
|
|
24
|
+
};
|
|
25
|
+
};
|
|
8
26
|
// GET
|
|
9
27
|
export const getAllAssets = async (args, config) => {
|
|
10
28
|
const { spaceId, search } = args;
|
|
@@ -38,18 +56,12 @@ export const getAssetByName = async ({ spaceId, fileName }, config) => {
|
|
|
38
56
|
};
|
|
39
57
|
const requestSignedUploadUrl = ({ spaceId, payload }, config) => {
|
|
40
58
|
const { sbApi, debug } = config;
|
|
41
|
-
const
|
|
42
|
-
const filename = getFileName(payload.filename);
|
|
43
|
-
const size = getSizeFromURL(payload.filename);
|
|
59
|
+
const signedUploadPayload = prepareSignedUploadPayload(payload);
|
|
44
60
|
return sbApi
|
|
45
|
-
.post(`spaces/${spaceId}/assets/`,
|
|
46
|
-
filename,
|
|
47
|
-
size,
|
|
48
|
-
...restPayload,
|
|
49
|
-
})
|
|
61
|
+
.post(`spaces/${spaceId}/assets/`, signedUploadPayload)
|
|
50
62
|
.then((signedResponseObject) => {
|
|
51
63
|
if (debug) {
|
|
52
|
-
Logger.log(`Signed upload URL has been requested for ${filename}.`);
|
|
64
|
+
Logger.log(`Signed upload URL has been requested for ${signedUploadPayload.filename}.`);
|
|
53
65
|
}
|
|
54
66
|
return signedResponseObject.data; // this is very bad... but storyblok-js-client types are pretty broken
|
|
55
67
|
})
|
|
@@ -67,15 +79,20 @@ const uploadFile = ({ signedResponseObject, pathToFile }) => {
|
|
|
67
79
|
// also append the file read stream
|
|
68
80
|
form.append("file", fs.createReadStream(file));
|
|
69
81
|
// submit your form
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
form.submit(signedResponseObject.post_url, (err, res) => {
|
|
84
|
+
if (err) {
|
|
85
|
+
reject(err);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const statusCode = res?.statusCode;
|
|
89
|
+
if (statusCode === 204) {
|
|
90
|
+
Logger.upload(`Asset uploaded ${getFileName(file)}`);
|
|
91
|
+
resolve();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
reject(new Error(`Asset upload failed with status code ${statusCode ?? "unknown"}`));
|
|
95
|
+
});
|
|
79
96
|
});
|
|
80
97
|
};
|
|
81
98
|
const downloadAsset = async (args, config) => {
|
|
@@ -121,6 +138,31 @@ export const migrateAsset = async ({ migrateTo, payload, syncDirection }, config
|
|
|
121
138
|
}
|
|
122
139
|
return true;
|
|
123
140
|
};
|
|
141
|
+
export const createAsset = async ({ spaceId, pathToFile, payload = {} }, config) => {
|
|
142
|
+
const signedResponseObject = await requestSignedUploadUrl({
|
|
143
|
+
spaceId,
|
|
144
|
+
payload: {
|
|
145
|
+
...payload,
|
|
146
|
+
filename: payload.filename ?? pathToFile,
|
|
147
|
+
},
|
|
148
|
+
}, config);
|
|
149
|
+
await uploadFile({ signedResponseObject, pathToFile });
|
|
150
|
+
return signedResponseObject;
|
|
151
|
+
};
|
|
152
|
+
export const updateAsset = async ({ spaceId, assetId, payload }, config) => {
|
|
153
|
+
const { sbApi } = config;
|
|
154
|
+
Logger.log(`Trying to update asset with id ${assetId}.`);
|
|
155
|
+
return sbApi
|
|
156
|
+
.put(`spaces/${spaceId}/assets/${assetId}`, payload)
|
|
157
|
+
.then((res) => {
|
|
158
|
+
Logger.success(`Asset '${assetId}' has been updated.`);
|
|
159
|
+
return res.data;
|
|
160
|
+
})
|
|
161
|
+
.catch((err) => {
|
|
162
|
+
Logger.error(`${err.message} in updateAsset function for asset ${assetId}`);
|
|
163
|
+
throw err;
|
|
164
|
+
});
|
|
165
|
+
};
|
|
124
166
|
// GET
|
|
125
167
|
export const getAssetById = async ({ spaceId, assetId }, config) => {
|
|
126
168
|
const { sbApi } = config;
|
|
@@ -35,6 +35,29 @@ export interface SBAllAssetRequestResult {
|
|
|
35
35
|
}
|
|
36
36
|
export type SignedResponseObject = any;
|
|
37
37
|
export type AssetPayload = Omit<SBAsset, "updated_at" | "created_at" | "id">;
|
|
38
|
+
export interface SignedUploadPayload {
|
|
39
|
+
filename: string;
|
|
40
|
+
id?: number;
|
|
41
|
+
asset_folder_id?: number | null;
|
|
42
|
+
size?: string;
|
|
43
|
+
validate_upload?: number;
|
|
44
|
+
}
|
|
45
|
+
export type CreateAssetPayload = Omit<SignedUploadPayload, "filename" | "id"> & Partial<Pick<SignedUploadPayload, "filename">>;
|
|
46
|
+
export type UpdateAssetPayload = {
|
|
47
|
+
asset_folder_id?: number | null;
|
|
48
|
+
internal_tag_ids?: number[];
|
|
49
|
+
is_private?: boolean;
|
|
50
|
+
locked?: boolean;
|
|
51
|
+
meta_data?: {
|
|
52
|
+
alt?: string;
|
|
53
|
+
copyright?: string;
|
|
54
|
+
source?: string;
|
|
55
|
+
title?: string;
|
|
56
|
+
[key: string]: unknown;
|
|
57
|
+
};
|
|
58
|
+
publish_at?: string | null;
|
|
59
|
+
[key: string]: unknown;
|
|
60
|
+
};
|
|
38
61
|
export type GetAllAssets = ({ spaceId, }: {
|
|
39
62
|
spaceId: string;
|
|
40
63
|
search?: string;
|
|
@@ -52,16 +75,26 @@ export type MigrateAsset = ({ migrateTo, payload, syncDirection, }: {
|
|
|
52
75
|
payload: AssetPayload;
|
|
53
76
|
syncDirection: SyncDirection;
|
|
54
77
|
}, config: RequestBaseConfig) => Promise<boolean>;
|
|
78
|
+
export type CreateAsset = ({ spaceId, pathToFile, payload, }: {
|
|
79
|
+
spaceId: string;
|
|
80
|
+
pathToFile: string;
|
|
81
|
+
payload?: CreateAssetPayload;
|
|
82
|
+
}, config: RequestBaseConfig) => Promise<SignedResponseObject>;
|
|
83
|
+
export type UpdateAsset = ({ spaceId, assetId, payload, }: {
|
|
84
|
+
spaceId: string;
|
|
85
|
+
assetId: number;
|
|
86
|
+
payload: UpdateAssetPayload;
|
|
87
|
+
}, config: RequestBaseConfig) => Promise<any>;
|
|
55
88
|
export type UploadFile = ({ signedResponseObject, pathToFile, }: {
|
|
56
89
|
signedResponseObject: SignedResponseObject;
|
|
57
90
|
pathToFile: string;
|
|
58
|
-
}) => void
|
|
91
|
+
}) => Promise<void>;
|
|
59
92
|
export type FinalizeUpload = ({ signedResponseObject, }: {
|
|
60
93
|
signedResponseObject: SignedResponseObject;
|
|
61
94
|
}) => void;
|
|
62
95
|
export type RequestSignedUploadUrl = ({ spaceId, payload, }: {
|
|
63
96
|
spaceId: string;
|
|
64
|
-
payload: AssetPayload;
|
|
97
|
+
payload: AssetPayload | SignedUploadPayload;
|
|
65
98
|
}, config: RequestBaseConfig) => Promise<any>;
|
|
66
99
|
export type DownloadAsset = (args: {
|
|
67
100
|
payload: AssetPayload;
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { getAllAssets, migrateAsset, getAsset, getAssetById, getAssetByName, } from "./assets.js";
|
|
1
|
+
export { getAllAssets, createAsset, migrateAsset, getAsset, getAssetById, getAssetByName, updateAsset, } from "./assets.js";
|
|
2
|
+
export type { CreateAssetPayload, SignedUploadPayload, UpdateAssetPayload, } from "./assets.types.js";
|
package/dist/api/assets/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { getAllAssets, migrateAsset, getAsset, getAssetById, getAssetByName, } from "./assets.js";
|
|
1
|
+
export { getAllAssets, createAsset, migrateAsset, getAsset, getAssetById, getAssetByName, updateAsset, } from "./assets.js";
|
|
@@ -109,7 +109,10 @@ export const removeComponent = (component, config) => {
|
|
|
109
109
|
return sbApi
|
|
110
110
|
.delete(`spaces/${spaceId}/components/${id}`, {})
|
|
111
111
|
.then((res) => res.data)
|
|
112
|
-
.catch((err) =>
|
|
112
|
+
.catch((err) => {
|
|
113
|
+
console.error(err);
|
|
114
|
+
throw err;
|
|
115
|
+
});
|
|
113
116
|
};
|
|
114
117
|
/*
|
|
115
118
|
*
|
|
@@ -165,7 +168,10 @@ export const removeComponentGroup = (componentGroup, config) => {
|
|
|
165
168
|
return sbApi
|
|
166
169
|
.delete(`spaces/${spaceId}/component_groups/${id}`, {})
|
|
167
170
|
.then((res) => res.data)
|
|
168
|
-
.catch((err) =>
|
|
171
|
+
.catch((err) => {
|
|
172
|
+
console.error(err);
|
|
173
|
+
throw err;
|
|
174
|
+
});
|
|
169
175
|
};
|
|
170
176
|
export const createComponentsGroup = (groupName, config) => {
|
|
171
177
|
const { spaceId, sbApi } = config;
|
|
@@ -27,6 +27,9 @@ const defaultProgress = (event) => {
|
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
import { createComponent, createComponentsGroup, getAllComponents, getAllComponentsGroups, removeComponent, removeComponentGroup, updateComponent, } from "./components.js";
|
|
30
|
+
function getErrorMessage(error) {
|
|
31
|
+
return error instanceof Error ? error.message : String(error);
|
|
32
|
+
}
|
|
30
33
|
async function ensureComponentGroupsExist(groupNames, config, options = {}) {
|
|
31
34
|
try {
|
|
32
35
|
const existing = await getAllComponentsGroups(config);
|
|
@@ -79,10 +82,34 @@ export async function syncComponentsData(args, config) {
|
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
84
|
else {
|
|
82
|
-
|
|
83
|
-
...(existingComponents ?? []).map((
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
const removalTargets = [
|
|
86
|
+
...(existingComponents ?? []).map((component) => ({
|
|
87
|
+
type: "component",
|
|
88
|
+
name: String(component?.name ?? component?.id ?? "unknown"),
|
|
89
|
+
remove: () => removeComponent(component, config),
|
|
90
|
+
})),
|
|
91
|
+
...(existingGroups ?? []).map((group) => ({
|
|
92
|
+
type: "component group",
|
|
93
|
+
name: String(group?.name ?? group?.id ?? "unknown"),
|
|
94
|
+
remove: () => removeComponentGroup(group, config),
|
|
95
|
+
})),
|
|
96
|
+
];
|
|
97
|
+
const removalResults = await Promise.allSettled(removalTargets.map((target) => target.remove()));
|
|
98
|
+
removalResults.forEach((removalResult, index) => {
|
|
99
|
+
if (removalResult.status === "fulfilled")
|
|
100
|
+
return;
|
|
101
|
+
const target = removalTargets[index];
|
|
102
|
+
if (!target)
|
|
103
|
+
return;
|
|
104
|
+
const name = `${target.type} '${target.name}'`;
|
|
105
|
+
const message = getErrorMessage(removalResult.reason);
|
|
106
|
+
result.skipped.push(name);
|
|
107
|
+
result.errors.push({
|
|
108
|
+
name,
|
|
109
|
+
message: `SSOT removal failed: ${message}`,
|
|
110
|
+
});
|
|
111
|
+
Logger.warning(`Could not remove ${name} during SSOT sync: ${message}`);
|
|
112
|
+
});
|
|
86
113
|
}
|
|
87
114
|
}
|
|
88
115
|
const nonEmptyComponents = components.filter((c) => !isObjectEmpty(c));
|
|
@@ -164,7 +191,7 @@ export async function syncComponentsData(args, config) {
|
|
|
164
191
|
catch (error) {
|
|
165
192
|
result.errors.push({
|
|
166
193
|
name,
|
|
167
|
-
message:
|
|
194
|
+
message: getErrorMessage(error),
|
|
168
195
|
});
|
|
169
196
|
progress({
|
|
170
197
|
type: "progress",
|
|
@@ -172,7 +199,7 @@ export async function syncComponentsData(args, config) {
|
|
|
172
199
|
total: totalComponents,
|
|
173
200
|
name,
|
|
174
201
|
action: "error",
|
|
175
|
-
message:
|
|
202
|
+
message: getErrorMessage(error),
|
|
176
203
|
});
|
|
177
204
|
}
|
|
178
205
|
}
|
|
@@ -213,7 +240,7 @@ export async function syncComponentsData(args, config) {
|
|
|
213
240
|
catch (error) {
|
|
214
241
|
result.errors.push({
|
|
215
242
|
name,
|
|
216
|
-
message:
|
|
243
|
+
message: getErrorMessage(error),
|
|
217
244
|
});
|
|
218
245
|
progress({
|
|
219
246
|
type: "progress",
|
|
@@ -221,7 +248,7 @@ export async function syncComponentsData(args, config) {
|
|
|
221
248
|
total: totalComponents,
|
|
222
249
|
name,
|
|
223
250
|
action: "error",
|
|
224
|
-
message:
|
|
251
|
+
message: getErrorMessage(error),
|
|
225
252
|
});
|
|
226
253
|
}
|
|
227
254
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PublishLanguagesOption } from "../stories/stories.types.js";
|
|
1
2
|
import type { RequestBaseConfig } from "../utils/request.js";
|
|
2
3
|
import { type MigrationComponentAliasesByMigration, type MigrationComponentOverridesByMigration } from "./migration-component-scope.js";
|
|
3
4
|
import { type PreparedMigrationValidator } from "./migration-validation.js";
|
|
@@ -43,6 +44,8 @@ interface MigrateItems {
|
|
|
43
44
|
startsWith?: string;
|
|
44
45
|
};
|
|
45
46
|
dryRun?: boolean;
|
|
47
|
+
publish?: boolean;
|
|
48
|
+
publishLanguages?: PublishLanguagesOption;
|
|
46
49
|
fromFilePath?: string;
|
|
47
50
|
fileName?: string;
|
|
48
51
|
preparedMigrationConfigs?: PreparedMigrationConfig[];
|
|
@@ -67,8 +70,8 @@ export declare const runMigrationPipelineInMemory: ({ itemType, itemsToMigrate,
|
|
|
67
70
|
itemsToMigrate: any[];
|
|
68
71
|
preparedMigrationConfigs: PreparedMigrationConfig[];
|
|
69
72
|
}) => MigrationPipelineResult;
|
|
70
|
-
export declare const migrateAllComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, filters, dryRun, fromFilePath, fileName, migrationComponentAliases, migrationComponentOverrides, }: Omit<MigrateItems, "componentsToMigrate" | "preparedMigrationConfigs">, config: RequestBaseConfig) => Promise<void>;
|
|
71
|
-
export declare const doTheMigration: ({ itemType, from, itemsToMigrate, migrationConfig, migrationConfigs, to, dryRun, migrateFrom, fromFilePath, fileName, }: {
|
|
73
|
+
export declare const migrateAllComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, filters, dryRun, publish, publishLanguages, fromFilePath, fileName, migrationComponentAliases, migrationComponentOverrides, }: Omit<MigrateItems, "componentsToMigrate" | "preparedMigrationConfigs">, config: RequestBaseConfig) => Promise<void>;
|
|
74
|
+
export declare const doTheMigration: ({ itemType, from, itemsToMigrate, migrationConfig, migrationConfigs, to, dryRun, publish, publishLanguages, migrateFrom, fromFilePath, fileName, }: {
|
|
72
75
|
itemType?: "story" | "preset";
|
|
73
76
|
from: string;
|
|
74
77
|
itemsToMigrate: any[];
|
|
@@ -76,9 +79,11 @@ export declare const doTheMigration: ({ itemType, from, itemsToMigrate, migratio
|
|
|
76
79
|
migrationConfigs?: PreparedMigrationConfig[];
|
|
77
80
|
to: string;
|
|
78
81
|
dryRun?: boolean;
|
|
82
|
+
publish?: boolean;
|
|
83
|
+
publishLanguages?: PublishLanguagesOption;
|
|
79
84
|
migrateFrom: MigrateFrom;
|
|
80
85
|
fromFilePath?: string;
|
|
81
86
|
fileName?: string;
|
|
82
87
|
}, config: RequestBaseConfig) => Promise<void>;
|
|
83
|
-
export declare const migrateProvidedComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, filters, dryRun, fromFilePath, fileName, preparedMigrationConfigs, migrationComponentAliases, migrationComponentOverrides, }: MigrateItems, config: RequestBaseConfig) => Promise<void>;
|
|
88
|
+
export declare const migrateProvidedComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, filters, dryRun, publish, publishLanguages, fromFilePath, fileName, preparedMigrationConfigs, migrationComponentAliases, migrationComponentOverrides, }: MigrateItems, config: RequestBaseConfig) => Promise<void>;
|
|
84
89
|
export {};
|
|
@@ -9,6 +9,7 @@ import { isObjectEmpty } from "../../utils/object-utils.js";
|
|
|
9
9
|
import { managementApi } from "../managementApi.js";
|
|
10
10
|
import { buildPreMigrationBackupBaseName, resolveOutputFileBaseName, shouldUseDatestampForArtifacts, } from "./file-naming.js";
|
|
11
11
|
import { extendMigrationMapperWithAliases, resolveMigrationComponentsToMigrate, } from "./migration-component-scope.js";
|
|
12
|
+
import { saveMigrationRunLog } from "./migration-run-log.js";
|
|
12
13
|
import { discoverMigrationValidatorForMigrationFile, MigrationValidationFailedError, runPreparedMigrationValidator, } from "./migration-validation.js";
|
|
13
14
|
import { summarizeMutationWriteResults } from "./write-summary.js";
|
|
14
15
|
export const normalizeMigrationConfigNames = (migrationConfig) => {
|
|
@@ -245,7 +246,6 @@ const applySingleMigrationToItems = ({ itemType, itemsToMigrate, preparedMigrati
|
|
|
245
246
|
};
|
|
246
247
|
};
|
|
247
248
|
export const runMigrationPipelineInMemory = ({ itemType, itemsToMigrate, preparedMigrationConfigs, }) => {
|
|
248
|
-
const originalItems = deepClone(itemsToMigrate);
|
|
249
249
|
let workingItems = deepClone(itemsToMigrate);
|
|
250
250
|
const stepReports = [];
|
|
251
251
|
for (const preparedMigrationConfig of preparedMigrationConfigs) {
|
|
@@ -282,7 +282,7 @@ export const runMigrationPipelineInMemory = ({ itemType, itemsToMigrate, prepare
|
|
|
282
282
|
stepReports.push(stepReport);
|
|
283
283
|
}
|
|
284
284
|
const changedItems = workingItems.filter((item, index) => {
|
|
285
|
-
const originalItem =
|
|
285
|
+
const originalItem = itemsToMigrate[index];
|
|
286
286
|
if (!originalItem) {
|
|
287
287
|
return true;
|
|
288
288
|
}
|
|
@@ -295,7 +295,7 @@ export const runMigrationPipelineInMemory = ({ itemType, itemsToMigrate, prepare
|
|
|
295
295
|
totalItems: workingItems.length,
|
|
296
296
|
};
|
|
297
297
|
};
|
|
298
|
-
const savePipelineSummary = async ({ artifactBaseName, useDatestamp, from, itemType, dryRun, migrateFrom, fromFilePath, pipelineResult, }, config) => {
|
|
298
|
+
const savePipelineSummary = async ({ artifactBaseName, useDatestamp, from, itemType, dryRun, publish, publishLanguages, migrateFrom, fromFilePath, pipelineResult, }, config) => {
|
|
299
299
|
await createAndSaveToFile({
|
|
300
300
|
datestamp: useDatestamp,
|
|
301
301
|
ext: "json",
|
|
@@ -308,6 +308,12 @@ const savePipelineSummary = async ({ artifactBaseName, useDatestamp, from, itemT
|
|
|
308
308
|
from,
|
|
309
309
|
fromFilePath: fromFilePath || null,
|
|
310
310
|
},
|
|
311
|
+
writeMode: itemType === "story" && publish ? "publish" : "save",
|
|
312
|
+
publishLanguages: itemType === "story" && publish
|
|
313
|
+
? {
|
|
314
|
+
requested: publishLanguages,
|
|
315
|
+
}
|
|
316
|
+
: null,
|
|
311
317
|
totalItems: pipelineResult.totalItems,
|
|
312
318
|
totalChangedItems: pipelineResult.changedItems.length,
|
|
313
319
|
steps: pipelineResult.stepReports,
|
|
@@ -378,7 +384,7 @@ const loadItemsToMigrate = async ({ itemType, migrateFrom, from, filters, fromFi
|
|
|
378
384
|
spaceId: from,
|
|
379
385
|
});
|
|
380
386
|
};
|
|
381
|
-
export const migrateAllComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, filters, dryRun, fromFilePath, fileName, migrationComponentAliases, migrationComponentOverrides, }, config) => {
|
|
387
|
+
export const migrateAllComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, filters, dryRun, publish, publishLanguages, fromFilePath, fileName, migrationComponentAliases, migrationComponentOverrides, }, config) => {
|
|
382
388
|
Logger.warning(`Trying to migrate all ${itemType} from ${migrateFrom}, ${from} to ${to}...`);
|
|
383
389
|
const preparedMigrationConfigs = prepareMigrationConfigs({
|
|
384
390
|
migrationConfig,
|
|
@@ -397,12 +403,14 @@ export const migrateAllComponentsDataInStories = async ({ itemType, migrationCon
|
|
|
397
403
|
to,
|
|
398
404
|
filters,
|
|
399
405
|
dryRun,
|
|
406
|
+
publish,
|
|
407
|
+
publishLanguages,
|
|
400
408
|
fromFilePath,
|
|
401
409
|
fileName,
|
|
402
410
|
preparedMigrationConfigs,
|
|
403
411
|
}, config);
|
|
404
412
|
};
|
|
405
|
-
export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate, migrationConfig, migrationConfigs, to, dryRun, migrateFrom, fromFilePath, fileName, }, config) => {
|
|
413
|
+
export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate, migrationConfig, migrationConfigs, to, dryRun, publish, publishLanguages, migrateFrom, fromFilePath, fileName, }, config) => {
|
|
406
414
|
const preparedMigrationConfigs = migrationConfigs ||
|
|
407
415
|
prepareMigrationConfigs({
|
|
408
416
|
migrationConfig: migrationConfig || [],
|
|
@@ -472,6 +480,8 @@ export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate,
|
|
|
472
480
|
from,
|
|
473
481
|
itemType,
|
|
474
482
|
dryRun,
|
|
483
|
+
publish,
|
|
484
|
+
publishLanguages,
|
|
475
485
|
migrateFrom,
|
|
476
486
|
fromFilePath,
|
|
477
487
|
pipelineResult,
|
|
@@ -489,11 +499,23 @@ export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate,
|
|
|
489
499
|
return;
|
|
490
500
|
}
|
|
491
501
|
let writeResults = [];
|
|
502
|
+
let resolvedPublishLanguages;
|
|
492
503
|
if (itemType === "story") {
|
|
504
|
+
if (publish && publishLanguages !== undefined) {
|
|
505
|
+
resolvedPublishLanguages =
|
|
506
|
+
await managementApi.stories.resolvePublishLanguageCodes(publishLanguages, {
|
|
507
|
+
...config,
|
|
508
|
+
spaceId: to,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
493
511
|
writeResults = await managementApi.stories.updateStories({
|
|
494
512
|
stories: pipelineResult.changedItems,
|
|
495
513
|
spaceId: to,
|
|
496
|
-
options: {
|
|
514
|
+
options: {
|
|
515
|
+
publish: Boolean(publish),
|
|
516
|
+
publishLanguages: resolvedPublishLanguages,
|
|
517
|
+
preservePublishState: Boolean(publish),
|
|
518
|
+
},
|
|
497
519
|
}, config);
|
|
498
520
|
}
|
|
499
521
|
else if (itemType === "preset") {
|
|
@@ -504,6 +526,27 @@ export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate,
|
|
|
504
526
|
}, config);
|
|
505
527
|
}
|
|
506
528
|
const writeSummary = summarizeMutationWriteResults(writeResults);
|
|
529
|
+
try {
|
|
530
|
+
await saveMigrationRunLog({
|
|
531
|
+
artifactBaseName,
|
|
532
|
+
useDatestamp,
|
|
533
|
+
from,
|
|
534
|
+
to,
|
|
535
|
+
itemType,
|
|
536
|
+
dryRun,
|
|
537
|
+
publish,
|
|
538
|
+
publishLanguages,
|
|
539
|
+
resolvedPublishLanguages,
|
|
540
|
+
migrateFrom,
|
|
541
|
+
fromFilePath,
|
|
542
|
+
pipelineResult,
|
|
543
|
+
writeResults,
|
|
544
|
+
writeSummary,
|
|
545
|
+
}, config);
|
|
546
|
+
}
|
|
547
|
+
catch (error) {
|
|
548
|
+
Logger.warning(`[MIGRATION] Could not write migration run log: ${error instanceof Error ? error.message : String(error)}`);
|
|
549
|
+
}
|
|
507
550
|
if (writeSummary.failed === 0) {
|
|
508
551
|
Logger.success(`[MIGRATION] Update complete. ${writeSummary.successful}/${writeSummary.total} ${itemType}(s) updated successfully.`);
|
|
509
552
|
return;
|
|
@@ -527,7 +570,7 @@ const saveBackupToFile = async ({ itemType, res, folder, filename }, config) =>
|
|
|
527
570
|
res: res,
|
|
528
571
|
}, config);
|
|
529
572
|
};
|
|
530
|
-
export const migrateProvidedComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, filters, dryRun, fromFilePath, fileName, preparedMigrationConfigs, migrationComponentAliases, migrationComponentOverrides, }, config) => {
|
|
573
|
+
export const migrateProvidedComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, filters, dryRun, publish, publishLanguages, fromFilePath, fileName, preparedMigrationConfigs, migrationComponentAliases, migrationComponentOverrides, }, config) => {
|
|
531
574
|
const resolvedMigrationConfigs = preparedMigrationConfigs ||
|
|
532
575
|
prepareMigrationConfigs({
|
|
533
576
|
migrationConfig,
|
|
@@ -562,6 +605,8 @@ export const migrateProvidedComponentsDataInStories = async ({ itemType, migrati
|
|
|
562
605
|
from,
|
|
563
606
|
to,
|
|
564
607
|
dryRun,
|
|
608
|
+
publish,
|
|
609
|
+
publishLanguages,
|
|
565
610
|
migrateFrom,
|
|
566
611
|
fromFilePath,
|
|
567
612
|
fileName,
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { MigrateFrom, MigrationPipelineResult } from "./component-data-migration.js";
|
|
2
|
+
import type { MutationWriteResult, MutationWriteSummary } from "./write-summary.js";
|
|
3
|
+
import type { PublishLanguagesOption } from "../stories/stories.types.js";
|
|
4
|
+
import type { RequestBaseConfig } from "../utils/request.js";
|
|
5
|
+
type MigrationRunLogEvent = "update_success" | "update_failed" | "publish_success" | "publish_failed" | "publish_skipped" | "migration_write_summary";
|
|
6
|
+
export interface MigrationRunLogRecord {
|
|
7
|
+
timestamp: string;
|
|
8
|
+
event: MigrationRunLogEvent;
|
|
9
|
+
runId: string;
|
|
10
|
+
itemType: "story" | "preset";
|
|
11
|
+
source: {
|
|
12
|
+
migrateFrom: MigrateFrom;
|
|
13
|
+
from: string;
|
|
14
|
+
fromFilePath: string | null;
|
|
15
|
+
};
|
|
16
|
+
target: {
|
|
17
|
+
to: string;
|
|
18
|
+
};
|
|
19
|
+
writeMode: "publish" | "save";
|
|
20
|
+
publishLanguages?: {
|
|
21
|
+
requested?: PublishLanguagesOption;
|
|
22
|
+
resolved?: string[];
|
|
23
|
+
};
|
|
24
|
+
dryRun: boolean;
|
|
25
|
+
migrationConfigs: string[];
|
|
26
|
+
totalItems: number;
|
|
27
|
+
totalChangedItems: number;
|
|
28
|
+
writeSummary?: {
|
|
29
|
+
total: number;
|
|
30
|
+
successful: number;
|
|
31
|
+
failed: number;
|
|
32
|
+
failedItems: Array<{
|
|
33
|
+
id?: number | string;
|
|
34
|
+
name?: string;
|
|
35
|
+
slug?: string;
|
|
36
|
+
spaceId?: string;
|
|
37
|
+
status?: number | string;
|
|
38
|
+
response?: string | null;
|
|
39
|
+
stage?: "update" | "publish";
|
|
40
|
+
sourcePublishState?: string;
|
|
41
|
+
publishSkippedReason?: string;
|
|
42
|
+
}>;
|
|
43
|
+
};
|
|
44
|
+
item?: {
|
|
45
|
+
index: number;
|
|
46
|
+
id?: number | string;
|
|
47
|
+
name?: string;
|
|
48
|
+
slug?: string;
|
|
49
|
+
spaceId?: string;
|
|
50
|
+
};
|
|
51
|
+
status?: number | string | null;
|
|
52
|
+
response?: string | null;
|
|
53
|
+
stage?: "update" | "publish";
|
|
54
|
+
sourcePublishState?: string;
|
|
55
|
+
publishSkippedReason?: string;
|
|
56
|
+
error?: unknown;
|
|
57
|
+
}
|
|
58
|
+
interface SaveMigrationRunLogArgs {
|
|
59
|
+
artifactBaseName: string;
|
|
60
|
+
useDatestamp: boolean;
|
|
61
|
+
from: string;
|
|
62
|
+
to: string;
|
|
63
|
+
itemType: "story" | "preset";
|
|
64
|
+
dryRun?: boolean;
|
|
65
|
+
publish?: boolean;
|
|
66
|
+
publishLanguages?: PublishLanguagesOption;
|
|
67
|
+
resolvedPublishLanguages?: string[];
|
|
68
|
+
migrateFrom: MigrateFrom;
|
|
69
|
+
fromFilePath?: string;
|
|
70
|
+
pipelineResult: MigrationPipelineResult;
|
|
71
|
+
writeResults: PromiseSettledResult<MutationWriteResult>[];
|
|
72
|
+
writeSummary: MutationWriteSummary;
|
|
73
|
+
}
|
|
74
|
+
export declare const buildMigrationRunLogRecords: ({ from, to, itemType, dryRun, publish, publishLanguages, resolvedPublishLanguages, migrateFrom, fromFilePath, pipelineResult, writeResults, writeSummary, }: Omit<SaveMigrationRunLogArgs, "artifactBaseName" | "useDatestamp">) => MigrationRunLogRecord[];
|
|
75
|
+
export declare const recordsToJsonl: (records: MigrationRunLogRecord[]) => string;
|
|
76
|
+
export declare const saveMigrationRunLog: (args: SaveMigrationRunLogArgs, config: RequestBaseConfig) => Promise<void>;
|
|
77
|
+
export {};
|