Enhance group and collection handling by adding style and extent properties, updating controllers and services, and implementing event handling for group creation and ungrouping.

This commit is contained in:
2025-11-01 10:57:32 +05:30
parent db884e0b28
commit 53e6860064
11 changed files with 407 additions and 310 deletions

View File

@@ -8,7 +8,7 @@ export const addGroupController = async (
): Promise<void> => { ): Promise<void> => {
try { try {
const { organization, userId } = req.user || {}; const { organization, userId } = req.user || {};
const { projectId, position, groupName, type, collections } = req.body; const { projectId, position, groupName, type, collections,style } = req.body;
const missing = Object.entries({ const missing = Object.entries({
organization, organization,
projectId, projectId,
@@ -31,7 +31,7 @@ export const addGroupController = async (
const data = { const data = {
organization: organization as string, organization: organization as string,
projectId, projectId,
position, position,style,
type, type,
groupName, groupName,
collections, collections,
@@ -101,7 +101,7 @@ export const unGroupController = async (
): Promise<void> => { ): Promise<void> => {
try { try {
const { organization, userId } = req.user || {}; const { organization, userId } = req.user || {};
const { projectId, position, groupId, collections } = req.body; const { projectId, position, groupId, collections,style } = req.body;
const missing = Object.entries({ const missing = Object.entries({
organization, organization,
projectId, projectId,
@@ -124,7 +124,7 @@ export const unGroupController = async (
organization: organization as string, organization: organization as string,
projectId, projectId,
position, position,
groupId, groupId,style,
collections, collections,
userId: userId as string, userId: userId as string,
}; };

View File

@@ -33,6 +33,7 @@ interface IAttributes {
} }
export interface ICollectionNode extends Document { export interface ICollectionNode extends Document {
type: string; type: string;
extent: string;
projectId: IProject["_id"]; projectId: IProject["_id"];
parentCollectionNodeId: ICollectionNode["_id"]; parentCollectionNodeId: ICollectionNode["_id"];
isSubCollection: boolean; isSubCollection: boolean;
@@ -119,6 +120,7 @@ const collectionSchema: Schema<ICollectionNode> = new Schema(
parentCollectionNodeId: { type: Schema.Types.ObjectId, ref: "Collection" }, parentCollectionNodeId: { type: Schema.Types.ObjectId, ref: "Collection" },
attributeparentId: { type: Schema.Types.ObjectId, ref: "Collection" }, attributeparentId: { type: Schema.Types.ObjectId, ref: "Collection" },
groupParentId: { type: Schema.Types.ObjectId, ref: "Collection" }, groupParentId: { type: Schema.Types.ObjectId, ref: "Collection" },
extent:{type:String},
collectionName: { type: String }, collectionName: { type: String },
type: { type: String, enum: ["collectionNode", "objectNode"] }, type: { type: String, enum: ["collectionNode", "objectNode"] },
backgroundColor: { type: backgroundColorSchema }, backgroundColor: { type: backgroundColorSchema },

View File

@@ -11,6 +11,10 @@ export interface IgroupModel extends Document {
y: number; y: number;
zoom: number; zoom: number;
}; };
style: {
width: number;
height: number;
};
projectId: IProject["_id"]; projectId: IProject["_id"];
collections: ICollectionNode["_id"][]; collections: ICollectionNode["_id"][];
isArchive: boolean; isArchive: boolean;
@@ -25,6 +29,10 @@ const GroupSchema = new Schema<IgroupModel>(
y: { type: Number }, y: { type: Number },
zoom: { type: Number }, zoom: { type: Number },
}, },
style: {
width: { type: Number },
height: { type: Number },
},
projectId: { type: Schema.Types.ObjectId, ref: "Project", required: true }, projectId: { type: Schema.Types.ObjectId, ref: "Project", required: true },
collections: [{ type: Schema.Types.ObjectId, ref: "Collection" }], collections: [{ type: Schema.Types.ObjectId, ref: "Collection" }],
isArchive: { type: Boolean, default: false }, isArchive: { type: Boolean, default: false },

View File

@@ -165,7 +165,7 @@ export const Nodecreation = async (
}); });
if (existingCollection) if (existingCollection)
return { status: "CollectionName already exists" }; return { status: "CollectionName already exists" };
let attributes = [{ key: "auto_id", type: "string" ,keyType:"primary"}]; let attributes = [{ key: "auto_id", type: "string",keyType:"primary" }];
const newAttributes = attributes; const newAttributes = attributes;
const updatedAttributesMap = new Map<string, any>(); const updatedAttributesMap = new Map<string, any>();
@@ -190,16 +190,9 @@ export const Nodecreation = async (
const models = [newCollectionnode.collectionName]; const models = [newCollectionnode.collectionName];
const finalResult = { const finalResult = {
collectionNodeId: newCollectionnode._id, collectionNodeId: newCollectionnode._id,
// fieldId: (newCollectionnode.attributes[0] as any)._id, fieldId: (newCollectionnode.attributes[0] as any)._id,
// models, models,
type:newCollectionnode.type,
position:newCollectionnode.position,
data:{
collectionName:newCollectionnode.collectionName,
collectionData:newCollectionnode.attributes,
}
}; };
// console.log("finalResult: ", finalResult);
const models1 = [ const models1 = [
{ {
name: newCollectionnode.collectionName, name: newCollectionnode.collectionName,
@@ -216,13 +209,13 @@ export const Nodecreation = async (
projectName: existingProject.projectName, // 👈 take from DB projectName: existingProject.projectName, // 👈 take from DB
models1, models1,
}; };
// console.log("fileData: ", fileData);
try { // try {
const fileResponse = await modelNodeFile(fileData); // const fileResponse = await modelNodeFile(fileData);
// console.log("📦 File backend response:", fileResponse); // console.log("📦 File backend response:", fileResponse);
} catch (err) { // } catch (err) {
// console.error("⚠️ File backend error:", err); // // console.error("⚠️ File backend error:", err);
} // }
return { status: "Success", data: finalResult }; return { status: "Success", data: finalResult };
} }
} }
@@ -257,8 +250,7 @@ export const updatecollection = async (
collectionName, collectionName,
userId, userId,
} = data; } = data;
console.log('data: nodeupdate', data); console.log("data:node ", data);
try { try {
const ExistingUser = await userModel(organization).findOne({ const ExistingUser = await userModel(organization).findOne({
_id: userId, _id: userId,
@@ -283,6 +275,7 @@ export const updatecollection = async (
} }
function updateModelName(oldName: string, newBaseName: string): string { function updateModelName(oldName: string, newBaseName: string): string {
console.log("newBaseName: ", newBaseName);
const [prefix, ...suffixParts] = oldName.split("_"); const [prefix, ...suffixParts] = oldName.split("_");
if (suffixParts.length > 0) { if (suffixParts.length > 0) {
@@ -293,6 +286,7 @@ export const updatecollection = async (
return newBaseName; return newBaseName;
} }
const newBaseName = collectionName as string;
// const referencedEdges = await collectionsModel(organization).find({ // const referencedEdges = await collectionsModel(organization).find({
// isArchive: false, // isArchive: false,
// projectId, // projectId,
@@ -305,7 +299,7 @@ export const updatecollection = async (
// }, // },
// }); // });
// console.log('referencedEdges: ', referencedEdges); // console.log("referencedEdges: ", referencedEdges);
// if (referencedEdges.length) { // if (referencedEdges.length) {
// for (const doc of referencedEdges) { // for (const doc of referencedEdges) {
// let updated = false; // let updated = false;
@@ -315,9 +309,9 @@ export const updatecollection = async (
// if ( // if (
// refKey?.collection_id?.toString() === collectionNodeId.toString() // refKey?.collection_id?.toString() === collectionNodeId.toString()
// ) { // ) {
// const newBaseName = collectionName as string;
// const oldName = attr.key; // const oldName = attr.key;
// const newKey = updateModelName(oldName, newBaseName); // const newKey = updateModelName(oldName, newBaseName);
// console.log("newKey: ", newKey);
// attr.key = newKey; // attr.key = newKey;
// updated = true; // updated = true;
// } // }
@@ -328,38 +322,46 @@ export const updatecollection = async (
// } // }
// } // }
// } // }
async function updateReferencedKeysRecursively( let referencedEdges
async function updateReferencedKeysRecursively(
organization: string, organization: string,
projectId: string, projectId: string,
collectionNodeId: any, collectionNodeId: any,
collectionName: string, collectionName: string,
visited = new Set<string>() visited = new Set<string>()
): Promise<void> { ) {
if (visited.has(collectionNodeId)) return; if (visited.has(collectionNodeId)) return [];
visited.add(collectionNodeId); visited.add(collectionNodeId);
const referencedEdges = await collectionsModel(organization).find({ const referencedEdges = await collectionsModel(organization).find({
isArchive: false, isArchive: false,
projectId, projectId,
$or: [ $or: [
{ "attributes.refKey.collection_id": new mongoose.Types.ObjectId(collectionNodeId) }, {
"attributes.refKey.collection_id": new mongoose.Types.ObjectId(
collectionNodeId
),
},
{ "attributes.refKey.collection_id": collectionNodeId }, { "attributes.refKey.collection_id": collectionNodeId },
], ],
}); });
if (!referencedEdges.length) return [];
// return referencedEdges
console.log('referencedEdges: ', referencedEdges); console.log("referencedEdges: ", referencedEdges);
console.log( console.log(
`🔍 Found ${referencedEdges.length} referencing collections for '${collectionName}'` `🔍 Found ${referencedEdges.length} referencing collections for '${collectionName}'`
); );
const updatedDocs = [];
for (const doc of referencedEdges) { for (const doc of referencedEdges) {
let updated = false; let updated = false;
for (const attr of doc.attributes) { for (const attr of doc.attributes) {
const refKey = attr.refKey as any; const refKey = attr.refKey as any;
if (refKey?.collection_id?.toString() === collectionNodeId.toString()) { if (
refKey?.collection_id?.toString() === collectionNodeId.toString()
) {
const oldName = attr.key; const oldName = attr.key;
const newBaseName = collectionName; const newBaseName = collectionName;
const newKey = updateModelName(oldName, newBaseName); const newKey = updateModelName(oldName, newBaseName);
@@ -375,37 +377,40 @@ async function updateReferencedKeysRecursively(
if (updated) { if (updated) {
await doc.save(); await doc.save();
updatedDocs.push(doc);
console.log(`✅ Saved changes in '${doc.collectionName}'`); console.log(`✅ Saved changes in '${doc.collectionName}'`);
} }
// 🔁 Recursive update if the collection itself has a valid name // 🔁 Recursive update if the collection itself has a valid name
if (doc.collectionName) { if (doc.collectionName) {
await updateReferencedKeysRecursively( const childUpdates: any = await updateReferencedKeysRecursively(
organization, organization,
projectId, projectId,
doc._id, doc._id,
doc.collectionName, doc.collectionName,
visited visited
); );
updatedDocs.push(...childUpdates);
} else { } else {
console.warn(`⚠️ Skipped recursion for ${doc._id} (no collectionName)`); console.warn(
`⚠️ Skipped recursion for ${doc._id} (no collectionName)`
);
} }
} }
} return updatedDocs;
const currentCollectionName = }
const currentCollectionName =
collectionName || existingCollection.collectionName; collectionName || existingCollection.collectionName;
await updateReferencedKeysRecursively( const updatedRefs = await updateReferencedKeysRecursively(
organization, organization,
projectId, projectId,
collectionNodeId, collectionNodeId,
currentCollectionName currentCollectionName
); );
console.log('updatedRefs: ', updatedRefs);
const oldName = existingCollection.collectionName; const oldName = existingCollection.collectionName;
const newName = collectionName; const newName = collectionName;
const collectionNameupdate = await collectionsModel( const collectionNameupdate = await collectionsModel(
organization organization
).findOneAndUpdate( ).findOneAndUpdate(
@@ -429,7 +434,7 @@ await updateReferencedKeysRecursively(
}, },
{ new: true } { new: true }
); );
// console.log('collectionNameupdate: ', collectionNameupdate); // console.log('collectionNameupdate: ', collectionNameupdate);
if (collectionNameupdate) { if (collectionNameupdate) {
// ✅ If name changed, trigger file rename // ✅ If name changed, trigger file rename
// if (collectionNameupdate && oldName !== newName) { // if (collectionNameupdate && oldName !== newName) {
@@ -447,7 +452,7 @@ await updateReferencedKeysRecursively(
status: "Success", status: "Success",
data: { data: {
collectionNameupdate: collectionNameupdate, collectionNameupdate: collectionNameupdate,
// updateFileds: referencedEdges, updateFileds: updatedRefs,
}, },
}; };
} else { } else {
@@ -646,29 +651,31 @@ export const GetNodesInProject = async (
); );
if (refAttribute) { if (refAttribute) {
console.log('refAttribute: ', refAttribute);
// targetFieldKey = refAttribute.key; // targetFieldKey = refAttribute.key;
targetFieldKey = e.to.fieldId; targetFieldKey = e.to.fieldId;
console.log('e: ', e.to.fieldId);
} }
} }
return { return {
edgeId: e._id, //mongo edgeid edgeId: e._id, //mongo edgeid
id: `e_source-${e.from.collection_id}-${e.from.collection_id}_${targetFieldKey}`, id: `e_source-${e.from.collection_id}-${e.from.fieldId}_${e.to.fieldId}`,
source: e.from.collection_id, source: e.from.collection_id,
sourceHandle: `source-${e.from.collection_id}-${e.from.fieldId}`, sourceHandle: `source-${e.from.collection_id}-${e.from.fieldId}`,
target: e.to.collection_id, target: e.to.collection_id,
targetHandle: `target-${e.to.collection_id}-${targetFieldKey}`, targetHandle: `target-${e.to.collection_id}-${e.to.fieldId}`,
// targetHandle: targetFieldKey || e.to.collection_id, // targetHandle: targetFieldKey || e.to.collection_id,
}; };
}) })
); );
let formattedGroups=[]; let formattedGroups = [];
const groupDatas = await groupModel(organization).find({ const groupDatas = await groupModel(organization).find({
isArchive: false, isArchive: false,
projectId:projectId, projectId: projectId,
}); });
// let formattedGroups
for (const grpdata of groupDatas) { for (const grpdata of groupDatas) {
console.log('grpdata: ', grpdata);
const collectionsDatas = await collectionsModel(organization).find({ const collectionsDatas = await collectionsModel(organization).find({
isArchive: false, isArchive: false,
projectId, projectId,
@@ -678,21 +685,20 @@ export const GetNodesInProject = async (
groupId: grpdata._id, groupId: grpdata._id,
type: grpdata.type, type: grpdata.type,
position: grpdata.position, position: grpdata.position,
style: grpdata.style,
data: { data: {
label: grpdata.groupName, label: grpdata.groupName,
childrenCount: collectionsDatas.length, childrenCount: collectionsDatas.length,
}, },
}); });
} }
// return formattedGroups
const formattedCollections = collectionNodes.map((collection: any) => { const formattedCollections = collectionNodes.map((collection: any) => {
// console.log("collection: ", collection);
// console.log("collection.groupParentId: ", collection.groupParentId);
const baseData = { const baseData = {
id: collection._id, id: collection._id,
position: collection.position, position: collection.position,
type: collection.type, type: collection.type,
parentGroupNode: `group - ${collection.groupParentId}`|| " ", parentGroupNode: `group-${collection.groupParentId}` || " ",
extent: collection.extent || " ",
data: { data: {
collectionName: collection.collectionName, collectionName: collection.collectionName,
collectionData: collection.attributes collectionData: collection.attributes
@@ -730,13 +736,11 @@ export const GetNodesInProject = async (
} }
return baseData; return baseData;
}); });
// console.log("filteredEdges: ", filteredEdges);
const finalResult = { const finalResult = {
nodes: formattedCollections, nodes: formattedCollections,
edges: filteredEdges, edges: filteredEdges,
groups: formattedGroups, groups: formattedGroups,
}; };
// console.log("finalResult: ", finalResult);
return { status: "Success", data: finalResult }; return { status: "Success", data: finalResult };
} }
} }
@@ -780,7 +784,6 @@ export const UpdateAttributes = async (
}); });
if (!existingCollection) return { status: "Collection not found" }; if (!existingCollection) return { status: "Collection not found" };
let referencedEdges;
for (const attr of attributes) { for (const attr of attributes) {
const fieldId = attr.fieldId; const fieldId = attr.fieldId;
if (attr.type === "Object") { if (attr.type === "Object") {
@@ -792,7 +795,7 @@ export const UpdateAttributes = async (
attributeparentId: fieldId, attributeparentId: fieldId,
isArchive: false, isArchive: false,
}); });
console.log("existingsubcollection: ", existingsubcollection);
if (!existingsubcollection) { if (!existingsubcollection) {
const fieldnamefind = await collectionsModel(organization).findOne( const fieldnamefind = await collectionsModel(organization).findOne(
{ {
@@ -824,10 +827,7 @@ export const UpdateAttributes = async (
}; };
} }
} }
const referencedEdges = await collectionsModel(organization).find({
if (attr.type) {
referencedEdges = await collectionsModel(organization)
.find({
isArchive: false, isArchive: false,
projectId, projectId,
attributes: { attributes: {
@@ -838,14 +838,44 @@ export const UpdateAttributes = async (
"refKey.fieldId": new mongoose.Types.ObjectId(fieldId), "refKey.fieldId": new mongoose.Types.ObjectId(fieldId),
}, },
}, },
}) });
.select("attributes projectId collectionName type");
console.log("referencedEdges: ", referencedEdges);
if (referencedEdges.length) { if (referencedEdges.length) {
for (const doc of referencedEdges) { for (const doc of referencedEdges) {
let updated = false; let updated = false;
// for (const oldattr of doc.attributes) {
// console.log("oldattr: ", oldattr);
// const refKey = oldattr.refKey as any;
// if (
// refKey?.collection_id?.toString() ===
// collectionNodeId.toString() &&
// refKey?.fieldId?.toString() === fieldId.toString()
// ) {
// const oldType = oldattr.type;
// const newType = attr.type;
// console.log("oldType:", oldType, "→ newType:", newType);
// // ✅ Update directly
// oldattr.type = newType as any;
// updated = true;
// }
// }
// if (updated) {
// doc.markModified("attributes");
// console.log("hi");
// await doc.save();
// console.log("hello");
// console.log("doc: ", doc);
// }
try { try {
for (const oldattr of doc.attributes) { for (const oldattr of doc.attributes) {
console.log("oldattr: ", oldattr);
const refKey = oldattr.refKey as any; const refKey = oldattr.refKey as any;
if ( if (
refKey?.collection_id?.toString() === refKey?.collection_id?.toString() ===
@@ -868,63 +898,6 @@ export const UpdateAttributes = async (
} }
} }
} }
}
if (attr.key) {
function updateModelName(
oldName: string,
newBaseName: string
): string {
const firstUnderscoreIndex = oldName.indexOf("_");
if (firstUnderscoreIndex === -1) {
return newBaseName;
}
let prefix = oldName.slice(0, firstUnderscoreIndex);
if (!prefix || prefix === "undefined" || prefix === "") {
prefix = existingCollection?.collectionName as string;
// console.log(`⚠️ Prefix invalid, fallback used: ${prefix}`);
}
return `${prefix}_${newBaseName}`;
}
referencedEdges = await collectionsModel(organization)
.find({
isArchive: false,
projectId,
attributes: {
$elemMatch: {
"refKey.fieldId": new mongoose.Types.ObjectId(fieldId),
},
},
})
.select("attributes projectId collectionName type");
if (referencedEdges.length) {
for (const doc of referencedEdges) {
let updated = false;
for (const oldattr of doc.attributes) {
const refKey = oldattr.refKey as any;
if (refKey?.fieldId?.toString() === fieldId.toString()) {
const oldName = oldattr.key;
const updateName = attr.key;
const newKey = updateModelName(oldName, updateName);
oldattr.key = newKey;
updated = true;
}
}
if (updated) {
await doc.save();
}
} catch (err: any) {
return { status: "Validation Error", data: err.message };
}
}
}
const editCollection = await collectionsModel( const editCollection = await collectionsModel(
organization organization
@@ -951,9 +924,9 @@ export const UpdateAttributes = async (
{ new: true } { new: true }
); );
// console.log("editCollection", editCollection); console.log("editCollection", editCollection);
} }
return { status: "Success", data: referencedEdges }; return { status: "Success" };
} }
} catch (error: unknown) { } catch (error: unknown) {
if (error instanceof Error) { if (error instanceof Error) {
@@ -1254,24 +1227,23 @@ export const DuplicateAttributes = async (
(attr: any) => attr.key.toLowerCase() === attrKey.toLowerCase() (attr: any) => attr.key.toLowerCase() === attrKey.toLowerCase()
); );
console.log("existingAttr: ", existingAttr);
if (existingAttr) { if (existingAttr) {
let counter = 1; let counter = 1;
let newKey = `${attrKey}(${counter})`; let newKey = `${attrKey}(${counter})`;
console.log("attrKey: ", attrKey);
console.log("newKey: ", newKey);
while ( while (
existingCollection.attributes.some( existingCollection.attributes.some(
(attr: any) => attr.key === newKey (attr: any) => attr.key === newKey
) )
) { ) {
console.log("hi");
counter++; counter++;
newKey = `${attrKey}(${counter})`; newKey = `${attrKey}(${counter})`;
console.log("newKey: ", newKey);
} }
// existingCollection.attributes.push({
// key: newKey,
// type: existingAttr.type,
// isArchive: false,
// });
} else { } else {
return { status: "Attribute doesnot match" }; return { status: "Attribute doesnot match" };
} }
@@ -1343,6 +1315,7 @@ export const DelAttributes = async (
data: IAttributesDel data: IAttributesDel
): Promise<Iresponse> => { ): Promise<Iresponse> => {
const { organization, userId, projectId, collectionNodeId, fieldId } = data; const { organization, userId, projectId, collectionNodeId, fieldId } = data;
console.log("data: ", data);
try { try {
const existingUser = await userModel(organization).findOne({ const existingUser = await userModel(organization).findOne({
@@ -1363,6 +1336,7 @@ export const DelAttributes = async (
isArchive: false, isArchive: false,
"attributes._id": new mongoose.Types.ObjectId(fieldId), "attributes._id": new mongoose.Types.ObjectId(fieldId),
}); });
console.log("existingCollection: ", existingCollection);
if (!existingCollection) return { status: "Collection not found" }; if (!existingCollection) return { status: "Collection not found" };
const attribute = existingCollection.attributes.find( const attribute = existingCollection.attributes.find(
@@ -1426,6 +1400,7 @@ export const addAttributes = async (
): Promise<Iresponse> => { ): Promise<Iresponse> => {
const { organization, projectId, userId, collectionNodeId, attributes } = const { organization, projectId, userId, collectionNodeId, attributes } =
data; data;
console.log("data: ", data);
try { try {
const existingUser = await userModel(organization).findOne({ const existingUser = await userModel(organization).findOne({
@@ -1453,6 +1428,7 @@ export const addAttributes = async (
const duplicate = existingAttributes.find( const duplicate = existingAttributes.find(
(exAttr) => exAttr.key === attr.key && !exAttr.isArchive (exAttr) => exAttr.key === attr.key && !exAttr.isArchive
); );
console.log("duplicate: ", duplicate);
if (duplicate) { if (duplicate) {
return { status: `Attribute "${attr.key}" already exists` }; return { status: `Attribute "${attr.key}" already exists` };
} }

View File

@@ -365,114 +365,56 @@ interface IResolvedKey {
* If "fromField" is a foreign key, it recursively traces back * If "fromField" is a foreign key, it recursively traces back
* to its original primary field and returns its reference info. * to its original primary field and returns its reference info.
*/ */
// export const resolveForeignKey = async (
// organization: string,
// fromCollection: any,
// fromField: IAttribute
// ): Promise<IResolvedKey> => {
// if (!fromField) {
// return {
// keyType: "normal",
// sourceCollectionName: fromCollection.collectionName,
// sourceCollectionId: fromCollection._id?.toString?.() || "",
// sourceFieldId: "",
// sourceFieldName: "",
// };
// }
// let keyType = fromField?.keyType || "normal";
// let sourceCollectionName = fromCollection.collectionName;
// let sourceCollectionId = fromCollection._id?.toString?.() || "";
// let sourceFieldId = fromField?._id?.toString?.() || "";
// let sourceFieldName = fromField?.key || "";
// // 🔁 If the field is foreign → trace back to original primary
// if (fromField?.keyType === "foreign" && fromField?.refKey) {
// const refCol = await collectionsModel(organization).findById(
// fromField.refKey.collection_id
// );
// if (refCol) {
// const primaryAttr = (refCol.attributes as IAttribute[]).find(
// (attr) => attr?._id?.toString?.() === fromField.refKey?.fieldId
// );
// if (primaryAttr) {
// console.log("🔁 Tracing back to original primary field...");
// // Recursive call to ensure multi-level tracing
// return await resolveForeignKey(organization, refCol, primaryAttr);
// }
// }
// }
// // ✅ If the field is primary, mark the propagated key as foreign
// if (fromField?.keyType === "primary") {
// keyType = "foreign";
// }
// return {
// keyType,
// sourceCollectionName,
// sourceCollectionId,
// sourceFieldId,
// sourceFieldName,
// };
// };
export const resolveForeignKey = async ( export const resolveForeignKey = async (
organization: string, organization: string,
fromCollection: any, fromCollection: any,
fromField?: IAttribute fromField: IAttribute
): Promise<IResolvedKey> => { ): Promise<IResolvedKey> => {
if (!fromField || !fromCollection) { if (!fromField) {
return { return {
keyType: "normal", keyType: "normal",
sourceCollectionName: fromCollection?.collectionName || "", sourceCollectionName: fromCollection.collectionName,
sourceCollectionId: fromCollection?._id?.toString?.() || "", sourceCollectionId: fromCollection._id?.toString?.() || "",
sourceFieldId: "", sourceFieldId: "",
sourceFieldName: "", sourceFieldName: "",
}; };
} }
let keyType = fromField?.keyType || "normal";
let keyType = fromField.keyType || "normal";
let sourceCollectionName = fromCollection.collectionName; let sourceCollectionName = fromCollection.collectionName;
let sourceCollectionId = fromCollection._id?.toString?.() || ""; let sourceCollectionId = fromCollection._id?.toString?.() || "";
let sourceFieldId = fromField._id?.toString?.() || ""; let sourceFieldId = fromField?._id?.toString?.() || "";
let sourceFieldName = fromField.key || ""; let sourceFieldName = fromField?.key || "";
// 🔁 Recursive tracing for both FOREIGN and SECONDARY with refKey // 🔁 If the field is foreign → trace back to original primary
if ( // if (fromField?.keyType === "foreign" && fromField?.refKey) {
// const refCol = await collectionsModel(organization).findById(
// fromField.refKey.collection_id
// );
if (
(fromField.keyType === "foreign" || fromField.keyType === "secondary") && (fromField.keyType === "foreign" || fromField.keyType === "secondary") &&
fromField.refKey fromField.refKey
) { ) {
const refCol = await collectionsModel(organization).findById( const refCol = await collectionsModel(organization).findById(
fromField.refKey.collection_id fromField.refKey.collection_id
); );
if (refCol) { if (refCol) {
const refField = (refCol.attributes as IAttribute[]).find( const primaryAttr = (refCol.attributes as IAttribute[]).find(
(attr) => attr?._id?.toString?.() === fromField.refKey?.fieldId (attr) => attr?._id?.toString?.() === fromField.refKey?.fieldId
); );
if (refField) { if (primaryAttr) {
console.log( console.log("🔁 Tracing back to original primary field...");
`🔁 Tracing ${fromField.keyType} key ${fromField.key}${refCol.collectionName}.${refField.key}`
); // Recursive call to ensure multi-level tracing
// Recursive call — trace until primary return await resolveForeignKey(organization, refCol, primaryAttr);
return await resolveForeignKey(organization, refCol, refField);
} else {
console.warn(
`⚠️ Field not found for refKey.fieldId: ${fromField.refKey?.fieldId}`
);
} }
} else {
console.warn(
`⚠️ Collection not found for refKey.collection_id: ${fromField.refKey?.collection_id}`
);
} }
} }
// ✅ Convert primary to foreign for propagation // ✅ If the field is primary, mark the propagated key as foreign
if (fromField.keyType === "primary") keyType = "foreign"; if (fromField?.keyType === "primary") {
keyType = "foreign";
}
return { return {
keyType, keyType,

View File

@@ -24,11 +24,24 @@ interface IgroupNode {
y: number; y: number;
zoom: number; zoom: number;
}; };
style: {
width: number;
height: number;
};
} }
interface IunGrp { interface IunGrp {
projectId: string; projectId: string;
groupId: string; groupId: string;
position: {
x: number;
y: number;
zoom: number;
};
style: {
width: number;
height: number;
};
userId: string; userId: string;
organization: string; organization: string;
collections: IcollectionsNode[]; collections: IcollectionsNode[];
@@ -41,6 +54,7 @@ export const groupcreationService = async (
organization, organization,
projectId, projectId,
position, position,
style,
userId, userId,
groupName, groupName,
type, type,
@@ -95,6 +109,7 @@ export const groupcreationService = async (
groupName, groupName,
type, type,
position, position,
style,
projectId, projectId,
createdBy: userId, createdBy: userId,
collections: validCollectionIds, collections: validCollectionIds,
@@ -102,7 +117,7 @@ export const groupcreationService = async (
if (validCollectionIds.length > 0) { if (validCollectionIds.length > 0) {
await collectionsModel(organization).updateMany( await collectionsModel(organization).updateMany(
{ _id: { $in: validCollectionIds } }, { _id: { $in: validCollectionIds } },
{ $set: { groupParentId: newGroup._id } } { $set: { groupParentId: newGroup._id, extent: "parent" } }
); );
} }
if (invalidIds.length > 0) { if (invalidIds.length > 0) {
@@ -148,10 +163,12 @@ export const groupcreationService = async (
if (newIds.length > 0) { if (newIds.length > 0) {
existingGroup.collections.push(...newIds); existingGroup.collections.push(...newIds);
existingGroup.style;
existingGroup.position;
await existingGroup.save(); await existingGroup.save();
await collectionsModel(organization).updateMany( await collectionsModel(organization).updateMany(
{ _id: { $in: newIds } }, { _id: { $in: newIds } },
{ $set: { groupParentId: existingGroup._id } } { $set: { groupParentId: existingGroup._id, extent: "parent" } }
); );
} }
@@ -178,7 +195,15 @@ export const groupcreationService = async (
}; };
export const unGrpService = async (data: IunGrp): Promise<Iresponse> => { export const unGrpService = async (data: IunGrp): Promise<Iresponse> => {
const { organization, projectId, userId, groupId, collections } = data; const {
organization,
projectId,
userId,
groupId,
style,
position,
collections,
} = data;
try { try {
const ExistingUser = await userModel(organization).findOne({ const ExistingUser = await userModel(organization).findOne({
_id: userId, _id: userId,
@@ -214,6 +239,8 @@ export const unGrpService = async (data: IunGrp): Promise<Iresponse> => {
if (index1 !== -1) { if (index1 !== -1) {
existingGroup.collections.splice(index1, 1); existingGroup.collections.splice(index1, 1);
existingGroup.style;
existingGroup.position;
await existingGroup.save(); await existingGroup.save();
const deleteCollection = await collectionsModel( const deleteCollection = await collectionsModel(
@@ -226,6 +253,7 @@ export const unGrpService = async (data: IunGrp): Promise<Iresponse> => {
if (deleteCollection) { if (deleteCollection) {
deleteCollection.groupParentId = undefined; deleteCollection.groupParentId = undefined;
deleteCollection.extent = "";
await deleteCollection.save(); await deleteCollection.save();
} }
} else { } else {

View File

@@ -110,6 +110,7 @@ export const CollectionUpdateHandleEvent = async (
return; return;
} }
const result = await updatecollection(data); const result = await updatecollection(data);
console.log('result: ', result);
const status = typeof result?.status === "string" ? result.status : "unknown"; const status = typeof result?.status === "string" ? result.status : "unknown";

View File

@@ -0,0 +1,130 @@
import { Socket, Server } from "socket.io";
import { EVENTS } from "../events/events";
import {
ErrorResponse,
FinalResponse,
validateFields,
} from "../utils/socketfunctionHelpers";
import { emitToRoom } from "../utils/emitEventResponse";
import { groupcreationService, unGrpService } from "../../shared/services/groupService";
export const addGroupHandleEvent = async (
event: string,
socket: Socket,
io: Server,
data: any,
connectedUsersByOrg: {
[org: string]: { socketId: string; userId: string; role: string }[];
}
) => {
if (event !== EVENTS.addGroupCollection || !data?.organization) return;
const requiredFields = ["position", "projectId", "organization","groupName"];
const missingFields = validateFields(data, requiredFields);
if (missingFields.length > 0) {
let room: string | undefined;
if (data?.projectId) {
room = data.projectId.toString();
}
if (room) {
emitToRoom(
io,
socket,
room,
EVENTS.addGroupCollectionResponse,
ErrorResponse(missingFields, socket, room)
);
}
return;
}
const result = await groupcreationService(data);
const status = typeof result?.status === "string" ? result.status : "unknown";
const messages: Record<string, { message: string }> = {
Success: { message: "Group created successfully" },
"Project not found": { message: "Project not found" },
"User not found": { message: "User not found" },
};
const msg = messages[status] || { message: "Internal server error" };
const result_Datas =
status === "Success" && result?.data ? result.data : undefined;
const response = FinalResponse(
status,
socket,
data.organization,
messages,
result_Datas
);
let room: string | undefined;
if (data?.projectId) {
room = data.projectId.toString();
}
if (room) {
console.log('room: ', room);
emitToRoom(io, socket, room, EVENTS.addGroupCollectionResponse, response);
}
};
export const unGroupHandleEvent = async (
event: string,
socket: Socket,
io: Server,
data: any,
connectedUsersByOrg: {
[org: string]: { socketId: string; userId: string; role: string }[];
}
) => {
if (event !== EVENTS.unGroupCollection || !data?.organization) return;
const requiredFields = ["position", "projectId", "organization","groupId"];
const missingFields = validateFields(data, requiredFields);
if (missingFields.length > 0) {
let room: string | undefined;
if (data?.projectId) {
room = data.projectId.toString();
}
if (room) {
emitToRoom(
io,
socket,
room,
EVENTS.unGroupCollectionResponse,
ErrorResponse(missingFields, socket, room)
);
}
return;
}
const result = await unGrpService(data);
const status = typeof result?.status === "string" ? result.status : "unknown";
const messages: Record<string, { message: string }> = {
Success: { message: "Group created successfully" },
"Project not found": { message: "Project not found" },
"User not found": { message: "User not found" },
};
const msg = messages[status] || { message: "Internal server error" };
const result_Datas =
status === "Success" && result?.data ? result.data : undefined;
const response = FinalResponse(
status,
socket,
data.organization,
messages,
result_Datas
);
let room: string | undefined;
if (data?.projectId) {
room = data.projectId.toString();
}
if (room) {
console.log('room: ', room);
emitToRoom(io, socket, room, EVENTS.unGroupCollectionResponse, response);
}
};

View File

@@ -55,27 +55,27 @@ export const projectHandleEvent = async (
} }
const result = await projectCreationService(data); const result = await projectCreationService(data);
const status = typeof result?.status === "string" ? result.status : "unknown"; const status = typeof result?.status === "string" ? result.status : "unknown";
if (result.status === "Success") { // if (result.status === "Success") {
const fetchResult = await folderProject( // const fetchResult = await folderProject(
subBkd, // subBkd,
data.projectName, // data.projectName,
data.language, // data.language,
data.apiProtocol, // data.apiProtocol,
data.application, // data.application,
data.architecture // data.architecture
); // );
// if (fetchResult) { // // if (fetchResult) {
// res.status(200).json({ // // res.status(200).json({
// message: "Project creation unsuccessfull", // // message: "Project creation unsuccessfull",
// projectId: result.data, // // projectId: result.data,
// }); // // });
// // return { status: "Success", data: fetchResult.data }; // // // return { status: "Success", data: fetchResult.data };
// } else { // // } else {
// res.status(500).json({ // // res.status(500).json({
// message: "error", // // message: "error",
// }); // // });
// } // // }
} // }
const messages: Record<string, { message: string }> = { const messages: Record<string, { message: string }> = {
Success: { message: "Project Created Successfully" }, Success: { message: "Project Created Successfully" },
"Project Already Exists": { message: "Project Already Exists" }, "Project Already Exists": { message: "Project Already Exists" },

View File

@@ -8,6 +8,7 @@ import {
setAttributesHandleEvent, setAttributesHandleEvent,
} from "../controllers/collectionNodeController"; } from "../controllers/collectionNodeController";
import { deleteEdgeHandleEvent, edgeHandleEvent } from "../controllers/edgeController"; import { deleteEdgeHandleEvent, edgeHandleEvent } from "../controllers/edgeController";
import { addGroupHandleEvent, unGroupHandleEvent } from "../controllers/groupContoller";
import { import {
projectClearHandleEvent, projectClearHandleEvent,
projectDeleteHandleEvent, projectDeleteHandleEvent,
@@ -32,4 +33,7 @@ export const eventHandlerMap: Record<string, Function> = {
[EVENTS.edgeConnect]: edgeHandleEvent, [EVENTS.edgeConnect]: edgeHandleEvent,
[EVENTS.deleteEdgeConnect]: deleteEdgeHandleEvent, [EVENTS.deleteEdgeConnect]: deleteEdgeHandleEvent,
[EVENTS.addGroupCollection]: addGroupHandleEvent,
[EVENTS.unGroupCollection]: unGroupHandleEvent,
}; };

View File

@@ -9,7 +9,6 @@ export const EVENTS = {
roomCreated: "roomCreated", roomCreated: "roomCreated",
roomDeleted: "roomDeleted", roomDeleted: "roomDeleted",
projectCreate: "v1:project:create", projectCreate: "v1:project:create",
projectCreateResponse: "v1:response:project:create", projectCreateResponse: "v1:response:project:create",
projectUpdate: "v1:project:update", projectUpdate: "v1:project:update",
@@ -33,12 +32,19 @@ export const EVENTS = {
collectionAttributeSet: "v1:collection-node:attributeSet", collectionAttributeSet: "v1:collection-node:attributeSet",
collectionAttributeSetResponse: "v1:response:collection-node:attributeSet", collectionAttributeSetResponse: "v1:response:collection-node:attributeSet",
collectionAttributeUpdate: "v1:collection-node:attributeUpdate", collectionAttributeUpdate: "v1:collection-node:attributeUpdate",
collectionAttributeUpdateResponse: "v1:response:collection-node:attributeUpdate", collectionAttributeUpdateResponse:
"v1:response:collection-node:attributeUpdate",
collectionAttributeDelete: "v1:collection-node:attributeDelete", collectionAttributeDelete: "v1:collection-node:attributeDelete",
collectionAttributeDeleteResponse: "v1:response:collection-node:attributeDelete", collectionAttributeDeleteResponse:
"v1:response:collection-node:attributeDelete",
edgeConnect: "v1:edge:connect", edgeConnect: "v1:edge:connect",
edgeConnectResponse: "v1:response:edge:connect", edgeConnectResponse: "v1:response:edge:connect",
deleteEdgeConnect: "v1:edge:deleteConnect", deleteEdgeConnect: "v1:edge:deleteConnect",
deleteEdgeConnectResponse: "v1:response:edge:deleteConnect", deleteEdgeConnectResponse: "v1:response:edge:deleteConnect",
}
addGroupCollection: "v1:group:add",
addGroupCollectionResponse: "v1:response:group:ungroup",
unGroupCollection: "v1:group:update",
unGroupCollectionResponse: "v1:response:group:ungroup",
};