Skip to content

Commit 51d4026

Browse files
authored
Merge pull request #757 from contentstack/bugfix/cmg-698
[CMG-698] - Issue with Wordpress Connector. Case #00044506 for Horizontal
2 parents 77cb8fb + 438388d commit 51d4026

File tree

6 files changed

+235
-119
lines changed

6 files changed

+235
-119
lines changed

.talismanrc

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ fileignoreconfig:
66

77
- filename: api/src/services/migration.service.ts
88
checksum: bd5374b31c0fad4a266bc9affdcf595f71456edb68b075c1fdfba762f7462e38
9-
10-
- filename: ui/src/components/AuditLogs/index.tsx
11-
checksum: e1402f3cd5b96328d5e1d1e5f2a7fa5179858d6133db831b1b96714eb0f8c260
129

1310
- filename: .github/workflows/secrets-scan.yml
1411
ignore_detectors:
@@ -53,7 +50,7 @@ fileignoreconfig:
5350
checksum: f37809ddd67b8ad143d7d9fbb2c305f7c9150a8eec1f3325724fca576c736656
5451

5552
- filename: ui/src/components/AuditLogs/index.tsx
56-
checksum: 51ce05f66ac49023452f200e587389e55c88357102a61e77eb468d0e02b52846
53+
checksum: 8b78783b54935cc1e9721ab2b0b4daf768f5a1dbd96e6594e2eeb1d5fd45d90f
5754

5855
- filename: ui/src/components/ContentMapper/index.tsx
5956
checksum: b5f66e808ecf4461ccb5c4fde937da1e6d9e640a2521d6d85858a22261df6571
@@ -83,13 +80,12 @@ fileignoreconfig:
8380
checksum: 4abcc89c75b7ddca8128fd72faafbb9b159f02611d96325bcd355283074ce287
8481
- filename: ui/src/components/ContentMapper/index.scss
8582
checksum: 46f8fde3b745feba40943f00d8d52714d0f320202c86b62f6c5ded73c2dbcf4c
83+
- filename: api/src/services/migration.service.ts
84+
checksum: abae249cacfab5c67e2d18a645c852649da2339954f26ae0f88c8698bdc016e6
85+
- filename: api/src/services/wordpress.service.ts
86+
checksum: 95e532836194547682e791d0a231fb70e402be99260ac234e808eddb6e80c6af
8687
- filename: ui/src/utilities/constants.ts
8788
checksum: faac1367b4d80e022d4b7a165f9d2ac8bfd428f1a91af849064992ae2b3d6b1f
8889
- filename: ui/src/components/DestinationStack/Actions/LoadLanguageMapper.tsx
8990
checksum: 988d93b93768ef0909305590ab0b89456333b9937ec12828edf23163f8640dfc
90-
fileignoreconfig:
91-
- filename: api/src/services/migration.service.ts
92-
checksum: abae249cacfab5c67e2d18a645c852649da2339954f26ae0f88c8698bdc016e6
93-
- filename: api/src/services/wordpress.service.ts
94-
checksum: 95e532836194547682e791d0a231fb70e402be99260ac234e808eddb6e80c6af
9591
version: "1.0"

api/src/services/wordpress.service.ts

Lines changed: 85 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -2260,19 +2260,22 @@ function limitConcurrency(maxConcurrency: number) {
22602260
}
22612261
const limit = limitConcurrency(5);
22622262

