Skip to content

Commit 282c5ab

Browse files
committed
fix: pre-validate duplicates before dropping unique indexes and classify duplicates as 422 validations
1 parent 6827144 commit 282c5ab

2 files changed

Lines changed: 40 additions & 17 deletions

File tree

apps/dashboard-api/src/controllers/project.controller.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,9 @@ module.exports.updateCollection = async (req, res, next) => {
10161016
if (err instanceof z.ZodError) {
10171017
return next(new AppError(400, err.issues?.[0]?.message || "Validation failed"));
10181018
}
1019+
if (err.message && err.message.startsWith("Cannot create unique index on")) {
1020+
err.status = 422;
1021+
}
10191022
const statusCode = err.status || err.statusCode || 500;
10201023
const message = (statusCode >= 400 && statusCode < 500)
10211024
? err.message

packages/common/src/utils/injectModel.js

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,43 @@ async function createUniqueIndexes(Model, fields = []) {
229229
}
230230
}
231231

232+
// Pre-validate duplicates for all fields requiring index creation or recreation to leave existing indexes untouched on failure
233+
for (const field of fields) {
234+
if (!field.unique) continue;
235+
if (!UNIQUE_SUPPORTED_TYPES_SET.has(field.type)) continue;
236+
237+
const normalizedKey = normalizeKey(field.key);
238+
if (!normalizedKey) continue;
239+
240+
const indexName = `unique_${normalizedKey}_1`;
241+
const existingIndex = existingIndexes.find((idx) => idx.name === indexName);
242+
243+
if (existingIndex) {
244+
const isExistingPartial = !!existingIndex.partialFilterExpression;
245+
const isDesiredPartial = !field.required;
246+
if (isExistingPartial === isDesiredPartial) {
247+
continue;
248+
}
249+
}
250+
251+
const duplicates = await findDuplicates(
252+
Model,
253+
normalizedKey,
254+
!!field.required,
255+
);
256+
257+
if (duplicates.length > 0) {
258+
const examples = duplicates
259+
.slice(0, 3)
260+
.map((d) => JSON.stringify(d._id))
261+
.join(", ");
262+
263+
throw new Error(
264+
`Cannot create unique index on '${normalizedKey}'. ${duplicates.length} duplicate values exist.${examples ? ` Examples: ${examples}` : ""}`,
265+
);
266+
}
267+
}
268+
232269
try {
233270
for (const field of fields) {
234271
if (!field.unique) continue;
@@ -252,23 +289,6 @@ async function createUniqueIndexes(Model, fields = []) {
252289
}
253290
}
254291

255-
const duplicates = await findDuplicates(
256-
Model,
257-
normalizedKey,
258-
!!field.required,
259-
);
260-
261-
if (duplicates.length > 0) {
262-
const examples = duplicates
263-
.slice(0, 3)
264-
.map((d) => JSON.stringify(d._id))
265-
.join(", ");
266-
267-
throw new Error(
268-
`Cannot create unique index on '${normalizedKey}'. ${duplicates.length} duplicate values exist.${examples ? ` Examples: ${examples}` : ""}`,
269-
);
270-
}
271-
272292
const indexOptions = {
273293
unique: true,
274294
name: indexName,

0 commit comments

Comments
 (0)