Skip to content

Commit

Permalink
Merge branch 'develop' into chore/migrate-model-livechat-visitor
Browse files Browse the repository at this point in the history
  • Loading branch information
KevLehman committed Jun 3, 2022
2 parents da4e420 + 0b1073e commit 7cd747c
Show file tree
Hide file tree
Showing 74 changed files with 4,521 additions and 1,216 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
/_templates/ @RocketChat/chat-engine
/apps/meteor/client/ @RocketChat/frontend
/apps/meteor/tests/ @RocketChat/chat-engine
/apps/meteor/app/apps/ @RocketChat/apps
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
}
],
"typescript.tsdk": "./node_modules/typescript/lib",
"cSpell.words": ["photoswipe"]
"cSpell.words": ["photoswipe", "tmid"]
}
2 changes: 1 addition & 1 deletion apps/meteor/app/api/server/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ type ActionThis<TMethod extends Method, TPathPattern extends PathPattern, TOptio
readonly request: Request;
/* @deprecated */
requestParams(): OperationParams<TMethod, TPathPattern>;
getLoggedInUser(): IUser | undefined;
getLoggedInUser(): TOptions extends { authRequired: true } ? IUser : IUser | undefined;
getPaginationItems(): {
readonly offset: number;
readonly count: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,22 @@ API.v1.addRoute(
},
);

