diff --git a/.gitignore b/.gitignore index be3ce85d..36166964 100644 --- a/.gitignore +++ b/.gitignore @@ -350,4 +350,5 @@ MigrationBackup/ .ionide/ uploade-api/node_modules -uploade-api/build \ No newline at end of file +uploade-api/build +package-lock.json \ No newline at end of file diff --git a/.npmrc b/.npmrc deleted file mode 100644 index ab64ce78..00000000 --- a/.npmrc +++ /dev/null @@ -1,3 +0,0 @@ -//npm.pkg.github.com/:_authToken=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 -@Contentstack_automation_test -:registry=https://npm.pkg.github.com/contentstack \ No newline at end of file diff --git a/api/package.json b/api/package.json index 3f734ad4..58a03e9b 100644 --- a/api/package.json +++ b/api/package.json @@ -8,7 +8,9 @@ "start": "NODE_ENV=production node dist/server.js", "dev": "NODE_ENV=production tsx watch ./src/server.ts", "prettify": "prettier --write .", - "lint:fix": "eslint --fix --ext .ts . --ignore-pattern './node_modules/' --ignore-pattern './dist/'", + "windev": "SET NODE_ENV=production&& tsx watch ./src/server.ts", + "winstart": "SET NODE_ENV=production&& node dist/server.js", + "lint:fix": "eslint --ext .ts --ignore-pattern './node_modules/' --ignore-pattern './dist/'", "precommit": "npm run prettify && npm run lint:fix" }, "type": "module", @@ -32,7 +34,6 @@ "helmet": "^7.1.0", "jsonwebtoken": "^9.0.2", "lowdb": "^7.0.1", - "mongoose": "^8.0.4", "uuid": "^9.0.1", "winston": "^3.11.0" }, diff --git a/api/src/config/dev.config.ts b/api/src/config/dev.config.ts index 3d3184a8..86cf8191 100644 --- a/api/src/config/dev.config.ts +++ b/api/src/config/dev.config.ts @@ -4,4 +4,10 @@ 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 df0154f2..4f152360 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 839120fd..2a124479 100644 --- a/api/src/config/prod.config.ts +++ b/api/src/config/prod.config.ts @@ -5,4 +5,10 @@ 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 588f6120..4042ff6e 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,14 @@ export const PREDEFINED_STATUS = [ "Failed", "Success", ]; -export const PREDEFINED_STEPS = [1, 2, 3, 4]; +export const PREDEFINED_STEPS = [1, 2, 3, 4, 5]; + +export const NEW_PROJECT_STATUS = { + 0: 0, //DRAFT + 1: 1, //READY_TO_TEST + 2: 2, //TESTING_IN_PROGRESS + 3: 3, //READY_FOR_MIGRATION + 4: 4, //MIGRATION_IN_PROGRESS + 5: 5, //MIGRATION_SUCCESSFUL + 6: 6 //MIGRATION_TERMINATED +}; \ No newline at end of file diff --git a/api/src/controllers/migration.controller.ts b/api/src/controllers/migration.controller.ts new file mode 100644 index 00000000..9816c5fe --- /dev/null +++ b/api/src/controllers/migration.controller.ts @@ -0,0 +1,17 @@ +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/database.ts b/api/src/database.ts index 2aaedd34..0e7d331a 100644 --- a/api/src/database.ts +++ b/api/src/database.ts @@ -1,27 +1,16 @@ // database.ts -import mongoose from "mongoose"; -import { config } from "./config/index.js"; import logger from "./utils/logger.js"; -import ProjectModel from "./models/project.js"; -import ContentTypesMapperModel from "./models/contentTypesMapper.js"; import fs from "fs"; const connectToDatabase = async () => { try { - await mongoose.connect(config.MONGODB_URI, { - ...(config.APP_ENV === "production" ? { autoIndex: false } : {}), - }); - - //check if the database folder exists if (!fs.existsSync("./database")) { fs.mkdirSync("./database"); } + logger.info("successfully connecting to Low DB"); - // Create the collection's if it doesn't exist - await ProjectModel.init(); - await ContentTypesMapperModel.init(); } catch (error) { - logger.error("Error while connecting to MongoDB:", error); + logger.error("Error while connecting to Low DB:", error); process.exit(1); } }; diff --git a/api/src/models/contentTypesMapper.ts b/api/src/models/contentTypesMapper.ts deleted file mode 100644 index c72d4c75..00000000 --- a/api/src/models/contentTypesMapper.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Schema, model, Document } from "mongoose"; - -interface ContentTypesMapper extends Document { - otherCmsTitle: string; - otherCmsUid: string; - isUpdated: boolean; - updateAt: Date; - contentstackTitle: string; - contentstackUid: string; - fieldMapping: []; -} - -const contentTypesMapperSchema = new Schema({ - otherCmsTitle: { type: String, required: true }, - otherCmsUid: { type: String, required: true }, - isUpdated: { type: Boolean, default: false }, - updateAt: { type: Date }, - contentstackTitle: { type: String }, - contentstackUid: { type: String }, - fieldMapping: [{ type: Schema.Types.ObjectId, ref: "FieldMapping" }], -}); - -const ContentTypesMapperModel = model( - "ContentTypes Mapper", - contentTypesMapperSchema -); - -export default ContentTypesMapperModel; diff --git a/api/src/models/project-lowdb.ts b/api/src/models/project-lowdb.ts index 6aa33ee5..bf413d59 100644 --- a/api/src/models/project-lowdb.ts +++ b/api/src/models/project-lowdb.ts @@ -31,9 +31,11 @@ interface Project { former_owner_ids: []; name: string; description: string; - status: string; + status: number; 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/models/project.ts b/api/src/models/project.ts deleted file mode 100644 index b4137dd5..00000000 --- a/api/src/models/project.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { Schema, model, Document } from "mongoose"; -import { - CS_REGIONS, - PREDEFINED_STATUS, - PREDEFINED_STEPS, - PROJECT_STATUS, - STEPPER_STEPS, -} from "../constants/index.js"; - -interface LegacyCMS { - cms: string; - affix: string; - affix_confirmation: boolean; - file_format: string; - file_format_confirmation: boolean; - file: { - id: string; - name: string; - size: number; - type: string; - path: string; - }; -} - -interface ExecutionLog { - log_url: string; - date: Date; -} - -interface ProjectDocument extends Document { - region: string; - org_id: string; - owner: string; - created_by: string; - updated_by: string; - former_owner_ids: []; - name: string; - description: string; - status: string; - current_step: number; - destination_stack_id: string; - legacy_cms: LegacyCMS; - content_mapper: []; - execution_log: [ExecutionLog]; - created_at: Date; - updated_at: Date; -} - -const projectSchema = new Schema( - { - region: { type: String, required: true, enum: CS_REGIONS }, - org_id: { type: String, required: true }, - owner: { type: String, required: true }, - created_by: { type: String, required: true }, - updated_by: { type: String }, - former_owner_ids: [{ type: String }], - name: { type: String, required: true }, - description: { type: String, required: true }, - status: { - type: String, - required: true, - default: PROJECT_STATUS.DRAFT, - enum: PREDEFINED_STATUS, - }, - current_step: { - type: Number, - required: true, - default: STEPPER_STEPS.LEGACY_CMS, - enum: PREDEFINED_STEPS, - }, - destination_stack_id: { type: String }, - legacy_cms: { - cms: { type: String }, - affix: { type: String }, - affix_confirmation: { type: Boolean }, - file_format: { type: String }, - file_format_confirmation: { type: Boolean }, - file: { - id: { type: String }, - name: { type: String }, - size: { type: String }, - type: { type: String }, - path: { type: String }, - }, - }, - content_mapper: [ - { type: Schema.Types.ObjectId, ref: "ContentTypes Mapper" }, - ], - execution_log: [ - { - log_url: { type: String }, - date: { type: Date }, - }, - ], - }, - { timestamps: { createdAt: "created_at", updatedAt: "updated_at" } } -); - -const ProjectModel = model("Project", projectSchema); - -export default ProjectModel; diff --git a/api/src/routes/migration.routes.ts b/api/src/routes/migration.routes.ts new file mode 100644 index 00000000..3239ff98 --- /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 33f1d635..9e0a1a8a 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/contentMapper.service.ts b/api/src/services/contentMapper.service.ts index 14084c9d..b50bb2c1 100644 --- a/api/src/services/contentMapper.service.ts +++ b/api/src/services/contentMapper.service.ts @@ -1,5 +1,4 @@ import { Request } from "express"; -import ProjectModel from "../models/project.js"; import { getLogMessage, isEmpty, safePromise } from "../utils/index.js"; import { BadRequestError, @@ -8,10 +7,8 @@ import { import { HTTP_TEXTS, HTTP_CODES, - POPULATE_CONTENT_MAPPER, - POPULATE_FIELD_MAPPING, - PROJECT_STATUS, STEPPER_STEPS, + NEW_PROJECT_STATUS } from "../constants/index.js"; import logger from "../utils/logger.js"; import { config } from "../config/index.js"; @@ -254,12 +251,13 @@ const updateContentType = async (req: Request) => { if ( [ - PROJECT_STATUS.DRAFT, - PROJECT_STATUS.SUCCESS, - PROJECT_STATUS.INPROGRESS, + NEW_PROJECT_STATUS[0], + NEW_PROJECT_STATUS[5], + NEW_PROJECT_STATUS[4], ].includes(project.status) || project.current_step < STEPPER_STEPS.CONTENT_MAPPING - ) { + ) + { logger.error( getLogMessage( srcFun, @@ -369,9 +367,9 @@ const resetToInitialMapping = async (req: Request) => { if ( [ - PROJECT_STATUS.DRAFT, - PROJECT_STATUS.SUCCESS, - PROJECT_STATUS.INPROGRESS, + NEW_PROJECT_STATUS[0], + NEW_PROJECT_STATUS[5], + NEW_PROJECT_STATUS[4], ].includes(project.status) || project.current_step < STEPPER_STEPS.CONTENT_MAPPING ) { @@ -457,14 +455,7 @@ const resetToInitialMapping = async (req: Request) => { }; const resetAllContentTypesMapping = async (projectId: string) => { const srcFunc = "resetAllContentTypesMapping"; - // const projectId = req?.params?.projectId; - - // const projectDetails: any = await ProjectModel.findOne({ - // _id: projectId, - // }).populate({ - // path: POPULATE_CONTENT_MAPPER, - // populate: { path: POPULATE_FIELD_MAPPING }, - // }); + await ProjectModelLowdb.read(); const projectDetails = ProjectModelLowdb.chain .get("projects") @@ -481,15 +472,6 @@ const resetAllContentTypesMapping = async (projectId: string) => { ); throw new BadRequestError(HTTP_TEXTS.CONTENTMAPPER_NOT_FOUND); } - await ContentTypesMapperModelLowdb.read(); - const data = contentMapperId.map((id: any) => { - const getFeildMappingData = ContentTypesMapperModelLowdb.chain - .get("ContentTypesMappers") - .find({ id: id }) - .value(); - return getFeildMappingData; - }); - if (isEmpty(projectDetails)) { logger.error( getLogMessage( @@ -499,48 +481,57 @@ const resetAllContentTypesMapping = async (projectId: string) => { ); throw new BadRequestError(HTTP_TEXTS.PROJECT_NOT_FOUND); } + await ContentTypesMapperModelLowdb.read(); + const cData = contentMapperId.map((cId: any) => { + const contentTypeData = ContentTypesMapperModelLowdb.chain + .get("ContentTypesMappers") + .find({ id: cId }) + .value(); + return contentTypeData; + }); try { - // const contentTypes = projectDetails?.content_mapper; - const contentTypes = data; - const contentTypesbulkWriteOperations: any = await Promise.all( - contentTypes?.map(async (contentType: any) => { - if (!isEmpty(contentType?.fieldMapping)) { + const contentTypes = cData; + for (const contentType of contentTypes) { + if (!isEmpty(contentType.fieldMapping)) { + for (const field of contentType.fieldMapping) { await FieldMapperModel.read(); - (contentType?.fieldMapping || []).forEach((field: any) => { - const fieldIndex = FieldMapperModel.data.field_mapper.findIndex( - (f: any) => f?.id === field?.id - ); - if (fieldIndex > -1) { - FieldMapperModel.update((data: any) => { - data.field_mapper[fieldIndex] = { - ...field, - contentstackField: "", - contentstackFieldUid: "", - ContentstackFieldType: field.backupFieldType, - }; - }); - } + const fieldData = FieldMapperModel.chain + .get("field_mapper") + .find({ id: field }) + .value(); + const fieldIndex = FieldMapperModel.chain + .get("field_mapper") + .findIndex({ id: field }) + .value(); + + if (fieldIndex > -1) { + await FieldMapperModel.update((fData: any) => { + fData.field_mapper[fieldIndex] = { + ...fieldData, + contentstackField: "", + contentstackFieldUid: "", + ContentstackFieldType: fieldData.backupFieldType, + }; + }); + } + } + } + await ContentTypesMapperModelLowdb.read(); + if (!isEmpty(contentType?.id)) { + const cIndex = ContentTypesMapperModelLowdb.chain + .get("ContentTypesMappers") + .findIndex({ id: contentType?.id }) + .value(); + if (cIndex > -1) { + await ContentTypesMapperModelLowdb.update((data: any) => { + data.ContentTypesMappers[cIndex].contentstackTitle = ""; + data.ContentTypesMappers[cIndex].contentstackUid = ""; }); } - return { - updateOne: { - filter: { _id: contentType._id }, - update: { - $set: { - contentstackTitle: "", - contentstackUid: "", - }, - }, - }, - }; - }) - ); + } + } - await ContentTypesMapperModelLowdb.read(); - ContentTypesMapperModelLowdb.update((data: any) => { - data.ContentTypesMappers.push(contentTypesbulkWriteOperations); - }); return projectDetails; } catch (error: any) { logger.error( @@ -559,13 +550,11 @@ const resetAllContentTypesMapping = async (projectId: string) => { }; const removeMapping = async (projectId: string) => { const srcFunc = "removeMapping"; - - const projectDetails: any = await ProjectModel.findOne({ - _id: projectId, - }).populate({ - path: POPULATE_CONTENT_MAPPER, - populate: { path: POPULATE_FIELD_MAPPING }, - }); + await ProjectModelLowdb.read(); + const projectDetails = ProjectModelLowdb.chain + .get("projects") + .find({ id: projectId }) + .value(); if (isEmpty(projectDetails)) { logger.error( @@ -576,40 +565,59 @@ const removeMapping = async (projectId: string) => { ); throw new BadRequestError(HTTP_TEXTS.PROJECT_NOT_FOUND); } + await ContentTypesMapperModelLowdb.read(); + const cData = projectDetails?.content_mapper.map((cId: any) => { + const contentTypeData = ContentTypesMapperModelLowdb.chain + .get("ContentTypesMappers") + .find({ id: cId }) + .value(); + return contentTypeData; + }); try { - const contentTypes = projectDetails?.content_mapper; - + const contentTypes = cData; //TODO: remove fieldMapping ids in ContentTypesMapperModel for each content types - const contentTypesbulkWriteOperations: any = await Promise.all( - contentTypes?.map(async (contentType: any) => { - if (!isEmpty(contentType?.fieldMapping)) { + + for (const contentType of contentTypes) { + if (!isEmpty(contentType.fieldMapping)) { + for (const field of contentType.fieldMapping) { await FieldMapperModel.read(); - (contentType?.fieldMapping || []).forEach((field: any) => { - const fieldIndex = FieldMapperModel.data.field_mapper.findIndex( - (f: any) => f?.id === field?.id - ); - if (fieldIndex > -1) { - FieldMapperModel.update((data: any) => { - delete data.field_mapper[fieldIndex]; - }); - } + const fieldIndex = FieldMapperModel.chain + .get("field_mapper") + .findIndex({ id: field }) + .value(); + if (fieldIndex > -1) { + await FieldMapperModel.update((fData: any) => { + delete fData.field_mapper[fieldIndex]; + }); + } + } + } + await ContentTypesMapperModelLowdb.read(); + if (!isEmpty(contentType?.id)) { + const cIndex = ContentTypesMapperModelLowdb.chain + .get("ContentTypesMappers") + .findIndex({ id: contentType?.id }) + .value(); + if (cIndex > -1) { + await ContentTypesMapperModelLowdb.update((data: any) => { + delete data.ContentTypesMappers[cIndex]; }); } - return { - deleteOne: { - filter: { _id: contentType._id }, - }, - }; - }) - ); + } + } - await ContentTypesMapperModelLowdb.read(); - ContentTypesMapperModelLowdb.update((data: any) => { - data.ContentTypesMappers.push(contentTypesbulkWriteOperations); - }); - projectDetails.content_mapper = []; - await projectDetails?.save(); + await ProjectModelLowdb.read(); + const projectIndex = ProjectModelLowdb.chain + .get("projects") + .findIndex({ id: projectId }) + .value(); + + if (projectIndex > -1) { + ProjectModelLowdb.update((data: any) => { + data.projects[projectIndex].content_mapper = []; + }); + } return projectDetails; } catch (error: any) { logger.error( @@ -626,6 +634,7 @@ const removeMapping = async (projectId: string) => { ); } }; + export const contentMapperService = { putTestData, getContentTypes, diff --git a/api/src/services/migration.service.ts b/api/src/services/migration.service.ts new file mode 100644 index 00000000..631f12d6 --- /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 dcdb98f4..81ddbe54 100644 --- a/api/src/services/projects.service.ts +++ b/api/src/services/projects.service.ts @@ -8,8 +8,8 @@ import { import { HTTP_TEXTS, HTTP_CODES, - PROJECT_STATUS, STEPPER_STEPS, + NEW_PROJECT_STATUS } from "../constants/index.js"; import { config } from "../config/index.js"; import { getLogMessage, safePromise } from "../utils/index.js"; @@ -74,9 +74,11 @@ const createProject = async (req: Request) => { former_owner_ids: [], name, description, - status: PROJECT_STATUS.DRAFT, + status: NEW_PROJECT_STATUS[0], current_step: STEPPER_STEPS.LEGACY_CMS, destination_stack_id: "", + test_stacks: [], + current_test_stack_id: "", legacy_cms: {}, content_mapper: [], execution_log: [], @@ -217,8 +219,8 @@ const updateLegacyCMS = async (req: Request) => { const project = ProjectModelLowdb.data.projects[projectIndex]; if ( - project.status === PROJECT_STATUS.INPROGRESS || - project.status === PROJECT_STATUS.SUCCESS + project.status === NEW_PROJECT_STATUS[4] || + project.status === NEW_PROJECT_STATUS[5] ) { logger.error( getLogMessage(srcFunc, HTTP_TEXTS.CANNOT_UPDATE_LEGACY_CMS, token_payload) @@ -241,7 +243,7 @@ const updateLegacyCMS = async (req: Request) => { ProjectModelLowdb.update((data: any) => { data.projects[projectIndex].legacy_cms.cms = legacy_cms; data.projects[projectIndex].current_step = STEPPER_STEPS.LEGACY_CMS; - data.projects[projectIndex].status = PROJECT_STATUS.DRAFT; + data.projects[projectIndex].status = NEW_PROJECT_STATUS[0]; data.projects[projectIndex].updated_at = new Date().toISOString(); }); @@ -358,8 +360,8 @@ const updateFileFormat = async (req: Request) => { const project = ProjectModelLowdb.data.projects[projectIndex]; if ( - project.status === PROJECT_STATUS.INPROGRESS || - project.status === PROJECT_STATUS.SUCCESS + project.status === NEW_PROJECT_STATUS[4] || + project.status === NEW_PROJECT_STATUS[5] ) { logger.error( getLogMessage( @@ -386,7 +388,7 @@ const updateFileFormat = async (req: Request) => { ProjectModelLowdb.update((data: any) => { data.projects[projectIndex].legacy_cms.file_format = file_format; data.projects[projectIndex].current_step = STEPPER_STEPS.LEGACY_CMS; - data.projects[projectIndex].status = PROJECT_STATUS.DRAFT; + data.projects[projectIndex].status = NEW_PROJECT_STATUS[0]; data.projects[projectIndex].updated_at = new Date().toISOString(); }); @@ -477,8 +479,8 @@ const updateDestinationStack = async (req: Request) => { ); if ( - project.status === PROJECT_STATUS.INPROGRESS || - project.status === PROJECT_STATUS.SUCCESS || + project.status === NEW_PROJECT_STATUS[4] || + project.status === NEW_PROJECT_STATUS[5] || project.current_step < STEPPER_STEPS.DESTINATION_STACK ) { logger.error( @@ -490,8 +492,7 @@ const updateDestinationStack = async (req: Request) => { ); throw new BadRequestError(HTTP_TEXTS.CANNOT_UPDATE_DESTINATION_STACK); } - - if (project.current_step > STEPPER_STEPS.LEGACY_CMS) { + if (project.current_step > STEPPER_STEPS.DESTINATION_STACK) { await contentMapperService.resetAllContentTypesMapping(projectId); logger.info( getLogMessage( @@ -530,7 +531,7 @@ const updateDestinationStack = async (req: Request) => { data.projects[projectIndex].destination_stack_id = stack_api_key; data.projects[projectIndex].current_step = STEPPER_STEPS.DESTINATION_STACK; - data.projects[projectIndex].status = PROJECT_STATUS.DRAFT; + data.projects[projectIndex].status = NEW_PROJECT_STATUS[0]; data.projects[projectIndex].updated_at = new Date().toISOString(); }); @@ -589,7 +590,7 @@ const updateCurrentStep = async (req: Request) => { switch (project.current_step) { case STEPPER_STEPS.LEGACY_CMS: { - if (project.status !== PROJECT_STATUS.DRAFT || !isStepCompleted) { + if (project.status !== NEW_PROJECT_STATUS[0] || !isStepCompleted) { logger.error( getLogMessage( srcFunc, @@ -609,7 +610,7 @@ const updateCurrentStep = async (req: Request) => { } case STEPPER_STEPS.DESTINATION_STACK: { if ( - project.status !== PROJECT_STATUS.DRAFT || + project.status !== NEW_PROJECT_STATUS[0] || !isStepCompleted || !project?.destination_stack_id ) { @@ -628,7 +629,7 @@ const updateCurrentStep = async (req: Request) => { ProjectModelLowdb.update((data: any) => { data.projects[projectIndex].current_step = STEPPER_STEPS.CONTENT_MAPPING; - data.projects[projectIndex].status = PROJECT_STATUS.READY; + data.projects[projectIndex].status = NEW_PROJECT_STATUS[3]; data.projects[projectIndex].updated_at = new Date().toISOString(); }); break; diff --git a/api/src/utils/index.ts b/api/src/utils/index.ts index be3ccd71..297e6c98 100644 --- a/api/src/utils/index.ts +++ b/api/src/utils/index.ts @@ -1,4 +1,3 @@ -import mongoose from "mongoose"; export const throwError = (message: string, statusCode: number) => { throw Object.assign(new Error(message), { statusCode }); @@ -28,7 +27,3 @@ export const getLogMessage = ( }; }; -export const isValidObjectId = (id: string | undefined) => - mongoose.isValidObjectId(id); - -export const getMongooseID = () => new mongoose.Types.ObjectId(); diff --git a/package.json b/package.json index 33479da9..a1b0b188 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ }, "husky": { "hooks": { - "pre-commit": "npx validate-branch-name && cd api && npm run precommit && cd ../ui && npm run precommit && cd .. && git add ." + "pre-commit": "npx validate-branch-name && cd ./api && npm run precommit && cd ./ui && npm run precommit && npm run precommit && cd ./ && cd .. && git add ." } }, "validate-branch-name": { diff --git a/ui/example.env b/ui/example.env deleted file mode 100644 index 26b0b6a1..00000000 --- a/ui/example.env +++ /dev/null @@ -1,15 +0,0 @@ -#Common and mandetory variables -REACT_APP_WEBSITE_BASE_URL="" -REACT_APP_BASE_API_URL="" -REACT_APP_API_VERSION= -REACT_APP_UPLOAD_SERVER="" - -#Set to true if want to use static cms content on entire app, By default it will take value as true. -REACT_APP_OFFLINE_CMS=true - -#IF REACT_APP_OFFLINE_CMS set to false then need to specify Delivery Token, API Key, cms environment,Host URL to get data entries from CMS -REACT_APP_CS_DELIVERY_TOKEN= -REACT_APP_CS_ENVIRONMENT= -REACT_APP_CS_API_KEY= -REACT_APP_CS_HOST= -REACT_APP_CS_REGION= \ No newline at end of file diff --git a/ui/package-lock.json b/ui/packaga-lock.json similarity index 99% rename from ui/package-lock.json rename to ui/packaga-lock.json index 402152c4..ba1efe92 100644 --- a/ui/package-lock.json +++ b/ui/packaga-lock.json @@ -6905,9 +6905,7 @@ "version": "0.0.8", "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "engines": [ - "node >= 0.8.0" - ], + "engines": ["node >= 0.8.0"], "bin": { "ansi-html": "bin/ansi-html" } @@ -11330,9 +11328,7 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } diff --git a/ui/public/index.html b/ui/public/index.html index 8a1920e4..4e8e345f 100644 --- a/ui/public/index.html +++ b/ui/public/index.html @@ -18,6 +18,7 @@ void; +} +const AdvancePropertise = (props: SchemaProps) => { + return ( + <> + + + + Validation (Regex) + + + + Options + +
+ + + + +

