Add chat.search, chat.sendMessage, POST push.token, fix DELETE push.token, and fix the structure of the rooms/subscriptions.get results

pull/8947/head
Bradley Hilton 8 years ago
parent 677b9fc8f2
commit 75a4a62b8d
No known key found for this signature in database
GPG Key ID: 0666B2C24C43C358
  1. 15
      packages/rocketchat-api/server/api.js
  2. 73
      packages/rocketchat-api/server/v1/chat.js
  3. 49
      packages/rocketchat-api/server/v1/push.js
  4. 38
      packages/rocketchat-api/server/v1/rooms.js
  5. 4
      packages/rocketchat-api/server/v1/settings.js
  6. 36
      packages/rocketchat-api/server/v1/subscriptions.js
  7. 9
      packages/rocketchat-lib/server/methods/sendMessage.js
  8. 52
      server/methods/messageSearch.js
  9. 5
      server/publications/subscription.js

@ -13,6 +13,7 @@ class API extends Restivus {
$loki: 0,
meta: 0,
members: 0,
usernames: 0, // Please use the `channel/dm/group.members` endpoint. This is disabled for performance reasons
importIds: 0
};
this.limitedUserFieldsToExclude = {
@ -31,7 +32,7 @@ class API extends Restivus {
customFields: 0
};
this._config.defaultOptionsEndpoint = function() {
this._config.defaultOptionsEndpoint = function _defaultOptionsEndpoint() {
if (this.request.method === 'OPTIONS' && this.request.headers['access-control-request-method']) {
if (RocketChat.settings.get('API_Enable_CORS') === true) {
this.response.writeHead(200, {
@ -57,6 +58,8 @@ class API extends Restivus {
success(result={}) {
if (_.isObject(result)) {
result.success = true;
// TODO: Remove this after three versions have been released. That means at 0.64 this should be gone. ;)
result.developerWarning = '[WARNING]: The "usernames" field has been removed for performance reasons. Please use the "*.members" endpoint to get a list of members/users in a room.';
}
return {
@ -96,6 +99,16 @@ class API extends Restivus {
};
}
notFound(msg) {
return {
statusCode: 404,
body: {
success: false,
error: msg ? msg : 'Nothing was found'
}
};
}
addRoute(routes, options, endpoints) {
//Note: required if the developer didn't provide options
if (typeof endpoints === 'undefined') {

@ -17,39 +17,48 @@ RocketChat.API.v1.addRoute('chat.delete', { authRequired: true }, {
return RocketChat.API.v1.failure('The room id provided does not match where the message is from.');
}
if (this.bodyParams.asUser && msg.u._id !== this.userId && !RocketChat.authz.hasPermission(Meteor.userId(), 'force-delete-message', msg.rid)) {
return RocketChat.API.v1.failure('Unauthorized. You must have the permission "force-delete-message" to delete other\'s message as them.');
}
Meteor.runAsUser(this.bodyParams.asUser ? msg.u._id : this.userId, () => {
Meteor.call('deleteMessage', { _id: msg._id });
});
return RocketChat.API.v1.success({
_id: msg._id,
ts: Date.now()
ts: Date.now(),
message: msg
});
}
});
RocketChat.API.v1.addRoute('chat.syncMessages', { authRequired: true }, {
get() {
const { rid } = this.queryParams;
let lastUpdate = this.queryParams;
lastUpdate = lastUpdate ? new Date(lastUpdate) : lastUpdate;
if (!rid) {
return RocketChat.API.v1.failure('The "rid" query parameter must be provided.');
const { roomId, lastUpdate } = this.queryParams;
if (!roomId) {
throw new Meteor.Error('error-roomId-param-not-provided', 'The required "roomId" query param is missing.');
}
if (!lastUpdate) {
return RocketChat.API.v1.failure('The "lastUpdate" query parameter must be provided.');
throw new Meteor.Error('error-lastUpdate-param-not-provided', 'The required "lastUpdate" query param is missing.');
} else if (isNaN(Date.parse(lastUpdate))) {
throw new Meteor.Error('error-roomId-param-invalid', 'The "lastUpdate" query parameter must be a valid date.');
}
let result;
Meteor.runAsUser(this.userId, () => {
result = Meteor.call('messages/get', rid, { lastUpdate });
result = Meteor.call('messages/get', roomId, { lastUpdate: new Date(lastUpdate) });
});
if (!result) {
return RocketChat.API.v1.failure();
}
return RocketChat.API.v1.success({result});
return RocketChat.API.v1.success({
result
});
}
});
@ -59,7 +68,6 @@ RocketChat.API.v1.addRoute('chat.getMessage', { authRequired: true }, {
return RocketChat.API.v1.failure('The "msgId" query parameter must be provided.');
}
let msg;
Meteor.runAsUser(this.userId, () => {
msg = Meteor.call('getSingleMessage', this.queryParams.msgId);
@ -78,7 +86,7 @@ RocketChat.API.v1.addRoute('chat.getMessage', { authRequired: true }, {
RocketChat.API.v1.addRoute('chat.pinMessage', { authRequired: true }, {
post() {
if (!this.bodyParams.messageId || !this.bodyParams.messageId.trim()) {
throw new Meteor.Error('error-messageid-param-not-provided', 'The required "messageId" param is required.');
throw new Meteor.Error('error-messageid-param-not-provided', 'The required "messageId" param is missing.');
}
const msg = RocketChat.models.Messages.findOneById(this.bodyParams.messageId);
@ -112,6 +120,49 @@ RocketChat.API.v1.addRoute('chat.postMessage', { authRequired: true }, {
}
});
RocketChat.API.v1.addRoute('chat.search', { authRequired: true }, {
get() {
const { roomId, searchText, limit } = this.queryParams;
if (!roomId) {
throw new Meteor.Error('error-roomId-param-not-provided', 'The required "roomId" query param is missing.');
}
if (!searchText) {
throw new Meteor.Error('error-searchText-param-not-provided', 'The required "searchText" query param is missing.');
}
if (limit && (typeof limit !== 'number' || isNaN(limit) || limit <= 0)) {
throw new Meteor.Error('error-limit-param-invalid', 'The "limit" query parameter must be a valid number and be greater than 0.');
}
let result;
Meteor.runAsUser(this.userId, () => result = Meteor.call('messageSearch', searchText, roomId, limit));
return RocketChat.API.v1.success({
messages: result.messages
});
}
});
// The difference between `chat.postMessage` and `chat.sendMessage` is that `chat.sendMessage` allows
// for passing a value for `_id` and the other one doesn't. Also, `chat.sendMessage` only sends it to
// one channel whereas the other one allows for sending to more than one channel at a time.
RocketChat.API.v1.addRoute('chat.sendMessage', { authRequired: true }, {
post() {
if (!this.bodyParams.message) {
throw new Meteor.Error('error-invalid-params', 'The "message" parameter must be provided.');
}
let message;
Meteor.runAsUser(this.userId, () => message = Meteor.call('sendMessage', this.bodyParams.message));
return RocketChat.API.v1.success({
message
});
}
});
RocketChat.API.v1.addRoute('chat.starMessage', { authRequired: true }, {
post() {
if (!this.bodyParams.messageId || !this.bodyParams.messageId.trim()) {

@ -1,20 +1,57 @@
/* globals Push */
RocketChat.API.v1.addRoute('push.token/:token', { authRequired: true }, {
RocketChat.API.v1.addRoute('push.token', { authRequired: true }, {
post() {
const { type, value, appName } = this.bodyParams;
let { id } = this.bodyParams;
if (id && typeof id !== 'string') {
throw new Meteor.Error('error-id-param-not-valid', 'The required "id" body param is invalid.');
} else {
id = Random.id();
}
if (!type || (type !== 'apn' && type !== 'gcm')) {
throw new Meteor.Error('error-type-param-not-valid', 'The required "type" body param is missing or invalid.');
}
if (!value || typeof value !== 'string') {
throw new Meteor.Error('error-token-param-not-valid', 'The required "token" body param is missing or invalid.');
}
if (!appName || typeof appName !== 'string') {
throw new Meteor.Error('error-appName-param-not-valid', 'The required "appName" body param is missing or invalid.');
}
let result;
Meteor.runAsUser(this.userId, () => result = Meteor.call('raix:push-update', {
id,
token: { [type]: value },
appName,
userId: this.userId
}));
return RocketChat.API.v1.success({ result });
},
delete() {
const { token } = this.bodyParams;
if (!token || typeof token !== 'string') {
throw new Meteor.Error('error-token-param-not-valid', 'The required "token" body param is missing or invalid.');
}
const affectedRecords = Push.appCollection.remove({
$or: [{
apn: this.urlParams._id
'token.apn': token
}, {
gcm: this.urlParams._id
'token.gcm': token
}],
userId: this.userId
});
if (affectedRecords === 0) {
return {
statusCode: 404
};
return RocketChat.API.v1.notFound();
}
return RocketChat.API.v1.success();

@ -1,25 +1,27 @@
RocketChat.API.v1.addRoute('rooms.get', { authRequired: true }, {
get: {
//This is defined as such only to provide an example of how the routes can be defined :X
action() {
let updatedAt;
if (typeof this.queryParams.updatedAt === 'string') {
try {
updatedAt = new Date(this.queryParams.updatedAt);
if (updatedAt.toString() === 'Invalid Date') {
return RocketChat.API.v1.failure('Invalid date for `updatedAt`');
}
} catch (error) {
return RocketChat.API.v1.failure('Invalid date for `updatedAt`');
}
get() {
const { updatedSince } = this.queryParams;
let updatedSinceDate;
if (updatedSince) {
if (isNaN(Date.parse(updatedSince))) {
throw new Meteor.Error('error-updatedSince-param-invalid', 'The "updatedSince" query parameter must be a valid date.');
} else {
updatedSinceDate = new Date(updatedSince);
}
}
return Meteor.runAsUser(this.userId, () => {
return RocketChat.API.v1.success(Meteor.call('rooms/get', updatedAt));
});
let result;
Meteor.runAsUser(this.userId, () => result = Meteor.call('rooms/get', updatedSinceDate));
if (Array.isArray(result)) {
result = {
update: result,
remove: []
};
}
return RocketChat.API.v1.success(result);
}
});

@ -89,6 +89,8 @@ RocketChat.API.v1.addRoute('service.configurations', { authRequired: false }, {
get() {
const ServiceConfiguration = Package['service-configuration'].ServiceConfiguration;
return RocketChat.API.v1.success(ServiceConfiguration.configurations.find({}, {fields: {secret: 0}}).fetch());
return RocketChat.API.v1.success({
configurations: ServiceConfiguration.configurations.find({}, {fields: {secret: 0}}).fetch()
});
}
});

@ -1,24 +1,26 @@
RocketChat.API.v1.addRoute('subscriptions.get', { authRequired: true }, {
get: {
//This is defined as such only to provide an example of how the routes can be defined :X
action() {
let updatedAt;
get() {
const { updatedSince } = this.queryParams;
if (typeof this.queryParams.updatedAt === 'string') {
try {
updatedAt = new Date(this.queryParams.updatedAt);
if (updatedAt.toString() === 'Invalid Date') {
return RocketChat.API.v1.failure('Invalid date for `updatedAt`');
}
} catch (error) {
return RocketChat.API.v1.failure('Invalid date for `updatedAt`');
}
let updatedSinceDate;
if (updatedSince) {
if (isNaN(Date.parse(updatedSince))) {
throw new Meteor.Error('error-roomId-param-invalid', 'The "lastUpdate" query parameter must be a valid date.');
} else {
updatedSinceDate = new Date(updatedSince);
}
}
return Meteor.runAsUser(this.userId, () => {
return RocketChat.API.v1.success(Meteor.call('subscriptions/get', updatedAt));
});
let result;
Meteor.runAsUser(this.userId, () => result = Meteor.call('subscriptions/get', updatedSinceDate));
if (Array.isArray(result)) {
result = {
update: result,
remove: []
};
}
return RocketChat.API.v1.success(result);
}
});

@ -3,11 +3,13 @@ import moment from 'moment';
Meteor.methods({
sendMessage(message) {
check(message, Object);
if (!Meteor.userId()) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: 'sendMessage'
});
}
if (message.ts) {
const tsDiff = Math.abs(moment(message.ts).diff());
if (tsDiff > 60000) {
@ -22,21 +24,25 @@ Meteor.methods({
} else {
message.ts = new Date();
}
if (message.msg && message.msg.length > RocketChat.settings.get('Message_MaxAllowedSize')) {
throw new Meteor.Error('error-message-size-exceeded', 'Message size exceeds Message_MaxAllowedSize', {
method: 'sendMessage'
});
}
const user = RocketChat.models.Users.findOneById(Meteor.userId(), {
fields: {
username: 1,
name: 1
}
});
const room = Meteor.call('canAccessRoom', message.rid, user._id);
if (!room) {
return false;
}
const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(message.rid, Meteor.userId());
if (subscription && subscription.blocked || subscription.blocker) {
RocketChat.Notifications.notifyUser(Meteor.userId(), 'message', {
@ -57,12 +63,15 @@ Meteor.methods({
});
return false;
}
if (message.alias == null && RocketChat.settings.get('Message_SetNameToAliasEnabled')) {
message.alias = user.name;
}
if (Meteor.settings['public'].sandstorm) {
message.sandstormSessionId = this.connection.sandstormSessionId();
}
RocketChat.metrics.messagesSent.inc(); // TODO This line needs to be moved to it's proper place. See the comments on: https://github.com/RocketChat/Rocket.Chat/pull/5736
return RocketChat.sendMessage(user, message, room);
}

@ -2,22 +2,16 @@ import s from 'underscore.string';
Meteor.methods({
messageSearch(text, rid, limit) {
check(text, String);
check(rid, String);
check(limit, Match.Optional(Number));
// TODO: Evaluate why we are returning `users` and `channels`, as the only thing that gets set is the `messages`.
const result = {
messages: [],
users: [],
channels: []
};
const query = {};
const options = {
sort: {
ts: -1
},
limit: limit || 20
};
check(text, String);
check(rid, String);
check(limit, Match.Optional(Number));
const currentUserId = Meteor.userId();
if (!currentUserId) {
@ -25,9 +19,23 @@ Meteor.methods({
method: 'messageSearch'
});
}
// Don't process anything else if the user can't access the room
if (!Meteor.call('canAccessRoom', rid, currentUserId)) {
return result;
}
const currentUserName = Meteor.user().username;
const currentUserTimezoneOffset = Meteor.user().utcOffset;
const query = {};
const options = {
sort: {
ts: -1
},
limit: limit || 20
};
// I would place these methods at the bottom of the file for clarity but travis doesn't appreciate that.
// (no-use-before-define)
@ -121,24 +129,28 @@ Meteor.methods({
from.push(username);
return '';
});
if (from.length > 0) {
query['u.username'] = {
$regex: from.join('|'),
$options: 'i'
};
}
// Query for senders
const mention = [];
text = text.replace(/mention:([a-z0-9.-_]+)/ig, function(match, username) {
mention.push(username);
return '';
});
if (mention.length > 0) {
query['mentions.username'] = {
$regex: mention.join('|'),
$options: 'i'
};
}
// Filter on messages that are starred by the current user.
text = text.replace(/has:star/g, filterStarred);
// Filter on messages that have an url.
@ -184,6 +196,7 @@ Meteor.methods({
};
}
}
if (Object.keys(query).length > 0) {
query.t = {
$ne: 'rm' //hide removed messages (useful when searching for user messages)
@ -191,17 +204,14 @@ Meteor.methods({
query._hidden = {
$ne: true // don't return _hidden messages
};
if (rid != null) {
query.rid = rid;
if (Meteor.call('canAccessRoom', rid, currentUserId) !== false) {
if (!RocketChat.settings.get('Message_ShowEditedStatus')) {
options.fields = {
'editedAt': 0
};
}
result.messages = RocketChat.models.Messages.find(query, options).fetch();
}
query.rid = rid;
if (!RocketChat.settings.get('Message_ShowEditedStatus')) {
options.fields = {
'editedAt': 0
};
}
result.messages = RocketChat.models.Messages.find(query, options).fetch();
}
return result;

@ -39,9 +39,7 @@ Meteor.methods({
this.unblock();
const options = {
fields
};
const options = { fields };
const records = RocketChat.models.Subscriptions.findByUserId(Meteor.userId(), options).fetch();
@ -60,6 +58,7 @@ Meteor.methods({
}).fetch()
};
}
return records;
}
});

Loading…
Cancel
Save