Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f915074
chore: adds a new endpoint to replace deprecated method
lucas-a-pelegrino Sep 17, 2025
c34fd1d
chore: replaces deprecated method by the new endpoint
lucas-a-pelegrino Sep 17, 2025
ab17083
tests: updates tests to use the new endpoint
lucas-a-pelegrino Sep 17, 2025
dbb86b0
chore: adds deprecation warning
lucas-a-pelegrino Sep 17, 2025
6c23a2a
fix: lint import errors
lucas-a-pelegrino Sep 17, 2025
e137035
fix: import paths
lucas-a-pelegrino Sep 17, 2025
46fe174
docs: adds changeset
lucas-a-pelegrino Sep 17, 2025
19ac582
chore: adds trycatch to handle meteor errors thrown by service function
lucas-a-pelegrino Sep 17, 2025
7d045c6
Merge branch 'develop' of github.com:RocketChat/Rocket.Chat into chor…
lucas-a-pelegrino Sep 22, 2025
0a255f7
chore: adds minor improvements to returnAsInquiry
lucas-a-pelegrino Sep 22, 2025
f2f7684
fix: lint errors
lucas-a-pelegrino Sep 22, 2025
5b01542
fix: lint errors
lucas-a-pelegrino Sep 22, 2025
d784c51
Merge remote-tracking branch 'origin/chore/v7/CTZ-61' into chore/v7/C…
lucas-a-pelegrino Sep 22, 2025
505efbc
fix: error message validation
lucas-a-pelegrino Sep 22, 2025
a669247
fix: invalid message in api test
lucas-a-pelegrino Sep 22, 2025
25bab34
fix: error message patterns
lucas-a-pelegrino Sep 23, 2025
8dcbf1b
chore: adds minor changes requested from code review
lucas-a-pelegrino Sep 23, 2025
db9b1ba
Merge branch 'develop' into chore/v7/CTZ-61
lucas-a-pelegrino Sep 23, 2025
5be06b6
Merge branch 'develop' of github.com:RocketChat/Rocket.Chat into chor…
lucas-a-pelegrino Sep 25, 2025
a5f4743
fix: import order
lucas-a-pelegrino Sep 25, 2025
ec7b110
Merge branch 'develop' into chore/v7/CTZ-61
kodiakhq[bot] Sep 26, 2025
1037b2b
Merge branch 'develop' into chore/v7/CTZ-61
kodiakhq[bot] Sep 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion apps/meteor/app/livechat/imports/server/rest/inquiries.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { Omnichannel } from '@rocket.chat/core-services';
import { LivechatInquiryStatus } from '@rocket.chat/core-typings';
import { LivechatInquiry, LivechatDepartment, Users } from '@rocket.chat/models';
import { LivechatInquiry, LivechatDepartment, Users, LivechatRooms } from '@rocket.chat/models';
import {
isGETLivechatInquiriesListParams,
isPOSTLivechatInquiriesTakeParams,
isGETLivechatInquiriesQueuedForUserParams,
isGETLivechatInquiriesGetOneParams,
validateBadRequestErrorResponse,
validateUnauthorizedErrorResponse,
validateForbiddenErrorResponse,
isPOSTLivechatInquiriesReturnAsInquiry,
POSTLivechatInquiriesReturnAsInquirySuccessResponse,
} from '@rocket.chat/rest-typings';

import { API } from '../../../../api/server';
import type { ExtractRoutesFromAPI } from '../../../../api/server/ApiClass';
import { getPaginationItems } from '../../../../api/server/helpers/getPaginationItems';
import { findInquiries, findOneInquiryByRoomId } from '../../../server/api/lib/inquiries';
import { returnRoomAsInquiry } from '../../../server/lib/rooms';
import { takeInquiry } from '../../../server/methods/takeInquiry';

API.v1.addRoute(
Expand Down Expand Up @@ -108,3 +116,45 @@ API.v1.addRoute(
},
},
);