// TODO: replace with something like client/lib/minimongo
const processQueryOptionsOnResult = (result, options = {}) => {
/* @deprecated */
const processQueryOptionsOnResult = <T extends { _id?: string } & Record<string, any>, F extends keyof T>(
result: T[],
options: {
fields?: {
[key in F]?: 1 | 0;
};
sort?: {
[key: string]: 1 | -1;
};
limit?: number;
skip?: number;
} = {},
): Pick<T, F>[] => {
if (result === undefined || result === null) {
return undefined;
return [];
}

if (Array.isArray(result)) {
Expand Down Expand Up @@ -74,66 +86,50 @@ const processQueryOptionsOnResult = (result, options = {}) => {
}
}

if (!options.fields) {
options.fields = {};
}

const fieldsToRemove = [];
const fieldsToGet = [];
const fieldsToRemove: F[] = [];
const fieldsToGet: F[] = [];

for (const field in options.fields) {
if (options.fields.hasOwnProperty(field)) {
if (options.fields[field] === 0) {
fieldsToRemove.push(field);
} else if (options.fields[field] === 1) {
fieldsToGet.push(field);
if (options.fields) {
for (const field in Object.keys(options.fields)) {
if (options.fields.hasOwnProperty(field as F)) {
if (options.fields[field as F] === 0) {
fieldsToRemove.push(field as F);
} else if (options.fields[field as F] === 1) {
fieldsToGet.push(field as F);
}
}
}
}

if (fieldsToRemove.length > 0 && fieldsToGet.length > 0) {
console.warn("Can't mix remove and get fields");
fieldsToRemove.splice(0, fieldsToRemove.length);
}

if (fieldsToGet.length > 0 && fieldsToGet.indexOf('_id') === -1) {
fieldsToGet.push('_id');
if (fieldsToGet.length > 0 && fieldsToGet.indexOf('_id' as F) === -1) {
fieldsToGet.push('_id' as F);
}

const pickFields = (obj, fields) => {
const picked = {};
fields.forEach((field) => {
if (field.indexOf('.') !== -1) {
objectPath.set(picked, field, objectPath.get(obj, field));
const pickFields = <F extends keyof T>(obj: T, fields: F[]): Pick<T, F> => {
const picked: Partial<T> = {};
fields.forEach((field: F) => {
if (String(field).indexOf('.') !== -1) {
objectPath.set(picked, String(field), objectPath.get(obj, String(field)));
} else {
picked[field] = obj[field];
}
});
return picked;
return picked as Pick<T, F>;
};

if (fieldsToRemove.length > 0 || fieldsToGet.length > 0) {
if (Array.isArray(result)) {
result = result.map((record) => {
if (fieldsToRemove.length > 0) {
return Object.fromEntries(Object.entries(record).filter(([key]) => !fieldsToRemove.includes(key)));
}

if (fieldsToGet.length > 0) {
return pickFields(record, fieldsToGet);
}
if (fieldsToRemove.length > 0 && fieldsToGet.length > 0) {
console.warn("Can't mix remove and get fields");
fieldsToRemove.splice(0, fieldsToRemove.length);
}

return null;
});
} else {
if (fieldsToRemove.length > 0 || fieldsToGet.length > 0) {
return result.map((record) => {
if (fieldsToRemove.length > 0) {
return Object.fromEntries(Object.entries(result).filter(([key]) => !fieldsToRemove.includes(key)));
return Object.fromEntries(Object.entries(record).filter(([key]) => !fieldsToRemove.includes(key as F))) as Pick<T, F>;
}

if (fieldsToGet.length > 0) {
return pickFields(result, fieldsToGet);
}
}
return pickFields(record, fieldsToGet);
});
}

return result;
Expand All @@ -149,20 +145,23 @@ API.v1.addRoute(

let commands = Object.values(slashCommands.commands);

if (query && query.command) {
if (query?.command) {
commands = commands.filter((command) => command.command === query.command);
}

const totalCount = commands.length;
commands = processQueryOptionsOnResult(commands, {
sort: sort || { name: 1 },
skip: offset,
limit: count,
fields,
});

if (fields) {
console.warn('commands.list -> fields is deprecated and will be removed in 5.0.0');
}

return API.v1.success({
commands,
commands: processQueryOptionsOnResult(commands, {
sort: sort || { name: 1 },
skip: offset,
limit: count,
fields,
}),
offset,
count: commands.length,
total: totalCount,
Expand All @@ -178,7 +177,6 @@ API.v1.addRoute(
{
post() {
const body = this.bodyParams;
const user = this.getLoggedInUser();

if (typeof body.command !== 'string') {
return API.v1.failure('You must provide a command to run.');
Expand All @@ -201,28 +199,28 @@ API.v1.addRoute(
return API.v1.failure('The command provided does not exist (or is disabled).');
}

if (!canAccessRoomId(body.roomId, user._id)) {
if (!canAccessRoomId(body.roomId, this.userId)) {
return API.v1.unauthorized();
}

const params = body.params ? body.params : '';
const message = {
_id: Random.id(),
rid: body.roomId,
msg: `/${cmd} ${params}`,
};

if (body.tmid) {
if (typeof body.tmid === 'string') {
const thread = Messages.findOneById(body.tmid);
if (!thread || thread.rid !== body.roomId) {
return API.v1.failure('Invalid thread.');
}
message.tmid = body.tmid;
}

const message = {
_id: Random.id(),
rid: body.roomId,
msg: `/${cmd} ${params}`,
...(body.tmid && { tmid: body.tmid }),
};

const { triggerId } = body;

const result = Meteor.runAsUser(user._id, () => slashCommands.run(cmd, params, message, triggerId));
const result = slashCommands.run(cmd, params, message, triggerId);

return API.v1.success({ result });
},
Expand All @@ -234,7 +232,7 @@ API.v1.addRoute(
{ authRequired: true },
{
// Expects these query params: command: 'giphy', params: 'mine', roomId: 'value'
get() {
async get() {
const query = this.queryParams;
const user = this.getLoggedInUser();

Expand All @@ -261,21 +259,18 @@ API.v1.addRoute(

const params = query.params ? query.params : '';

let preview;
Meteor.runAsUser(user._id, () => {
preview = Meteor.call('getSlashCommandPreviews', {
cmd,
params,
msg: { rid: query.roomId },
});
const preview = Meteor.call('getSlashCommandPreviews', {
cmd,
params,
msg: { rid: query.roomId },
});

return API.v1.success({ preview });
},

// Expects a body format of: { command: 'giphy', params: 'mine', roomId: 'value', tmid: 'value', triggerId: 'value', previewItem: { id: 'sadf8' type: 'image', value: 'https://dev.null/gif' } }
post() {
const body = this.bodyParams;
const user = this.getLoggedInUser();

if (typeof body.command !== 'string') {
return API.v1.failure('You must provide a command to run the preview item on.');
Expand Down Expand Up @@ -310,35 +305,33 @@ API.v1.addRoute(
return API.v1.failure('The command provided does not exist (or is disabled).');
}

if (!canAccessRoomId(body.roomId, user._id)) {
if (!canAccessRoomId(body.roomId, this.userId)) {
return API.v1.unauthorized();
}

const params = body.params ? body.params : '';
const message = {
rid: body.roomId,
};

const { params = '' } = body;
if (body.tmid) {
const thread = Messages.findOneById(body.tmid);
if (!thread || thread.rid !== body.roomId) {
return API.v1.failure('Invalid thread.');
}
message.tmid = body.tmid;
}

Meteor.runAsUser(user._id, () => {
Meteor.call(
'executeSlashCommandPreview',
{
cmd,
params,
msg: { rid: body.roomId, tmid: body.tmid },
},
body.previewItem,
body.triggerId,
);
});
const msg = {
rid: body.roomId,
...(body.tmid && { tmid: body.tmid }),
};

Meteor.call(
'executeSlashCommandPreview',
{
cmd,
params,
msg,
},
body.previewItem,
body.triggerId,
);

return API.v1.success();
},
Expand Down
11 changes: 9 additions & 2 deletions apps/meteor/app/api/server/v1/im.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
* Docs: https://github.com/RocketChat/developer-docs/blob/master/reference/api/rest-api/endpoints/team-collaboration-endpoints/im-endpoints
*/
import type { IMessage, IRoom, ISetting, ISubscription, IUpload, IUser } from '@rocket.chat/core-typings';
import { isDmDeleteProps, isDmFileProps, isDmMemberProps, isDmMessagesProps, isDmCreateProps } from '@rocket.chat/rest-typings';
import {
isDmDeleteProps,
isDmFileProps,
isDmMemberProps,
isDmMessagesProps,
isDmCreateProps,
isDmHistoryProps,
} from '@rocket.chat/rest-typings';
import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';

Expand Down Expand Up @@ -240,7 +247,7 @@ API.v1.addRoute(

API.v1.addRoute(
['dm.history', 'im.history'],
{ authRequired: true },
{ authRequired: true, validateParams: isDmHistoryProps },
{
async get() {
const { offset = 0, count = 20 } = this.getPaginationItems();
Expand Down
13 changes: 9 additions & 4 deletions apps/meteor/app/apps/server/bridges/commands.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { SlashCommandContext, ISlashCommand, ISlashCommandPreviewItem } from '@rocket.chat/apps-engine/definition/slashcommands';
import { CommandBridge } from '@rocket.chat/apps-engine/server/bridges/CommandBridge';
import type { IMessage } from '@rocket.chat/core-typings';
import type { IMessage, RequiredField, SlashCommand } from '@rocket.chat/core-typings';

import { slashCommands } from '../../../utils/server';
import { Utilities } from '../../lib/misc/Utilities';
Expand Down Expand Up @@ -114,7 +114,7 @@ export class AppCommandsBridge extends CommandBridge {
previewCallback: (!command.executePreviewItem ? undefined : this._appCommandPreviewExecutor.bind(this)) as
| typeof slashCommands.commands[string]['previewCallback']
| undefined,
};
} as SlashCommand;

slashCommands.commands[command.command.toLowerCase()] = item;
this.orch.getNotifier().commandAdded(command.command.toLowerCase());
Expand Down Expand Up @@ -160,7 +160,12 @@ export class AppCommandsBridge extends CommandBridge {
}
}

private _appCommandExecutor(command: string, parameters: any, message: IMessage, triggerId: string): void {
private _appCommandExecutor(
command: string,
parameters: any,
message: RequiredField<Partial<IMessage>, 'rid'>,
triggerId?: string,
): void {
const user = this.orch.getConverters()?.get('users').convertById(Meteor.userId());
const room = this.orch.getConverters()?.get('rooms').convertById(message.rid);
const threadId = message.tmid;
Expand Down Expand Up @@ -195,6 +200,6 @@ export class AppCommandsBridge extends CommandBridge {

const context = new SlashCommandContext(Object.freeze(user), Object.freeze(room), Object.freeze(params), threadId, triggerId);

Promise.await(this.orch.getManager()?.getCommandManager().executePreview(command, preview, context));
await this.orch.getManager()?.getCommandManager().executePreview(command, preview, context);
}
}
7 changes: 4 additions & 3 deletions apps/meteor/app/livechat/imports/server/rest/departments.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isLivechatDepartmentProps } from '@rocket.chat/rest-typings';
import { Match, check } from 'meteor/check';

import { API } from '../../../../api/server';
Expand All @@ -14,9 +15,9 @@ import {

API.v1.addRoute(
'livechat/department',
{ authRequired: true },
{ authRequired: true, validateParams: isLivechatDepartmentProps },
{
get() {
async get() {
const { offset, count } = this.getPaginationItems();
const { sort } = this.parseJsonQuery();

Expand All @@ -26,7 +27,7 @@ API.v1.addRoute(
findDepartments({
userId: this.userId,
text,
enabled,
enabled: enabled === 'true',
onlyMyDepartments: onlyMyDepartments === 'true',
excludeDepartmentId,
pagination: {
Expand Down
Loading

0 comments on commit 7cd747c

Please sign in to comment.