Skip to content
Merged

Dev #337

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2405c2c
intial code
umeshmore45 Oct 9, 2024
6001788
intial code
umeshmore45 Oct 11, 2024
81130af
[CMG-347] - Test Migration | when started test migration and when its…
sayalijoshi27 Oct 15, 2024
374f743
refactor:added try-catch block for error handling
AishDani Oct 16, 2024
d0cec0e
Merge branch 'dev' of github.com:contentstack/migration-v2-node-serve…
AishDani Oct 16, 2024
e17c202
refactor:resolved sonarlint warnings
AishDani Oct 16, 2024
d623f0d
refactor:reverted the hadleSelected entries function of mapper
AishDani Oct 16, 2024
4cbf389
Merge pull request #327 from contentstack/feature/dropdown-field-choices
v1shalpatel Oct 16, 2024
492fe41
Added try-catch block to api calls in Content Mapper, Test Migration …
sayalijoshi27 Oct 16, 2024
e8b05d7
Merge branch 'dev' of https://github.com/contentstack/migration-v2-no…
sayalijoshi27 Oct 16, 2024
691e514
Code refactor
sayalijoshi27 Oct 16, 2024
8295a92
Merge pull request #328 from contentstack/feature/test-migration
v1shalpatel Oct 16, 2024
7e28402
refactor code
umeshmore45 Oct 18, 2024
8e3957f
refactor:resolved undefined content-mapper bug and steps disability o…
AishDani Oct 21, 2024
1636b15
Merge branch 'dev' into feature/logger-new
umeshmore45 Oct 21, 2024
8c3b755
Merge pull request #330 from contentstack/feature/logger-new
sayalijoshi27 Oct 21, 2024
3fb6068
refactor:refactored the logic for disabling the save and continue button
AishDani Oct 21, 2024
28f1199
Merge branch 'dev' of github.com:contentstack/migration-v2-node-serve…
AishDani Oct 21, 2024
df9c39f
Merge pull request #331 from contentstack/feature/dropdown-field-choices
sayalijoshi27 Oct 22, 2024
c9eb883
Migration Execution api integration done
sayalijoshi27 Oct 22, 2024
37a0c8c
Conflict resolved
sayalijoshi27 Oct 22, 2024
faa793a
refactor:removed unused summary components from legacy and destinatio…
AishDani Oct 22, 2024
3f48eac
refactor:resolved css of progressBar of upload file
AishDani Oct 22, 2024
624346f
dev merged
AishDani Oct 22, 2024
4755839
Merge pull request #333 from contentstack/feature/dropdown-field-choices
v1shalpatel Oct 23, 2024
1eefa72
Merge branch 'dev' of https://github.com/contentstack/migration-v2-no…
sayalijoshi27 Oct 23, 2024
e2fd621
Start button disabled after migration started
sayalijoshi27 Oct 24, 2024
fcf7257
Merge pull request #334 from contentstack/feature/test-migration
v1shalpatel Oct 24, 2024
eb708ec
refactor:resolved steps rendering issue when there is loader and gett…
AishDani Oct 25, 2024
b773de5
Merge branch 'dev' of github.com:contentstack/migration-v2-node-serve…
AishDani Oct 25, 2024
ca7389b
Merge pull request #335 from contentstack/feature/dropdown-field-choices
v1shalpatel Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/src/config/prod.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ export const prodConfig = {
GCP_NA: "https://gcp-na-app.contentstack.com/#!",
},
LOG_FILE_PATH:
process.platform === "win32" ? ".\\combine.log" : "./combine.log", // Replace with the actual path to your log file
process.platform === "win32" ? ".\\sample.log" : "./sample.log", // Replace with the actual path to your log file
};
2 changes: 2 additions & 0 deletions api/src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ export const HTTP_TEXTS = {
"Project Deleted Successfully",
PROJECT_REVERT:
"Project Reverted Successfully",
LOGS_NOT_FOUND:
"Sorry, no logs found for requested stack migration."
};