const livechatInquiriesEndpoints = API.v1.post(
'livechat/inquiries.returnAsInquiry',
{
response: {
200: POSTLivechatInquiriesReturnAsInquirySuccessResponse,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
authRequired: true,
permissionsRequired: ['view-l-room'],
body: isPOSTLivechatInquiriesReturnAsInquiry,
},
async function action() {
const { roomId, departmentId } = this.bodyParams;

const room = await LivechatRooms.findOneById(roomId);
if (!room || room.t !== 'l') {
return API.v1.failure('invalid-room');
}

if (!room.open) {
Comment thread
lucas-a-pelegrino marked this conversation as resolved.
Outdated
return API.v1.failure('room-closed');
}

if (!(await Omnichannel.isWithinMACLimit(room))) {
return API.v1.failure('mac-limit-reached');
}
Comment thread
lucas-a-pelegrino marked this conversation as resolved.
Outdated

const result = await returnRoomAsInquiry(room, departmentId);

return API.v1.success({ result });
},
);
Comment thread
lucas-a-pelegrino marked this conversation as resolved.

type LivechatInquiriesEndpoints = ExtractRoutesFromAPI<typeof livechatInquiriesEndpoints>;

declare module '@rocket.chat/rest-typings' {
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface
interface Endpoints extends LivechatInquiriesEndpoints {}
}
2 changes: 2 additions & 0 deletions apps/meteor/app/livechat/server/methods/returnAsInquiry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { LivechatRooms } from '@rocket.chat/models';
import { Meteor } from 'meteor/meteor';

import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
import { returnRoomAsInquiry } from '../lib/rooms';

declare module '@rocket.chat/ddp-client' {
Expand All @@ -16,6 +17,7 @@ declare module '@rocket.chat/ddp-client' {

Meteor.methods<ServerMethods>({
async 'livechat:returnAsInquiry'(rid, departmentId) {
methodDeprecationLogger.method('livechat:returnAsInquiry', '8.0.0', '/v1/livechat/inquiries.returnAsInquiry');
const uid = Meteor.userId();
if (!uid || !(await hasPermissionAsync(uid, 'view-l-room'))) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IRoom } from '@rocket.chat/core-typings';
import { useMethod } from '@rocket.chat/ui-contexts';
import { useEndpoint } from '@rocket.chat/ui-contexts';
import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
import { useMutation, useQueryClient } from '@tanstack/react-query';

Expand All @@ -8,13 +8,13 @@ import { roomsQueryKeys, subscriptionsQueryKeys } from '../../../../../../lib/qu
export const useReturnChatToQueueMutation = (
options?: Omit<UseMutationOptions<void, Error, IRoom['_id']>, 'mutationFn'>,
): UseMutationResult<void, Error, IRoom['_id']> => {
const returnChatToQueue = useMethod('livechat:returnAsInquiry');
const returnChatToQueue = useEndpoint('POST', '/v1/livechat/inquiries.returnAsInquiry');

const queryClient = useQueryClient();

return useMutation({
mutationFn: async (rid) => {
await returnChatToQueue(rid);
await returnChatToQueue({ roomId: rid });
},
...options,
onSuccess: async (data, rid, context) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IRoom } from '@rocket.chat/core-typings';
import { useMethod } from '@rocket.chat/ui-contexts';
import { useEndpoint } from '@rocket.chat/ui-contexts';
import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
import { useMutation, useQueryClient } from '@tanstack/react-query';

Expand All @@ -8,13 +8,13 @@ import { roomsQueryKeys, subscriptionsQueryKeys } from '../../../../../../lib/qu
export const useReturnChatToQueueMutation = (
options?: Omit<UseMutationOptions<void, Error, IRoom['_id']>, 'mutationFn'>,
): UseMutationResult<void, Error, IRoom['_id']> => {
const returnChatToQueue = useMethod('livechat:returnAsInquiry');
const returnChatToQueue = useEndpoint('POST', '/v1/livechat/inquiries.returnAsInquiry');

const queryClient = useQueryClient();

return useMutation({
mutationFn: async (rid) => {
await returnChatToQueue(rid);
await returnChatToQueue({ roomId: rid });
},
...options,
onSuccess: async (data, rid, context) => {
Expand Down
114 changes: 33 additions & 81 deletions apps/meteor/tests/end-to-end/api/livechat/05-inquiries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { expect } from 'chai';
import { before, describe, it, after } from 'mocha';
import type { Response } from 'supertest';

import { getCredentials, api, request, credentials, methodCall } from '../../../data/api-data';
import { getCredentials, api, request, credentials } from '../../../data/api-data';
import { deleteDepartment } from '../../../data/livechat/department';
import {
closeOmnichannelRoom,
Expand All @@ -19,7 +19,7 @@ import {
startANewLivechatRoomAndTakeIt,
takeInquiry,
} from '../../../data/livechat/rooms';
import { parseMethodResponse, sleep } from '../../../data/livechat/utils';
import { sleep } from '../../../data/livechat/utils';
import {
removePermissionFromAllRoles,
restorePermissionToRoles,
Expand Down Expand Up @@ -324,7 +324,7 @@ describe('LIVECHAT - inquiries', () => {
});
});

describe('livechat:returnAsInquiry', () => {
describe('livechat/inquiries.returnAsInquiry', () => {
let testUser: { user: IUser; credentials: Credentials };
before(async () => {
const user = await createUser();
Expand All @@ -344,81 +344,54 @@ describe('LIVECHAT - inquiries', () => {
it('should throw an error if user doesnt have view-l-room permission', async () => {
await removePermissionFromAllRoles('view-l-room');
const { body } = await request
.post(methodCall('livechat:returnAsInquiry'))
.post(api('livechat/inquiries.returnAsInquiry'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'livechat:returnAsInquiry',
params: ['test'],
id: 'id',
msg: 'method',
}),
})
.send({ roomId: 'test' })
.expect('Content-Type', 'application/json')
.expect(200);

const response = parseMethodResponse(body);
.expect(403);

expect(response.error.error).to.be.equal('error-not-allowed');
expect(body).to.have.property('success', false);
expect(body.error).to.have.equal('User does not have the permissions required for this action [error-unauthorized]');
});
it('should fail if provided room doesnt exists', async () => {
await restorePermissionToRoles('view-l-room');
const { body } = await request
.post(methodCall('livechat:returnAsInquiry'))
.post(api('livechat/inquiries.returnAsInquiry'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'livechat:returnAsInquiry',
params: ['test'],
id: 'id',
msg: 'method',
}),
roomId: 'test',
})
.expect('Content-Type', 'application/json')
.expect(200);
.expect(400);

const response = parseMethodResponse(body);
expect(response.error.error).to.be.equal('error-invalid-room');
expect(body).to.have.property('success', false);
expect(body).to.have.property('error', 'invalid-room');
});
it('should fail if room is not a livechat room', async () => {
const { body } = await request
.post(methodCall('livechat:returnAsInquiry'))
.post(api('livechat/inquiries.returnAsInquiry'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'livechat:returnAsInquiry',
params: ['GENERAL'],
id: 'id',
msg: 'method',
}),
})
.send({ roomId: 'GENERAL' })
.expect('Content-Type', 'application/json')
.expect(200);
.expect(400);

const response = parseMethodResponse(body);
expect(response.error.error).to.be.equal('error-invalid-room');
expect(body).to.have.property('success', false);
expect(body).to.have.property('error', 'invalid-room');
});
it('should fail if room is closed', async () => {
const visitor = await createVisitor();
const room = await createLivechatRoom(visitor.token);
await closeOmnichannelRoom(room._id);

const { body } = await request
.post(methodCall('livechat:returnAsInquiry'))
.post(api('livechat/inquiries.returnAsInquiry'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'livechat:returnAsInquiry',
params: [room._id],
id: 'id',
msg: 'method',
}),
})
.send({ roomId: room._id })
.expect('Content-Type', 'application/json')
.expect(200);
.expect(400);

const response = parseMethodResponse(body);
expect(response.error.error).to.be.equal('room-closed');
expect(body).to.have.property('success', false);
expect(body).to.have.property('error', 'room-closed');
});
describe('no serving', () => {
let room: IOmnichannelRoom;
Expand All @@ -431,21 +404,14 @@ describe('LIVECHAT - inquiries', () => {
});
it('should fail if no one is serving the room', async () => {
const { body } = await request
.post(methodCall('livechat:returnAsInquiry'))
.post(api('livechat/inquiries.returnAsInquiry'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'livechat:returnAsInquiry',
params: [room._id],
id: 'id',
msg: 'method',
}),
})
.send({ roomId: room._id })
.expect('Content-Type', 'application/json')
.expect(200);

const response = parseMethodResponse(body);
expect(response.result).to.be.false;
expect(body).to.have.property('success', true);
expect(body).to.have.property('result', false);
});
});

Expand All @@ -460,21 +426,14 @@ describe('LIVECHAT - inquiries', () => {
await takeInquiry(inq._id, testUser.credentials);

const { body } = await request
.post(methodCall('livechat:returnAsInquiry'))
.post(api('livechat/inquiries.returnAsInquiry'))
.set(testUser.credentials)
.send({
message: JSON.stringify({
method: 'livechat:returnAsInquiry',
params: [room._id],
id: 'id',
msg: 'method',
}),
})
.send({ roomId: room._id })
.expect('Content-Type', 'application/json')
.expect(200);

const response = parseMethodResponse(body);
expect(response.result).to.be.true;
expect(body).to.have.property('success', true);
expect(body).to.have.property('result', true);
});
(IS_EE ? it : it.skip)('should appear on users queued elements', async () => {
const { body } = await request
Expand Down Expand Up @@ -554,16 +513,9 @@ describe('LIVECHAT - inquiries', () => {
await request.post(api('livechat/message')).send({ token: visitor.token, rid: room._id, msg: msgText }).expect(200);

await request
.post(methodCall('livechat:returnAsInquiry'))
.post(api('livechat/inquiries.returnAsInquiry'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'livechat:returnAsInquiry',
params: [room._id],
id: 'id',
msg: 'method',
}),
})
.send({ roomId: room._id })
.expect('Content-Type', 'application/json')
.expect(200);

Expand Down
42 changes: 42 additions & 0 deletions packages/rest-typings/src/v1/omnichannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3389,6 +3389,48 @@ const GETLivechatInquiriesGetOneParamsSchema = {

export const isGETLivechatInquiriesGetOneParams = ajv.compile<GETLivechatInquiriesGetOneParams>(GETLivechatInquiriesGetOneParamsSchema);

type POSTLivechatInquiriesReturnAsInquiry = {
roomId: string;
departmentId?: string;
};

const POSTLivechatInquiriesReturnAsInquirySchema = {
type: 'object',
properties: {
roomId: {
type: 'string',
},
departmentId: {
type: 'string',
nullable: true,
},
},
required: ['roomId'],
additionalProperties: false,
};

export const isPOSTLivechatInquiriesReturnAsInquiry = ajv.compile<POSTLivechatInquiriesReturnAsInquiry>(
POSTLivechatInquiriesReturnAsInquirySchema,
);

const POSTLivechatInquiriesReturnAsInquirySuccessResponseSchema = {
type: 'object',
properties: {
result: {
type: 'boolean',
},
success: {
type: 'boolean',
enum: [true],
},
},
additionalProperties: false,
};

export const POSTLivechatInquiriesReturnAsInquirySuccessResponse = ajv.compile<{ result: boolean }>(
POSTLivechatInquiriesReturnAsInquirySuccessResponseSchema,
);

Comment thread
lucas-a-pelegrino marked this conversation as resolved.
type GETDashboardTotalizers = {
start: string;
end: string;
Expand Down
Loading