@kontent-ai/migration-toolkit 1.0.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.
Files changed (240) hide show
  1. package/.npmignore +13 -0
  2. package/LICENSE.md +9 -0
  3. package/README.md +272 -0
  4. package/dist/es2022/core/helpers/workflow-helper.d.ts +37 -0
  5. package/dist/es2022/core/helpers/workflow-helper.js +67 -0
  6. package/dist/es2022/core/helpers/workflow-helper.js.map +1 -0
  7. package/dist/es2022/core/index.d.ts +15 -0
  8. package/dist/es2022/core/index.js +16 -0
  9. package/dist/es2022/core/index.js.map +1 -0
  10. package/dist/es2022/core/logs/loggers.d.ts +2 -0
  11. package/dist/es2022/core/logs/loggers.js +69 -0
  12. package/dist/es2022/core/logs/loggers.js.map +1 -0
  13. package/dist/es2022/core/models/core.models.d.ts +91 -0
  14. package/dist/es2022/core/models/core.models.js +2 -0
  15. package/dist/es2022/core/models/core.models.js.map +1 -0
  16. package/dist/es2022/core/models/log.models.d.ts +15 -0
  17. package/dist/es2022/core/models/log.models.js +2 -0
  18. package/dist/es2022/core/models/log.models.js.map +1 -0
  19. package/dist/es2022/core/models/migration.models.d.ts +136 -0
  20. package/dist/es2022/core/models/migration.models.js +2 -0
  21. package/dist/es2022/core/models/migration.models.js.map +1 -0
  22. package/dist/es2022/core/utils/array.utils.d.ts +5 -0
  23. package/dist/es2022/core/utils/array.utils.js +27 -0
  24. package/dist/es2022/core/utils/array.utils.js.map +1 -0
  25. package/dist/es2022/core/utils/binary-data.utils.d.ts +5 -0
  26. package/dist/es2022/core/utils/binary-data.utils.js +21 -0
  27. package/dist/es2022/core/utils/binary-data.utils.js.map +1 -0
  28. package/dist/es2022/core/utils/confirm.utils.d.ts +31 -0
  29. package/dist/es2022/core/utils/confirm.utils.js +73 -0
  30. package/dist/es2022/core/utils/confirm.utils.js.map +1 -0
  31. package/dist/es2022/core/utils/error.utils.d.ts +4 -0
  32. package/dist/es2022/core/utils/error.utils.js +50 -0
  33. package/dist/es2022/core/utils/error.utils.js.map +1 -0
  34. package/dist/es2022/core/utils/external-id.utils.d.ts +5 -0
  35. package/dist/es2022/core/utils/external-id.utils.js +5 -0
  36. package/dist/es2022/core/utils/external-id.utils.js.map +1 -0
  37. package/dist/es2022/core/utils/global.utils.d.ts +14 -0
  38. package/dist/es2022/core/utils/global.utils.js +45 -0
  39. package/dist/es2022/core/utils/global.utils.js.map +1 -0
  40. package/dist/es2022/core/utils/http.utils.d.ts +3 -0
  41. package/dist/es2022/core/utils/http.utils.js +29 -0
  42. package/dist/es2022/core/utils/http.utils.js.map +1 -0
  43. package/dist/es2022/core/utils/management-client-utils.d.ts +19 -0
  44. package/dist/es2022/core/utils/management-client-utils.js +129 -0
  45. package/dist/es2022/core/utils/management-client-utils.js.map +1 -0
  46. package/dist/es2022/core/utils/processing-utils.d.ts +12 -0
  47. package/dist/es2022/core/utils/processing-utils.js +39 -0
  48. package/dist/es2022/core/utils/processing-utils.js.map +1 -0
  49. package/dist/es2022/core/utils/run.utils.d.ts +10 -0
  50. package/dist/es2022/core/utils/run.utils.js +37 -0
  51. package/dist/es2022/core/utils/run.utils.js.map +1 -0
  52. package/dist/es2022/export/context/export-context-fetcher.d.ts +4 -0
  53. package/dist/es2022/export/context/export-context-fetcher.js +289 -0
  54. package/dist/es2022/export/context/export-context-fetcher.js.map +1 -0
  55. package/dist/es2022/export/export-manager.d.ts +5 -0
  56. package/dist/es2022/export/export-manager.js +167 -0
  57. package/dist/es2022/export/export-manager.js.map +1 -0
  58. package/dist/es2022/export/export.models.d.ts +55 -0
  59. package/dist/es2022/export/export.models.js +2 -0
  60. package/dist/es2022/export/export.models.js.map +1 -0
  61. package/dist/es2022/export/index.d.ts +2 -0
  62. package/dist/es2022/export/index.js +3 -0
  63. package/dist/es2022/export/index.js.map +1 -0
  64. package/dist/es2022/export/utils/export.utils.d.ts +2 -0
  65. package/dist/es2022/export/utils/export.utils.js +5 -0
  66. package/dist/es2022/export/utils/export.utils.js.map +1 -0
  67. package/dist/es2022/file/file-manager.d.ts +5 -0
  68. package/dist/es2022/file/file-manager.js +29 -0
  69. package/dist/es2022/file/file-manager.js.map +1 -0
  70. package/dist/es2022/file/index.d.ts +1 -0
  71. package/dist/es2022/file/index.js +2 -0
  72. package/dist/es2022/file/index.js.map +1 -0
  73. package/dist/es2022/import/comparers/asset-comparer.d.ts +12 -0
  74. package/dist/es2022/import/comparers/asset-comparer.js +65 -0
  75. package/dist/es2022/import/comparers/asset-comparer.js.map +1 -0
  76. package/dist/es2022/import/context/import-context-fetcher.d.ts +4 -0
  77. package/dist/es2022/import/context/import-context-fetcher.js +235 -0
  78. package/dist/es2022/import/context/import-context-fetcher.js.map +1 -0
  79. package/dist/es2022/import/import-manager.d.ts +4 -0
  80. package/dist/es2022/import/import-manager.js +80 -0
  81. package/dist/es2022/import/import-manager.js.map +1 -0
  82. package/dist/es2022/import/import.models.d.ts +45 -0
  83. package/dist/es2022/import/import.models.js +2 -0
  84. package/dist/es2022/import/import.models.js.map +1 -0
  85. package/dist/es2022/import/importers/assets-importer.d.ts +10 -0
  86. package/dist/es2022/import/importers/assets-importer.js +175 -0
  87. package/dist/es2022/import/importers/assets-importer.js.map +1 -0
  88. package/dist/es2022/import/importers/content-items-importer.d.ts +10 -0
  89. package/dist/es2022/import/importers/content-items-importer.js +97 -0
  90. package/dist/es2022/import/importers/content-items-importer.js.map +1 -0
  91. package/dist/es2022/import/importers/language-variant-importer.d.ts +11 -0
  92. package/dist/es2022/import/importers/language-variant-importer.js +215 -0
  93. package/dist/es2022/import/importers/language-variant-importer.js.map +1 -0
  94. package/dist/es2022/import/importers/workflow-importer.d.ts +18 -0
  95. package/dist/es2022/import/importers/workflow-importer.js +122 -0
  96. package/dist/es2022/import/importers/workflow-importer.js.map +1 -0
  97. package/dist/es2022/import/index.d.ts +2 -0
  98. package/dist/es2022/import/index.js +3 -0
  99. package/dist/es2022/import/index.js.map +1 -0
  100. package/dist/es2022/import/utils/import.utils.d.ts +2 -0
  101. package/dist/es2022/import/utils/import.utils.js +5 -0
  102. package/dist/es2022/import/utils/import.utils.js.map +1 -0
  103. package/dist/es2022/index.d.ts +8 -0
  104. package/dist/es2022/index.js +10 -0
  105. package/dist/es2022/index.js.map +1 -0
  106. package/dist/es2022/metadata.d.ts +5 -0
  107. package/dist/es2022/metadata.js +6 -0
  108. package/dist/es2022/metadata.js.map +1 -0
  109. package/dist/es2022/node/cli/actions/export-action.d.ts +2 -0
  110. package/dist/es2022/node/cli/actions/export-action.js +40 -0
  111. package/dist/es2022/node/cli/actions/export-action.js.map +1 -0
  112. package/dist/es2022/node/cli/actions/import-action.d.ts +2 -0
  113. package/dist/es2022/node/cli/actions/import-action.js +29 -0
  114. package/dist/es2022/node/cli/actions/import-action.js.map +1 -0
  115. package/dist/es2022/node/cli/actions/migrate-action.d.ts +2 -0
  116. package/dist/es2022/node/cli/actions/migrate-action.js +47 -0
  117. package/dist/es2022/node/cli/actions/migrate-action.js.map +1 -0
  118. package/dist/es2022/node/cli/app.d.ts +2 -0
  119. package/dist/es2022/node/cli/app.js +27 -0
  120. package/dist/es2022/node/cli/app.js.map +1 -0
  121. package/dist/es2022/node/cli/args/args-fetcher.d.ts +2 -0
  122. package/dist/es2022/node/cli/args/args-fetcher.js +46 -0
  123. package/dist/es2022/node/cli/args/args-fetcher.js.map +1 -0
  124. package/dist/es2022/node/cli/args/args-setter.d.ts +2 -0
  125. package/dist/es2022/node/cli/args/args-setter.js +31 -0
  126. package/dist/es2022/node/cli/args/args-setter.js.map +1 -0
  127. package/dist/es2022/node/cli/cli.models.d.ts +24 -0
  128. package/dist/es2022/node/cli/cli.models.js +2 -0
  129. package/dist/es2022/node/cli/cli.models.js.map +1 -0
  130. package/dist/es2022/node/cli/commands.d.ts +1 -0
  131. package/dist/es2022/node/cli/commands.js +157 -0
  132. package/dist/es2022/node/cli/commands.js.map +1 -0
  133. package/dist/es2022/toolkit/elements-builder.d.ts +14 -0
  134. package/dist/es2022/toolkit/elements-builder.js +69 -0
  135. package/dist/es2022/toolkit/elements-builder.js.map +1 -0
  136. package/dist/es2022/toolkit/export.d.ts +3 -0
  137. package/dist/es2022/toolkit/export.js +21 -0
  138. package/dist/es2022/toolkit/export.js.map +1 -0
  139. package/dist/es2022/toolkit/file.d.ts +12 -0
  140. package/dist/es2022/toolkit/file.js +50 -0
  141. package/dist/es2022/toolkit/file.js.map +1 -0
  142. package/dist/es2022/toolkit/import.d.ts +2 -0
  143. package/dist/es2022/toolkit/import.js +21 -0
  144. package/dist/es2022/toolkit/import.js.map +1 -0
  145. package/dist/es2022/toolkit/index.d.ts +5 -0
  146. package/dist/es2022/toolkit/index.js +6 -0
  147. package/dist/es2022/toolkit/index.js.map +1 -0
  148. package/dist/es2022/toolkit/migrate.d.ts +21 -0
  149. package/dist/es2022/toolkit/migrate.js +39 -0
  150. package/dist/es2022/toolkit/migrate.js.map +1 -0
  151. package/dist/es2022/translation/extraction/items-extraction.processor.d.ts +24 -0
  152. package/dist/es2022/translation/extraction/items-extraction.processor.js +108 -0
  153. package/dist/es2022/translation/extraction/items-extraction.processor.js.map +1 -0
  154. package/dist/es2022/translation/helpers/rich-text.processor.d.ts +22 -0
  155. package/dist/es2022/translation/helpers/rich-text.processor.js +209 -0
  156. package/dist/es2022/translation/helpers/rich-text.processor.js.map +1 -0
  157. package/dist/es2022/translation/index.d.ts +4 -0
  158. package/dist/es2022/translation/index.js +5 -0
  159. package/dist/es2022/translation/index.js.map +1 -0
  160. package/dist/es2022/translation/transforms/export-transforms.d.ts +3 -0
  161. package/dist/es2022/translation/transforms/export-transforms.js +196 -0
  162. package/dist/es2022/translation/transforms/export-transforms.js.map +1 -0
  163. package/dist/es2022/translation/transforms/import-transforms.d.ts +7 -0
  164. package/dist/es2022/translation/transforms/import-transforms.js +183 -0
  165. package/dist/es2022/translation/transforms/import-transforms.js.map +1 -0
  166. package/dist/es2022/zip/index.d.ts +4 -0
  167. package/dist/es2022/zip/index.js +5 -0
  168. package/dist/es2022/zip/index.js.map +1 -0
  169. package/dist/es2022/zip/zip-manager.d.ts +6 -0
  170. package/dist/es2022/zip/zip-manager.js +27 -0
  171. package/dist/es2022/zip/zip-manager.js.map +1 -0
  172. package/dist/es2022/zip/zip-packager.d.ts +3 -0
  173. package/dist/es2022/zip/zip-packager.js +69 -0
  174. package/dist/es2022/zip/zip-packager.js.map +1 -0
  175. package/dist/es2022/zip/zip-transformer.d.ts +6 -0
  176. package/dist/es2022/zip/zip-transformer.js +82 -0
  177. package/dist/es2022/zip/zip-transformer.js.map +1 -0
  178. package/dist/es2022/zip/zip.models.d.ts +13 -0
  179. package/dist/es2022/zip/zip.models.js +2 -0
  180. package/dist/es2022/zip/zip.models.js.map +1 -0
  181. package/lib/core/helpers/workflow-helper.ts +104 -0
  182. package/lib/core/index.ts +17 -0
  183. package/lib/core/logs/loggers.ts +80 -0
  184. package/lib/core/models/core.models.ts +134 -0
  185. package/lib/core/models/log.models.ts +31 -0
  186. package/lib/core/models/migration.models.ts +165 -0
  187. package/lib/core/utils/array.utils.ts +49 -0
  188. package/lib/core/utils/binary-data.utils.ts +29 -0
  189. package/lib/core/utils/confirm.utils.ts +139 -0
  190. package/lib/core/utils/error.utils.ts +65 -0
  191. package/lib/core/utils/external-id.utils.ts +9 -0
  192. package/lib/core/utils/global.utils.ts +59 -0
  193. package/lib/core/utils/http.utils.ts +35 -0
  194. package/lib/core/utils/management-client-utils.ts +182 -0
  195. package/lib/core/utils/processing-utils.ts +71 -0
  196. package/lib/core/utils/run.utils.ts +51 -0
  197. package/lib/export/context/export-context-fetcher.ts +475 -0
  198. package/lib/export/export-manager.ts +249 -0
  199. package/lib/export/export.models.ts +87 -0
  200. package/lib/export/index.ts +2 -0
  201. package/lib/export/utils/export.utils.ts +10 -0
  202. package/lib/file/file-manager.ts +37 -0
  203. package/lib/file/index.ts +1 -0
  204. package/lib/import/comparers/asset-comparer.ts +113 -0
  205. package/lib/import/context/import-context-fetcher.ts +367 -0
  206. package/lib/import/import-manager.ts +103 -0
  207. package/lib/import/import.models.ts +84 -0
  208. package/lib/import/importers/assets-importer.ts +239 -0
  209. package/lib/import/importers/content-items-importer.ts +142 -0
  210. package/lib/import/importers/language-variant-importer.ts +342 -0
  211. package/lib/import/importers/workflow-importer.ts +159 -0
  212. package/lib/import/index.ts +2 -0
  213. package/lib/import/utils/import.utils.ts +10 -0
  214. package/lib/index.ts +9 -0
  215. package/lib/metadata.ts +6 -0
  216. package/lib/node/cli/actions/export-action.ts +45 -0
  217. package/lib/node/cli/actions/import-action.ts +34 -0
  218. package/lib/node/cli/actions/migrate-action.ts +52 -0
  219. package/lib/node/cli/app.ts +29 -0
  220. package/lib/node/cli/args/args-fetcher.ts +61 -0
  221. package/lib/node/cli/args/args-setter.ts +35 -0
  222. package/lib/node/cli/cli.models.ts +28 -0
  223. package/lib/node/cli/commands.ts +161 -0
  224. package/lib/toolkit/elements-builder.ts +84 -0
  225. package/lib/toolkit/export.ts +21 -0
  226. package/lib/toolkit/file.ts +70 -0
  227. package/lib/toolkit/import.ts +21 -0
  228. package/lib/toolkit/index.ts +5 -0
  229. package/lib/toolkit/migrate.ts +71 -0
  230. package/lib/translation/extraction/items-extraction.processor.ts +181 -0
  231. package/lib/translation/helpers/rich-text.processor.ts +300 -0
  232. package/lib/translation/index.ts +4 -0
  233. package/lib/translation/transforms/export-transforms.ts +253 -0
  234. package/lib/translation/transforms/import-transforms.ts +215 -0
  235. package/lib/zip/index.ts +4 -0
  236. package/lib/zip/zip-manager.ts +33 -0
  237. package/lib/zip/zip-packager.ts +86 -0
  238. package/lib/zip/zip-transformer.ts +105 -0
  239. package/lib/zip/zip.models.ts +12 -0
  240. package/package.json +88 -0