2263-
async function featuredImageMapping(postid: string, post: any, postdata: any) {
2263+
async function featuredImageMapping(postid: string, post: any, postdata: any, assetsSchemaPath: string) {
22642264
try {
2265-
const filePath = path.join(process.cwd(), assetsSave, MIGRATION_DATA_CONFIG.ASSETS_SCHEMA_FILE);
2266-
const fileContent = fs.readFileSync(filePath, 'utf8').trim();
2265+
// **THE FIX: Use the path argument directly, don't use the global variable**
2266+
const fileContent = fs.readFileSync(assetsSchemaPath, 'utf8').trim();
22672267

2268-
if (!fileContent) {
2269-
throw new Error(`File ${filePath} is empty or missing`);
2270-
}
2271-
const assetsId = JSON?.parse(fileContent);
2272-
if (!post["wp:postmeta"] || !assetsId) return;
2268+
if (!fileContent) {
2269+
// File is empty, so no assets have been processed. Nothing to map.
2270+
return postdata;
2271+
}
22732272

2274-
const postmetaArray = Array.isArray(post["wp:postmeta"]) ? post["wp:postmeta"]
2275-
: [post["wp:postmeta"]];
2273+
const assetsId = JSON.parse(fileContent);
2274+
if (!post["wp:postmeta"] || !assetsId) {
2275+
return postdata;
2276+
}
2277+
2278+
const postmetaArray = Array.isArray(post["wp:postmeta"]) ? post["wp:postmeta"] : [post["wp:postmeta"]];
22762279

22772280
const assetsDetails = postmetaArray
22782281
.filter((meta) => meta["wp:meta_key"] === "_thumbnail_id")
@@ -2283,14 +2286,17 @@ async function featuredImageMapping(postid: string, post: any, postdata: any) {
22832286
(asset: any) => asset.uid === attachmentid
22842287
);
22852288
})
2286-
.filter(Boolean); // Filter out undefined matches
2289+
.filter(Boolean);
22872290

2288-
if (assetsDetails?.length > 0) {
2291+
if (assetsDetails.length > 0 && postdata[postid]) {
2292+
// Assign the found asset object to the 'featured_image' field
22892293
postdata[postid]["featured_image"] = assetsDetails[0];
22902294
}
22912295
return postdata;
22922296
} catch (error) {
2293-
console.error(error);
2297+
console.error("Error during featured image mapping:", error);
2298+
// Return the original postdata if an error occurs to avoid losing the entry
2299+
return postdata;
22942300
}
22952301
}
22962302

