mirror of
https://github.com/RocketChat/Rocket.Chat.git
synced 2025-12-28 06:47:25 +00:00
chore: Adds deprecation warning for livechat:saveTag and new endpoint to replace it (#37281)
This commit is contained in:
parent
7809e0401a
commit
dc67590d14
6
.changeset/seven-otters-turn.md
Normal file
6
.changeset/seven-otters-turn.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
"@rocket.chat/meteor": patch
|
||||
"@rocket.chat/rest-typings": patch
|
||||
---
|
||||
|
||||
Adds deprecation warning for `livechat:saveTag` and new endpoint to replace it; `livechat/tags.save`
|
||||
@ -8,7 +8,7 @@ import {
|
||||
ContextualbarHeader,
|
||||
ContextualbarClose,
|
||||
} from '@rocket.chat/ui-client';
|
||||
import { useToastMessageDispatch, useMethod } from '@rocket.chat/ui-contexts';
|
||||
import { useToastMessageDispatch, useEndpoint } from '@rocket.chat/ui-contexts';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useId } from 'react';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
@ -35,7 +35,7 @@ const TagEdit = ({ tagData, currentDepartments, onClose }: TagEditProps) => {
|
||||
const handleDeleteTag = useRemoveTag();
|
||||
|
||||
const dispatchToastMessage = useToastMessageDispatch();
|
||||
const saveTag = useMethod('livechat:saveTag');
|
||||
const saveTag = useEndpoint('POST', '/v1/livechat/tags.save');
|
||||
|
||||
const { _id, name, description } = tagData || {};
|
||||
|
||||
@ -56,7 +56,11 @@ const TagEdit = ({ tagData, currentDepartments, onClose }: TagEditProps) => {
|
||||
const departmentsId = departments?.map((dep) => dep.value) || [''];
|
||||
|
||||
try {
|
||||
await saveTag(_id as unknown as string, { name, description }, departmentsId);
|
||||
await saveTag({
|
||||
_id,
|
||||
tagData: { name, description },
|
||||
...(departmentsId.length > 0 && { tagDepartments: departmentsId }),
|
||||
});
|
||||
dispatchToastMessage({ type: 'success', message: t('Saved') });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ['livechat-tags'],
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { ILivechatTag } from '@rocket.chat/core-typings';
|
||||
import type { ILivechatTag, FindTagsResult } from '@rocket.chat/core-typings';
|
||||
import { LivechatTag } from '@rocket.chat/models';
|
||||
import { escapeRegExp } from '@rocket.chat/string-helpers';
|
||||
import type { Filter, FindOptions } from 'mongodb';
|
||||
@ -19,13 +19,6 @@ type FindTagsParams = {
|
||||
viewAll?: boolean;
|
||||
};
|
||||
|
||||
type FindTagsResult = {
|
||||
tags: ILivechatTag[];
|
||||
count: number;
|
||||
offset: number;
|
||||
total: number;
|
||||
};
|
||||
|
||||
type FindTagsByIdParams = {
|
||||
userId: string;
|
||||
tagId: string;
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import {
|
||||
isPOSTLivechatTagsRemoveParams,
|
||||
POSTLivechatTagsRemoveSuccessResponse,
|
||||
isPOSTLivechatTagsSaveParams,
|
||||
POSTLivechatTagsSaveSuccessResponse,
|
||||
isPOSTLivechatTagsDeleteParams,
|
||||
POSTLivechatTagsDeleteSuccessResponse,
|
||||
validateBadRequestErrorResponse,
|
||||
validateForbiddenErrorResponse,
|
||||
validateUnauthorizedErrorResponse,
|
||||
@ -67,35 +69,51 @@ API.v1.addRoute(
|
||||
},
|
||||
);
|
||||
|
||||
const livechatTagsEndpoints = API.v1.post(
|
||||
'livechat/tags.delete',
|
||||
{
|
||||
response: {
|
||||
200: POSTLivechatTagsRemoveSuccessResponse,
|
||||
400: validateBadRequestErrorResponse,
|
||||
401: validateUnauthorizedErrorResponse,
|
||||
403: validateForbiddenErrorResponse,
|
||||
const livechatTagsEndpoints = API.v1
|
||||
.post(
|
||||
'livechat/tags.save',
|
||||
{
|
||||
response: {
|
||||
200: POSTLivechatTagsSaveSuccessResponse,
|
||||
400: validateBadRequestErrorResponse,
|
||||
401: validateUnauthorizedErrorResponse,
|
||||
403: validateForbiddenErrorResponse,
|
||||
},
|
||||
authRequired: true,
|
||||
permissions: ['manage-livechat-tags'],
|
||||
license: ['livechat-enterprise'],
|
||||
body: isPOSTLivechatTagsSaveParams,
|
||||
},
|
||||
authRequired: true,
|
||||
permissions: ['manage-livechat-tags'],
|
||||
license: ['livechat-enterprise'],
|
||||
body: isPOSTLivechatTagsRemoveParams,
|
||||
},
|
||||
async function action() {
|
||||
const { id } = this.bodyParams;
|
||||
try {
|
||||
async function action() {
|
||||
const { _id, tagData, tagDepartments } = this.bodyParams;
|
||||
|
||||
const result = await LivechatEnterprise.saveTag(_id, tagData, tagDepartments);
|
||||
|
||||
return API.v1.success(result);
|
||||
},
|
||||
)
|
||||
.post(
|
||||
'livechat/tags.delete',
|
||||
{
|
||||
response: {
|
||||
200: POSTLivechatTagsDeleteSuccessResponse,
|
||||
400: validateBadRequestErrorResponse,
|
||||
401: validateUnauthorizedErrorResponse,
|
||||
403: validateForbiddenErrorResponse,
|
||||
},
|
||||
authRequired: true,
|
||||
permissions: ['manage-livechat-tags'],
|
||||
license: ['livechat-enterprise'],
|
||||
body: isPOSTLivechatTagsDeleteParams,
|
||||
},
|
||||
async function action() {
|
||||
const { id } = this.bodyParams;
|
||||
|
||||
await LivechatEnterprise.removeTag(id);
|
||||
|
||||
return API.v1.success();
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Meteor.Error) {
|
||||
return API.v1.failure(error.reason);
|
||||
}
|
||||
|
||||
return API.v1.failure('error-removing-tag');
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
type LivechatTagsEndpoints = ExtractRoutesFromAPI<typeof livechatTagsEndpoints>;
|
||||
|
||||
|
||||
@ -141,7 +141,7 @@ export const LivechatEnterprise = {
|
||||
return LivechatTag.removeById(_id);
|
||||
},
|
||||
|
||||
async saveTag(_id: string | undefined, tagData: { name: string; description?: string }, tagDepartments: string[]) {
|
||||
async saveTag(_id: string | undefined, tagData: { name: string; description?: string }, tagDepartments: string[] | undefined) {
|
||||
return LivechatTag.createOrUpdateTag(_id, tagData, tagDepartments);
|
||||
},
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import { check } from 'meteor/check';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
import { hasPermissionAsync } from '../../../../../app/authorization/server/functions/hasPermission';
|
||||
import { methodDeprecationLogger } from '../../../../../app/lib/server/lib/deprecationWarningLogger';
|
||||
import { LivechatEnterprise } from '../lib/LivechatEnterprise';
|
||||
|
||||
declare module '@rocket.chat/ddp-client' {
|
||||
@ -15,9 +16,10 @@ declare module '@rocket.chat/ddp-client' {
|
||||
|
||||
Meteor.methods<ServerMethods>({
|
||||
async 'livechat:saveTag'(_id, tagData, tagDepartments) {
|
||||
methodDeprecationLogger.method('livechat:saveTag', '8.0.0', 'POST /v1/livechat/tags.save');
|
||||
const uid = Meteor.userId();
|
||||
if (!uid || !(await hasPermissionAsync(uid, 'manage-livechat-tags'))) {
|
||||
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'livechat:saveTags' });
|
||||
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'livechat:saveTag' });
|
||||
}
|
||||
|
||||
check(_id, Match.Maybe(String));
|
||||
|
||||
@ -1,49 +1,40 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import type { ILivechatTag } from '@rocket.chat/core-typings';
|
||||
import type { ILivechatTag, FindTagsResult } from '@rocket.chat/core-typings';
|
||||
|
||||
import { credentials, methodCall, request } from '../api-data';
|
||||
import type { DummyResponse } from './utils';
|
||||
import { credentials, request, api } from '../api-data';
|
||||
|
||||
export const saveTags = (departments: string[] = []): Promise<ILivechatTag> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
void request
|
||||
.post(methodCall(`livechat:saveTag`))
|
||||
.set(credentials)
|
||||
.send({
|
||||
message: JSON.stringify({
|
||||
method: 'livechat:saveTag',
|
||||
params: [undefined, { name: faker.string.uuid(), description: faker.lorem.sentence() }, departments],
|
||||
id: '101',
|
||||
msg: 'method',
|
||||
}),
|
||||
})
|
||||
.end((err: Error, res: DummyResponse<string, 'wrapped'>) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(JSON.parse(res.body.message).result);
|
||||
});
|
||||
});
|
||||
export const listTags = async (): Promise<FindTagsResult> => {
|
||||
const { body } = await request.get(api('livechat/tags')).set(credentials).query({ viewAll: 'true' });
|
||||
|
||||
return body;
|
||||
};
|
||||
|
||||
export const removeTag = (id: string): Promise<boolean> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
void request
|
||||
.post(methodCall(`livechat:removeTag`))
|
||||
.set(credentials)
|
||||
.send({
|
||||
message: JSON.stringify({
|
||||
method: 'livechat:removeTag',
|
||||
params: [id],
|
||||
id: '101',
|
||||
msg: 'method',
|
||||
}),
|
||||
})
|
||||
.end((err: Error, res: DummyResponse<string, 'wrapped'>) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(JSON.parse(res.body.message).result);
|
||||
});
|
||||
});
|
||||
export const saveTags = async (departments: string[] = []): Promise<ILivechatTag> => {
|
||||
const { body } = await request
|
||||
.post(api('livechat/tags.save'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
tagData: {
|
||||
name: faker.string.uuid(),
|
||||
description: faker.lorem.sentence(),
|
||||
},
|
||||
...(departments.length > 0 && { tagDepartments: departments }),
|
||||
});
|
||||
|
||||
return body;
|
||||
};
|
||||
|
||||
export const removeTag = async (id: string): Promise<boolean> => {
|
||||
const res = await request.post(api('livechat/tags.delete')).set(credentials).send({ id });
|
||||
|
||||
return res.status === 200;
|
||||
};
|
||||
|
||||
export const removeAllTags = async (): Promise<boolean> => {
|
||||
const tagsList = await listTags();
|
||||
await Promise.all(tagsList.tags.map((tag) => removeTag(tag._id)));
|
||||
|
||||
const response = await request.get(api('livechat/tags')).set(credentials).expect('Content-Type', 'application/json').expect(200);
|
||||
|
||||
return response.body.tags.length === 0;
|
||||
};
|
||||
|
||||
@ -1,32 +1,35 @@
|
||||
import type { ILivechatTag } from '@rocket.chat/core-typings';
|
||||
|
||||
import { parseMeteorResponse } from '../parseMeteorResponse';
|
||||
import type { BaseTest } from '../test';
|
||||
|
||||
type CreateTagParams = {
|
||||
id?: string | null;
|
||||
name?: string;
|
||||
description?: string;
|
||||
departments?: { departmentId: string }[];
|
||||
departments?: string[];
|
||||
};
|
||||
|
||||
const removeTag = async (api: BaseTest['api'], id: string) => api.post('/livechat/tags.delete', { id });
|
||||
|
||||
export const createTag = async (api: BaseTest['api'], { id = null, name, description = '', departments = [] }: CreateTagParams = {}) => {
|
||||
const response = await api.post('/method.call/livechat:saveTag', {
|
||||
message: JSON.stringify({
|
||||
msg: 'method',
|
||||
id: '33',
|
||||
method: 'livechat:saveTag',
|
||||
params: [id, { name, description }, departments],
|
||||
}),
|
||||
const response = await api.post('/livechat/tags.save', {
|
||||
_id: id,
|
||||
tagData: {
|
||||
name,
|
||||
description,
|
||||
},
|
||||
...(departments.length > 0 && { tagDepartments: departments }),
|
||||
});
|
||||
|
||||
const tag = await parseMeteorResponse<ILivechatTag>(response);
|
||||
if (response.status() !== 200) {
|
||||
throw new Error(`Failed to create tag [http status: ${response.status()}]`);
|
||||
}
|
||||
|
||||
const data: ILivechatTag = await response.json();
|
||||
|
||||
return {
|
||||
response,
|
||||
data: tag,
|
||||
delete: async () => removeTag(api, tag?._id),
|
||||
data,
|
||||
delete: async () => removeTag(api, data._id),
|
||||
};
|
||||
};
|
||||
|
||||
@ -4,7 +4,7 @@ import { after, before, describe, it } from 'mocha';
|
||||
|
||||
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
||||
import { createDepartmentWithAnOnlineAgent } from '../../../data/livechat/department';
|
||||
import { removeTag, saveTags } from '../../../data/livechat/tags';
|
||||
import { saveTags, removeAllTags } from '../../../data/livechat/tags';
|
||||
import { createMonitor, createUnit } from '../../../data/livechat/units';
|
||||
import { removePermissionFromAllRoles, restorePermissionToRoles, updatePermission, updateSetting } from '../../../data/permissions.helper';
|
||||
import type { IUserWithCredentials } from '../../../data/user';
|
||||
@ -38,19 +38,7 @@ import { IS_EE } from '../../../e2e/config/constants';
|
||||
};
|
||||
|
||||
// remove all existing tags
|
||||
const allTags = await request
|
||||
.get(api('livechat/tags'))
|
||||
.set(credentials)
|
||||
.query({ viewAll: 'true' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
const { tags } = allTags.body;
|
||||
for await (const tag of tags) {
|
||||
await removeTag(tag._id);
|
||||
}
|
||||
const response = await request.get(api('livechat/tags')).set(credentials).expect('Content-Type', 'application/json').expect(200);
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body).to.have.property('tags').and.to.be.an('array').that.is.empty;
|
||||
await removeAllTags();
|
||||
|
||||
// should add 3 tags
|
||||
const { department: dA, agent: agentA } = await createDepartmentWithAnOnlineAgent();
|
||||
@ -67,6 +55,7 @@ import { IS_EE } from '../../../e2e/config/constants';
|
||||
|
||||
after(async () => {
|
||||
await deleteUser(monitor.user);
|
||||
await removeAllTags();
|
||||
});
|
||||
|
||||
it('should throw unauthorized error when the user does not have the necessary permission', async () => {
|
||||
|
||||
@ -5,3 +5,10 @@ export interface ILivechatTag {
|
||||
numDepartments: number;
|
||||
departments: Array<string>;
|
||||
}
|
||||
|
||||
export type FindTagsResult = {
|
||||
tags: ILivechatTag[];
|
||||
count: number;
|
||||
offset: number;
|
||||
total: number;
|
||||
};
|
||||
|
||||
@ -575,11 +575,85 @@ const POSTLivechatMonitorsDeleteSuccessSchema = {
|
||||
|
||||
export const POSTLivechatMonitorsDeleteSuccessResponse = ajv.compile<void>(POSTLivechatMonitorsDeleteSuccessSchema);
|
||||
|
||||
type POSTLivechatTagsRemoveParams = {
|
||||
type POSTLivechatTagsSaveParams = {
|
||||
_id?: string;
|
||||
tagData: {
|
||||
name: string;
|
||||
description?: string;
|
||||
};
|
||||
tagDepartments?: string[];
|
||||
};
|
||||
|
||||
const POSTLivechatTagsSaveParamsSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
_id: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
},
|
||||
tagData: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
tagDepartments: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
minItems: 1,
|
||||
nullable: true,
|
||||
},
|
||||
},
|
||||
required: ['tagData'],
|
||||
additionalProperties: false,
|
||||
};
|
||||
|
||||
export const isPOSTLivechatTagsSaveParams = ajv.compile<POSTLivechatTagsSaveParams>(POSTLivechatTagsSaveParamsSchema);
|
||||
|
||||
const POSTLivechatTagsSaveSuccessResponseSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
_id: {
|
||||
type: 'string',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
},
|
||||
numDepartments: {
|
||||
type: 'number',
|
||||
},
|
||||
departments: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
success: {
|
||||
type: 'boolean',
|
||||
enum: [true],
|
||||
},
|
||||
},
|
||||
required: ['_id', 'name', 'description', 'numDepartments', 'departments', 'success'],
|
||||
additionalProperties: false,
|
||||
};
|
||||
|
||||
export const POSTLivechatTagsSaveSuccessResponse = ajv.compile<ILivechatTag>(POSTLivechatTagsSaveSuccessResponseSchema);
|
||||
|
||||
type POSTLivechatTagsDeleteParams = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const POSTLivechatTagsRemoveSchema = {
|
||||
const POSTLivechatTagsDeleteSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
@ -590,9 +664,9 @@ const POSTLivechatTagsRemoveSchema = {
|
||||
additionalProperties: false,
|
||||
};
|
||||
|
||||
export const isPOSTLivechatTagsRemoveParams = ajv.compile<POSTLivechatTagsRemoveParams>(POSTLivechatTagsRemoveSchema);
|
||||
export const isPOSTLivechatTagsDeleteParams = ajv.compile<POSTLivechatTagsDeleteParams>(POSTLivechatTagsDeleteSchema);
|
||||
|
||||
const POSTLivechatTagsRemoveSuccessResponseSchema = {
|
||||
const POSTLivechatTagsDeleteSuccessResponseSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
success: {
|
||||
@ -603,7 +677,7 @@ const POSTLivechatTagsRemoveSuccessResponseSchema = {
|
||||
additionalProperties: false,
|
||||
};
|
||||
|
||||
export const POSTLivechatTagsRemoveSuccessResponse = ajv.compile<void>(POSTLivechatTagsRemoveSuccessResponseSchema);
|
||||
export const POSTLivechatTagsDeleteSuccessResponse = ajv.compile<void>(POSTLivechatTagsDeleteSuccessResponseSchema);
|
||||
|
||||
type LivechatTagsListProps = PaginatedRequest<{ text: string; viewAll?: 'true' | 'false'; department?: string }, 'name'>;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user