Skip to content

Commit

Permalink
Merge pull request #14963 from Automattic/vkarpov15/gh-14952
Browse files Browse the repository at this point in the history
fix(document): recursively clear modified subpaths when setting deeply nested subdoc to null
  • Loading branch information
vkarpov15 authored Oct 17, 2024
2 parents 562aabd + e6c9dd3 commit 94b2873
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 3 deletions.
16 changes: 13 additions & 3 deletions lib/helpers/document/cleanModifiedSubpaths.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,21 @@ module.exports = function cleanModifiedSubpaths(doc, path, options) {
++deleted;

if (doc.$isSubdocument) {
const owner = doc.ownerDocument();
const fullPath = doc.$__fullPath(modifiedPath);
owner.$__.activePaths.clearPath(fullPath);
cleanParent(doc, modifiedPath);
}
}
}
return deleted;
};

function cleanParent(doc, path, seen = new Set()) {
if (seen.has(doc)) {
throw new Error('Infinite subdocument loop: subdoc with _id ' + doc._id + ' is a parent of itself');
}
const parent = doc.$parent();
const newPath = doc.$__pathRelativeToParent(void 0, false) + '.' + path;
parent.$__.activePaths.clearPath(newPath);
if (parent.$isSubdocument) {
cleanParent(parent, newPath, seen);
}
}
78 changes: 78 additions & 0 deletions test/document.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13978,6 +13978,84 @@ describe('document', function() {
assert.ok(Buffer.isBuffer(reloaded.pdfSettings.fileContent));
assert.strictEqual(reloaded.pdfSettings.fileContent.toString('utf8'), 'hello');
});

it('clears modified subpaths when setting deeply nested subdoc to null (gh-14952)', async function() {
const currentMilestoneSchema = new Schema(
{
id: { type: String, required: true }
},
{
_id: false
}
);

const milestoneSchema = new Schema(
{
current: {
type: currentMilestoneSchema,
required: true
}
},
{
_id: false
}
);

const campaignSchema = new Schema(
{
milestones: {
type: milestoneSchema,
required: false
}
},
{
_id: false
}
);
const questSchema = new Schema(
{
campaign: { type: campaignSchema, required: false }
},
{
_id: false
}
);

const parentSchema = new Schema({
quests: [questSchema]
});

const ParentModel = db.model('Parent', parentSchema);
const doc = new ParentModel({
quests: [
{
campaign: {
milestones: {
current: {
id: 'milestone1'
}
}
}
}
]
});

await doc.save();

// Set the nested schema to null
doc.quests[0].campaign.milestones.current = {
id: 'milestone1'
};
doc.quests[0].campaign.milestones.current = {
id: ''
};

doc.quests[0].campaign.milestones = null;
await doc.save();

const fromDb = await ParentModel.findById(doc._id).orFail();
assert.strictEqual(fromDb.quests[0].campaign.milestones, null);
});
});

describe('Check if instance function that is supplied in schema option is available', function() {
Expand Down

0 comments on commit 94b2873

Please sign in to comment.