@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,342 @@
1
+ import {
2
+ ContentItemModels,
3
+ ElementContracts,
4
+ LanguageVariantModels,
5
+ ManagementClient,
6
+ WorkflowModels
7
+ } from '@kontent-ai/management-sdk';
8
+ import {
9
+ Logger,
10
+ processItemsAsync,
11
+ runMapiRequestAsync,
12
+ MigrationItem,
13
+ LogSpinnerData,
14
+ MigrationElement,
15
+ MigrationItemVersion,
16
+ workflowHelper,
17
+ findRequired
18
+ } from '../../core/index.js';
19
+ import chalk from 'chalk';
20
+ import { ImportContext } from '../import.models.js';
21
+ import { importTransforms } from '../../translation/index.js';
22
+ import { workflowImporter } from './workflow-importer.js';
23
+ import { throwErrorForMigrationItem } from '../utils/import.utils.js';
24
+
25
+ export function languageVariantImporter(config: {
26
+ readonly logger: Logger;
27
+ readonly preparedContentItems: readonly ContentItemModels.ContentItem[];
28
+ readonly importContext: ImportContext;
29
+ readonly client: Readonly<ManagementClient>;
30
+ }) {
31
+ const upsertLanguageVariantAsync = async (data: {
32
+ readonly workflow: Readonly<WorkflowModels.Workflow>;
33
+ readonly logSpinner: LogSpinnerData;
34
+ readonly migrationItem: MigrationItem;
35
+ readonly migrationItemVersion: MigrationItemVersion;
36
+ readonly preparedContentItem: Readonly<ContentItemModels.ContentItem>;
37
+ }): Promise<Readonly<LanguageVariantModels.ContentItemLanguageVariant>> => {
38
+ return await runMapiRequestAsync({
39
+ logger: config.logger,
40
+ func: async () => {
41
+ return (
42
+ await config.client
43
+ .upsertLanguageVariant()
44
+ .byItemCodename(data.preparedContentItem.codename)
45
+ .byLanguageCodename(data.migrationItem.system.language.codename)
46
+ .withData(() => {
47
+ return {
48
+ elements: Object.entries(data.migrationItemVersion.elements).map(
49
+ ([codename, migrationElement]) => {
50
+ return getElementContract(data.migrationItem, migrationElement, codename);
51
+ }
52
+ ),
53
+ workflow: {
54
+ workflow_identifier: {
55
+ codename: data.workflow.codename
56
+ },
57
+ step_identifier: {
58
+ codename: data.workflow.steps[0].codename // use always first step
59
+ }
60
+ }
61
+ };
62
+ })
63
+ .toPromise()
64
+ ).data;
65
+ },
66
+ action: 'upsert',
67
+ type: 'languageVariant',
68
+ logSpinner: data.logSpinner,
69
+ itemName: `${data.migrationItem.system.codename} (${data.migrationItem.system.language.codename})`
70
+ });
71
+ };
72
+
73
+ const categorizeVersions = (
74
+ migrationItem: MigrationItem
75
+ ): { publishedVersion: MigrationItemVersion | undefined; draftVersion: MigrationItemVersion | undefined } => {
76
+ const workflow = workflowHelper(config.importContext.environmentData.workflows).getWorkflowByCodename(
77
+ migrationItem.system.workflow.codename
78
+ );
79
+
80
+ const publishedVersions = migrationItem.versions.filter((version) =>
81
+ isPublishedWorkflowStep(version.workflow_step.codename, workflow)
82
+ );
83
+ const draftVersions = migrationItem.versions.filter(
84
+ (version) => !isPublishedWorkflowStep(version.workflow_step.codename, workflow)
85
+ );
86
+
87
+ if (publishedVersions.length > 1) {
88
+ throwErrorForMigrationItem(
89
+ migrationItem,
90
+ `There can be only 1 published version. There are '${publishedVersions.length}' published versions for the item.`
91
+ );
92
+ }
93
+
94
+ if (draftVersions.length > 1) {
95
+ throwErrorForMigrationItem(
96
+ migrationItem,
97
+ `There can be only 1 draft version. There are '${publishedVersions.length}' draft versions for the item.`
98
+ );
99
+ }
100
+
101
+ return {
102
+ draftVersion: draftVersions?.[0],
103
+ publishedVersion: publishedVersions?.[0]
104
+ };
105
+ };
106
+
107
+ const importVersionAsync = async (data: {
108
+ readonly logSpinner: LogSpinnerData;
109
+ readonly migrationItem: MigrationItem;
110
+ readonly migrationItemVersion: MigrationItemVersion;
111
+ readonly preparedContentItem: Readonly<ContentItemModels.ContentItem>;
112
+ readonly workflowStepCodenameInTargetEnvironment: string | undefined;
113
+ }): Promise<Readonly<LanguageVariantModels.ContentItemLanguageVariant>> => {
114
+ // validate workflow
115
+ const { step, workflow } = workflowHelper(
116
+ config.importContext.environmentData.workflows
117
+ ).getWorkflowAndStepByCodenames({
118
+ workflowCodename: data.migrationItem.system.workflow.codename,
119
+ stepCodename: data.migrationItemVersion.workflow_step.codename
120
+ });
121
+
122
+ // prepare target variant for upsert if it exists in target env
123
+ if (data.workflowStepCodenameInTargetEnvironment) {
124
+ await prepareLanguageVariantForImportAsync({
125
+ logSpinner: data.logSpinner,
126
+ workflowStepCodenameInTargetEnvironment: data.workflowStepCodenameInTargetEnvironment,
127
+ migrationItem: data.migrationItem,
128
+ workflow
129
+ });
130
+ }
131
+
132
+ // upsert language variant
133
+ const languageVariant = await upsertLanguageVariantAsync({
134
+ logSpinner: data.logSpinner,
135
+ migrationItem: data.migrationItem,
136
+ preparedContentItem: data.preparedContentItem,
137
+ migrationItemVersion: data.migrationItemVersion,
138
+ workflow
139
+ });
140
+
141
+ // set workflow accordingly (publish, move to workflow step, archive ...)
142
+ await workflowImporter({
143
+ logger: config.logger,
144
+ managementClient: config.client,
145
+ workflows: config.importContext.environmentData.workflows
146
+ }).setWorkflowOfLanguageVariantAsync({
147
+ logSpinner: data.logSpinner,
148
+ migrationItem: data.migrationItem,
149
+ workflowCodename: workflow.codename,
150
+ stepCodename: step.codename
151
+ });
152
+
153
+ return languageVariant;
154
+ };
155
+
156
+ const importLanguageVariantAsync = async (
157
+ logSpinner: LogSpinnerData,
158
+ migrationItem: MigrationItem,
159
+ preparedContentItem: Readonly<ContentItemModels.ContentItem>
160
+ ): Promise<readonly LanguageVariantModels.ContentItemLanguageVariant[]> => {
161
+ const { draftVersion, publishedVersion } = categorizeVersions(migrationItem);
162
+
163
+ let publishedLanguageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant> | undefined;
164
+ let draftLanguageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant> | undefined;
165
+ let lastWorkflowStepCodenameInTargetEnvironment: string | undefined;
166
+
167
+ const workflow = workflowHelper(config.importContext.environmentData.workflows).getWorkflowByCodename(
168
+ migrationItem.system.workflow.codename
169
+ );
170
+
171
+ // get initial state of language variant from target env
172
+ const languageVariantState = config.importContext.getLanguageVariantStateInTargetEnvironment(
173
+ migrationItem.system.codename,
174
+ migrationItem.system.language.codename
175
+ );
176
+
177
+ if (languageVariantState.step) {
178
+ // keep track of last worfklow step
179
+ lastWorkflowStepCodenameInTargetEnvironment = languageVariantState.step.codename;
180
+ }
181
+
182
+ // first import published version if it exists
183
+ if (publishedVersion) {
184
+ publishedLanguageVariant = await importVersionAsync({
185
+ logSpinner: logSpinner,
186
+ migrationItem: migrationItem,
187
+ preparedContentItem: preparedContentItem,
188
+ migrationItemVersion: publishedVersion,
189
+ workflowStepCodenameInTargetEnvironment: lastWorkflowStepCodenameInTargetEnvironment
190
+ });
191
+
192
+ // set last published workflow step to publish as language variant has just been published in target environment
193
+ lastWorkflowStepCodenameInTargetEnvironment = workflow.publishedStep.codename;
194
+ }
195
+
196
+ if (draftVersion) {
197
+ draftLanguageVariant = await importVersionAsync({
198
+ logSpinner: logSpinner,
199
+ migrationItem: migrationItem,
200
+ preparedContentItem: preparedContentItem,
201
+ migrationItemVersion: draftVersion,
202
+ workflowStepCodenameInTargetEnvironment: lastWorkflowStepCodenameInTargetEnvironment
203
+ });
204
+ }
205
+
206
+ return [
207
+ ...(publishedLanguageVariant ? [publishedLanguageVariant] : []),
208
+ ...(draftLanguageVariant ? [draftLanguageVariant] : [])
209
+ ];
210
+ };
211
+
212
+ const createNewVersionOfLanguageVariantAsync = async (
213
+ logSpinner: LogSpinnerData,
214
+ migrationItem: MigrationItem
215
+ ): Promise<void> => {
216
+ await runMapiRequestAsync({
217
+ logger: config.logger,
218
+ func: async () => {
219
+ await config.client
220
+ .createNewVersionOfLanguageVariant()
221
+ .byItemCodename(migrationItem.system.codename)
222
+ .byLanguageCodename(migrationItem.system.language.codename)
223
+ .toPromise();
224
+ },
225
+ action: 'createNewVersion',
226
+ type: 'languageVariant',
227
+ logSpinner: logSpinner,
228
+ itemName: `${migrationItem.system.codename} (${migrationItem.system.language.codename})`
229
+ });
230
+ };
231
+
232
+ const moveToDraftStepAsync = async (
233
+ logSpinner: LogSpinnerData,
234
+ migrationItem: MigrationItem,
235
+ workflow: Readonly<WorkflowModels.Workflow>
236
+ ): Promise<void> => {
237
+ const firstWorkflowStep = workflow.steps?.[0];
238
+
239
+ if (firstWorkflowStep) {
240
+ await runMapiRequestAsync({
241
+ logger: config.logger,
242
+ func: async () => {
243
+ await config.client
244
+ .changeWorkflowStepOfLanguageVariant()
245
+ .byItemCodename(migrationItem.system.codename)
246
+ .byLanguageCodename(migrationItem.system.language.codename)
247
+ .byWorkflowStepCodename(firstWorkflowStep.codename)
248
+ .toPromise();
249
+ },
250
+ action: 'changeWorkflowStep',
251
+ type: 'languageVariant',
252
+ logSpinner: logSpinner,
253
+ itemName: `${migrationItem.system.codename} (${migrationItem.system.language.codename}) -> ${firstWorkflowStep.codename}`
254
+ });
255
+ }
256
+ };
257
+
258
+ const prepareLanguageVariantForImportAsync = async (data: {
259
+ readonly logSpinner: LogSpinnerData;
260
+ readonly migrationItem: MigrationItem;
261
+ readonly workflowStepCodenameInTargetEnvironment: string;
262
+ readonly workflow: Readonly<WorkflowModels.Workflow>;
263
+ }): Promise<void> => {
264
+ // create new version if language variant is published
265
+ if (isPublishedWorkflowStep(data.workflowStepCodenameInTargetEnvironment, data.workflow)) {
266
+ await createNewVersionOfLanguageVariantAsync(data.logSpinner, data.migrationItem);
267
+ }
268
+
269
+ // move to draft step if language variant is archived
270
+ if (isArchivedWorkflowStep(data.workflowStepCodenameInTargetEnvironment, data.workflow)) {
271
+ await moveToDraftStepAsync(data.logSpinner, data.migrationItem, data.workflow);
272
+ }
273
+ };
274
+
275
+ const isPublishedWorkflowStep = (stepCodename: string, workflow: Readonly<WorkflowModels.Workflow>): boolean => {
276
+ return workflow.publishedStep.codename === stepCodename;
277
+ };
278
+
279
+ const isArchivedWorkflowStep = (stepCodename: string, workflow: Readonly<WorkflowModels.Workflow>): boolean => {
280
+ return workflow.archivedStep.codename === stepCodename;
281
+ };
282
+
283
+ const getElementContract = (
284
+ migrationItem: MigrationItem,
285
+ element: MigrationElement,
286
+ elementCodename: string
287
+ ): Readonly<ElementContracts.IContentItemElementContract> => {
288
+ const importTransformResult = importTransforms[
289
+ config.importContext.getElement(migrationItem.system.type.codename, elementCodename, element.type).type
290
+ ]({
291
+ elementCodename: elementCodename,
292
+ importContext: config.importContext,
293
+ migrationItems: config.importContext.categorizedImportData.contentItems,
294
+ value: element.value
295
+ });
296
+
297
+ return importTransformResult;
298
+ };
299
+
300
+ const importAsync = async (): Promise<readonly Readonly<LanguageVariantModels.ContentItemLanguageVariant>[]> => {
301
+ config.logger.log({
302
+ type: 'info',
303
+ message: `Importing '${chalk.yellow(
304
+ config.importContext.categorizedImportData.contentItems.length.toString()
305
+ )}' language variants`
306
+ });
307
+
308
+ return (
309
+ await processItemsAsync<
310
+ MigrationItem,
311
+ readonly Readonly<LanguageVariantModels.ContentItemLanguageVariant>[]
312
+ >({
313
+ action: 'Importing language variants',
314
+ logger: config.logger,
315
+ parallelLimit: 1,
316
+ items: config.importContext.categorizedImportData.contentItems,
317
+ itemInfo: (input) => {
318
+ return {
319
+ itemType: 'languageVariant',
320
+ title: input.system.name,
321
+ partA: input.system.language.codename
322
+ };
323
+ },
324
+ processAsync: async (migrationItem, logSpinner) => {
325
+ const contentItem = findRequired(
326
+ config.preparedContentItems,
327
+ (item) => item.codename === migrationItem.system.codename,
328
+ `Missing content item with codename '${chalk.red(
329
+ migrationItem.system.codename
330
+ )}'. Content item should have been prepepared.`
331
+ );
332
+
333
+ return await importLanguageVariantAsync(logSpinner, migrationItem, contentItem);
334
+ }
335
+ })
336
+ ).flatMap((m) => m.map((s) => s));
337
+ };
338
+
339
+ return {
340
+ importAsync
341
+ };
342
+ }
@@ -0,0 +1,159 @@
1
+ import { ManagementClient, SharedModels, WorkflowModels } from '@kontent-ai/management-sdk';
2
+ import { Logger, runMapiRequestAsync, LogSpinnerData, MigrationItem, workflowHelper } from '../../core/index.js';
3
+
4
+ export function workflowImporter(config: {
5
+ logger: Logger;
6
+ managementClient: Readonly<ManagementClient>;
7
+ workflows: readonly Readonly<WorkflowModels.Workflow>[];
8
+ }) {
9
+ const publishLanguageVariantAsync = async (data: {
10
+ readonly logSpinner: LogSpinnerData;
11
+ readonly migrationItem: MigrationItem;
12
+ }): Promise<void> => {
13
+ await runMapiRequestAsync({
14
+ logger: config.logger,
15
+ func: async () =>
16
+ (
17
+ await config.managementClient
18
+ .publishLanguageVariant()
19
+ .byItemCodename(data.migrationItem.system.codename)
20
+ .byLanguageCodename(data.migrationItem.system.language.codename)
21
+ .withoutData()
22
+ .toPromise()
23
+ ).data,
24
+ action: 'publish',
25
+ type: 'languageVariant',
26
+ logSpinner: data.logSpinner,
27
+ itemName: `${data.migrationItem.system.codename} (${data.migrationItem.system.language.codename})`
28
+ });
29
+ };
30
+
31
+ const unpublishLanguageVariantAsync = async (data: {
32
+ readonly logSpinner: LogSpinnerData;
33
+ readonly migrationItem: MigrationItem;
34
+ }): Promise<void> => {
35
+ // unpublish the language variant first if published
36
+ // there is no way to determine if language variant is published via MAPI
37
+ // so we have to always try unpublishing first and catching possible errors
38
+ try {
39
+ await runMapiRequestAsync({
40
+ logger: config.logger,
41
+ func: async () =>
42
+ (
43
+ await config.managementClient
44
+ .unpublishLanguageVariant()
45
+ .byItemCodename(data.migrationItem.system.codename)
46
+ .byLanguageCodename(data.migrationItem.system.language.codename)
47
+ .withoutData()
48
+ .toPromise()
49
+ ).data,
50
+ action: 'unpublish',
51
+ type: 'languageVariant',
52
+ logSpinner: data.logSpinner,
53
+ itemName: `${data.migrationItem.system.codename} (${data.migrationItem.system.language.codename})`
54
+ });
55
+ } catch (error) {
56
+ if (error instanceof SharedModels.ContentManagementBaseKontentError) {
57
+ data.logSpinner({
58
+ type: 'unpublish',
59
+ message: `Unpublish failed, but this may be expected behavior as we cannot determine if there is a published version already. Error received: ${error.message}`
60
+ });
61
+ } else {
62
+ throw error;
63
+ }
64
+ }
65
+ };
66
+
67
+ const archiveLanguageVariantAsync = async (data: {
68
+ readonly logSpinner: LogSpinnerData;
69
+ readonly workflowCodename: string;
70
+ readonly migrationItem: MigrationItem;
71
+ }): Promise<void> => {
72
+ const workflow = workflowHelper(config.workflows).getWorkflowByCodename(data.workflowCodename);
73
+ await runMapiRequestAsync({
74
+ logger: config.logger,
75
+ func: async () =>
76
+ (
77
+ await config.managementClient
78
+ .changeWorkflowOfLanguageVariant()
79
+ .byItemCodename(data.migrationItem.system.codename)
80
+ .byLanguageCodename(data.migrationItem.system.language.codename)
81
+ .withData({
82
+ step_identifier: {
83
+ codename: workflow.archivedStep.codename
84
+ },
85
+ workflow_identifier: {
86
+ codename: workflow.codename
87
+ }
88
+ })
89
+ .toPromise()
90
+ ).data,
91
+ action: 'archive',
92
+ type: 'languageVariant',
93
+ logSpinner: data.logSpinner,
94
+ itemName: `${data.migrationItem.system.codename} (${data.migrationItem.system.language.codename}) -> ${workflow.archivedStep.codename}`
95
+ });
96
+ };
97
+
98
+ const changeWorkflowOfLanguageVariantAsync = async (data: {
99
+ readonly logSpinner: LogSpinnerData;
100
+ readonly workflowCodename: string;
101
+ readonly stepCodename: string;
102
+ readonly migrationItem: MigrationItem;
103
+ }): Promise<void> => {
104
+ const { workflow, step } = workflowHelper(config.workflows).getWorkflowAndStepByCodenames({
105
+ workflowCodename: data.workflowCodename,
106
+ stepCodename: data.stepCodename
107
+ });
108
+
109
+ await runMapiRequestAsync({
110
+ logger: config.logger,
111
+ func: async () =>
112
+ (
113
+ await config.managementClient
114
+ .changeWorkflowOfLanguageVariant()
115
+ .byItemCodename(data.migrationItem.system.codename)
116
+ .byLanguageCodename(data.migrationItem.system.language.codename)
117
+ .withData({
118
+ step_identifier: {
119
+ codename: step.codename
120
+ },
121
+ workflow_identifier: {
122
+ codename: workflow.codename
123
+ }
124
+ })
125
+ .toPromise()
126
+ ).data,
127
+ action: 'changeWorkflowStep',
128
+ type: 'languageVariant',
129
+ logSpinner: data.logSpinner,
130
+ itemName: `${data.migrationItem.system.codename} (${data.migrationItem.system.language.codename}) -> ${step.codename}`
131
+ });
132
+ };
133
+
134
+ const setWorkflowOfLanguageVariantAsync = async (data: {
135
+ readonly logSpinner: LogSpinnerData;
136
+ readonly workflowCodename: string;
137
+ readonly stepCodename: string;
138
+ readonly migrationItem: MigrationItem;
139
+ }): Promise<void> => {
140
+ if (workflowHelper(config.workflows).isPublishedStepByCodename(data.stepCodename)) {
141
+ await publishLanguageVariantAsync(data);
142
+ } else if (workflowHelper(config.workflows).isScheduledStepByCodename(data.stepCodename)) {
143
+ data.logSpinner({
144
+ type: 'skip',
145
+ message: `${data.migrationItem.system.codename} (${data.migrationItem.system.language.codename}) -> Skipping scheduled workflow step assignment`
146
+ });
147
+ } else if (workflowHelper(config.workflows).isArchivedStepByCodename(data.stepCodename)) {
148
+ await unpublishLanguageVariantAsync(data);
149
+ await archiveLanguageVariantAsync(data);
150
+ } else {
151
+ await changeWorkflowOfLanguageVariantAsync(data);
152
+ }
153
+ };
154
+
155
+ return {
156
+ setWorkflowOfLanguageVariantAsync,
157
+ publishLanguageVariantAsync
158
+ };
159
+ }
@@ -0,0 +1,2 @@
1
+ export * from './import.models.js';
2
+ export * from './import-manager.js';
@@ -0,0 +1,10 @@
1
+ import chalk from 'chalk';
2
+ import { MigrationItem } from 'lib/core/index.js';
3
+
4
+ export function throwErrorForMigrationItem(migrationItem: MigrationItem, message: string): never {
5
+ throw Error(
6
+ `Impot failed for item '${chalk.yellow(migrationItem.system.codename)}' in language '${chalk.cyan(
7
+ migrationItem.system.language
8
+ )}'. Reason: ${message}`
9
+ );
10
+ }
package/lib/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ // Public API
2
+ export * from './core/index.js';
3
+ export * from './export/index.js';
4
+ export * from './import/index.js';
5
+ export * from './file/index.js';
6
+ export * from './zip/index.js';
7
+ export * from './toolkit/index.js';
8
+ export * from './file/index.js';
9
+ export * from './translation/index.js';
@@ -0,0 +1,6 @@
1
+
2
+ export const libMetadata = {
3
+ name: '@kontent-ai/migration-toolkit',
4
+ timestamp: 'Wed, 10 Jul 2024 10:38:24 GMT',
5
+ version: '1.0.0'
6
+ };
@@ -0,0 +1,45 @@
1
+ import { confirmExportAsync, getDefaultZipFilename, getDefaultLogger } from '../../../core/index.js';
2
+ import { exportAsync, storeAsync } from '../../../toolkit/index.js';
3
+ import { CliArgumentsFetcher } from '../cli.models.js';
4
+
5
+ export async function exportActionAsync(cliFetcher: CliArgumentsFetcher): Promise<void> {
6
+ const logger = getDefaultLogger();
7
+ const language = cliFetcher.getRequiredArgumentValue('language');
8
+ const environmentId = cliFetcher.getRequiredArgumentValue('sourceEnvironmentId');
9
+ const apiKey = cliFetcher.getRequiredArgumentValue('sourceApiKey');
10
+ const items = cliFetcher.getRequiredArgumentValue('items').split(',');
11
+ const baseUrl = cliFetcher.getOptionalArgumentValue('baseUrl');
12
+ const force = cliFetcher.getBooleanArgumentValue('force', false);
13
+ const filename = cliFetcher.getOptionalArgumentValue('filename') ?? getDefaultZipFilename();
14
+
15
+ await confirmExportAsync({
16
+ force: force,
17
+ apiKey: apiKey,
18
+ environmentId: environmentId,
19
+ logger: logger,
20
+ dataToExport: {
21
+ itemsCount: items.length
22
+ }
23
+ });
24
+
25
+ const exportedData = await exportAsync({
26
+ logger: logger,
27
+ environmentId: environmentId,
28
+ apiKey: apiKey,
29
+ baseUrl: baseUrl,
30
+ exportItems: items.map((m) => {
31
+ return {
32
+ itemCodename: m,
33
+ languageCodename: language
34
+ };
35
+ })
36
+ });
37
+
38
+ await storeAsync({
39
+ data: exportedData,
40
+ filename: filename,
41
+ logger: logger
42
+ });
43
+
44
+ logger.log({ type: 'completed', message: `Export has been successful` });
45
+ }
@@ -0,0 +1,34 @@
1
+ import { confirmImportAsync, getDefaultZipFilename, getDefaultLogger } from '../../../core/index.js';
2
+ import { extractAsync, importAsync } from '../../../toolkit/index.js';
3
+ import { CliArgumentsFetcher } from '../cli.models.js';
4
+
5
+ export async function importActionAsync(argsFetcher: CliArgumentsFetcher): Promise<void> {
6
+ const log = getDefaultLogger();
7
+ const environmentId = argsFetcher.getRequiredArgumentValue('targetEnvironmentId');
8
+ const apiKey = argsFetcher.getRequiredArgumentValue('targetApiKey');
9
+ const baseUrl = argsFetcher.getOptionalArgumentValue('baseUrl');
10
+ const force = argsFetcher.getBooleanArgumentValue('force', false);
11
+ const filename = argsFetcher.getOptionalArgumentValue('filename') ?? getDefaultZipFilename();
12
+
13
+ await confirmImportAsync({
14
+ force: force,
15
+ apiKey: apiKey,
16
+ environmentId: environmentId,
17
+ logger: log
18
+ });
19
+
20
+ const importData = await extractAsync({
21
+ logger: log,
22
+ filename: filename
23
+ });
24
+
25
+ await importAsync({
26
+ logger: log,
27
+ data: importData,
28
+ baseUrl: baseUrl,
29
+ environmentId: environmentId,
30
+ apiKey: apiKey
31
+ });
32
+
33
+ log.log({ type: 'completed', message: `Import has been successful` });
34
+ }
@@ -0,0 +1,52 @@
1
+ import { migrateAsync } from '../../../toolkit/index.js';
2
+ import { confirmMigrateAsync, getDefaultLogger } from '../../../core/index.js';
3
+ import { SourceExportItem } from '../../../export/index.js';
4
+ import { CliArgumentsFetcher } from '../cli.models.js';
5
+
6
+ export async function migrateActionAsync(argsFetcher: CliArgumentsFetcher): Promise<void> {
7
+ const log = getDefaultLogger();
8
+ const sourceEnvironmentId = argsFetcher.getRequiredArgumentValue('sourceEnvironmentId');
9
+ const sourceApiKey = argsFetcher.getRequiredArgumentValue('sourceApiKey');
10
+ const targetEnvironmentId = argsFetcher.getRequiredArgumentValue('targetEnvironmentId');
11
+ const targetApiKey = argsFetcher.getRequiredArgumentValue('targetApiKey');
12
+ const force = argsFetcher.getBooleanArgumentValue('force', false);
13
+ const items = argsFetcher.getRequiredArgumentValue('items')?.split(',');
14
+ const language = argsFetcher.getRequiredArgumentValue('language');
15
+ const migrateItems: readonly SourceExportItem[] = items.map((m) => {
16
+ return {
17
+ itemCodename: m,
18
+ languageCodename: language
19
+ };
20
+ });
21
+
22
+ await confirmMigrateAsync({
23
+ force: force,
24
+ sourceEnvironment: {
25
+ apiKey: sourceApiKey,
26
+ environmentId: sourceEnvironmentId
27
+ },
28
+ targetEnvironment: {
29
+ apiKey: targetApiKey,
30
+ environmentId: targetEnvironmentId
31
+ },
32
+ logger: log,
33
+ dataToMigrate: {
34
+ itemsCount: migrateItems.length
35
+ }
36
+ });
37
+
38
+ await migrateAsync({
39
+ logger: log,
40
+ sourceEnvironment: {
41
+ environmentId: sourceEnvironmentId,
42
+ apiKey: sourceApiKey,
43
+ items: migrateItems
44
+ },
45
+ targetEnvironment: {
46
+ environmentId: targetEnvironmentId,
47
+ apiKey: targetApiKey
48
+ }
49
+ });
50
+
51
+ log.log({ type: 'completed', message: `Migration has been successful` });
52
+ }