Skip to content
110 changes: 110 additions & 0 deletions spec/ParseRole.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -601,4 +601,114 @@ describe('Parse Role testing', () => {
});
});
});

it('should trigger afterSave hook when using Parse.Role', done => {
let afterSaveCalled = false;

Parse.Cloud.afterSave(Parse.Role, req => {
afterSaveCalled = true;
expect(req.object).toBeDefined();
expect(req.object.get('name')).toBe('AnotherTestRole');
});

const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
const role = new Parse.Role('AnotherTestRole', acl);

role
.save({}, { useMasterKey: true })
.then(savedRole => {
expect(savedRole.id).toBeDefined();
// Give the afterSave hook some time to execute
return new Promise(resolve => setTimeout(resolve, 100));
})
.then(() => {
expect(afterSaveCalled).toBe(true);
done();
})
.catch(err => {
fail(`Should not have failed: ${err.message}`);
done();
});
});

it('should trigger beforeSave hook and allow modifying role in beforeSave', done => {
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);

role
.save({}, { useMasterKey: true })
.then(savedRole => {
expect(savedRole.id).toBeDefined();
expect(savedRole.get('customField')).toBe('addedInBeforeSave');
done();
})
.catch(err => {
fail(`Should not have failed: ${err.message}`);
done();
});
});

it('should trigger beforeSave hook using Parse.Role', done => {
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);

role
.save({}, { useMasterKey: true })
.then(savedRole => {
expect(savedRole.id).toBeDefined();
expect(beforeSaveCalled).toBe(true);
done();
})
.catch(err => {
fail(`Should not have failed: ${err.message}`);
done();
});
});

it('should allow modifying role name in beforeSave hook', done => {
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);

role
.save({}, { useMasterKey: true })
.then(savedRole => {
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);
return query.get(savedRole.id, { useMasterKey: true });
})
.then(fetchedRole => {
expect(fetchedRole.get('name')).toBe('ModifiedName');
done();
})
.catch(err => {
fail(`Should not have failed: ${err.message}`);
done();
});
});
});
8 changes: 8 additions & 0 deletions src/RestWrite.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
Loading