From 7866ec3f33cb35b566471846eae4cb815e5ec016 Mon Sep 17 00:00:00 2001 From: Gleidson Daniel Silva Date: Mon, 14 Feb 2022 13:20:29 -0300 Subject: [PATCH] Chore: Add REST API definitions from server (#3721) * create first definitions * chore: implements get and post types * fix lint * add ts-ignore * add teams.removeRoom method * Remove unused endpoints Co-authored-by: Diego Mello --- app/definitions/ICustomEmojiDescriptor.ts | 7 + app/definitions/IInvite.ts | 11 + app/definitions/ILivechatAgent.ts | 16 ++ app/definitions/ILivechatDepartment.ts | 18 ++ app/definitions/ILivechatDepartmentAgents.ts | 9 + app/definitions/ILivechatMonitor.ts | 8 + app/definitions/ILivechatTag.ts | 7 + app/definitions/ILivechatVisitor.ts | 53 ++++ app/definitions/IMessage.ts | 1 + app/definitions/IRocketChat.ts | 2 +- app/definitions/IRocketChatRecord.ts | 10 + app/definitions/IRole.ts | 6 + app/definitions/IRoom.ts | 77 +++++- app/definitions/ISetting.ts | 237 ++++++++++++++++++ app/definitions/ITeam.ts | 45 +++- app/definitions/IUser.ts | 155 +++++++++++- app/definitions/UserStatus.ts | 6 + .../rest/helpers/PaginatedRequest.ts | 5 + .../rest/helpers/PaginatedResult.ts | 5 + app/definitions/rest/helpers/index.ts | 93 +++++++ app/definitions/rest/v1/channels.ts | 20 ++ app/definitions/rest/v1/chat.ts | 28 +++ app/definitions/rest/v1/customUserStatus.ts | 7 + app/definitions/rest/v1/dm.ts | 21 ++ app/definitions/rest/v1/emojiCustom.ts | 21 ++ app/definitions/rest/v1/groups.ts | 20 ++ app/definitions/rest/v1/im.ts | 36 +++ app/definitions/rest/v1/index.ts | 33 +++ app/definitions/rest/v1/invites.ts | 25 ++ app/definitions/rest/v1/omnichannel.ts | 194 ++++++++++++++ app/definitions/rest/v1/permissions.ts | 17 ++ app/definitions/rest/v1/roles.ts | 76 ++++++ app/definitions/rest/v1/rooms.ts | 44 ++++ app/definitions/rest/v1/settings.ts | 103 ++++++++ app/definitions/rest/v1/teams.ts | 7 + app/definitions/rest/v1/user.ts | 14 ++ app/definitions/rest/v1/users.ts | 14 ++ app/lib/rocketchat/rocketchat.js | 4 +- app/lib/rocketchat/services/sdk.ts | 49 +++- app/views/AddExistingChannelView.tsx | 2 + 40 files changed, 1486 insertions(+), 20 deletions(-) create mode 100644 app/definitions/ICustomEmojiDescriptor.ts create mode 100644 app/definitions/IInvite.ts create mode 100644 app/definitions/ILivechatAgent.ts create mode 100644 app/definitions/ILivechatDepartment.ts create mode 100644 app/definitions/ILivechatDepartmentAgents.ts create mode 100644 app/definitions/ILivechatMonitor.ts create mode 100644 app/definitions/ILivechatTag.ts create mode 100644 app/definitions/ILivechatVisitor.ts create mode 100644 app/definitions/IRocketChatRecord.ts create mode 100644 app/definitions/ISetting.ts create mode 100644 app/definitions/UserStatus.ts create mode 100644 app/definitions/rest/helpers/PaginatedRequest.ts create mode 100644 app/definitions/rest/helpers/PaginatedResult.ts create mode 100644 app/definitions/rest/helpers/index.ts create mode 100644 app/definitions/rest/v1/channels.ts create mode 100644 app/definitions/rest/v1/chat.ts create mode 100644 app/definitions/rest/v1/customUserStatus.ts create mode 100644 app/definitions/rest/v1/dm.ts create mode 100644 app/definitions/rest/v1/emojiCustom.ts create mode 100644 app/definitions/rest/v1/groups.ts create mode 100644 app/definitions/rest/v1/im.ts create mode 100644 app/definitions/rest/v1/index.ts create mode 100644 app/definitions/rest/v1/invites.ts create mode 100644 app/definitions/rest/v1/omnichannel.ts create mode 100644 app/definitions/rest/v1/permissions.ts create mode 100644 app/definitions/rest/v1/roles.ts create mode 100644 app/definitions/rest/v1/rooms.ts create mode 100644 app/definitions/rest/v1/settings.ts create mode 100644 app/definitions/rest/v1/teams.ts create mode 100644 app/definitions/rest/v1/user.ts create mode 100644 app/definitions/rest/v1/users.ts diff --git a/app/definitions/ICustomEmojiDescriptor.ts b/app/definitions/ICustomEmojiDescriptor.ts new file mode 100644 index 000000000..faf263c19 --- /dev/null +++ b/app/definitions/ICustomEmojiDescriptor.ts @@ -0,0 +1,7 @@ +import { IRocketChatRecord } from './IRocketChatRecord'; + +export interface ICustomEmojiDescriptor extends IRocketChatRecord { + name: string; + aliases: string[]; + extension: string; +} diff --git a/app/definitions/IInvite.ts b/app/definitions/IInvite.ts new file mode 100644 index 000000000..e9604f6e2 --- /dev/null +++ b/app/definitions/IInvite.ts @@ -0,0 +1,11 @@ +import { IRocketChatRecord } from './IRocketChatRecord'; + +export interface IInvite extends IRocketChatRecord { + days: number; + maxUses: number; + rid: string; + userId: string; + createdAt: Date; + expires: Date | null; + uses: number; +} diff --git a/app/definitions/ILivechatAgent.ts b/app/definitions/ILivechatAgent.ts new file mode 100644 index 000000000..01d97d082 --- /dev/null +++ b/app/definitions/ILivechatAgent.ts @@ -0,0 +1,16 @@ +import { IUser } from './IUser'; + +export enum ILivechatAgentStatus { + AVAILABLE = 'available', + UNAVAILABLE = 'unavailable' +} + +export interface ILivechatAgent extends IUser { + statusLivechat: ILivechatAgentStatus; + livechat: { + maxNumberSimultaneousChat: number; + }; + livechatCount: number; + lastRoutingTime: Date; + livechatStatusSystemModified?: boolean; +} diff --git a/app/definitions/ILivechatDepartment.ts b/app/definitions/ILivechatDepartment.ts new file mode 100644 index 000000000..a3e67e9f2 --- /dev/null +++ b/app/definitions/ILivechatDepartment.ts @@ -0,0 +1,18 @@ +export interface ILivechatDepartment { + _id: string; + name: string; + enabled: boolean; + description?: string; + showOnRegistration: boolean; + showOnOfflineForm: boolean; + requestTagBeforeClosingChat?: boolean; + email: string; + chatClosingTags?: string[]; + offlineMessageChannelName: string; + numAgents: number; + _updatedAt?: Date; + businessHourId?: string; + fallbackForwardDepartment?: string; + // extra optional fields + [k: string]: any; +} diff --git a/app/definitions/ILivechatDepartmentAgents.ts b/app/definitions/ILivechatDepartmentAgents.ts new file mode 100644 index 000000000..e33c80ff9 --- /dev/null +++ b/app/definitions/ILivechatDepartmentAgents.ts @@ -0,0 +1,9 @@ +export interface ILivechatDepartmentAgents { + _id: string; + departmentId: string; + departmentEnabled: boolean; + agentId: string; + username: string; + count: number; + order: number; +} diff --git a/app/definitions/ILivechatMonitor.ts b/app/definitions/ILivechatMonitor.ts new file mode 100644 index 000000000..231e4c855 --- /dev/null +++ b/app/definitions/ILivechatMonitor.ts @@ -0,0 +1,8 @@ +export interface ILivechatMonitor { + _id: string; + name: string; + enabled: boolean; + numMonitors: number; + type: string; + visibility: string; +} diff --git a/app/definitions/ILivechatTag.ts b/app/definitions/ILivechatTag.ts new file mode 100644 index 000000000..196c74c8d --- /dev/null +++ b/app/definitions/ILivechatTag.ts @@ -0,0 +1,7 @@ +export interface ILivechatTag { + _id: string; + name: string; + description: string; + numDepartments: number; + departments: Array; +} diff --git a/app/definitions/ILivechatVisitor.ts b/app/definitions/ILivechatVisitor.ts new file mode 100644 index 000000000..847d3b26c --- /dev/null +++ b/app/definitions/ILivechatVisitor.ts @@ -0,0 +1,53 @@ +import { IRocketChatRecord } from './IRocketChatRecord'; + +export interface IVisitorPhone { + phoneNumber: string; +} + +export interface IVisitorLastChat { + _id: string; + ts: string; +} + +export interface ILivechatVisitorConnectionData { + httpHeaders: { + [k: string]: string; + }; + clientAddress: string; +} + +export interface IVisitorEmail { + address: string; +} + +export interface ILivechatVisitor extends IRocketChatRecord { + username: string; + ts: Date; + token: string; + department?: string; + name?: string; + phone?: IVisitorPhone[] | null; + lastChat?: IVisitorLastChat; + userAgent?: string; + ip?: string; + host?: string; + visitorEmails?: IVisitorEmail[]; +} + +export interface ILivechatVisitorDTO { + id: string; + token: string; + name: string; + email: string; + department: string; + phone: string | { number: string }; + username: string; + customFields: { + key: string; + value: string; + overwrite: boolean; + }[]; + connectionData: { + httpHeaders: Record; + }; +} diff --git a/app/definitions/IMessage.ts b/app/definitions/IMessage.ts index d4c9d3266..423883b32 100644 --- a/app/definitions/IMessage.ts +++ b/app/definitions/IMessage.ts @@ -54,6 +54,7 @@ export interface ILastMessage { } export interface IMessage { + _id?: string; msg?: string; t?: SubscriptionType; ts: Date; diff --git a/app/definitions/IRocketChat.ts b/app/definitions/IRocketChat.ts index 199cf4891..654e2c92c 100644 --- a/app/definitions/IRocketChat.ts +++ b/app/definitions/IRocketChat.ts @@ -4,6 +4,6 @@ type TRocketChat = typeof rocketchat; export interface IRocketChat extends TRocketChat { sdk: any; - activeUsersSubTimeout: any; + activeUsersSubTimeout: any; roomsSub: any; } diff --git a/app/definitions/IRocketChatRecord.ts b/app/definitions/IRocketChatRecord.ts new file mode 100644 index 000000000..bed6d1147 --- /dev/null +++ b/app/definitions/IRocketChatRecord.ts @@ -0,0 +1,10 @@ +export interface IRocketChatRecord { + _id: string; + _updatedAt: Date; +} + +export type RocketChatRecordDeleted = T & + IRocketChatRecord & { + _deletedAt: Date; + __collection__: string; + }; diff --git a/app/definitions/IRole.ts b/app/definitions/IRole.ts index 1fec42150..332dab65d 100644 --- a/app/definitions/IRole.ts +++ b/app/definitions/IRole.ts @@ -3,6 +3,12 @@ import Model from '@nozbe/watermelondb/Model'; export interface IRole { id: string; description?: string; + mandatory2fa?: boolean; + name: string; + protected: boolean; + // scope?: string; + scope: 'Users' | 'Subscriptions'; + _id: string; } export type TRoleModel = IRole & Model; diff --git a/app/definitions/IRoom.ts b/app/definitions/IRoom.ts index 503ea4dd7..11130215b 100644 --- a/app/definitions/IRoom.ts +++ b/app/definitions/IRoom.ts @@ -1,9 +1,20 @@ import Model from '@nozbe/watermelondb/Model'; +import { IMessage } from './IMessage'; import { IServedBy } from './IServedBy'; import { SubscriptionType } from './ISubscription'; +import { IUser } from './IUser'; + +interface IRequestTranscript { + email: string; + requestedAt: Date; + requestedBy: IUser; + subject: string; +} export interface IRoom { + _id?: string; + fname?: string; id: string; rid: string; prid: string; @@ -15,13 +26,77 @@ export interface IRoom { broadcast: boolean; encrypted: boolean; ro: boolean; - v?: string[]; + v?: { + _id?: string; + token?: string; + status: 'online' | 'busy' | 'away' | 'offline'; + }; servedBy?: IServedBy; departmentId?: string; livechatData?: any; tags?: string[]; e2eKeyId?: string; avatarETag?: string; + default?: true; + featured?: true; +} + +export enum OmnichannelSourceType { + WIDGET = 'widget', + EMAIL = 'email', + SMS = 'sms', + APP = 'app', + API = 'api', + OTHER = 'other' // catch-all source type +} +export interface IOmnichannelRoom extends Omit { + t: SubscriptionType.OMNICHANNEL; + v: { + _id?: string; + token?: string; + status: 'online' | 'busy' | 'away' | 'offline'; + }; + email?: { + // Data used when the room is created from an email, via email Integration. + inbox: string; + thread: string; + replyTo: string; + subject: string; + }; + source: { + // TODO: looks like this is not so required as the definition suggests + // The source, or client, which created the Omnichannel room + type: OmnichannelSourceType; + // An optional identification of external sources, such as an App + id?: string; + // A human readable alias that goes with the ID, for post analytical purposes + alias?: string; + // A label to be shown in the room info + label?: string; + // The sidebar icon + sidebarIcon?: string; + // The default sidebar icon + defaultIcon?: string; + }; + transcriptRequest?: IRequestTranscript; + servedBy?: IServedBy; + onHold?: boolean; + departmentId?: string; + + lastMessage?: IMessage & { token?: string }; + + tags: any; + closedAt: any; + metrics: any; + waitingResponse: any; + responseBy: any; + priorityId: any; + livechatData: any; + queuedAt?: Date; + + ts: Date; + label?: string; + crmData?: unknown; } export type TRoomModel = IRoom & Model; diff --git a/app/definitions/ISetting.ts b/app/definitions/ISetting.ts new file mode 100644 index 000000000..a51494db5 --- /dev/null +++ b/app/definitions/ISetting.ts @@ -0,0 +1,237 @@ +export type SettingId = string; +export type GroupId = SettingId; +export type TabId = SettingId; +export type SectionName = string; + +export enum SettingEditor { + COLOR = 'color', + EXPRESSION = 'expression' +} +type AssetValue = { defaultUrl?: string }; +export type SettingValueMultiSelect = (string | number)[]; +export type SettingValueRoomPick = Array<{ _id: string; name: string }> | string; +export type SettingValue = string | boolean | number | SettingValueMultiSelect | Date | AssetValue | undefined; + +export interface ISettingSelectOption { + key: string | number; + i18nLabel: string; +} + +export type ISetting = ISettingBase | ISettingEnterprise | ISettingColor | ISettingCode | ISettingAction; + +export interface ISettingBase { + _id: SettingId; + type: + | 'boolean' + | 'timezone' + | 'string' + | 'relativeUrl' + | 'password' + | 'int' + | 'select' + | 'multiSelect' + | 'language' + | 'color' + | 'font' + | 'code' + | 'action' + | 'asset' + | 'roomPick' + | 'group' + | 'date'; + public: boolean; + env: boolean; + group?: GroupId; + section?: SectionName; + tab?: TabId; + i18nLabel: string; + value: SettingValue; + packageValue: SettingValue; + blocked: boolean; + // enableQuery?: string | FilterQuery | FilterQuery[]; + // displayQuery?: string | FilterQuery | FilterQuery[]; + sorter: number; + properties?: unknown; + enterprise?: boolean; + requiredOnWizard?: boolean; + hidden?: boolean; + modules?: Array; + invalidValue?: SettingValue; + valueSource?: string; + secret?: boolean; + i18nDescription?: string; + autocomplete?: boolean; + processEnvValue?: SettingValue; + meteorSettingsValue?: SettingValue; + ts: Date; + createdAt: Date; + _updatedAt?: Date; + multiline?: boolean; + values?: Array; + placeholder?: string; + wizard?: { + step: number; + order: number; + } | null; + persistent?: boolean; // todo: remove + readonly?: boolean; // todo: remove + alert?: string; // todo: check if this is still used + private?: boolean; // todo: remove +} + +export interface ISettingGroup { + _id: string; + hidden: boolean; + blocked: boolean; + ts?: Date; + sorter: number; + i18nLabel: string; + // displayQuery?: string | FilterQuery | FilterQuery[]; + i18nDescription: string; + value?: undefined; + type: 'group'; + + alert?: string; // todo: check if this is needed +} + +export interface ISettingEnterprise extends ISettingBase { + enterprise: true; + invalidValue: SettingValue; +} + +export interface ISettingColor extends ISettingBase { + type: 'color'; + editor: SettingEditor; + packageEditor?: SettingEditor; +} +export interface ISettingCode extends ISettingBase { + type: 'code'; + code?: string; +} + +export interface ISettingAction extends ISettingBase { + type: 'action'; + value: string; + actionText?: string; +} +export interface ISettingAsset extends ISettingBase { + type: 'asset'; + value: AssetValue; +} + +export interface ISettingDate extends ISettingBase { + type: 'date'; + value: Date; +} + +export const isDateSetting = (setting: ISetting): setting is ISettingDate => setting.type === 'date'; + +export const isSettingEnterprise = (setting: ISettingBase): setting is ISettingEnterprise => setting.enterprise === true; + +export const isSettingColor = (setting: ISettingBase): setting is ISettingColor => setting.type === 'color'; + +export const isSettingCode = (setting: ISettingBase): setting is ISettingCode => setting.type === 'code'; + +export const isSettingAction = (setting: ISettingBase): setting is ISettingAction => setting.type === 'action'; + +export const isSettingAsset = (setting: ISettingBase): setting is ISettingAsset => setting.type === 'asset'; + +export interface ISettingStatistics { + account2fa?: boolean; + cannedResponsesEnabled?: boolean; + e2e?: boolean; + e2eDefaultDirectRoom?: boolean; + e2eDefaultPrivateRoom?: boolean; + smtpHost?: string; + smtpPort?: string; + fromEmail?: string; + frameworkDevMode?: boolean; + frameworkEnable?: boolean; + surveyEnabled?: boolean; + updateChecker?: boolean; + liveStream?: boolean; + broadcasting?: boolean; + allowEditing?: boolean; + allowDeleting?: boolean; + allowUnrecognizedSlashCommand?: boolean; + allowBadWordsFilter?: boolean; + readReceiptEnabled?: boolean; + readReceiptStoreUsers?: boolean; + otrEnable?: boolean; + pushEnable?: boolean; + globalSearchEnabled?: boolean; + threadsEnabled?: boolean; + bigBlueButton?: boolean; + jitsiEnabled?: boolean; + webRTCEnableChannel?: boolean; + webRTCEnablePrivate?: boolean; + webRTCEnableDirect?: boolean; +} + +export interface ISettingStatisticsObject { + accounts?: { + account2fa?: boolean; + }; + cannedResponses?: { + cannedResponsesEnabled?: boolean; + }; + e2ee?: { + e2e?: boolean; + e2eDefaultDirectRoom?: boolean; + e2eDefaultPrivateRoom?: boolean; + }; + email?: { + smtp?: { + smtpHost?: string; + smtpPort?: string; + fromEmail?: string; + }; + }; + general?: { + apps?: { + frameworkDevMode?: boolean; + frameworkEnable?: boolean; + }; + nps?: { + surveyEnabled?: boolean; + }; + update?: { + updateChecker?: boolean; + }; + }; + liveStreamAndBroadcasting?: { + liveStream?: boolean; + broadcasting?: boolean; + }; + message?: { + allowEditing?: boolean; + allowDeleting?: boolean; + allowUnrecognizedSlashCommand?: boolean; + allowBadWordsFilter?: boolean; + readReceiptEnabled?: boolean; + readReceiptStoreUsers?: boolean; + }; + otr?: { + otrEnable?: boolean; + }; + push?: { + pushEnable?: boolean; + }; + search?: { + defaultProvider?: { + globalSearchEnabled?: boolean; + }; + }; + threads?: { + threadsEnabled?: boolean; + }; + videoConference?: { + bigBlueButton?: boolean; + jitsiEnabled?: boolean; + }; + webRTC?: { + webRTCEnableChannel?: boolean; + webRTCEnablePrivate?: boolean; + webRTCEnableDirect?: boolean; + }; +} diff --git a/app/definitions/ITeam.ts b/app/definitions/ITeam.ts index 5a76a2aad..3b1774054 100644 --- a/app/definitions/ITeam.ts +++ b/app/definitions/ITeam.ts @@ -1,5 +1,48 @@ -// https://github.com/RocketChat/Rocket.Chat/blob/develop/definition/ITeam.ts +import { IRocketChatRecord } from './IRocketChatRecord'; +import { IUser } from './IUser'; + export enum TEAM_TYPE { PUBLIC = 0, PRIVATE = 1 } + +export type SortType = -1 | 1; + +export interface ITeam extends IRocketChatRecord { + name: string; + type: TEAM_TYPE; + roomId: string; + createdBy: Pick; + createdAt: Date; +} + +export interface ITeamMember extends IRocketChatRecord { + teamId: string; + userId: string; + roles?: Array; + createdBy: Pick; + createdAt: Date; +} +export interface IPaginationOptions { + offset: number; + count: number; +} +export interface IRecordsWithTotal { + records: Array; + total: number; +} + +export interface ITeamStatData { + teamId: string; + mainRoom: string; + totalRooms: number; + totalMessages: number; + totalPublicRooms: number; + totalPrivateRooms: number; + totalDefaultRooms: number; + totalMembers: number; +} +export interface ITeamStats { + totalTeams: number; + teamStats: Array; +} diff --git a/app/definitions/IUser.ts b/app/definitions/IUser.ts index 012ef8087..1b5d244c8 100644 --- a/app/definitions/IUser.ts +++ b/app/definitions/IUser.ts @@ -1,10 +1,155 @@ import Model from '@nozbe/watermelondb/Model'; -export interface IUser { - _id: string; - name?: string; - username: string; - avatarETag?: string; +import { UserStatus } from './UserStatus'; +import { IRocketChatRecord } from './IRocketChatRecord'; + +export interface ILoginToken { + hashedToken: string; + twoFactorAuthorizedUntil?: Date; + twoFactorAuthorizedHash?: string; } +export interface IMeteorLoginToken extends ILoginToken { + when: Date; +} + +export interface IPersonalAccessToken extends ILoginToken { + type: 'personalAccessToken'; + createdAt: Date; + lastTokenPart: string; + name?: string; + bypassTwoFactor?: boolean; +} + +export interface IUserEmailVerificationToken { + token: string; + address: string; + when: Date; +} + +export interface IUserEmailCode { + code: string; + expire: Date; +} + +type LoginToken = IMeteorLoginToken & IPersonalAccessToken; +export type Username = string; + +export type ILoginUsername = + | { + username: string; + } + | { + email: string; + }; +export type LoginUsername = string | ILoginUsername; + +export interface IUserServices { + password?: { + bcrypt: string; + }; + passwordHistory?: string[]; + email?: { + verificationTokens?: IUserEmailVerificationToken[]; + }; + resume?: { + loginTokens?: LoginToken[]; + }; + google?: any; + facebook?: any; + github?: any; + totp?: { + enabled: boolean; + hashedBackup: string[]; + secret: string; + }; + email2fa?: { + enabled: boolean; + changedAt: Date; + }; + emailCode: IUserEmailCode[]; + saml?: { + inResponseTo?: string; + provider?: string; + idp?: string; + idpSession?: string; + nameID?: string; + }; + ldap?: { + id: string; + idAttribute?: string; + }; +} + +export interface IUserEmail { + address: string; + verified: boolean; +} + +export interface IUserSettings { + profile: any; + preferences: { + [key: string]: any; + }; +} + +export interface IUser extends IRocketChatRecord { + _id: string; + createdAt: Date; + roles: string[]; + type: string; + active: boolean; + username?: string; + name?: string; + services?: IUserServices; + emails?: IUserEmail[]; + status?: UserStatus; + statusConnection?: string; + lastLogin?: Date; + avatarOrigin?: string; + avatarETag?: string; + utcOffset?: number; + language?: string; + statusDefault?: UserStatus; + statusText?: string; + oauth?: { + authorizedClients: string[]; + }; + _updatedAt: Date; + statusLivechat?: string; + e2e?: { + private_key: string; + public_key: string; + }; + requirePasswordChange?: boolean; + customFields?: { + [key: string]: any; + }; + settings?: IUserSettings; + defaultRoom?: string; + ldap?: boolean; +} + +export interface IRegisterUser extends IUser { + username: string; + name: string; +} +export const isRegisterUser = (user: IUser): user is IRegisterUser => user.username !== undefined && user.name !== undefined; + +export type IUserDataEvent = { + id: unknown; +} & ( + | ({ + type: 'inserted'; + } & IUser) + | { + type: 'removed'; + } + | { + type: 'updated'; + diff: Partial; + unset: Record; + } +); + export type TUserModel = IUser & Model; diff --git a/app/definitions/UserStatus.ts b/app/definitions/UserStatus.ts new file mode 100644 index 000000000..fafb9ccaa --- /dev/null +++ b/app/definitions/UserStatus.ts @@ -0,0 +1,6 @@ +export enum UserStatus { + ONLINE = 'online', + AWAY = 'away', + OFFLINE = 'offline', + BUSY = 'busy' +} diff --git a/app/definitions/rest/helpers/PaginatedRequest.ts b/app/definitions/rest/helpers/PaginatedRequest.ts new file mode 100644 index 000000000..3543675e3 --- /dev/null +++ b/app/definitions/rest/helpers/PaginatedRequest.ts @@ -0,0 +1,5 @@ +export type PaginatedRequest = { + count?: number; + offset?: number; + sort?: `{ "${S}": ${1 | -1} }` | string; +} & T; diff --git a/app/definitions/rest/helpers/PaginatedResult.ts b/app/definitions/rest/helpers/PaginatedResult.ts new file mode 100644 index 000000000..ea153093c --- /dev/null +++ b/app/definitions/rest/helpers/PaginatedResult.ts @@ -0,0 +1,5 @@ +export type PaginatedResult = { + count: number; + offset: number; + total: number; +} & T; diff --git a/app/definitions/rest/helpers/index.ts b/app/definitions/rest/helpers/index.ts new file mode 100644 index 000000000..00a73d1ff --- /dev/null +++ b/app/definitions/rest/helpers/index.ts @@ -0,0 +1,93 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { Endpoints } from '../v1'; + +type ReplacePlaceholders = string extends TPath + ? TPath + : TPath extends `${infer Start}:${infer _Param}/${infer Rest}` + ? `${Start}${string}/${ReplacePlaceholders}` + : TPath extends `${infer Start}:${infer _Param}` + ? `${Start}${string}` + : TPath; + +type KeyOfEach = T extends any ? keyof T : never; + +type GetParams = TOperation extends (...args: any) => any + ? Parameters[0] extends void + ? void + : Parameters[0] + : never; + +type GetResult = TOperation extends (...args: any) => any ? ReturnType : never; + +type OperationsByPathPatternAndMethod< + TPathPattern extends keyof Endpoints, + TMethod extends KeyOfEach = KeyOfEach +> = TMethod extends any + ? { + pathPattern: TPathPattern; + method: TMethod; + path: ReplacePlaceholders; + params: GetParams; + result: GetResult; + } + : never; + +type OperationsByPathPattern = TPathPattern extends any + ? OperationsByPathPatternAndMethod + : never; + +type Operations = OperationsByPathPattern; + +type Method = Operations['method']; + +export type PathPattern = Operations['pathPattern']; + +type Path = Operations['path']; + +// + +export type Serialized = T extends Date + ? Exclude | string + : T extends boolean | number | string | null | undefined + ? T + : T extends {} + ? { + [K in keyof T]: Serialized; + } + : null; + +export type MatchPathPattern = TPath extends any + ? Extract['pathPattern'] + : never; + +export type OperationResult< + TMethod extends Method, + TPathPattern extends PathPattern +> = TMethod extends keyof Endpoints[TPathPattern] ? GetResult : never; + +export type PathFor = TMethod extends any ? Extract['path'] : never; + +export type OperationParams< + TMethod extends Method, + TPathPattern extends PathPattern +> = TMethod extends keyof Endpoints[TPathPattern] ? GetParams : never; + +type SuccessResult = T & { success: true }; + +type FailureResult = { + success: false; + error: T; + stack: TStack; + errorType: TErrorType; + details: TErrorDetails; +}; + +type UnauthorizedResult = { + success: false; + error: T | 'unauthorized'; +}; + +export type ResultFor = + | SuccessResult> + | FailureResult + | UnauthorizedResult; diff --git a/app/definitions/rest/v1/channels.ts b/app/definitions/rest/v1/channels.ts new file mode 100644 index 000000000..d16798774 --- /dev/null +++ b/app/definitions/rest/v1/channels.ts @@ -0,0 +1,20 @@ +import type { IMessage } from '../../IMessage'; +import type { IRoom } from '../../IRoom'; +import type { IUser } from '../../IUser'; + +export type ChannelsEndpoints = { + 'channels.files': { + GET: (params: { roomId: IRoom['_id']; offset: number; count: number; sort: string; query: string }) => { + files: IMessage[]; + total: number; + }; + }; + 'channels.members': { + GET: (params: { roomId: IRoom['_id']; offset?: number; count?: number; filter?: string; status?: string[] }) => { + count: number; + offset: number; + members: IUser[]; + total: number; + }; + }; +}; diff --git a/app/definitions/rest/v1/chat.ts b/app/definitions/rest/v1/chat.ts new file mode 100644 index 000000000..d33cd7949 --- /dev/null +++ b/app/definitions/rest/v1/chat.ts @@ -0,0 +1,28 @@ +import type { IMessage } from '../../IMessage'; +import type { IRoom } from '../../IRoom'; + +export type ChatEndpoints = { + 'chat.getMessage': { + GET: (params: { msgId: IMessage['_id'] }) => { + message: IMessage; + }; + }; + 'chat.followMessage': { + POST: (params: { mid: IMessage['_id'] }) => void; + }; + 'chat.unfollowMessage': { + POST: (params: { mid: IMessage['_id'] }) => void; + }; + 'chat.getDiscussions': { + GET: (params: { roomId: IRoom['_id']; text?: string; offset: number; count: number }) => { + messages: IMessage[]; + total: number; + }; + }; + 'chat.getThreadsList': { + GET: (params: { rid: IRoom['_id']; type: 'unread' | 'following' | 'all'; text?: string; offset: number; count: number }) => { + threads: IMessage[]; + total: number; + }; + }; +}; diff --git a/app/definitions/rest/v1/customUserStatus.ts b/app/definitions/rest/v1/customUserStatus.ts new file mode 100644 index 000000000..db5e76ee4 --- /dev/null +++ b/app/definitions/rest/v1/customUserStatus.ts @@ -0,0 +1,7 @@ +export type CustomUserStatusEndpoints = { + 'custom-user-status.list': { + GET: (params: { query: string }) => { + statuses: unknown[]; + }; + }; +}; diff --git a/app/definitions/rest/v1/dm.ts b/app/definitions/rest/v1/dm.ts new file mode 100644 index 000000000..6783cbc07 --- /dev/null +++ b/app/definitions/rest/v1/dm.ts @@ -0,0 +1,21 @@ +import type { IRoom } from '../../IRoom'; +import type { IUser } from '../../IUser'; + +export type DmEndpoints = { + 'dm.create': { + POST: ( + params: ( + | { + username: Exclude; + } + | { + usernames: string; + } + ) & { + excludeSelf?: boolean; + } + ) => { + room: IRoom & { rid: IRoom['_id'] }; + }; + }; +}; diff --git a/app/definitions/rest/v1/emojiCustom.ts b/app/definitions/rest/v1/emojiCustom.ts new file mode 100644 index 000000000..8ef956e5a --- /dev/null +++ b/app/definitions/rest/v1/emojiCustom.ts @@ -0,0 +1,21 @@ +import type { ICustomEmojiDescriptor } from '../../ICustomEmojiDescriptor'; +import { PaginatedRequest } from '../helpers/PaginatedRequest'; +import { PaginatedResult } from '../helpers/PaginatedResult'; + +export type EmojiCustomEndpoints = { + 'emoji-custom.all': { + GET: (params: PaginatedRequest<{ query: string }, 'name'>) => { + emojis: ICustomEmojiDescriptor[]; + } & PaginatedResult; + }; + 'emoji-custom.list': { + GET: (params: { query: string }) => { + emojis?: { + update: ICustomEmojiDescriptor[]; + }; + }; + }; + 'emoji-custom.delete': { + POST: (params: { emojiId: ICustomEmojiDescriptor['_id'] }) => void; + }; +}; diff --git a/app/definitions/rest/v1/groups.ts b/app/definitions/rest/v1/groups.ts new file mode 100644 index 000000000..ca6454734 --- /dev/null +++ b/app/definitions/rest/v1/groups.ts @@ -0,0 +1,20 @@ +import type { IMessage } from '../../IMessage'; +import type { IRoom } from '../../IRoom'; +import type { IUser } from '../../IUser'; + +export type GroupsEndpoints = { + 'groups.files': { + GET: (params: { roomId: IRoom['_id']; count: number; sort: string; query: string }) => { + files: IMessage[]; + total: number; + }; + }; + 'groups.members': { + GET: (params: { roomId: IRoom['_id']; offset?: number; count?: number; filter?: string; status?: string[] }) => { + count: number; + offset: number; + members: IUser[]; + total: number; + }; + }; +}; diff --git a/app/definitions/rest/v1/im.ts b/app/definitions/rest/v1/im.ts new file mode 100644 index 000000000..a9502ab2d --- /dev/null +++ b/app/definitions/rest/v1/im.ts @@ -0,0 +1,36 @@ +import type { IMessage } from '../../IMessage'; +import type { IRoom } from '../../IRoom'; +import type { IUser } from '../../IUser'; + +export type ImEndpoints = { + 'im.create': { + POST: ( + params: ( + | { + username: Exclude; + } + | { + usernames: string; + } + ) & { + excludeSelf?: boolean; + } + ) => { + room: IRoom; + }; + }; + 'im.files': { + GET: (params: { roomId: IRoom['_id']; count: number; sort: string; query: string }) => { + files: IMessage[]; + total: number; + }; + }; + 'im.members': { + GET: (params: { roomId: IRoom['_id']; offset?: number; count?: number; filter?: string; status?: string[] }) => { + count: number; + offset: number; + members: IUser[]; + total: number; + }; + }; +}; diff --git a/app/definitions/rest/v1/index.ts b/app/definitions/rest/v1/index.ts new file mode 100644 index 000000000..ae5f29262 --- /dev/null +++ b/app/definitions/rest/v1/index.ts @@ -0,0 +1,33 @@ +import { ChannelsEndpoints } from './channels'; +import { ChatEndpoints } from './chat'; +import { CustomUserStatusEndpoints } from './customUserStatus'; +import { DmEndpoints } from './dm'; +import { EmojiCustomEndpoints } from './emojiCustom'; +import { GroupsEndpoints } from './groups'; +import { ImEndpoints } from './im'; +import { InvitesEndpoints } from './invites'; +import { OmnichannelEndpoints } from './omnichannel'; +import { PermissionsEndpoints } from './permissions'; +import { RolesEndpoints } from './roles'; +import { RoomsEndpoints } from './rooms'; +import { OauthCustomConfiguration } from './settings'; +import { UserEndpoints } from './user'; +import { UsersEndpoints } from './users'; +import { TeamsEndpoints } from './teams'; + +export type Endpoints = ChannelsEndpoints & + ChatEndpoints & + CustomUserStatusEndpoints & + DmEndpoints & + EmojiCustomEndpoints & + GroupsEndpoints & + ImEndpoints & + InvitesEndpoints & + OmnichannelEndpoints & + PermissionsEndpoints & + RolesEndpoints & + RoomsEndpoints & + OauthCustomConfiguration & + UserEndpoints & + UsersEndpoints & + TeamsEndpoints; diff --git a/app/definitions/rest/v1/invites.ts b/app/definitions/rest/v1/invites.ts new file mode 100644 index 000000000..6682c9e32 --- /dev/null +++ b/app/definitions/rest/v1/invites.ts @@ -0,0 +1,25 @@ +import type { IInvite } from '../../IInvite'; +import type { IRoom } from '../../IRoom'; + +export type InvitesEndpoints = { + listInvites: { + GET: () => Array; + }; + 'removeInvite/:_id': { + DELETE: () => void; + }; + '/v1/useInviteToken': { + POST: (params: { token: string }) => { + room: { + rid: IRoom['_id']; + prid: IRoom['prid']; + fname: IRoom['fname']; + name: IRoom['name']; + t: IRoom['t']; + }; + }; + }; + '/v1/validateInviteToken': { + POST: (params: { token: string }) => { valid: boolean }; + }; +}; diff --git a/app/definitions/rest/v1/omnichannel.ts b/app/definitions/rest/v1/omnichannel.ts new file mode 100644 index 000000000..784415a53 --- /dev/null +++ b/app/definitions/rest/v1/omnichannel.ts @@ -0,0 +1,194 @@ +import { ILivechatAgent } from '../../ILivechatAgent'; +import { ILivechatDepartment } from '../../ILivechatDepartment'; +import { ILivechatDepartmentAgents } from '../../ILivechatDepartmentAgents'; +import { ILivechatMonitor } from '../../ILivechatMonitor'; +import { ILivechatTag } from '../../ILivechatTag'; +import { ILivechatVisitor, ILivechatVisitorDTO } from '../../ILivechatVisitor'; +import { IMessage } from '../../IMessage'; +import { IOmnichannelRoom, IRoom } from '../../IRoom'; +import { ISetting } from '../../ISetting'; +import { PaginatedRequest } from '../helpers/PaginatedRequest'; +import { PaginatedResult } from '../helpers/PaginatedResult'; + +type booleanString = 'true' | 'false'; + +export type OmnichannelEndpoints = { + 'livechat/appearance': { + GET: () => { + appearance: ISetting[]; + }; + }; + 'livechat/visitors.info': { + GET: (params: { visitorId: string }) => { + visitor: { + visitorEmails: Array<{ + address: string; + }>; + }; + }; + }; + 'livechat/room.onHold': { + POST: (params: { roomId: IRoom['_id'] }) => void; + }; + 'livechat/monitors.list': { + GET: (params: PaginatedRequest<{ text: string }>) => PaginatedResult<{ + monitors: ILivechatMonitor[]; + }>; + }; + 'livechat/tags.list': { + GET: (params: PaginatedRequest<{ text: string }, 'name'>) => PaginatedResult<{ + tags: ILivechatTag[]; + }>; + }; + 'livechat/department': { + GET: ( + params: PaginatedRequest<{ + text: string; + onlyMyDepartments?: booleanString; + enabled?: boolean; + excludeDepartmentId?: string; + }> + ) => PaginatedResult<{ + departments: ILivechatDepartment[]; + }>; + POST: (params: { department: Partial; agents: string[] }) => { + department: ILivechatDepartment; + agents: any[]; + }; + }; + 'livechat/department/:_id': { + GET: (params: { onlyMyDepartments?: booleanString; includeAgents?: booleanString }) => { + department: ILivechatDepartment | null; + agents?: any[]; + }; + PUT: (params: { department: Partial[]; agents: any[] }) => { + department: ILivechatDepartment; + agents: any[]; + }; + DELETE: () => void; + }; + 'livechat/department.autocomplete': { + GET: (params: { selector: string; onlyMyDepartments: booleanString }) => { + items: ILivechatDepartment[]; + }; + }; + 'livechat/department/:departmentId/agents': { + GET: (params: { sort: string }) => PaginatedResult<{ agents: ILivechatDepartmentAgents[] }>; + POST: (params: { upsert: string[]; remove: string[] }) => void; + }; + 'livechat/departments.available-by-unit/:id': { + GET: (params: PaginatedRequest<{ text: string }>) => PaginatedResult<{ + departments: ILivechatDepartment[]; + }>; + }; + 'livechat/departments.by-unit/': { + GET: (params: PaginatedRequest<{ text: string }>) => PaginatedResult<{ + departments: ILivechatDepartment[]; + }>; + }; + + 'livechat/departments.by-unit/:id': { + GET: (params: PaginatedRequest<{ text: string }>) => PaginatedResult<{ + departments: ILivechatDepartment[]; + }>; + }; + + 'livechat/department.listByIds': { + GET: (params: { ids: string[]; fields?: Record }) => { departments: ILivechatDepartment[] }; + }; + + 'livechat/custom-fields': { + GET: (params: PaginatedRequest<{ text: string }>) => PaginatedResult<{ + customFields: [ + { + _id: string; + label: string; + } + ]; + }>; + }; + 'livechat/rooms': { + GET: (params: { + guest: string; + fname: string; + servedBy: string[]; + status: string; + department: string; + from: string; + to: string; + customFields: any; + current: number; + itemsPerPage: number; + tags: string[]; + }) => PaginatedResult<{ + rooms: IOmnichannelRoom[]; + }>; + }; + 'livechat/:rid/messages': { + GET: (params: PaginatedRequest<{ query: string }>) => PaginatedResult<{ + messages: IMessage[]; + }>; + }; + 'livechat/users/agent': { + GET: (params: PaginatedRequest<{ text?: string }>) => PaginatedResult<{ + users: { + _id: string; + emails: { + address: string; + verified: boolean; + }[]; + status: string; + name: string; + username: string; + statusLivechat: string; + livechat: { + maxNumberSimultaneousChat: number; + }; + }[]; + }>; + }; + + 'livechat/visitor': { + POST: (params: { visitor: ILivechatVisitorDTO }) => { visitor: ILivechatVisitor }; + }; + + 'livechat/visitor/:token': { + GET: (params: { token: string }) => { visitor: ILivechatVisitor }; + DELETE: (params: { token: string }) => { visitor: { _id: string; ts: string } }; + }; + + 'livechat/visitor/:token/room': { + GET: (params: { token: string }) => { rooms: IOmnichannelRoom[] }; + }; + + 'livechat/visitor.callStatus': { + POST: (params: { token: string; callStatus: string; rid: string; callId: string }) => { + token: string; + callStatus: string; + }; + }; + + 'livechat/visitor.status': { + POST: (params: { token: string; status: string }) => { token: string; status: string }; + }; + + 'livechat/queue': { + GET: (params: { + agentId?: ILivechatAgent['_id']; + includeOfflineAgents?: boolean; + departmentId?: ILivechatAgent['_id']; + offset: number; + count: number; + sort: string; + }) => { + queue: { + chats: number; + department: { _id: string; name: string }; + user: { _id: string; username: string; status: string }; + }[]; + count: number; + offset: number; + total: number; + }; + }; +}; diff --git a/app/definitions/rest/v1/permissions.ts b/app/definitions/rest/v1/permissions.ts new file mode 100644 index 000000000..58369aa07 --- /dev/null +++ b/app/definitions/rest/v1/permissions.ts @@ -0,0 +1,17 @@ +import { IPermission } from '../../IPermission'; + +type PermissionsUpdateProps = { permissions: { _id: string; roles: string[] }[] }; + +export type PermissionsEndpoints = { + 'permissions.listAll': { + GET: (params: { updatedSince?: string }) => { + update: IPermission[]; + remove: IPermission[]; + }; + }; + 'permissions.update': { + POST: (params: PermissionsUpdateProps) => { + permissions: IPermission[]; + }; + }; +}; diff --git a/app/definitions/rest/v1/roles.ts b/app/definitions/rest/v1/roles.ts new file mode 100644 index 000000000..15cb596db --- /dev/null +++ b/app/definitions/rest/v1/roles.ts @@ -0,0 +1,76 @@ +import { IRole } from '../../IRole'; +import { RocketChatRecordDeleted } from '../../IRocketChatRecord'; +import { IUser } from '../../IUser'; + +type RoleCreateProps = Pick & Partial>; + +type RoleUpdateProps = { roleId: IRole['_id']; name: IRole['name'] } & Partial; + +type RoleDeleteProps = { roleId: IRole['_id'] }; + +type RoleAddUserToRoleProps = { + username: string; + roleName: string; + roomId?: string; +}; + +type RoleRemoveUserFromRoleProps = { + username: string; + roleName: string; + roomId?: string; + scope?: string; +}; + +type RoleSyncProps = { + updatedSince?: string; +}; + +export type RolesEndpoints = { + 'roles.list': { + GET: () => { + roles: IRole[]; + }; + }; + 'roles.sync': { + GET: (params: RoleSyncProps) => { + roles: { + update: IRole[]; + remove: RocketChatRecordDeleted[]; + }; + }; + }; + 'roles.create': { + POST: (params: RoleCreateProps) => { + role: IRole; + }; + }; + + 'roles.addUserToRole': { + POST: (params: RoleAddUserToRoleProps) => { + role: IRole; + }; + }; + + 'roles.getUsersInRole': { + GET: (params: { roomId: string; role: string; offset: number; count: number }) => { + users: IUser[]; + total: number; + }; + }; + + 'roles.update': { + POST: (role: RoleUpdateProps) => { + role: IRole; + }; + }; + + 'roles.delete': { + POST: (prop: RoleDeleteProps) => void; + }; + + 'roles.removeUserFromRole': { + POST: (props: RoleRemoveUserFromRoleProps) => { + role: IRole; + }; + }; +}; diff --git a/app/definitions/rest/v1/rooms.ts b/app/definitions/rest/v1/rooms.ts new file mode 100644 index 000000000..8306cd68f --- /dev/null +++ b/app/definitions/rest/v1/rooms.ts @@ -0,0 +1,44 @@ +import type { IMessage } from '../../IMessage'; +import type { IRoom } from '../../IRoom'; +import type { IUser } from '../../IUser'; + +export type RoomsEndpoints = { + 'rooms.autocomplete.channelAndPrivate': { + GET: (params: { selector: string }) => { + items: IRoom[]; + }; + }; + 'rooms.autocomplete.channelAndPrivate.withPagination': { + GET: (params: { selector: string; offset?: number; count?: number; sort?: string }) => { + items: IRoom[]; + count: number; + offset: number; + total: number; + }; + }; + 'rooms.autocomplete.availableForTeams': { + GET: (params: { name: string }) => { + items: IRoom[]; + }; + }; + 'rooms.info': { + GET: (params: { roomId: string } | { roomName: string }) => { + room: IRoom; + }; + }; + 'rooms.createDiscussion': { + POST: (params: { + prid: IRoom['_id']; + pmid?: IMessage['_id']; + t_name: IRoom['fname']; + users?: IUser['username'][]; + encrypted?: boolean; + reply?: string; + }) => { + discussion: IRoom; + }; + }; + 'rooms.favorite': { + POST: (params: { roomId: string; favorite: boolean }) => {}; + }; +}; diff --git a/app/definitions/rest/v1/settings.ts b/app/definitions/rest/v1/settings.ts new file mode 100644 index 000000000..1e2256e23 --- /dev/null +++ b/app/definitions/rest/v1/settings.ts @@ -0,0 +1,103 @@ +import { ISetting, ISettingColor } from '../../ISetting'; +import { PaginatedResult } from '../helpers/PaginatedResult'; + +type SettingsUpdateProps = SettingsUpdatePropDefault | SettingsUpdatePropsActions | SettingsUpdatePropsColor; + +type SettingsUpdatePropsActions = { + execute: boolean; +}; + +export type OauthCustomConfiguration = { + _id: string; + clientId?: string; + custom: unknown; + service?: string; + serverURL: unknown; + tokenPath: unknown; + identityPath: unknown; + authorizePath: unknown; + scope: unknown; + loginStyle: 'popup' | 'redirect'; + tokenSentVia: unknown; + identityTokenSentVia: unknown; + keyField: unknown; + usernameField: unknown; + emailField: unknown; + nameField: unknown; + avatarField: unknown; + rolesClaim: unknown; + groupsClaim: unknown; + mapChannels: unknown; + channelsMap: unknown; + channelsAdmin: unknown; + mergeUsers: unknown; + mergeRoles: unknown; + accessTokenParam: unknown; + showButton: unknown; + + appId: unknown; + consumerKey?: string; + + clientConfig: unknown; + buttonLabelText: unknown; + buttonLabelColor: unknown; + buttonColor: unknown; +}; + +export const isOauthCustomConfiguration = (config: any): config is OauthCustomConfiguration => Boolean(config); + +export const isSettingsUpdatePropsActions = (props: Partial): props is SettingsUpdatePropsActions => + 'execute' in props; + +type SettingsUpdatePropsColor = { + editor: ISettingColor['editor']; + value: ISetting['value']; +}; + +export const isSettingsUpdatePropsColor = (props: Partial): props is SettingsUpdatePropsColor => + 'editor' in props && 'value' in props; + +type SettingsUpdatePropDefault = { + value: ISetting['value']; +}; + +export const isSettingsUpdatePropDefault = (props: Partial): props is SettingsUpdatePropDefault => + 'value' in props; + +export type SettingsEndpoints = { + 'settings.public': { + GET: () => PaginatedResult & { + settings: Array; + }; + }; + + 'settings.oauth': { + GET: () => { + services: Partial[]; + }; + }; + + 'settings.addCustomOAuth': { + POST: (params: { name: string }) => void; + }; + + settings: { + GET: () => { + settings: ISetting[]; + }; + }; + + 'settings/:_id': { + GET: () => Pick; + POST: (params: SettingsUpdateProps) => void; + }; + + 'service.configurations': { + GET: () => { + configurations: Array<{ + appId: string; + secret: string; + }>; + }; + }; +}; diff --git a/app/definitions/rest/v1/teams.ts b/app/definitions/rest/v1/teams.ts new file mode 100644 index 000000000..29047149d --- /dev/null +++ b/app/definitions/rest/v1/teams.ts @@ -0,0 +1,7 @@ +import { IRoom } from '../../IRoom'; + +export type TeamsEndpoints = { + 'teams.removeRoom': { + POST: (params: { roomId: string; teamId: string }) => { room: IRoom }; + }; +}; diff --git a/app/definitions/rest/v1/user.ts b/app/definitions/rest/v1/user.ts new file mode 100644 index 000000000..7de12aabf --- /dev/null +++ b/app/definitions/rest/v1/user.ts @@ -0,0 +1,14 @@ +import { IUser } from '../../IUser'; + +export type UserEndpoints = { + 'users.info': { + GET: (params: { userId: IUser['_id'] }) => { + user: IUser; + success: boolean; + }; + POST: (params: { userId: IUser['_id'] }) => { + user: IUser; + success: boolean; + }; + }; +}; diff --git a/app/definitions/rest/v1/users.ts b/app/definitions/rest/v1/users.ts new file mode 100644 index 000000000..337a2182f --- /dev/null +++ b/app/definitions/rest/v1/users.ts @@ -0,0 +1,14 @@ +import type { ITeam } from '../../ITeam'; +import type { IUser } from '../../IUser'; + +export type UsersEndpoints = { + 'users.2fa.sendEmailCode': { + POST: (params: { emailOrUsername: string }) => void; + }; + 'users.autocomplete': { + GET: (params: { selector: string }) => { items: IUser[] }; + }; + 'users.listTeams': { + GET: (params: { userId: IUser['_id'] }) => { teams: Array }; + }; +}; diff --git a/app/lib/rocketchat/rocketchat.js b/app/lib/rocketchat/rocketchat.js index a173f422d..2d3fd5654 100644 --- a/app/lib/rocketchat/rocketchat.js +++ b/app/lib/rocketchat/rocketchat.js @@ -777,7 +777,7 @@ const RocketChat = { createDirectMessage(username) { // RC 0.59.0 - return this.post('im.create', { username }); + return sdk.post('im.create', { username }); }, createGroupChat() { @@ -831,7 +831,7 @@ const RocketChat = { }, removeTeamRoom({ roomId, teamId }) { // RC 3.13.0 - return this.post('teams.removeRoom', { roomId, teamId }); + return sdk.post('teams.removeRoom', { roomId, teamId }); }, leaveTeam({ teamId, rooms }) { // RC 3.13.0 diff --git a/app/lib/rocketchat/services/sdk.ts b/app/lib/rocketchat/services/sdk.ts index b08307c6e..869657a29 100644 --- a/app/lib/rocketchat/services/sdk.ts +++ b/app/lib/rocketchat/services/sdk.ts @@ -1,10 +1,18 @@ import { Rocketchat } from '@rocket.chat/sdk'; -import isEmpty from 'lodash/isEmpty'; import EJSON from 'ejson'; +import isEmpty from 'lodash/isEmpty'; -import reduxStore from '../../createStore'; -import { useSsl } from '../../../utils/url'; import { twoFactor } from '../../../utils/twoFactor'; +import { useSsl } from '../../../utils/url'; +import reduxStore from '../../createStore'; +import { + Serialized, + OperationResult, + MatchPathPattern, + OperationParams, + PathFor, + ResultFor +} from '../../../definitions/rest/helpers'; class Sdk { private sdk: typeof Rocketchat; @@ -31,15 +39,35 @@ class Sdk { return null; } - get(...args: any[]): Promise { - return this.sdk.get(...args); + get>( + endpoint: TPath, + params: void extends OperationParams<'GET', MatchPathPattern> + ? void + : Serialized>> = undefined as void extends OperationParams< + 'GET', + MatchPathPattern + > + ? void + : Serialized>> + ): Promise>>> { + return this.sdk.get(endpoint, params); } - post(...args: any[]): Promise { + post>( + endpoint: TPath, + params: void extends OperationParams<'POST', MatchPathPattern> + ? void + : Serialized>> = undefined as void extends OperationParams< + 'POST', + MatchPathPattern + > + ? void + : Serialized>> + ): Promise>> { return new Promise(async (resolve, reject) => { - const isMethodCall = args[0]?.startsWith('method.call/'); + const isMethodCall = endpoint?.startsWith('method.call/'); try { - const result = await this.sdk.post(...args); + const result = await this.sdk.post(endpoint, params); /** * if API_Use_REST_For_DDP_Calls is enabled and it's a method call, @@ -61,10 +89,10 @@ class Sdk { const { details } = isMethodCall ? e : e?.data; try { await twoFactor({ method: details?.method, invalid: errorType === totpInvalid }); - return resolve(this.post(...args)); + return resolve(this.post(endpoint, params)); } catch { // twoFactor was canceled - return resolve({}); + return resolve({} as any); } } else { reject(e); @@ -100,6 +128,7 @@ class Sdk { const { user } = reduxStore.getState().login; if (API_Use_REST_For_DDP_Calls) { const url = isEmpty(user) ? 'method.callAnon' : 'method.call'; + // @ts-ignore return this.post(`${url}/${method}`, { message: EJSON.stringify({ method, params }) }); diff --git a/app/views/AddExistingChannelView.tsx b/app/views/AddExistingChannelView.tsx index 2a36734eb..d307f3679 100644 --- a/app/views/AddExistingChannelView.tsx +++ b/app/views/AddExistingChannelView.tsx @@ -136,6 +136,8 @@ class AddExistingChannelView extends React.Component