diff --git a/api/src/config/dev.config.ts b/api/src/config/dev.config.ts index 3d3184a82..ca5cc867a 100644 --- a/api/src/config/dev.config.ts +++ b/api/src/config/dev.config.ts @@ -4,4 +4,11 @@ export const devConfig = { EU: "https://stag-eu-api.csnonprod.com/v3", AZURE_NA: "https://stag-azure-na-api.csnonprod.com/v3", }, + CS_URL: { + NA: "https://app.contentstack.com/#!", + EU: "https://eu-app.contentstack.com/#!", + AZURE_NA: "https://azure-na-app.contentstack.com/#!", + AZURE_EU: "https://azure-eu-app.contentstack.com/#!", + } + }; diff --git a/api/src/config/index.ts b/api/src/config/index.ts index df0154f2e..4f1523603 100644 --- a/api/src/config/index.ts +++ b/api/src/config/index.ts @@ -20,6 +20,12 @@ export type ConfigType = { AZURE_NA: string; AZURE_EU?: string; }; + CS_URL: { + NA: string; + EU: string; + AZURE_NA: string; + AZURE_EU?: string; + }; }; export const config: ConfigType = { diff --git a/api/src/config/prod.config.ts b/api/src/config/prod.config.ts index 839120fd6..30393acf1 100644 --- a/api/src/config/prod.config.ts +++ b/api/src/config/prod.config.ts @@ -5,4 +5,11 @@ export const prodConfig = { AZURE_NA: "https://azure-na-api.contentstack.com/v3", AZURE_EU: "https://azure-eu-api.contentstack.com/v3", }, + CS_URL: { + NA: "https://app.contentstack.com/#!", + EU: "https://eu-app.contentstack.com/#!", + AZURE_NA: "https://azure-na-app.contentstack.com/#!", + AZURE_EU: "https://azure-eu-app.contentstack.com/#!", + } + }; diff --git a/api/src/constants/index.ts b/api/src/constants/index.ts index 588f6120b..790045df0 100644 --- a/api/src/constants/index.ts +++ b/api/src/constants/index.ts @@ -111,7 +111,8 @@ export const STEPPER_STEPS = { LEGACY_CMS: 1, DESTINATION_STACK: 2, CONTENT_MAPPING: 3, - MIGRATION: 4, + TESTING : 4, + MIGRATION: 5, }; export const PREDEFINED_STATUS = [ "Draft", @@ -120,4 +121,4 @@ export const PREDEFINED_STATUS = [ "Failed", "Success", ]; -export const PREDEFINED_STEPS = [1, 2, 3, 4]; +export const PREDEFINED_STEPS = [1, 2, 3, 4,5]; diff --git a/api/src/controllers/migration.controller.ts b/api/src/controllers/migration.controller.ts new file mode 100644 index 000000000..c24085e60 --- /dev/null +++ b/api/src/controllers/migration.controller.ts @@ -0,0 +1,18 @@ +import { Request, Response } from "express"; +import { migrationService } from "../services/migration.service.js"; + +const createTestStack = async (req: Request, res: Response): Promise => { + const resp = await migrationService.createTestStack(req); + res.status(200).json(resp); +}; + +const deleteTestStack = async (req: Request, res: Response): Promise => { + const resp = await migrationService.deleteTestStack(req); + res.status(200).json(resp); +}; + + +export const migrationController = { + createTestStack, + deleteTestStack +}; diff --git a/api/src/models/project-lowdb.ts b/api/src/models/project-lowdb.ts index 6aa33ee5e..c5952733e 100644 --- a/api/src/models/project-lowdb.ts +++ b/api/src/models/project-lowdb.ts @@ -34,6 +34,8 @@ interface Project { status: string; current_step: number; destination_stack_id: string; + test_stacks: [] + current_test_stack_id:string; legacy_cms: LegacyCMS; content_mapper: []; execution_log: [ExecutionLog]; diff --git a/api/src/routes/migration.routes.ts b/api/src/routes/migration.routes.ts new file mode 100644 index 000000000..3239ff985 --- /dev/null +++ b/api/src/routes/migration.routes.ts @@ -0,0 +1,16 @@ +import express from "express"; + +import { asyncRouter } from "../utils/async-router.utils.js"; +import { migrationController } from "../controllers/migration.controller.js"; + +const router = express.Router({ mergeParams: true }); +// Create a new project route +router.post( + "/test-stack/:orgId/:projectId", + asyncRouter(migrationController.createTestStack) +); +router.post( + "/test-stack/:projectId", + asyncRouter(migrationController.deleteTestStack) +); +export default router; diff --git a/api/src/server.ts b/api/src/server.ts index 33f1d6355..9e0a1a8a5 100644 --- a/api/src/server.ts +++ b/api/src/server.ts @@ -15,6 +15,7 @@ import { requestHeadersMiddleware } from "./middlewares/req-headers.middleware.j import { unmatchedRoutesMiddleware } from "./middlewares/unmatched-routes.middleware.js"; import logger from "./utils/logger.js"; import contentMapperRoutes from "./routes/contentMapper.routes.js"; +import migrationRoutes from "./routes/migration.routes.js"; try { const app = express(); @@ -36,6 +37,7 @@ try { app.use("/v2/org/:orgId", authenticateUser, orgRoutes); app.use("/v2/org/:orgId/project", authenticateUser, projectRoutes); app.use("/v2/mapper", authenticateUser, contentMapperRoutes); + app.use("/v2/migration", authenticateUser, migrationRoutes); //For unmatched route patterns app.use(unmatchedRoutesMiddleware); diff --git a/api/src/services/migration.service.ts b/api/src/services/migration.service.ts new file mode 100644 index 000000000..631f12d6e --- /dev/null +++ b/api/src/services/migration.service.ts @@ -0,0 +1,180 @@ +import { Request } from "express"; +import { config } from "../config/index.js"; +import { safePromise, getLogMessage } from "../utils/index.js"; +import https from "../utils/https.utils.js"; +import { LoginServiceType } from "../models/types.js"; +import getAuthtoken from "../utils/auth.utils.js"; +import logger from "../utils/logger.js"; +import { HTTP_TEXTS, HTTP_CODES } from "../constants/index.js"; +import { ExceptionFunction } from "../utils/custom-errors.utils.js"; +import ProjectModelLowdb from "../models/project-lowdb.js"; + +const createTestStack = async (req: Request): Promise => { + const srcFun = "createTestStack"; + const orgId = req?.params?.orgId; + const projectId = req?.params?.projectId; + const { token_payload, name, description, master_locale } = req.body; + + try { + const authtoken = await getAuthtoken( + token_payload?.region, + token_payload?.user_id + ); + + await ProjectModelLowdb.read(); + const projectData = ProjectModelLowdb.chain.get("projects").value(); + const testStackCount = projectData[0].test_stacks.length + 1; + const newName = name + "-" + testStackCount; + + const [err, res] = await safePromise( + https({ + method: "POST", + url: `${config.CS_API[ + token_payload?.region as keyof typeof config.CS_API + ]!}/stacks`, + headers: { + organization_uid: orgId, + authtoken, + }, + data: { + stack: { + name: newName, + description, + master_locale, + }, + }, + }) + ); + + if (err) { + logger.error( + getLogMessage( + srcFun, + HTTP_TEXTS.CS_ERROR, + token_payload, + err.response.data + ) + ); + + return { + data: err.response.data, + status: err.response.status, + }; + } + + const index = ProjectModelLowdb.chain + .get("projects") + .findIndex({ id: projectId }) + .value(); + console.log(index); + if (index > -1) { + ProjectModelLowdb.update((data: any) => { + data.projects[index].current_test_stack_id = res.data.stack.uid; + data.projects[index].test_stacks.push(res.data.stack.uid); + }); + } + return { + data: { + data: res.data, + url: `${ + config.CS_URL[token_payload?.region as keyof typeof config.CS_URL] + }/stack/${res.data.stack.api_key}/dashboard`, + }, + status: res.status, + }; + } catch (error: any) { + logger.error( + getLogMessage( + srcFun, + "Error while creating a stack", + token_payload, + error + ) + ); + + throw new ExceptionFunction( + error?.message || HTTP_TEXTS.INTERNAL_ERROR, + error?.statusCode || error?.status || HTTP_CODES.SERVER_ERROR + ); + } +}; + +const deleteTestStack = async (req: Request): Promise => { + const srcFun = "deleteTestStack"; + const projectId = req?.params?.projectId; + const { token_payload, stack_key } = req.body; + + try { + const authtoken = await getAuthtoken( + token_payload?.region, + token_payload?.user_id + ); + + const [err, res] = await safePromise( + https({ + method: "DELETE", + url: `${config.CS_API[ + token_payload?.region as keyof typeof config.CS_API + ]!}/stacks`, + headers: { + api_key: stack_key, + authtoken, + }, + }) + ); + + if (err) { + logger.error( + getLogMessage( + srcFun, + HTTP_TEXTS.CS_ERROR, + token_payload, + err.response.data + ) + ); + + return { + data: err.response.data, + status: err.response.status, + }; + } + + const index = ProjectModelLowdb.chain + .get("projects") + .findIndex({ id: projectId }) + .value(); + console.log(index); + if (index > -1) { + ProjectModelLowdb.update((data: any) => { + data.projects[index].current_test_stack_id = ""; + const stackIndex = data.projects[index].test_stacks.indexOf(stack_key); + if (stackIndex > -1) { + data.projects[index].test_stacks.splice(stackIndex, 1); + } + }); + } + return { + data: res.data, + status: res.status, + }; + } catch (error: any) { + logger.error( + getLogMessage( + srcFun, + "Error while creating a stack", + token_payload, + error + ) + ); + + throw new ExceptionFunction( + error?.message || HTTP_TEXTS.INTERNAL_ERROR, + error?.statusCode || error?.status || HTTP_CODES.SERVER_ERROR + ); + } +}; + +export const migrationService = { + createTestStack, + deleteTestStack, +}; diff --git a/api/src/services/projects.service.ts b/api/src/services/projects.service.ts index f5f38a22a..1f3eb767a 100644 --- a/api/src/services/projects.service.ts +++ b/api/src/services/projects.service.ts @@ -77,6 +77,8 @@ const createProject = async (req: Request) => { status: PROJECT_STATUS.DRAFT, current_step: STEPPER_STEPS.LEGACY_CMS, destination_stack_id: "", + test_stacks: [], + current_test_stack_id: "", legacy_cms: {}, content_mapper: [], execution_log: [],