Skip to content

Commit eb052d8

Browse files
authored
fix: Error in afterSave trigger for Parse.Role due to name field (#9883)
1 parent 1de329d commit eb052d8

File tree

3 files changed

+87
-5
lines changed

3 files changed

+87
-5
lines changed

spec/CloudCodeLogger.spec.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe('Cloud Code Logger', () => {
2525
})
2626
.then(() => {
2727
return Parse.User.signUp('tester', 'abc')
28-
.catch(() => {})
28+
.catch(() => { })
2929
.then(loggedInUser => (user = loggedInUser))
3030
.then(() => Parse.User.logIn(user.get('username'), 'abc'));
3131
})
@@ -139,7 +139,7 @@ describe('Cloud Code Logger', () => {
139139
});
140140

141141
it_id('9857e15d-bb18-478d-8a67-fdaad3e89565')(it)('should log an afterSave', done => {
142-
Parse.Cloud.afterSave('MyObject', () => {});
142+
Parse.Cloud.afterSave('MyObject', () => { });
143143
new Parse.Object('MyObject')
144144
.save()
145145
.then(() => {
@@ -271,7 +271,7 @@ describe('Cloud Code Logger', () => {
271271
});
272272

273273
Parse.Cloud.run('aFunction', { foo: 'bar' })
274-
.catch(() => {})
274+
.catch(() => { })
275275
.then(() => {
276276
const logs = spy.calls.all().reverse();
277277
expect(logs[0].args[1]).toBe('Parse error: ');
@@ -384,8 +384,8 @@ describe('Cloud Code Logger', () => {
384384
Parse.Cloud.beforeSave('TestClassError', () => {
385385
throw new Error('Failed');
386386
});
387-
Parse.Cloud.beforeSave('TestClass', () => {});
388-
Parse.Cloud.afterSave('TestClass', () => {});
387+
Parse.Cloud.beforeSave('TestClass', () => { });
388+
Parse.Cloud.afterSave('TestClass', () => { });
389389

390390
spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough();
391391

spec/ParseRole.spec.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,4 +601,78 @@ describe('Parse Role testing', () => {
601601
});
602602
});
603603
});
604+
605+
it('should trigger afterSave hook when using Parse.Role', async () => {
606+
const afterSavePromise = new Promise(resolve => {
607+
Parse.Cloud.afterSave(Parse.Role, req => {
608+
expect(req.object).toBeDefined();
609+
expect(req.object.get('name')).toBe('AnotherTestRole');
610+
resolve();
611+
});
612+
});
613+
614+
const acl = new Parse.ACL();
615+
acl.setPublicReadAccess(true);
616+
const role = new Parse.Role('AnotherTestRole', acl);
617+
618+
const savedRole = await role.save({}, { useMasterKey: true });
619+
expect(savedRole.id).toBeDefined();
620+
621+
await afterSavePromise;
622+
});
623+
624+
it('should trigger beforeSave hook and allow modifying role in beforeSave', async () => {
625+
Parse.Cloud.beforeSave(Parse.Role, req => {
626+
// Add a custom field in beforeSave
627+
req.object.set('customField', 'addedInBeforeSave');
628+
});
629+
630+
const acl = new Parse.ACL();
631+
acl.setPublicReadAccess(true);
632+
const role = new Parse.Role('ModifiedRole', acl);
633+
634+
const savedRole = await role.save({}, { useMasterKey: true });
635+
expect(savedRole.id).toBeDefined();
636+
expect(savedRole.get('customField')).toBe('addedInBeforeSave');
637+
});
638+
639+
it('should trigger beforeSave hook using Parse.Role', async () => {
640+
let beforeSaveCalled = false;
641+
642+
Parse.Cloud.beforeSave(Parse.Role, req => {
643+
beforeSaveCalled = true;
644+
expect(req.object).toBeDefined();
645+
expect(req.object.get('name')).toBe('BeforeSaveWithClassRef');
646+
});
647+
648+
const acl = new Parse.ACL();
649+
acl.setPublicReadAccess(true);
650+
const role = new Parse.Role('BeforeSaveWithClassRef', acl);
651+
652+
const savedRole = await role.save({}, { useMasterKey: true });
653+
expect(savedRole.id).toBeDefined();
654+
expect(beforeSaveCalled).toBe(true);
655+
});
656+
657+
it('should allow modifying role name in beforeSave hook', async () => {
658+
Parse.Cloud.beforeSave(Parse.Role, req => {
659+
// Modify the role name in beforeSave
660+
if (req.object.get('name') === 'OriginalName') {
661+
req.object.set('name', 'ModifiedName');
662+
}
663+
});
664+
665+
const acl = new Parse.ACL();
666+
acl.setPublicReadAccess(true);
667+
const role = new Parse.Role('OriginalName', acl);
668+
669+
const savedRole = await role.save({}, { useMasterKey: true });
670+
expect(savedRole.id).toBeDefined();
671+
expect(savedRole.get('name')).toBe('ModifiedName');
672+
673+
// Verify the name was actually saved to the database
674+
const query = new Parse.Query(Parse.Role);
675+
const fetchedRole = await query.get(savedRole.id, { useMasterKey: true });
676+
expect(fetchedRole.get('name')).toBe('ModifiedName');
677+
});
604678
});

src/RestWrite.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1743,6 +1743,14 @@ RestWrite.prototype.buildParseObjects = function () {
17431743
const readOnlyAttributes = className.constructor.readOnlyAttributes
17441744
? className.constructor.readOnlyAttributes()
17451745
: [];
1746+
1747+
// For _Role class, 'name' cannot be set after the role has an objectId.
1748+
// In afterSave context, _handleSaveResponse has already set the objectId,
1749+
// so we treat 'name' as read-only to avoid Parse SDK validation errors.
1750+
const isRoleAfterSave = this.className === '_Role' && this.response && !this.query;
1751+
if (isRoleAfterSave && this.data.name && !readOnlyAttributes.includes('name')) {
1752+
readOnlyAttributes.push('name');
1753+
}
17461754
if (!this.originalData) {
17471755
for (const attribute of readOnlyAttributes) {
17481756
extraData[attribute] = this.data[attribute];

0 commit comments

Comments
 (0)