Skip to content

Commit 5ad9b20

Browse files
authored
Merge pull request #1037 from contentstack/feature/cmg-774
Feature/cmg 774
2 parents 55c2530 + 98ce6fd commit 5ad9b20

15 files changed

+220
-39
lines changed

api/package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"@contentstack/json-rte-serializer": "^3.0.5",
3636
"@contentstack/marketplace-sdk": "^1.5.0",
3737
"@wordpress/block-serialization-default-parser": "^5.39.0",
38-
"axios": "^1.13.5",
38+
"axios": "^1.15.0",
3939
"cheerio": "^1.2.0",
4040
"chokidar": "^3.6.0",
4141
"cors": "^2.8.5",
@@ -91,6 +91,9 @@
9191
"vitest": "^4.0.18"
9292
},
9393
"overrides": {
94+
"@contentstack/cli-utilities": {
95+
"axios": ">=1.15.0"
96+
},
9497
"qs": ">=6.14.2",
9598
"tmp": ">=0.2.4",
9699
"minimatch": ">=10.2.3",

api/src/services/contentMapper.service.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,6 +1581,75 @@ const getExistingTaxonomies = async (req: Request) => {
15811581
};
15821582
}
15831583
};
1584+
const getExistingExtensions = async ({existingStackId, token_payload}: any) => {
1585+
try {
1586+
const url = `${config?.CS_API[
1587+
token_payload?.region as keyof typeof config.CS_API
1588+
]!}/extensions`;
1589+
1590+
const headers: Record<string, string> = { api_key: existingStackId };
1591+
if (token_payload?.is_sso) {
1592+
const accessToken = await getAccessToken(
1593+
token_payload?.region,
1594+
token_payload?.user_id,
1595+
);
1596+
headers.authorization = `Bearer ${accessToken}`;
1597+
} else {
1598+
headers.authtoken = await getAuthtoken(
1599+
token_payload?.region,
1600+
token_payload?.user_id,
1601+
);
1602+
}
1603+
1604+
const requestConfig = {
1605+
method: 'GET' as const,
1606+
url,
1607+
headers,
1608+
};
1609+
1610+
const [err, res] = token_payload?.is_sso
1611+
? await requestWithSsoTokenRefresh(token_payload, requestConfig)
1612+
: await safePromise(https(requestConfig));
1613+
1614+
if (err) {
1615+
const e = err as {
1616+
message?: string;
1617+
response?: { status?: number; data?: unknown };
1618+
};
1619+
const detail =
1620+
e.response?.data != null
1621+
? typeof e.response.data === 'string'
1622+
? e.response.data
1623+
: JSON.stringify(e.response.data)
1624+
: e.message;
1625+
const httpErr = new Error(`Error in getExistingExtensions: ${detail}`);
1626+
if (e.response?.status != null) {
1627+
Object.assign(httpErr, { statusCode: e.response.status });
1628+
}
1629+
throw httpErr;
1630+
}
1631+
1632+
const extensions = res?.data?.extensions;
1633+
console.info('extensions', extensions);
1634+
if (!Array.isArray(extensions)) {
1635+
throw new Error(
1636+
'Error in getExistingExtensions: extensions is not an array',
1637+
);
1638+
}
1639+
1640+
return extensions.filter((ext: { type?: string }) => ext?.type === 'field');
1641+
1642+
} catch (error: any) {
1643+
logger.error(`Error in getExistingExtensions: ${error.message}`, error);
1644+
return {
1645+
data: error?.message,
1646+
status: error?.statusCode || error?.status || 500,
1647+
};
1648+
1649+
1650+
}
1651+
1652+
}
15841653