export const HTTP_RESPONSE_HEADERS = {
Expand Down
8 changes: 7 additions & 1 deletion api/src/controllers/migration.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,15 @@ const deleteTestStack = async (req: Request, res: Response): Promise<void> => {
res.status(200).json(resp);
};

const getLogs = async (req: Request, res: Response): Promise<void> => {
const resp = await migrationService.getLogs(req);
res.status(200).json(resp);
};

export const migrationController = {
createTestStack,
deleteTestStack,
startTestMigration,
startMigration
startMigration,
getLogs,
};
3 changes: 2 additions & 1 deletion api/src/models/FieldMapper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { JSONFile } from "lowdb/node";
import LowWithLodash from "../utils/lowdb-lodash.utils.js";
import path from "path";

/**
* Represents the advanced configuration options for a field mapper.
Expand Down Expand Up @@ -44,7 +45,7 @@ const defaultData: FieldMapper = { field_mapper: [] };
* Represents the database instance for the FieldMapper model.
*/
const db = new LowWithLodash(
new JSONFile<FieldMapper>("database/field-mapper.json"),
new JSONFile<FieldMapper>(path.join(process.cwd(), "database", "field-mapper.json")),
defaultData
);

Expand Down
3 changes: 2 additions & 1 deletion api/src/models/contentTypesMapper-lowdb.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { JSONFile } from "lowdb/node";
import path from 'path';
import LowWithLodash from "../utils/lowdb-lodash.utils.js";

/**
Expand Down Expand Up @@ -80,7 +81,7 @@ const defaultData: ContentTypeMapperDocument = { ContentTypesMappers: [] };
* Represents the database instance for the content types mapper.
*/
const db = new LowWithLodash(
new JSONFile<ContentTypeMapperDocument>("database/contentTypesMapper.json"),
new JSONFile<ContentTypeMapperDocument>(path.join(process.cwd(), "database", 'contentTypesMapper.json')),
defaultData
);

Expand Down
3 changes: 2 additions & 1 deletion api/src/models/project-lowdb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ interface Project {
test_stacks: [];
current_test_stack_id: string;
legacy_cms: LegacyCMS;
content_mapper: [];
content_mapper: any[];
execution_log: [ExecutionLog];
created_at: string;
updated_at: string;
Expand All @@ -73,6 +73,7 @@ interface Project {
stackDetails: [];
mapperKeys: {};
extract_path: string;
isMigrationStarted: boolean;
}

interface ProjectDocument {
Expand Down
6 changes: 6 additions & 0 deletions api/src/routes/migration.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,11 @@ router.post(
asyncRouter(migrationController.startMigration)
);

router.get(
"/get_migration_logs/:orgId/:projectId/:stackId",
asyncRouter(migrationController.getLogs)

)


export default router;
68 changes: 40 additions & 28 deletions api/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,48 @@ import migrationRoutes from "./routes/migration.routes.js";
import chokidar from "chokidar";
import { Server } from "socket.io";
import fs from "fs";
import path from "path";

// Initialize file watcher for the log file
const watcher = chokidar.watch(config.LOG_FILE_PATH, {
usePolling: true, // Enables polling to detect changes in all environments
interval: 100, // Poll every 100ms (you can adjust this if needed)
interval: 1, // Poll every 100ms (you can adjust this if needed)
awaitWriteFinish: { // Wait for file to finish being written before triggering
stabilityThreshold: 500, // Time to wait before considering the file stable
pollInterval: 100, // Interval at which to poll for file stability
stabilityThreshold: 1, // Time to wait before considering the file stable
pollInterval: 1, // Interval at which to poll for file stability
},
persistent: true, // Keeps watching the file even after initial change
}); // Initialize with initial log path

let io: Server; // Socket.IO server instance

// Dynamically change the log file path and update the watcher
export async function setLogFilePath(path: string) {
console.info(`Setting new log file path: ${path}`);

// Stop watching the old log file
watcher.unwatch(config.LOG_FILE_PATH);

// Update the config and start watching the new log file
config.LOG_FILE_PATH = path;
watcher.add(path);
export async function setLogFilePath(newPath: string) {
try {
// Ensure the new log file path is absolute and valid
const absolutePath = path.resolve(newPath);
console.info(`Attempting to set new log file path: ${absolutePath}`);
// Check if the new log file exists
// Stop watching the old log file
if (config.LOG_FILE_PATH) {
console.info(`Stopping watcher for previous log file: ${config.LOG_FILE_PATH}`);
watcher.unwatch(config.LOG_FILE_PATH);
}
// Update the config to use the new log file path
config.LOG_FILE_PATH = absolutePath;

// Start watching the new log file
console.info(`Starting watcher for new log file: ${absolutePath}`);
watcher.add(absolutePath);

} catch (error: any) {
console.error(`Failed to set new log file path: ${error.message}`);
// Optional: fallback to default or previous log file if the new path is invalid
if (config.LOG_FILE_PATH) {
console.info(`Reverting to previous log file path: ${config.LOG_FILE_PATH}`);
watcher.add(config.LOG_FILE_PATH); // Re-watch previous log file
}
}
}

try {
Expand Down Expand Up @@ -99,26 +117,20 @@ try {
});

// Emit initial log file content to connected clients

// File watcher for log file changes
watcher.on("change", (path) => {
// File watcher for log file changes
watcher.on("change", async (path) => {
console.info(`File changed: ${path}`);
// Read the updated file content
fs.readFile(path, "utf8", (err, data) => {
if (err) {
logger.error(`Error reading log file: ${err}`);
return;
}
try {
// Emit the updated log content to connected clients
io.emit("logUpdate", data);
} catch (error) {
logger.error(`Error emitting log data: ${error}`);
}
});
try {
const data = await fs.promises.readFile(path, "utf8")
// Emit the updated log content to connected clients
io.emit("logUpdate", data);
} catch (error) {
logger.error(`Error emitting log data: ${error}`);
}
});

})();

} catch (e) {
logger.error("Error while starting the server!");
logger.error(e);
Expand Down
18 changes: 10 additions & 8 deletions api/src/services/contentMapper.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const putTestData = async (req: Request) => {
const projectId = req.params.projectId;
const contentTypes = req.body.contentTypes;


await FieldMapperModel.read();

/*
Expand Down Expand Up @@ -61,7 +62,7 @@ const putTestData = async (req: Request) => {
});

await ContentTypesMapperModelLowdb.read();
const contentIds: string[] = [];
const contentIds: any[] = [];

/*
this code snippet is iterating over an array called contentTypes and
Expand All @@ -70,7 +71,7 @@ const putTestData = async (req: Request) => {
and the generated id values are pushed into the contentIds array.
*/
const contentType = contentTypes.map((item: any) => {
const id = item?.id.replace(/[{}]/g, "")?.toLowerCase() || uuidv4();
const id = item?.id?.replace(/[{}]/g, "")?.toLowerCase() || uuidv4();
item.id = id;
contentIds.push(id);
return { ...item, id, projectId };
Expand All @@ -88,11 +89,12 @@ const putTestData = async (req: Request) => {
.get("projects")
.findIndex({ id: projectId })
.value();
if (index > -1) {
ProjectModelLowdb.update((data: any) => {
data.projects[index].content_mapper = contentIds;
data.projects[index].extract_path = req?.body?.extractPath;
});
if (index > -1 && contentIds?.length) {
ProjectModelLowdb.data.projects[index].content_mapper = contentIds;
ProjectModelLowdb.data.projects[index].extract_path = req?.body?.extractPath;
ProjectModelLowdb.write();
} else {
throw new BadRequestError(HTTP_TEXTS.CONTENT_TYPE_NOT_FOUND);
}

const pData = ProjectModelLowdb.chain
Expand Down Expand Up @@ -900,7 +902,7 @@ const getSingleGlobalField = async (req: Request) => {
},
})
);

if (err)
return {
data: err.response.data,
Expand Down
90 changes: 82 additions & 8 deletions api/src/services/migration.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Request } from "express";
import path from "path";
import ProjectModelLowdb from "../models/project-lowdb.js";
import { config } from "../config/index.js";
import { safePromise, getLogMessage } from "../utils/index.js";
Expand All @@ -7,11 +8,15 @@ import { LoginServiceType } from "../models/types.js"
import getAuthtoken from "../utils/auth.utils.js";
import logger from "../utils/logger.js";
import { HTTP_TEXTS, HTTP_CODES, LOCALE_MAPPER, STEPPER_STEPS } from "../constants/index.js";
import { ExceptionFunction } from "../utils/custom-errors.utils.js";
import { BadRequestError, ExceptionFunction } from "../utils/custom-errors.utils.js";
import { fieldAttacher } from "../utils/field-attacher.utils.js";
import { siteCoreService } from "./sitecore.service.js";
import { testFolderCreator } from "../utils/test-folder-creator.utils.js";
import { utilsCli } from './runCli.service.js';
import customLogger from "../utils/custom-logger.utils.js";
import { setLogFilePath } from "../server.js";
import fs from 'fs';




Expand Down Expand Up @@ -208,12 +213,16 @@ const startTestMigration = async (req: Request): Promise<any> => {
const project = ProjectModelLowdb.chain.get("projects").find({ id: projectId }).value();
const packagePath = project?.extract_path;
if (packagePath && project?.current_test_stack_id) {
const loggerPath = path.join(process.cwd(), 'logs', projectId, `${project?.current_test_stack_id}.log`);
const message = getLogMessage('startTestMigration', 'Starting Test Migration...', {});
await customLogger(projectId, project?.current_test_stack_id, 'info', message);
await setLogFilePath(loggerPath);
const contentTypes = await fieldAttacher({ orgId, projectId, destinationStackId: project?.current_test_stack_id });
await siteCoreService?.createEntry({ packagePath, contentTypes, destinationStackId: project?.current_test_stack_id });
await siteCoreService?.createLocale(req, project?.current_test_stack_id);
await siteCoreService?.createEntry({ packagePath, contentTypes, destinationStackId: project?.current_test_stack_id, projectId });
await siteCoreService?.createLocale(req, project?.current_test_stack_id, projectId);
await siteCoreService?.createVersionFile(project?.current_test_stack_id);
await testFolderCreator?.({ destinationStackId: project?.current_test_stack_id });
await utilsCli?.runCli(region, user_id, project?.current_test_stack_id, projectId, true);
await utilsCli?.runCli(region, user_id, project?.current_test_stack_id, projectId, true, loggerPath);
}
}

Expand All @@ -228,19 +237,84 @@ const startMigration = async (req: Request): Promise<any> => {
const { region, user_id } = req?.body?.token_payload ?? {};
await ProjectModelLowdb.read();
const project = ProjectModelLowdb.chain.get("projects").find({ id: projectId }).value();

const index = ProjectModelLowdb.chain.get("projects").findIndex({ id: projectId }).value();
if (index > -1) {
ProjectModelLowdb.update((data: any) => {
data.projects[index].isMigrationStarted = true;
});
}

const packagePath = project?.extract_path;
if (packagePath && project?.destination_stack_id) {
const loggerPath = path.join(process.cwd(), 'logs', projectId, `${project?.destination_stack_id}.log`);
const message = getLogMessage('startTestMigration', 'Starting Migration...', {});
await customLogger(projectId, project?.destination_stack_id, 'info', message);
await setLogFilePath(loggerPath);
const contentTypes = await fieldAttacher({ orgId, projectId, destinationStackId: project?.destination_stack_id });
await siteCoreService?.createEntry({ packagePath, contentTypes, destinationStackId: project?.destination_stack_id });
await siteCoreService?.createLocale(req, project?.destination_stack_id);
await siteCoreService?.createEntry({ packagePath, contentTypes, destinationStackId: project?.destination_stack_id, projectId });
await siteCoreService?.createLocale(req, project?.destination_stack_id, projectId);
await siteCoreService?.createVersionFile(project?.destination_stack_id);
await utilsCli?.runCli(region, user_id, project?.destination_stack_id, projectId);
await utilsCli?.runCli(region, user_id, project?.destination_stack_id, projectId, false, loggerPath);
}
}

const getLogs = async (req: Request): Promise<any> => {
const orgId = req?.params?.orgId;
const projectId = req?.params?.projectId;
const stackId = req?.params?.stackId;
const srcFunc = "getLogs";
const { region, user_id } = req?.body?.token_payload ?? {};
try {
const loggerPath = path.join(process.cwd(), 'logs', projectId, `${stackId}.log`);
if(fs.existsSync(loggerPath)){
const logs = fs.readFileSync(loggerPath,'utf-8');
const logEntries = logs
.split('\n')
.map(line => {
try {
return JSON.parse(line);
} catch (error) {
return null;
}
})
.filter(entry => entry !== null);
return logEntries

}
else{
logger.error(
getLogMessage(
srcFunc,
HTTP_TEXTS.LOGS_NOT_FOUND,

)
);
throw new BadRequestError(HTTP_TEXTS.LOGS_NOT_FOUND);

}

} catch (error:any) {
logger.error(
getLogMessage(
srcFunc,
HTTP_TEXTS.LOGS_NOT_FOUND,
error
)
);
throw new ExceptionFunction(
error?.message || HTTP_TEXTS.INTERNAL_ERROR,
error?.statusCode || error?.status || HTTP_CODES.SERVER_ERROR
);

}

}

export const migrationService = {
createTestStack,
deleteTestStack,
startTestMigration,
startMigration
startMigration,
getLogs,
};
3 changes: 2 additions & 1 deletion api/src/services/projects.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ const createProject = async (req: Request) => {
created_at: '',
isNewStack: false
},
mapperKeys: {}
mapperKeys: {},
isMigrationStarted: false
};

try {
Expand Down
Loading