|
1 | 1 | import { Apps } from '@rocket.chat/apps'; |
2 | | -import type { SlashCommand } from '@rocket.chat/core-typings'; |
| 2 | +import type { SlashCommand, SlashCommandPreviewItem } from '@rocket.chat/core-typings'; |
3 | 3 | import { Messages } from '@rocket.chat/models'; |
4 | 4 | import { Random } from '@rocket.chat/random'; |
5 | | -import { ajv, validateUnauthorizedErrorResponse, validateBadRequestErrorResponse } from '@rocket.chat/rest-typings'; |
| 5 | +import { |
| 6 | + ajv, |
| 7 | + validateUnauthorizedErrorResponse, |
| 8 | + validateBadRequestErrorResponse, |
| 9 | + validateForbiddenErrorResponse, |
| 10 | +} from '@rocket.chat/rest-typings'; |
6 | 11 | import objectPath from 'object-path'; |
7 | 12 |
|
8 | 13 | import { canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom'; |
@@ -235,171 +240,225 @@ API.v1.addRoute( |
235 | 240 | }, |
236 | 241 | ); |
237 | 242 |
|
238 | | -// Expects a body of: { command: 'gimme', params: 'any string value', roomId: 'value', triggerId: 'value' } |
239 | | -API.v1.addRoute( |
240 | | - 'commands.run', |
241 | | - { authRequired: true }, |
242 | | - { |
243 | | - async post() { |
244 | | - const body = this.bodyParams; |
| 243 | +const isCommandsRunProps = ajv.compile<{ command: string; params?: string; roomId: string; tmid?: string; triggerId: string }>({ |
| 244 | + type: 'object', |
| 245 | + properties: { |
| 246 | + command: { type: 'string' }, |
| 247 | + params: { type: 'string', nullable: true }, |
| 248 | + roomId: { type: 'string' }, |
| 249 | + tmid: { type: 'string', nullable: true }, |
| 250 | + triggerId: { type: 'string' }, |
| 251 | + }, |
| 252 | + required: ['command', 'roomId', 'triggerId'], |
| 253 | + additionalProperties: false, |
| 254 | +}); |
245 | 255 |
|
246 | | - if (typeof body.command !== 'string') { |
247 | | - return API.v1.failure('You must provide a command to run.'); |
248 | | - } |
| 256 | +const commandsRunResponseSchema = ajv.compile<{ result: unknown }>({ |
| 257 | + type: 'object', |
| 258 | + properties: { |
| 259 | + result: {}, |
| 260 | + success: { type: 'boolean', enum: [true] }, |
| 261 | + }, |
| 262 | + required: ['success'], |
| 263 | + additionalProperties: true, |
| 264 | +}); |
249 | 265 |
|
250 | | - if (body.params && typeof body.params !== 'string') { |
251 | | - return API.v1.failure('The parameters for the command must be a single string.'); |
252 | | - } |
| 266 | +const isCommandsPreviewGetProps = ajv.compile<{ command: string; params?: string; roomId: string }>({ |
| 267 | + type: 'object', |
| 268 | + properties: { |
| 269 | + command: { type: 'string' }, |
| 270 | + params: { type: 'string', nullable: true }, |
| 271 | + roomId: { type: 'string' }, |
| 272 | + }, |
| 273 | + required: ['command', 'roomId'], |
| 274 | + additionalProperties: false, |
| 275 | +}); |
253 | 276 |
|
254 | | - if (typeof body.roomId !== 'string') { |
255 | | - return API.v1.failure("The room's id where to execute this command must be provided and be a string."); |
256 | | - } |
| 277 | +const commandsPreviewGetResponseSchema = ajv.compile<{ preview: Record<string, unknown> | undefined }>({ |
| 278 | + type: 'object', |
| 279 | + properties: { |
| 280 | + preview: { type: 'object', nullable: true }, |
| 281 | + success: { type: 'boolean', enum: [true] }, |
| 282 | + }, |
| 283 | + required: ['success'], |
| 284 | + additionalProperties: false, |
| 285 | +}); |
| 286 | + |
| 287 | +const isCommandsPreviewPostProps = ajv.compile<{ |
| 288 | + command: string; |
| 289 | + params?: string; |
| 290 | + roomId: string; |
| 291 | + tmid?: string; |
| 292 | + triggerId?: string; |
| 293 | + previewItem: SlashCommandPreviewItem; |
| 294 | +}>({ |
| 295 | + type: 'object', |
| 296 | + properties: { |
| 297 | + command: { type: 'string' }, |
| 298 | + params: { type: 'string', nullable: true }, |
| 299 | + roomId: { type: 'string' }, |
| 300 | + tmid: { type: 'string', nullable: true }, |
| 301 | + triggerId: { type: 'string', nullable: true }, |
| 302 | + previewItem: { |
| 303 | + type: 'object', |
| 304 | + properties: { |
| 305 | + id: { type: 'string' }, |
| 306 | + type: { type: 'string', enum: ['image', 'video', 'audio', 'text', 'other'] }, |
| 307 | + value: { type: 'string' }, |
| 308 | + }, |
| 309 | + required: ['id', 'type', 'value'], |
| 310 | + additionalProperties: false, |
| 311 | + }, |
| 312 | + }, |
| 313 | + required: ['command', 'roomId', 'previewItem'], |
| 314 | + additionalProperties: false, |
| 315 | +}); |
257 | 316 |
|
258 | | - if (body.tmid && typeof body.tmid !== 'string') { |
259 | | - return API.v1.failure('The tmid parameter when provided must be a string.'); |
260 | | - } |
| 317 | +const commandsPreviewPostResponseSchema = ajv.compile<void>({ |
| 318 | + type: 'object', |
| 319 | + properties: { |
| 320 | + success: { type: 'boolean', enum: [true] }, |
| 321 | + }, |
| 322 | + required: ['success'], |
| 323 | + additionalProperties: false, |
| 324 | +}); |
261 | 325 |
|
262 | | - const cmd = body.command.toLowerCase(); |
263 | | - if (!slashCommands.commands[cmd]) { |
264 | | - return API.v1.failure('The command provided does not exist (or is disabled).'); |
265 | | - } |
| 326 | +// Expects a body of: { command: 'gimme', params: 'any string value', roomId: 'value', triggerId: 'value' } |
| 327 | +API.v1.post( |
| 328 | + 'commands.run', |
| 329 | + { |
| 330 | + authRequired: true, |
| 331 | + body: isCommandsRunProps, |
| 332 | + response: { |
| 333 | + 200: commandsRunResponseSchema, |
| 334 | + 400: validateBadRequestErrorResponse, |
| 335 | + 401: validateUnauthorizedErrorResponse, |
| 336 | + 403: validateForbiddenErrorResponse, |
| 337 | + }, |
| 338 | + }, |
| 339 | + async function action() { |
| 340 | + const body = this.bodyParams; |
266 | 341 |
|
267 | | - if (!(await canAccessRoomIdAsync(body.roomId, this.userId))) { |
268 | | - return API.v1.forbidden(); |
269 | | - } |
| 342 | + const cmd = body.command.toLowerCase(); |
| 343 | + if (!slashCommands.commands[cmd]) { |
| 344 | + return API.v1.failure('The command provided does not exist (or is disabled).'); |
| 345 | + } |
270 | 346 |
|
271 | | - const params = body.params ? body.params : ''; |
272 | | - if (typeof body.tmid === 'string') { |
273 | | - const thread = await Messages.findOneById(body.tmid); |
274 | | - if (!thread || thread.rid !== body.roomId) { |
275 | | - return API.v1.failure('Invalid thread.'); |
276 | | - } |
| 347 | + if (!(await canAccessRoomIdAsync(body.roomId, this.userId))) { |
| 348 | + return API.v1.forbidden('Not allowed'); |
| 349 | + } |
| 350 | + |
| 351 | + const params = body.params ? body.params : ''; |
| 352 | + if (body.tmid) { |
| 353 | + const thread = await Messages.findOneById(body.tmid); |
| 354 | + if (thread?.rid !== body.roomId) { |
| 355 | + return API.v1.failure('Invalid thread.'); |
277 | 356 | } |
| 357 | + } |
278 | 358 |
|
279 | | - const message = { |
280 | | - _id: Random.id(), |
281 | | - rid: body.roomId, |
282 | | - msg: `/${cmd} ${params}`, |
283 | | - ...(body.tmid && { tmid: body.tmid }), |
284 | | - }; |
| 359 | + const message = { |
| 360 | + _id: Random.id(), |
| 361 | + rid: body.roomId, |
| 362 | + msg: `/${cmd} ${params}`, |
| 363 | + ...(body.tmid && { tmid: body.tmid }), |
| 364 | + }; |
285 | 365 |
|
286 | | - const { triggerId } = body; |
| 366 | + const { triggerId } = body; |
287 | 367 |
|
288 | | - const result = await slashCommands.run({ command: cmd, params, message, triggerId, userId: this.userId }); |
| 368 | + const result = await slashCommands.run({ command: cmd, params, message, triggerId, userId: this.userId }); |
289 | 369 |
|
290 | | - return API.v1.success({ result }); |
291 | | - }, |
| 370 | + return API.v1.success({ result }); |
292 | 371 | }, |
293 | 372 | ); |
294 | 373 |
|
295 | | -API.v1.addRoute( |
| 374 | +// Expects these query params: command: 'giphy', params: 'mine', roomId: 'value' |
| 375 | +API.v1.get( |
296 | 376 | 'commands.preview', |
297 | | - { authRequired: true }, |
298 | 377 | { |
299 | | - // Expects these query params: command: 'giphy', params: 'mine', roomId: 'value' |
300 | | - async get() { |
301 | | - const query = this.queryParams; |
302 | | - |
303 | | - if (typeof query.command !== 'string') { |
304 | | - return API.v1.failure('You must provide a command to get the previews from.'); |
305 | | - } |
306 | | - |
307 | | - if (query.params && typeof query.params !== 'string') { |
308 | | - return API.v1.failure('The parameters for the command must be a single string.'); |
309 | | - } |
310 | | - |
311 | | - if (typeof query.roomId !== 'string') { |
312 | | - return API.v1.failure("The room's id where the previews are being displayed must be provided and be a string."); |
313 | | - } |
314 | | - |
315 | | - const cmd = query.command.toLowerCase(); |
316 | | - if (!slashCommands.commands[cmd]) { |
317 | | - return API.v1.failure('The command provided does not exist (or is disabled).'); |
318 | | - } |
319 | | - |
320 | | - if (!(await canAccessRoomIdAsync(query.roomId, this.userId))) { |
321 | | - return API.v1.forbidden(); |
322 | | - } |
323 | | - |
324 | | - const params = query.params ? query.params : ''; |
325 | | - |
326 | | - const preview = await getSlashCommandPreviews({ |
327 | | - cmd, |
328 | | - params, |
329 | | - msg: { rid: query.roomId }, |
330 | | - userId: this.userId, |
331 | | - }); |
332 | | - |
333 | | - return API.v1.success({ preview }); |
| 378 | + authRequired: true, |
| 379 | + query: isCommandsPreviewGetProps, |
| 380 | + response: { |
| 381 | + 200: commandsPreviewGetResponseSchema, |
| 382 | + 400: validateBadRequestErrorResponse, |
| 383 | + 401: validateUnauthorizedErrorResponse, |
| 384 | + 403: validateForbiddenErrorResponse, |
334 | 385 | }, |
| 386 | + }, |
| 387 | + async function action() { |
| 388 | + const query = this.queryParams; |
335 | 389 |
|
336 | | - // 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' } } |
337 | | - async post() { |
338 | | - const body = this.bodyParams; |
339 | | - |
340 | | - if (typeof body.command !== 'string') { |
341 | | - return API.v1.failure('You must provide a command to run the preview item on.'); |
342 | | - } |
| 390 | + const cmd = query.command.toLowerCase(); |
| 391 | + if (!slashCommands.commands[cmd]) { |
| 392 | + return API.v1.failure('The command provided does not exist (or is disabled).'); |
| 393 | + } |
343 | 394 |
|
344 | | - if (body.params && typeof body.params !== 'string') { |
345 | | - return API.v1.failure('The parameters for the command must be a single string.'); |
346 | | - } |
| 395 | + if (!(await canAccessRoomIdAsync(query.roomId, this.userId))) { |
| 396 | + return API.v1.forbidden('Not allowed'); |
| 397 | + } |
347 | 398 |
|
348 | | - if (typeof body.roomId !== 'string') { |
349 | | - return API.v1.failure("The room's id where the preview is being executed in must be provided and be a string."); |
350 | | - } |
| 399 | + const params = query.params ? query.params : ''; |
351 | 400 |
|
352 | | - if (typeof body.previewItem === 'undefined') { |
353 | | - return API.v1.failure('The preview item being executed must be provided.'); |
354 | | - } |
| 401 | + const preview = await getSlashCommandPreviews({ |
| 402 | + cmd, |
| 403 | + params, |
| 404 | + msg: { rid: query.roomId }, |
| 405 | + userId: this.userId, |
| 406 | + }); |
355 | 407 |
|
356 | | - if (!body.previewItem.id || !body.previewItem.type || typeof body.previewItem.value === 'undefined') { |
357 | | - return API.v1.failure('The preview item being executed is in the wrong format.'); |
358 | | - } |
| 408 | + return API.v1.success({ preview }); |
| 409 | + }, |
| 410 | +); |
359 | 411 |
|
360 | | - if (body.tmid && typeof body.tmid !== 'string') { |
361 | | - return API.v1.failure('The tmid parameter when provided must be a string.'); |
362 | | - } |
| 412 | +// 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' } } |
| 413 | +API.v1.post( |
| 414 | + 'commands.preview', |
| 415 | + { |
| 416 | + authRequired: true, |
| 417 | + body: isCommandsPreviewPostProps, |
| 418 | + response: { |
| 419 | + 200: commandsPreviewPostResponseSchema, |
| 420 | + 400: validateBadRequestErrorResponse, |
| 421 | + 401: validateUnauthorizedErrorResponse, |
| 422 | + 403: validateForbiddenErrorResponse, |
| 423 | + }, |
| 424 | + }, |
| 425 | + async function action() { |
| 426 | + const body = this.bodyParams; |
363 | 427 |
|
364 | | - if (body.triggerId && typeof body.triggerId !== 'string') { |
365 | | - return API.v1.failure('The triggerId parameter when provided must be a string.'); |
366 | | - } |
| 428 | + const cmd = body.command.toLowerCase(); |
| 429 | + if (!slashCommands.commands[cmd]) { |
| 430 | + return API.v1.failure('The command provided does not exist (or is disabled).'); |
| 431 | + } |
367 | 432 |
|
368 | | - const cmd = body.command.toLowerCase(); |
369 | | - if (!slashCommands.commands[cmd]) { |
370 | | - return API.v1.failure('The command provided does not exist (or is disabled).'); |
371 | | - } |
| 433 | + if (!(await canAccessRoomIdAsync(body.roomId, this.userId))) { |
| 434 | + return API.v1.forbidden('Not allowed'); |
| 435 | + } |
372 | 436 |
|
373 | | - if (!(await canAccessRoomIdAsync(body.roomId, this.userId))) { |
374 | | - return API.v1.forbidden(); |
| 437 | + const { params = '' } = body; |
| 438 | + if (body.tmid) { |
| 439 | + const thread = await Messages.findOneById(body.tmid); |
| 440 | + if (thread?.rid !== body.roomId) { |
| 441 | + return API.v1.failure('Invalid thread.'); |
375 | 442 | } |
| 443 | + } |
376 | 444 |
|
377 | | - const { params = '' } = body; |
378 | | - if (body.tmid) { |
379 | | - const thread = await Messages.findOneById(body.tmid); |
380 | | - if (!thread || thread.rid !== body.roomId) { |
381 | | - return API.v1.failure('Invalid thread.'); |
382 | | - } |
383 | | - } |
| 445 | + const msg = { |
| 446 | + rid: body.roomId, |
| 447 | + ...(body.tmid && { tmid: body.tmid }), |
| 448 | + }; |
384 | 449 |
|
385 | | - const msg = { |
386 | | - rid: body.roomId, |
387 | | - ...(body.tmid && { tmid: body.tmid }), |
388 | | - }; |
389 | | - |
390 | | - await executeSlashCommandPreview( |
391 | | - { |
392 | | - cmd, |
393 | | - params, |
394 | | - msg, |
395 | | - triggerId: body.triggerId, |
396 | | - }, |
397 | | - body.previewItem, |
398 | | - this.userId, |
399 | | - ); |
| 450 | + await executeSlashCommandPreview( |
| 451 | + { |
| 452 | + cmd, |
| 453 | + params, |
| 454 | + msg, |
| 455 | + triggerId: body.triggerId, |
| 456 | + }, |
| 457 | + body.previewItem, |
| 458 | + this.userId, |
| 459 | + ); |
400 | 460 |
|
401 | | - return API.v1.success(); |
402 | | - }, |
| 461 | + return API.v1.success(); |
403 | 462 | }, |
404 | 463 | ); |
405 | 464 |
|
|
0 commit comments