Chore: RestApiClient as Package (#25469)

<!-- This is a pull request template, you do not need to uncomment or remove the comments, they won't show up in the PR text. -->

<!-- Your Pull Request name should start with one of the following tags
  [NEW] For new features
  [IMPROVE] For an improvement (performance or little improvements) in existing features
  [FIX] For bug fixes that affect the end-user
  [BREAK] For pull requests including breaking changes
  Chore: For small tasks
  Doc: For documentation
-->

<!-- Checklist!!! If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code. 
  - I have read the Contributing Guide - https://github.com/RocketChat/Rocket.Chat/blob/develop/.github/CONTRIBUTING.md#contributing-to-rocketchat doc
  - I have signed the CLA - https://cla-assistant.io/RocketChat/Rocket.Chat
  - Lint and unit tests pass locally with my changes
  - I have added tests that prove my fix is effective or that my feature works (if applicable)
  - I have added necessary documentation (if applicable)
  - Any dependent changes have been merged and published in downstream modules
-->

## Proposed changes (including videos or screenshots)
<!-- CHANGELOG -->
<!--
  Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request.
  If it fixes a bug or resolves a feature request, be sure to link to that issue below.
  This description will appear in the release notes if we accept the contribution.
-->

<!-- END CHANGELOG -->

## Issue(s)
<!-- Link the issues being closed by or related to this PR. For example, you can use #594 if this PR closes issue number 594 -->

## Steps to test or reproduce
<!-- Mention how you would reproduce the bug if not mentioned on the issue page already. Also mention which screens are going to have the changes if applicable -->

## Further comments
<!-- If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... -->

Co-authored-by: Tasso Evangelista <2263066+tassoevan@users.noreply.github.com>
pull/25774/head^2
Guilherme Gazzo 4 years ago committed by GitHub
parent d5367a725b
commit 611c0b31fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      apps/meteor/app/api/server/lib/getUploadFormData.js
  2. 79
      apps/meteor/app/api/server/v1/emoji-custom.ts
  3. 2
      apps/meteor/app/api/server/v1/settings.ts
  4. 2
      apps/meteor/app/apps/client/RealAppsEngineUIHost.js
  5. 2
      apps/meteor/app/apps/client/communication/websockets.js
  6. 2
      apps/meteor/app/apps/client/gameCenter/gameCenter.js
  7. 4
      apps/meteor/app/apps/client/gameCenter/gameContainer.js
  8. 36
      apps/meteor/app/apps/client/orchestrator.ts
  9. 4
      apps/meteor/app/authorization/client/startup.js
  10. 10
      apps/meteor/app/authorization/server/methods/saveRole.ts
  11. 2
      apps/meteor/app/emoji-custom/client/lib/emojiCustom.js
  12. 4
      apps/meteor/app/livechat/client/lib/stream/queueManager.js
  13. 6
      apps/meteor/app/livechat/client/views/app/dialog/closeRoom.js
  14. 6
      apps/meteor/app/livechat/client/views/app/livechatReadOnly.js
  15. 6
      apps/meteor/app/livechat/client/views/app/tabbar/agentEdit.js
  16. 6
      apps/meteor/app/livechat/client/views/app/tabbar/agentInfo.js
  17. 6
      apps/meteor/app/livechat/client/views/app/tabbar/contactChatHistory.js
  18. 18
      apps/meteor/app/livechat/client/views/app/tabbar/contactChatHistoryMessages.js
  19. 8
      apps/meteor/app/livechat/client/views/app/tabbar/visitorEdit.js
  20. 57
      apps/meteor/app/livechat/client/views/app/tabbar/visitorForward.html
  21. 2
      apps/meteor/app/livechat/client/views/app/tabbar/visitorForward.js
  22. 8
      apps/meteor/app/livechat/client/views/app/tabbar/visitorInfo.js
  23. 7
      apps/meteor/app/livechat/client/views/app/tabbar/visitorNavigation.js
  24. 4
      apps/meteor/app/livechat/client/views/app/tabbar/visitorTranscript.js
  25. 5
      apps/meteor/app/mentions-flextab/client/views/mentionsFlexTab.js
  26. 5
      apps/meteor/app/message-pin/client/views/pinnedMessages.js
  27. 2
      apps/meteor/app/message-snippet/client/page/snippetPage.js
  28. 5
      apps/meteor/app/message-snippet/client/tabBar/views/snippetedMessages.js
  29. 5
      apps/meteor/app/message-star/client/views/starredMessages.js
  30. 2
      apps/meteor/app/meteor-autocomplete/client/autocomplete-client.js
  31. 2
      apps/meteor/app/models/server/raw/EmojiCustom.ts
  32. 2
      apps/meteor/app/oauth2-server-config/client/oauth/oauth2-client.js
  33. 2
      apps/meteor/app/otr/client/rocketchat.otr.room.js
  34. 2
      apps/meteor/app/ui-message/client/ActionButtonSyncer.ts
  35. 5
      apps/meteor/app/ui-message/client/ActionManager.js
  36. 2
      apps/meteor/app/ui/client/lib/chatMessages.js
  37. 164
      apps/meteor/app/ui/client/lib/fileUpload.js
  38. 236
      apps/meteor/app/ui/client/lib/fileUpload.ts
  39. 52
      apps/meteor/app/utils/client/lib/RestApiClient.ts
  40. 8
      apps/meteor/app/utils/client/lib/_RestApiClient.d.ts
  41. 0
      apps/meteor/app/utils/client/lib/_RestApiClient.js
  42. 2
      apps/meteor/app/utils/lib/fileUploadRestrictions.js
  43. 2
      apps/meteor/app/utils/lib/slashCommand.ts
  44. 2
      apps/meteor/app/videobridge/client/actionLink.js
  45. 2
      apps/meteor/app/webdav/client/startup/sync.js
  46. 2
      apps/meteor/app/webrtc/client/actionLink.tsx
  47. 2
      apps/meteor/app/webrtc/client/tabBar.tsx
  48. 2
      apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx
  49. 2
      apps/meteor/client/components/CreateDiscussion/DefaultParentRoomField.tsx
  50. 2
      apps/meteor/client/components/Omnichannel/Tags.tsx
  51. 3
      apps/meteor/client/components/Omnichannel/hooks/useAgentsList.ts
  52. 3
      apps/meteor/client/components/Omnichannel/hooks/useAvailableAgentsList.ts
  53. 2
      apps/meteor/client/components/Omnichannel/hooks/useDepartmentsList.ts
  54. 2
      apps/meteor/client/components/Omnichannel/modals/CloseChatModalData.tsx
  55. 2
      apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx
  56. 2
      apps/meteor/client/components/RoomAutoComplete/RoomAutoComplete.tsx
  57. 3
      apps/meteor/client/components/RoomAutoComplete/hooks/useRoomsList.ts
  58. 3
      apps/meteor/client/components/RoomIcon/OmnichannelRoomIcon/lib/OmnichannelRoomIcon.ts
  59. 2
      apps/meteor/client/components/TwoFactorModal/TwoFactorEmailModal.tsx
  60. 2
      apps/meteor/client/components/UserAutoComplete/UserAutoComplete.tsx
  61. 2
      apps/meteor/client/components/UserAutoCompleteMultiple/UserAutoCompleteMultiple.tsx
  62. 4
      apps/meteor/client/components/message/Metrics/Thread.tsx
  63. 7
      apps/meteor/client/hooks/useEndpointUpload.ts
  64. 6
      apps/meteor/client/hooks/useUpdateAvatar.ts
  65. 6
      apps/meteor/client/lib/meteorCallWrapper.ts
  66. 11
      apps/meteor/client/lib/presence.ts
  67. 2
      apps/meteor/client/lib/userData.ts
  68. 8
      apps/meteor/client/providers/CallProvider/CallProvider.tsx
  69. 4
      apps/meteor/client/providers/CallProvider/hooks/useVoipClient.ts
  70. 17
      apps/meteor/client/providers/ServerProvider.tsx
  71. 2
      apps/meteor/client/sidebar/footer/voip/index.tsx
  72. 4
      apps/meteor/client/sidebar/header/CreateChannelWithData.tsx
  73. 2
      apps/meteor/client/sidebar/header/CreateDirectMessage.tsx
  74. 10
      apps/meteor/client/startup/banners.ts
  75. 4
      apps/meteor/client/startup/routes.tsx
  76. 8
      apps/meteor/client/startup/slashCommands.ts
  77. 4
      apps/meteor/client/stories/contexts/ServerContextMock.tsx
  78. 2
      apps/meteor/client/views/account/AccountProfilePage.js
  79. 2
      apps/meteor/client/views/account/tokens/AccountTokensPage.js
  80. 2
      apps/meteor/client/views/admin/cloud/PasteStep.tsx
  81. 2
      apps/meteor/client/views/admin/customEmoji/AddCustomEmoji.tsx
  82. 2
      apps/meteor/client/views/admin/customEmoji/CustomEmoji.tsx
  83. 4
      apps/meteor/client/views/admin/customEmoji/EditCustomEmoji.tsx
  84. 2
      apps/meteor/client/views/admin/customEmoji/EditCustomEmojiWithData.tsx
  85. 2
      apps/meteor/client/views/admin/customSounds/AdminSoundsRoute.tsx
  86. 2
      apps/meteor/client/views/admin/customSounds/EditCustomSound.tsx
  87. 2
      apps/meteor/client/views/admin/customUserStatus/CustomUserStatusFormWithData.tsx
  88. 2
      apps/meteor/client/views/admin/customUserStatus/CustomUserStatusTable/CustomUserStatusTable.tsx
  89. 2
      apps/meteor/client/views/admin/emailInbox/EmailInboxEditWithData.tsx
  90. 8
      apps/meteor/client/views/admin/emailInbox/EmailInboxForm.js
  91. 2
      apps/meteor/client/views/admin/emailInbox/EmailInboxTable.tsx
  92. 2
      apps/meteor/client/views/admin/emailInbox/SendTestButton.tsx
  93. 8
      apps/meteor/client/views/admin/import/ImportHistoryPage.js
  94. 4
      apps/meteor/client/views/admin/import/ImportProgressPage.js
  95. 4
      apps/meteor/client/views/admin/import/NewImportPage.js
  96. 6
      apps/meteor/client/views/admin/import/PrepareImportPage.js
  97. 2
      apps/meteor/client/views/admin/info/InformationRoute.tsx
  98. 2
      apps/meteor/client/views/admin/info/LicenseCard.tsx
  99. 2
      apps/meteor/client/views/admin/integrations/IntegrationsTable.js
  100. 2
      apps/meteor/client/views/admin/integrations/edit/EditIncomingWebhookWithData.js
  101. Some files were not shown because too many files have changed in this diff Show More

@ -9,7 +9,6 @@ export const getUploadFormData = async ({ request }) =>
bb.on('file', (fieldname, file, { filename, encoding, mimeType: mimetype }) => {
const fileData = [];
console.log(file);
file.on('data', (data) => fileData.push(data));
file.on('end', () => {

@ -10,27 +10,29 @@ API.v1.addRoute(
'emoji-custom.list',
{ authRequired: true },
{
get() {
async get() {
const { query } = this.parseJsonQuery();
const { updatedSince } = this.queryParams;
let updatedSinceDate;
if (updatedSince) {
const updatedSinceDate = new Date(updatedSince);
if (isNaN(Date.parse(updatedSince))) {
throw new Meteor.Error('error-roomId-param-invalid', 'The "updatedSince" query parameter must be a valid date.');
} else {
updatedSinceDate = new Date(updatedSince);
}
const [update, remove] = await Promise.all([
EmojiCustom.find({ ...query, _updatedAt: { $gt: updatedSinceDate } }).toArray(),
EmojiCustom.trashFindDeletedAfter(updatedSinceDate).toArray(),
]);
return API.v1.success({
emojis: {
update: Promise.await(EmojiCustom.find({ ...query, _updatedAt: { $gt: updatedSinceDate } }).toArray()),
remove: Promise.await(EmojiCustom.trashFindDeletedAfter(updatedSinceDate).toArray()),
update,
remove,
},
});
}
return API.v1.success({
emojis: {
update: Promise.await(EmojiCustom.find(query).toArray()),
update: await EmojiCustom.find(query).toArray(),
remove: [],
},
});
@ -42,21 +44,19 @@ API.v1.addRoute(
'emoji-custom.all',
{ authRequired: true },
{
get() {
async get() {
const { offset, count } = this.getPaginationItems();
const { sort, query } = this.parseJsonQuery();
return API.v1.success(
Promise.await(
findEmojisCustom({
query,
pagination: {
offset,
count,
sort,
},
}),
),
await findEmojisCustom({
query,
pagination: {
offset,
count,
sort,
},
}),
);
},
},
@ -66,18 +66,16 @@ API.v1.addRoute(
'emoji-custom.create',
{ authRequired: true },
{
post() {
const { emoji, ...fields } = Promise.await(
getUploadFormData({
request: this.request,
}),
);
async post() {
const { emoji, ...fields } = await getUploadFormData({
request: this.request,
});
if (!emoji) {
throw new Meteor.Error('invalid-field');
}
const isUploadable = Promise.await(Media.isImage(emoji.fileBuffer));
const isUploadable = await Media.isImage(emoji.fileBuffer);
if (!isUploadable) {
throw new Meteor.Error('emoji-is-not-image', "Emoji file provided cannot be uploaded since it's not an image");
}
@ -88,10 +86,10 @@ API.v1.addRoute(
fields.newFile = true;
fields.aliases = fields.aliases || '';
Meteor.runAsUser(this.userId, () => {
Meteor.call('insertOrUpdateEmoji', fields);
Meteor.call('uploadEmojiCustom', emoji.fileBuffer, emoji.mimetype, fields);
});
Meteor.call('insertOrUpdateEmoji', fields);
Meteor.call('uploadEmojiCustom', emoji.fileBuffer, emoji.mimetype, fields);
return API.v1.success();
},
},
);
@ -100,12 +98,10 @@ API.v1.addRoute(
'emoji-custom.update',
{ authRequired: true },
{
post() {
const { emoji, ...fields } = Promise.await(
getUploadFormData({
request: this.request,
}),
);
async post() {
const { emoji, ...fields } = await getUploadFormData({
request: this.request,
});
if (!fields._id) {
throw new Meteor.Error('The required "_id" query param is missing.');
@ -133,12 +129,11 @@ API.v1.addRoute(
fields.extension = emojiToUpdate.extension;
}
Meteor.runAsUser(this.userId, () => {
Meteor.call('insertOrUpdateEmoji', fields);
if (fields.newFile) {
Meteor.call('uploadEmojiCustom', emoji.fileBuffer, emoji.mimetype, fields);
}
});
Meteor.call('insertOrUpdateEmoji', fields);
if (fields.newFile) {
Meteor.call('uploadEmojiCustom', emoji.fileBuffer, emoji.mimetype, fields);
}
return API.v1.success();
},
},
);
@ -153,7 +148,7 @@ API.v1.addRoute(
return API.v1.failure('The "emojiId" params is required!');
}
Meteor.runAsUser(this.userId, () => Meteor.call('deleteEmojiCustom', emojiId));
Meteor.call('deleteEmojiCustom', emojiId);
return API.v1.success();
},

@ -154,7 +154,7 @@ API.v1.addRoute(
},
post: {
twoFactorRequired: true,
async action(): Promise<ResultFor<'POST', 'settings/:_id'>> {
async action(): Promise<ResultFor<'POST', '/v1/settings/:_id'>> {
if (!hasPermission(this.userId, 'edit-privileged-setting')) {
return API.v1.unauthorized();
}

@ -29,7 +29,7 @@ export class RealAppsEngineUIHost extends AppsEngineUIHost {
let cachedMembers = [];
try {
const { members } = await APIClient.get('v1/groups.members', { roomId: id });
const { members } = await APIClient.get('/v1/groups.members', { roomId: id });
cachedMembers = members.map(({ _id, username }) => ({
id: _id,

@ -50,7 +50,7 @@ export class AppWebsocketReceiver extends Emitter {
}
onCommandAddedOrUpdated = (command) => {
APIClient.v1.get('commands.get', { command }).then((result) => {
APIClient.get('/v1/commands.get', { command }).then((result) => {
slashCommands.commands[command] = result.command;
});
};

@ -8,7 +8,7 @@ import { handleError } from '../../../../client/lib/utils/handleError';
const getExternalComponents = async (instance) => {
try {
const { externalComponents } = await APIClient.get('apps/externalComponents');
const { externalComponents } = await APIClient.get('/apps/externalComponents');
instance.games.set(externalComponents);
} catch (e) {
handleError(e);

@ -62,7 +62,7 @@ Template.GameContainer.events({
Template.GameContainer.onCreated(async () => {
const externalComponent = await getExternalComponent();
APIClient.post('apps/externalComponentEvent', {
APIClient.post('/apps/externalComponentEvent', {
event: 'IPostExternalComponentOpened',
externalComponent,
});
@ -71,7 +71,7 @@ Template.GameContainer.onCreated(async () => {
Template.GameContainer.onDestroyed(async () => {
const externalComponent = await getExternalComponent();
APIClient.post('apps/externalComponentEvent', {
APIClient.post('/apps/externalComponentEvent', {
event: 'IPostExternalComponentClosed',
externalComponent,
});

@ -124,7 +124,7 @@ class AppClientOrchestrator {
}
public screenshots(appId: string): IAppScreenshots {
return APIClient.get(`apps/${appId}/screenshots`);
return APIClient.get(`/v1/apps/${appId}/screenshots`);
}
public isEnabled(): Promise<boolean> | undefined {
@ -132,12 +132,12 @@ class AppClientOrchestrator {
}
public async getApps(): Promise<App[]> {
const { apps } = await APIClient.get('apps');
const { apps } = await APIClient.get('/v1/apps');
return apps;
}
public async getAppsFromMarketplace(): Promise<IAppsFromMarketplace[]> {
const appsOverviews: IAppFromMarketplace[] = await APIClient.get('apps', { marketplace: 'true' });
const appsOverviews: IAppFromMarketplace[] = await APIClient.get('/v1/apps', { marketplace: 'true' });
return appsOverviews.map((app: IAppFromMarketplace) => {
const { latest, price, pricingPlans, purchaseType, isEnterpriseOnly, modifiedAt } = app;
return {
@ -152,22 +152,22 @@ class AppClientOrchestrator {
}
public async getAppsOnBundle(bundleId: string): Promise<App[]> {
const { apps } = await APIClient.get(`apps/bundles/${bundleId}/apps`);
const { apps } = await APIClient.get(`/v1/apps/bundles/${bundleId}/apps`);
return apps;
}
public async getAppsLanguages(): Promise<IAppLanguage> {
const { apps } = await APIClient.get('apps/languages');
const { apps } = await APIClient.get('/v1/apps/languages');
return apps;
}
public async getApp(appId: string): Promise<App> {
const { app } = await APIClient.get(`apps/${appId}`);
const { app } = await APIClient.get(`/v1/apps/${appId}`);
return app;
}
public async getAppFromMarketplace(appId: string, version: string): Promise<App> {
const { app } = await APIClient.get(`apps/${appId}`, {
const { app } = await APIClient.get(`/v1/apps/${appId}`, {
marketplace: 'true',
version,
});
@ -175,7 +175,7 @@ class AppClientOrchestrator {
}
public async getLatestAppFromMarketplace(appId: string, version: string): Promise<App> {
const { app } = await APIClient.get(`apps/${appId}`, {
const { app } = await APIClient.get(`/v1/apps/${appId}`, {
marketplace: 'true',
update: 'true',
appVersion: version,
@ -184,27 +184,27 @@ class AppClientOrchestrator {
}
public async getAppSettings(appId: string): Promise<ISettingsReturn> {
const { settings } = await APIClient.get(`apps/${appId}/settings`);
const { settings } = await APIClient.get(`/v1/apps/${appId}/settings`);
return settings;
}
public async setAppSettings(appId: string, settings: ISettingsPayload): Promise<ISettingsSetReturn> {
const { updated } = await APIClient.post(`apps/${appId}/settings`, undefined, { settings });
const { updated } = await APIClient.post(`/v1/apps/${appId}/settings`, undefined, { settings });
return updated;
}
public async getAppApis(appId: string): Promise<IApiEndpointMetadata[]> {
const { apis } = await APIClient.get(`apps/${appId}/apis`);
const { apis } = await APIClient.get(`/v1/apps/${appId}/apis`);
return apis;
}
public async getAppLanguages(appId: string): Promise<IAppStorageItem['languageContent']> {
const { languages } = await APIClient.get(`apps/${appId}/languages`);
const { languages } = await APIClient.get(`/v1/apps/${appId}/languages`);
return languages;
}
public async installApp(appId: string, version: string, permissionsGranted: IPermission[]): Promise<IDeletedInstalledApp> {
const { app } = await APIClient.post('apps/', {
const { app } = await APIClient.post('/v1/apps/', {
appId,
marketplace: true,
version,
@ -214,7 +214,7 @@ class AppClientOrchestrator {
}
public async updateApp(appId: string, version: string, permissionsGranted: IPermission[]): Promise<App> {
const { app } = await APIClient.post(`apps/${appId}`, {
const { app } = await APIClient.post(`/v1/apps/${appId}`, {
appId,
marketplace: true,
version,
@ -228,11 +228,11 @@ class AppClientOrchestrator {
}
public syncApp(appId: string): IAppSynced {
return APIClient.post(`apps/${appId}/sync`);
return APIClient.post(`/v1/apps/${appId}/sync`);
}
public async setAppStatus(appId: string, status: AppStatus): Promise<string> {
const { status: effectiveStatus } = await APIClient.post(`apps/${appId}/status`, { status });
const { status: effectiveStatus } = await APIClient.post(`/v1/apps/${appId}/status`, { status });
return effectiveStatus;
}
@ -245,7 +245,7 @@ class AppClientOrchestrator {
}
public buildExternalUrl(appId: string, purchaseType = 'buy', details = false): IAppExternalURL {
return APIClient.get('apps', {
return APIClient.get('/v1/apps', {
buildExternalUrl: 'true',
appId,
purchaseType,
@ -254,7 +254,7 @@ class AppClientOrchestrator {
}
public async getCategories(): Promise<ICategory[]> {
const categories = await APIClient.get('apps', { categories: 'true' });
const categories = await APIClient.get('/v1/apps', { categories: 'true' });
return categories;
}

@ -2,13 +2,13 @@ import { Meteor } from 'meteor/meteor';
import { Tracker } from 'meteor/tracker';
import { CachedCollectionManager } from '../../ui-cached-collection';
import { APIClient } from '../../utils/client';
import { APIClient } from '../../utils/client/lib/RestApiClient';
import { Roles } from '../../models/client';
import { rolesStreamer } from './lib/streamer';
Meteor.startup(() => {
CachedCollectionManager.onLogin(async () => {
const { roles } = await APIClient.v1.get('roles.list');
const { roles } = await APIClient.get('/v1/roles.list');
// if a role is checked before this collection is populated, it will return undefined
Roles._collection._docs._map = new Map(roles.map((record) => [Roles._collection._docs._idStringify(record._id), record]));
Object.values(Roles._collection.queries).forEach((query) => Roles._collection._recomputeResults(query));

@ -13,16 +13,16 @@ Meteor.methods({
methodDeprecationLogger.warn('authorization:saveRole will be deprecated in future versions of Rocket.Chat');
const userId = Meteor.userId();
if (!userId || !hasPermission(userId, 'access-permissions')) {
throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', {
if (!isRoleCreateProps(roleData)) {
throw new Meteor.Error('error-invalid-role-properties', 'The role properties are invalid.', {
method: 'authorization:saveRole',
action: 'Accessing_permissions',
});
}
if (!isRoleCreateProps(roleData)) {
throw new Meteor.Error('error-invalid-role-properties', 'The role properties are invalid.', {
if (!userId || !hasPermission(userId, 'access-permissions')) {
throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed', {
method: 'authorization:saveRole',
action: 'Accessing_permissions',
});
}

@ -184,7 +184,7 @@ Meteor.startup(() =>
try {
const {
emojis: { update: emojis },
} = await APIClient.v1.get('emoji-custom.list');
} = await APIClient.get('/v1/emoji-custom.list');
emoji.packages.emojiCustom.emojisByCategory = { rocket: [] };
for (const currentEmoji of emojis) {

@ -45,7 +45,7 @@ const updateCollection = (inquiry) => {
};
const getInquiriesFromAPI = async () => {
const { inquiries } = await APIClient.v1.get('livechat/inquiries.queuedForUser?sort={"ts": 1}');
const { inquiries } = await APIClient.get('/v1/livechat/inquiries.queuedForUser?sort={"ts": 1}');
return inquiries;
};
@ -68,7 +68,7 @@ const updateInquiries = async (inquiries = []) =>
inquiries.forEach((inquiry) => LivechatInquiry.upsert({ _id: inquiry._id }, { ...inquiry, _updatedAt: new Date(inquiry._updatedAt) }));
const getAgentsDepartments = async (userId) => {
const { departments } = await APIClient.v1.get(`livechat/agents/${userId}/departments?enabledDepartmentsOnly=true`);
const { departments } = await APIClient.get(`/v1/livechat/agents/${userId}/departments?enabledDepartmentsOnly=true`);
return departments;
};

@ -167,16 +167,16 @@ Template.closeRoom.onCreated(async function () {
this.onEnterTag = () => this.invalidTags.set(!validateRoomTags(this.tagsRequired.get(), this.tags.get()));
const { rid } = Template.currentData();
const { room } = await APIClient.v1.get(`rooms.info?roomId=${rid}`);
const { room } = await APIClient.get(`/v1/rooms.info?roomId=${rid}`);
this.tags.set(room?.tags || []);
if (room?.departmentId) {
const { department } = await APIClient.v1.get(`livechat/department/${room.departmentId}?includeAgents=false`);
const { department } = await APIClient.get(`/v1/livechat/department/${room.departmentId}?includeAgents=false`);
this.tagsRequired.set(department?.requestTagBeforeClosingChat);
}
const uid = Meteor.userId();
const { departments } = await APIClient.v1.get(`livechat/agents/${uid}/departments`);
const { departments } = await APIClient.get(`/v1/livechat/agents/${uid}/departments`);
const agentDepartments = departments.map((dept) => dept.departmentId);
this.agentDepartments.set(agentDepartments);

@ -61,7 +61,7 @@ Template.livechatReadOnly.events({
event.stopPropagation();
try {
const { success } = (await APIClient.v1.get(`livechat/room.join?roomId=${this.rid}`)) || {};
const { success } = (await APIClient.get(`/v1/livechat/room.join?roomId=${this.rid}`)) || {};
if (!success) {
throw new Meteor.Error('error-join-room', 'Error joining room');
}
@ -99,13 +99,13 @@ Template.livechatReadOnly.onCreated(async function () {
this.loadRoomAndInquiry = async (roomId) => {
this.preparing.set(true);
const { inquiry } = await APIClient.v1.get(`livechat/inquiries.getOne?roomId=${roomId}`);
const { inquiry } = await APIClient.get(`/v1/livechat/inquiries.getOne?roomId=${roomId}`);
this.inquiry.set(inquiry);
if (inquiry && inquiry._id) {
inquiryDataStream.on(inquiry._id, this.updateInquiry);
}
const { room } = await APIClient.v1.get(`rooms.info?roomId=${roomId}`);
const { room } = await APIClient.get(`/v1/rooms.info?roomId=${roomId}`);
this.room.set(room);
if (room && room._id) {
RoomManager.roomStream.on(roomId, (room) => this.room.set(room));

@ -133,7 +133,7 @@ Template.agentEdit.onCreated(async function () {
this.availableDepartments = new ReactiveVar([]);
this.back = Template.currentData().back;
const { departments } = await APIClient.v1.get('livechat/department?sort={"name": 1}');
const { departments } = await APIClient.get('/v1/livechat/department?sort={"name": 1}');
this.departments.set(departments);
this.availableDepartments.set(departments.filter(({ enabled }) => enabled));
@ -146,8 +146,8 @@ Template.agentEdit.onCreated(async function () {
return;
}
const { user } = await APIClient.v1.get(`livechat/users/agent/${agentId}`);
const { departments } = await APIClient.v1.get(`livechat/agents/${agentId}/departments`);
const { user } = await APIClient.get(`/v1/livechat/users/agent/${agentId}`);
const { departments } = await APIClient.get(`/v1/livechat/agents/${agentId}/departments`);
this.agent.set(user);
this.agentDepartments.set((departments || []).map((department) => department.departmentId));
this.ready.set(true);

@ -153,14 +153,14 @@ Template.agentInfo.onCreated(async function () {
this.tabBar = Template.currentData().tabBar;
this.onRemoveAgent = Template.currentData().onRemoveAgent;
const { departments } = await APIClient.v1.get('livechat/department?sort={"name": 1}');
const { departments } = await APIClient.get('/v1/livechat/department?sort={"name": 1}');
this.departments.set(departments);
this.availableDepartments.set(departments.filter(({ enabled }) => enabled));
const loadAgentData = async (agentId) => {
this.ready.set(false);
const { user } = await APIClient.v1.get(`livechat/users/agent/${agentId}`);
const { departments } = await APIClient.v1.get(`livechat/agents/${agentId}/departments`);
const { user } = await APIClient.get(`/v1/livechat/users/agent/${agentId}`);
const { departments } = await APIClient.get(`/v1/livechat/agents/${agentId}/departments`);
this.agent.set(user);
this.agentDepartments.set((departments || []).map((department) => department.departmentId));
this.ready.set(true);

@ -70,7 +70,7 @@ Template.contactChatHistory.onCreated(async function () {
const offset = this.offset.get();
const searchTerm = this.searchTerm.get();
let baseUrl = `livechat/visitors.searchChats/room/${
let baseUrl = `/v1/livechat/visitors.searchChats/room/${
currentData.rid
}/visitor/${this.visitorId.get()}?count=${limit}&offset=${offset}&closedChatsOnly=true&servedChatsOnly=true`;
if (searchTerm) {
@ -78,14 +78,14 @@ Template.contactChatHistory.onCreated(async function () {
}
this.isLoading.set(true);
const { history, total } = await APIClient.v1.get(baseUrl);
const { history, total } = await APIClient.get(baseUrl);
this.history.set(offset === 0 ? history : this.history.get().concat(history));
this.hasMore.set(total > this.history.get().length);
this.isLoading.set(false);
});
this.autorun(async () => {
const { room } = await APIClient.v1.get(`rooms.info?roomId=${currentData.rid}`);
const { room } = await APIClient.get(`/v1/rooms.info?roomId=${currentData.rid}`);
if (room?.v) {
this.visitorId.set(room.v._id);
}

@ -81,12 +81,12 @@ Template.contactChatHistoryMessages.onCreated(function () {
this.hasError = new ReactiveVar(false);
this.error = new ReactiveVar(null);
this.loadMessages = async (url) => {
this.loadMessages = async (url, params) => {
this.isLoading.set(true);
const offset = this.offset.get();
try {
const { messages, total } = await APIClient.v1.get(url);
const { messages, total } = await APIClient.get(url, params);
this.messages.set(offset === 0 ? messages : this.messages.get().concat(messages));
this.hasMore.set(total > this.messages.get().length);
} catch (e) {
@ -103,10 +103,20 @@ Template.contactChatHistoryMessages.onCreated(function () {
const searchTerm = this.searchTerm.get();
if (searchTerm !== '') {
return this.loadMessages(`chat.search/?roomId=${this.rid}&searchText=${searchTerm}&count=${limit}&offset=${offset}&sort={"ts": 1}`);
return this.loadMessages('/v1/chat.search', {
roomId: this.rid,
searchText: searchTerm,
count: limit,
offset,
sort: '{"ts": 1}',
});
}
this.loadMessages(`livechat/${this.rid}/messages?count=${limit}&offset=${offset}&sort={"ts": 1}`);
this.loadMessages(`/v1/livechat/${this.rid}/messages`, {
count: limit,
offset,
sort: '{"ts": 1}',
});
});
this.autorun(() => {

@ -114,7 +114,7 @@ Template.visitorEdit.onCreated(async function () {
this.autorun(async () => {
const { visitorId } = Template.currentData();
if (visitorId) {
const { visitor } = await APIClient.v1.get(`livechat/visitors.info?visitorId=${visitorId}`);
const { visitor } = await APIClient.get('/v1/livechat/visitors.info', { visitorId });
this.visitor.set(visitor);
}
});
@ -122,15 +122,15 @@ Template.visitorEdit.onCreated(async function () {
const rid = Template.currentData().roomId;
this.autorun(async () => {
const { room } = await APIClient.v1.get(`rooms.info?roomId=${rid}`);
const { customFields } = await APIClient.v1.get(`livechat/custom-fields?count=${CUSTOM_FIELDS_COUNT}`);
const { room } = await APIClient.get('/v1/rooms.info', { roomId: rid });
const { customFields } = await APIClient.get('/v1/livechat/custom-fields', { count: CUSTOM_FIELDS_COUNT });
this.room.set(room);
this.tags.set((room && room.tags) || []);
this.customFields.set(customFields || []);
});
const uid = Meteor.userId();
const { departments } = await APIClient.v1.get(`livechat/agents/${uid}/departments`);
const { departments } = await APIClient.get(`/v1/livechat/agents/${uid}/departments`);
const agentDepartments = departments.map((dept) => dept.departmentId);
this.agentDepartments.set(agentDepartments);
Meteor.call('livechat:getTagsList', (err, tagsList) => {

@ -3,34 +3,20 @@
<div class="edit-form">
<h3>{{_ "Forward_chat"}}</h3>
{{#with visitor}}
<div class="input-line">
<label for="name">{{_ "Name"}}</label>
<span>{{username}}</span>
</div>
<div class="input-line">
<label for="name">{{_ "Name"}}</label>
<span>{{username}}</span>
</div>
{{/with}}
<form>
<div class="input-line">
<label for="name">{{_ "Forward_to_department"}}</label>
<div class="form-group">
{{> livechatAutocompleteUser
onClickTag=onClickTagDepartment
list=selectedDepartments
onSelect=onSelectDepartments
collection='CachedDepartmentList'
endpoint='livechat/department.autocomplete'
field='name'
sort='name'
icon="queue"
label="Enter_a_department_name"
placeholder="Enter_a_department_name"
name="department"
noMatchTemplate="userSearchEmpty"
templateItem="popupList_item_channel"
template="roomSearch"
noMatchTemplate="roomSearchEmpty"
modifier=departmentModifier
conditions=departmentConditions
}}
{{> livechatAutocompleteUser onClickTag=onClickTagDepartment list=selectedDepartments onSelect=onSelectDepartments
collection='CachedDepartmentList' endpoint='livechat/department.autocomplete' field='name' sort='name' icon="queue"
label="Enter_a_department_name" placeholder="Enter_a_department_name" name="department" noMatchTemplate="userSearchEmpty"
templateItem="popupList_item_channel" template="roomSearch" noMatchTemplate="roomSearchEmpty" modifier=departmentModifier
conditions=departmentConditions }}
</div>
</div>
<div class="form-divisor">
@ -40,23 +26,10 @@
<div class="input-line">
<label for="agent">{{_ "Forward_to_user"}}</label>
<div class="form-group">
{{> livechatAutocompleteUser
onClickTag=onClickTagAgent
list=selectedAgents
onSelect=onSelectAgents
collection='UserAndRoom'
endpoint='users.autocomplete'
field='username'
sort='username'
label="Select_a_user"
placeholder="Select_a_user"
name="agent"
icon="at"
noMatchTemplate="userSearchEmpty"
templateItem="popupList_item_default"
modifier=agentModifier
conditions=agentConditions
}}
{{> livechatAutocompleteUser onClickTag=onClickTagAgent list=selectedAgents onSelect=onSelectAgents collection='UserAndRoom'
endpoint='/v1/users.autocomplete' field='username' sort='username' label="Select_a_user" placeholder="Select_a_user"
name="agent" icon="at" noMatchTemplate="userSearchEmpty" templateItem="popupList_item_default" modifier=agentModifier
conditions=agentConditions }}
</div>
</div>
<div class="input-line">
@ -66,8 +39,8 @@
</div>
</div>
<div class="rc-user-info__flex rc-user-info__row">
<button class='rc-button cancel' type="button"><span>{{_ "Cancel"}}</span></button>
<button class='rc-button rc-button--primary save'><span>{{_ "Forward"}}</span></button>
<button class="rc-button cancel" type="button"><span>{{_ "Cancel"}}</span></button>
<button class="rc-button rc-button--primary save"><span>{{_ "Forward"}}</span></button>
</div>
</form>
</div>

@ -99,7 +99,7 @@ Template.visitorForward.onCreated(async function () {
}
});
const { departments } = await APIClient.v1.get('livechat/department?enabled=true');
const { departments } = await APIClient.get('/v1/livechat/department', { enabled: true });
this.departments.set(departments);
});

@ -355,7 +355,7 @@ Template.visitorInfo.events({
confirmButtonText: t('Yes'),
},
async () => {
const { success } = await APIClient.v1.post('livechat/room.onHold', { roomId: this.rid });
const { success } = await APIClient.post('/v1/livechat/room.onHold', { roomId: this.rid });
if (success) {
modal.open({
title: t('Chat_On_Hold'),
@ -382,7 +382,7 @@ Template.visitorInfo.onCreated(function () {
this.room = new ReactiveVar({});
this.updateVisitor = async (visitorId) => {
const { visitor } = await APIClient.v1.get(`livechat/visitors.info?visitorId=${visitorId}`);
const { visitor } = await APIClient.get('/v1/livechat/visitors.info', { visitorId });
this.user.set(visitor);
};
@ -409,7 +409,7 @@ Template.visitorInfo.onCreated(function () {
});
const loadRoomData = async (rid) => {
const { room } = await APIClient.v1.get(`rooms.info?roomId=${rid}`);
const { room } = await APIClient.get('/v1/rooms.info', { roomId: rid });
this.updateRoom(room);
};
@ -420,7 +420,7 @@ Template.visitorInfo.onCreated(function () {
this.autorun(async () => {
if (this.departmentId.get()) {
const { department } = await APIClient.v1.get(`livechat/department/${this.departmentId.get()}?includeAgents=false`);
const { department } = await APIClient.get(`/v1/livechat/department/${this.departmentId.get()}`, { includeAgents: false });
this.department.set(department);
}
});

@ -65,9 +65,10 @@ Template.visitorNavigation.onCreated(async function () {
this.isLoading.set(true);
const offset = this.offset.get();
if (currentData && currentData.rid) {
const { pages, total } = await APIClient.v1.get(
`livechat/visitors.pagesVisited/${currentData.rid}?count=${ITEMS_COUNT}&offset=${offset}`,
);
const { pages, total } = await APIClient.get(`/v1/livechat/visitors.pagesVisited/${currentData.rid}`, {
count: ITEMS_COUNT,
offset,
});
this.isLoading.set(false);
this.total.set(total);
this.pages.set(this.pages.get().concat(pages));

@ -169,12 +169,12 @@ Template.visitorTranscript.onCreated(async function () {
this.infoMessage = new ReactiveVar('');
this.autorun(async () => {
const { visitor } = await APIClient.v1.get(`livechat/visitors.info?visitorId=${Template.currentData().visitorId}`);
const { visitor } = await APIClient.get('/v1/livechat/visitors.info', { visitorId: Template.currentData().visitorId });
this.visitor.set(visitor);
});
this.autorun(async () => {
const { room } = await APIClient.v1.get(`rooms.info?roomId=${Template.currentData().roomId}`);
const { room } = await APIClient.get('/v1/rooms.info', { roomId: Template.currentData().roomId });
this.room.set(room);
if (room?.transcriptRequest) {

@ -61,7 +61,10 @@ Template.mentionsFlexTab.onCreated(function () {
this.autorun(async () => {
const limit = this.limit.get();
const { messages, total } = await APIClient.v1.get(`chat.getMentionedMessages?roomId=${this.data.rid}&count=${limit}`);
const { messages, total } = await APIClient.get('/v1/chat.getMentionedMessages', {
roomId: this.data.rid,
count: limit,
});
upsertMessageBulk({ msgs: messages }, this.messages);

@ -62,7 +62,10 @@ Template.pinnedMessages.onCreated(function () {
this.autorun(async () => {
const limit = this.limit.get();
const { messages, total } = await APIClient.v1.get(`chat.getPinnedMessages?roomId=${this.rid}&count=${limit}`);
const { messages, total } = await APIClient.get('/v1/chat.getPinnedMessages', {
roomId: this.rid,
count: limit,
});
upsertMessageBulk({ msgs: messages }, this.messages);

@ -39,6 +39,6 @@ Template.snippetPage.onCreated(async function () {
const snippetId = FlowRouter.getParam('snippetId');
this.message = new ReactiveVar({});
const { message } = await APIClient.v1.get(`chat.getSnippetedMessageById?messageId=${snippetId}`);
const { message } = await APIClient.get('/v1/chat.getSnippetedMessageById', { messageId: snippetId });
this.message.set(message);
});

@ -56,7 +56,10 @@ Template.snippetedMessages.onCreated(function () {
this.autorun(async () => {
const limit = this.limit.get();
const { messages, total } = await APIClient.v1.get(`chat.getSnippetedMessages?roomId=${this.rid}&count=${limit}`);
const { messages, total } = await APIClient.get('/v1/chat.getSnippetedMessages', {
roomId: this.rid,
count: limit,
});
upsertMessageBulk({ msgs: messages }, this.messages);

@ -61,7 +61,10 @@ Template.starredMessages.onCreated(function () {
this.autorun(async () => {
const limit = this.limit.get();
const { messages, total } = await APIClient.v1.get(`chat.getStarredMessages?roomId=${this.rid}&count=${limit}`);
const { messages, total } = await APIClient.get('/v1/chat.getStarredMessages', {
roomId: this.rid,
count: limit,
});
upsertMessageBulk({ msgs: messages }, this.messages);

@ -131,7 +131,7 @@ export default class AutoComplete {
// console.debug 'Subscribing to <%s> in <%s>.<%s>', filter, rule.collection, rule.field
this.setLoaded(false);
const endpointName = rule.endpoint || 'users.autocomplete';
const { items } = await APIClient.v1.get(`${endpointName}?selector=${JSON.stringify(selector)}`);
const { items } = await APIClient.get(`/v1/${endpointName}`, { selector: JSON.stringify(selector) });
AutoCompleteRecords.remove({});
items.forEach((item) => AutoCompleteRecords.insert(item));
this.setLoaded(true);

@ -43,7 +43,7 @@ export class EmojiCustomRaw extends BaseRaw<T> {
return this.updateOne({ _id }, update);
}
setAliases(_id: string, aliases: string): Promise<UpdateWriteOpResult> {
setAliases(_id: string, aliases: string[]): Promise<UpdateWriteOpResult> {
const update = {
$set: {
aliases,

@ -7,7 +7,7 @@ import { APIClient } from '../../../utils/client';
Template.authorize.onCreated(async function () {
this.oauthApp = new ReactiveVar({});
const { oauthApp } = await APIClient.v1.get(`oauth-apps.get?clientId=${this.data.client_id()}`);
const { oauthApp } = await APIClient.get(`/v1/oauth-apps.get?clientId=${this.data.client_id()}`);
this.oauthApp.set(oauthApp);
});

@ -62,7 +62,7 @@ OTR.Room = class {
}
acknowledge() {
APIClient.v1.post('statistics.telemetry', { params: [{ eventName: 'otrStats', timestamp: Date.now(), rid: this.roomId }] });
APIClient.post('/v1/statistics.telemetry', { params: [{ eventName: 'otrStats', timestamp: Date.now(), rid: this.roomId }] });
Notifications.notifyUser(this.peerId, 'otr', 'acknowledge', {
roomId: this.roomId,

@ -46,7 +46,7 @@ export const removeButton = (button: IUIActionButton): void => {
};
export const loadButtons = (): Promise<void> =>
APIClient.get('apps/actionButtons').then((value: Array<IUIActionButton>) => {
APIClient.get('/apps/actionButtons').then((value) => {
registeredButtons.forEach((button) => removeButton(button));
registeredButtons = [];
value.map(addButton);

@ -8,10 +8,9 @@ import { UIKitInteractionTypes } from '@rocket.chat/core-typings';
import Notifications from '../../notifications/client/lib/Notifications';
import { CachedCollectionManager } from '../../ui-cached-collection';
import { modal } from '../../ui-utils/client/lib/modal';
import { APIClient } from '../../utils';
import { APIClient, t } from '../../utils/client';
import * as banners from '../../../client/lib/banners';
import { dispatchToastMessage } from '../../../client/lib/toast';
import { t } from '../../utils/client';
const events = new Emitter();
@ -169,7 +168,7 @@ export const triggerAction = async ({ type, actionId, appId, rid, mid, viewId, c
setTimeout(reject, TRIGGER_TIMEOUT, [TRIGGER_TIMEOUT_ERROR, { triggerId, appId }]);
const { type: interactionType, ...data } = await APIClient.post(`apps/ui.interaction/${appId}`, {
const { type: interactionType, ...data } = await APIClient.post(`/apps/ui.interaction/${appId}`, {
type,
actionId,
payload,

@ -428,7 +428,7 @@ export class ChatMessages {
if (commandOptions.clientOnly) {
commandOptions.callback(command, param, msgObject);
} else {
APIClient.v1.post('statistics.telemetry', { params: [{ eventName: 'slashCommandsStats', timestamp: Date.now(), command }] });
APIClient.post('/v1/statistics.telemetry', { params: [{ eventName: 'slashCommandsStats', timestamp: Date.now(), command }] });
const triggerId = generateTriggerId(slashCommands.commands[command].appId);
Meteor.call('slashCommand', { cmd: command, params: param, msg: msgObject, triggerId }, (err, result) => {
typeof commandOptions.result === 'function' &&

@ -1,164 +0,0 @@
import { Tracker } from 'meteor/tracker';
import { Session } from 'meteor/session';
import { Random } from 'meteor/random';
import { Meteor } from 'meteor/meteor';
import { settings } from '../../../settings/client';
import { UserAction, USER_ACTIVITIES } from '../index';
import { fileUploadIsValidContentType, APIClient } from '../../../utils';
import { imperativeModal } from '../../../../client/lib/imperativeModal';
import FileUploadModal from '../../../../client/views/room/modals/FileUploadModal';
import { prependReplies } from '../../../../client/lib/utils/prependReplies';
import { chatMessages } from '../views/app/room';
export const uploadFileWithMessage = async (rid, tmid, { description, fileName, msg, file }) => {
const data = new FormData();
description && data.append('description', description);
msg && data.append('msg', msg);
tmid && data.append('tmid', tmid);
data.append('file', file.file, fileName);
const uploads = Session.get('uploading') || [];
const upload = {
id: Random.id(),
name: fileName,
percentage: 0,
};
uploads.push(upload);
Session.set('uploading', uploads);
const { xhr, promise } = APIClient.upload(`v1/rooms.upload/${rid}`, {}, data, {
progress(progress) {
const uploads = Session.get('uploading') || [];
if (progress === 100) {
return;
}
uploads
.filter((u) => u.id === upload.id)
.forEach((u) => {
u.percentage = Math.round(progress) || 0;
});
Session.set('uploading', uploads);
},
error(error) {
const uploads = Session.get('uploading') || [];
uploads
.filter((u) => u.id === upload.id)
.forEach((u) => {
u.error = error.message;
u.percentage = 0;
});
Session.set('uploading', uploads);
},
});
if (Session.get('uploading').length) {
UserAction.performContinuously(rid, USER_ACTIVITIES.USER_UPLOADING, { tmid });
}
Tracker.autorun((computation) => {
const isCanceling = Session.get(`uploading-cancel-${upload.id}`);
if (!isCanceling) {
return;
}
computation.stop();
Session.delete(`uploading-cancel-${upload.id}`);
xhr.abort();
const uploads = Session.get('uploading') || {};
Session.set(
'uploading',
uploads.filter((u) => u.id !== upload.id),
);
});
try {
await promise;
const uploads = Session.get('uploading') || [];
const remainingUploads = Session.set(
'uploading',
uploads.filter((u) => u.id !== upload.id),
);
if (!Session.get('uploading').length) {
UserAction.stop(rid, USER_ACTIVITIES.USER_UPLOADING, { tmid });
}
return remainingUploads;
} catch (error) {
const uploads = Session.get('uploading') || [];
uploads
.filter((u) => u.id === upload.id)
.forEach((u) => {
u.error = (error.xhr && error.xhr.responseJSON && error.xhr.responseJSON.error) || error.message;
u.percentage = 0;
});
if (!uploads.length) {
UserAction.stop(rid, USER_ACTIVITIES.USER_UPLOADING, { tmid });
}
Session.set('uploading', uploads);
}
};
export const fileUpload = async (files, input, { rid, tmid }) => {
const threadsEnabled = settings.get('Threads_enabled');
files = [].concat(files);
const replies = $(input).data('reply') || [];
const mention = $(input).data('mention-user') || false;
let msg = '';
if (!mention || !threadsEnabled) {
msg = await prependReplies('', replies, mention);
}
if (mention && threadsEnabled && replies.length) {
tmid = replies[0]._id;
}
const key = ['messagebox', rid, tmid].filter(Boolean).join('_');
const messageBoxText = Meteor._localStorage.getItem(key) || '';
const uploadNextFile = () => {
const file = files.pop();
if (!file) {
return;
}
imperativeModal.open({
component: FileUploadModal,
props: {
file: file.file,
fileName: file.name,
fileDescription: messageBoxText,
onClose: () => {
imperativeModal.close();
uploadNextFile();
},
onSubmit: (fileName, description) => {
uploadFileWithMessage(rid, tmid, {
description,
fileName,
msg: msg || undefined,
file,
});
const localStorageKey = ['messagebox', rid, tmid].filter(Boolean).join('_');
const chatMessageKey = [rid, tmid].filter(Boolean).join('-');
const { input } = chatMessages[chatMessageKey];
input.value = null;
$(input).trigger('input');
Meteor._localStorage.removeItem(localStorageKey);
imperativeModal.close();
uploadNextFile();
},
invalidContentType: file.file.type && !fileUploadIsValidContentType(file.file.type),
},
});
};
uploadNextFile();
};

@ -0,0 +1,236 @@
import { Tracker } from 'meteor/tracker';
import { Session } from 'meteor/session';
import { Random } from 'meteor/random';
import { Meteor } from 'meteor/meteor';
import { settings } from '../../../settings/client';
import { UserAction, USER_ACTIVITIES } from '../index';
import { fileUploadIsValidContentType, APIClient } from '../../../utils/client';
import { imperativeModal } from '../../../../client/lib/imperativeModal';
import FileUploadModal from '../../../../client/views/room/modals/FileUploadModal';
import { prependReplies } from '../../../../client/lib/utils/prependReplies';
import { chatMessages } from '../views/app/room';
type Uploading = {
id: string;
name: string;
percentage: number;
error?: Error;
};
declare module 'meteor/session' {
// eslint-disable-next-line @typescript-eslint/interface-name-prefix
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Session {
function get(key: 'uploading'): Uploading[];
function set(key: 'uploading', param: Uploading[]): void;
}
}
Session.setDefault('uploading', []);
export const uploadFileWithMessage = async (
rid: string,
{
description,
msg,
file,
}: {
file: File;
description?: string;
msg?: string;
},
tmid?: string,
): Promise<void> => {
const uploads = Session.get('uploading');
const upload = {
id: Random.id(),
name: file.name,
percentage: 0,
};
uploads.push(upload);
Session.set('uploading', uploads);
try {
await new Promise((resolve, reject) => {
const xhr = APIClient.upload(
`/v1/rooms.upload/${rid}`,
{
msg,
tmid,
file,
description,
},
{
load: (event) => {
return resolve(event);
},
progress: (event) => {
if (!event.lengthComputable) {
return;
}
const progress = (event.loaded / event.total) * 100;
if (progress === 100) {
return;
}
const uploads = Session.get('uploading');
uploads
.filter((u) => u.id === upload.id)
.forEach((u) => {
u.percentage = Math.round(progress) || 0;
});
Session.set('uploading', uploads);
},
error: (error) => {
const uploads = Session.get('uploading');
uploads
.filter((u) => u.id === upload.id)
.forEach((u) => {
u.error = new Error(xhr.responseText);
u.percentage = 0;
});
Session.set('uploading', uploads);
reject(error);
},
},
);
if (Session.get('uploading').length) {
UserAction.performContinuously(rid, USER_ACTIVITIES.USER_UPLOADING, { tmid });
}
Tracker.autorun((computation) => {
const isCanceling = Session.get(`uploading-cancel-${upload.id}`);
if (!isCanceling) {
return;
}
computation.stop();
Session.delete(`uploading-cancel-${upload.id}`);
xhr.abort();
const uploads = Session.get('uploading');
Session.set(
'uploading',
uploads.filter((u) => u.id !== upload.id),
);
});
});
const uploads = Session.get('uploading');
Session.set(
'uploading',
uploads.filter((u) => u.id !== upload.id),
);
if (!Session.get('uploading').length) {
UserAction.stop(rid, USER_ACTIVITIES.USER_UPLOADING, { tmid });
}
} catch (error) {
const uploads = Session.get('uploading');
uploads
.filter((u) => u.id === upload.id)
.forEach((u) => {
u.error = (error.xhr && error.xhr.responseJSON && error.xhr.responseJSON.error) || error.message;
u.percentage = 0;
});
if (!uploads.length) {
UserAction.stop(rid, USER_ACTIVITIES.USER_UPLOADING, { tmid });
}
Session.set('uploading', uploads);
}
};
type SingleOrArray<T> = T | T[];
type FileUploadProp = SingleOrArray<{
file: File;
name: string;
}>;
/* @deprecated */
export const fileUpload = async (
f: FileUploadProp,
input: HTMLInputElement,
{
rid,
tmid,
}: {
rid: string;
tmid?: string;
},
): Promise<void> => {
if (!f) {
throw new Error('No files to upload');
}
const threadsEnabled = settings.get('Threads_enabled');
const files = Array.isArray(f) ? f : [f];
const replies = $(input).data('reply') || [];
const mention = $(input).data('mention-user') || false;
let msg = '';
if (!mention || !threadsEnabled) {
msg = await prependReplies('', replies, mention);
}
if (mention && threadsEnabled && replies.length) {
tmid = replies[0]._id;
}
const key = ['messagebox', rid, tmid].filter(Boolean).join('_');
const messageBoxText = Meteor._localStorage.getItem(key) || '';
const uploadNextFile = (): void => {
const file = files.pop();
if (!file) {
return;
}
imperativeModal.open({
component: FileUploadModal,
props: {
file: file.file,
fileName: file.name,
fileDescription: messageBoxText,
onClose: (): void => {
imperativeModal.close();
uploadNextFile();
},
onSubmit: (fileName: string, description?: string): void => {
Object.defineProperty(file.file, 'name', {
writable: true,
value: fileName,
});
uploadFileWithMessage(
rid,
{
description,
msg,
file: file.file,
},
tmid,
);
const localStorageKey = ['messagebox', rid, tmid].filter(Boolean).join('_');
const chatMessageKey = [rid, tmid].filter(Boolean).join('-');
const { input } = chatMessages[chatMessageKey];
input.value = null;
$(input).trigger('input');
Meteor._localStorage.removeItem(localStorageKey);
imperativeModal.close();
uploadNextFile();
},
invalidContentType: Boolean(file.file.type && !fileUploadIsValidContentType(file.file.type)),
},
});
};
uploadNextFile();
};

@ -0,0 +1,52 @@
import { RestClient } from '@rocket.chat/api-client';
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import { baseURI } from '../../../../client/lib/baseURI';
import { process2faReturn } from '../../../../client/lib/2fa/process2faReturn';
export class RestApiClient extends RestClient {
getCredentials():
| {
'X-User-Id': string;
'X-Auth-Token': string;
}
| undefined {
const [uid, token] = [Meteor._localStorage.getItem(Accounts.USER_ID_KEY), Meteor._localStorage.getItem(Accounts.LOGIN_TOKEN_KEY)];
if (!uid || !token) {
return;
}
return {
'X-User-Id': uid,
'X-Auth-Token': token,
};
}
}
export const APIClient = new RestApiClient({
baseUrl: baseURI.replace(/\/$/, ''),
});
APIClient.use(function (request, next) {
try {
return next(...request);
} catch (e) {
return new Promise((resolve, reject) => {
process2faReturn({
error: e,
result: null,
emailOrUsername: undefined,
originalCallback: () => reject(e),
onCode(code, method) {
return resolve(
next(request[0], request[1], {
...request[2],
headers: { ...request[2].headers, 'x-2fa-code': code, 'x-2fa-method': method },
}),
);
},
});
});
}
});

@ -1,4 +1,5 @@
import { Serialized } from '@rocket.chat/core-typings';
import { RestClientInterface } from '@rocket.chat/api-client';
export declare const APIClient: {
delete<P, R = any>(endpoint: string, params?: Serialized<P>): Promise<Serialized<R>>;
@ -18,11 +19,8 @@ export declare const APIClient: {
'X-Auth-Token': string;
};
_jqueryCall(method?: string, endpoint?: string, params?: any, body?: any, headers?: Record<string, string>, dataType?: string): any;
/* @deprecated */
v1: {
delete<P, R = any>(endpoint: string, params?: Serialized<P>): Promise<Serialized<R>>;
get<P, R = any>(endpoint: string, params?: Serialized<P>): Promise<Serialized<R>>;
post<P, B, R = any>(endpoint: string, params?: Serialized<P>, body?: B): Promise<Serialized<R>>;
put<P, B, R = any>(endpoint: string, params?: Serialized<P>, body?: B): Promise<Serialized<R>>;
upload<P, B, R = any>(
endpoint: string,
params?: Serialized<P>,
@ -32,5 +30,5 @@ export declare const APIClient: {
error: (ev: ProgressEvent<XMLHttpRequestEventTarget>) => void;
},
): { promise: Promise<Serialized<R>> };
};
} & RestClientInterface;
};

@ -8,7 +8,7 @@ if (Meteor.isClient) {
settings = require('../../settings/server').settings;
}
const fileUploadMediaWhiteList = function (customWhiteList) {
export const fileUploadMediaWhiteList = function (customWhiteList) {
const mediaTypeWhiteList = customWhiteList || settings.get('FileUpload_MediaTypeWhiteList');
if (!mediaTypeWhiteList || mediaTypeWhiteList === '*') {

@ -27,7 +27,7 @@ export const slashCommands = {
permission: options.permission,
clientOnly: options.clientOnly || false,
result,
providesPreview,
providesPreview: Boolean(providesPreview),
previewer,
previewCallback,
} as SlashCommand;

@ -29,7 +29,7 @@ actionLinks.register('joinJitsiCall', function (message, params, instance) {
const clickTime = new Date();
const jitsiTimeout = new Date(room.jitsiTimeout);
APIClient.v1.post('statistics.telemetry', {
APIClient.post('/v1/statistics.telemetry', {
params: [{ eventName: 'updateCounter', timestamp: Date.now(), settingsId: 'Jitsi_Click_To_Join_Count' }],
});

@ -14,7 +14,7 @@ Tracker.autorun(async () => {
if (!Meteor.userId()) {
return;
}
const { accounts } = await APIClient.v1.get('webdav.getMyAccounts');
const { accounts } = await APIClient.get('/v1/webdav.getMyAccounts');
accounts.forEach((account) => WebdavAccounts.insert(account));
Notifications.onUser('webdav', ({ type, account }) => events[type](account));
});

@ -22,6 +22,6 @@ actionLinks.register('endLivechatWebRTCCall', async (message: IMessage) => {
dispatchToastMessage({ type: 'info', message: TAPi18n.__('Call_Already_Ended') });
return;
}
await APIClient.v1.put(`livechat/webrtc.call/${message._id}`, {}, { rid: _id, status: 'ended' });
await APIClient.put(`/v1/livechat/webrtc.call/${message._id}`, { rid: _id, status: 'ended' });
Notifications.notifyRoom(_id, 'webrtc', 'callStatus', { callStatus: 'ended' });
});

@ -9,7 +9,7 @@ addAction('webRTCVideo', ({ room }) => {
const handleClick = useCallback(async (): Promise<void> => {
if (!room.callStatus || room.callStatus === 'declined' || room.callStatus === 'ended') {
await APIClient.v1.get('livechat/webrtc.call', { rid: room._id });
await APIClient.get('/v1/livechat/webrtc.call', { rid: room._id });
}
window.open(`/meet/${room._id}`, room._id);
}, [room._id, room.callStatus]);

@ -43,7 +43,7 @@ const CreateDiscussion = ({ onClose, defaultParentRoom, parentMessageId, nameSug
const canCreate = (parentRoom || defaultParentRoom) && name;
const createDiscussion = useEndpointActionExperimental('POST', 'rooms.createDiscussion');
const createDiscussion = useEndpointActionExperimental('POST', '/v1/rooms.createDiscussion');
const create = useMutableCallback(async (): Promise<void> => {
try {

@ -9,7 +9,7 @@ import { roomCoordinator } from '../../lib/rooms/roomCoordinator';
const DefaultParentRoomField = ({ defaultParentRoom }: { defaultParentRoom: string }): ReactElement => {
const t = useTranslation();
const { value, phase } = useEndpointData(
'rooms.info',
'/v1/rooms.info',
useMemo(
() => ({
roomId: defaultParentRoom,

@ -23,7 +23,7 @@ const Tags = ({
const t = useTranslation();
const forms = useSubscription<any>(formsSubscription);
const { value: tagsResult, phase: stateTags } = useEndpointData('livechat/tags.list');
const { value: tagsResult, phase: stateTags } = useEndpointData('/v1/livechat/tags.list');
// TODO: Refactor the formsSubscription to use components instead of hooks (since the only thing the hook does is return a component)
const { useCurrentChatTags } = forms;

@ -22,9 +22,8 @@ export const useAgentsList = (
const t = useTranslation();
const [itemsList, setItemsList] = useState(() => new RecordList<ILivechatAgent>());
const reload = useCallback(() => setItemsList(new RecordList<ILivechatAgent>()), []);
const endpoint = 'livechat/users/agent';
const getAgents = useEndpoint('GET', endpoint);
const getAgents = useEndpoint('GET', '/v1/livechat/users/agent');
useComponentDidUpdate(() => {
options && reload();

@ -21,9 +21,8 @@ export const useAvailableAgentsList = (
} => {
const [itemsList, setItemsList] = useState(() => new RecordList<ILivechatAgent>());
const reload = useCallback(() => setItemsList(new RecordList<ILivechatAgent>()), []);
const endpoint = 'omnichannel/agents/available';
const getAgents = useEndpoint('GET', endpoint);
const getAgents = useEndpoint('GET', '/v1/omnichannel/agents/available');
useComponentDidUpdate(() => {
options && reload();

@ -28,7 +28,7 @@ export const useDepartmentsList = (
const [itemsList, setItemsList] = useState(() => new RecordList<ILivechatDepartmentRecord>());
const reload = useCallback(() => setItemsList(new RecordList<ILivechatDepartmentRecord>()), []);
const getDepartments = useEndpoint('GET', 'livechat/department');
const getDepartments = useEndpoint('GET', '/v1/livechat/department');
useComponentDidUpdate(() => {
options && reload();

@ -15,7 +15,7 @@ const CloseChatModalData = ({
onCancel: () => void;
onConfirm: (comment?: string, tags?: string[]) => Promise<void>;
}): ReactElement => {
const { value: data, phase: state } = useEndpointData(`livechat/department/${departmentId}`);
const { value: data, phase: state } = useEndpointData(`/v1/livechat/department/${departmentId}`);
if ([state].includes(AsyncStatePhase.LOADING)) {
return <FormSkeleton />;

@ -22,7 +22,7 @@ const ForwardChatModal = ({
room: IOmnichannelRoom;
}): ReactElement => {
const t = useTranslation();
const getUserData = useEndpoint('GET', 'users.info');
const getUserData = useEndpoint('GET', '/v1/users.info');
const { getValues, handleSubmit, register, setFocus, setValue, watch } = useForm();

@ -20,7 +20,7 @@ type RoomAutoCompleteProps<T> = Omit<ComponentProps<typeof AutoComplete>, 'value
const RoomAutoComplete = <T,>(props: RoomAutoCompleteProps<T>): ReactElement => {
const [filter, setFilter] = useState('');
const { value: data } = useEndpointData(
'rooms.autocomplete.channelAndPrivate',
'/v1/rooms.autocomplete.channelAndPrivate',
useMemo(() => query(filter), [filter]),
);
const options = useMemo(

@ -20,9 +20,8 @@ export const useRoomsList = (
} => {
const [itemsList, setItemsList] = useState(() => new RecordList<IRoom>());
const reload = useCallback(() => setItemsList(new RecordList<IRoom>()), []);
const endpoint = 'rooms.autocomplete.channelAndPrivate.withPagination';
const getRooms = useEndpoint('GET', endpoint);
const getRooms = useEndpoint('GET', '/v1/rooms.autocomplete.channelAndPrivate.withPagination');
useComponentDidUpdate(() => {
options && reload();

@ -14,10 +14,11 @@ const OmnichannelRoomIcon = new (class extends Emitter {
if (!appId || !icon) {
return;
}
if (this.icons.has(`${appId}-${icon}`)) {
return `${appId}-${icon}`;
}
APIClient.get(`apps/public/${appId}/get-sidebar-icon`, { icon }).then((response) => {
APIClient.get(`/apps/public/${appId}/get-sidebar-icon`, { icon }).then((response: any) => {
this.icons.set(
`${appId}-${icon}`,
DOMPurify.sanitize(response, {

@ -18,7 +18,7 @@ const TwoFactorEmailModal = ({ onConfirm, onClose, emailOrUsername }: TwoFactorE
const [code, setCode] = useState<string>('');
const ref = useAutoFocus<HTMLInputElement>();
const sendEmailCode = useEndpoint('POST', 'users.2fa.sendEmailCode');
const sendEmailCode = useEndpoint('POST', '/v1/users.2fa.sendEmailCode');
const onClickResendCode = async (): Promise<void> => {
try {

@ -25,7 +25,7 @@ const UserAutoComplete = ({ value, ...props }: UserAutoCompleteProps): ReactElem
const [filter, setFilter] = useState('');
const debouncedFilter = useDebouncedValue(filter, 1000);
const { value: data } = useEndpointData(
'users.autocomplete',
'/v1/users.autocomplete',
// eslint-disable-next-line react-hooks/exhaustive-deps
useMemo(() => query(debouncedFilter, conditions), [filter]),
);

@ -22,7 +22,7 @@ const UserAutoCompleteMultiple = ({ onChange, ...props }: UserAutoCompleteMultip
const [filter, setFilter] = useState('');
const debouncedFilter = useDebouncedValue(filter, 1000);
const { value: data } = useEndpointData(
'users.autocomplete',
'/v1/users.autocomplete',
useMemo(() => query(debouncedFilter), [debouncedFilter]),
);

@ -22,8 +22,8 @@ type ThreadReplyOptions = {
const ThreadMetric: FC<ThreadReplyOptions> = ({ unread, mention, all, rid, mid, counter, participants, following, lm, openThread }) => {
const t = useTranslation();
const followMessage = useEndpoint('POST', 'chat.followMessage');
const unfollowMessage = useEndpoint('POST', 'chat.unfollowMessage');
const followMessage = useEndpoint('POST', '/v1/chat.followMessage');
const unfollowMessage = useEndpoint('POST', '/v1/chat.unfollowMessage');
const format = useTimeAgo();
const handleFollow = useCallback(

@ -2,8 +2,7 @@ import { useToastMessageDispatch, UploadResult, useUpload } from '@rocket.chat/u
import { useCallback } from 'react';
export const useEndpointUpload = (
endpoint: string,
params = {},
endpoint: Parameters<typeof useUpload>[0],
successMessage: string,
): ((formData: FormData) => Promise<{ success: boolean }>) => {
const sendData = useUpload(endpoint);
@ -12,7 +11,7 @@ export const useEndpointUpload = (
return useCallback(
async (formData: FormData) => {
try {
const data = sendData(params, formData);
const data = sendData(formData);
const promise = data instanceof Promise ? data : data.promise;
@ -30,6 +29,6 @@ export const useEndpointUpload = (
return { success: false };
}
},
[dispatchToastMessage, params, sendData, successMessage],
[dispatchToastMessage, sendData, successMessage],
);
};

@ -48,9 +48,9 @@ export const useUpdateAvatar = (avatarObj: AvatarObject, userId: IUser['_id']):
[userId],
);
const saveAvatarAction = useEndpointUpload('users.setAvatar', saveAvatarQuery, successText);
const saveAvatarUrlAction = useEndpointAction('POST', 'users.setAvatar', saveAvatarQuery, successText);
const resetAvatarAction = useEndpointAction('POST', 'users.resetAvatar', resetAvatarQuery, successText);
const saveAvatarAction = useEndpointUpload('/v1/users.setAvatar', successText);
const saveAvatarUrlAction = useEndpointAction('POST', '/v1/users.setAvatar', saveAvatarQuery, successText);
const resetAvatarAction = useEndpointAction('POST', '/v1/users.resetAvatar', resetAvatarQuery, successText);
const updateAvatar = useCallback(async () => {
if (isAvatarReset(avatarObj)) {

@ -50,8 +50,10 @@ function wrapMeteorDDPCalls(): void {
Meteor.connection.onMessage(_message);
};
APIClient.v1
.post(`${endpoint}/${encodeURIComponent(message.method.replace(/\//g, ':'))}`, restParams)
APIClient.post(
`/v1/${endpoint}/${encodeURIComponent(message.method.replace(/\//g, ':'))}` as Parameters<typeof APIClient.post>[0],
restParams as any,
)
.then(({ message: _message }) => {
processResult(_message);
if (message.method === 'login') {

@ -51,6 +51,15 @@ const notify = (presence: UserPresence): void => {
}
};
declare module '@rocket.chat/rest-typings' {
// eslint-disable-next-line @typescript-eslint/interface-name-prefix
export interface Endpoints {
'/v1/users.presence': {
GET: (params: { ids: string[] }) => UsersPresencePayload;
};
}
}
const getPresence = ((): ((uid: UserPresence['_id']) => void) => {
let timer: ReturnType<typeof setTimeout>;
@ -81,7 +90,7 @@ const getPresence = ((): ((uid: UserPresence['_id']) => void) => {
ids: [...currentUids],
};
const { users } = (await APIClient.v1.get('users.presence', params)) as UsersPresencePayload;
const { users } = await APIClient.get('/v1/users.presence', params);
users.forEach((user) => {
if (!store.has(user._id)) {

@ -81,7 +81,7 @@ export const synchronizeUserData = async (uid: Meteor.User['_id']): Promise<RawU
}
});
const userData: RawUserData = await APIClient.v1.get('me');
const userData = await APIClient.get('/v1/me');
if (userData) {
updateUser({
...userData,

@ -44,7 +44,7 @@ type NetworkState = 'online' | 'offline';
export const CallProvider: FC = ({ children }) => {
const voipEnabled = useSetting('VoIP_Enabled');
const subscribeToNotifyUser = useStream('notify-user');
const dispatchEvent = useEndpoint('POST', 'voip/events');
const dispatchEvent = useEndpoint('POST', '/v1/voip/events');
const result = useVoipClient();
const user = useUser();
@ -234,9 +234,9 @@ export const CallProvider: FC = ({ children }) => {
};
}, [onNetworkConnected, onNetworkDisconnected, result.voipClient]);
const visitorEndpoint = useEndpoint('POST', 'livechat/visitor');
const voipEndpoint = useEndpoint('GET', 'voip/room');
const voipCloseRoomEndpoint = useEndpoint('POST', 'voip/room.close');
const visitorEndpoint = useEndpoint('POST', '/v1/livechat/visitor');
const voipEndpoint = useEndpoint('GET', '/v1/voip/room');
const voipCloseRoomEndpoint = useEndpoint('POST', '/v1/voip/room.close');
const [roomInfo, setRoomInfo] = useState<{ v: { token?: string }; rid: string }>();

@ -22,8 +22,8 @@ export const useVoipClient = (): UseVoipClientResult => {
const [voipEnabled, setVoipEnabled] = useSafely(useState(useSetting('VoIP_Enabled')));
const voipRetryCount = useSetting('VoIP_Retry_Count');
const enableKeepAlive = useSetting('VoIP_Enable_Keep_Alive_For_Unstable_Networks');
const registrationInfo = useEndpoint('GET', 'connector.extension.getRegistrationInfoByUserId');
const membership = useEndpoint('GET', 'voip/queues.getMembershipSubscription');
const registrationInfo = useEndpoint('GET', '/v1/connector.extension.getRegistrationInfoByUserId');
const membership = useEndpoint('GET', '/v1/voip/queues.getMembershipSubscription');
const user = useUser();
const subscribeToNotifyLoggedIn = useStream('notify-logged');
const iceServers = useWebRtcServers();

@ -28,31 +28,22 @@ const callEndpoint = <TMethod extends Method, TPath extends PathFor<TMethod>>(
path: TPath,
params: Serialized<OperationParams<TMethod, MatchPathPattern<TPath>>>,
): Promise<Serialized<OperationResult<TMethod, MatchPathPattern<TPath>>>> => {
const api = path[0] === '/' ? APIClient : APIClient.v1;
const endpointPath = path[0] === '/' ? path.slice(1) : path;
switch (method) {
case 'GET':
return api.get(endpointPath, params);
return APIClient.get(path as Parameters<typeof APIClient.get>[0], params) as any;
case 'POST':
return api.post(endpointPath, {}, params);
return APIClient.post(path as Parameters<typeof APIClient.post>[0], params) as ReturnType<typeof APIClient.post>;
case 'DELETE':
return api.delete(endpointPath, params);
return APIClient.delete(path as Parameters<typeof APIClient.delete>[0], params) as ReturnType<typeof APIClient.delete>;
default:
throw new Error('Invalid HTTP method');
}
};
const uploadToEndpoint = (endpoint: string, params: any, formData: any): Promise<UploadResult> => {
if (endpoint[0] === '/') {
return APIClient.upload(endpoint.slice(1), params, formData).promise;
}
return APIClient.v1.upload(endpoint, params, formData).promise;
};
const uploadToEndpoint = (endpoint: PathFor<'POST'>, formData: any): Promise<UploadResult> => APIClient.post(endpoint, formData);
const getStream = (streamName: string, options: {} = {}): (<T>(eventName: string, callback: (data: T) => void) => () => void) => {
const streamer = Meteor.StreamerCentral.instances[streamName]

@ -16,7 +16,7 @@ export const VoipFooter = (): ReactElement | null => {
const t = useTranslation();
const callerInfo = useCallerInfo();
const callActions = useCallActions();
const dispatchEvent = useEndpoint('POST', 'voip/events');
const dispatchEvent = useEndpoint('POST', '/v1/voip/events');
const createRoom = useCallCreateRoom();
const openRoom = useCallOpenRoom();

@ -25,8 +25,8 @@ type UseFormValues = {
};
const CreateChannelWithData = ({ onClose, teamId = '', reload }: CreateChannelWithDataProps): ReactElement => {
const createChannel = useEndpointActionExperimental('POST', 'channels.create');
const createPrivateChannel = useEndpointActionExperimental('POST', 'groups.create');
const createChannel = useEndpointActionExperimental('POST', '/v1/channels.create');
const createPrivateChannel = useEndpointActionExperimental('POST', '/v1/groups.create');
const canCreateChannel = usePermission('create-c');
const canCreatePrivateChannel = usePermission('create-p');
const e2eEnabledForPrivateByDefault = useSetting('E2E_Enabled_Default_PrivateRooms');

@ -18,7 +18,7 @@ const CreateDirectMessage: FC<CreateDirectMessageProps> = ({ onClose }) => {
const t = useTranslation();
const [users, setUsers] = useState<Array<Username>>([]);
const createDirect = useEndpointActionExperimental('POST', 'dm.create');
const createDirect = useEndpointActionExperimental('POST', '/v1/dm.create');
const onChangeUsers = useMutableCallback((value: Username | any, action: 'remove' | undefined) => {
if (!action) {

@ -1,4 +1,4 @@
import { IBanner, BannerPlatform, Serialized } from '@rocket.chat/core-typings';
import { BannerPlatform } from '@rocket.chat/core-typings';
import { Meteor } from 'meteor/meteor';
import { Tracker } from 'meteor/tracker';
@ -7,9 +7,7 @@ import { APIClient } from '../../app/utils/client';
import * as banners from '../lib/banners';
const fetchInitialBanners = async (): Promise<void> => {
const response: Serialized<{
banners: IBanner[];
}> = await APIClient.get('v1/banners', {
const response = await APIClient.get('/v1/banners', {
platform: BannerPlatform.Web,
});
@ -22,9 +20,7 @@ const fetchInitialBanners = async (): Promise<void> => {
};
const handleBanner = async (event: { bannerId: string }): Promise<void> => {
const response: Serialized<{
banners: IBanner[];
}> = await APIClient.get(`v1/banners/${event.bannerId}`, {
const response = await APIClient.get(`/v1/banners/${event.bannerId}`, {
platform: BannerPlatform.Web,
});

@ -74,8 +74,8 @@ FlowRouter.route('/meet/:rid', {
async action(_params, queryParams) {
if (queryParams?.token !== undefined) {
// visitor login
const visitor = await APIClient.v1.get(`livechat/visitor/${queryParams?.token}`);
if (visitor?.visitor) {
const result = await APIClient.get(`/v1/livechat/visitor/${queryParams.token}`);
if ('visitor' in result) {
appLayout.render(<MeetPage />);
return;
}

@ -6,12 +6,12 @@ import { slashCommands, APIClient } from '../../app/utils/client';
let oldUserId: IUser['_id'] | null = null;
Tracker.autorun(() => {
Tracker.autorun(async () => {
const newUserId = Meteor.userId();
if (oldUserId === null && newUserId) {
APIClient.v1.get('commands.list').then((result) => {
result.commands.forEach((command: typeof slashCommands.commands[string]) => {
slashCommands.commands[command.command] = command;
APIClient.get('/v1/commands.list').then((result) => {
result.commands.forEach((command) => {
slashCommands.add(command.command);
});
});
}

@ -9,8 +9,8 @@ const logAction = action('ServerContext');
const randomDelay = (): Promise<UploadResult> => new Promise((resolve) => setTimeout(resolve, Math.random() * 1000));
const uploadToEndpoint = (endpoint: string, params: any, formData: any): Promise<UploadResult> =>
Promise.resolve(logAction('uploadToEndpoint', endpoint, params, formData)).then(randomDelay);
const uploadToEndpoint = (endpoint: PathFor<'POST'>, formData: any): Promise<UploadResult> =>
Promise.resolve(logAction('uploadToEndpoint', endpoint, formData)).then(randomDelay);
const getStream = (streamName: string, options: {} = {}): (<T>(eventName: string, callback: (data: T) => void) => () => void) => {
logAction('getStream', streamName, options);

@ -47,7 +47,7 @@ const AccountProfilePage = () => {
const logout = useLogout();
const [loggingOut, setLoggingOut] = useState(false);
const logoutOtherClients = useEndpoint('POST', 'users.logoutOtherClients');
const logoutOtherClients = useEndpoint('POST', '/v1/users.logoutOtherClients');
const deleteOwnAccount = useMethod('deleteUserOwnAccount');
const saveFn = useMethod('saveUserProfile');

@ -8,7 +8,7 @@ import AddToken from './AddToken';
const AccountTokensPage = () => {
const t = useTranslation();
const { value: data, reload } = useEndpointData('users.getPersonalAccessTokens');
const { value: data, reload } = useEndpointData('/v1/users.getPersonalAccessTokens');
return (
<Page>

@ -18,7 +18,7 @@ const PasteStep: FC<PasteStepProps> = ({ onBackButtonClick, onFinish }) => {
setCloudKey(e.currentTarget.value);
};
const registerManually = useEndpoint('POST', 'cloud.manualRegister');
const registerManually = useEndpoint('POST', '/v1/cloud.manualRegister');
const handleFinishButtonClick = async (): Promise<void> => {
setLoading(true);

@ -28,7 +28,7 @@ const AddCustomEmoji = ({ close, onChange, ...props }: AddCustomEmojiProps): Rea
[setEmojiFile],
);
const saveAction = useEndpointUpload('emoji-custom.create', {}, t('Custom_Emoji_Added_Successfully'));
const saveAction = useEndpointUpload('/v1/emoji-custom.create', t('Custom_Emoji_Added_Successfully'));
const handleSave = useCallback(async () => {
if (!name) {

@ -45,7 +45,7 @@ const CustomEmoji: FC<CustomEmojiProps> = function CustomEmoji({ onClick, reload
500,
);
const { value: data, phase, reload: reloadEndPoint } = useEndpointData('emoji-custom.all', query);
const { value: data, phase, reload: reloadEndPoint } = useEndpointData('/v1/emoji-custom.all', query);
useEffect(() => {
reload.current = reloadEndPoint;

@ -53,7 +53,7 @@ const EditCustomEmoji: FC<EditCustomEmojiProps> = ({ close, onChange, data, ...p
[previousName, name, aliases, previousAliases, emojiFile],
);
const saveAction = useEndpointUpload('emoji-custom.update', {}, t('Custom_Emoji_Updated_Successfully'));
const saveAction = useEndpointUpload('/v1/emoji-custom.update', t('Custom_Emoji_Updated_Successfully'));
const handleSave = useCallback(async () => {
if (!name) {
@ -82,7 +82,7 @@ const EditCustomEmoji: FC<EditCustomEmojiProps> = ({ close, onChange, data, ...p
const deleteAction = useEndpointAction(
'POST',
'emoji-custom.delete',
'/v1/emoji-custom.delete',
useMemo(() => ({ emojiId: _id }), [_id]),
);

@ -25,7 +25,7 @@ const EditCustomEmojiWithData: FC<EditCustomEmojiWithDataProps> = ({ _id, onChan
phase: state,
error,
reload,
} = useEndpointData('emoji-custom.list', query);
} = useEndpointData('/v1/emoji-custom.list', query);
if (state === AsyncStatePhase.LOADING) {
return (

@ -48,7 +48,7 @@ function CustomSoundsRoute(): ReactElement {
500,
);
const { reload, ...result } = useEndpointData('custom-sounds.list', query);
const { reload, ...result } = useEndpointData('/v1/custom-sounds.list', query);
const handleItemClick = useCallback(
(_id) => (): void => {

@ -14,7 +14,7 @@ type EditCustomSoundProps = {
function EditCustomSound({ _id, onChange, ...props }: EditCustomSoundProps): ReactElement {
const query = useMemo(() => ({ query: JSON.stringify({ _id }) }), [_id]);
const { value: data, phase: state, error, reload } = useEndpointData('custom-sounds.list', query);
const { value: data, phase: state, error, reload } = useEndpointData('/v1/custom-sounds.list', query);
if (state === AsyncStatePhase.LOADING) {
return (

@ -17,7 +17,7 @@ const CustomUserStatusFormWithData = ({ _id, onReload, onClose }: CustomUserStat
const t = useTranslation();
const query = useMemo(() => ({ query: JSON.stringify({ _id }) }), [_id]);
const { value: data, phase: state, error, reload } = useEndpointData('custom-user-status.list', query);
const { value: data, phase: state, error, reload } = useEndpointData('/v1/custom-user-status.list', query);
const handleReload = (): void => {
onReload?.();

@ -41,7 +41,7 @@ const CustomUserStatus = ({ reload, onClick }: CustomUserStatusProps): ReactElem
500,
);
const { value, reload: reloadEndpoint, phase } = useEndpointData('custom-user-status.list', query);
const { value, reload: reloadEndpoint, phase } = useEndpointData('/v1/custom-user-status.list', query);
useEffect(() => {
reload.current = reloadEndpoint;

@ -13,7 +13,7 @@ type EmailInboxEditWithDataProps = {
const EmailInboxEditWithData = ({ id }: EmailInboxEditWithDataProps): ReactElement => {
const t = useTranslation();
const { value: data, error, phase: state } = useEndpointData(`email-inbox/${id}`);
const { value: data, error, phase: state } = useEndpointData(`/v1/email-inbox/${id}`);
if ([state].includes(AsyncStatePhase.LOADING)) {
return <FormSkeleton />;

@ -123,9 +123,9 @@ function EmailInboxForm({ id, data }) {
const close = useCallback(() => router.push({}), [router]);
const saveEmailInbox = useEndpoint('POST', 'email-inbox');
const deleteAction = useEndpoint('DELETE', `email-inbox/${id}`);
const emailAlreadyExistsAction = useEndpoint('GET', `email-inbox.search?email=${email}`);
const saveEmailInbox = useEndpoint('POST', '/v1/email-inbox');
const deleteAction = useEndpoint('DELETE', `/v1/email-inbox/${id}`);
const emailAlreadyExistsAction = useEndpoint('GET', '/v1/email-inbox.search');
useComponentDidUpdate(() => {
setEmailError(!validateEmail(email) ? t('Validate_email_address') : null);
@ -202,7 +202,7 @@ function EmailInboxForm({ id, data }) {
if (!email && !validateEmail(email)) {
return;
}
const { emailInbox } = await emailAlreadyExistsAction();
const { emailInbox } = await emailAlreadyExistsAction({ email });
if (!emailInbox || (id && emailInbox._id === id)) {
return;

@ -59,7 +59,7 @@ const EmailInboxTable = (): ReactElement => {
[router],
);
const { phase, value: { emailInboxes = [], count = 0 } = {} } = useEndpointData('email-inbox.list', query);
const { phase, value: { emailInboxes = [], count = 0 } = {} } = useEndpointData('/v1/email-inbox.list', query);
return (
<>

@ -10,7 +10,7 @@ const SendTestButton = ({ id }: SendTestButtonProps): ReactElement => {
const t = useTranslation();
const dispatchToastMessage = useToastMessageDispatch();
const sendTest = useEndpoint('POST', `email-inbox.send-test/${id}`);
const sendTest = useEndpoint('POST', `/v1/email-inbox.send-test/${id}`);
const handleOnClick = (e: React.MouseEvent<HTMLElement, MouseEvent>): void => {
e.preventDefault();

@ -15,10 +15,10 @@ function ImportHistoryPage() {
const [currentOperation, setCurrentOperation] = useSafely(useState());
const [latestOperations, setLatestOperations] = useSafely(useState([]));
const getCurrentImportOperation = useEndpoint('GET', 'getCurrentImportOperation');
const getLatestImportOperations = useEndpoint('GET', 'getLatestImportOperations');
const downloadPendingFiles = useEndpoint('POST', 'downloadPendingFiles');
const downloadPendingAvatars = useEndpoint('POST', 'downloadPendingAvatars');
const getCurrentImportOperation = useEndpoint('GET', '/v1/getCurrentImportOperation');
const getLatestImportOperations = useEndpoint('GET', '/v1/getLatestImportOperations');
const downloadPendingFiles = useEndpoint('POST', '/v1/downloadPendingFiles');
const downloadPendingAvatars = useEndpoint('POST', '/v1/downloadPendingAvatars');
const newImportRoute = useRoute('admin-import-new');
const importProgressRoute = useRoute('admin-import-progress');

@ -19,8 +19,8 @@ function ImportProgressPage() {
const [completed, setCompleted] = useSafely(useState(0));
const [total, setTotal] = useSafely(useState(0));
const getCurrentImportOperation = useEndpoint('GET', 'getCurrentImportOperation');
const getImportProgress = useEndpoint('GET', 'getImportProgress');
const getCurrentImportOperation = useEndpoint('GET', '/v1/getCurrentImportOperation');
const getImportProgress = useEndpoint('GET', '/v1/getImportProgress');
const importHistoryRoute = useRoute('admin-import');
const prepareImportRoute = useRoute('admin-import-prepare');

@ -38,8 +38,8 @@ function NewImportPage() {
const newImportRoute = useRoute('admin-import-new');
const prepareImportRoute = useRoute('admin-import-prepare');
const uploadImportFile = useEndpoint('POST', 'uploadImportFile');
const downloadPublicImportFile = useEndpoint('POST', 'downloadPublicImportFile');
const uploadImportFile = useEndpoint('POST', '/v1/uploadImportFile');
const downloadPublicImportFile = useEndpoint('POST', '/v1/downloadPublicImportFile');
useEffect(() => {
if (importerKey && !importer) {

@ -53,9 +53,9 @@ function PrepareImportPage() {
const newImportRoute = useRoute('admin-import-new');
const importProgressRoute = useRoute('admin-import-progress');
const getImportFileData = useEndpoint('GET', 'getImportFileData');
const getCurrentImportOperation = useEndpoint('GET', 'getCurrentImportOperation');
const startImport = useEndpoint('POST', 'startImport');
const getImportFileData = useEndpoint('GET', '/v1/getImportFileData');
const getCurrentImportOperation = useEndpoint('GET', '/v1/getCurrentImportOperation');
const startImport = useEndpoint('POST', '/v1/startImport');
useEffect(() => {
const streamer = new Meteor.Streamer('importers');

@ -20,7 +20,7 @@ const InformationRoute = (): ReactElement => {
const [statistics, setStatistics] = useState<IStats>();
const [instances, setInstances] = useState([]);
const [fetchStatistics, setFetchStatistics] = useState<fetchStatisticsCallback>(() => (): void => undefined);
const getStatistics = useEndpoint('GET', 'statistics');
const getStatistics = useEndpoint('GET', '/v1/statistics');
const getInstances = useMethod('instances/get');
useEffect(() => {

@ -19,7 +19,7 @@ const LicenseCard = (): ReactElement => {
const isAirGapped = true;
const { value, phase, error } = useEndpointData('licenses.get');
const { value, phase, error } = useEndpointData('/v1/licenses.get');
const endpointLoading = phase === AsyncStatePhase.LOADING;
const { modules = [] } = endpointLoading || error || !value?.licenses.length ? {} : value.licenses[0];

@ -36,7 +36,7 @@ function IntegrationsTable({ type }) {
const debouncedSort = useDebouncedValue(sort, 500);
const query = useQuery({ ...params, text: debouncedText, type }, debouncedSort);
const { value: data } = useEndpointData('integrations.list', query);
const { value: data } = useEndpointData('/v1/integrations.list', query);
const router = useRoute('admin-integrations');

@ -10,7 +10,7 @@ function EditIncomingWebhookWithData({ integrationId, ...props }) {
const t = useTranslation();
const params = useMemo(() => ({ integrationId }), [integrationId]);
const { value: data, phase: state, error, reload } = useEndpointData('integrations.get', params);
const { value: data, phase: state, error, reload } = useEndpointData('/v1/integrations.get', params);
const onChange = () => {
reload();

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save