Skip to content

Commit

Permalink
fix: instance beforeUpdate hooks should not modify any Raw if there i…
Browse files Browse the repository at this point in the history
…s no Raw assignment in them (#283)

Co-authored-by: JimmyDaddy <[email protected]>
  • Loading branch information
2 people authored and cyjake committed Feb 28, 2022
1 parent 10cf7f7 commit 6c8c347
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 7 deletions.
8 changes: 4 additions & 4 deletions src/bone.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,14 @@ class Bone {
}

/**
*
* clone instance
* @protected clone instance
* @param {Bone} target
* @memberof Bone
*/
_clone(target) {
Object.assign(this.#raw, target.getRaw());
Object.assign(this.#rawSaved, target.getRawSaved());
this.#raw = Object.assign({}, target.getRaw());
this.#rawSaved = Object.assign({}, target.getRawSaved());
this.#rawPrevious = Object.assign({}, target.getRawPrevious());
}

/**
Expand Down
23 changes: 21 additions & 2 deletions src/setup_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,28 @@ function addHook(target, hookName, func) {
if (useHooks && type === hookType.BEFORE) {
// this.change(key) or this.attributeChanged(key) should work at before update
if (method === 'update' && typeof arguments[0] === 'object' && !arguments[0] != null) {
for (const name in arguments[0]) this[name] = arguments[0][name];
const values = arguments[0];
const fields = arguments[1] && arguments[1].fields && arguments[1].fields.length? arguments[1].fields : [];
const originalRaw = {};
const changeRaw = {};
for (const name in values) {
if (!fields.length || fields.includes(name)) {
originalRaw[name] = this.attribute(name);
this[name] = values[name];
changeRaw[name] = this.attribute(name);
}
}
await func.apply(this, args);
// revert instance after before hooks
Object.keys(originalRaw).forEach((key) => {
const current = this.attribute(key);
// raw[key] may changed in beforeUpdate hooks
if (current !== originalRaw[key] && current !== changeRaw[key]) return;
this.attribute(key, originalRaw[key]);
});
} else {
await func.apply(this, args);
}
await func.apply(this, args);
}
const res = await instanceOriginFunc.call(this, ...arguments);
if (useHooks && type === hookType.AFTER) {
Expand Down
45 changes: 44 additions & 1 deletion test/unit/hooks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ const attributes = {
},
desc: {
type: DataTypes.STRING,
}
},
fingerprint: DataTypes.TEXT,
};

describe('hooks', function() {
Expand Down Expand Up @@ -136,6 +137,10 @@ describe('hooks', function() {
constructor(opts) {
super(opts);
}

getFingerprint() {
return this.attribute('fingerprint');
}
}
User.init(attributes, {
hooks: {
Expand All @@ -149,6 +154,16 @@ describe('hooks', function() {
obj.status = 11;
}
},
},
}, {
set fingerprint(value) {
if (this.attribute('fingerprint') != null) {
throw new Error('user fingerprint cannot be modified');
}
this.attribute('fingerprint', value);
},
get fingerprint() {
return undefined;
}
});

Expand All @@ -175,6 +190,34 @@ describe('hooks', function() {
});
assert.equal(user.email, '[email protected]');
assert.equal(user.status, 11);

// instance.update before hooks special logic: setup_hooks.js#L131-L151
assert.deepEqual(user.fingerprint, undefined);
assert.deepEqual(user.getFingerprint(), null);

await assert.doesNotReject(async () => {
await user.update({
fingerprint: 'halo'
});
});
assert.deepEqual(user.fingerprint, undefined);
assert.deepEqual(user.getFingerprint(), 'halo');
await assert.rejects(async () => {
await user.update({
fingerprint: 'halo'
});
}, /Error: user fingerprint cannot be modified/);

await assert.doesNotReject(async () => {
await user.update({
fingerprint: 'halo',
nickname: 'Elden Lord',
}, {
fields: [ 'nickname' ]
});
});
assert.equal(user.nickname, 'Elden Lord');

});

it('update skip hooks', async () => {
Expand Down

0 comments on commit 6c8c347

Please sign in to comment.