@@ -0,0 +1,475 @@
1
+ import {
2
+ WorkflowModels,
3
+ LanguageVariantModels,
4
+ ContentItemModels,
5
+ AssetModels,
6
+ CollectionModels,
7
+ LanguageModels
8
+ } from '@kontent-ai/management-sdk';
9
+ import chalk from 'chalk';
10
+ import {
11
+ processItemsAsync,
12
+ runMapiRequestAsync,
13
+ is404Error,
14
+ ItemStateInSourceEnvironmentById,
15
+ AssetStateInSourceEnvironmentById,
16
+ FlattenedContentType,
17
+ isNotUndefined,
18
+ managementClientUtils,
19
+ LogSpinnerData,
20
+ findRequired,
21
+ workflowHelper
22
+ } from '../../core/index.js';
23
+ import { itemsExtractionProcessor } from '../../translation/index.js';
24
+ import {
25
+ DefaultExportContextConfig,
26
+ ExportContext,
27
+ ExportContextEnvironmentData,
28
+ SourceExportItem,
29
+ ExportItem,
30
+ GetFlattenedElementByIds,
31
+ ExportItemVersion
32
+ } from '../export.models.js';
33
+ import { throwErrorForItemRequest } from '../utils/export.utils.js';
34
+
35
+ export async function exportContextFetcherAsync(config: DefaultExportContextConfig) {
36
+ const getEnvironmentDataAsync = async (): Promise<ExportContextEnvironmentData> => {
37
+ const mapiUtils = managementClientUtils(config.managementClient, config.logger);
38
+
39
+ return await config.logger.logWithSpinnerAsync(async (spinnerData) => {
40
+ spinnerData({ type: 'info', message: `Loading environment data` });
41
+
42
+ const environmentData: ExportContextEnvironmentData = {
43
+ collections: await mapiUtils.getAllCollectionsAsync(spinnerData),
44
+ contentTypes: await mapiUtils.getFlattenedContentTypesAsync(spinnerData),
45
+ languages: await mapiUtils.getAllLanguagesAsync(spinnerData),
46
+ workflows: await mapiUtils.getAllWorkflowsAsync(spinnerData),
47
+ taxonomies: await mapiUtils.getAllTaxonomiesAsync(spinnerData)
48
+ };
49
+
50
+ spinnerData({ type: 'info', message: `Environmental data loaded` });
51
+
52
+ return environmentData;
53
+ });
54
+ };
55
+
56
+ const environmentData = await getEnvironmentDataAsync();
57
+
58
+ const getContentItemAsync = async (
59
+ sourceItem: SourceExportItem,
60
+ logSpinner: LogSpinnerData
61
+ ): Promise<Readonly<ContentItemModels.ContentItem>> => {
62
+ return await runMapiRequestAsync({
63
+ logger: config.logger,
64
+ logSpinner: logSpinner,
65
+ func: async () =>
66
+ (
67
+ await config.managementClient.viewContentItem().byItemCodename(sourceItem.itemCodename).toPromise()
68
+ ).data,
69
+ action: 'view',
70
+ type: 'contentItem',
71
+ itemName: `codename -> ${sourceItem.itemCodename}`
72
+ });
73
+ };
74
+
75
+ const getLatestLanguageVariantAsync = async (
76
+ sourceItem: SourceExportItem,
77
+ logSpinner: LogSpinnerData
78
+ ): Promise<Readonly<LanguageVariantModels.ContentItemLanguageVariant>> => {
79
+ return await runMapiRequestAsync({
80
+ logger: config.logger,
81
+ logSpinner: logSpinner,
82
+ func: async () =>
83
+ (
84
+ await config.managementClient
85
+ .viewLanguageVariant()
86
+ .byItemCodename(sourceItem.itemCodename)
87
+ .byLanguageCodename(sourceItem.languageCodename)
88
+ .toPromise()
89
+ ).data,
90
+ action: 'view',
91
+ type: 'languageVariant',
92
+ itemName: `codename -> ${sourceItem.itemCodename} -> latest (${sourceItem.languageCodename})`
93
+ });
94
+ };
95
+
96
+ const isLanguageVariantPublished = (
97
+ languageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant>
98
+ ): boolean => {
99
+ return environmentData.workflows.find(
100
+ (workflow) => workflow.publishedStep.id === languageVariant.workflow.stepIdentifier.id
101
+ )
102
+ ? true
103
+ : false;
104
+ };
105
+
106
+ const mapToExportVersionItem = (
107
+ sourceItem: SourceExportItem,
108
+ contentItem: Readonly<ContentItemModels.ContentItem>,
109
+ languageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant>
110
+ ): ExportItemVersion => {
111
+ return {
112
+ languageVariant: languageVariant,
113
+ workflowStepCodename: validateExportItem({
114
+ sourceItem: sourceItem,
115
+ contentItem: contentItem,
116
+ languageVariant: languageVariant
117
+ }).workflowStepCodename
118
+ };
119
+ };
120
+
121
+ const getExportItemVersionsAsync = async (
122
+ sourceItem: SourceExportItem,
123
+ contentItem: Readonly<ContentItemModels.ContentItem>,
124
+ logSpinner: LogSpinnerData
125
+ ): Promise<readonly ExportItemVersion[]> => {
126
+ const latestLanguageVariant = await getLatestLanguageVariantAsync(sourceItem, logSpinner);
127
+ const latestExportVersion = mapToExportVersionItem(sourceItem, contentItem, latestLanguageVariant);
128
+
129
+ if (isLanguageVariantPublished(latestLanguageVariant)) {
130
+ // latest language variant is also published = no need to fetch published version
131
+ return [latestExportVersion];
132
+ }
133
+
134
+ const publishedLanguageVariant = await getPublishedLanguageVariantAsync(sourceItem, logSpinner);
135
+
136
+ if (!publishedLanguageVariant) {
137
+ return [latestExportVersion];
138
+ }
139
+
140
+ return [latestExportVersion, mapToExportVersionItem(sourceItem, contentItem, publishedLanguageVariant)];
141
+ };
142
+
143
+ const getPublishedLanguageVariantAsync = async (
144
+ sourceItem: SourceExportItem,
145
+ logSpinner: LogSpinnerData
146
+ ): Promise<Readonly<LanguageVariantModels.ContentItemLanguageVariant> | undefined> => {
147
+ return await runMapiRequestAsync({
148
+ logger: config.logger,
149
+ logSpinner: logSpinner,
150
+ func: async () => {
151
+ try {
152
+ return (
153
+ await config.managementClient
154
+ .viewLanguageVariant()
155
+ .byItemCodename(sourceItem.itemCodename)
156
+ .byLanguageCodename(sourceItem.languageCodename)
157
+ .published()
158
+ .toPromise()
159
+ ).data;
160
+ } catch (error) {
161
+ if (is404Error(error)) {
162
+ return undefined;
163
+ }
164
+ throw error;
165
+ }
166
+ },
167
+ action: 'view',
168
+ type: 'languageVariant',
169
+ itemName: `codename -> ${sourceItem.itemCodename} -> published (${sourceItem.languageCodename})`
170
+ });
171
+ };
172
+
173
+ const validateExportItem = (data: {
174
+ readonly sourceItem: SourceExportItem;
175
+ readonly contentItem: Readonly<ContentItemModels.ContentItem>;
176
+ readonly languageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant>;
177
+ }): {
178
+ collection: Readonly<CollectionModels.Collection>;
179
+ language: Readonly<LanguageModels.LanguageModel>;
180
+ workflow: Readonly<WorkflowModels.Workflow>;
181
+ contentType: Readonly<FlattenedContentType>;
182
+ workflowStepCodename: string;
183
+ } => {
184
+ const collection = findRequired(
185
+ environmentData.collections,
186
+ (collection) => collection.id === data.contentItem.collection.id,
187
+ () => {
188
+ throwErrorForItemRequest(
189
+ data.sourceItem,
190
+ `Invalid collection '${chalk.yellow(data.contentItem.collection.id ?? '')}'`
191
+ );
192
+ }
193
+ );
194
+
195
+ const contentType = findRequired(
196
+ environmentData.contentTypes,
197
+ (contentType) => contentType.contentTypeId === data.contentItem.type.id,
198
+ () => {
199
+ throwErrorForItemRequest(
200
+ data.sourceItem,
201
+ `Invalid content type '${chalk.red(data.contentItem.type.id)}'`
202
+ );
203
+ }
204
+ );
205
+
206
+ const language = findRequired(
207
+ environmentData.languages,
208
+ (language) => language.id === data.languageVariant.language.id,
209
+ () => {
210
+ throwErrorForItemRequest(
211
+ data.sourceItem,
212
+ `Invalid language '${chalk.red(data.languageVariant.language.id ?? '')}'`
213
+ );
214
+ }
215
+ );
216
+
217
+ const workflow = findRequired(
218
+ environmentData.workflows,
219
+ (workflow) => workflow.id === data.languageVariant.workflow.workflowIdentifier.id,
220
+ () => {
221
+ throwErrorForItemRequest(
222
+ data.sourceItem,
223
+ `Invalid workflow '${chalk.red(data.languageVariant.workflow.workflowIdentifier.id ?? '')}'`
224
+ );
225
+ }
226
+ );
227
+
228
+ const workflowStep = workflowHelper(environmentData.workflows).getWorkflowStep(workflow, {
229
+ match: (step) => step.id === data.languageVariant.workflow.stepIdentifier.id,
230
+ errorMessage: `Invalid workflow step '${chalk.red(data.languageVariant.workflow.stepIdentifier.id ?? '')}'`
231
+ });
232
+
233
+ return {
234
+ collection,
235
+ language,
236
+ workflow,
237
+ contentType,
238
+ workflowStepCodename: workflowStep.codename
239
+ };
240
+ };
241
+
242
+ const prepareExportItemsAsync = async (
243
+ exportItems: readonly SourceExportItem[]
244
+ ): Promise<readonly ExportItem[]> => {
245
+ config.logger.log({
246
+ type: 'info',
247
+ message: `Preparing '${chalk.yellow(config.exportItems.length.toString())}' items for export`
248
+ });
249
+
250
+ return await processItemsAsync<SourceExportItem, ExportItem>({
251
+ logger: config.logger,
252
+ action: 'Preparing content items & language variants',
253
+ parallelLimit: 1,
254
+ itemInfo: (input) => {
255
+ return {
256
+ title: `${input.itemCodename} (${input.languageCodename})`,
257
+ itemType: 'exportedItem'
258
+ };
259
+ },
260
+ items: exportItems,
261
+ processAsync: async (requestItem, logSpinner) => {
262
+ const contentItem = await getContentItemAsync(requestItem, logSpinner);
263
+ const versions = await getExportItemVersionsAsync(requestItem, contentItem, logSpinner);
264
+
265
+ // get shared attributes from any version
266
+ const anyVersion = versions[0];
267
+ if (!anyVersion) {
268
+ throwErrorForItemRequest(requestItem, `Expected at least 1 version of the content item`);
269
+ }
270
+
271
+ const { collection, contentType, language, workflow } = validateExportItem({
272
+ sourceItem: requestItem,
273
+ contentItem: contentItem,
274
+ languageVariant: anyVersion.languageVariant
275
+ });
276
+
277
+ return {
278
+ contentItem,
279
+ versions,
280
+ contentType,
281
+ requestItem,
282
+ workflow,
283
+ collection,
284
+ language
285
+ };
286
+ }
287
+ });
288
+ };
289
+
290
+ const getContentItemsByIdsAsync = async (
291
+ itemIds: ReadonlySet<string>
292
+ ): Promise<readonly Readonly<ContentItemModels.ContentItem>[]> => {
293
+ return (
294
+ await processItemsAsync<string, Readonly<ContentItemModels.ContentItem> | undefined>({
295
+ logger: config.logger,
296
+ action: 'Fetching content items',
297
+ parallelLimit: 1,
298
+ items: Array.from(itemIds),
299
+ itemInfo: (id) => {
300
+ return {
301
+ itemType: 'contentItem',
302
+ title: id
303
+ };
304
+ },
305
+ processAsync: async (id, logSpinner) => {
306
+ try {
307
+ return await runMapiRequestAsync({
308
+ logSpinner: logSpinner,
309
+ logger: config.logger,
310
+ func: async () =>
311
+ (
312
+ await config.managementClient.viewContentItem().byItemId(id).toPromise()
313
+ ).data,
314
+ action: 'view',
315
+ type: 'contentItem',
316
+ itemName: `id -> ${id}`
317
+ });
318
+ } catch (error) {
319
+ if (!is404Error(error)) {
320
+ throw error;
321
+ }
322
+
323
+ return undefined;
324
+ }
325
+ }
326
+ })
327
+ ).filter(isNotUndefined);
328
+ };
329
+
330
+ const getAssetsByIdsAsync = async (
331
+ itemIds: ReadonlySet<string>
332
+ ): Promise<readonly Readonly<AssetModels.Asset>[]> => {
333
+ return (
334
+ await processItemsAsync<string, Readonly<AssetModels.Asset> | undefined>({
335
+ logger: config.logger,
336
+ action: 'Fetching assets',
337
+ parallelLimit: 1,
338
+ items: Array.from(itemIds),
339
+ itemInfo: (id) => {
340
+ return {
341
+ itemType: 'asset',
342
+ title: id
343
+ };
344
+ },
345
+ processAsync: async (id, logSpinner) => {
346
+ try {
347
+ return await runMapiRequestAsync({
348
+ logger: config.logger,
349
+ logSpinner: logSpinner,
350
+ func: async () =>
351
+ (
352
+ await config.managementClient.viewAsset().byAssetId(id).toPromise()
353
+ ).data,
354
+ action: 'view',
355
+ type: 'asset',
356
+ itemName: `id -> ${id}`
357
+ });
358
+ } catch (error) {
359
+ if (!is404Error(error)) {
360
+ throw error;
361
+ }
362
+
363
+ return undefined;
364
+ }
365
+ }
366
+ })
367
+ ).filter(isNotUndefined);
368
+ };
369
+
370
+ const getItemStatesAsync = async (
371
+ itemIds: ReadonlySet<string>
372
+ ): Promise<readonly ItemStateInSourceEnvironmentById[]> => {
373
+ const items = await getContentItemsByIdsAsync(itemIds);
374
+
375
+ return Array.from(itemIds).map<ItemStateInSourceEnvironmentById>((itemId) => {
376
+ const item = items.find((m) => m.id === itemId);
377
+ return {
378
+ id: itemId,
379
+ item: item,
380
+ state: item ? 'exists' : 'doesNotExists'
381
+ };
382
+ });
383
+ };
384
+
385
+ const getAssetStatesAsync = async (
386
+ assetIds: ReadonlySet<string>
387
+ ): Promise<readonly AssetStateInSourceEnvironmentById[]> => {
388
+ const assets = await getAssetsByIdsAsync(assetIds);
389
+
390
+ return Array.from(assetIds).map<AssetStateInSourceEnvironmentById>((assetId) => {
391
+ const asset = assets.find((m) => m.id === assetId);
392
+ return {
393
+ id: assetId,
394
+ asset: asset,
395
+ state: asset ? 'exists' : 'doesNotExists'
396
+ };
397
+ });
398
+ };
399
+
400
+ const getElementByIds = (): GetFlattenedElementByIds => {
401
+ const getFunc: GetFlattenedElementByIds = (contentTypeId: string, elementId: string) => {
402
+ const contentType = findRequired(
403
+ environmentData.contentTypes,
404
+ (contentType) => contentType.contentTypeId === contentTypeId,
405
+ `Could not find content type with id '${chalk.red(contentTypeId)}'`
406
+ );
407
+
408
+ const element = findRequired(
409
+ contentType.elements,
410
+ (element) => element.id === elementId,
411
+ `Could not find element with id '${chalk.red(elementId)}' in content type '${chalk.red(
412
+ contentType.contentTypeCodename
413
+ )}'`
414
+ );
415
+
416
+ return element;
417
+ };
418
+
419
+ return getFunc;
420
+ };
421
+
422
+ const getExportContextAsync = async (): Promise<ExportContext> => {
423
+ const preparedItems = await prepareExportItemsAsync(config.exportItems);
424
+
425
+ config.logger.log({
426
+ type: 'info',
427
+ message: `Extracting referenced items & assets from content`
428
+ });
429
+
430
+ const referencedData = itemsExtractionProcessor().extractReferencedDataFromExtractItems(
431
+ preparedItems.map((exportItem) => {
432
+ return {
433
+ contentTypeId: exportItem.contentType.contentTypeId,
434
+ elements: exportItem.versions.flatMap((m) => m.languageVariant).flatMap((s) => s.elements)
435
+ };
436
+ }),
437
+ getElementByIds()
438
+ );
439
+
440
+ const itemStates: readonly ItemStateInSourceEnvironmentById[] = await getItemStatesAsync(
441
+ // fetch both referenced items and items that are set to be exported
442
+ new Set<string>([...referencedData.itemIds, ...preparedItems.map((m) => m.contentItem.id)])
443
+ );
444
+ const assetStates: readonly AssetStateInSourceEnvironmentById[] = await getAssetStatesAsync(
445
+ new Set<string>([...referencedData.assetIds])
446
+ );
447
+
448
+ const exportContext: ExportContext = {
449
+ getElement: getElementByIds(),
450
+ exportItems: preparedItems,
451
+ environmentData: environmentData,
452
+ referencedData: referencedData,
453
+ getAssetStateInSourceEnvironment: (id) =>
454
+ findRequired(
455
+ assetStates,
456
+ (m) => m.id === id,
457
+ `Invalid state for asset '${chalk.red(
458
+ id
459
+ )}'. It is expected that all asset states will be initialized`
460
+ ),
461
+ getItemStateInSourceEnvironment: (id) =>
462
+ findRequired(
463
+ itemStates,
464
+ (m) => m.id === id,
465
+ `Invalid state for item '${chalk.red(id)}'. It is expected that all item states will be initialized`
466
+ )
467
+ };
468
+
469
+ return exportContext;
470
+ };
471
+
472
+ return {
473
+ getExportContextAsync
474
+ };
475
+ }