@contentstack/cli-cm-export-query 1.0.0-beta.1
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/LICENSE +21 -0
- package/README.md +108 -0
- package/lib/commands/cm/stacks/export-query.d.ts +11 -0
- package/lib/commands/cm/stacks/export-query.js +79 -0
- package/lib/config/export-config.json +1 -0
- package/lib/config/index.d.ts +3 -0
- package/lib/config/index.js +57 -0
- package/lib/config/index.ts +59 -0
- package/lib/core/module-exporter.d.ts +11 -0
- package/lib/core/module-exporter.js +75 -0
- package/lib/core/query-executor.d.ts +17 -0
- package/lib/core/query-executor.js +253 -0
- package/lib/types/index.d.ts +161 -0
- package/lib/types/index.js +2 -0
- package/lib/utils/common-helper.d.ts +1 -0
- package/lib/utils/common-helper.js +12 -0
- package/lib/utils/config-handler.d.ts +2 -0
- package/lib/utils/config-handler.js +37 -0
- package/lib/utils/content-type-helper.d.ts +19 -0
- package/lib/utils/content-type-helper.js +90 -0
- package/lib/utils/dependency-resolver.d.ts +17 -0
- package/lib/utils/dependency-resolver.js +126 -0
- package/lib/utils/file-helper.d.ts +2 -0
- package/lib/utils/file-helper.js +5 -0
- package/lib/utils/index.d.ts +8 -0
- package/lib/utils/index.js +15 -0
- package/lib/utils/logger.d.ts +8 -0
- package/lib/utils/logger.js +158 -0
- package/lib/utils/query-parser.d.ts +9 -0
- package/lib/utils/query-parser.js +60 -0
- package/lib/utils/referenced-asset-handler.d.ts +22 -0
- package/lib/utils/referenced-asset-handler.js +120 -0
- package/messages/index.json +1 -0
- package/oclif.manifest.json +108 -0
- package/package.json +94 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QueryExporter = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const cli_utilities_1 = require("@contentstack/cli-utilities");
|
|
6
|
+
const path = tslib_1.__importStar(require("path"));
|
|
7
|
+
const query_parser_1 = require("../utils/query-parser");
|
|
8
|
+
const module_exporter_1 = require("./module-exporter");
|
|
9
|
+
const logger_1 = require("../utils/logger");
|
|
10
|
+
const utils_1 = require("../utils");
|
|
11
|
+
const utils_2 = require("../utils");
|
|
12
|
+
const utils_3 = require("../utils");
|
|
13
|
+
const utils_4 = require("../utils");
|
|
14
|
+
class QueryExporter {
|
|
15
|
+
constructor(managementAPIClient, exportQueryConfig) {
|
|
16
|
+
this.exportQueryConfig = exportQueryConfig;
|
|
17
|
+
this.stackAPIClient = managementAPIClient.stack({
|
|
18
|
+
api_key: exportQueryConfig.stackApiKey,
|
|
19
|
+
management_token: exportQueryConfig.managementToken,
|
|
20
|
+
});
|
|
21
|
+
// Initialize components
|
|
22
|
+
this.queryParser = new query_parser_1.QueryParser(this.exportQueryConfig);
|
|
23
|
+
this.moduleExporter = new module_exporter_1.ModuleExporter(exportQueryConfig);
|
|
24
|
+
}
|
|
25
|
+
async execute() {
|
|
26
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Starting query-based export...', 'info');
|
|
27
|
+
// Step 1: Parse and validate query
|
|
28
|
+
const parsedQuery = await this.queryParser.parse(this.exportQueryConfig.query);
|
|
29
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Query parsed and validated successfully', 'success');
|
|
30
|
+
// Step 2: Always export general modules
|
|
31
|
+
await this.exportGeneralModules();
|
|
32
|
+
// Step 4: Export queried modules
|
|
33
|
+
await this.exportQueriedModule(parsedQuery);
|
|
34
|
+
// Step 1: Read initial content types and mark them as exported
|
|
35
|
+
const contentTypesFilePath = path.join((0, cli_utilities_1.sanitizePath)(this.exportQueryConfig.exportDir), (0, cli_utilities_1.sanitizePath)(this.exportQueryConfig.branchName || ''), 'content_types', 'schema.json');
|
|
36
|
+
const contentTypes = utils_2.fsUtil.readFile((0, cli_utilities_1.sanitizePath)(contentTypesFilePath)) || [];
|
|
37
|
+
if (contentTypes.length === 0) {
|
|
38
|
+
(0, logger_1.log)(this.exportQueryConfig, 'No content types found, skipping export', 'info');
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
// Step 5: export other content types which are referenced in previous step
|
|
42
|
+
await this.exportReferencedContentTypes();
|
|
43
|
+
// Step 6: export dependent modules global fields, extensions, taxonomies
|
|
44
|
+
await this.exportDependentModules();
|
|
45
|
+
// Step 7: export content modules entries, assets
|
|
46
|
+
await this.exportContentModules();
|
|
47
|
+
// Step 9: export all other modules
|
|
48
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Query-based export completed successfully!', 'success');
|
|
49
|
+
}
|
|
50
|
+
// export general modules
|
|
51
|
+
async exportGeneralModules() {
|
|
52
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Exporting general modules...', 'info');
|
|
53
|
+
for (const module of this.exportQueryConfig.modules.general) {
|
|
54
|
+
await this.moduleExporter.exportModule(module);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async exportQueriedModule(parsedQuery) {
|
|
58
|
+
for (const [moduleName] of Object.entries(parsedQuery.modules)) {
|
|
59
|
+
const module = moduleName;
|
|
60
|
+
if (!this.exportQueryConfig.modules.queryable.includes(module)) {
|
|
61
|
+
(0, logger_1.log)(this.exportQueryConfig, `Module "${module}" is not queryable`, 'error');
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
(0, logger_1.log)(this.exportQueryConfig, `Exporting ${moduleName} with query...`, 'info');
|
|
65
|
+
// Export the queried module
|
|
66
|
+
await this.moduleExporter.exportModule(module, { query: parsedQuery });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async exportReferencedContentTypes() {
|
|
70
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Starting export of referenced content types...', 'info');
|
|
71
|
+
try {
|
|
72
|
+
const referencedHandler = new utils_1.ReferencedContentTypesHandler(this.exportQueryConfig);
|
|
73
|
+
const exportedContentTypeUIDs = new Set();
|
|
74
|
+
// Step 1: Read initial content types and mark them as exported
|
|
75
|
+
const contentTypesFilePath = path.join((0, cli_utilities_1.sanitizePath)(this.exportQueryConfig.exportDir), (0, cli_utilities_1.sanitizePath)(this.exportQueryConfig.branchName || ''), 'content_types', 'schema.json');
|
|
76
|
+
const contentTypes = utils_2.fsUtil.readFile((0, cli_utilities_1.sanitizePath)(contentTypesFilePath)) || [];
|
|
77
|
+
if (contentTypes.length === 0) {
|
|
78
|
+
(0, logger_1.log)(this.exportQueryConfig, 'No content types found, skipping referenced content types export', 'info');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Step 2: Start with initial batch (all currently exported content types)
|
|
82
|
+
let currentBatch = [...contentTypes];
|
|
83
|
+
(0, logger_1.log)(this.exportQueryConfig, `Starting with ${currentBatch.length} initial content types`, 'info');
|
|
84
|
+
// track reference depth
|
|
85
|
+
let iterationCount = 0;
|
|
86
|
+
// Step 3: Process batches until no new references are found
|
|
87
|
+
while (currentBatch.length > 0 && iterationCount < this.exportQueryConfig.maxCTReferenceDepth) {
|
|
88
|
+
iterationCount++;
|
|
89
|
+
currentBatch.forEach((ct) => exportedContentTypeUIDs.add(ct.uid));
|
|
90
|
+
// Extract referenced content types from current batch
|
|
91
|
+
const referencedUIDs = await referencedHandler.extractReferencedContentTypes(currentBatch);
|
|
92
|
+
// Filter out already exported content types
|
|
93
|
+
const newReferencedUIDs = referencedUIDs.filter((uid) => !exportedContentTypeUIDs.has(uid));
|
|
94
|
+
if (newReferencedUIDs.length > 0) {
|
|
95
|
+
(0, logger_1.log)(this.exportQueryConfig, `Found ${newReferencedUIDs.length} new referenced content types to fetch`, 'info');
|
|
96
|
+
// // Add to exported set to avoid duplicates in future iterations
|
|
97
|
+
// newReferencedUIDs.forEach((uid) => exportedContentTypeUIDs.add(uid));
|
|
98
|
+
// Step 4: Fetch new content types using moduleExporter
|
|
99
|
+
const query = {
|
|
100
|
+
modules: {
|
|
101
|
+
'content-types': {
|
|
102
|
+
uid: {
|
|
103
|
+
$in: newReferencedUIDs,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
await this.moduleExporter.exportModule('content-types', { query });
|
|
109
|
+
const newContentTypes = utils_2.fsUtil.readFile((0, cli_utilities_1.sanitizePath)(contentTypesFilePath));
|
|
110
|
+
currentBatch = [...newContentTypes];
|
|
111
|
+
// Push new content types to main array
|
|
112
|
+
contentTypes.push(...newContentTypes);
|
|
113
|
+
(0, logger_1.log)(this.exportQueryConfig, `Fetched ${currentBatch.length} new content types for next iteration`, 'info');
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
(0, logger_1.log)(this.exportQueryConfig, 'No new referenced content types found, stopping recursion', 'info');
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
utils_2.fsUtil.writeFile((0, cli_utilities_1.sanitizePath)(contentTypesFilePath), contentTypes);
|
|
121
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Referenced content types export completed successfully', 'success');
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
(0, logger_1.log)(this.exportQueryConfig, `Error exporting referenced content types: ${error.message}`, 'error');
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async exportDependentModules() {
|
|
129
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Starting export of dependent modules...', 'info');
|
|
130
|
+
try {
|
|
131
|
+
const dependenciesHandler = new utils_3.ContentTypeDependenciesHandler(this.stackAPIClient, this.exportQueryConfig);
|
|
132
|
+
// Extract dependencies from all exported content types
|
|
133
|
+
const dependencies = await dependenciesHandler.extractDependencies();
|
|
134
|
+
// Export Global Fields
|
|
135
|
+
if (dependencies.globalFields.size > 0) {
|
|
136
|
+
const globalFieldUIDs = Array.from(dependencies.globalFields);
|
|
137
|
+
(0, logger_1.log)(this.exportQueryConfig, `Exporting ${globalFieldUIDs.length} global fields...`, 'info');
|
|
138
|
+
const query = {
|
|
139
|
+
modules: {
|
|
140
|
+
'global-fields': {
|
|
141
|
+
uid: { $in: globalFieldUIDs },
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
await this.moduleExporter.exportModule('global-fields', { query });
|
|
146
|
+
}
|
|
147
|
+
// Export Extensions
|
|
148
|
+
if (dependencies.extensions.size > 0) {
|
|
149
|
+
const extensionUIDs = Array.from(dependencies.extensions);
|
|
150
|
+
(0, logger_1.log)(this.exportQueryConfig, `Exporting ${extensionUIDs.length} extensions...`, 'info');
|
|
151
|
+
const query = {
|
|
152
|
+
modules: {
|
|
153
|
+
extensions: {
|
|
154
|
+
uid: { $in: extensionUIDs },
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
await this.moduleExporter.exportModule('extensions', { query });
|
|
159
|
+
}
|
|
160
|
+
// export marketplace apps
|
|
161
|
+
if (dependencies.marketplaceApps.size > 0) {
|
|
162
|
+
const marketplaceAppInstallationUIDs = Array.from(dependencies.marketplaceApps);
|
|
163
|
+
(0, logger_1.log)(this.exportQueryConfig, `Exporting ${marketplaceAppInstallationUIDs.length} marketplace apps...`, 'info');
|
|
164
|
+
const query = {
|
|
165
|
+
modules: {
|
|
166
|
+
'marketplace-apps': {
|
|
167
|
+
installation_uid: { $in: marketplaceAppInstallationUIDs },
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
await this.moduleExporter.exportModule('marketplace-apps', { query });
|
|
172
|
+
}
|
|
173
|
+
// Export Taxonomies
|
|
174
|
+
if (dependencies.taxonomies.size > 0) {
|
|
175
|
+
const taxonomyUIDs = Array.from(dependencies.taxonomies);
|
|
176
|
+
(0, logger_1.log)(this.exportQueryConfig, `Exporting ${taxonomyUIDs.length} taxonomies...`, 'info');
|
|
177
|
+
const query = {
|
|
178
|
+
modules: {
|
|
179
|
+
taxonomies: {
|
|
180
|
+
uid: { $in: taxonomyUIDs },
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
await this.moduleExporter.exportModule('taxonomies', { query });
|
|
185
|
+
}
|
|
186
|
+
// export personalize
|
|
187
|
+
await this.moduleExporter.exportModule('personalize');
|
|
188
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Dependent modules export completed successfully', 'success');
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
(0, logger_1.log)(this.exportQueryConfig, `Error exporting dependent modules: ${error.message}`, 'error');
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async exportContentModules() {
|
|
196
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Starting export of content modules...', 'info');
|
|
197
|
+
try {
|
|
198
|
+
// Step 1: Export entries for all exported content types
|
|
199
|
+
await this.exportEntries();
|
|
200
|
+
// Step 2: Export referenced assets from entries
|
|
201
|
+
// add a delay of 5 seconds
|
|
202
|
+
const delay = this.exportQueryConfig.exportDelayMs || 5000;
|
|
203
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
204
|
+
await this.exportReferencedAssets();
|
|
205
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Content modules export completed successfully', 'success');
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
(0, logger_1.log)(this.exportQueryConfig, `Error exporting content modules: ${error.message}`, 'error');
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async exportEntries() {
|
|
213
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Exporting entries...', 'info');
|
|
214
|
+
try {
|
|
215
|
+
// Export entries - module exporter will automatically read exported content types
|
|
216
|
+
// and export entries for all of them
|
|
217
|
+
await this.moduleExporter.exportModule('entries');
|
|
218
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Entries export completed successfully', 'success');
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
(0, logger_1.log)(this.exportQueryConfig, `Error exporting entries: ${error.message}`, 'error');
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async exportReferencedAssets() {
|
|
226
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Starting export of referenced assets...', 'info');
|
|
227
|
+
try {
|
|
228
|
+
const assetHandler = new utils_4.AssetReferenceHandler(this.exportQueryConfig);
|
|
229
|
+
// Extract referenced asset UIDs from all entries
|
|
230
|
+
const assetUIDs = assetHandler.extractReferencedAssets();
|
|
231
|
+
if (assetUIDs.length > 0) {
|
|
232
|
+
(0, logger_1.log)(this.exportQueryConfig, `Exporting ${assetUIDs.length} referenced assets...`, 'info');
|
|
233
|
+
const query = {
|
|
234
|
+
modules: {
|
|
235
|
+
assets: {
|
|
236
|
+
uid: { $in: assetUIDs },
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
await this.moduleExporter.exportModule('assets', { query });
|
|
241
|
+
(0, logger_1.log)(this.exportQueryConfig, 'Referenced assets exported successfully', 'success');
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
(0, logger_1.log)(this.exportQueryConfig, 'No referenced assets found in entries', 'info');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
(0, logger_1.log)(this.exportQueryConfig, `Error exporting referenced assets: ${error.message}`, 'error');
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
exports.QueryExporter = QueryExporter;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
export interface AuthOptions {
|
|
2
|
+
contentstackClient: any;
|
|
3
|
+
}
|
|
4
|
+
export interface ContentStackManagementClient {
|
|
5
|
+
contentstackClient: object;
|
|
6
|
+
}
|
|
7
|
+
export interface PrintOptions {
|
|
8
|
+
color?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface InquirePayload {
|
|
11
|
+
type: string;
|
|
12
|
+
name: string;
|
|
13
|
+
message: string;
|
|
14
|
+
choices?: Array<any>;
|
|
15
|
+
transformer?: Function;
|
|
16
|
+
}
|
|
17
|
+
export interface User {
|
|
18
|
+
email: string;
|
|
19
|
+
authtoken: string;
|
|
20
|
+
}
|
|
21
|
+
export interface Region {
|
|
22
|
+
name: string;
|
|
23
|
+
cma: string;
|
|
24
|
+
cda: string;
|
|
25
|
+
uiHost: string;
|
|
26
|
+
}
|
|
27
|
+
export type Modules = 'stack' | 'locales' | 'environments' | 'content-types' | 'global-fields' | 'extensions' | 'taxonomies' | 'entries' | 'assets' | 'webhooks' | 'workflows' | 'custom-roles' | 'labels' | 'marketplace-apps' | 'personalize';
|
|
28
|
+
export interface ModuleQueryConfig {
|
|
29
|
+
supportedFields: string[];
|
|
30
|
+
supportedOperators: string[];
|
|
31
|
+
defaultLimit: number;
|
|
32
|
+
includeGlobalFieldSchema?: boolean;
|
|
33
|
+
includePublishDetails?: boolean;
|
|
34
|
+
includeDimension?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface DependencyAnalysisConfig {
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
fields?: string[];
|
|
39
|
+
extractors?: string[];
|
|
40
|
+
}
|
|
41
|
+
export interface ModuleDefinition {
|
|
42
|
+
dirName: string;
|
|
43
|
+
fileName: string;
|
|
44
|
+
apiEndpoint: string;
|
|
45
|
+
queryable: boolean;
|
|
46
|
+
dependencies: Modules[];
|
|
47
|
+
queryConfig?: ModuleQueryConfig;
|
|
48
|
+
dependencyAnalysis?: DependencyAnalysisConfig;
|
|
49
|
+
limit?: number;
|
|
50
|
+
batchLimit?: number;
|
|
51
|
+
}
|
|
52
|
+
export interface DependencyExtractor {
|
|
53
|
+
fieldType: string;
|
|
54
|
+
extract: (data: any) => string[];
|
|
55
|
+
targetModule: Modules;
|
|
56
|
+
}
|
|
57
|
+
export interface ExportOptions {
|
|
58
|
+
query?: any;
|
|
59
|
+
alias?: string;
|
|
60
|
+
directory?: string;
|
|
61
|
+
branch?: string;
|
|
62
|
+
skipReferences?: boolean;
|
|
63
|
+
skipDependencies?: boolean;
|
|
64
|
+
securedAssets?: boolean;
|
|
65
|
+
includeGlobalFieldSchema?: boolean;
|
|
66
|
+
includePublishDetails?: boolean;
|
|
67
|
+
includeDimension?: boolean;
|
|
68
|
+
contentTypes?: string[];
|
|
69
|
+
uids?: string[];
|
|
70
|
+
configPath?: string;
|
|
71
|
+
fetchConcurrency?: number;
|
|
72
|
+
writeConcurrency?: number;
|
|
73
|
+
batchSize?: number;
|
|
74
|
+
[key: string]: any;
|
|
75
|
+
}
|
|
76
|
+
export interface DefaultConfig {
|
|
77
|
+
contentVersion: number;
|
|
78
|
+
host: string;
|
|
79
|
+
exportDir?: string;
|
|
80
|
+
stackApiKey?: string;
|
|
81
|
+
managementToken?: string;
|
|
82
|
+
region?: Region;
|
|
83
|
+
branchName?: string;
|
|
84
|
+
securedAssets?: boolean;
|
|
85
|
+
query?: string;
|
|
86
|
+
queryInput?: string;
|
|
87
|
+
skipReferences?: boolean;
|
|
88
|
+
skipDependencies?: boolean;
|
|
89
|
+
isQueryBasedExport?: boolean;
|
|
90
|
+
modules: {
|
|
91
|
+
general: Modules[];
|
|
92
|
+
queryable: Modules[];
|
|
93
|
+
dependent: Modules[];
|
|
94
|
+
content: Modules[];
|
|
95
|
+
exportOrder: Modules[];
|
|
96
|
+
definitions?: Record<Modules, ModuleDefinition>;
|
|
97
|
+
};
|
|
98
|
+
queryConfig: {
|
|
99
|
+
maxRecursionDepth?: number;
|
|
100
|
+
batchSize?: number;
|
|
101
|
+
metadataFileName?: string;
|
|
102
|
+
validation: {
|
|
103
|
+
maxQueryDepth?: number;
|
|
104
|
+
maxArraySize?: number;
|
|
105
|
+
allowedDateFormats?: string[];
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
dependencyExtractors?: Record<string, DependencyExtractor>;
|
|
109
|
+
fetchConcurrency: number;
|
|
110
|
+
writeConcurrency: number;
|
|
111
|
+
developerHubBaseUrl?: string;
|
|
112
|
+
branches?: Array<{
|
|
113
|
+
uid: string;
|
|
114
|
+
source: string;
|
|
115
|
+
}>;
|
|
116
|
+
branchEnabled?: boolean;
|
|
117
|
+
branchDir?: string;
|
|
118
|
+
apis: {
|
|
119
|
+
stacks: string;
|
|
120
|
+
locales: string;
|
|
121
|
+
environments: string;
|
|
122
|
+
content_types: string;
|
|
123
|
+
global_fields: string;
|
|
124
|
+
extensions: string;
|
|
125
|
+
taxonomies: string;
|
|
126
|
+
entries: string;
|
|
127
|
+
assets: string;
|
|
128
|
+
};
|
|
129
|
+
externalConfigPath?: string;
|
|
130
|
+
maxCTReferenceDepth: number;
|
|
131
|
+
}
|
|
132
|
+
export interface QueryExportConfig extends DefaultConfig {
|
|
133
|
+
query: string;
|
|
134
|
+
skipReferences: boolean;
|
|
135
|
+
skipDependencies: boolean;
|
|
136
|
+
stackApiKey: string;
|
|
137
|
+
managementToken?: string;
|
|
138
|
+
branchName: string;
|
|
139
|
+
securedAssets: boolean;
|
|
140
|
+
logsPath: string;
|
|
141
|
+
dataPath: string;
|
|
142
|
+
exportDelayMs?: number;
|
|
143
|
+
}
|
|
144
|
+
export interface QueryMetadata {
|
|
145
|
+
query: any;
|
|
146
|
+
flags: {
|
|
147
|
+
skipReferences: boolean;
|
|
148
|
+
skipDependencies: boolean;
|
|
149
|
+
};
|
|
150
|
+
timestamp: string;
|
|
151
|
+
cliVersion: string;
|
|
152
|
+
exportedModules: string[];
|
|
153
|
+
contentTypes: Array<{
|
|
154
|
+
uid: string;
|
|
155
|
+
title: string;
|
|
156
|
+
}>;
|
|
157
|
+
summary: {
|
|
158
|
+
totalContentTypes: number;
|
|
159
|
+
totalModules: number;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const askAPIKey: () => Promise<string>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.askAPIKey = void 0;
|
|
4
|
+
const cli_utilities_1 = require("@contentstack/cli-utilities");
|
|
5
|
+
const askAPIKey = async () => {
|
|
6
|
+
return await cli_utilities_1.cliux.inquire({
|
|
7
|
+
type: 'input',
|
|
8
|
+
message: 'Enter the stack api key',
|
|
9
|
+
name: 'apiKey',
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
exports.askAPIKey = askAPIKey;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setupQueryExportConfig = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const path = tslib_1.__importStar(require("path"));
|
|
6
|
+
const cli_utilities_1 = require("@contentstack/cli-utilities");
|
|
7
|
+
const config_1 = tslib_1.__importDefault(require("../config"));
|
|
8
|
+
const common_helper_1 = require("./common-helper");
|
|
9
|
+
async function setupQueryExportConfig(flags) {
|
|
10
|
+
var _a;
|
|
11
|
+
const exportDir = (0, cli_utilities_1.sanitizePath)(flags['data-dir'] || (0, cli_utilities_1.pathValidator)('export'));
|
|
12
|
+
const exportQueryConfig = Object.assign(Object.assign({}, config_1.default), { exportDir, stackApiKey: flags['stack-api-key'] || '', managementToken: flags.alias ? (_a = cli_utilities_1.configHandler.get(`tokens.${flags.alias}`)) === null || _a === void 0 ? void 0 : _a.token : undefined, query: flags.query, skipReferences: flags['skip-references'] || false, skipDependencies: flags['skip-dependencies'] || false, branchName: flags.branch, securedAssets: flags['secured-assets'] || false, isQueryBasedExport: true, logsPath: exportDir, dataPath: exportDir,
|
|
13
|
+
// Todo: accept the path of the config file from the user
|
|
14
|
+
externalConfigPath: path.join(__dirname, '../config/export-config.json') });
|
|
15
|
+
// Handle authentication
|
|
16
|
+
if (flags.alias) {
|
|
17
|
+
const { token, apiKey } = cli_utilities_1.configHandler.get(`tokens.${flags.alias}`) || {};
|
|
18
|
+
config_1.default.managementToken = token;
|
|
19
|
+
config_1.default.stackApiKey = apiKey;
|
|
20
|
+
if (!config_1.default.managementToken) {
|
|
21
|
+
throw new Error(`No management token found on given alias ${flags.alias}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (!config_1.default.managementToken) {
|
|
25
|
+
if (!(0, cli_utilities_1.isAuthenticated)()) {
|
|
26
|
+
throw new Error('Please login or provide an alias for the management token');
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
config_1.default.stackApiKey = flags['stack-api-key'] || (await (0, common_helper_1.askAPIKey)());
|
|
30
|
+
if (typeof config_1.default.stackApiKey !== 'string') {
|
|
31
|
+
throw new Error('Invalid API key received');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return exportQueryConfig;
|
|
36
|
+
}
|
|
37
|
+
exports.setupQueryExportConfig = setupQueryExportConfig;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { QueryExportConfig } from '../types';
|
|
2
|
+
export declare class ReferencedContentTypesHandler {
|
|
3
|
+
private exportQueryConfig;
|
|
4
|
+
constructor(exportQueryConfig: QueryExportConfig);
|
|
5
|
+
/**
|
|
6
|
+
* Extract referenced content types from a batch of content types
|
|
7
|
+
* This method only processes the given batch, doesn't orchestrate the entire process
|
|
8
|
+
*/
|
|
9
|
+
extractReferencedContentTypes(contentTypeBatch: any[]): Promise<string[]>;
|
|
10
|
+
/**
|
|
11
|
+
* Filter content types to get only newly fetched ones based on UIDs
|
|
12
|
+
*/
|
|
13
|
+
filterNewlyFetchedContentTypes(allContentTypes: any[], previousUIDs: Set<string>): any[];
|
|
14
|
+
/**
|
|
15
|
+
* Extract referenced content types from a content type schema
|
|
16
|
+
* Moved from content-type-helper.ts for better encapsulation
|
|
17
|
+
*/
|
|
18
|
+
private getReferencedContentTypes;
|
|
19
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReferencedContentTypesHandler = void 0;
|
|
4
|
+
const logger_1 = require("./logger");
|
|
5
|
+
class ReferencedContentTypesHandler {
|
|
6
|
+
constructor(exportQueryConfig) {
|
|
7
|
+
this.exportQueryConfig = exportQueryConfig;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Extract referenced content types from a batch of content types
|
|
11
|
+
* This method only processes the given batch, doesn't orchestrate the entire process
|
|
12
|
+
*/
|
|
13
|
+
async extractReferencedContentTypes(contentTypeBatch) {
|
|
14
|
+
const allReferencedTypes = new Set();
|
|
15
|
+
(0, logger_1.log)(this.exportQueryConfig, `Extracting references from ${contentTypeBatch.length} content types`, 'info');
|
|
16
|
+
for (const contentType of contentTypeBatch) {
|
|
17
|
+
if (contentType.schema) {
|
|
18
|
+
const referencedTypes = this.getReferencedContentTypes(contentType.schema);
|
|
19
|
+
referencedTypes.forEach((type) => allReferencedTypes.add(type));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const result = Array.from(allReferencedTypes);
|
|
23
|
+
(0, logger_1.log)(this.exportQueryConfig, `Found ${result.length} referenced content types`, 'info');
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Filter content types to get only newly fetched ones based on UIDs
|
|
28
|
+
*/
|
|
29
|
+
filterNewlyFetchedContentTypes(allContentTypes, previousUIDs) {
|
|
30
|
+
return allContentTypes.filter((ct) => !previousUIDs.has(ct.uid));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Extract referenced content types from a content type schema
|
|
34
|
+
* Moved from content-type-helper.ts for better encapsulation
|
|
35
|
+
*/
|
|
36
|
+
getReferencedContentTypes(schema) {
|
|
37
|
+
const referencedTypes = new Set();
|
|
38
|
+
const traverseSchema = (schemaArray) => {
|
|
39
|
+
var _a, _b, _c, _d;
|
|
40
|
+
for (const field of schemaArray) {
|
|
41
|
+
if (field.data_type === 'group' || field.data_type === 'global_field') {
|
|
42
|
+
// Recursively traverse group and global field schemas
|
|
43
|
+
traverseSchema(field.schema);
|
|
44
|
+
}
|
|
45
|
+
else if (field.data_type === 'blocks') {
|
|
46
|
+
// Traverse each block's schema
|
|
47
|
+
for (const blockKey in field.blocks) {
|
|
48
|
+
traverseSchema(field.blocks[blockKey].schema);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else if (field.data_type === 'reference' && field.reference_to) {
|
|
52
|
+
// Add reference field targets
|
|
53
|
+
field.reference_to.forEach((ref) => {
|
|
54
|
+
if (ref !== 'sys_assets') {
|
|
55
|
+
// Exclude system assets
|
|
56
|
+
referencedTypes.add(ref);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
else if (
|
|
61
|
+
// Handle JSON RTE with embedded entries
|
|
62
|
+
field.data_type === 'json' &&
|
|
63
|
+
((_a = field.field_metadata) === null || _a === void 0 ? void 0 : _a.rich_text_type) &&
|
|
64
|
+
((_b = field.field_metadata) === null || _b === void 0 ? void 0 : _b.embed_entry) &&
|
|
65
|
+
field.reference_to) {
|
|
66
|
+
field.reference_to.forEach((ref) => {
|
|
67
|
+
if (ref !== 'sys_assets') {
|
|
68
|
+
referencedTypes.add(ref);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
else if (
|
|
73
|
+
// Handle Text RTE with embedded entries
|
|
74
|
+
field.data_type === 'text' &&
|
|
75
|
+
((_c = field.field_metadata) === null || _c === void 0 ? void 0 : _c.rich_text_type) &&
|
|
76
|
+
((_d = field.field_metadata) === null || _d === void 0 ? void 0 : _d.embed_entry) &&
|
|
77
|
+
field.reference_to) {
|
|
78
|
+
field.reference_to.forEach((ref) => {
|
|
79
|
+
if (ref !== 'sys_assets') {
|
|
80
|
+
referencedTypes.add(ref);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
traverseSchema(schema);
|
|
87
|
+
return Array.from(referencedTypes);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.ReferencedContentTypesHandler = ReferencedContentTypesHandler;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { QueryExportConfig } from '../types';
|
|
2
|
+
export declare class ContentTypeDependenciesHandler {
|
|
3
|
+
private exportQueryConfig;
|
|
4
|
+
private stackAPIClient;
|
|
5
|
+
constructor(stackAPIClient: any, exportQueryConfig: QueryExportConfig);
|
|
6
|
+
extractDependencies(): Promise<{
|
|
7
|
+
globalFields: Set<string>;
|
|
8
|
+
extensions: Set<string>;
|
|
9
|
+
taxonomies: Set<string>;
|
|
10
|
+
marketplaceApps: Set<string>;
|
|
11
|
+
}>;
|
|
12
|
+
fetchExtensionsAndMarketplaceApps(extensionUIDs: string[]): Promise<{
|
|
13
|
+
extensions: string[];
|
|
14
|
+
marketplaceApps: string[];
|
|
15
|
+
}>;
|
|
16
|
+
private traverseSchemaForDependencies;
|
|
17
|
+
}
|