sb-mig 5.7.0 → 5.8.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/dist/api/components/components.sync.d.ts +1 -0
- package/dist/api/components/components.sync.js +50 -8
- package/dist/api/datasources/datasource-entries.js +31 -15
- package/dist/api/datasources/datasources.d.ts +2 -1
- package/dist/api/datasources/datasources.js +33 -3
- package/dist/api/datasources/datasources.sync.js +2 -2
- package/dist/api/datasources/datasources.types.d.ts +6 -1
- package/dist/api/datasources/error-formatting.d.ts +1 -0
- package/dist/api/datasources/error-formatting.js +24 -0
- package/dist/api/migrate.js +50 -12
- package/dist/api/migrate.types.d.ts +14 -6
- package/dist/api/plugins/plugins.d.ts +2 -1
- package/dist/api/plugins/plugins.js +19 -1
- package/dist/api/plugins/plugins.sync.js +8 -4
- package/dist/api/plugins/plugins.types.d.ts +1 -0
- package/dist/api/roles/roles.d.ts +2 -1
- package/dist/api/roles/roles.js +16 -3
- package/dist/api/roles/roles.sync.js +2 -2
- package/dist/api/roles/roles.types.d.ts +6 -3
- package/dist/api/stories/stories.js +29 -1
- package/dist/api/sync/sync.types.d.ts +3 -0
- package/dist/api-v2/sync/index.js +4 -89
- package/dist/cli/cli-descriptions.d.ts +1 -1
- package/dist/cli/cli-descriptions.js +4 -0
- package/dist/cli/commands/sync.js +109 -64
- package/dist/cli/datasources/sync.js +5 -2
- package/dist/cli/roles/sync.js +4 -4
- package/dist-cjs/api/components/components.sync.js +50 -8
- package/dist-cjs/api/datasources/datasource-entries.js +31 -15
- package/dist-cjs/api/datasources/datasources.js +33 -3
- package/dist-cjs/api/datasources/error-formatting.js +28 -0
- package/dist-cjs/api/plugins/plugins.js +19 -1
- package/dist-cjs/api/roles/roles.js +16 -3
- package/dist-cjs/api/stories/stories.js +29 -1
- package/dist-cjs/api-v2/sync/index.js +4 -89
- package/package.json +1 -1
|
@@ -27,12 +27,16 @@ const defaultProgress = (event) => {
|
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
import { createComponent, createComponentsGroup, getAllComponents, getAllComponentsGroups, removeComponent, removeComponentGroup, updateComponent, } from "./components.js";
|
|
30
|
-
async function ensureComponentGroupsExist(groupNames, config) {
|
|
30
|
+
async function ensureComponentGroupsExist(groupNames, config, options = {}) {
|
|
31
31
|
try {
|
|
32
32
|
const existing = await getAllComponentsGroups(config);
|
|
33
33
|
const existingNames = new Set((existing ?? []).map((g) => g.name));
|
|
34
34
|
for (const groupName of groupNames) {
|
|
35
35
|
if (!existingNames.has(groupName)) {
|
|
36
|
+
if (options.dryRun) {
|
|
37
|
+
Logger.warning(`[dry-run] Would create component group '${groupName}'.`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
36
40
|
await createComponentsGroup(groupName, config);
|
|
37
41
|
}
|
|
38
42
|
}
|
|
@@ -52,7 +56,7 @@ function resolveGroupUuid(component, remoteGroups) {
|
|
|
52
56
|
return { ...component, component_group_uuid: match.uuid };
|
|
53
57
|
}
|
|
54
58
|
export async function syncComponentsData(args, config) {
|
|
55
|
-
const { components, presets, ssot, onProgress } = args;
|
|
59
|
+
const { components, presets, ssot, dryRun, onProgress } = args;
|
|
56
60
|
const progress = onProgress ?? defaultProgress;
|
|
57
61
|
const result = {
|
|
58
62
|
created: [],
|
|
@@ -60,23 +64,37 @@ export async function syncComponentsData(args, config) {
|
|
|
60
64
|
skipped: [],
|
|
61
65
|
errors: [],
|
|
62
66
|
};
|
|
67
|
+
if (dryRun) {
|
|
68
|
+
Logger.warning("[dry-run] Component sync will only read remote data and report planned changes.");
|
|
69
|
+
}
|
|
63
70
|
if (ssot) {
|
|
64
71
|
const existingComponents = await getAllComponents(config);
|
|
65
72
|
const existingGroups = await getAllComponentsGroups(config);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
if (dryRun) {
|
|
74
|
+
for (const component of existingComponents ?? []) {
|
|
75
|
+
Logger.warning(`[dry-run] Would remove component '${component.name}'.`);
|
|
76
|
+
}
|
|
77
|
+
for (const group of existingGroups ?? []) {
|
|
78
|
+
Logger.warning(`[dry-run] Would remove component group '${group.name}'.`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
await Promise.allSettled([
|
|
83
|
+
...(existingComponents ?? []).map((c) => removeComponent(c, config)),
|
|
84
|
+
...(existingGroups ?? []).map((g) => removeComponentGroup(g, config)),
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
70
87
|
}
|
|
71
88
|
const nonEmptyComponents = components.filter((c) => !isObjectEmpty(c));
|
|
72
89
|
const groupsToCheck = uniqueValuesFrom(nonEmptyComponents
|
|
73
90
|
.filter((c) => c.component_group_name)
|
|
74
91
|
.map((c) => c.component_group_name));
|
|
75
|
-
await ensureComponentGroupsExist(groupsToCheck, config);
|
|
92
|
+
await ensureComponentGroupsExist(groupsToCheck, config, { dryRun });
|
|
76
93
|
let remoteComponents = [];
|
|
77
94
|
let remoteGroups = [];
|
|
78
95
|
try {
|
|
79
|
-
remoteComponents =
|
|
96
|
+
remoteComponents =
|
|
97
|
+
ssot && dryRun ? [] : ((await getAllComponents(config)) ?? []);
|
|
80
98
|
}
|
|
81
99
|
catch (error) {
|
|
82
100
|
Logger.warning(`Could not fetch remote components: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -121,6 +139,18 @@ export async function syncComponentsData(args, config) {
|
|
|
121
139
|
action: "updating",
|
|
122
140
|
});
|
|
123
141
|
try {
|
|
142
|
+
if (dryRun) {
|
|
143
|
+
Logger.warning(`[dry-run] Would update component '${name}'.`);
|
|
144
|
+
result.updated.push(name);
|
|
145
|
+
progress({
|
|
146
|
+
type: "progress",
|
|
147
|
+
current: currentIndex,
|
|
148
|
+
total: totalComponents,
|
|
149
|
+
name,
|
|
150
|
+
action: "updated",
|
|
151
|
+
});
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
124
154
|
await updateComponent(component, presets, config);
|
|
125
155
|
result.updated.push(name);
|
|
126
156
|
progress({
|
|
@@ -158,6 +188,18 @@ export async function syncComponentsData(args, config) {
|
|
|
158
188
|
action: "creating",
|
|
159
189
|
});
|
|
160
190
|
try {
|
|
191
|
+
if (dryRun) {
|
|
192
|
+
Logger.warning(`[dry-run] Would create component '${name}'.`);
|
|
193
|
+
result.created.push(name);
|
|
194
|
+
progress({
|
|
195
|
+
type: "progress",
|
|
196
|
+
current: currentIndex,
|
|
197
|
+
total: totalComponents,
|
|
198
|
+
name,
|
|
199
|
+
action: "created",
|
|
200
|
+
});
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
161
203
|
await createComponent(component, presets, config);
|
|
162
204
|
result.created.push(name);
|
|
163
205
|
progress({
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import Logger from "../../utils/logger.js";
|
|
3
3
|
import { isObjectEmpty } from "../../utils/object-utils.js";
|
|
4
|
+
import { getAllItemsWithPagination } from "../utils/request.js";
|
|
4
5
|
import { getDatasource } from "./datasources.js";
|
|
6
|
+
import { formatDatasourceApiError } from "./error-formatting.js";
|
|
5
7
|
const _decorateWithDimensions = async (args, config) => {
|
|
6
8
|
const { currentDatasource, dimensionsData, _callback } = args;
|
|
7
9
|
const { spaceId, sbApi } = config;
|
|
@@ -35,29 +37,43 @@ export const getDatasourceEntries = async (args, config) => {
|
|
|
35
37
|
Logger.log(`Trying to get '${datasourceName}' datasource entries.`);
|
|
36
38
|
const data = await getDatasource({ datasourceName }, config); // TODO: maybe this step is not needed, i think we can retrieve entries directly using slug (but using delivery api, not management)
|
|
37
39
|
if (data) {
|
|
38
|
-
|
|
39
|
-
.get(`spaces/${spaceId}/datasource_entries/`, {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
})
|
|
47
|
-
|
|
40
|
+
const datasourceEntries = await getAllItemsWithPagination({
|
|
41
|
+
apiFn: ({ per_page, page }) => sbApi.get(`spaces/${spaceId}/datasource_entries/`, {
|
|
42
|
+
datasource_id: data[0].id,
|
|
43
|
+
per_page,
|
|
44
|
+
page,
|
|
45
|
+
}),
|
|
46
|
+
params: {},
|
|
47
|
+
itemsKey: "datasource_entries",
|
|
48
|
+
});
|
|
49
|
+
Logger.success(`Datasource Entries for '${datasourceName}' datasource successfully retrieved.`);
|
|
50
|
+
return { datasource_entries: datasourceEntries };
|
|
48
51
|
}
|
|
52
|
+
return { datasource_entries: [] };
|
|
49
53
|
};
|
|
50
54
|
export const createDatasourceEntries = (args, config) => {
|
|
51
|
-
const { datasource_entries, remoteDatasourceEntries, data } = args;
|
|
55
|
+
const { datasource_entries, remoteDatasourceEntries, data, dryRun } = args;
|
|
56
|
+
const remoteEntries = Array.isArray(remoteDatasourceEntries?.datasource_entries)
|
|
57
|
+
? remoteDatasourceEntries.datasource_entries
|
|
58
|
+
: [];
|
|
52
59
|
return Promise.all(datasource_entries.map((datasourceEntry) => {
|
|
53
|
-
const
|
|
54
|
-
|
|
60
|
+
const datasourceEntryName = datasourceEntry.name ?? Object.values(datasourceEntry)[0];
|
|
61
|
+
const datasourceToBeUpdated = remoteEntries.find((remoteDatasourceEntry) => remoteDatasourceEntry.name === datasourceEntryName);
|
|
62
|
+
if (dryRun) {
|
|
63
|
+
const action = datasourceToBeUpdated ? "update" : "create";
|
|
64
|
+
Logger.warning(`[dry-run] Would ${action} datasource entry '${datasourceEntryName}' in '${data.datasource.name}' datasource.`);
|
|
65
|
+
return data;
|
|
66
|
+
}
|
|
55
67
|
if (datasourceToBeUpdated) {
|
|
56
68
|
return updateDatasourceEntry({ data, datasourceEntry, datasourceToBeUpdated }, config);
|
|
57
69
|
}
|
|
58
70
|
return createDatasourceEntry({ data, datasourceEntry }, config);
|
|
59
71
|
}))
|
|
60
72
|
.then((_) => {
|
|
73
|
+
if (dryRun) {
|
|
74
|
+
Logger.warning(`[dry-run] Datasource entries for ${data.datasource.id} datasource id were planned without API writes.`);
|
|
75
|
+
return data;
|
|
76
|
+
}
|
|
61
77
|
Logger.success(`Datasource entries for ${data.datasource.id} datasource id has been successfully synced.`);
|
|
62
78
|
return data;
|
|
63
79
|
})
|
|
@@ -84,7 +100,7 @@ const _createDatasourceEntry = (args, config) => {
|
|
|
84
100
|
console.log("Full Create error: ");
|
|
85
101
|
console.log(err);
|
|
86
102
|
}
|
|
87
|
-
Logger.error(`Unable to create datasource entry in ${currentDatasource.datasource.name} datasource
|
|
103
|
+
Logger.error(`Unable to create datasource entry '${finalDatasource_entry.name}' in ${currentDatasource.datasource.name} datasource. Value: '${finalDatasource_entry.value}'. ${formatDatasourceApiError(err)}`);
|
|
88
104
|
});
|
|
89
105
|
};
|
|
90
106
|
export const createDatasourceEntry = (args, config) => {
|
|
@@ -125,7 +141,7 @@ const _updateDatasourceEntry = (args, config) => {
|
|
|
125
141
|
console.log("Full update error: ");
|
|
126
142
|
console.log(err);
|
|
127
143
|
}
|
|
128
|
-
Logger.error(`Unable to update datasource entry in ${currentDatasource.datasource.name} datasource
|
|
144
|
+
Logger.error(`Unable to update datasource entry '${finalDatasource_entry.name}' in ${currentDatasource.datasource.name} datasource. Value: '${finalDatasource_entry.value}'. ${formatDatasourceApiError(err)}`);
|
|
129
145
|
});
|
|
130
146
|
};
|
|
131
147
|
export const updateDatasourceEntry = (args, config) => {
|
|
@@ -4,6 +4,7 @@ export declare const getAllDatasources: GetAllDatasources;
|
|
|
4
4
|
export declare const getDatasource: GetDatasource;
|
|
5
5
|
export declare const createDatasource: CreateDatasource;
|
|
6
6
|
export declare const updateDatasource: UpdateDatasource;
|
|
7
|
-
export declare const syncDatasourcesData: ({ datasources }: {
|
|
7
|
+
export declare const syncDatasourcesData: ({ datasources, dryRun }: {
|
|
8
8
|
datasources: any[];
|
|
9
|
+
dryRun?: boolean;
|
|
9
10
|
}, config: any) => Promise<SyncResult>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Logger from "../../utils/logger.js";
|
|
2
2
|
import { getAllItemsWithPagination } from "../utils/request.js";
|
|
3
3
|
import { createDatasourceEntries, getDatasourceEntries, } from "./datasource-entries.js";
|
|
4
|
+
import { formatDatasourceApiError } from "./error-formatting.js";
|
|
4
5
|
// GET
|
|
5
6
|
export const getAllDatasources = (config) => {
|
|
6
7
|
const { sbApi, spaceId } = config;
|
|
@@ -75,7 +76,7 @@ export const createDatasource = (args, config) => {
|
|
|
75
76
|
datasource_entries: datasource.datasource_entries,
|
|
76
77
|
};
|
|
77
78
|
})
|
|
78
|
-
.catch((err) => Logger.error(err));
|
|
79
|
+
.catch((err) => Logger.error(`Unable to create datasource '${datasource.name}' with slug '${datasource.slug}'. ${formatDatasourceApiError(err)}`));
|
|
79
80
|
};
|
|
80
81
|
export const updateDatasource = (args, config) => {
|
|
81
82
|
const { datasource, datasourceToBeUpdated } = args;
|
|
@@ -107,16 +108,19 @@ export const updateDatasource = (args, config) => {
|
|
|
107
108
|
datasource_entries: datasource.datasource_entries,
|
|
108
109
|
};
|
|
109
110
|
})
|
|
110
|
-
.catch((err) => Logger.error(err));
|
|
111
|
+
.catch((err) => Logger.error(`Unable to update datasource '${datasource.name}' with id '${datasourceToBeUpdated.id}'. ${formatDatasourceApiError(err)}`));
|
|
111
112
|
};
|
|
112
113
|
// File-based sync wrapper lives in `datasources.sync.ts` to keep this module CJS-safe.
|
|
113
|
-
export const syncDatasourcesData = async ({ datasources }, config) => {
|
|
114
|
+
export const syncDatasourcesData = async ({ datasources, dryRun }, config) => {
|
|
114
115
|
const result = {
|
|
115
116
|
created: [],
|
|
116
117
|
updated: [],
|
|
117
118
|
skipped: [],
|
|
118
119
|
errors: [],
|
|
119
120
|
};
|
|
121
|
+
if (dryRun) {
|
|
122
|
+
Logger.warning("[dry-run] Datasource sync will only read remote data and report planned changes.");
|
|
123
|
+
}
|
|
120
124
|
const remoteDatasourcesRaw = await getAllDatasources(config);
|
|
121
125
|
const remoteDatasources = Array.isArray(remoteDatasourcesRaw)
|
|
122
126
|
? remoteDatasourcesRaw
|
|
@@ -129,6 +133,32 @@ export const syncDatasourcesData = async ({ datasources }, config) => {
|
|
|
129
133
|
}
|
|
130
134
|
try {
|
|
131
135
|
const datasourceToBeUpdated = remoteDatasources.find((remoteDatasource) => datasource.name === remoteDatasource.name);
|
|
136
|
+
if (dryRun) {
|
|
137
|
+
if (datasourceToBeUpdated) {
|
|
138
|
+
result.updated.push(name);
|
|
139
|
+
Logger.warning(`[dry-run] Would update datasource '${name}'.`);
|
|
140
|
+
const remoteDatasourceEntries = await getDatasourceEntries({
|
|
141
|
+
datasourceName: name,
|
|
142
|
+
}, config);
|
|
143
|
+
await createDatasourceEntries({
|
|
144
|
+
data: { datasource: datasourceToBeUpdated },
|
|
145
|
+
datasource_entries: datasource.datasource_entries ?? [],
|
|
146
|
+
remoteDatasourceEntries,
|
|
147
|
+
dryRun,
|
|
148
|
+
}, config);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
const entriesCount = Array.isArray(datasource.datasource_entries)
|
|
152
|
+
? datasource.datasource_entries.length
|
|
153
|
+
: 0;
|
|
154
|
+
result.created.push(name);
|
|
155
|
+
Logger.warning(`[dry-run] Would create datasource '${name}'.`);
|
|
156
|
+
if (entriesCount > 0) {
|
|
157
|
+
Logger.warning(`[dry-run] Would create ${entriesCount} datasource entries for '${name}' after datasource creation.`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
132
162
|
const opResult = datasourceToBeUpdated
|
|
133
163
|
? await updateDatasource({ datasource, datasourceToBeUpdated }, config)
|
|
134
164
|
: await createDatasource({ datasource }, config);
|
|
@@ -2,10 +2,10 @@ import { getFileContentWithRequire } from "../../utils/files.js";
|
|
|
2
2
|
import Logger from "../../utils/logger.js";
|
|
3
3
|
import { syncDatasourcesData } from "./datasources.js";
|
|
4
4
|
export const syncDatasources = async (args, config) => {
|
|
5
|
-
const { providedDatasources } = args;
|
|
5
|
+
const { providedDatasources, dryRun } = args;
|
|
6
6
|
Logger.log(`Trying to sync provided datasources: `);
|
|
7
7
|
const providedDatasourcesContent = await Promise.all(providedDatasources.map((datasource) => {
|
|
8
8
|
return getFileContentWithRequire({ file: datasource.p });
|
|
9
9
|
}));
|
|
10
|
-
await syncDatasourcesData({ datasources: providedDatasourcesContent }, config);
|
|
10
|
+
await syncDatasourcesData({ datasources: providedDatasourcesContent, dryRun }, config);
|
|
11
11
|
};
|
|
@@ -13,11 +13,15 @@ export type UpdateDatasource = (args: {
|
|
|
13
13
|
}, config: RequestBaseConfig) => Promise<any>;
|
|
14
14
|
export type SyncDatasources = (args: {
|
|
15
15
|
providedDatasources: OneFileElement[];
|
|
16
|
+
dryRun?: boolean;
|
|
16
17
|
}, config: RequestBaseConfig) => Promise<any>;
|
|
17
18
|
export type SyncProvidedDatasources = (args: {
|
|
18
19
|
datasources: string[];
|
|
20
|
+
dryRun?: boolean;
|
|
19
21
|
}, config: RequestBaseConfig) => Promise<void>;
|
|
20
|
-
export type SyncAllDatasources = (config: RequestBaseConfig
|
|
22
|
+
export type SyncAllDatasources = (config: RequestBaseConfig, args?: {
|
|
23
|
+
dryRun?: boolean;
|
|
24
|
+
}) => Promise<void>;
|
|
21
25
|
export type GetDatasourceEntries = (args: {
|
|
22
26
|
datasourceName: string;
|
|
23
27
|
}, config: RequestBaseConfig) => Promise<any>;
|
|
@@ -25,6 +29,7 @@ export type CreateDatasourceEntries = (args: {
|
|
|
25
29
|
data: any;
|
|
26
30
|
datasource_entries: any;
|
|
27
31
|
remoteDatasourceEntries: any;
|
|
32
|
+
dryRun?: boolean;
|
|
28
33
|
}, config: RequestBaseConfig) => Promise<any> | void;
|
|
29
34
|
export type CreateDatasourceEntry = (args: {
|
|
30
35
|
data: any;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const formatDatasourceApiError: (err: any) => string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const formatDatasourceApiError = (err) => {
|
|
2
|
+
const details = [];
|
|
3
|
+
const status = err?.response?.status;
|
|
4
|
+
const statusText = err?.response?.statusText;
|
|
5
|
+
const responseData = err?.response?.data;
|
|
6
|
+
if (status || statusText) {
|
|
7
|
+
details.push(`status: ${[status, statusText].filter(Boolean).join(" ")}`);
|
|
8
|
+
}
|
|
9
|
+
if (responseData?.message) {
|
|
10
|
+
details.push(`message: ${responseData.message}`);
|
|
11
|
+
}
|
|
12
|
+
if (responseData?.error) {
|
|
13
|
+
details.push(`error: ${responseData.error}`);
|
|
14
|
+
}
|
|
15
|
+
if (responseData?.errors) {
|
|
16
|
+
details.push(`errors: ${typeof responseData.errors === "string"
|
|
17
|
+
? responseData.errors
|
|
18
|
+
: JSON.stringify(responseData.errors)}`);
|
|
19
|
+
}
|
|
20
|
+
if (!details.length && err?.message) {
|
|
21
|
+
details.push(`message: ${err.message}`);
|
|
22
|
+
}
|
|
23
|
+
return details.length ? details.join("; ") : String(err);
|
|
24
|
+
};
|
package/dist/api/migrate.js
CHANGED
|
@@ -53,7 +53,7 @@ const _resolveGroups = async (component, existedGroups, remoteComponentsGroups)
|
|
|
53
53
|
return { ...component, component_group_uuid };
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
|
-
export const syncComponents = async (specifiedComponents, presets, config) => {
|
|
56
|
+
export const syncComponents = async (specifiedComponents, presets, config, options = {}) => {
|
|
57
57
|
Logger.log("sync2Components: ");
|
|
58
58
|
let specifiedComponentsContent = await Promise.all(specifiedComponents.map((component) => {
|
|
59
59
|
return getFileContentWithRequire({ file: component.p });
|
|
@@ -64,9 +64,14 @@ export const syncComponents = async (specifiedComponents, presets, config) => {
|
|
|
64
64
|
* - .sb.resolvers.ts files resolvers
|
|
65
65
|
*/
|
|
66
66
|
specifiedComponentsContent = await resolveGlobalTransformations(specifiedComponentsContent);
|
|
67
|
-
await syncComponentsData({
|
|
67
|
+
await syncComponentsData({
|
|
68
|
+
components: specifiedComponentsContent,
|
|
69
|
+
presets,
|
|
70
|
+
ssot: options.ssot,
|
|
71
|
+
dryRun: options.dryRun,
|
|
72
|
+
}, config);
|
|
68
73
|
};
|
|
69
|
-
export const syncAllComponents = async (presets, config) => {
|
|
74
|
+
export const syncAllComponents = async (presets, config, options = {}) => {
|
|
70
75
|
// #1: discover all external .sb.js files
|
|
71
76
|
const allLocalSbComponentsSchemaFiles = await discover({
|
|
72
77
|
scope: SCOPE.local,
|
|
@@ -83,9 +88,12 @@ export const syncAllComponents = async (presets, config) => {
|
|
|
83
88
|
external: allExternalSbComponentsSchemaFiles,
|
|
84
89
|
});
|
|
85
90
|
// #4: sync - do all stuff already done (groups resolving, and so on)
|
|
86
|
-
return await syncComponents([...local, ...external], presets, config
|
|
91
|
+
return await syncComponents([...local, ...external], presets, config, {
|
|
92
|
+
dryRun: options.dryRun,
|
|
93
|
+
ssot: options.ssot,
|
|
94
|
+
});
|
|
87
95
|
};
|
|
88
|
-
export const syncProvidedComponents = async (presets, components, packageName, config) => {
|
|
96
|
+
export const syncProvidedComponents = async (presets, components, packageName, config, options = {}) => {
|
|
89
97
|
if (!packageName) {
|
|
90
98
|
// #1: discover all external .sb.js files
|
|
91
99
|
const allLocalSbComponentsSchemaFiles = await discoverMany({
|
|
@@ -105,7 +113,9 @@ export const syncProvidedComponents = async (presets, components, packageName, c
|
|
|
105
113
|
external: allExternalSbComponentsSchemaFiles,
|
|
106
114
|
});
|
|
107
115
|
// #4: sync - do all stuff already done (groups resolving, and so on)
|
|
108
|
-
return await syncComponents([...local, ...external], presets, config
|
|
116
|
+
return await syncComponents([...local, ...external], presets, config, {
|
|
117
|
+
dryRun: options.dryRun,
|
|
118
|
+
});
|
|
109
119
|
}
|
|
110
120
|
else {
|
|
111
121
|
// implement discovering and syncrhonizing with packageName
|
|
@@ -125,13 +135,20 @@ export const syncProvidedComponents = async (presets, components, packageName, c
|
|
|
125
135
|
external: allExternalSbComponentsSchemaFiles,
|
|
126
136
|
});
|
|
127
137
|
// #4: sync - do all stuff already done (groups resolving, and so on)
|
|
128
|
-
return syncComponents([...local, ...external], presets, config
|
|
138
|
+
return syncComponents([...local, ...external], presets, config, {
|
|
139
|
+
dryRun: options.dryRun,
|
|
140
|
+
});
|
|
129
141
|
}
|
|
130
142
|
};
|
|
131
|
-
export const syncAssets = async ({ transmission: { from, to }, syncDirection }, config) => {
|
|
143
|
+
export const syncAssets = async ({ transmission: { from, to }, syncDirection, dryRun }, config) => {
|
|
132
144
|
Logger.log(`We would try to migrate Assets data from: ${from} to: ${to}`);
|
|
133
145
|
const allAssets = await getAllAssets({ spaceId: from }, config);
|
|
134
|
-
|
|
146
|
+
const assets = Array.isArray(allAssets?.assets) ? allAssets.assets : [];
|
|
147
|
+
if (dryRun) {
|
|
148
|
+
Logger.warning(`[dry-run] Would sync ${assets.length} assets from ${from} to ${to}.`);
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
await Promise.all(assets.map((asset) => {
|
|
135
152
|
const { id, created_at, updated_at, ...newAssetPayload } = asset;
|
|
136
153
|
return migrateAsset({
|
|
137
154
|
migrateTo: to,
|
|
@@ -139,13 +156,18 @@ export const syncAssets = async ({ transmission: { from, to }, syncDirection },
|
|
|
139
156
|
syncDirection,
|
|
140
157
|
}, config);
|
|
141
158
|
}));
|
|
159
|
+
return true;
|
|
142
160
|
};
|
|
143
|
-
const syncStories = async ({ transmission: { from, to }, stories, toSpaceId }, config) => {
|
|
161
|
+
const syncStories = async ({ transmission: { from, to }, stories, toSpaceId, dryRun }, config) => {
|
|
144
162
|
Logger.log(`We would try to migrate Stories data from: ${from} to: ${to}`);
|
|
145
163
|
const storiesToPass = stories
|
|
146
164
|
.map((item) => item.story)
|
|
147
165
|
.map((item) => item.parent_id === 0 ? { ...item, parent_id: null } : item);
|
|
148
166
|
Logger.warning(`Amount of all stories to migrate: ${storiesToPass.length}`);
|
|
167
|
+
if (dryRun) {
|
|
168
|
+
Logger.warning(`[dry-run] Would sync ${storiesToPass.length} stories from ${from} to ${to}.`);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
149
171
|
const storiesToPassJson = JSON.stringify(storiesToPass, null, 2);
|
|
150
172
|
if (config.debug) {
|
|
151
173
|
dumpToFile("storiesToPass.json", storiesToPassJson);
|
|
@@ -156,10 +178,23 @@ const syncStories = async ({ transmission: { from, to }, stories, toSpaceId }, c
|
|
|
156
178
|
dumpToFile("tree.json", jsonString);
|
|
157
179
|
}
|
|
158
180
|
await traverseAndCreate({ tree, realParentId: null, spaceId: toSpaceId }, config);
|
|
181
|
+
return true;
|
|
159
182
|
};
|
|
160
|
-
export const syncContent = async ({ type, transmission, syncDirection, filename }, config) => {
|
|
183
|
+
export const syncContent = async ({ type, transmission, syncDirection, filename, dryRun }, config) => {
|
|
184
|
+
if (dryRun) {
|
|
185
|
+
Logger.warning("[dry-run] Content sync will only read source data and report planned changes.");
|
|
186
|
+
}
|
|
161
187
|
if (type === "stories") {
|
|
162
188
|
if (syncDirection === "fromSpaceToFile") {
|
|
189
|
+
if (dryRun) {
|
|
190
|
+
const stories = await getAllStories({}, {
|
|
191
|
+
...config,
|
|
192
|
+
spaceId: transmission.from,
|
|
193
|
+
sbApi: config.sbApi,
|
|
194
|
+
});
|
|
195
|
+
Logger.warning(`[dry-run] Would back up ${stories.length} stories from ${transmission.from} to '${transmission.to}'.`);
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
163
198
|
await backupStories({
|
|
164
199
|
filename: transmission.to,
|
|
165
200
|
suffix: ".sb.stories",
|
|
@@ -176,6 +211,7 @@ export const syncContent = async ({ type, transmission, syncDirection, filename
|
|
|
176
211
|
transmission,
|
|
177
212
|
stories,
|
|
178
213
|
toSpaceId: transmission.to,
|
|
214
|
+
dryRun,
|
|
179
215
|
}, config);
|
|
180
216
|
}
|
|
181
217
|
if (syncDirection === "fromFileToSpace") {
|
|
@@ -191,6 +227,7 @@ export const syncContent = async ({ type, transmission, syncDirection, filename
|
|
|
191
227
|
transmission,
|
|
192
228
|
stories: storiesFileContent[0],
|
|
193
229
|
toSpaceId: transmission.to,
|
|
230
|
+
dryRun,
|
|
194
231
|
}, config);
|
|
195
232
|
}
|
|
196
233
|
if (syncDirection === "fromAWSToSpace") {
|
|
@@ -199,6 +236,7 @@ export const syncContent = async ({ type, transmission, syncDirection, filename
|
|
|
199
236
|
transmission,
|
|
200
237
|
stories: data.stories,
|
|
201
238
|
toSpaceId: transmission.to,
|
|
239
|
+
dryRun,
|
|
202
240
|
}, config);
|
|
203
241
|
}
|
|
204
242
|
return true;
|
|
@@ -209,7 +247,7 @@ export const syncContent = async ({ type, transmission, syncDirection, filename
|
|
|
209
247
|
}
|
|
210
248
|
else if (syncDirection === "fromSpaceToSpace" ||
|
|
211
249
|
syncDirection === "fromSpaceToFile") {
|
|
212
|
-
await syncAssets({ transmission, syncDirection }, config);
|
|
250
|
+
await syncAssets({ transmission, syncDirection, dryRun }, config);
|
|
213
251
|
}
|
|
214
252
|
else {
|
|
215
253
|
Logger.warning(`${syncDirection} with ${type} This is not implemented yet!`);
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import type { RequestBaseConfig } from "./utils/request.js";
|
|
2
2
|
import type { SyncDirection } from "../cli/sync.types.js";
|
|
3
|
+
import type { SyncOptions } from "./sync/sync.types.js";
|
|
3
4
|
import type { OneFileElement } from "../cli/utils/discover.js";
|
|
4
|
-
export type SyncComponents = (specifiedComponents: OneFileElement[], presets: boolean, config: RequestBaseConfig
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export type
|
|
5
|
+
export type SyncComponents = (specifiedComponents: OneFileElement[], presets: boolean, config: RequestBaseConfig, options?: SyncOptions & {
|
|
6
|
+
ssot?: boolean;
|
|
7
|
+
}) => Promise<any>;
|
|
8
|
+
export type SyncAllComponents = (presets: boolean, config: RequestBaseConfig, options?: SyncOptions & {
|
|
9
|
+
ssot?: boolean;
|
|
10
|
+
}) => Promise<any>;
|
|
11
|
+
export type SyncProvidedComponents = (presets: boolean, components: string[], packageName: boolean, config: RequestBaseConfig, options?: SyncOptions) => Promise<any>;
|
|
12
|
+
export type SyncStories = ({ transmission, stories, toSpaceId, dryRun, }: {
|
|
8
13
|
transmission: SyncContent["transmission"];
|
|
9
14
|
stories: any[];
|
|
10
15
|
toSpaceId: string;
|
|
16
|
+
dryRun?: boolean;
|
|
11
17
|
}, config: RequestBaseConfig) => Promise<any>;
|
|
12
18
|
export interface SyncContent {
|
|
13
19
|
type: "stories" | "assets";
|
|
@@ -17,9 +23,11 @@ export interface SyncContent {
|
|
|
17
23
|
};
|
|
18
24
|
syncDirection: SyncDirection;
|
|
19
25
|
filename?: string;
|
|
26
|
+
dryRun?: boolean;
|
|
20
27
|
}
|
|
21
|
-
export type SyncContentFunction = ({ type, transmission, syncDirection, filename }: SyncContent, config: RequestBaseConfig) => Promise<any>;
|
|
22
|
-
export type SyncAssets = ({ transmission, }: {
|
|
28
|
+
export type SyncContentFunction = ({ type, transmission, syncDirection, filename, dryRun }: SyncContent, config: RequestBaseConfig) => Promise<any>;
|
|
29
|
+
export type SyncAssets = ({ transmission, dryRun, }: {
|
|
23
30
|
transmission: SyncContent["transmission"];
|
|
24
31
|
syncDirection: SyncDirection;
|
|
32
|
+
dryRun?: boolean;
|
|
25
33
|
}, config: RequestBaseConfig) => Promise<any>;
|
|
@@ -5,9 +5,10 @@ export declare const getPlugin: GetPlugin;
|
|
|
5
5
|
export declare const getPluginDetails: GetPluginDetails;
|
|
6
6
|
export declare const updatePlugin: UpdatePlugin;
|
|
7
7
|
export declare const createPlugin: CreatePlugin;
|
|
8
|
-
export declare const syncPluginsData: ({ plugins }: {
|
|
8
|
+
export declare const syncPluginsData: ({ plugins, dryRun, }: {
|
|
9
9
|
plugins: {
|
|
10
10
|
name: string;
|
|
11
11
|
body: string;
|
|
12
12
|
}[];
|
|
13
|
+
dryRun?: boolean;
|
|
13
14
|
}, config: any) => Promise<SyncResult>;
|
|
@@ -87,13 +87,17 @@ export const createPlugin = (pluginName, config) => {
|
|
|
87
87
|
});
|
|
88
88
|
};
|
|
89
89
|
// File-based sync wrapper lives in `plugins.sync.ts` to keep this module CJS-safe.
|
|
90
|
-
export const syncPluginsData = async ({ plugins }, config) => {
|
|
90
|
+
export const syncPluginsData = async ({ plugins, dryRun, }, config) => {
|
|
91
91
|
const result = {
|
|
92
92
|
created: [],
|
|
93
93
|
updated: [],
|
|
94
94
|
skipped: [],
|
|
95
95
|
errors: [],
|
|
96
96
|
};
|
|
97
|
+
if (dryRun) {
|
|
98
|
+
Logger.warning("[dry-run] Plugin sync will only read remote data and report planned changes.");
|
|
99
|
+
}
|
|
100
|
+
const remotePlugins = dryRun ? await getAllPlugins(config) : [];
|
|
97
101
|
for (const p of plugins) {
|
|
98
102
|
const name = String(p?.name ?? "unknown");
|
|
99
103
|
if (!p?.name) {
|
|
@@ -101,6 +105,20 @@ export const syncPluginsData = async ({ plugins }, config) => {
|
|
|
101
105
|
continue;
|
|
102
106
|
}
|
|
103
107
|
try {
|
|
108
|
+
if (dryRun) {
|
|
109
|
+
const plugin = Array.isArray(remotePlugins)
|
|
110
|
+
? remotePlugins.find((remotePlugin) => remotePlugin.name === name)
|
|
111
|
+
: undefined;
|
|
112
|
+
if (plugin) {
|
|
113
|
+
Logger.warning(`[dry-run] Would update plugin '${name}'.`);
|
|
114
|
+
result.updated.push(name);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
Logger.warning(`[dry-run] Would create plugin '${name}'.`);
|
|
118
|
+
result.created.push(name);
|
|
119
|
+
}
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
104
122
|
const plugin = await getPlugin(name, config);
|
|
105
123
|
if (plugin) {
|
|
106
124
|
await updatePlugin({ plugin: plugin.field_type, body: p.body }, config);
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { readFile } from "../../utils/files.js";
|
|
2
2
|
import { syncPluginsData } from "./plugins.js";
|
|
3
|
-
export const syncProvidedPlugins = async ({ plugins }, config) => {
|
|
4
|
-
const body = await readFile("dist/export.js");
|
|
5
|
-
if (!body) {
|
|
3
|
+
export const syncProvidedPlugins = async ({ plugins, dryRun }, config) => {
|
|
4
|
+
const body = dryRun ? "" : await readFile("dist/export.js");
|
|
5
|
+
if (!body && !dryRun) {
|
|
6
6
|
throw new Error("Unable to read plugin bundle from dist/export.js");
|
|
7
7
|
}
|
|
8
8
|
await syncPluginsData({
|
|
9
|
-
plugins: plugins.map((name) => ({
|
|
9
|
+
plugins: plugins.map((name) => ({
|
|
10
|
+
name: String(name),
|
|
11
|
+
body: body ?? "",
|
|
12
|
+
})),
|
|
13
|
+
dryRun,
|
|
10
14
|
}, config);
|
|
11
15
|
};
|
|
@@ -9,6 +9,7 @@ interface UpdatePluginDTO {
|
|
|
9
9
|
}
|
|
10
10
|
interface SyncProvidedPluginsDTO {
|
|
11
11
|
plugins: string[];
|
|
12
|
+
dryRun?: boolean;
|
|
12
13
|
}
|
|
13
14
|
export type GetPlugin = (pluginName: string | undefined, config: RequestBaseConfig) => Promise<any>;
|
|
14
15
|
export type GetAllPlugins = (config: RequestBaseConfig) => Promise<any>;
|
|
@@ -4,6 +4,7 @@ export declare const createRole: CreateRole;
|
|
|
4
4
|
export declare const updateRole: UpdateRole;
|
|
5
5
|
export declare const getAllRoles: GetAllRoles;
|
|
6
6
|
export declare const getRole: GetRole;
|
|
7
|
-
export declare const syncRolesData: ({ roles }: {
|
|
7
|
+
export declare const syncRolesData: ({ roles, dryRun }: {
|
|
8
8
|
roles: any[];
|
|
9
|
+
dryRun?: boolean;
|
|
9
10
|
}, config: any) => Promise<SyncResult>;
|