diff --git a/package.json b/package.json index 6ffe1e4..17581a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@knovator/masters-node", - "version": "2.3.0", + "version": "2.4.0", "license": "MIT", "author": "knovator (https://knovator.com/)", "description": "NodeJS backend for @knovator/masters", diff --git a/src/controllers/masterController.ts b/src/controllers/masterController.ts index 4132407..58b08c2 100644 --- a/src/controllers/masterController.ts +++ b/src/controllers/masterController.ts @@ -13,14 +13,22 @@ import { createDocument, getDocumentByQuery, } from '../helpers/dbService'; +import { mergeSubmastersFunc } from '../helpers/utils'; const catchAsync = (fn: any) => { return defaults.catchAsync(fn); }; export const createMaster = catchAsync(async (req: any, res: any) => { + const { mergeSubmasters = [], synonym = [], ...restBody } = req.body; + const mergeSubmastersID = mergeSubmasters.map( + (submaster: any) => submaster.value + ); + let mergedSynonyms = await mergeSubmastersFunc(mergeSubmastersID, Master); + mergedSynonyms = [...mergedSynonyms, ...synonym]; const data = new Master({ - ...req.body, + ...restBody, + synonym: mergedSynonyms, }); if (data.parentId && data.isDefault) { await bulkUpdate( @@ -33,6 +41,18 @@ export const createMaster = catchAsync(async (req: any, res: any) => { const result = await Master.populate(masterData, [ { path: 'img', select: 'uri' }, ]); + + if (mergeSubmastersID.length > 0) { + try { + await Master.deleteMany({ _id: { $in: mergeSubmastersID } }); + defaults.onMastersMerged(mergeSubmastersID, masterData._id.toString()); + } catch (error) { + throw new Error( + `Failed to delete mergeSubmasters: ${(error as Error).message}` + ); + } + } + if (result) { let section = result.parentCode ? 'submaster' : 'master'; res.message = req?.i18n?.t(`${section}.create`); @@ -41,8 +61,17 @@ export const createMaster = catchAsync(async (req: any, res: any) => { }); export const updateMaster = catchAsync(async (req: any, res: any) => { + const { mergeSubmasters = [], synonym = [], ...restBody } = req.body; + const mergeSubmastersID = mergeSubmasters.map( + (submaster: any) => submaster.value + ); + let mergedSynonyms = await mergeSubmastersFunc(mergeSubmastersID, Master); + mergedSynonyms = [...mergedSynonyms, ...synonym]; + const data = { + ...restBody, + synonym: mergedSynonyms, + }; const id = req.params.id; - const data = req.body; if (data.isDefault) { // checking if data contains isDefault, if contains, reset all defaults const masterData: any = await getDocumentByQuery(Master, { _id: id }); @@ -63,6 +92,16 @@ export const updateMaster = catchAsync(async (req: any, res: any) => { { new: true }, { path: 'img', select: 'uri' } ); + if (mergeSubmastersID?.length > 0) { + try { + await Master.deleteMany({ _id: { $in: mergeSubmastersID } }); + defaults.onMastersMerged(mergeSubmastersID, id); + } catch (error) { + throw new Error( + `Failed to delete mergeSubmasters: ${(error as Error).message}` + ); + } + } const result = await Master.findOne({ _id: id }); if (result) { let section = result.parentCode ? 'submaster' : 'master'; @@ -165,6 +204,7 @@ export const deleteMaster = catchAsync(async (req: any, res: any) => { export const listMaster = catchAsync(async (req: any, res: any) => { let { page, limit, sort, populate } = req.body.options; const isCountOnly = req.body.isCountOnly || false; + const exclude = req.body.exclude; const search = req.body.search || ''; const customQuery = req.body.query || {}; let sortMaster = sort ? sort : { seq: 1 }; @@ -191,6 +231,7 @@ export const listMaster = catchAsync(async (req: any, res: any) => { customOptions, isCountOnly, search, + exclude, customQuery, isActive === null ? [true, false] : [isActive], populate, diff --git a/src/helpers/defaults.ts b/src/helpers/defaults.ts index b7cc35c..cded12e 100644 --- a/src/helpers/defaults.ts +++ b/src/helpers/defaults.ts @@ -2,6 +2,7 @@ import { RESPONSE_CODE, internalServerError } from "../constants/common"; export default { logger: console, + onMastersMerged: (_removedMasterIds: string[], _newMasterId: string) => {}, catchAsync: function (fn: any) { return function (req: any, res: any, next: any) { Promise.resolve(fn(req, res, next)).catch((err) => { diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts new file mode 100644 index 0000000..934d568 --- /dev/null +++ b/src/helpers/utils.ts @@ -0,0 +1,22 @@ +export async function mergeSubmastersFunc(mergeSubmastersID: object, Master: any) { + const submasters = await Master.find({ _id: { $in: mergeSubmastersID } }).select('names name'); + + const namesSet: Set = new Set(); + + submasters.forEach((submaster: any) => { + if (submaster.names) { + Object.values(submaster.names || {}).forEach((masterName) => { + namesSet.add(masterName as string); + }); + } + + if (submaster.name) { + namesSet.add(submaster.name); + } + }); + + const namesArr = Array.from(namesSet); + + return namesArr; + } + \ No newline at end of file diff --git a/src/helpers/validations/create.ts b/src/helpers/validations/create.ts index e79e335..677599e 100644 --- a/src/helpers/validations/create.ts +++ b/src/helpers/validations/create.ts @@ -29,6 +29,8 @@ export default joi deletedBy: joi.object().optional(), deletedAt: joi.date().optional(), isActive: joi.boolean().default(true), + synonym: joi.array().optional(), + mergeSubmasters: joi.array().optional() }) .custom(async (obj) => { const { parentId, code } = obj; diff --git a/src/helpers/validations/list.ts b/src/helpers/validations/list.ts index b4d542b..d4fcc1b 100644 --- a/src/helpers/validations/list.ts +++ b/src/helpers/validations/list.ts @@ -3,6 +3,7 @@ import joi from 'joi'; export default joi .object({ language: joi.string().optional(), + exclude: joi.string().optional(), search: joi.string().allow('').default(''), query: joi .object({ diff --git a/src/helpers/validations/update.ts b/src/helpers/validations/update.ts index 0f0fa2d..fbf0449 100644 --- a/src/helpers/validations/update.ts +++ b/src/helpers/validations/update.ts @@ -15,5 +15,7 @@ export default joi seq: joi.number().optional(), updatedBy: joi.object().optional(), extra: joi.string().optional(), + synonym: joi.array().optional(), + mergeSubmasters: joi.array().optional() }) .unknown(false); diff --git a/src/index.ts b/src/index.ts index e68abb4..df3f15f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ interface MastersProps { preDelete: (_record: any) => Promise<{}>; postUpdate: (_record: any) => Promise<{}>; languages: LanguageType[]; + onMastersMerged: any } export function masters({ @@ -18,7 +19,9 @@ export function masters({ preDelete, postUpdate, languages, + onMastersMerged }: Partial = defaults) { + if (typeof onMastersMerged === 'function') defaults.onMastersMerged = onMastersMerged if (typeof catchAsync === 'function') defaults.catchAsync = catchAsync; if (typeof authentication === 'function') defaults.authentication = authentication; diff --git a/src/models/Master.ts b/src/models/Master.ts index 5925e83..1472502 100644 --- a/src/models/Master.ts +++ b/src/models/Master.ts @@ -44,6 +44,7 @@ export const MasterSchema = new Schema( createdBy: { type: Schema.Types.ObjectId, ref: 'user' }, updatedBy: [{ type: Schema.Types.ObjectId, ref: 'user' }], deletedBy: { type: Schema.Types.ObjectId, ref: 'user' }, + synonym: [], }, { timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' } } ); diff --git a/src/services/master.ts b/src/services/master.ts index fbadf7c..7d53d97 100644 --- a/src/services/master.ts +++ b/src/services/master.ts @@ -62,6 +62,7 @@ export const listMaster = async ( customOptions: any, isCountOnly: any, search: any, + exclude: string, customQuery: any, onlyActive = [true], populate: any, @@ -88,6 +89,12 @@ export const listMaster = async ( }, }, ]), + { + synonym : { + $regex: search, + $options: 'i', + }, + }, { code: { $regex: search.replace(/\s+/g, '_'), @@ -132,6 +139,9 @@ export const listMaster = async ( } : {}), }; + if (exclude) { + (query as any)._id = { $ne: exclude }; + } let options = { select: [], collation: '', diff --git a/types/index.d.ts b/types/index.d.ts index 25e1dd1..f47f7ca 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -34,6 +34,7 @@ type MasterType = { createdBy: import('mongoose').ObjectId; updatedBy: [import('mongoose').ObjectId]; deletedBy: import('mongoose').ObjectId; + synonym: [] }; type EntityType = MasterType;