15851654
export const contentMapperService = {
15861655
putTestData,
@@ -1597,4 +1666,5 @@ export const contentMapperService = {
15971666
getExistingGlobalFields,
15981667
getSingleGlobalField,
15991668
getExistingTaxonomies,
1669+
getExistingExtensions,
16001670
};

api/src/services/drupal/taxonomy.service.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import customLogger from '../../utils/custom-logger.utils.js';
66
import { getLogMessage } from '../../utils/index.js';
77
import { MIGRATION_DATA_CONFIG } from '../../constants/index.js';
88

9-
const { DATA, TAXONOMIES_DIR_NAME } = MIGRATION_DATA_CONFIG;
9+
const { DATA, TAXONOMIES_DIR_NAME, TAXONOMIES_FILE_NAME} = MIGRATION_DATA_CONFIG;
1010

1111
interface DrupalTaxonomyTerm {
1212
taxonomy_uid: string; // vid (vocabulary id)
@@ -331,18 +331,34 @@ const saveTaxonomyFiles = async (
331331
};
332332
}
333333

334-
const taxonomiesFilePath = path.join(taxonomiesPath, 'taxonomies.json');
334+
const taxonomiesFilePath = path.join(taxonomiesPath, TAXONOMIES_FILE_NAME);
335+
let mergedTaxonomiesMeta: Record<string, any> = { ...taxonomiesDataObject };
336+
337+
if (fs.existsSync(taxonomiesFilePath)) {
338+
try {
339+
const existingRaw = await fs.promises.readFile(taxonomiesFilePath, 'utf8');
340+
const existing = JSON.parse(existingRaw) as Record<string, any>;
341+
if (existing && typeof existing === 'object' && !Array.isArray(existing)) {
342+
mergedTaxonomiesMeta = { ...existing, ...taxonomiesDataObject };
343+
}
344+
} catch {
345+
mergedTaxonomiesMeta = { ...taxonomiesDataObject };
346+
}
347+
}
348+
335349
await fs.promises.writeFile(
336350
taxonomiesFilePath,
337-
JSON.stringify(taxonomiesDataObject, null, 2),
351+
JSON.stringify(mergedTaxonomiesMeta, null, 2),
338352
'utf8'
339353
);
340354

341355
const consolidatedMessage = getLogMessage(
342356
srcFunc,
343357
`Saved consolidated taxonomies.json with ${
344-
Object.keys(taxonomiesDataObject).length
345-
} vocabularies.`,
358+
Object.keys(mergedTaxonomiesMeta)?.length
359+
} vocabularies (${
360+
Object.keys(taxonomiesDataObject)?.length
361+
} from this run).`,
346362
{}
347363
);
348364
await customLogger(
@@ -393,7 +409,9 @@ export const createTaxonomy = async (
393409
);
394410

395411
// Create taxonomies directory
396-
await fs.promises.mkdir(taxonomiesPath, { recursive: true });
412+
if(!fs.existsSync(taxonomiesPath)){
413+
await fs.promises.mkdir(taxonomiesPath, { recursive: true });
414+
}
397415

398416
const message = getLogMessage(srcFunc, `Exporting taxonomies...`, {});
399417
await customLogger(projectId, destination_stack_id, 'info', message);

api/src/services/extension.service.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import path from "path";
22
import fs from 'fs';
33
import { MIGRATION_DATA_CONFIG, LIST_EXTENSION_UID } from "../constants/index.js";
4+
import { contentMapperService } from "./contentMapper.service.js";
45

56
const {
67
CUSTOM_MAPPER_FILE_NAME,
@@ -27,6 +28,26 @@ const writeExtFile = async ({ destinationStackId, extensionData }: any) => {
2728
console.error("🚀 ~ fs.writeFile ~ err:", writeErr);
2829
}
2930
}
31+
const formatExtensionData = (extension: any, destinationStackId: string) => {
32+
return {
33+
"stackHeaders": { "api_key": destinationStackId },
34+
"urlPath": `/extensions/${extension?.uid}`,
35+
"uid": extension?.uid,
36+
"created_at": extension?.created_at,
37+
"updated_at": extension?.updated_at,
38+
"created_by": extension?.created_by,
39+
"updated_by": extension?.updated_by,
40+
"tags": extension?.tags,
41+
"_version": extension?._version,
42+
"title": extension?.title,
43+
"config": extension?.config,
44+
"type": extension?.type,
45+
"data_type": extension?.data_type,
46+
"multiple": extension?.multiple,
47+
"srcdoc": extension?.srcdoc,
48+
49+
}
50+
}
3051

3152
const getExtension = ({ uid, destinationStackId }: any) => {
3253
if (uid === LIST_EXTENSION_UID) {
@@ -50,8 +71,12 @@ const getExtension = ({ uid, destinationStackId }: any) => {
5071
}
5172
return null;
5273
}
74+
const getExsitingExtension = async ({ existingStackId, token_payload }: any) => {
75+
const result = await contentMapperService.getExistingExtensions({ existingStackId, token_payload});
76+
return result;
77+
}
5378

54-
const createExtension = async ({ destinationStackId }: any) => {
79+
const createExtension = async ({ destinationStackId, existingStackId, token_payload }: any) => {
5580
const extensionPath = path.join(MIGRATION_DATA_CONFIG.DATA, destinationStackId, CUSTOM_MAPPER_FILE_NAME);
5681
const extMapper: any = await fs.promises.readFile(extensionPath, "utf-8").catch(async () => { });
5782
if (extMapper !== undefined) {
@@ -66,6 +91,21 @@ const createExtension = async ({ destinationStackId }: any) => {
6691
}
6792
await writeExtFile({ destinationStackId, extensionData })
6893
}
94+
else{
95+
const existingExtension = await getExsitingExtension({ existingStackId, token_payload });
96+
if (existingExtension && Array?.isArray(existingExtension)) {
97+
const extensionData: any = {};
98+
for await (const extension of existingExtension) {
99+
const extData = formatExtensionData(extension, destinationStackId );
100+
if (extData) {
101+
extensionData[extension?.uid] = extension;
102+
}
103+
}
104+
await writeExtFile({ destinationStackId, extensionData })
105+
106+
}
107+
108+
}
69109
}
70110

71111

api/src/services/migration.service.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -425,15 +425,21 @@ const startTestMigration = async (req: Request): Promise<any> => {
425425
});
426426
await extensionService?.createExtension({
427427
destinationStackId: project?.current_test_stack_id,
428+
existingStackId: project?.destination_stack_id,
429+
token_payload: {
430+
region,
431+
user_id,
432+
is_sso,
433+
},
434+
});
435+
await taxonomyService?.createTaxonomy({
436+
orgId,
437+
projectId,
438+
stackId: project?.destination_stack_id,
439+
current_test_stack_id: project?.current_test_stack_id,
440+
region,
441+
userId: user_id,
428442
});
429-
// await taxonomyService?.createTaxonomy({
430-
// orgId,
431-
// projectId,
432-
// stackId: project?.destination_stack_id,
433-
// current_test_stack_id: project?.current_test_stack_id,
434-
// region,
435-
// userId: user_id,
436-
// });
437443
await globalFieldServie?.createGlobalField({
438444
region,
439445
user_id,
@@ -819,6 +825,12 @@ const startMigration = async (req: Request): Promise<any> => {
819825
});
820826
await extensionService?.createExtension({
821827
destinationStackId: project?.destination_stack_id,
828+
existingStackId: project?.source_stack_id,
829+
token_payload: {
830+
region,
831+
user_id,
832+
is_sso,
833+
},
822834
});
823835
await taxonomyService?.createTaxonomy({
824836
orgId,

api/src/utils/content-type-creator.utils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,7 +1151,12 @@ const mergeTwoCts = async (ct: any, mergeCts: any) => {
11511151
title: mergeCts?.title,
11521152
uid: mergeCts?.uid,
11531153
options: {
1154+
is_page: true,
11541155
"singleton": false,
1156+
title: "title",
1157+
url_pattern: '/:title',
1158+
url_prefix: `/`,
1159+
sub_title: ['url']
11551160
}
11561161
}
11571162

api/tests/unit/controllers/projects.contentMapper.controller.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ describe('projects.contentMapper.controller', () => {
8383
expect(res.status).toHaveBeenCalledWith(200);
8484
});
8585

86+
it('getExistingTaxonomies should default to 200 when status is omitted', async () => {
87+
mockContentMapperService.getExistingTaxonomies.mockResolvedValue({ taxonomies: [] });
88+
89+
await contentMapperController.getExistingTaxonomies(req, res);
90+
91+
expect(res.status).toHaveBeenCalledWith(200);
92+
});
93+
8694
it('getSingleContentTypes should return 201', async () => {
8795
mockContentMapperService.getSingleContentTypes.mockResolvedValue({ title: 'Blog' });
8896

api/tests/unit/services/extension.service.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,16 @@ const {
1616
vi.mock('fs', () => ({
1717
default: { promises: mockFsPromises },
1818
}));
19-
vi.mock('path', () => ({ default: { join: mockPathJoin } }));
19+
vi.mock('path', async (importOriginal) => {
20+
const actual = await importOriginal<typeof import('path')>();
21+
return {
22+
...actual,
23+
default: {
24+
...actual.default,
25+
join: mockPathJoin,
26+
},
27+
};
28+
});
2029
vi.mock('../../../src/constants/index.js', () => ({
2130
MIGRATION_DATA_CONFIG: {
2231
DATA: './cmsMigrationData',

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)