diff --git a/.DS_Store b/.DS_Store index ef3e9916d..711cbae9a 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/api/.DS_Store b/api/.DS_Store index cb7a19b95..bda49881c 100644 Binary files a/api/.DS_Store and b/api/.DS_Store differ diff --git a/api/.gitignore b/api/.gitignore index b2104e9d2..6179ebf9a 100644 --- a/api/.gitignore +++ b/api/.gitignore @@ -17,6 +17,9 @@ dist/ # Mono auto generated files mono_crash.* +#MAC DS_Store File +/api/.DS_Store + # Build results [Dd]ebug/ [Dd]ebugPublic/ diff --git a/api/production.env b/api/production.env deleted file mode 100644 index ceeb1a0bd..000000000 --- a/api/production.env +++ /dev/null @@ -1,3 +0,0 @@ -APP_TOKEN_KEY=MIGRATION_V2 -PORT=5001 - diff --git a/api/src/.DS_Store b/api/src/.DS_Store index 24cf0c64c..79b1a1dc6 100644 Binary files a/api/src/.DS_Store and b/api/src/.DS_Store differ diff --git a/api/src/controllers/migration.controller.ts b/api/src/controllers/migration.controller.ts index 9f46dc4ac..7c2c1abe4 100644 --- a/api/src/controllers/migration.controller.ts +++ b/api/src/controllers/migration.controller.ts @@ -1,5 +1,5 @@ import { Request, Response } from "express"; -import { migrationService } from "../services/migration.service.js"; +import { migrationService } from "../services/migration.service.js" /** * Creates a test stack. @@ -55,10 +55,22 @@ const getLogs = async (req: Request, res: Response): Promise => { res.status(200).json(resp); }; +const saveLocales = async (req:Request, res: Response):Promise =>{ + const resp = await migrationService.createSourceLocales(req) + res.status(200).json(resp); +} + +const saveMappedLocales = async (req:Request, res:Response):Promise =>{ + const resp = await migrationService.updateLocaleMapper(req); + res.status(200).json(resp); +} + export const migrationController = { createTestStack, deleteTestStack, startTestMigration, startMigration, getLogs, + saveLocales, + saveMappedLocales }; diff --git a/api/src/routes/migration.routes.ts b/api/src/routes/migration.routes.ts index e22a87cf0..fdcefaee6 100644 --- a/api/src/routes/migration.routes.ts +++ b/api/src/routes/migration.routes.ts @@ -65,5 +65,31 @@ router.get( ) +/** + * Route for updating the source locales from legacy CMS + * @route POST /validator + * @group Migration + * @param {string} projectID - Current project ID + * @body {Object} locales - Fetched Locales + * @returns {Promise} - A promise which resolves when the locales are updated in the DB + */ +router.post( + "/localeMapper/:projectId", + asyncRouter(migrationController.saveLocales) +) + +/** + * Route for updating the mapped locales by user + * @route POST /validator + * @group Migration + * @param {string} projectID - Current project ID + * @body {Object} locales - Mapped Locales + * @returns {Promise} - A promise which resolves when the locales are updated in the DB + */ +router.post( + "/updateLocales/:projectId", + asyncRouter(migrationController.saveMappedLocales) +) + export default router; diff --git a/api/src/services/.DS_Store b/api/src/services/.DS_Store new file mode 100644 index 000000000..920eccd28 Binary files /dev/null and b/api/src/services/.DS_Store differ diff --git a/api/src/services/migration.service.ts b/api/src/services/migration.service.ts index 30563a446..15ea20cdc 100644 --- a/api/src/services/migration.service.ts +++ b/api/src/services/migration.service.ts @@ -25,7 +25,6 @@ import { extensionService } from "./extension.service.js"; - /** * Creates a test stack. * @@ -411,10 +410,111 @@ const getLogs = async (req: Request): Promise => { } +/** + * @description - This function takes all the fetched locales from the exported data of the legacy CMS and stores/updates them in the project.json in DB + * + * @param req - A request body object with fetched locales [] as payload along with project ID in params + * @return - void + * @throws Exception if the project ID is invalid or the when the path to project.json is incorrect + */ +export const createSourceLocales = async (req: Request) => { + + const projectFilePath = path.join(process.cwd(), 'database', 'project.json'); // Adjusted path to project.json + const projectId = req.params.projectId; + + const locales = req.body.locale + + try { + // Check if the project.json file exists + if (!fs.existsSync(projectFilePath)) { + console.error(`project.json not found at ${projectFilePath}`); + throw new Error(`project.json not found.`); + } + + // Find the project with the specified projectId + const project: any = ProjectModelLowdb.chain.get("projects").find({ id: projectId }).value(); + if (project) { + const index = ProjectModelLowdb.chain.get("projects").findIndex({ id: projectId }).value(); + if (index > -1) { + + ProjectModelLowdb.update((data: any) => { + data.projects[index].source_locales = locales; + }); + } // Write back the updated projects + } else { + logger.error(`Project with ID: ${projectId} not found`,{ + status: HTTP_CODES?.NOT_FOUND, + message: HTTP_TEXTS?.INVALID_ID + }) + } + } catch (err: any) { + console.error("🚀 ~ createSourceLocales ~ err:", err?.response?.data ?? err, err) + logger.warn('Bad Request', { + status: HTTP_CODES?.BAD_REQUEST, + message: HTTP_TEXTS?.INTERNAL_ERROR, + }); + throw new ExceptionFunction( + err?.message || HTTP_TEXTS.INTERNAL_ERROR, + err?.statusCode || err?.status || HTTP_CODES.SERVER_ERROR + ); + } +} + + +/** + * @description - Function retrieves the mapped locales and updates them in the project.json in DB + * @param req - A request body object with mapped locales as payload and project ID in the params + * @return - void + * @throws Exception if the project ID is invalid or the when the path to project.json is incorrect + */ +export const updateLocaleMapper = async (req:Request) =>{ + const mapperObject = req.body.mapper; + const projectFilePath = path.join(process.cwd(), 'database', 'project.json'); // Adjusted path to project.json + const projectId = req.params.projectId; + + try { + // Check if the project.json file exists + if (!fs.existsSync(projectFilePath)) { + console.error(`project.json not found at ${projectFilePath}`); + throw new Error(`project.json not found.`); + } + + // Find the project with the specified projectId + const project: any = ProjectModelLowdb.chain.get("projects").find({ id: projectId }).value(); + if (project) { + const index = ProjectModelLowdb.chain.get("projects").findIndex({ id: projectId }).value(); + if (index > -1) { + ProjectModelLowdb.update((data: any) => { + data.projects[index].master_locale = mapperObject?.master_locale; + data.projects[index].locales = mapperObject?.locales; + }); + } // Write back the updated projects + } else { + logger.error(`Project with ID: ${projectId} not found`,{ + status: HTTP_CODES?.NOT_FOUND, + message: HTTP_TEXTS?.INVALID_ID + }) + } + } catch (err: any) { + console.error("🚀 ~ createSourceLocales ~ err:", err?.response?.data ?? err, err) + logger.warn('Bad Request', { + status: HTTP_CODES?.BAD_REQUEST, + message: HTTP_TEXTS?.INTERNAL_ERROR, + }); + throw new ExceptionFunction( + err?.message || HTTP_TEXTS.INTERNAL_ERROR, + err?.statusCode || err?.status || HTTP_CODES.SERVER_ERROR + ); + } + +} + export const migrationService = { createTestStack, deleteTestStack, startTestMigration, startMigration, getLogs, + createSourceLocales, + updateLocaleMapper }; diff --git a/api/src/services/sitecore.service.ts b/api/src/services/sitecore.service.ts index 6d403a3d6..9cb5737b5 100644 --- a/api/src/services/sitecore.service.ts +++ b/api/src/services/sitecore.service.ts @@ -9,8 +9,10 @@ import { orgService } from './org.service.js'; import { getLogMessage } from '../utils/index.js'; import customLogger from '../utils/custom-logger.utils.js'; + const append = "a"; + const baseDirName = MIGRATION_DATA_CONFIG.DATA const { ENTRIES_DIR_NAME, diff --git a/api/src/services/wordpress.service.ts b/api/src/services/wordpress.service.ts index 668c2a68a..cd8b4b52f 100644 --- a/api/src/services/wordpress.service.ts +++ b/api/src/services/wordpress.service.ts @@ -10,12 +10,14 @@ import customLogger from "../utils/custom-logger.utils.js"; import { getLogMessage } from "../utils/index.js"; import { Advanced } from "../models/FieldMapper.js"; -const { JSDOM } = jsdom; +const { JSDOM } = jsdom; const virtualConsole = new jsdom.VirtualConsole(); // Get the current file's path const __filename = fileURLToPath(import.meta.url); + + // Get the current directory const __dirname = path.dirname(__filename); diff --git a/index.js b/index.js index 92e49eb6f..8993572b9 100644 --- a/index.js +++ b/index.js @@ -33,6 +33,7 @@ const envContents = { 'Upload-API': uploadAPIEnvContent, }; + // Function to create env files const createEnvFiles = () => { // Loop through each key in the envFilePaths object diff --git a/package-lock.json b/package-lock.json index ae8a316bf..1ed2d4fab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -121,6 +121,16 @@ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", "dev": true }, + "node_modules/@types/xml2js": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz", + "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -828,6 +838,13 @@ "node": ">=8" } }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, "node_modules/validate-branch-name": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/validate-branch-name/-/validate-branch-name-1.3.1.tgz", diff --git a/package.json b/package.json index 6b6a76662..742eff064 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "husky": "^4.3.8", "prettier": "^2.4.1", "rimraf": "^3.0.2", - "validate-branch-name": "^1.3.0" + "validate-branch-name": "^1.3.0", + "xml2js":"^0.4.14" }, "husky": { "hooks": {} diff --git a/ui/.env.local b/ui/.env.local deleted file mode 100644 index 9be1549ab..000000000 --- a/ui/.env.local +++ /dev/null @@ -1,6 +0,0 @@ -REACT_APP_WEBSITE_BASE_URL="http://localhost:3000/" -REACT_APP_BASE_API_URL="http://localhost:5001/" -REACT_APP_API_VERSION=v2 -REACT_APP_HOST="http://localhost:3000" -REACT_APP_UPLOAD_SERVER="http://localhost:4002/" -REACT_APP_OFFLINE_CMS=true \ No newline at end of file diff --git a/upload-api/migration-contentful/config/index.json b/upload-api/migration-contentful/config/index.json index 9d1da5482..3583969a2 100644 --- a/upload-api/migration-contentful/config/index.json +++ b/upload-api/migration-contentful/config/index.json @@ -5,5 +5,6 @@ }, "fileName": "en-us.json" + } \ No newline at end of file diff --git a/upload-api/migration-sitecore/constants/index.js b/upload-api/migration-sitecore/constants/index.js index d355f8175..442b7035f 100644 --- a/upload-api/migration-sitecore/constants/index.js +++ b/upload-api/migration-sitecore/constants/index.js @@ -42,6 +42,8 @@ const MIGRATION_DATA_CONFIG = { EXPORT_INFO_FILE: "export-info.json" } + + module.exports = { MIGRATION_DATA_CONFIG diff --git a/upload-api/migration-sitecore/index.js b/upload-api/migration-sitecore/index.js index a4cd9a931..0f5977b86 100644 --- a/upload-api/migration-sitecore/index.js +++ b/upload-api/migration-sitecore/index.js @@ -1,15 +1,18 @@ // eslint-disable-next-line @typescript-eslint/no-var-requires -const contentTypes = require("./libs/contenttypes.js"); +import contentTypes from "./libs/contenttypes.js"; // eslint-disable-next-line @typescript-eslint/no-var-requires -const ExtractConfiguration = require("./libs/configuration.js") +import ExtractConfiguration from "./libs/configuration.js" // eslint-disable-next-line @typescript-eslint/no-var-requires -const reference = require("./libs/reference.js"); +import reference from "./libs/reference.js"; // eslint-disable-next-line @typescript-eslint/no-var-requires -const ExtractFiles = require("./libs/convert.js") +import ExtractFiles from "./libs/convert.js" -module.exports = { +import {findAndExtractLanguages} from './libs/extractLocales.js' + +export { contentTypes, ExtractConfiguration, reference, - ExtractFiles + ExtractFiles, + findAndExtractLanguages } diff --git a/upload-api/migration-sitecore/libs/extractLocales.js b/upload-api/migration-sitecore/libs/extractLocales.js new file mode 100644 index 000000000..8526f1389 --- /dev/null +++ b/upload-api/migration-sitecore/libs/extractLocales.js @@ -0,0 +1,29 @@ +import fs from 'fs' +import path from 'path' + +const uniqueLanguages = new Set(); // Define uniqueLanguages globally or pass it as a parameter + +export const findAndExtractLanguages = (dir) => { + const items = fs.readdirSync(dir, { withFileTypes: true }); + + for (const item of items) { + const fullPath = path.join(dir, item.name); + + if (item.isDirectory()) { + findAndExtractLanguages(fullPath); // Proper recursion + } else if (item.isFile() && item.name === "data.json.json") { + try { + const rawData = fs.readFileSync(fullPath, "utf8"); + const jsonData = JSON.parse(rawData); + const language = jsonData?.item?.$?.language; + + if (language) { + uniqueLanguages.add(language); + } + } catch (error) { + console.error(`Error reading ${fullPath}:`, error.message); + } + } + } + return uniqueLanguages; +}; diff --git a/upload-api/migration-wordpress/config/index.json b/upload-api/migration-wordpress/config/index.json index 094e3d5b9..454ee3a21 100644 --- a/upload-api/migration-wordpress/config/index.json +++ b/upload-api/migration-wordpress/config/index.json @@ -46,9 +46,9 @@ "dirName": "posts", "fileName": "en-us.json", "masterfile": "posts.json" - } - } - + + } + } } diff --git a/upload-api/src/config/index.ts b/upload-api/src/config/index.ts index 3d9d5784d..540ea078e 100644 --- a/upload-api/src/config/index.ts +++ b/upload-api/src/config/index.ts @@ -2,7 +2,7 @@ export default { plan: { dropdown: { optionLimit: 100 } }, - cmsType: 'Wordpress', + cmsType: 'Sitecore', isLocalPath: true, awsData: { awsRegion: 'us-east-2', @@ -12,8 +12,6 @@ export default { bucketName: 'migartion-test', buketKey: 'project/package 45.zip' }, - - - localPath: '/home/gaurishn/Documents/contentstack/sitetitle.xml' //package 45.zip' + localPath: '/Users/shobhit.upadhyay/Downloads/package 47.zip' //package 45.zip' // localPath: '/Users/umesh.more/Documents/ui-migration/migration-v2-node-server/upload-api/extracted_files/package 45.zip' }; diff --git a/upload-api/src/constants/index.ts b/upload-api/src/constants/index.ts index b1a7d5853..6f2ccf3f5 100644 --- a/upload-api/src/constants/index.ts +++ b/upload-api/src/constants/index.ts @@ -22,7 +22,8 @@ export const HTTP_TEXTS = { VALIDATION_SUCCESSFULL: ' File validated successfully.', ZIP_FILE_SAVE: 'Issue While Saving Zip File.', XML_FILE_SAVE: 'Issue While Saving XML File.', - MAPPER_SAVED: 'Mapping process completed successfull.' + MAPPER_SAVED: 'Mapping process completed successfull.', + LOCALE_SAVED: 'Source locales stored successfully' }; export const HTTP_RESPONSE_HEADERS = { diff --git a/upload-api/src/controllers/sitecore/index.ts b/upload-api/src/controllers/sitecore/index.ts index 8e71af502..9cfe5d1c8 100644 --- a/upload-api/src/controllers/sitecore/index.ts +++ b/upload-api/src/controllers/sitecore/index.ts @@ -6,7 +6,7 @@ import logger from "../../utils/logger"; import { HTTP_CODES, HTTP_TEXTS, MIGRATION_DATA_CONFIG } from "../../constants"; // eslint-disable-next-line @typescript-eslint/no-var-requires -const { contentTypes, ExtractConfiguration, reference, ExtractFiles } = require('migration-sitecore'); +const { contentTypes, ExtractConfiguration, reference, ExtractFiles, findAndExtractLanguages } = require('migration-sitecore'); const { CONTENT_TYPES_DIR_NAME, @@ -17,6 +17,11 @@ const { const createSitecoreMapper = async (filePath: string = "", projectId: string | string[], app_token: string | string[], affix: string | string[], config: object) => { try { const newPath = path.join(filePath, 'items'); + + const localeData = await findAndExtractLanguages(path.join(filePath, 'items','master','sitecore','content')); + console.log("Fetched Locales: ", localeData); + + await ExtractFiles(newPath); await ExtractConfiguration(newPath); await contentTypes(newPath, affix, config); @@ -38,7 +43,6 @@ const createSitecoreMapper = async (filePath: string = "", projectId: string | s fieldMapping.contentTypes.push(element); } } - // console.log("🚀 ~ createSitecoreMapper ~ fieldMapping:", fieldMapping) const config = { method: 'post', maxBodyLength: Infinity, @@ -57,6 +61,28 @@ const createSitecoreMapper = async (filePath: string = "", projectId: string | s message: HTTP_TEXTS?.MAPPER_SAVED, }); } + + const mapperConfig = { + method: 'post', + maxBodyLength: Infinity, + url: `${process.env.NODE_BACKEND_API}/v2/migration/localeMapper/${projectId}`, + headers: { + app_token, + 'Content-Type': 'application/json' + }, + data: { + locale:Array.from(localeData) + }, + }; + + const mapRes = await axios.request(mapperConfig) + if(mapRes?.status==200){ + logger.info('Legacy CMS', { + status: HTTP_CODES?.OK, + message: HTTP_TEXTS?.LOCALE_SAVED, + }); + } + } } catch (err: any) { console.error("🚀 ~ createSitecoreMapper ~ err:", err?.response?.data ?? err)