@@ -2383,96 +2389,67 @@ async function processChunkData(
23832389
isLastChunk: boolean,
23842390
contenttype: any,
23852391
authorsFilePath: string,
2386-
referencesFilePath: string
2392+
referencesFilePath: string,
2393+
assetsSchemaPath: string
23872394
) {
2388-
const postdata: any = {};
2389-
const formattedPosts: any = {};
2390-
let postdataCombined = {};
2395+
let postdataCombined: Record<string, any> = {};
23912396

23922397
try {
2393-
const writePromises = [];
2398+
// This filter is good, it correctly selects only post-like items.
2399+
const filteredChunk = chunkData?.filter((item: any) =>
2400+
item["wp:post_type"] !== "page" &&
2401+
item["wp:post_type"] !== "attachment" &&
2402+
["publish", "inherit", "draft"]?.includes(item["wp:status"])
2403+
);
23942404

2395-
const filteredChunk = chunkData?.filter((item:any) => item["wp:post_type"] === "post" && ["publish", "inherit", "draft"]?.includes(item["wp:status"]));
2405+
// The main loop processes one item at a time.
23962406
for (const data of filteredChunk) {
2397-
writePromises.push(
2398-
limit(async () => {
2399-
const { postCategories, postTags, postTerms } = extractPostCategories(data["category"], referencesFilePath);
2400-
2401-
2402-
// Extract author
2403-
const postAuthor = extractPostAuthor(data["dc:creator"], authorsFilePath);
2404-
2405-
const dom = new JSDOM(
2406-
data["content:encoded"]
2407-
.replace(/<!--.*?-->/g, "")
2408-
.replace(/&lt;!--?\s+\/?wp:.*?--&gt;/g, ""),
2409-
{ virtualConsole }
2410-
);
2411-
const htmlDoc = dom.window.document.querySelector("body");
2412-
const jsonValue = htmlToJson(htmlDoc);
2413-
2414-
// Format date safely
2415-
let postDate: string | null = null;
2416-
try {
2417-
const parsed = new Date(data["wp:post_date_gmt"]);
2418-
if (!isNaN(parsed.getTime())) {
2419-
postDate = parsed.toISOString();
2420-
}
2421-
} catch (error) {
2422-
console.error(`Error parsing date for post ${data["wp:post_id"]}:`, error);
2423-
}
2424-
2425-
const base = blog_base_url?.split("/")?.filter(Boolean);
2426-
const blogname = base[base?.length - 1];
2427-
const url = data["link"]?.split(blogname)[1];
2428-
const uid = `posts_${data["wp:post_id"]}`;
2429-
const customId = idCorrector(uid);
2430-
2431-
postdata[customId] = {
2432-
title: data["title"] || `Posts - ${data["wp:post_id"]}`,
2433-
uid: customId,
2434-
url: url,
2435-
date: postDate,
2436-
full_description: jsonValue,
2437-
excerpt: (data["excerpt:encoded"] || "")
2438-
.replace(/<!--.*?-->/g, "")
2439-
.replace(/&lt;!--?\s+\/?wp:.*?--&gt;/g, ""),
2440-
author: postAuthor,
2441-
category: postCategories,
2442-
terms: postTerms,
2443-
tag: postTags,
2444-
featured_image: '',
2445-
publish_details: [],
2446-
};
2447-
2448-
for (const [key, value] of Object.entries(postdata as { [key: string]: any })) {
2449-
const customId = idCorrector(value?.uid);
2450-
formattedPosts[customId] = {
2451-
...formattedPosts[customId],
2452-
uid: customId,
2453-
...(await mapContentTypeToEntry(contenttype, value)),
2454-
};
2455-
formattedPosts[customId].publish_details = [];
2456-
}
2407+
const customId = idCorrector(`posts_${data["wp:post_id"]}`);
2408+
2409+
// Step 1: Resolve all references for the CURRENT post
2410+
const { postCategories, postTags, postTerms } = extractPostCategories(data["category"], referencesFilePath);
2411+
const postAuthor = extractPostAuthor(data["dc:creator"], authorsFilePath);
2412+
const jsonValue = htmlToJson(new JSDOM(data["content:encoded"].replace("//g", "").replace(/&lt;!--?\s+\/?wp:.*?--&gt;/g, "")).window.document.querySelector("body"));
2413+
let postDate = null;
2414+
if (data["wp:post_date_gmt"] && !data["wp:post_date_gmt"].startsWith("0000")) {
2415+
postDate = new Date(data["wp:post_date_gmt"]).toISOString();
2416+
}
24572417

2458-
const formattedPostsWithImage = await featuredImageMapping(
2459-
`posts_${data["wp:post_id"]}`,
2460-
data,
2461-
formattedPosts
2462-
);
2418+
// Step 2: Create a temporary object with all raw data for the CURRENT post
2419+
const rawPostData = {
2420+
title: data["title"] || `Posts - ${data["wp:post_id"]}`,
2421+
uid: customId,
2422+
url: data["link"]?.split(blog_base_url?.split("/").filter(Boolean).pop())[1],
2423+
date: postDate,
2424+
full_description: jsonValue,
2425+
excerpt: (data["excerpt:encoded"] || "").replace("//g", "").replace(/&lt;!--?\s+\/?wp:.*?--&gt;/g, ""),
2426+
author: postAuthor,
2427+
category: postCategories,
2428+
terms: postTerms,
2429+
tag: postTags,
2430+
featured_image: '',
2431+
publish_details: [],
2432+
};
2433+
2434+
// Step 3: Create the final, formatted post object using mapContentTypeToEntry
2435+
let formattedPost = {
2436+
uid: customId,
2437+
...(await mapContentTypeToEntry(contenttype, rawPostData)),
2438+
publish_details: [],
2439+
};
24632440

2464-
postdataCombined = { ...postdataCombined, ...formattedPostsWithImage };
2465-
})
2441+
// Step 4: Map the featured image for ONLY the CURRENT post
2442+
const formattedPostWithImage = await featuredImageMapping(
2443+
customId, // Use the corrected ID
2444+
data,
2445+
{ [customId]: formattedPost }, // Pass an object with only the current post
2446+
assetsSchemaPath
24662447
);
2467-
}
2468-
2469-
const results: any = await Promise.all(writePromises);
2470-
const allSuccess = results.every(
2471-
(result: any) => typeof result !== "object" || result?.success
2472-
);
2473-
2474-
if (isLastChunk && allSuccess) {
2475-
console.info("last data");
2448+
2449+
// Step 5: Add the final, complete post to the combined results
2450+
if (formattedPostWithImage && formattedPostWithImage[customId]) {
2451+
postdataCombined[customId] = formattedPostWithImage[customId];
2452+
}
24762453
}
24772454

24782455
return postdataCombined;
@@ -2498,6 +2475,9 @@ async function extractPosts( packagePath: string, destinationStackId: string, pr
24982475
referencesFolder = path.join(MIGRATION_DATA_CONFIG.DATA, destinationStackId, MIGRATION_DATA_CONFIG.REFERENCES_DIR_NAME);
24992476
const referencesFilePath = path.join(referencesFolder, MIGRATION_DATA_CONFIG.REFERENCES_FILE_NAME);
25002477

2478+
assetsSave = path.join(MIGRATION_DATA_CONFIG.DATA, destinationStackId, MIGRATION_DATA_CONFIG.ASSETS_DIR_NAME);
2479+
const assetsSchemaPath = path.join(assetsSave, MIGRATION_DATA_CONFIG.ASSETS_SCHEMA_FILE);
2480+
25012481
const alldata: any = await fs.promises.readFile(packagePath, "utf8");
25022482
const alldataParsed = JSON.parse(alldata);
25032483
blog_base_url =
@@ -2515,7 +2495,7 @@ async function extractPosts( packagePath: string, destinationStackId: string, pr
25152495
const chunkData = JSON.parse(data);
25162496
const isLastChunk = filename === lastChunk;
25172497

2518-
const chunkPostData = await processChunkData(chunkData, filename, isLastChunk, contenttype, authorsFilePath, referencesFilePath);
2498+
const chunkPostData = await processChunkData(chunkData, filename, isLastChunk, contenttype, authorsFilePath, referencesFilePath, assetsSchemaPath);
25192499
postdataCombined = { ...postdataCombined, ...chunkPostData };
25202500

25212501
const seenTitles = new Map();
@@ -2656,7 +2636,8 @@ const extractPageParent = (parentId?: string): any[] => {
26562636
async function handlePagesChunkData(
26572637
items: any[],
26582638
contenttype: any,
2659-
authorsFilePath: string
2639+
authorsFilePath: string,
2640+
assetsSchemaPath: string
26602641
): Promise<Record<string, any>> {
26612642
const pageDataCombined: Record<string, any> = {};
26622643

@@ -2666,18 +2647,16 @@ async function handlePagesChunkData(
26662647

26672648
for (const item of items) {
26682649
if (!allowedPageTypes.includes(item['wp:post_type']) || !allowedStatuses.includes(item['wp:status'])) {
2669-
continue; // Skip items that aren't valid pages
2650+
continue;
26702651
}
26712652

26722653
const uid = `pages_${item['wp:post_id']}`;
26732654
const customId = idCorrector(uid);
26742655

2675-
// 1. Resolve references for the current item
26762656
const authorRef = extractPageAuthor(item['dc:creator'], authorsFilePath);
26772657
const parentRef = extractPageParent(item['wp:post_parent']);
26782658
const body = htmlToJson(new JSDOM(item["content:encoded"].replace("//g", "").replace(/&lt;!--?\s+\/?wp:.*?--&gt;/g, "")).window.document.querySelector('body'));
26792659

2680-
// 2. Create a temporary object with all the raw data
26812660
const rawPageData = {
26822661
uid: customId,
26832662
title: item['title'] || 'Untitled',
@@ -2701,9 +2680,10 @@ async function handlePagesChunkData(
27012680

27022681

27032682
const formattedPageWithImage = await featuredImageMapping(
2704-
`pages_${item["wp:post_id"]}`,
2683+
customId,
27052684
item,
2706-
{ [customId]: formattedPage }
2685+
{ [customId]: formattedPage },
2686+
assetsSchemaPath
27072687
);
27082688

27092689

@@ -2743,6 +2723,8 @@ async function extractPages(
27432723
await startingDirPages(ct, master_locale, project?.locales);
27442724
const authorsCtName = keyMapper?.["authors"] || MIGRATION_DATA_CONFIG.AUTHORS_DIR_NAME;
27452725
const authorsFilePath = path.join(entrySave, authorsCtName, master_locale, `${master_locale}.json`);
2726+
assetsSave = path.join(MIGRATION_DATA_CONFIG.DATA, destinationStackId, MIGRATION_DATA_CONFIG.ASSETS_DIR_NAME);
2727+
const assetsSchemaPath = path.join(assetsSave, MIGRATION_DATA_CONFIG.ASSETS_SCHEMA_FILE);
27462728
const alldata: any = await fs.promises.readFile(packagePath, "utf8");
27472729
const alldataParsed = JSON.parse(alldata);
27482730
blog_base_url =
@@ -2765,7 +2747,7 @@ async function extractPages(
27652747

27662748
const isLastChunk = filename === lastChunk;
27672749

2768-
const chunkPages = await handlePagesChunkData(chunkData, contenttype, authorsFilePath);
2750+
const chunkPages = await handlePagesChunkData(chunkData, contenttype, authorsFilePath, assetsSchemaPath);
27692751

27702752
console.info(
27712753
`${filename} → Mapped entries: ${Object.keys(chunkPages).length}`

ui/src/components/LogScreen/MigrationLogViewer.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ const MigrationLogViewer = ({ serverPath }: LogsType) => {
208208
notificationContent: { text: message },
209209
notificationProps: {
210210
position: 'bottom-center',
211-
hideProgressBar: false
211+
hideProgressBar: true
212212
},
213213
type: 'success'
214214
});
@@ -242,7 +242,7 @@ const MigrationLogViewer = ({ serverPath }: LogsType) => {
242242
{newMigrationData?.migration_execution?.migrationCompleted ? (
243243
<div>
244244
<div className="log-entry text-center">
245-
<div className="log-message">
245+
<div className="log-message generic-log-message">
246246
Migration Execution process is completed in the selected stack
247247
<Link href={stackLink} target="_blank" className="ml-5">
248248
<strong>{newMigrationData?.stackDetails?.label}</strong>
@@ -277,7 +277,7 @@ const MigrationLogViewer = ({ serverPath }: LogsType) => {
277277
style={logStyles[level || ''] || logStyles.info}
278278
className="log-entry text-center"
279279
>
280-
<div className="log-message">
280+
<div className="log-message generic-log-message">
281281
Migration has already done in selected stack. Please create a new project.
282282
</div>
283283
</div>
@@ -292,7 +292,7 @@ const MigrationLogViewer = ({ serverPath }: LogsType) => {
292292
style={logStyles[level || ''] || logStyles.info}
293293
className="log-entry text-center"
294294
>
295-
<div className="log-message">{message}</div>
295+
<div className="log-message generic-log-message">{message}</div>
296296
</div>
297297
) : (
298298
<div

ui/src/components/LogScreen/index.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,5 +86,8 @@
8686
.log-message {
8787
flex-grow: 1;
8888
padding: 0 $px-16;
89+
&.generic-log-message {
90+
padding: $px-16;
91+
}
8992
}
9093
}

ui/src/components/LogScreen/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ const TestMigrationLogViewer = ({ serverPath, sendDataToParent, projectId }: Log
187187
notificationContent: { text: message },
188188
notificationProps: {
189189
position: 'bottom-center',
190-
hideProgressBar: false
190+
hideProgressBar: true
191191
},
192192
type: 'success'
193193
});
@@ -260,7 +260,7 @@ const TestMigrationLogViewer = ({ serverPath, sendDataToParent, projectId }: Log
260260
{migratedStack?.isMigrated ? (
261261
<div>
262262
<div className="log-entry text-center">
263-
<div className="log-message">
263+
<div className="log-message generic-log-message">
264264
Test Migration is completed for stack{' '}
265265
<Link href={newMigrationData?.test_migration?.stack_link} target="_blank">
266266
<strong>{migratedStack?.stackName}</strong>
@@ -293,7 +293,7 @@ const TestMigrationLogViewer = ({ serverPath, sendDataToParent, projectId }: Log
293293
style={logStyles[level || ''] || logStyles.info}
294294
className="log-entry text-center"
295295
>
296-
<div className="log-message">{message}</div>
296+
<div className="log-message generic-log-message">{message}</div>
297297
</div>
298298
) : (
299299
<div

0 commit comments

Comments
 (0)