Skip to content

Commit

Permalink
[NEW] Add REST API endpoints channels.setCustomFields and `groups.s…
Browse files Browse the repository at this point in the history
…etCustomFields` (#9733)

* Add channels.setCustomFields and groups.setCustomFields
Signed-off-by: Eugene Bolshakov <pub@relvarsoft.com>

* Delete unused `user` parameter
Signed-off-by: Eugene Bolshakov <pub@relvarsoft.com>

* Add tests for channels.setCustomFields and groups.setCustomFields
Signed-off-by: Eugene Bolshakov <pub@relvarsoft.com>

* Fix lint
Signed-off-by: Eugene Bolshakov <pub@relvarsoft.com>

* Fix lint
Signed-off-by: Eugene Bolshakov <pub@relvarsoft.com>

* Propogate setCustomFields to Subscriptions
Signed-off-by: Eugene Bolshakov <pub@relvarsoft.com>

* Fix semicolon
Signed-off-by: Eugene Bolshakov <pub@relvarsoft.com>
  • Loading branch information
xbolshe authored and rodrigok committed May 18, 2018
1 parent 83982a8 commit fbe8c99
Show file tree
Hide file tree
Showing 9 changed files with 450 additions and 1 deletion.
18 changes: 18 additions & 0 deletions packages/rocketchat-api/server/v1/channels.js
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,24 @@ RocketChat.API.v1.addRoute('channels.rename', { authRequired: true }, {
}
});

RocketChat.API.v1.addRoute('channels.setCustomFields', { authRequired: true }, {
post() {
if (!this.bodyParams.customFields || !(typeof this.bodyParams.customFields === 'object')) {
return RocketChat.API.v1.failure('The bodyParam "customFields" is required with a type like object.');
}

const findResult = findChannelByIdOrName({ params: this.requestParams() });

Meteor.runAsUser(this.userId, () => {
Meteor.call('saveRoomSettings', findResult._id, 'roomCustomFields', this.bodyParams.customFields);
});

return RocketChat.API.v1.success({
channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude })
});
}
});

