Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(update): set CastError path to full path if casting update fails #14161

Merged
merged 7 commits into from
Dec 24, 2023
Merged
9 changes: 8 additions & 1 deletion lib/schema/bigint.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,14 @@ SchemaBigInt.prototype.castForQuery = function($conditional, val, context) {
return this.applySetters(null, val, context);
}

return this.applySetters(val, context);
try {
return this.applySetters(val, context);
} catch (err) {
if (err instanceof CastError && err.path === this.path && this.$fullPath != null) {
err.path = this.$fullPath;
}
throw err;
}
};

/**
Expand Down
9 changes: 8 additions & 1 deletion lib/schema/boolean.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,14 @@ SchemaBoolean.prototype.castForQuery = function($conditional, val, context) {
return this.applySetters(null, val, context);
}

return this.applySetters(val, context);
try {
return this.applySetters(val, context);
} catch (err) {
if (err instanceof CastError && err.path === this.path && this.$fullPath != null) {
err.path = this.$fullPath;
}
throw err;
}
};

/**
Expand Down
11 changes: 10 additions & 1 deletion lib/schema/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,16 @@ SchemaBuffer.prototype.castForQuery = function($conditional, val, context) {
}
return handler.call(this, val);
}
const casted = this.applySetters(val, context);

let casted;
try {
casted = this.applySetters(val, context);
} catch (err) {
if (err instanceof CastError && err.path === this.path && this.$fullPath != null) {
err.path = this.$fullPath;
}
throw err;
}
return casted ? casted.toObject({ transform: false, virtuals: false }) : casted;
};

Expand Down
9 changes: 8 additions & 1 deletion lib/schema/date.js
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,14 @@ SchemaDate.prototype.$conditionalHandlers = {

SchemaDate.prototype.castForQuery = function($conditional, val, context) {
if ($conditional == null) {
return this.applySetters(val, context);
try {
return this.applySetters(val, context);
} catch (err) {
if (err instanceof CastError && err.path === this.path && this.$fullPath != null) {
err.path = this.$fullPath;
}
throw err;
}
}

const handler = this.$conditionalHandlers[$conditional];
Expand Down
11 changes: 10 additions & 1 deletion lib/schema/number.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,16 @@ SchemaNumber.prototype.castForQuery = function($conditional, val, context) {
}
return handler.call(this, val, context);
}
val = this.applySetters(val, context);

try {
val = this.applySetters(val, context);
} catch (err) {
if (err instanceof CastError && err.path === this.path && this.$fullPath != null) {
err.path = this.$fullPath;
}
throw err;
}

return val;
};

Expand Down
9 changes: 8 additions & 1 deletion lib/schema/string.js
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,14 @@ SchemaString.prototype.castForQuery = function($conditional, val, context) {
return val;
}

return this.applySetters(val, context);
try {
return this.applySetters(val, context);
} catch (err) {
if (err instanceof CastError && err.path === this.path && this.$fullPath != null) {
err.path = this.$fullPath;
}
throw err;
}
};

/*!
Expand Down
11 changes: 9 additions & 2 deletions lib/schema/uuid.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,15 @@ SchemaUUID.prototype.castForQuery = function($conditional, val, context) {
if (!handler)
throw new Error('Can\'t use ' + $conditional + ' with UUID.');
return handler.call(this, val, context);
} else {
return this.cast(val);
}

try {
return this.applySetters(val, context);
} catch (err) {
if (err instanceof CastError && err.path === this.path && this.$fullPath != null) {
err.path = this.$fullPath;
}
throw err;
}
};

Expand Down
9 changes: 8 additions & 1 deletion lib/schemaType.js
Original file line number Diff line number Diff line change
Expand Up @@ -1630,7 +1630,14 @@ SchemaType.prototype.castForQuery = function($conditional, val, context) {
return handler.call(this, val, context);
}

return this.applySetters(val, context);
try {
return this.applySetters(val, context);
} catch (err) {
if (err instanceof CastError && err.path === this.path && this.$fullPath != null) {
err.path = this.$fullPath;
}
throw err;
}
};

/**
Expand Down
30 changes: 30 additions & 0 deletions test/model.findOneAndUpdate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2200,4 +2200,34 @@ describe('model: findOneAndUpdate:', function() {
);
assert.equal(updated.defaultField, 'some non-default value');
});

it('sets CastError path to full path (gh-14114)', async function() {
const testSchema = new mongoose.Schema({
id: mongoose.Schema.Types.ObjectId,
name: String,
accessories: [
{
isEnabled: Boolean,
additionals: [
{
k: String,
v: Number
}
]
}
]
});
const Test = db.model('Test', testSchema);
const err = await Test.findOneAndUpdate(
{},
{
$set: {
'accessories.0.additionals.0.k': ['test']
}
}
).then(() => null, err => err);
assert.ok(err);
assert.equal(err.name, 'CastError');
assert.equal(err.path, 'accessories.0.additionals.0.k');
});
});
4 changes: 3 additions & 1 deletion test/schema.select.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ describe('schema select option', function() {

before(function() {
db = start();
mongoose.set('debug', true);
});

after(async function() {
await db.close();
mongoose.set('debug', false);
});

beforeEach(() => db.deleteModel(/.*/));
Expand Down Expand Up @@ -53,7 +55,7 @@ describe('schema select option', function() {
assert.equal(findByIdDocAgain.isSelected('name'), false);
assert.equal(findByIdDocAgain.isSelected('docs.name'), false);
assert.strictEqual(undefined, findByIdDocAgain.name);
const findUpdateDoc = await Test.findOneAndUpdate({ _id: doc._id });
const findUpdateDoc = await Test.findOneAndUpdate({ _id: doc._id }, { name: 'the excluded' });
assert.equal(findUpdateDoc.isSelected('name'), false);
assert.equal(findUpdateDoc.isSelected('docs.name'), false);
assert.strictEqual(undefined, findUpdateDoc.name);
Expand Down