@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,239 @@
1
+ import { AssetModels, ManagementClient } from '@kontent-ai/management-sdk';
2
+ import {
3
+ MigrationAsset,
4
+ Logger,
5
+ processItemsAsync,
6
+ runMapiRequestAsync,
7
+ MigrationAssetDescription,
8
+ MigrationReference,
9
+ geSizeInBytes,
10
+ isNotUndefined,
11
+ LogSpinnerData
12
+ } from '../../core/index.js';
13
+ import mime from 'mime';
14
+ import chalk from 'chalk';
15
+ import { ImportContext, ImportResult } from '../import.models.js';
16
+ import { shouldReplaceBinaryFile, shouldUpdateAsset } from '../comparers/asset-comparer.js';
17
+
18
+ interface AssetToEdit {
19
+ migrationAsset: MigrationAsset;
20
+ targetAsset: Readonly<AssetModels.Asset>;
21
+ replaceBinaryFile: boolean;
22
+ }
23
+
24
+ export function assetsImporter(data: {
25
+ readonly logger: Logger;
26
+ readonly client: Readonly<ManagementClient>;
27
+ readonly importContext: ImportContext;
28
+ }) {
29
+ const getAssetsToUpload = (): readonly MigrationAsset[] => {
30
+ return data.importContext.categorizedImportData.assets.filter((asset) => {
31
+ return data.importContext.getAssetStateInTargetEnvironment(asset.codename).state === 'doesNotExists';
32
+ });
33
+ };
34
+
35
+ const getAssetsToEdit = (): readonly AssetToEdit[] => {
36
+ return data.importContext.categorizedImportData.assets
37
+ .map<AssetToEdit | undefined>((migrationAsset) => {
38
+ const assetState = data.importContext.getAssetStateInTargetEnvironment(migrationAsset.codename);
39
+
40
+ if (!assetState.asset || assetState.state === 'doesNotExists') {
41
+ return undefined;
42
+ }
43
+
44
+ if (
45
+ !shouldUpdateAsset({
46
+ collections: data.importContext.environmentData.collections,
47
+ languages: data.importContext.environmentData.languages,
48
+ migrationAsset: migrationAsset,
49
+ targetAsset: assetState.asset
50
+ })
51
+ ) {
52
+ return undefined;
53
+ }
54
+
55
+ return {
56
+ migrationAsset: migrationAsset,
57
+ replaceBinaryFile: shouldReplaceBinaryFile({
58
+ migrationAsset: migrationAsset,
59
+ targetAsset: assetState.asset
60
+ }),
61
+ targetAsset: assetState.asset
62
+ };
63
+ })
64
+ .filter(isNotUndefined);
65
+ };
66
+
67
+ const editAssets = async (
68
+ assetsToEdit: readonly AssetToEdit[]
69
+ ): Promise<readonly Readonly<AssetModels.Asset>[]> => {
70
+ data.logger.log({
71
+ type: 'upsert',
72
+ message: `Upserting '${chalk.yellow(assetsToEdit.length.toString())}' assets`
73
+ });
74
+
75
+ return await processItemsAsync<AssetToEdit, Readonly<AssetModels.Asset>>({
76
+ action: 'Upserting assets',
77
+ logger: data.logger,
78
+ parallelLimit: 1,
79
+ items: assetsToEdit,
80
+ itemInfo: (input) => {
81
+ return {
82
+ itemType: 'asset',
83
+ title: input.migrationAsset.title
84
+ };
85
+ },
86
+ processAsync: async (assetEditRequest, logSpinner) => {
87
+ let uploadedBinaryFile: Readonly<AssetModels.AssetFileReference> | undefined;
88
+
89
+ if (assetEditRequest.replaceBinaryFile) {
90
+ uploadedBinaryFile = await uploadBinaryFileAsync(assetEditRequest.migrationAsset, logSpinner);
91
+ }
92
+
93
+ return await runMapiRequestAsync({
94
+ logger: data.logger,
95
+ func: async () => {
96
+ return (
97
+ await data.client
98
+ .upsertAsset()
99
+ .byAssetCodename(assetEditRequest.migrationAsset.codename)
100
+ .withData(() => {
101
+ return {
102
+ title: assetEditRequest.migrationAsset.title,
103
+ descriptions: mapAssetDescriptions(
104
+ assetEditRequest.migrationAsset.descriptions
105
+ ),
106
+ collection: mapAssetCollection(assetEditRequest.migrationAsset.collection),
107
+ file_reference: uploadedBinaryFile ?? undefined
108
+ };
109
+ })
110
+ .toPromise()
111
+ ).data;
112
+ },
113
+ action: 'upsert',
114
+ type: 'asset',
115
+ logSpinner: logSpinner,
116
+ itemName: `${assetEditRequest.migrationAsset.title ?? assetEditRequest.migrationAsset.filename}`
117
+ });
118
+ }
119
+ });
120
+ };
121
+
122
+ const mapAssetCollection = (
123
+ migrationCollection: MigrationReference | undefined
124
+ ): Readonly<AssetModels.IAssetCollectionReferenceObject> | undefined => {
125
+ return migrationCollection
126
+ ? {
127
+ reference: {
128
+ codename: migrationCollection.codename
129
+ }
130
+ }
131
+ : undefined;
132
+ };
133
+
134
+ const mapAssetDescriptions = (
135
+ migrationDescription: readonly MigrationAssetDescription[] | undefined
136
+ ): Readonly<AssetModels.IAssetFileDescription>[] => {
137
+ return (migrationDescription ?? []).map((m) => {
138
+ const assetDescription: Readonly<AssetModels.IAssetFileDescription> = {
139
+ description: m.description ?? '',
140
+ language: {
141
+ codename: m.language.codename
142
+ }
143
+ };
144
+
145
+ return assetDescription;
146
+ });
147
+ };
148
+
149
+ const uploadBinaryFileAsync = async (
150
+ migrationAsset: MigrationAsset,
151
+ logSpinner: LogSpinnerData
152
+ ): Promise<Readonly<AssetModels.AssetFileReference>> => {
153
+ return await runMapiRequestAsync({
154
+ logger: data.logger,
155
+ func: async () => {
156
+ return (
157
+ await data.client
158
+ .uploadBinaryFile()
159
+ .withData({
160
+ binaryData: migrationAsset.binaryData,
161
+ contentLength: geSizeInBytes(migrationAsset.binaryData),
162
+ contentType: mime.getType(migrationAsset.filename) ?? '',
163
+ filename: migrationAsset.filename
164
+ })
165
+ .toPromise()
166
+ ).data;
167
+ },
168
+ action: 'upload',
169
+ type: 'binaryFile',
170
+ logSpinner: logSpinner,
171
+ itemName: `${migrationAsset.title ?? migrationAsset.filename}`
172
+ });
173
+ };
174
+
175
+ const uploadAssetsAsync = async (
176
+ assetsToUpload: readonly MigrationAsset[]
177
+ ): Promise<readonly Readonly<AssetModels.Asset>[]> => {
178
+ data.logger.log({
179
+ type: 'upload',
180
+ message: `Uploading '${chalk.yellow(assetsToUpload.length.toString())}' assets`
181
+ });
182
+
183
+ return await processItemsAsync<MigrationAsset, Readonly<AssetModels.Asset>>({
184
+ action: 'Uploading assets',
185
+ logger: data.logger,
186
+ parallelLimit: 3,
187
+ items: assetsToUpload,
188
+ itemInfo: (input) => {
189
+ return {
190
+ itemType: 'asset',
191
+ title: input.title
192
+ };
193
+ },
194
+ processAsync: async (migrationAsset, logSpinner) => {
195
+ const uploadedBinaryFile = await uploadBinaryFileAsync(migrationAsset, logSpinner);
196
+
197
+ return await runMapiRequestAsync({
198
+ logger: data.logger,
199
+ func: async () =>
200
+ (
201
+ await data.client
202
+ .addAsset()
203
+ .withData(() => {
204
+ const assetStateInTargetEnv = data.importContext.getAssetStateInTargetEnvironment(
205
+ migrationAsset.codename
206
+ );
207
+
208
+ const assetRequestData: Readonly<AssetModels.IAddAssetRequestData> = {
209
+ file_reference: uploadedBinaryFile,
210
+ codename: migrationAsset.codename,
211
+ title: migrationAsset.title,
212
+ external_id: assetStateInTargetEnv.externalIdToUse,
213
+ collection: mapAssetCollection(migrationAsset.collection),
214
+ descriptions: mapAssetDescriptions(migrationAsset.descriptions)
215
+ };
216
+ return assetRequestData;
217
+ })
218
+ .toPromise()
219
+ ).data,
220
+ action: 'create',
221
+ type: 'asset',
222
+ logSpinner: logSpinner,
223
+ itemName: `${migrationAsset.title ?? migrationAsset.filename}`
224
+ });
225
+ }
226
+ });
227
+ };
228
+
229
+ const importAsync = async (): Promise<Pick<ImportResult, 'editedAssets' | 'uploadedAssets'>> => {
230
+ return {
231
+ editedAssets: await editAssets(getAssetsToEdit()),
232
+ uploadedAssets: await uploadAssetsAsync(getAssetsToUpload())
233
+ };
234
+ };
235
+
236
+ return {
237
+ importAsync
238
+ };
239
+ }
@@ -0,0 +1,142 @@
1
+ import { ContentItemModels, ManagementClient } from '@kontent-ai/management-sdk';
2
+ import {
3
+ Logger,
4
+ processItemsAsync,
5
+ runMapiRequestAsync,
6
+ MigrationItem,
7
+ LogSpinnerData,
8
+ findRequired
9
+ } from '../../core/index.js';
10
+ import chalk from 'chalk';
11
+ import { ImportContext } from '../import.models.js';
12
+
13
+ export function contentItemsImporter(data: {
14
+ readonly logger: Logger;
15
+ readonly client: Readonly<ManagementClient>;
16
+ readonly importContext: ImportContext;
17
+ }) {
18
+ const shouldUpdateContentItem = (
19
+ migrationContentItem: MigrationItem,
20
+ contentItem: Readonly<ContentItemModels.ContentItem>
21
+ ): boolean => {
22
+ const collection = findRequired(
23
+ data.importContext.environmentData.collections,
24
+ (collection) => collection.codename === migrationContentItem.system.collection.codename,
25
+ `Invalid collection '${migrationContentItem.system.collection.codename}'`
26
+ );
27
+
28
+ return (
29
+ migrationContentItem.system.name !== contentItem.name ||
30
+ migrationContentItem.system.collection.codename !== collection.codename
31
+ );
32
+ };
33
+
34
+ const prepareContentItemAsync = async (
35
+ logSpinner: LogSpinnerData,
36
+ migrationContentItem: MigrationItem
37
+ ): Promise<{ contentItem: Readonly<ContentItemModels.ContentItem>; status: 'created' | 'itemAlreadyExists' }> => {
38
+ const itemStateInTargetEnv = data.importContext.getItemStateInTargetEnvironment(
39
+ migrationContentItem.system.codename
40
+ );
41
+
42
+ if (itemStateInTargetEnv.state === 'exists' && itemStateInTargetEnv.item) {
43
+ return {
44
+ contentItem: itemStateInTargetEnv.item,
45
+ status: 'itemAlreadyExists'
46
+ };
47
+ }
48
+
49
+ const createdContentItem = await runMapiRequestAsync({
50
+ logger: data.logger,
51
+ func: async () =>
52
+ (
53
+ await data.client
54
+ .addContentItem()
55
+ .withData({
56
+ name: migrationContentItem.system.name,
57
+ type: {
58
+ codename: migrationContentItem.system.type.codename
59
+ },
60
+ external_id: itemStateInTargetEnv.externalIdToUse,
61
+ codename: migrationContentItem.system.codename,
62
+ collection: {
63
+ codename: migrationContentItem.system.collection.codename
64
+ }
65
+ })
66
+ .toPromise()
67
+ ).data,
68
+ action: 'create',
69
+ type: 'contentItem',
70
+ logSpinner: logSpinner,
71
+ itemName: `${migrationContentItem.system.codename} (${migrationContentItem.system.language.codename})`
72
+ });
73
+
74
+ return {
75
+ contentItem: createdContentItem,
76
+ status: 'created'
77
+ };
78
+ };
79
+
80
+ const importContentItemAsync = async (
81
+ logSpinner: LogSpinnerData,
82
+ migrationItem: MigrationItem
83
+ ): Promise<Readonly<ContentItemModels.ContentItem>> => {
84
+ const preparedContentItemResult = await prepareContentItemAsync(logSpinner, migrationItem);
85
+
86
+ // check if name should be updated, no other changes are supported
87
+ if (preparedContentItemResult.status === 'itemAlreadyExists') {
88
+ if (shouldUpdateContentItem(migrationItem, preparedContentItemResult.contentItem)) {
89
+ await runMapiRequestAsync({
90
+ logger: data.logger,
91
+ func: async () => {
92
+ await data.client
93
+ .upsertContentItem()
94
+ .byItemCodename(migrationItem.system.codename)
95
+ .withData({
96
+ name: migrationItem.system.name,
97
+ collection: {
98
+ codename: migrationItem.system.collection.codename
99
+ }
100
+ })
101
+ .toPromise();
102
+ },
103
+ action: 'upsert',
104
+ type: 'contentItem',
105
+ logSpinner: logSpinner,
106
+ itemName: `${migrationItem.system.codename} (${migrationItem.system.language.codename})`
107
+ });
108
+ }
109
+ }
110
+
111
+ return preparedContentItemResult.contentItem;
112
+ };
113
+
114
+ const importAsync = async (): Promise<readonly Readonly<ContentItemModels.ContentItem>[]> => {
115
+ const contentItemsToImport = data.importContext.categorizedImportData.contentItems;
116
+
117
+ data.logger.log({
118
+ type: 'info',
119
+ message: `Importing '${chalk.yellow(contentItemsToImport.length.toString())}' content items`
120
+ });
121
+
122
+ return await processItemsAsync<MigrationItem, Readonly<ContentItemModels.ContentItem>>({
123
+ action: 'Importing content items',
124
+ logger: data.logger,
125
+ parallelLimit: 1,
126
+ items: contentItemsToImport,
127
+ itemInfo: (item) => {
128
+ return {
129
+ itemType: 'contentItem',
130
+ title: `${item.system.codename} -> ${item.system.language.codename}`
131
+ };
132
+ },
133
+ processAsync: async (item, logSpinner) => {
134
+ return await importContentItemAsync(logSpinner, item);
135
+ }
136
+ });
137
+ };
138
+
139
+ return {
140
+ importAsync
141
+ };
142
+ }