RocketChat.API.v1.addRoute('channels.setDescription', { authRequired: true }, {
post() {
if (!this.bodyParams.description || !this.bodyParams.description.trim()) {
Expand Down
18 changes: 18 additions & 0 deletions packages/rocketchat-api/server/v1/groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,24 @@ RocketChat.API.v1.addRoute('groups.rename', { authRequired: true }, {
}
});

RocketChat.API.v1.addRoute('groups.setCustomFields', { authRequired: true }, {
post() {
if (!this.bodyParams.customFields || !(typeof this.bodyParams.customFields === 'object')) {
return RocketChat.API.v1.failure('The bodyParam "customFields" is required with a type like object.');
}

const findResult = findPrivateGroupByIdOrName({ params: this.requestParams(), userId: this.userId });

Meteor.runAsUser(this.userId, () => {
Meteor.call('saveRoomSettings', findResult.rid, 'roomCustomFields', this.bodyParams.customFields);
});

return RocketChat.API.v1.success({
group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude })
});
}
});

RocketChat.API.v1.addRoute('groups.setDescription', { authRequired: true }, {
post() {
if (!this.bodyParams.description || !this.bodyParams.description.trim()) {
Expand Down
1 change: 1 addition & 0 deletions packages/rocketchat-channel-settings/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Package.onUse(function(api) {
'server/functions/saveReactWhenReadOnly.js',
'server/functions/saveRoomType.js',
'server/functions/saveRoomTopic.js',
'server/functions/saveRoomCustomFields.js',
'server/functions/saveRoomAnnouncement.js',
'server/functions/saveRoomName.js',
'server/functions/saveRoomReadOnly.js',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
RocketChat.saveRoomCustomFields = function(rid, roomCustomFields) {
if (!Match.test(rid, String)) {
throw new Meteor.Error('invalid-room', 'Invalid room', {
'function': 'RocketChat.saveRoomCustomFields'
});
}
if (!Match.test(roomCustomFields, Object)) {
throw new Meteor.Error('invalid-roomCustomFields-type', 'Invalid roomCustomFields type', {
'function': 'RocketChat.saveRoomCustomFields'
});
}
const ret = RocketChat.models.Rooms.setCustomFieldsById(rid, roomCustomFields);

// Update customFields of any user's Subscription related with this rid
RocketChat.models.Subscriptions.updateCustomFieldsByRoomId(rid, roomCustomFields);

return ret;
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const fields = ['roomName', 'roomTopic', 'roomAnnouncement', 'roomDescription', 'roomType', 'readOnly', 'reactWhenReadOnly', 'systemMessages', 'default', 'joinCode', 'tokenpass', 'streamingOptions'];
const fields = ['roomName', 'roomTopic', 'roomAnnouncement', 'roomCustomFields', 'roomDescription', 'roomType', 'readOnly', 'reactWhenReadOnly', 'systemMessages', 'default', 'joinCode', 'tokenpass', 'streamingOptions'];
Meteor.methods({
saveRoomSettings(rid, settings, value) {
if (!Meteor.userId()) {
Expand Down Expand Up @@ -86,6 +86,11 @@ Meteor.methods({
RocketChat.saveRoomAnnouncement(rid, value, user);
}
break;
case 'roomCustomFields':
if (value !== room.customFields) {
RocketChat.saveRoomCustomFields(rid, value);
}
break;
case 'roomDescription':
if (value !== room.description) {
RocketChat.saveRoomDescription(rid, value, user);
Expand Down
12 changes: 12 additions & 0 deletions packages/rocketchat-lib/server/models/Rooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,18 @@ class ModelRooms extends RocketChat.models._Base {
return this.update(query, update);
}

setCustomFieldsById(_id, customFields) {
const query = {_id};

const update = {
$set: {
customFields
}
};

return this.update(query, update);
}

muteUsernameByRoomId(_id, username) {
const query = {_id};

Expand Down
12 changes: 12 additions & 0 deletions packages/rocketchat-lib/server/models/Subscriptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,18 @@ class ModelSubscriptions extends RocketChat.models._Base {
return this.update(query, update) && this.update(query2, update2);
}

updateCustomFieldsByRoomId(rid, cfields) {
const query = {rid};
const customFields = cfields || {};
const update = {
$set: {
customFields
}
};

return this.update(query, update, { multi: true });
}

updateTypeByRoomId(roomId, type) {
const query =
{ rid: roomId };
Expand Down
183 changes: 183 additions & 0 deletions tests/end-to-end/api/02-channels.js
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,189 @@ describe('[Channels]', function() {
.end(done);
});


describe('/channels.setCustomFields:', () => {
let cfchannel;
it('create channel with customFields', (done) => {
const customFields = {'field0':'value0'};
request.post(api('channels.create'))
.set(credentials)
.send({
name: `channel.cf.${ Date.now() }`,
customFields
})
.end((err, res) => {
cfchannel = res.body.channel;
done();
});
});
it('get customFields using channels.info', (done) => {
request.get(api('channels.info'))
.set(credentials)
.query({
roomId: cfchannel._id
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('channel.customFields.field0', 'value0');
})
.end(done);
});
it('change customFields', async(done) => {
const customFields = {'field9':'value9'};
request.post(api('channels.setCustomFields'))
.set(credentials)
.send({
roomId: cfchannel._id,
customFields
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('channel._id');
expect(res.body).to.have.nested.property('channel.name', cfchannel.name);
expect(res.body).to.have.nested.property('channel.t', 'c');
expect(res.body).to.have.nested.property('channel.customFields.field9', 'value9');
expect(res.body).to.have.not.nested.property('channel.customFields.field0', 'value0');
})
.end(done);
});
it('get customFields using channels.info', (done) => {
request.get(api('channels.info'))
.set(credentials)
.query({
roomId: cfchannel._id
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('channel.customFields.field9', 'value9');
})
.end(done);
});
it('delete channels with customFields', (done) => {
request.post(api('channels.delete'))
.set(credentials)
.send({
roomName: cfchannel.name
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
it('create channel without customFields', (done) => {
request.post(api('channels.create'))
.set(credentials)
.send({
name: `channel.cf.${ Date.now() }`
})
.end((err, res) => {
cfchannel = res.body.channel;
done();
});
});
it('set customFields with one nested field', async(done) => {
const customFields = {'field1':'value1'};
request.post(api('channels.setCustomFields'))
.set(credentials)
.send({
roomId: cfchannel._id,
customFields
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('channel._id');
expect(res.body).to.have.nested.property('channel.name', cfchannel.name);
expect(res.body).to.have.nested.property('channel.t', 'c');
expect(res.body).to.have.nested.property('channel.customFields.field1', 'value1');
})
.end(done);
});
it('set customFields with multiple nested fields', async(done) => {
const customFields = {'field2':'value2', 'field3':'value3', 'field4':'value4'};

request.post(api('channels.setCustomFields'))
.set(credentials)
.send({
roomName: cfchannel.name,
customFields
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('channel._id');
expect(res.body).to.have.nested.property('channel.name', cfchannel.name);
expect(res.body).to.have.nested.property('channel.t', 'c');
expect(res.body).to.have.nested.property('channel.customFields.field2', 'value2');
expect(res.body).to.have.nested.property('channel.customFields.field3', 'value3');
expect(res.body).to.have.nested.property('channel.customFields.field4', 'value4');
expect(res.body).to.have.not.nested.property('channel.customFields.field1', 'value1');
})
.end(done);
});
it('set customFields to empty object', async(done) => {
const customFields = {};

request.post(api('channels.setCustomFields'))
.set(credentials)
.send({
roomName: cfchannel.name,
customFields
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('channel._id');
expect(res.body).to.have.nested.property('channel.name', cfchannel.name);
expect(res.body).to.have.nested.property('channel.t', 'c');
expect(res.body).to.have.not.nested.property('channel.customFields.field2', 'value2');
expect(res.body).to.have.not.nested.property('channel.customFields.field3', 'value3');
expect(res.body).to.have.not.nested.property('channel.customFields.field4', 'value4');
})
.end(done);
});
it('set customFields as a string -> should return 400', async(done) => {
const customFields = '';

request.post(api('channels.setCustomFields'))
.set(credentials)
.send({
roomName: cfchannel.name,
customFields
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
})
.end(done);
});
it('delete channel with empty customFields', (done) => {
request.post(api('channels.delete'))
.set(credentials)
.send({
roomName: cfchannel.name
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
});

it('/channels.setJoinCode', async(done) => {
const roomInfo = await getRoomInfo(channel._id);

Expand Down
Loading

0 comments on commit fbe8c99

Please sign in to comment.