+ If enabled, editing this field is restricted in localized entries. The field will use + the value of the master-language entry in all localized entries. +

+
+
+ + ); +}; + +export default AdvancePropertise; diff --git a/ui/src/components/Card/card.interface.ts b/ui/src/components/Card/card.interface.ts index 73a939c1..74c43df1 100644 --- a/ui/src/components/Card/card.interface.ts +++ b/ui/src/components/Card/card.interface.ts @@ -1,5 +1,5 @@ import { ProjectsObj } from '../../pages/Projects/projects.interface'; export interface ProjectType { - project: ProjectsObj; + project?: ProjectsObj; } diff --git a/ui/src/components/Card/card.scss b/ui/src/components/Card/card.scss index 0008605c..a1f21cbe 100644 --- a/ui/src/components/Card/card.scss +++ b/ui/src/components/Card/card.scss @@ -11,6 +11,13 @@ margin-bottom: 2.125rem; padding: 1.75rem 2rem 0.688rem; } +.ProjectCard__title { + color: $color-font-black; + font-size: $size-font-2-xl; + font-weight: $font-weight-extra-bold; + height: 1.563rem; + line-height: $line-height-reset; +} .ProjectCard__footer { display: flex; position: relative; @@ -37,22 +44,33 @@ flex-wrap: nowrap; justify-content: center; } +.ProjectCard__stats-category { + color: $color-font-active; + font-size: $size-font-small; + font-weight: $font-weight-regular; + line-height: 1.42; +} .ProjectCard__stats-number { - font-size: 14px; - font-size: 0.875rem; - font-weight: 600; - letter-spacing: 0.14px; + display: block; + font-size: $size-font-large; + font-weight: $font-weight-semi-bold; letter-spacing: 0.00875rem; line-height: 150%; + margin-bottom: $space-4; } .validation-color { color: $color-brand-warning-medium; } .ProjectCard__cms { - font-size: 12px; + font-size: $size-font-small; color: $color-font-black; text-transform: capitalize; } .ProjectCard__modified-date { + color: $color-font-base; + display: block; + font-size: $size-font-small; + font-weight: $font-weight-regular; + line-height: $line-height-reset; padding-left: 8px; } diff --git a/ui/src/components/Card/index.tsx b/ui/src/components/Card/index.tsx index 7ff8700f..4c33db92 100644 --- a/ui/src/components/Card/index.tsx +++ b/ui/src/components/Card/index.tsx @@ -19,19 +19,17 @@ const CardList = ({ project }: ProjectType) => { if (isEmptyString(id)) return; navigate(`/projects/${id}/migration/steps/1`); }; - useEffect(() => { const fetchProject = async () => { - const { data, status } = await getProject( - selectedOrganisation?.value || '', - project?.id || '' - ); - if (data?.legacy_cms && status === 200) { - setprojectDetails(data?.legacy_cms?.cms); + if (selectedOrganisation?.value && project?.id) { + const { data, status } = await getProject(selectedOrganisation?.value, project?.id); + if (status === 200 && data?.legacy_cms) { + setprojectDetails(data?.legacy_cms.cms); + } } }; fetchProject(); - }, [projectDetails]); + }, [selectedOrganisation?.value, project?.id]); return (
@@ -46,12 +44,6 @@ const CardList = ({ project }: ProjectType) => { Project Status {project?.status}
-
- Current Status - - Validation Failed - -
diff --git a/ui/src/components/Common/AddStack/addStack.tsx b/ui/src/components/Common/AddStack/addStack.tsx index fbf2310d..d53755f0 100644 --- a/ui/src/components/Common/AddStack/addStack.tsx +++ b/ui/src/components/Common/AddStack/addStack.tsx @@ -42,9 +42,9 @@ const AddStack = (props: any): JSX.Element => { const onSubmit = async (formData: any) => { setIsProcessing(true); const resp = await props.onSubmit({ - name: formData?.name || props.defaultValues.name, - description: formData?.description || props.defaultValues.description, - locale: formData?.locale?.value || props.defaultValues.locale + name: formData?.name || props?.defaultValues?.name, + description: formData?.description || props?.defaultValues?.description, + locale: formData?.locale?.value || props?.defaultValues?.locale }); if (resp) { @@ -61,7 +61,7 @@ const AddStack = (props: any): JSX.Element => { useEffect(() => { //check if offline CMS data field is set to true, if then read data from cms data file. - getCMSDataFromFile(CS_ENTRIES.ADD_STACK) + getCMSDataFromFile(CS_ENTRIES?.ADD_STACK) .then((data: AddStackCMSData) => { //Check for null if (!data) { @@ -93,10 +93,10 @@ const AddStack = (props: any): JSX.Element => { keepDirtyOnReinitialize={true} validate={(values): any => { const errors: any = {}; - if (!values.name || values.name.trim().lenght < 1) { + if (!values?.name || values?.name?.trim().lenght < 1) { errors.name = 'Stack name required'; } - if (!values.locale || values.locale === '') { + if (!values?.locale || values?.locale === '') { errors.locale = 'Required'; } return errors; @@ -109,7 +109,7 @@ const AddStack = (props: any): JSX.Element => { <>
- + @@ -120,30 +120,30 @@ const AddStack = (props: any): JSX.Element => { required testId="cs-stack-create-title" version="v2" - error={meta.error && meta.touched && true} + error={meta?.error && meta?.touched && true} htmlFor="name" > - {addStackCMSData.stack_name} + {addStackCMSData?.stack_name} { - input.onChange(event); + input?.onChange(event); }} name="name" autoComplete="off" type="text" - placeholder={addStackCMSData.stack_name_placeholder} - error={(meta.error || meta.submitError) && meta.touched} + placeholder={addStackCMSData?.stack_name_placeholder} + error={(meta?.error || meta?.submitError) && meta?.touched} /> - {meta.error && meta.touched && ( + {meta?.error && meta?.touched && ( - {meta.error} + {meta?.error} )} @@ -162,7 +162,7 @@ const AddStack = (props: any): JSX.Element => { version="v2" htmlFor="description" > - {addStackCMSData.stack_description} + {addStackCMSData?.stack_description}