sb-mig 5.5.4-beta.1 → 5.6.0-beta.2
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 +20 -19
- package/dist/api/assets/assets.js +1 -14
- package/dist/api/components/components.js +2 -1
- package/dist/api/components/components.sync.d.ts +8 -0
- package/dist/api/components/components.sync.js +193 -0
- package/dist/api/data-migration/component-data-migration.d.ts +6 -2
- package/dist/api/data-migration/component-data-migration.js +35 -11
- package/dist/api/datasources/datasource-entries.js +4 -5
- package/dist/api/datasources/datasources.d.ts +5 -2
- package/dist/api/datasources/datasources.js +42 -35
- package/dist/api/datasources/datasources.sync.d.ts +2 -0
- package/dist/api/datasources/datasources.sync.js +11 -0
- package/dist/api/datasources/datasources.types.d.ts +1 -1
- package/dist/api/datasources/index.d.ts +2 -1
- package/dist/api/datasources/index.js +2 -1
- package/dist/api/managementApi.d.ts +2 -2
- package/dist/api/migrate.d.ts +1 -1
- package/dist/api/migrate.js +3 -48
- package/dist/api/plugins/index.d.ts +2 -1
- package/dist/api/plugins/index.js +2 -1
- package/dist/api/plugins/plugins.d.ts +7 -2
- package/dist/api/plugins/plugins.js +28 -15
- package/dist/api/plugins/plugins.sync.d.ts +2 -0
- package/dist/api/plugins/plugins.sync.js +11 -0
- package/dist/api/roles/index.d.ts +2 -1
- package/dist/api/roles/index.js +2 -1
- package/dist/api/roles/roles.d.ts +5 -2
- package/dist/api/roles/roles.js +34 -11
- package/dist/api/roles/roles.sync.d.ts +2 -0
- package/dist/api/roles/roles.sync.js +6 -0
- package/dist/api/roles/roles.types.d.ts +1 -1
- package/dist/api/stories/stories.js +5 -13
- package/dist/api/sync/sync.types.d.ts +30 -0
- package/dist/api/sync/sync.types.js +1 -0
- package/dist/api/testApi.d.ts +2 -2
- package/dist/api/utils/helper-functions.d.ts +5 -1
- package/dist/api/utils/helper-functions.js +6 -1
- package/dist/api/utils/request.d.ts +1 -1
- package/dist/api/utils/request.js +11 -2
- package/dist/api/utils/resolverTransformations.js +2 -57
- package/dist/api-v2/assets/index.d.ts +13 -0
- package/dist/api-v2/assets/index.js +25 -0
- package/dist/api-v2/auth/index.d.ts +3 -0
- package/dist/api-v2/auth/index.js +8 -0
- package/dist/api-v2/client.d.ts +13 -0
- package/dist/api-v2/client.js +17 -0
- package/dist/api-v2/components/index.d.ts +10 -0
- package/dist/api-v2/components/index.js +29 -0
- package/dist/api-v2/datasources/index.d.ts +8 -0
- package/dist/api-v2/datasources/index.js +58 -0
- package/dist/api-v2/discover/discover.d.ts +36 -0
- package/dist/api-v2/discover/discover.js +281 -0
- package/dist/api-v2/discover/index.d.ts +2 -0
- package/dist/api-v2/discover/index.js +1 -0
- package/dist/api-v2/index.d.ts +19 -0
- package/dist/api-v2/index.js +21 -0
- package/dist/api-v2/plugins/index.d.ts +9 -0
- package/dist/api-v2/plugins/index.js +42 -0
- package/dist/api-v2/precompile/index.d.ts +2 -0
- package/dist/api-v2/precompile/index.js +1 -0
- package/dist/api-v2/precompile/precompile.d.ts +65 -0
- package/dist/api-v2/precompile/precompile.js +127 -0
- package/dist/api-v2/presets/index.d.ts +13 -0
- package/dist/api-v2/presets/index.js +25 -0
- package/dist/api-v2/requestConfig.d.ts +5 -0
- package/dist/api-v2/requestConfig.js +34 -0
- package/dist/api-v2/roles/index.d.ts +5 -0
- package/dist/api-v2/roles/index.js +35 -0
- package/dist/api-v2/spaces/index.d.ts +7 -0
- package/dist/api-v2/spaces/index.js +11 -0
- package/dist/api-v2/stories/index.d.ts +34 -0
- package/dist/api-v2/stories/index.js +172 -0
- package/dist/api-v2/stories/types.d.ts +28 -0
- package/dist/api-v2/stories/types.js +1 -0
- package/dist/api-v2/sync/index.d.ts +24 -0
- package/dist/api-v2/sync/index.js +109 -0
- package/dist/api-v2/sync/types.d.ts +1 -0
- package/dist/api-v2/sync/types.js +1 -0
- package/dist/api-v2/test.d.ts +15 -0
- package/dist/api-v2/test.js +21 -0
- package/dist/cli/cli-descriptions.d.ts +1 -1
- package/dist/cli/cli-descriptions.js +4 -0
- package/dist/cli/commands/backup.js +7 -3
- package/dist/cli/commands/copy.js +2 -2
- package/dist/cli/commands/migrate.js +13 -2
- package/dist/cli/commands/migrations.js +2 -2
- package/dist/cli/commands/remove.js +1 -1
- package/dist/cli/commands/revert.js +2 -2
- package/dist/cli/commands/sync.js +1 -2
- package/dist/cli/index.js +8 -1
- package/dist/cli/utils/cli-utils.d.ts +69 -0
- package/dist/cli/utils/cli-utils.js +100 -0
- package/dist/cli/utils/discover.d.ts +3 -22
- package/dist/cli/utils/discover.js +4 -51
- package/dist/config/config.d.ts +2 -39
- package/dist/config/config.types.d.ts +40 -0
- package/dist/config/config.types.js +1 -0
- package/dist/config/defaultConfig.d.ts +1 -1
- package/dist/config/defaultConfig.js +2 -2
- package/dist/utils/array-utils.d.ts +20 -0
- package/dist/utils/array-utils.js +20 -0
- package/dist/utils/async-utils.d.ts +13 -0
- package/dist/utils/async-utils.js +13 -0
- package/dist/utils/date-utils.d.ts +14 -0
- package/dist/utils/date-utils.js +21 -0
- package/dist/utils/files.d.ts +35 -0
- package/dist/utils/files.js +57 -2
- package/dist/utils/main.d.ts +8 -18
- package/dist/utils/main.js +12 -104
- package/dist/utils/migrations.d.ts +9 -3
- package/dist/utils/object-utils.d.ts +46 -0
- package/dist/utils/object-utils.js +71 -0
- package/dist/utils/others.d.ts +6 -9
- package/dist/utils/others.js +8 -15
- package/dist/utils/path-utils.d.ts +89 -0
- package/dist/utils/path-utils.js +106 -0
- package/dist/utils/pkg.d.ts +16 -2
- package/dist/utils/pkg.js +16 -3
- package/dist/utils/string-utils.d.ts +33 -0
- package/dist/utils/string-utils.js +45 -0
- package/dist/utils/transform-utils.d.ts +62 -0
- package/dist/utils/transform-utils.js +113 -0
- package/dist-cjs/api/auth/auth.js +28 -0
- package/dist-cjs/api/auth/auth.types.js +2 -0
- package/dist-cjs/api/components/components.js +202 -0
- package/dist-cjs/api/components/components.sync.js +199 -0
- package/dist-cjs/api/components/components.types.js +2 -0
- package/dist-cjs/api/datasources/datasource-entries.js +166 -0
- package/dist-cjs/api/datasources/datasources.js +166 -0
- package/dist-cjs/api/datasources/datasources.types.js +2 -0
- package/dist-cjs/api/plugins/plugins.js +132 -0
- package/dist-cjs/api/plugins/plugins.types.js +2 -0
- package/dist-cjs/api/presets/componentPresets.js +25 -0
- package/dist-cjs/api/presets/presets.js +92 -0
- package/dist-cjs/api/presets/presets.types.js +2 -0
- package/dist-cjs/api/presets/resolvePresets.js +49 -0
- package/dist-cjs/api/roles/roles.js +131 -0
- package/dist-cjs/api/roles/roles.types.js +2 -0
- package/dist-cjs/api/spaces/spaces.js +34 -0
- package/dist-cjs/api/spaces/spaces.types.js +2 -0
- package/dist-cjs/api/stories/stories.js +214 -0
- package/dist-cjs/api/stories/stories.types.js +2 -0
- package/dist-cjs/api/sync/sync.types.js +2 -0
- package/dist-cjs/api/utils/request.js +48 -0
- package/dist-cjs/api/utils/resolvers.types.js +2 -0
- package/dist-cjs/api-v2/assets/index.js +30 -0
- package/dist-cjs/api-v2/auth/index.js +12 -0
- package/dist-cjs/api-v2/client.js +23 -0
- package/dist-cjs/api-v2/components/index.js +40 -0
- package/dist-cjs/api-v2/datasources/index.js +64 -0
- package/dist-cjs/api-v2/discover/discover.js +321 -0
- package/dist-cjs/api-v2/discover/index.js +9 -0
- package/dist-cjs/api-v2/index.js +60 -0
- package/dist-cjs/api-v2/plugins/index.js +49 -0
- package/dist-cjs/api-v2/precompile/index.js +7 -0
- package/dist-cjs/api-v2/precompile/precompile.js +136 -0
- package/dist-cjs/api-v2/presets/index.js +33 -0
- package/dist-cjs/api-v2/requestConfig.js +37 -0
- package/dist-cjs/api-v2/roles/index.js +41 -0
- package/dist-cjs/api-v2/spaces/index.js +16 -0
- package/dist-cjs/api-v2/stories/index.js +180 -0
- package/dist-cjs/api-v2/stories/types.js +2 -0
- package/dist-cjs/api-v2/sync/index.js +115 -0
- package/dist-cjs/api-v2/sync/types.js +2 -0
- package/dist-cjs/api-v2/test.js +25 -0
- package/dist-cjs/config/config.types.js +2 -0
- package/dist-cjs/config/constants.js +29 -0
- package/dist-cjs/package.json +3 -0
- package/dist-cjs/utils/array-utils.js +24 -0
- package/dist-cjs/utils/logger.js +32 -0
- package/dist-cjs/utils/object-utils.js +77 -0
- package/dist-cjs/utils/path-utils.js +115 -0
- package/package.json +37 -20
- package/dist/utils/pkg-require.d.ts +0 -2
- package/dist/utils/pkg-require.js +0 -4
package/README.md
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
<p align="center">
|
|
3
2
|
<img width="250" height="250" src="./sb-mig-logo.png" alt="Logo" />
|
|
4
3
|
</p>
|
|
@@ -10,17 +9,16 @@ If you've found an issue or you have feature request - <a href="https://github.c
|
|
|
10
9
|
[](https://github.com/sb-mig/sb-mig/issues?q=is%3Aopen+is%3Aissue)
|
|
11
10
|

|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
12
|
# Requirements:
|
|
16
13
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
| Node
|
|
14
|
+
| | |
|
|
15
|
+
| ---- | ------------ |
|
|
16
|
+
| Node | LTS (18.x.x) |
|
|
20
17
|
|
|
21
18
|
# 5.x.x version released!
|
|
22
19
|
|
|
23
20
|
## Important Updates
|
|
21
|
+
|
|
24
22
|
- Complete codebase overhaul to facilitate the utilization of features and requests to Storyblok. This development decreases the tight coupling with CLI, while improving folder and file structure.
|
|
25
23
|
- New feature: Content synchronization (including stories and assets) in various directions, ranging from space to space, from space to file, and from file to space.
|
|
26
24
|
- New feature: Introduced support for TypeScript Schema, with the added ability to precompile them on-the-fly before synchronization, improving usage with sb-mig.
|
|
@@ -33,12 +31,12 @@ If you've found an issue or you have feature request - <a href="https://github.c
|
|
|
33
31
|
- Expanded test coverage (with more additions anticipated).
|
|
34
32
|
|
|
35
33
|
## Breaking changes
|
|
34
|
+
|
|
36
35
|
- Please note that sb-mig no longer extends support for Node versions older than 18.x.x, as part of its adoption of native ESM support.
|
|
37
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).
|
|
38
37
|
|
|
39
38
|
Do not hesitate to get in touch if you encounter any issues or require further clarification on any points.
|
|
40
39
|
|
|
41
|
-
|
|
42
40
|
# 4.x.x version released!
|
|
43
41
|
|
|
44
42
|
- Whole deployment now, is handled by [semantic-release](https://github.com/semantic-release/semantic-release). And is just normal repository, instead of Lerna monorepo which is not needed anymore, and it was recently unmaintained (now it was passed to `nrwl` to maintain (https://github.com/lerna/lerna/issues/3121) will see what will happen in future with it :)
|
|
@@ -57,10 +55,10 @@ Do not hesitate to get in touch if you encounter any issues or require further c
|
|
|
57
55
|
|
|
58
56
|
- [How to install and configure](#how-to-install-and-configure)
|
|
59
57
|
- [Schema documentation:](#schema-documentation)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
- [Basics](#basics)
|
|
59
|
+
- [Syncing components](#syncing-components)
|
|
60
|
+
- [Syncing datasources](#syncing-datasources)
|
|
61
|
+
- [Presets support](#presets-support)
|
|
64
62
|
- [Development](#development)
|
|
65
63
|
- [Roadmap](#roadmap)
|
|
66
64
|
|
|
@@ -189,7 +187,6 @@ sb-mig sync components row column
|
|
|
189
187
|
|
|
190
188
|
This command will look for `row.sb.js` and `column.sb.js` files inside a directories mentioned in `componentDirectories` field. (You can change directories name mapping by modifying `componentDirectories` inside `storyblok.config.js`). You can also change the extension searched by changing `schemaFileExt`. [How to install and configure](#how-to-install-and-configure))
|
|
191
189
|
|
|
192
|
-
|
|
193
190
|
## Syncing datasources
|
|
194
191
|
|
|
195
192
|
You can also sync your `datasources`.
|
|
@@ -269,7 +266,6 @@ You can also sync all datasources, and that's the command we strongly recommend.
|
|
|
269
266
|
sb-mig sync datasources --all
|
|
270
267
|
```
|
|
271
268
|
|
|
272
|
-
|
|
273
269
|
## Presets support
|
|
274
270
|
|
|
275
271
|
Writing your own predefined data (presets) for components can be a pain, so with `sb-mig` you can create presets for your components in the storyblok gui, and then export them to a schema based `.sb.js` file to be picked up while syncing.
|
|
@@ -342,36 +338,41 @@ git clone git@github.com:sb-mig/sb-mig.git
|
|
|
342
338
|
Install packages
|
|
343
339
|
|
|
344
340
|
```
|
|
345
|
-
|
|
341
|
+
npm install
|
|
346
342
|
```
|
|
347
343
|
|
|
348
344
|
Run development command
|
|
345
|
+
|
|
349
346
|
```bash
|
|
350
|
-
|
|
347
|
+
npm run build:dev
|
|
351
348
|
```
|
|
352
349
|
|
|
353
350
|
It will watch a file change, and on every change, will rebuild typescript and build the whole lib/cli.
|
|
354
351
|
|
|
355
|
-
The you can use
|
|
352
|
+
The you can use
|
|
353
|
+
|
|
356
354
|
```
|
|
357
|
-
node dist/index.js debug
|
|
355
|
+
node dist/index.js debug
|
|
358
356
|
```
|
|
357
|
+
|
|
359
358
|
to access `sb-mig`
|
|
360
359
|
|
|
361
360
|
For your conveniece, you can also, link it to proper `sb-mi` name:
|
|
361
|
+
|
|
362
362
|
```
|
|
363
|
-
|
|
363
|
+
npm link
|
|
364
364
|
```
|
|
365
365
|
|
|
366
366
|
And then you can use it like that:
|
|
367
|
+
|
|
367
368
|
```
|
|
368
369
|
sb-mig debug
|
|
369
370
|
```
|
|
370
371
|
|
|
371
|
-
|
|
372
372
|
# Releasing
|
|
373
373
|
|
|
374
374
|
## Flow of branching out and merging
|
|
375
|
+
|
|
375
376
|
- Before creating feature branch from beta, make sure you have newest beta. (by `git pull origin beta`).
|
|
376
377
|
- Create feature branch from beta
|
|
377
378
|
- Do your changes
|
|
@@ -4,6 +4,7 @@ import path from "path";
|
|
|
4
4
|
import FormData from "form-data";
|
|
5
5
|
import { createDir, isDirectoryExists } from "../../utils/files.js";
|
|
6
6
|
import Logger from "../../utils/logger.js";
|
|
7
|
+
import { getFileName, getSizeFromURL } from "../../utils/string-utils.js";
|
|
7
8
|
// GET
|
|
8
9
|
export const getAllAssets = async (args, config) => {
|
|
9
10
|
const { spaceId, search } = args;
|
|
@@ -77,20 +78,6 @@ const uploadFile = ({ signedResponseObject, pathToFile }) => {
|
|
|
77
78
|
}
|
|
78
79
|
});
|
|
79
80
|
};
|
|
80
|
-
const getFileName = (fileUrl) => {
|
|
81
|
-
const fileName = fileUrl.split("/").pop();
|
|
82
|
-
if (fileName) {
|
|
83
|
-
return fileName;
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
throw Error("File name couldn't be extracted from URL.");
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
const getSizeFromURL = (fileUrl) => {
|
|
90
|
-
const data = fileUrl.split("/");
|
|
91
|
-
const sizePos = data.length - 3;
|
|
92
|
-
return data[sizePos];
|
|
93
|
-
};
|
|
94
81
|
const downloadAsset = async (args, config) => {
|
|
95
82
|
const { debug, sbmigWorkingDirectory } = config;
|
|
96
83
|
const { payload } = args;
|
|
@@ -55,7 +55,7 @@ export const createComponent = (component, presets, config) => {
|
|
|
55
55
|
Logger.log(`Trying to create '${component.name}'`);
|
|
56
56
|
const componentWithPresets = component;
|
|
57
57
|
const { all_presets, ...componentWithoutPresets } = componentWithPresets;
|
|
58
|
-
sbApi
|
|
58
|
+
return sbApi
|
|
59
59
|
.post(`spaces/${spaceId}/components/`, {
|
|
60
60
|
component: componentWithoutPresets,
|
|
61
61
|
})
|
|
@@ -67,6 +67,7 @@ export const createComponent = (component, presets, config) => {
|
|
|
67
67
|
})
|
|
68
68
|
.catch((err) => {
|
|
69
69
|
Logger.error(`${err.message} in migration of ${component.name} in createComponent function`);
|
|
70
|
+
throw err;
|
|
70
71
|
});
|
|
71
72
|
};
|
|
72
73
|
/*
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SyncProgressCallback, SyncResult } from "../sync/sync.types.js";
|
|
2
|
+
import type { RequestBaseConfig } from "../utils/request.js";
|
|
3
|
+
export declare function syncComponentsData(args: {
|
|
4
|
+
components: any[];
|
|
5
|
+
presets: boolean;
|
|
6
|
+
ssot?: boolean;
|
|
7
|
+
onProgress?: SyncProgressCallback;
|
|
8
|
+
}, config: RequestBaseConfig): Promise<SyncResult>;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { uniqueValuesFrom } from "../../utils/array-utils.js";
|
|
2
|
+
import Logger from "../../utils/logger.js";
|
|
3
|
+
import { isObjectEmpty } from "../../utils/object-utils.js";
|
|
4
|
+
/**
|
|
5
|
+
* Default progress callback that logs to console
|
|
6
|
+
*/
|
|
7
|
+
const defaultProgress = (event) => {
|
|
8
|
+
if (event.type === "start") {
|
|
9
|
+
Logger.log(`Starting sync of ${event.total} components...`);
|
|
10
|
+
}
|
|
11
|
+
else if (event.type === "progress" && event.name) {
|
|
12
|
+
const status = event.action === "creating"
|
|
13
|
+
? "Creating"
|
|
14
|
+
: event.action === "updating"
|
|
15
|
+
? "Updating"
|
|
16
|
+
: event.action === "created"
|
|
17
|
+
? "✓ Created"
|
|
18
|
+
: event.action === "updated"
|
|
19
|
+
? "✓ Updated"
|
|
20
|
+
: event.action === "skipped"
|
|
21
|
+
? "⏭ Skipped"
|
|
22
|
+
: "✘ Error";
|
|
23
|
+
Logger.log(`[${event.current}/${event.total}] ${status}: ${event.name}`);
|
|
24
|
+
}
|
|
25
|
+
else if (event.type === "complete") {
|
|
26
|
+
Logger.success(`Sync complete: ${event.message ?? "done"}`);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
import { createComponent, createComponentsGroup, getAllComponents, getAllComponentsGroups, removeComponent, removeComponentGroup, updateComponent, } from "./components.js";
|
|
30
|
+
async function ensureComponentGroupsExist(groupNames, config) {
|
|
31
|
+
try {
|
|
32
|
+
const existing = await getAllComponentsGroups(config);
|
|
33
|
+
const existingNames = new Set((existing ?? []).map((g) => g.name));
|
|
34
|
+
for (const groupName of groupNames) {
|
|
35
|
+
if (!existingNames.has(groupName)) {
|
|
36
|
+
await createComponentsGroup(groupName, config);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
// Log but don't fail - component groups are optional
|
|
42
|
+
Logger.warning(`Could not fetch component groups: ${error instanceof Error ? error.message : String(error)}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function resolveGroupUuid(component, remoteGroups) {
|
|
46
|
+
if (!component.component_group_name) {
|
|
47
|
+
return { ...component, component_group_uuid: null };
|
|
48
|
+
}
|
|
49
|
+
const match = remoteGroups.find((g) => g.name === component.component_group_name);
|
|
50
|
+
if (!match)
|
|
51
|
+
return { ...component, component_group_uuid: null };
|
|
52
|
+
return { ...component, component_group_uuid: match.uuid };
|
|
53
|
+
}
|
|
54
|
+
export async function syncComponentsData(args, config) {
|
|
55
|
+
const { components, presets, ssot, onProgress } = args;
|
|
56
|
+
const progress = onProgress ?? defaultProgress;
|
|
57
|
+
const result = {
|
|
58
|
+
created: [],
|
|
59
|
+
updated: [],
|
|
60
|
+
skipped: [],
|
|
61
|
+
errors: [],
|
|
62
|
+
};
|
|
63
|
+
if (ssot) {
|
|
64
|
+
const existingComponents = await getAllComponents(config);
|
|
65
|
+
const existingGroups = await getAllComponentsGroups(config);
|
|
66
|
+
await Promise.allSettled([
|
|
67
|
+
...(existingComponents ?? []).map((c) => removeComponent(c, config)),
|
|
68
|
+
...(existingGroups ?? []).map((g) => removeComponentGroup(g, config)),
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
const nonEmptyComponents = components.filter((c) => !isObjectEmpty(c));
|
|
72
|
+
const groupsToCheck = uniqueValuesFrom(nonEmptyComponents
|
|
73
|
+
.filter((c) => c.component_group_name)
|
|
74
|
+
.map((c) => c.component_group_name));
|
|
75
|
+
await ensureComponentGroupsExist(groupsToCheck, config);
|
|
76
|
+
let remoteComponents = [];
|
|
77
|
+
let remoteGroups = [];
|
|
78
|
+
try {
|
|
79
|
+
remoteComponents = (await getAllComponents(config)) ?? [];
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
Logger.warning(`Could not fetch remote components: ${error instanceof Error ? error.message : String(error)}`);
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
remoteGroups = (await getAllComponentsGroups(config)) ?? [];
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
Logger.warning(`Could not fetch remote groups: ${error instanceof Error ? error.message : String(error)}`);
|
|
89
|
+
}
|
|
90
|
+
const componentsToUpdate = [];
|
|
91
|
+
const componentsToCreate = [];
|
|
92
|
+
for (const component of nonEmptyComponents) {
|
|
93
|
+
if (!component?.name) {
|
|
94
|
+
result.skipped.push("unknown");
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const remote = remoteComponents.find((rc) => rc.name === component.name);
|
|
98
|
+
if (remote) {
|
|
99
|
+
componentsToUpdate.push({ id: remote.id, ...component });
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
componentsToCreate.push(component);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Resolve group uuids after ensureComponentGroupsExist
|
|
106
|
+
const updatePayloads = componentsToUpdate.map((c) => resolveGroupUuid(c, remoteGroups));
|
|
107
|
+
const createPayloads = componentsToCreate.map((c) => resolveGroupUuid(c, remoteGroups));
|
|
108
|
+
const totalComponents = updatePayloads.length + createPayloads.length;
|
|
109
|
+
let currentIndex = 0;
|
|
110
|
+
// Report start
|
|
111
|
+
progress({ type: "start", total: totalComponents });
|
|
112
|
+
// Process updates sequentially for progress reporting
|
|
113
|
+
for (const component of updatePayloads) {
|
|
114
|
+
const name = String(component?.name ?? "unknown");
|
|
115
|
+
currentIndex++;
|
|
116
|
+
progress({
|
|
117
|
+
type: "progress",
|
|
118
|
+
current: currentIndex,
|
|
119
|
+
total: totalComponents,
|
|
120
|
+
name,
|
|
121
|
+
action: "updating",
|
|
122
|
+
});
|
|
123
|
+
try {
|
|
124
|
+
await updateComponent(component, presets, config);
|
|
125
|
+
result.updated.push(name);
|
|
126
|
+
progress({
|
|
127
|
+
type: "progress",
|
|
128
|
+
current: currentIndex,
|
|
129
|
+
total: totalComponents,
|
|
130
|
+
name,
|
|
131
|
+
action: "updated",
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
result.errors.push({
|
|
136
|
+
name,
|
|
137
|
+
message: error instanceof Error ? error.message : String(error),
|
|
138
|
+
});
|
|
139
|
+
progress({
|
|
140
|
+
type: "progress",
|
|
141
|
+
current: currentIndex,
|
|
142
|
+
total: totalComponents,
|
|
143
|
+
name,
|
|
144
|
+
action: "error",
|
|
145
|
+
message: error instanceof Error ? error.message : String(error),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Process creates sequentially for progress reporting
|
|
150
|
+
for (const component of createPayloads) {
|
|
151
|
+
const name = String(component?.name ?? "unknown");
|
|
152
|
+
currentIndex++;
|
|
153
|
+
progress({
|
|
154
|
+
type: "progress",
|
|
155
|
+
current: currentIndex,
|
|
156
|
+
total: totalComponents,
|
|
157
|
+
name,
|
|
158
|
+
action: "creating",
|
|
159
|
+
});
|
|
160
|
+
try {
|
|
161
|
+
await createComponent(component, presets, config);
|
|
162
|
+
result.created.push(name);
|
|
163
|
+
progress({
|
|
164
|
+
type: "progress",
|
|
165
|
+
current: currentIndex,
|
|
166
|
+
total: totalComponents,
|
|
167
|
+
name,
|
|
168
|
+
action: "created",
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
result.errors.push({
|
|
173
|
+
name,
|
|
174
|
+
message: error instanceof Error ? error.message : String(error),
|
|
175
|
+
});
|
|
176
|
+
progress({
|
|
177
|
+
type: "progress",
|
|
178
|
+
current: currentIndex,
|
|
179
|
+
total: totalComponents,
|
|
180
|
+
name,
|
|
181
|
+
action: "error",
|
|
182
|
+
message: error instanceof Error ? error.message : String(error),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Report completion
|
|
187
|
+
progress({
|
|
188
|
+
type: "complete",
|
|
189
|
+
total: totalComponents,
|
|
190
|
+
message: `${result.created.length} created, ${result.updated.length} updated, ${result.errors.length} errors`,
|
|
191
|
+
});
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
@@ -7,11 +7,15 @@ interface MigrateItems {
|
|
|
7
7
|
migrateFrom: MigrateFrom;
|
|
8
8
|
migrationConfig: string;
|
|
9
9
|
componentsToMigrate: string[];
|
|
10
|
+
filters?: {
|
|
11
|
+
withSlug?: string[];
|
|
12
|
+
startsWith?: string;
|
|
13
|
+
};
|
|
10
14
|
}
|
|
11
15
|
export type MapperDefinition = (data: any) => any;
|
|
12
16
|
export declare const prepareStoriesFromLocalFile: ({ from }: any) => any;
|
|
13
17
|
export declare const prepareMigrationConfig: ({ migrationConfig }: any) => any;
|
|
14
|
-
export declare const migrateAllComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, }: Omit<MigrateItems, "componentsToMigrate">, config: RequestBaseConfig) => Promise<void>;
|
|
18
|
+
export declare const migrateAllComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, filters, }: Omit<MigrateItems, "componentsToMigrate">, config: RequestBaseConfig) => Promise<void>;
|
|
15
19
|
export declare const doTheMigration: ({ itemType, from, itemsToMigrate, componentsToMigrate, migrationConfigFileContent, migrationConfig, to, }: any, config: RequestBaseConfig) => Promise<void>;
|
|
16
|
-
export declare const migrateProvidedComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, }: MigrateItems, config: RequestBaseConfig) => Promise<void>;
|
|
20
|
+
export declare const migrateProvidedComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, filters, }: MigrateItems, config: RequestBaseConfig) => Promise<void>;
|
|
17
21
|
export {};
|
|
@@ -2,10 +2,10 @@ import path from "path";
|
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { discoverMigrationConfig, discoverStories, LOOKUP_TYPE, SCOPE, } from "../../cli/utils/discover.js";
|
|
4
4
|
import storyblokConfig from "../../config/config.js";
|
|
5
|
-
import { createAndSaveToFile } from "../../utils/files.js";
|
|
5
|
+
import { createAndSaveToFile, getFilesContentWithRequire, } from "../../utils/files.js";
|
|
6
6
|
import Logger from "../../utils/logger.js";
|
|
7
|
-
import { getFilesContentWithRequire, isObjectEmpty } from "../../utils/main.js";
|
|
8
7
|
import { modifyOrCreateAppliedMigrationsFile } from "../../utils/migrations.js";
|
|
8
|
+
import { isObjectEmpty } from "../../utils/object-utils.js";
|
|
9
9
|
import { managementApi } from "../managementApi.js";
|
|
10
10
|
function replaceComponentData({ parent, key, components, mapper, depth, maxDepth, sumOfReplacing, }) {
|
|
11
11
|
let currentMaxDepth = depth;
|
|
@@ -101,7 +101,7 @@ export const prepareMigrationConfig = ({ migrationConfig }) => {
|
|
|
101
101
|
Logger.success(`Migration config loaded.`);
|
|
102
102
|
return migrationConfigFileContent;
|
|
103
103
|
};
|
|
104
|
-
export const migrateAllComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, }, config) => {
|
|
104
|
+
export const migrateAllComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, filters, }, config) => {
|
|
105
105
|
Logger.warning(`Trying to migrate all ${itemType} from ${migrateFrom}, ${from} to ${to}...`);
|
|
106
106
|
const migrationConfigFileContent = prepareMigrationConfig({
|
|
107
107
|
migrationConfig,
|
|
@@ -119,6 +119,7 @@ export const migrateAllComponentsDataInStories = async ({ itemType, migrationCon
|
|
|
119
119
|
from,
|
|
120
120
|
to,
|
|
121
121
|
componentsToMigrate,
|
|
122
|
+
filters,
|
|
122
123
|
}, config);
|
|
123
124
|
};
|
|
124
125
|
export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate, componentsToMigrate, migrationConfigFileContent, migrationConfig, to, }, config) => {
|
|
@@ -128,9 +129,10 @@ export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate,
|
|
|
128
129
|
if (storyblokConfig.debug) {
|
|
129
130
|
Logger.success(`# ${index} #`);
|
|
130
131
|
}
|
|
131
|
-
|
|
132
|
+
let json = itemType === "story" ? item[itemType].content : item[itemType];
|
|
133
|
+
const rootWrapper = { root: json };
|
|
132
134
|
const maxDepth = replaceComponentData({
|
|
133
|
-
parent:
|
|
135
|
+
parent: rootWrapper,
|
|
134
136
|
key: "root",
|
|
135
137
|
components: componentsToMigrate,
|
|
136
138
|
mapper: migrationConfigFileContent,
|
|
@@ -138,6 +140,8 @@ export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate,
|
|
|
138
140
|
maxDepth: 0,
|
|
139
141
|
sumOfReplacing,
|
|
140
142
|
});
|
|
143
|
+
// After replacement, take potentially new root (root may be reassigned inside)
|
|
144
|
+
json = rootWrapper.root;
|
|
141
145
|
arrayOfMaxDepths.push(maxDepth);
|
|
142
146
|
if (Object.keys(sumOfReplacing).length > 0) {
|
|
143
147
|
console.log(" ");
|
|
@@ -220,14 +224,17 @@ const saveBackupToFile = async ({ itemType, res, folder, filename }, config) =>
|
|
|
220
224
|
res: res,
|
|
221
225
|
}, config);
|
|
222
226
|
};
|
|
223
|
-
export const migrateProvidedComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, }, config) => {
|
|
227
|
+
export const migrateProvidedComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, filters, }, config) => {
|
|
224
228
|
const migrationConfigFileContent = prepareMigrationConfig({
|
|
225
229
|
migrationConfig,
|
|
226
230
|
});
|
|
227
231
|
if (migrateFrom === "file") {
|
|
228
232
|
Logger.log("Migrating using file....");
|
|
229
233
|
// Get all stories to be migrated from file
|
|
230
|
-
const
|
|
234
|
+
const itemsFromFile = prepareStoriesFromLocalFile({ from });
|
|
235
|
+
const itemsToMigrate = Array.isArray(itemsFromFile)
|
|
236
|
+
? itemsFromFile.filter((it) => !(it?.story?.is_folder === true))
|
|
237
|
+
: itemsFromFile;
|
|
231
238
|
await doTheMigration({
|
|
232
239
|
itemsToMigrate,
|
|
233
240
|
componentsToMigrate,
|
|
@@ -239,10 +246,27 @@ export const migrateProvidedComponentsDataInStories = async ({ itemType, migrati
|
|
|
239
246
|
// Get all stories to be migrated from storyblok space
|
|
240
247
|
let itemsToMigrate = [];
|
|
241
248
|
if (itemType === "story") {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
249
|
+
if (filters?.withSlug && filters.withSlug.length > 0) {
|
|
250
|
+
const results = await Promise.all(filters.withSlug.map((slug) => managementApi.stories.getStoryBySlug(slug, {
|
|
251
|
+
...config,
|
|
252
|
+
spaceId: from,
|
|
253
|
+
})));
|
|
254
|
+
itemsToMigrate = results.filter(Boolean).filter((it) => !(it?.story?.is_folder === true));
|
|
255
|
+
}
|
|
256
|
+
else if (filters?.startsWith) {
|
|
257
|
+
itemsToMigrate = await managementApi.stories.getAllStories({ options: { starts_with: filters.startsWith } }, {
|
|
258
|
+
...config,
|
|
259
|
+
spaceId: from,
|
|
260
|
+
});
|
|
261
|
+
itemsToMigrate = itemsToMigrate.filter((it) => !(it?.story?.is_folder === true));
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
itemsToMigrate = await managementApi.stories.getAllStories({}, {
|
|
265
|
+
...config,
|
|
266
|
+
spaceId: from,
|
|
267
|
+
});
|
|
268
|
+
itemsToMigrate = itemsToMigrate.filter((it) => !(it?.story?.is_folder === true));
|
|
269
|
+
}
|
|
246
270
|
}
|
|
247
271
|
else if (itemType === "preset") {
|
|
248
272
|
itemsToMigrate = await managementApi.presets.getAllPresets({
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import storyblokConfig from "../../config/config.js";
|
|
3
2
|
import Logger from "../../utils/logger.js";
|
|
4
|
-
import { isObjectEmpty } from "../../utils/
|
|
3
|
+
import { isObjectEmpty } from "../../utils/object-utils.js";
|
|
5
4
|
import { getDatasource } from "./datasources.js";
|
|
6
5
|
const _decorateWithDimensions = async (args, config) => {
|
|
7
6
|
const { currentDatasource, dimensionsData, _callback } = args;
|
|
@@ -67,7 +66,7 @@ export const createDatasourceEntries = (args, config) => {
|
|
|
67
66
|
const _createDatasourceEntry = (args, config) => {
|
|
68
67
|
const { currentDatasource, finalDatasource_entry } = args;
|
|
69
68
|
const { spaceId, sbApi } = config;
|
|
70
|
-
if (
|
|
69
|
+
if (config.debug) {
|
|
71
70
|
console.log("############# Entity to Create: ");
|
|
72
71
|
console.log(finalDatasource_entry);
|
|
73
72
|
console.log("################################");
|
|
@@ -81,7 +80,7 @@ const _createDatasourceEntry = (args, config) => {
|
|
|
81
80
|
return data;
|
|
82
81
|
})
|
|
83
82
|
.catch((err) => {
|
|
84
|
-
if (
|
|
83
|
+
if (config.debug) {
|
|
85
84
|
console.log("Full Create error: ");
|
|
86
85
|
console.log(err);
|
|
87
86
|
}
|
|
@@ -122,7 +121,7 @@ const _updateDatasourceEntry = (args, config) => {
|
|
|
122
121
|
return true;
|
|
123
122
|
})
|
|
124
123
|
.catch((err) => {
|
|
125
|
-
if (
|
|
124
|
+
if (config.debug) {
|
|
126
125
|
console.log("Full update error: ");
|
|
127
126
|
console.log(err);
|
|
128
127
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import type { CreateDatasource, GetAllDatasources, GetDatasource,
|
|
1
|
+
import type { CreateDatasource, GetAllDatasources, GetDatasource, UpdateDatasource } from "./datasources.types.js";
|
|
2
|
+
import type { SyncResult } from "../sync/sync.types.js";
|
|
2
3
|
export declare const getAllDatasources: GetAllDatasources;
|
|
3
4
|
export declare const getDatasource: GetDatasource;
|
|
4
5
|
export declare const createDatasource: CreateDatasource;
|
|
5
6
|
export declare const updateDatasource: UpdateDatasource;
|
|
6
|
-
export declare const
|
|
7
|
+
export declare const syncDatasourcesData: ({ datasources }: {
|
|
8
|
+
datasources: any[];
|
|
9
|
+
}, config: any) => Promise<SyncResult>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import Logger from "../../utils/logger.js";
|
|
2
|
-
import { getFileContentWithRequire, } from "../../utils/main.js";
|
|
3
2
|
import { getAllItemsWithPagination } from "../utils/request.js";
|
|
4
3
|
import { createDatasourceEntries, getDatasourceEntries, } from "./datasource-entries.js";
|
|
5
4
|
// GET
|
|
@@ -110,39 +109,47 @@ export const updateDatasource = (args, config) => {
|
|
|
110
109
|
})
|
|
111
110
|
.catch((err) => Logger.error(err));
|
|
112
111
|
};
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
112
|
+
// File-based sync wrapper lives in `datasources.sync.ts` to keep this module CJS-safe.
|
|
113
|
+
export const syncDatasourcesData = async ({ datasources }, config) => {
|
|
114
|
+
const result = {
|
|
115
|
+
created: [],
|
|
116
|
+
updated: [],
|
|
117
|
+
skipped: [],
|
|
118
|
+
errors: [],
|
|
119
|
+
};
|
|
120
|
+
const remoteDatasourcesRaw = await getAllDatasources(config);
|
|
121
|
+
const remoteDatasources = Array.isArray(remoteDatasourcesRaw)
|
|
122
|
+
? remoteDatasourcesRaw
|
|
123
|
+
: [];
|
|
124
|
+
for (const datasource of datasources) {
|
|
125
|
+
const name = String(datasource?.name ?? "unknown");
|
|
126
|
+
if (!datasource || typeof datasource !== "object" || !datasource.name) {
|
|
127
|
+
result.skipped.push(name);
|
|
128
|
+
continue;
|
|
124
129
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
130
|
+
try {
|
|
131
|
+
const datasourceToBeUpdated = remoteDatasources.find((remoteDatasource) => datasource.name === remoteDatasource.name);
|
|
132
|
+
const opResult = datasourceToBeUpdated
|
|
133
|
+
? await updateDatasource({ datasource, datasourceToBeUpdated }, config)
|
|
134
|
+
: await createDatasource({ datasource }, config);
|
|
135
|
+
if (datasourceToBeUpdated)
|
|
136
|
+
result.updated.push(name);
|
|
137
|
+
else
|
|
138
|
+
result.created.push(name);
|
|
139
|
+
if (opResult?.data?.datasource && opResult?.datasource_entries) {
|
|
140
|
+
const remoteDatasourceEntries = await getDatasourceEntries({
|
|
141
|
+
datasourceName: opResult.data.datasource.name,
|
|
142
|
+
}, config);
|
|
143
|
+
await createDatasourceEntries({
|
|
144
|
+
data: opResult.data,
|
|
145
|
+
datasource_entries: opResult.datasource_entries,
|
|
146
|
+
remoteDatasourceEntries,
|
|
147
|
+
}, config);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (e) {
|
|
151
|
+
result.errors.push({ name, message: String(e) });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
148
155
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getFileContentWithRequire } from "../../utils/files.js";
|
|
2
|
+
import Logger from "../../utils/logger.js";
|
|
3
|
+
import { syncDatasourcesData } from "./datasources.js";
|
|
4
|
+
export const syncDatasources = async (args, config) => {
|
|
5
|
+
const { providedDatasources } = args;
|
|
6
|
+
Logger.log(`Trying to sync provided datasources: `);
|
|
7
|
+
const providedDatasourcesContent = await Promise.all(providedDatasources.map((datasource) => {
|
|
8
|
+
return getFileContentWithRequire({ file: datasource.p });
|
|
9
|
+
}));
|
|
10
|
+
await syncDatasourcesData({ datasources: providedDatasourcesContent }, config);
|
|
11
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OneFileElement } from "../../
|
|
1
|
+
import type { OneFileElement } from "../../utils/path-utils.js";
|
|
2
2
|
import type { RequestBaseConfig } from "../utils/request.js";
|
|
3
3
|
export type GetAllDatasources = (config: RequestBaseConfig) => Promise<any>;
|
|
4
4
|
export type GetDatasource = (args: {
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { createDatasource, getDatasource, getAllDatasources,
|
|
1
|
+
export { createDatasource, getDatasource, getAllDatasources, updateDatasource, } from "./datasources.js";
|
|
2
|
+
export { syncDatasources } from "./datasources.sync.js";
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { createDatasource, getDatasource, getAllDatasources,
|
|
1
|
+
export { createDatasource, getDatasource, getAllDatasources, updateDatasource, } from "./datasources.js";
|
|
2
|
+
export { syncDatasources } from "./datasources.sync.js";
|