diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index a405b6fc48..bcbc4f91cf 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -25,7 +25,7 @@ describe('Cloud Code Logger', () => { }) .then(() => { return Parse.User.signUp('tester', 'abc') - .catch(() => {}) + .catch(() => { }) .then(loggedInUser => (user = loggedInUser)) .then(() => Parse.User.logIn(user.get('username'), 'abc')); }) @@ -139,7 +139,7 @@ describe('Cloud Code Logger', () => { }); it_id('9857e15d-bb18-478d-8a67-fdaad3e89565')(it)('should log an afterSave', done => { - Parse.Cloud.afterSave('MyObject', () => {}); + Parse.Cloud.afterSave('MyObject', () => { }); new Parse.Object('MyObject') .save() .then(() => { @@ -271,7 +271,7 @@ describe('Cloud Code Logger', () => { }); Parse.Cloud.run('aFunction', { foo: 'bar' }) - .catch(() => {}) + .catch(() => { }) .then(() => { const logs = spy.calls.all().reverse(); expect(logs[0].args[1]).toBe('Parse error: '); @@ -384,8 +384,8 @@ describe('Cloud Code Logger', () => { Parse.Cloud.beforeSave('TestClassError', () => { throw new Error('Failed'); }); - Parse.Cloud.beforeSave('TestClass', () => {}); - Parse.Cloud.afterSave('TestClass', () => {}); + Parse.Cloud.beforeSave('TestClass', () => { }); + Parse.Cloud.afterSave('TestClass', () => { }); spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); diff --git a/spec/ParseRole.spec.js b/spec/ParseRole.spec.js index 35a91c6c15..95e6189a6a 100644 --- a/spec/ParseRole.spec.js +++ b/spec/ParseRole.spec.js @@ -601,4 +601,78 @@ describe('Parse Role testing', () => { }); }); }); + + it('should trigger afterSave hook when using Parse.Role', async () => { + const afterSavePromise = new Promise(resolve => { + Parse.Cloud.afterSave(Parse.Role, req => { + expect(req.object).toBeDefined(); + expect(req.object.get('name')).toBe('AnotherTestRole'); + resolve(); + }); + }); + + const acl = new Parse.ACL(); + acl.setPublicReadAccess(true); + const role = new Parse.Role('AnotherTestRole', acl); + + const savedRole = await role.save({}, { useMasterKey: true }); + expect(savedRole.id).toBeDefined(); + + await afterSavePromise; + }); + + it('should trigger beforeSave hook and allow modifying role in beforeSave', async () => { + Parse.Cloud.beforeSave(Parse.Role, req => { + // Add a custom field in beforeSave + req.object.set('customField', 'addedInBeforeSave'); + }); + + const acl = new Parse.ACL(); + acl.setPublicReadAccess(true); + const role = new Parse.Role('ModifiedRole', acl); + + const savedRole = await role.save({}, { useMasterKey: true }); + expect(savedRole.id).toBeDefined(); + expect(savedRole.get('customField')).toBe('addedInBeforeSave'); + }); + + it('should trigger beforeSave hook using Parse.Role', async () => { + let beforeSaveCalled = false; + + Parse.Cloud.beforeSave(Parse.Role, req => { + beforeSaveCalled = true; + expect(req.object).toBeDefined(); + expect(req.object.get('name')).toBe('BeforeSaveWithClassRef'); + }); + + const acl = new Parse.ACL(); + acl.setPublicReadAccess(true); + const role = new Parse.Role('BeforeSaveWithClassRef', acl); + + const savedRole = await role.save({}, { useMasterKey: true }); + expect(savedRole.id).toBeDefined(); + expect(beforeSaveCalled).toBe(true); + }); + + it('should allow modifying role name in beforeSave hook', async () => { + Parse.Cloud.beforeSave(Parse.Role, req => { + // Modify the role name in beforeSave + if (req.object.get('name') === 'OriginalName') { + req.object.set('name', 'ModifiedName'); + } + }); + + const acl = new Parse.ACL(); + acl.setPublicReadAccess(true); + const role = new Parse.Role('OriginalName', acl); + + const savedRole = await role.save({}, { useMasterKey: true }); + expect(savedRole.id).toBeDefined(); + expect(savedRole.get('name')).toBe('ModifiedName'); + + // Verify the name was actually saved to the database + const query = new Parse.Query(Parse.Role); + const fetchedRole = await query.get(savedRole.id, { useMasterKey: true }); + expect(fetchedRole.get('name')).toBe('ModifiedName'); + }); }); diff --git a/src/RestWrite.js b/src/RestWrite.js index 78dd8c8878..41b6c23468 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -1743,6 +1743,14 @@ RestWrite.prototype.buildParseObjects = function () { const readOnlyAttributes = className.constructor.readOnlyAttributes ? className.constructor.readOnlyAttributes() : []; + + // For _Role class, 'name' cannot be set after the role has an objectId. + // In afterSave context, _handleSaveResponse has already set the objectId, + // so we treat 'name' as read-only to avoid Parse SDK validation errors. + const isRoleAfterSave = this.className === '_Role' && this.response && !this.query; + if (isRoleAfterSave && this.data.name && !readOnlyAttributes.includes('name')) { + readOnlyAttributes.push('name'); + } if (!this.originalData) { for (const attribute of readOnlyAttributes) { extraData[attribute] = this.data[attribute];