Refactor rest client class to use mixin and a bit of cleanup (#5318)

This commit is contained in:
Elias Nahum 2021-04-15 10:04:36 -04:00 committed by GitHub
parent 154b2e6adb
commit 13eab98143
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
161 changed files with 2261 additions and 7960 deletions

View file

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {ActionFunc} from '@mm-redux/types/actions';
import {AppCallResponse, AppForm, AppCallRequest, AppCallType} from '@mm-redux/types/apps';
import {AppCallTypes, AppCallResponseTypes} from '@mm-redux/constants/apps';

View file

@ -3,7 +3,7 @@
/* eslint-disable no-import-assign */
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {Preferences} from '@mm-redux/constants';
import {PreferenceTypes} from '@mm-redux/action_types';

View file

@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import {ChannelTypes, PreferenceTypes, RoleTypes, UserTypes} from '@mm-redux/action_types';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {General, Preferences} from '@mm-redux/constants';
import {getCurrentChannelId, getRedirectChannelNameForTeam, getChannelsNameMapInTeam} from '@mm-redux/selectors/entities/channels';
import {getConfig} from '@mm-redux/selectors/entities/general';

View file

@ -15,7 +15,7 @@ import {
import {savePreferences} from '@mm-redux/actions/preferences';
import {getLicense} from '@mm-redux/selectors/entities/general';
import {addUserToTeam, getTeamByName, removeUserFromTeam, selectTeam} from '@mm-redux/actions/teams';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {General, Preferences} from '@mm-redux/constants';
import {getPostIdsInChannel} from '@mm-redux/selectors/entities/posts';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';

View file

@ -5,7 +5,7 @@ import {batchActions} from 'redux-batched-actions';
import {EmojiTypes} from '@mm-redux/action_types';
import {addReaction as serviceAddReaction, getNeededCustomEmojis} from '@mm-redux/actions/posts';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {getPostIdsInCurrentChannel, makeGetPostIdsForThread} from '@mm-redux/selectors/entities/posts';
import {ViewTypes} from 'app/constants';

View file

@ -7,7 +7,7 @@ import {getDataRetentionPolicy} from '@mm-redux/actions/general';
import {GeneralTypes} from '@mm-redux/action_types';
import {getSessions} from '@mm-redux/actions/users';
import {autoUpdateTimezone} from '@mm-redux/actions/timezone';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {getConfig, getLicense} from '@mm-redux/selectors/entities/general';
import {isTimezoneEnabled} from '@mm-redux/selectors/entities/timezone';
import {getCurrentUserId} from '@mm-redux/selectors/entities/users';

View file

@ -4,7 +4,7 @@
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {handleSuccessfulLogin} from 'app/actions/views/login';

View file

@ -15,7 +15,7 @@ import {
receivedPostsSince,
receivedPostsInThread,
} from '@mm-redux/actions/posts';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {Posts} from '@mm-redux/constants';
import {getPost as selectPost, getPostIdsInChannel} from '@mm-redux/selectors/entities/posts';
import {getCurrentChannelId} from '@mm-redux/selectors/entities/channels';

View file

@ -6,7 +6,7 @@
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {PostTypes, UserTypes} from '@mm-redux/action_types';
import * as PostSelectors from '@mm-redux/selectors/entities/posts';

View file

@ -10,7 +10,7 @@ import {fetchMyChannelsAndMembers, getChannelAndMyMember} from '@mm-redux/action
import {getDataRetentionPolicy} from '@mm-redux/actions/general';
import {receivedNewPost} from '@mm-redux/actions/posts';
import {getMyTeams, getMyTeamMembers} from '@mm-redux/actions/teams';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {General} from '@mm-redux/constants';
import EventEmitter from '@mm-redux/utils/event_emitter';
import initialState from '@store/initial_state';

View file

@ -4,7 +4,7 @@
import moment from 'moment-timezone';
import {getSessions} from '@mm-redux/actions/users';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {getConfig} from '@mm-redux/selectors/entities/general';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';

View file

@ -8,7 +8,7 @@ import {GeneralTypes, RoleTypes, UserTypes} from '@mm-redux/action_types';
import {getDataRetentionPolicy} from '@mm-redux/actions/general';
import * as HelperActions from '@mm-redux/actions/helpers';
import {autoUpdateTimezone} from '@mm-redux/actions/timezone';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {General} from '@mm-redux/constants';
import EventEmitter from '@mm-redux/utils/event_emitter';
import {getConfig, getLicense} from '@mm-redux/selectors/entities/general';

View file

@ -12,7 +12,7 @@ import configureMockStore from 'redux-mock-store';
import {ChannelTypes, RoleTypes} from '@mm-redux/action_types';
import * as ChannelActions from '@mm-redux/actions/channels';
import * as TeamActions from '@mm-redux/actions/teams';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {General} from '@mm-redux/constants';
import * as Actions from '@actions/websocket';

View file

@ -5,7 +5,7 @@ import {fetchChannelAndMyMember} from '@actions/helpers/channels';
import {loadChannelsForTeam} from '@actions/views/channel';
import {WebsocketEvents} from '@constants';
import {markChannelAsRead} from '@mm-redux/actions/channels';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {ChannelTypes, TeamTypes, RoleTypes} from '@mm-redux/action_types';
import {General} from '@mm-redux/constants';
import {

View file

@ -5,7 +5,7 @@
import assert from 'assert';
import {Server, WebSocket as MockWebSocket} from 'mock-socket';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import * as Actions from '@actions/websocket';
import {WebsocketEvents} from '@constants';

View file

@ -7,7 +7,7 @@ import {loadMe} from '@actions/views/user';
import {WebsocketEvents} from '@constants';
import {ChannelTypes, GeneralTypes, PreferenceTypes, TeamTypes, UserTypes, RoleTypes} from '@mm-redux/action_types';
import {getProfilesByIds, getStatusesByIds} from '@mm-redux/actions/users';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {General} from '@mm-redux/constants';
import {getCurrentChannelId, getCurrentChannelStats} from '@mm-redux/selectors/entities/channels';
import {getConfig} from '@mm-redux/selectors/entities/general';

View file

@ -3,7 +3,7 @@
import assert from 'assert';
import {Server, WebSocket as MockWebSocket} from 'mock-socket';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import * as Actions from '@actions/websocket';
import {WebsocketEvents} from '@constants';

View file

@ -9,7 +9,7 @@ import {Server, WebSocket as MockWebSocket} from 'mock-socket';
import * as ChannelActions from '@mm-redux/actions/channels';
import * as PostActions from '@mm-redux/actions/posts';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {General, Posts} from '@mm-redux/constants';
import * as PostSelectors from '@mm-redux/selectors/entities/posts';
import EventEmitter from '@mm-redux/utils/event_emitter';

View file

@ -4,7 +4,7 @@
import assert from 'assert';
import {Server, WebSocket as MockWebSocket} from 'mock-socket';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import * as Actions from '@actions/websocket';
import {WebsocketEvents} from '@constants';

View file

@ -7,7 +7,7 @@ import {Server, WebSocket as MockWebSocket} from 'mock-socket';
import {batchActions} from 'redux-batched-actions';
import {TeamTypes, UserTypes} from '@mm-redux/action_types';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import * as Actions from '@actions/websocket';
import {WebsocketEvents} from '@constants';

View file

@ -3,7 +3,7 @@
import {RoleTypes, TeamTypes} from '@mm-redux/action_types';
import {notVisibleUsersActions} from '@mm-redux/actions/helpers';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {getCurrentTeamId, getTeams as getTeamsSelector} from '@mm-redux/selectors/entities/teams';
import {getCurrentUser} from '@mm-redux/selectors/entities/users';
import {ActionResult, DispatchFunc, GenericAction, GetStateFunc, batchActions} from '@mm-redux/types/actions';

View file

@ -5,7 +5,7 @@ import assert from 'assert';
import {Server, WebSocket as MockWebSocket} from 'mock-socket';
import {batchActions} from 'redux-batched-actions';
import {TeamTypes, UserTypes} from '@mm-redux/action_types';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import * as Actions from '@actions/websocket';
import {WebsocketEvents} from '@constants';

View file

@ -6,7 +6,7 @@ import {loadChannelsForTeam} from '@actions/views/channel';
import {getMe} from '@actions/views/user';
import {ChannelTypes, TeamTypes, UserTypes, RoleTypes} from '@mm-redux/action_types';
import {notVisibleUsersActions} from '@mm-redux/actions/helpers';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {General} from '@mm-redux/constants';
import {getAllChannels, getCurrentChannelId, getChannelMembersInChannels} from '@mm-redux/selectors/entities/channels';
import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams';
@ -171,7 +171,7 @@ export function handleUserRoleUpdated(msg: WebSocketMessage) {
dispatch({
type: RoleTypes.RECEIVED_ROLES,
data: data.roles,
data,
});
} catch {
// do nothing

View file

@ -11,7 +11,7 @@ import configureMockStore from 'redux-mock-store';
import {GeneralTypes, UserTypes} from '@mm-redux/action_types';
import {notVisibleUsersActions} from '@mm-redux/actions/helpers';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {General, Posts, RequestStatus} from '@mm-redux/constants';
import * as Actions from '@actions/websocket';

36
app/client/rest/apps.ts Normal file
View file

@ -0,0 +1,36 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {AppBinding, AppCallRequest, AppCallResponse, AppCallType} from '@mm-redux/types/apps';
export interface ClientAppsMix {
executeAppCall: (call: AppCallRequest, type: AppCallType) => Promise<AppCallResponse>;
getAppsBindings: (userID: string, channelID: string) => Promise<AppBinding[]>;
}
const ClientApps = (superclass: any) => class extends superclass {
executeAppCall = async (call: AppCallRequest, type: AppCallType) => {
const callCopy = {
...call,
path: `${call.path}/${type}`,
context: {
...call.context,
user_agent: 'mobile',
},
};
return this.doFetch(
`${this.getAppsProxyRoute()}/api/v1/call`,
{method: 'post', body: JSON.stringify(callCopy)},
);
}
getAppsBindings = async (userID: string, channelID: string) => {
return this.doFetch(
this.getAppsProxyRoute() + `/api/v1/bindings?user_id=${userID}&channel_id=${channelID}&user_agent_type=mobile`,
{method: 'get'},
);
}
};
export default ClientApps;

373
app/client/rest/base.ts Normal file
View file

@ -0,0 +1,373 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {RNFetchBlobFetchRepsonse} from 'rn-fetch-blob';
import urlParse from 'url-parse';
import {Options} from '@mm-redux/types/client4';
import * as ClientConstants from './constants';
import ClientError from './error';
export default class ClientBase {
clusterId = '';
csrf = '';
defaultHeaders: {[x: string]: string} = {};
diagnosticId = '';
enableLogging = false;
includeCookies = true;
logToConsole = false;
managedConfig: any = null;
serverVersion = '';
token = '';
translations = {
connectionError: 'There appears to be a problem with your internet connection.',
unknownError: 'We received an unexpected status code from the server.',
};
userAgent: string|null = null;
url = '';
urlVersion = '/api/v4';
getAbsoluteUrl(baseUrl: string) {
if (typeof baseUrl !== 'string' || !baseUrl.startsWith('/')) {
return baseUrl;
}
return this.getUrl() + baseUrl;
}
getOptions(options: Options) {
const newOptions: Options = {...options};
const headers: {[x: string]: string} = {
[ClientConstants.HEADER_REQUESTED_WITH]: 'XMLHttpRequest',
...this.defaultHeaders,
};
if (this.token) {
headers[ClientConstants.HEADER_AUTH] = `${ClientConstants.HEADER_BEARER} ${this.token}`;
}
const csrfToken = this.csrf || '';
if (options.method && options.method.toLowerCase() !== 'get' && csrfToken) {
headers[ClientConstants.HEADER_X_CSRF_TOKEN] = csrfToken;
}
if (this.includeCookies) {
newOptions.credentials = 'include';
}
if (this.userAgent) {
headers[ClientConstants.HEADER_USER_AGENT] = this.userAgent;
}
if (newOptions.headers) {
Object.assign(headers, newOptions.headers);
}
return {
...newOptions,
headers,
};
}
getServerVersion() {
return this.serverVersion;
}
getToken() {
return this.token;
}
getUrl() {
return this.url;
}
getUrlVersion() {
return this.urlVersion;
}
getWebSocketUrl = () => {
return `${this.getBaseRoute()}/websocket`;
}
setAcceptLanguage(locale: string) {
this.defaultHeaders['Accept-Language'] = locale;
}
setCSRF(csrfToken: string) {
this.csrf = csrfToken;
}
setDiagnosticId(diagnosticId: string) {
this.diagnosticId = diagnosticId;
}
setEnableLogging(enable: boolean) {
this.enableLogging = enable;
}
setIncludeCookies(include: boolean) {
this.includeCookies = include;
}
setManagedConfig(config: any) {
this.managedConfig = config;
}
setUserAgent(userAgent: string) {
this.userAgent = userAgent;
}
setToken(token: string) {
this.token = token;
}
setUrl(url: string) {
this.url = url.replace(/\/+$/, '');
}
// Routes
getBaseRoute() {
return `${this.url}${this.urlVersion}`;
}
getUsersRoute() {
return `${this.getBaseRoute()}/users`;
}
getUserRoute(userId: string) {
return `${this.getUsersRoute()}/${userId}`;
}
getTeamsRoute() {
return `${this.getBaseRoute()}/teams`;
}
getTeamRoute(teamId: string) {
return `${this.getTeamsRoute()}/${teamId}`;
}
getTeamNameRoute(teamName: string) {
return `${this.getTeamsRoute()}/name/${teamName}`;
}
getTeamMembersRoute(teamId: string) {
return `${this.getTeamRoute(teamId)}/members`;
}
getTeamMemberRoute(teamId: string, userId: string) {
return `${this.getTeamMembersRoute(teamId)}/${userId}`;
}
getChannelsRoute() {
return `${this.getBaseRoute()}/channels`;
}
getChannelRoute(channelId: string) {
return `${this.getChannelsRoute()}/${channelId}`;
}
getChannelMembersRoute(channelId: string) {
return `${this.getChannelRoute(channelId)}/members`;
}
getChannelMemberRoute(channelId: string, userId: string) {
return `${this.getChannelMembersRoute(channelId)}/${userId}`;
}
getPostsRoute() {
return `${this.getBaseRoute()}/posts`;
}
getPostRoute(postId: string) {
return `${this.getPostsRoute()}/${postId}`;
}
getReactionsRoute() {
return `${this.getBaseRoute()}/reactions`;
}
getCommandsRoute() {
return `${this.getBaseRoute()}/commands`;
}
getFilesRoute() {
return `${this.getBaseRoute()}/files`;
}
getFileRoute(fileId: string) {
return `${this.getFilesRoute()}/${fileId}`;
}
getPreferencesRoute(userId: string) {
return `${this.getUserRoute(userId)}/preferences`;
}
getIncomingHooksRoute() {
return `${this.getBaseRoute()}/hooks/incoming`;
}
getIncomingHookRoute(hookId: string) {
return `${this.getBaseRoute()}/hooks/incoming/${hookId}`;
}
getOutgoingHooksRoute() {
return `${this.getBaseRoute()}/hooks/outgoing`;
}
getOutgoingHookRoute(hookId: string) {
return `${this.getBaseRoute()}/hooks/outgoing/${hookId}`;
}
getOAuthRoute() {
return `${this.url}/oauth`;
}
getOAuthAppsRoute() {
return `${this.getBaseRoute()}/oauth/apps`;
}
getOAuthAppRoute(appId: string) {
return `${this.getOAuthAppsRoute()}/${appId}`;
}
getEmojisRoute() {
return `${this.getBaseRoute()}/emoji`;
}
getEmojiRoute(emojiId: string) {
return `${this.getEmojisRoute()}/${emojiId}`;
}
getBrandRoute() {
return `${this.getBaseRoute()}/brand`;
}
getBrandImageUrl(timestamp: string) {
return `${this.getBrandRoute()}/image?t=${timestamp}`;
}
getDataRetentionRoute() {
return `${this.getBaseRoute()}/data_retention`;
}
getRolesRoute() {
return `${this.getBaseRoute()}/roles`;
}
getTimezonesRoute() {
return `${this.getBaseRoute()}/system/timezones`;
}
getRedirectLocationRoute() {
return `${this.getBaseRoute()}/redirect_location`;
}
getBotsRoute() {
return `${this.getBaseRoute()}/bots`;
}
getBotRoute(botUserId: string) {
return `${this.getBotsRoute()}/${botUserId}`;
}
getAppsProxyRoute() {
return `${this.url}/plugins/com.mattermost.apps`;
}
// Client Helpers
handleRedirectProtocol = (url: string, response: RNFetchBlobFetchRepsonse) => {
const serverUrl = this.getUrl();
const parsed = urlParse(url);
const {redirects} = response.rnfbRespInfo;
if (redirects) {
const redirectUrl = urlParse(redirects[redirects.length - 1]);
if (serverUrl === parsed.origin && parsed.host === redirectUrl.host && parsed.protocol !== redirectUrl.protocol) {
this.setUrl(serverUrl.replace(parsed.protocol, redirectUrl.protocol));
}
}
};
doFetch = async (url: string, options: Options) => {
const {data} = await this.doFetchWithResponse(url, options);
return data;
};
doFetchWithResponse = async (url: string, options: Options) => {
const response = await fetch(url, this.getOptions(options));
const headers = parseAndMergeNestedHeaders(response.headers);
let data;
try {
data = await response.json();
} catch (err) {
throw new ClientError(this.getUrl(), {
message: 'Received invalid response from the server.',
intl: {
id: 'mobile.request.invalid_response',
defaultMessage: 'Received invalid response from the server.',
},
url,
});
}
if (headers.has(ClientConstants.HEADER_X_VERSION_ID) && !headers.get('Cache-Control')) {
const serverVersion = headers.get(ClientConstants.HEADER_X_VERSION_ID);
if (serverVersion && this.serverVersion !== serverVersion) {
this.serverVersion = serverVersion;
}
}
if (headers.has(ClientConstants.HEADER_X_CLUSTER_ID)) {
const clusterId = headers.get(ClientConstants.HEADER_X_CLUSTER_ID);
if (clusterId && this.clusterId !== clusterId) {
this.clusterId = clusterId;
}
}
if (response.ok) {
return {
response,
headers,
data,
};
}
const msg = data.message || '';
if (this.logToConsole) {
console.error(msg); // eslint-disable-line no-console
}
throw new ClientError(this.getUrl(), {
message: msg,
server_error_id: data.id,
status_code: data.status_code,
url,
});
};
}
function parseAndMergeNestedHeaders(originalHeaders: any) {
const headers = new Map();
let nestedHeaders = new Map();
originalHeaders.forEach((val: string, key: string) => {
const capitalizedKey = key.replace(/\b[a-z]/g, (l) => l.toUpperCase());
let realVal = val;
if (val && val.match(/\n\S+:\s\S+/)) {
const nestedHeaderStrings = val.split('\n');
realVal = nestedHeaderStrings.shift() as string;
const moreNestedHeaders = new Map(
nestedHeaderStrings.map((h: any) => h.split(/:\s/)),
);
nestedHeaders = new Map([...nestedHeaders, ...moreNestedHeaders]);
}
headers.set(capitalizedKey, realVal);
});
return new Map([...headers, ...nestedHeaders]);
}

39
app/client/rest/bots.ts Normal file
View file

@ -0,0 +1,39 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {buildQueryString} from '@mm-redux/utils/helpers';
import type {Bot} from '@mm-redux/types/bots';
export interface ClientBotsMix {
getBot: (botUserId: string) => Promise<Bot>;
getBots: (page?: number, perPage?: number) => Promise<Bot[]>;
getBotsIncludeDeleted: (page?: number, perPage?: number) => Promise<Bot[]>;
}
const PER_PAGE_DEFAULT = 60;
const ClientBots = (superclass: any) => class extends superclass {
getBot = async (botUserId: string) => {
return this.doFetch(
`${this.getBotRoute(botUserId)}`,
{method: 'get'},
);
}
getBots = async (page = 0, perPage = PER_PAGE_DEFAULT) => {
return this.doFetch(
`${this.getBotsRoute()}${buildQueryString({page, per_page: perPage})}`,
{method: 'get'},
);
}
getBotsIncludeDeleted = async (page = 0, perPage = PER_PAGE_DEFAULT) => {
return this.doFetch(
`${this.getBotsRoute()}${buildQueryString({include_deleted: true, page, per_page: perPage})}`,
{method: 'get'},
);
}
};
export default ClientBots;

308
app/client/rest/channels.ts Normal file
View file

@ -0,0 +1,308 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {analytics} from '@init/analytics';
import {Channel, ChannelMemberCountByGroup, ChannelMembership, ChannelNotifyProps, ChannelStats} from '@mm-redux/types/channels';
import {buildQueryString} from '@mm-redux/utils/helpers';
import {PER_PAGE_DEFAULT} from './constants';
export interface ClientChannelsMix {
getAllChannels: (page?: number, perPage?: number, notAssociatedToGroup?: string, excludeDefaultChannels?: boolean, includeTotalCount?: boolean) => Promise<any>;
createChannel: (channel: Channel) => Promise<Channel>;
createDirectChannel: (userIds: string[]) => Promise<Channel>;
createGroupChannel: (userIds: string[]) => Promise<Channel>;
deleteChannel: (channelId: string) => Promise<any>;
unarchiveChannel: (channelId: string) => Promise<Channel>;
updateChannel: (channel: Channel) => Promise<Channel>;
convertChannelToPrivate: (channelId: string) => Promise<Channel>;
updateChannelPrivacy: (channelId: string, privacy: any) => Promise<Channel>;
patchChannel: (channelId: string, channelPatch: Partial<Channel>) => Promise<Channel>;
updateChannelNotifyProps: (props: ChannelNotifyProps & {channel_id: string, user_id: string}) => Promise<any>;
getChannel: (channelId: string) => Promise<Channel>;
getChannelByName: (teamId: string, channelName: string, includeDeleted?: boolean) => Promise<Channel>;
getChannelByNameAndTeamName: (teamName: string, channelName: string, includeDeleted?: boolean) => Promise<Channel>;
getChannels: (teamId: string, page?: number, perPage?: number) => Promise<Channel[]>;
getArchivedChannels: (teamId: string, page?: number, perPage?: number) => Promise<Channel[]>;
getMyChannels: (teamId: string, includeDeleted?: boolean) => Promise<Channel[]>;
getMyChannelMember: (channelId: string) => Promise<ChannelMembership>;
getMyChannelMembers: (teamId: string) => Promise<ChannelMembership[]>;
getChannelMembers: (channelId: string, page?: number, perPage?: number) => Promise<ChannelMembership[]>;
getChannelTimezones: (channelId: string) => Promise<string[]>;
getChannelMember: (channelId: string, userId: string) => Promise<ChannelMembership>;
getChannelMembersByIds: (channelId: string, userIds: string[]) => Promise<ChannelMembership[]>;
addToChannel: (userId: string, channelId: string, postRootId?: string) => Promise<ChannelMembership>;
removeFromChannel: (userId: string, channelId: string) => Promise<any>;
getChannelStats: (channelId: string) => Promise<ChannelStats>;
getChannelMemberCountsByGroup: (channelId: string, includeTimezones: boolean) => Promise<ChannelMemberCountByGroup[]>;
viewMyChannel: (channelId: string, prevChannelId?: string) => Promise<any>;
autocompleteChannels: (teamId: string, name: string) => Promise<Channel[]>;
autocompleteChannelsForSearch: (teamId: string, name: string) => Promise<Channel[]>;
searchChannels: (teamId: string, term: string) => Promise<Channel[]>;
searchArchivedChannels: (teamId: string, term: string) => Promise<Channel[]>;
}
const ClientChannels = (superclass: any) => class extends superclass {
getAllChannels = async (page = 0, perPage = PER_PAGE_DEFAULT, notAssociatedToGroup = '', excludeDefaultChannels = false, includeTotalCount = false) => {
const queryData = {
page,
per_page: perPage,
not_associated_to_group: notAssociatedToGroup,
exclude_default_channels: excludeDefaultChannels,
include_total_count: includeTotalCount,
};
return this.doFetch(
`${this.getChannelsRoute()}${buildQueryString(queryData)}`,
{method: 'get'},
);
};
createChannel = async (channel: Channel) => {
analytics.trackAPI('api_channels_create', {team_id: channel.team_id});
return this.doFetch(
`${this.getChannelsRoute()}`,
{method: 'post', body: JSON.stringify(channel)},
);
};
createDirectChannel = async (userIds: string[]) => {
analytics.trackAPI('api_channels_create_direct');
return this.doFetch(
`${this.getChannelsRoute()}/direct`,
{method: 'post', body: JSON.stringify(userIds)},
);
};
createGroupChannel = async (userIds: string[]) => {
analytics.trackAPI('api_channels_create_group');
return this.doFetch(
`${this.getChannelsRoute()}/group`,
{method: 'post', body: JSON.stringify(userIds)},
);
};
deleteChannel = async (channelId: string) => {
analytics.trackAPI('api_channels_delete', {channel_id: channelId});
return this.doFetch(
`${this.getChannelRoute(channelId)}`,
{method: 'delete'},
);
};
unarchiveChannel = async (channelId: string) => {
analytics.trackAPI('api_channels_unarchive', {channel_id: channelId});
return this.doFetch(
`${this.getChannelRoute(channelId)}/restore`,
{method: 'post'},
);
};
updateChannel = async (channel: Channel) => {
analytics.trackAPI('api_channels_update', {channel_id: channel.id});
return this.doFetch(
`${this.getChannelRoute(channel.id)}`,
{method: 'put', body: JSON.stringify(channel)},
);
};
convertChannelToPrivate = async (channelId: string) => {
analytics.trackAPI('api_channels_convert_to_private', {channel_id: channelId});
return this.doFetch(
`${this.getChannelRoute(channelId)}/convert`,
{method: 'post'},
);
};
updateChannelPrivacy = async (channelId: string, privacy: any) => {
analytics.trackAPI('api_channels_update_privacy', {channel_id: channelId, privacy});
return this.doFetch(
`${this.getChannelRoute(channelId)}/privacy`,
{method: 'put', body: JSON.stringify({privacy})},
);
};
patchChannel = async (channelId: string, channelPatch: Partial<Channel>) => {
analytics.trackAPI('api_channels_patch', {channel_id: channelId});
return this.doFetch(
`${this.getChannelRoute(channelId)}/patch`,
{method: 'put', body: JSON.stringify(channelPatch)},
);
};
updateChannelNotifyProps = async (props: ChannelNotifyProps & {channel_id: string, user_id: string}) => {
analytics.trackAPI('api_users_update_channel_notifications', {channel_id: props.channel_id});
return this.doFetch(
`${this.getChannelMemberRoute(props.channel_id, props.user_id)}/notify_props`,
{method: 'put', body: JSON.stringify(props)},
);
};
getChannel = async (channelId: string) => {
analytics.trackAPI('api_channel_get', {channel_id: channelId});
return this.doFetch(
`${this.getChannelRoute(channelId)}`,
{method: 'get'},
);
};
getChannelByName = async (teamId: string, channelName: string, includeDeleted = false) => {
return this.doFetch(
`${this.getTeamRoute(teamId)}/channels/name/${channelName}?include_deleted=${includeDeleted}`,
{method: 'get'},
);
};
getChannelByNameAndTeamName = async (teamName: string, channelName: string, includeDeleted = false) => {
analytics.trackAPI('api_channel_get_by_name_and_teamName', {channel_name: channelName, team_name: teamName, include_deleted: includeDeleted});
return this.doFetch(
`${this.getTeamNameRoute(teamName)}/channels/name/${channelName}?include_deleted=${includeDeleted}`,
{method: 'get'},
);
};
getChannels = async (teamId: string, page = 0, perPage = PER_PAGE_DEFAULT) => {
return this.doFetch(
`${this.getTeamRoute(teamId)}/channels${buildQueryString({page, per_page: perPage})}`,
{method: 'get'},
);
};
getArchivedChannels = async (teamId: string, page = 0, perPage = PER_PAGE_DEFAULT) => {
return this.doFetch(
`${this.getTeamRoute(teamId)}/channels/deleted${buildQueryString({page, per_page: perPage})}`,
{method: 'get'},
);
};
getMyChannels = async (teamId: string, includeDeleted = false) => {
return this.doFetch(
`${this.getUserRoute('me')}/teams/${teamId}/channels${buildQueryString({include_deleted: includeDeleted})}`,
{method: 'get'},
);
};
getMyChannelMember = async (channelId: string) => {
return this.doFetch(
`${this.getChannelMemberRoute(channelId, 'me')}`,
{method: 'get'},
);
};
getMyChannelMembers = async (teamId: string) => {
return this.doFetch(
`${this.getUserRoute('me')}/teams/${teamId}/channels/members`,
{method: 'get'},
);
};
getChannelMembers = async (channelId: string, page = 0, perPage = PER_PAGE_DEFAULT) => {
return this.doFetch(
`${this.getChannelMembersRoute(channelId)}${buildQueryString({page, per_page: perPage})}`,
{method: 'get'},
);
};
getChannelTimezones = async (channelId: string) => {
return this.doFetch(
`${this.getChannelRoute(channelId)}/timezones`,
{method: 'get'},
);
};
getChannelMember = async (channelId: string, userId: string) => {
return this.doFetch(
`${this.getChannelMemberRoute(channelId, userId)}`,
{method: 'get'},
);
};
getChannelMembersByIds = async (channelId: string, userIds: string[]) => {
return this.doFetch(
`${this.getChannelMembersRoute(channelId)}/ids`,
{method: 'post', body: JSON.stringify(userIds)},
);
};
addToChannel = async (userId: string, channelId: string, postRootId = '') => {
analytics.trackAPI('api_channels_add_member', {channel_id: channelId});
const member = {user_id: userId, channel_id: channelId, post_root_id: postRootId};
return this.doFetch(
`${this.getChannelMembersRoute(channelId)}`,
{method: 'post', body: JSON.stringify(member)},
);
};
removeFromChannel = async (userId: string, channelId: string) => {
analytics.trackAPI('api_channels_remove_member', {channel_id: channelId});
return this.doFetch(
`${this.getChannelMemberRoute(channelId, userId)}`,
{method: 'delete'},
);
};
getChannelStats = async (channelId: string) => {
return this.doFetch(
`${this.getChannelRoute(channelId)}/stats`,
{method: 'get'},
);
};
getChannelMemberCountsByGroup = async (channelId: string, includeTimezones: boolean) => {
return this.doFetch(
`${this.getChannelRoute(channelId)}/member_counts_by_group?include_timezones=${includeTimezones}`,
{method: 'get'},
);
};
viewMyChannel = async (channelId: string, prevChannelId?: string) => {
const data = {channel_id: channelId, prev_channel_id: prevChannelId};
return this.doFetch(
`${this.getChannelsRoute()}/members/me/view`,
{method: 'post', body: JSON.stringify(data)},
);
};
autocompleteChannels = async (teamId: string, name: string) => {
return this.doFetch(
`${this.getTeamRoute(teamId)}/channels/autocomplete${buildQueryString({name})}`,
{method: 'get'},
);
};
autocompleteChannelsForSearch = async (teamId: string, name: string) => {
return this.doFetch(
`${this.getTeamRoute(teamId)}/channels/search_autocomplete${buildQueryString({name})}`,
{method: 'get'},
);
};
searchChannels = async (teamId: string, term: string) => {
return this.doFetch(
`${this.getTeamRoute(teamId)}/channels/search`,
{method: 'post', body: JSON.stringify({term})},
);
};
searchArchivedChannels = async (teamId: string, term: string) => {
return this.doFetch(
`${this.getTeamRoute(teamId)}/channels/search_archived`,
{method: 'post', body: JSON.stringify({term})},
);
};
};
export default ClientChannels;

View file

@ -0,0 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export const HEADER_AUTH = 'Authorization';
export const HEADER_BEARER = 'BEARER';
export const HEADER_REQUESTED_WITH = 'X-Requested-With';
export const HEADER_USER_AGENT = 'User-Agent';
export const HEADER_X_CLUSTER_ID = 'X-Cluster-Id';
export const HEADER_X_CSRF_TOKEN = 'X-CSRF-Token';
export const HEADER_TOKEN = 'Token';
export const HEADER_X_VERSION_ID = 'X-Version-Id';
export const DEFAULT_LIMIT_BEFORE = 30;
export const DEFAULT_LIMIT_AFTER = 30;
export const PER_PAGE_DEFAULT = 60;

101
app/client/rest/emojis.ts Normal file
View file

@ -0,0 +1,101 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import FormData from 'form-data';
import {analytics} from '@init/analytics';
import {CustomEmoji} from '@mm-redux/types/emojis';
import {buildQueryString} from '@mm-redux/utils/helpers';
import {PER_PAGE_DEFAULT} from './constants';
export interface ClientEmojisMix {
createCustomEmoji: (emoji: CustomEmoji, imageData: any) => Promise<CustomEmoji>;
getCustomEmoji: (id: string) => Promise<CustomEmoji>;
getCustomEmojiByName: (name: string) => Promise<CustomEmoji>;
getCustomEmojis: (page?: number, perPage?: number, sort?: string) => Promise<CustomEmoji[]>;
deleteCustomEmoji: (emojiId: string) => Promise<any>;
getSystemEmojiImageUrl: (filename: string) => string;
getCustomEmojiImageUrl: (id: string) => string;
searchCustomEmoji: (term: string, options?: Record<string, any>) => Promise<CustomEmoji[]>;
autocompleteCustomEmoji: (name: string) => Promise<CustomEmoji[]>;
}
const ClientEmojis = (superclass: any) => class extends superclass {
createCustomEmoji = async (emoji: CustomEmoji, imageData: any) => {
analytics.trackAPI('api_emoji_custom_add');
const formData = new FormData();
formData.append('image', imageData);
formData.append('emoji', JSON.stringify(emoji));
const request: any = {
method: 'post',
body: formData,
};
if (formData.getBoundary) {
request.headers = {
'Content-Type': `multipart/form-data; boundary=${formData.getBoundary()}`,
};
}
return this.doFetch(
`${this.getEmojisRoute()}`,
request,
);
};
getCustomEmoji = async (id: string) => {
return this.doFetch(
`${this.getEmojisRoute()}/${id}`,
{method: 'get'},
);
};
getCustomEmojiByName = async (name: string) => {
return this.doFetch(
`${this.getEmojisRoute()}/name/${name}`,
{method: 'get'},
);
};
getCustomEmojis = async (page = 0, perPage = PER_PAGE_DEFAULT, sort = '') => {
return this.doFetch(
`${this.getEmojisRoute()}${buildQueryString({page, per_page: perPage, sort})}`,
{method: 'get'},
);
};
deleteCustomEmoji = async (emojiId: string) => {
analytics.trackAPI('api_emoji_custom_delete');
return this.doFetch(
`${this.getEmojiRoute(emojiId)}`,
{method: 'delete'},
);
};
getSystemEmojiImageUrl = (filename: string) => {
return `${this.url}/static/emoji/${filename}.png`;
};
getCustomEmojiImageUrl = (id: string) => {
return `${this.getEmojiRoute(id)}/image`;
};
searchCustomEmoji = async (term: string, options = {}) => {
return this.doFetch(
`${this.getEmojisRoute()}/search`,
{method: 'post', body: JSON.stringify({term, ...options})},
);
};
autocompleteCustomEmoji = async (name: string) => {
return this.doFetch(
`${this.getEmojisRoute()}/autocomplete${buildQueryString({name})}`,
{method: 'get'},
);
};
};
export default ClientEmojis;

26
app/client/rest/error.ts Normal file
View file

@ -0,0 +1,26 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {cleanUrlForLogging} from '@mm-redux/utils/sentry';
export default class ClientError extends Error {
url: string;
intl: { defaultMessage: string; id: string } | { defaultMessage: string; id: string } | { id: string; defaultMessage: string; values: any } | { id: string; defaultMessage: string };
server_error_id: any;
status_code: any;
details: Error;
constructor(baseUrl: string, data: any) {
super(data.message + ': ' + cleanUrlForLogging(baseUrl, data.url));
this.message = data.message;
this.url = data.url;
this.intl = data.intl;
this.server_error_id = data.server_error_id;
this.status_code = data.status_code;
this.details = data.details;
// Ensure message is treated as a property of this class when object spreading. Without this,
// copying the object by using `{...error}` would not include the message.
Object.defineProperty(this, 'message', {enumerable: true});
}
}

47
app/client/rest/files.ts Normal file
View file

@ -0,0 +1,47 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export interface ClientFilesMix {
getFileUrl: (fileId: string, timestamp: number) => string;
getFileThumbnailUrl: (fileId: string, timestamp: number) => string;
getFilePreviewUrl: (fileId: string, timestamp: number) => string;
getFilePublicLink: (fileId: string) => Promise<any>;
}
const ClientFiles = (superclass: any) => class extends superclass {
getFileUrl(fileId: string, timestamp: number) {
let url = `${this.getFileRoute(fileId)}`;
if (timestamp) {
url += `?${timestamp}`;
}
return url;
}
getFileThumbnailUrl(fileId: string, timestamp: number) {
let url = `${this.getFileRoute(fileId)}/thumbnail`;
if (timestamp) {
url += `?${timestamp}`;
}
return url;
}
getFilePreviewUrl(fileId: string, timestamp: number) {
let url = `${this.getFileRoute(fileId)}/preview`;
if (timestamp) {
url += `?${timestamp}`;
}
return url;
}
getFilePublicLink = async (fileId: string) => {
return this.doFetch(
`${this.getFileRoute(fileId)}/link`,
{method: 'get'},
);
}
};
export default ClientFiles;

View file

@ -0,0 +1,98 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Config} from '@mm-redux/types/config';
import {Role} from '@mm-redux/types/roles';
import {Dictionary} from '@mm-redux/types/utilities';
import {buildQueryString} from '@mm-redux/utils/helpers';
import ClientError from './error';
export interface ClientGeneralMix {
getOpenGraphMetadata: (url: string) => Promise<any>;
ping: () => Promise<any>;
logClientError: (message: string, level?: string) => Promise<any>;
getClientConfigOld: () => Promise<Config>;
getClientLicenseOld: () => Promise<any>;
getTimezones: () => Promise<string[]>;
getDataRetentionPolicy: () => Promise<any>;
getRolesByNames: (rolesNames: string[]) => Promise<Role[]>;
getRedirectLocation: (urlParam: string) => Promise<Dictionary<string>>;
}
const ClientGeneral = (superclass: any) => class extends superclass {
getOpenGraphMetadata = async (url: string) => {
return this.doFetch(
`${this.getBaseRoute()}/opengraph`,
{method: 'post', body: JSON.stringify({url})},
);
};
ping = async () => {
return this.doFetch(
`${this.getBaseRoute()}/system/ping?time=${Date.now()}`,
{method: 'get'},
);
};
logClientError = async (message: string, level = 'ERROR') => {
const url = `${this.getBaseRoute()}/logs`;
if (!this.enableLogging) {
throw new ClientError(this.getUrl(), {
message: 'Logging disabled.',
url,
});
}
return this.doFetch(
url,
{method: 'post', body: JSON.stringify({message, level})},
);
};
getClientConfigOld = async () => {
return this.doFetch(
`${this.getBaseRoute()}/config/client?format=old`,
{method: 'get'},
);
};
getClientLicenseOld = async () => {
return this.doFetch(
`${this.getBaseRoute()}/license/client?format=old`,
{method: 'get'},
);
};
getTimezones = async () => {
return this.doFetch(
`${this.getTimezonesRoute()}`,
{method: 'get'},
);
};
getDataRetentionPolicy = () => {
return this.doFetch(
`${this.getDataRetentionRoute()}/policy`,
{method: 'get'},
);
};
getRolesByNames = async (rolesNames: string[]) => {
return this.doFetch(
`${this.getRolesRoute()}/names`,
{method: 'post', body: JSON.stringify(rolesNames)},
);
};
getRedirectLocation = async (urlParam: string) => {
if (!urlParam.length) {
return Promise.resolve();
}
const url = `${this.getRedirectLocationRoute()}${buildQueryString({url: urlParam})}`;
return this.doFetch(url, {method: 'get'});
};
};
export default ClientGeneral;

53
app/client/rest/groups.ts Normal file
View file

@ -0,0 +1,53 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Group} from '@mm-redux/types/groups';
import {buildQueryString} from '@mm-redux/utils/helpers';
import {PER_PAGE_DEFAULT} from './constants';
export interface ClientGroupsMix {
getGroups: (filterAllowReference?: boolean, page?: number, perPage?: number) => Promise<Group[]>;
getGroupsByUserId: (userID: string) => Promise<Group[]>;
getAllGroupsAssociatedToTeam: (teamID: string, filterAllowReference?: boolean) => Promise<Group[]>;
getAllGroupsAssociatedToChannelsInTeam: (teamID: string, filterAllowReference?: boolean) => Promise<Group[]>;
getAllGroupsAssociatedToChannel: (channelID: string, filterAllowReference?: boolean) => Promise<Group[]>;
}
const ClientGroups = (superclass: any) => class extends superclass {
getGroups = async (filterAllowReference = false, page = 0, perPage = PER_PAGE_DEFAULT) => {
return this.doFetch(
`${this.getBaseRoute()}/groups${buildQueryString({filter_allow_reference: filterAllowReference, page, per_page: perPage})}`,
{method: 'get'},
);
};
getGroupsByUserId = async (userID: string) => {
return this.doFetch(
`${this.getUsersRoute()}/${userID}/groups`,
{method: 'get'},
);
}
getAllGroupsAssociatedToTeam = async (teamID: string, filterAllowReference = false) => {
return this.doFetch(
`${this.getBaseRoute()}/teams/${teamID}/groups${buildQueryString({paginate: false, filter_allow_reference: filterAllowReference})}`,
{method: 'get'},
);
};
getAllGroupsAssociatedToChannelsInTeam = async (teamID: string, filterAllowReference = false) => {
return this.doFetch(
`${this.getBaseRoute()}/teams/${teamID}/groups_by_channels${buildQueryString({paginate: false, filter_allow_reference: filterAllowReference})}`,
{method: 'get'},
);
};
getAllGroupsAssociatedToChannel = async (channelID: string, filterAllowReference = false) => {
return this.doFetch(
`${this.getBaseRoute()}/channels/${channelID}/groups${buildQueryString({paginate: false, filter_allow_reference: filterAllowReference})}`,
{method: 'get'},
);
};
};
export default ClientGroups;

View file

@ -4,7 +4,8 @@
import assert from 'assert';
import nock from 'nock';
import {ClientError, HEADER_X_VERSION_ID} from '@mm-redux/client/client4';
import {HEADER_X_VERSION_ID} from '@client/rest/constants';
import ClientError from '@client/rest/error';
import TestHelper from 'test/test_helper';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';

56
app/client/rest/index.ts Normal file
View file

@ -0,0 +1,56 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import mix from '@utils/mix';
import {DEFAULT_LIMIT_AFTER, DEFAULT_LIMIT_BEFORE, HEADER_X_VERSION_ID} from './constants';
import ClientApps, {ClientAppsMix} from './apps';
import ClientBase from './base';
import ClientBots, {ClientBotsMix} from './bots';
import ClientChannels, {ClientChannelsMix} from './channels';
import ClientEmojis, {ClientEmojisMix} from './emojis';
import ClientFiles, {ClientFilesMix} from './files';
import ClientGeneral, {ClientGeneralMix} from './general';
import ClientGroups, {ClientGroupsMix} from './groups';
import ClientIntegrations, {ClientIntegrationsMix} from './integrations';
import ClientPosts, {ClientPostsMix} from './posts';
import ClientPreferences, {ClientPreferencesMix} from './preferences';
import ClientTeams, {ClientTeamsMix} from './teams';
import ClientTos, {ClientTosMix} from './tos';
import ClientUsers, {ClientUsersMix} from './users';
interface Client extends ClientBase,
ClientAppsMix,
ClientBotsMix,
ClientChannelsMix,
ClientEmojisMix,
ClientFilesMix,
ClientGeneralMix,
ClientGroupsMix,
ClientIntegrationsMix,
ClientPostsMix,
ClientPreferencesMix,
ClientTeamsMix,
ClientTosMix,
ClientUsersMix
{}
class Client extends mix(ClientBase).with(
ClientApps,
ClientBots,
ClientChannels,
ClientEmojis,
ClientFiles,
ClientGeneral,
ClientGroups,
ClientIntegrations,
ClientPosts,
ClientPreferences,
ClientTeams,
ClientTos,
ClientUsers,
) {}
const Client4 = new Client();
export {Client4, Client, DEFAULT_LIMIT_AFTER, DEFAULT_LIMIT_BEFORE, HEADER_X_VERSION_ID};

View file

@ -0,0 +1,68 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {analytics} from '@init/analytics';
import {Command, DialogSubmission} from '@mm-redux/types/integrations';
import {buildQueryString} from '@mm-redux/utils/helpers';
import {PER_PAGE_DEFAULT} from './constants';
export interface ClientIntegrationsMix {
getCommandsList: (teamId: string) => Promise<Command[]>;
getCommandAutocompleteSuggestionsList: (userInput: string, teamId: string, commandArgs?: Record<string, any>) => Promise<Command[]>;
getAutocompleteCommandsList: (teamId: string, page?: number, perPage?: number) => Promise<Command[]>;
executeCommand: (command: Command, commandArgs?: Record<string, any>) => Promise<any>;
addCommand: (command: Command) => Promise<Command>;
submitInteractiveDialog: (data: DialogSubmission) => Promise<any>;
}
const ClientIntegrations = (superclass: any) => class extends superclass {
getCommandsList = async (teamId: string) => {
return this.doFetch(
`${this.getCommandsRoute()}?team_id=${teamId}`,
{method: 'get'},
);
};
getCommandAutocompleteSuggestionsList = async (userInput: string, teamId: string, commandArgs: {}) => {
return this.doFetch(
`${this.getTeamRoute(teamId)}/commands/autocomplete_suggestions${buildQueryString({...commandArgs, user_input: userInput})}`,
{method: 'get'},
);
};
getAutocompleteCommandsList = async (teamId: string, page = 0, perPage = PER_PAGE_DEFAULT) => {
return this.doFetch(
`${this.getTeamRoute(teamId)}/commands/autocomplete${buildQueryString({page, per_page: perPage})}`,
{method: 'get'},
);
};
executeCommand = async (command: Command, commandArgs = {}) => {
analytics.trackAPI('api_integrations_used');
return this.doFetch(
`${this.getCommandsRoute()}/execute`,
{method: 'post', body: JSON.stringify({command, ...commandArgs})},
);
};
addCommand = async (command: Command) => {
analytics.trackAPI('api_integrations_created');
return this.doFetch(
`${this.getCommandsRoute()}`,
{method: 'post', body: JSON.stringify(command)},
);
};
submitInteractiveDialog = async (data: DialogSubmission) => {
analytics.trackAPI('api_interactive_messages_dialog_submitted');
return this.doFetch(
`${this.getBaseRoute()}/actions/dialogs/submit`,
{method: 'post', body: JSON.stringify(data)},
);
};
};
export default ClientIntegrations;

237
app/client/rest/posts.ts Normal file
View file

@ -0,0 +1,237 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {analytics} from '@init/analytics';
import {FileInfo} from '@mm-redux/types/files';
import {Post} from '@mm-redux/types/posts';
import {buildQueryString} from '@mm-redux/utils/helpers';
import {PER_PAGE_DEFAULT} from './constants';
export interface ClientPostsMix {
createPost: (post: Post) => Promise<Post>;
updatePost: (post: Post) => Promise<Post>;
getPost: (postId: string) => Promise<Post>;
patchPost: (postPatch: Partial<Post> & {id: string}) => Promise<Post>;
deletePost: (postId: string) => Promise<any>;
getPostThread: (postId: string) => Promise<any>;
getPosts: (channelId: string, page?: number, perPage?: number) => Promise<any>;
getPostsSince: (channelId: string, since: number) => Promise<any>;
getPostsBefore: (channelId: string, postId: string, page?: number, perPage?: number) => Promise<any>;
getPostsAfter: (channelId: string, postId: string, page?: number, perPage?: number) => Promise<any>;
getFileInfosForPost: (postId: string) => Promise<FileInfo[]>;
getFlaggedPosts: (userId: string, channelId?: string, teamId?: string, page?: number, perPage?: number) => Promise<any>;
getPinnedPosts: (channelId: string) => Promise<any>;
markPostAsUnread: (userId: string, postId: string) => Promise<any>;
pinPost: (postId: string) => Promise<any>;
unpinPost: (postId: string) => Promise<any>;
addReaction: (userId: string, postId: string, emojiName: string) => Promise<any>;
removeReaction: (userId: string, postId: string, emojiName: string) => Promise<any>;
getReactionsForPost: (postId: string) => Promise<any>;
searchPostsWithParams: (teamId: string, params: any) => Promise<any>;
searchPosts: (teamId: string, terms: string, isOrSearch: boolean) => Promise<any>;
doPostAction: (postId: string, actionId: string, selectedOption?: string) => Promise<any>;
doPostActionWithCookie: (postId: string, actionId: string, actionCookie: string, selectedOption?: string) => Promise<any>;
}
const ClientPosts = (superclass: any) => class extends superclass {
createPost = async (post: Post) => {
analytics.trackAPI('api_posts_create', {channel_id: post.channel_id});
if (post.root_id != null && post.root_id !== '') {
analytics.trackAPI('api_posts_replied', {channel_id: post.channel_id});
}
return this.doFetch(
`${this.getPostsRoute()}`,
{method: 'post', body: JSON.stringify(post)},
);
};
updatePost = async (post: Post) => {
analytics.trackAPI('api_posts_update', {channel_id: post.channel_id});
return this.doFetch(
`${this.getPostRoute(post.id)}`,
{method: 'put', body: JSON.stringify(post)},
);
};
getPost = async (postId: string) => {
return this.doFetch(
`${this.getPostRoute(postId)}`,
{method: 'get'},
);
};
patchPost = async (postPatch: Partial<Post> & {id: string}) => {
analytics.trackAPI('api_posts_patch', {channel_id: postPatch.channel_id});
return this.doFetch(
`${this.getPostRoute(postPatch.id)}/patch`,
{method: 'put', body: JSON.stringify(postPatch)},
);
};
deletePost = async (postId: string) => {
analytics.trackAPI('api_posts_delete');
return this.doFetch(
`${this.getPostRoute(postId)}`,
{method: 'delete'},
);
};
getPostThread = async (postId: string) => {
return this.doFetch(
`${this.getPostRoute(postId)}/thread`,
{method: 'get'},
);
};
getPosts = async (channelId: string, page = 0, perPage = PER_PAGE_DEFAULT) => {
return this.doFetch(
`${this.getChannelRoute(channelId)}/posts${buildQueryString({page, per_page: perPage})}`,
{method: 'get'},
);
};
getPostsSince = async (channelId: string, since: number) => {
return this.doFetch(
`${this.getChannelRoute(channelId)}/posts${buildQueryString({since})}`,
{method: 'get'},
);
};
getPostsBefore = async (channelId: string, postId: string, page = 0, perPage = PER_PAGE_DEFAULT) => {
analytics.trackAPI('api_posts_get_before', {channel_id: channelId});
return this.doFetch(
`${this.getChannelRoute(channelId)}/posts${buildQueryString({before: postId, page, per_page: perPage})}`,
{method: 'get'},
);
};
getPostsAfter = async (channelId: string, postId: string, page = 0, perPage = PER_PAGE_DEFAULT) => {
analytics.trackAPI('api_posts_get_after', {channel_id: channelId});
return this.doFetch(
`${this.getChannelRoute(channelId)}/posts${buildQueryString({after: postId, page, per_page: perPage})}`,
{method: 'get'},
);
};
getFileInfosForPost = async (postId: string) => {
return this.doFetch(
`${this.getPostRoute(postId)}/files/info`,
{method: 'get'},
);
};
getFlaggedPosts = async (userId: string, channelId = '', teamId = '', page = 0, perPage = PER_PAGE_DEFAULT) => {
analytics.trackAPI('api_posts_get_flagged', {team_id: teamId});
return this.doFetch(
`${this.getUserRoute(userId)}/posts/flagged${buildQueryString({channel_id: channelId, team_id: teamId, page, per_page: perPage})}`,
{method: 'get'},
);
};
getPinnedPosts = async (channelId: string) => {
analytics.trackAPI('api_posts_get_pinned', {channel_id: channelId});
return this.doFetch(
`${this.getChannelRoute(channelId)}/pinned`,
{method: 'get'},
);
};
markPostAsUnread = async (userId: string, postId: string) => {
analytics.trackAPI('api_post_set_unread_post');
return this.doFetch(
`${this.getUserRoute(userId)}/posts/${postId}/set_unread`,
{method: 'post'},
);
}
pinPost = async (postId: string) => {
analytics.trackAPI('api_posts_pin');
return this.doFetch(
`${this.getPostRoute(postId)}/pin`,
{method: 'post'},
);
};
unpinPost = async (postId: string) => {
analytics.trackAPI('api_posts_unpin');
return this.doFetch(
`${this.getPostRoute(postId)}/unpin`,
{method: 'post'},
);
};
addReaction = async (userId: string, postId: string, emojiName: string) => {
analytics.trackAPI('api_reactions_save', {post_id: postId});
return this.doFetch(
`${this.getReactionsRoute()}`,
{method: 'post', body: JSON.stringify({user_id: userId, post_id: postId, emoji_name: emojiName})},
);
};
removeReaction = async (userId: string, postId: string, emojiName: string) => {
analytics.trackAPI('api_reactions_delete', {post_id: postId});
return this.doFetch(
`${this.getUserRoute(userId)}/posts/${postId}/reactions/${emojiName}`,
{method: 'delete'},
);
};
getReactionsForPost = async (postId: string) => {
return this.doFetch(
`${this.getPostRoute(postId)}/reactions`,
{method: 'get'},
);
};
searchPostsWithParams = async (teamId: string, params: any) => {
analytics.trackAPI('api_posts_search', {team_id: teamId});
return this.doFetch(
`${this.getTeamRoute(teamId)}/posts/search`,
{method: 'post', body: JSON.stringify(params)},
);
};
searchPosts = async (teamId: string, terms: string, isOrSearch: boolean) => {
return this.searchPostsWithParams(teamId, {terms, is_or_search: isOrSearch});
};
doPostAction = async (postId: string, actionId: string, selectedOption = '') => {
return this.doPostActionWithCookie(postId, actionId, '', selectedOption);
};
doPostActionWithCookie = async (postId: string, actionId: string, actionCookie: string, selectedOption = '') => {
if (selectedOption) {
analytics.trackAPI('api_interactive_messages_menu_selected');
} else {
analytics.trackAPI('api_interactive_messages_button_clicked');
}
const msg: any = {
selected_option: selectedOption,
};
if (actionCookie !== '') {
msg.cookie = actionCookie;
}
return this.doFetch(
`${this.getPostRoute(postId)}/actions/${encodeURIComponent(actionId)}`,
{method: 'post', body: JSON.stringify(msg)},
);
};
};
export default ClientPosts;

View file

@ -0,0 +1,35 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {PreferenceType} from '@mm-redux/types/preferences';
export interface ClientPreferencesMix {
savePreferences: (userId: string, preferences: PreferenceType[]) => Promise<any>;
deletePreferences: (userId: string, preferences: PreferenceType[]) => Promise<any>;
getMyPreferences: () => Promise<PreferenceType>;
}
const ClientPreferences = (superclass: any) => class extends superclass {
savePreferences = async (userId: string, preferences: PreferenceType[]) => {
return this.doFetch(
`${this.getPreferencesRoute(userId)}`,
{method: 'put', body: JSON.stringify(preferences)},
);
};
getMyPreferences = async () => {
return this.doFetch(
`${this.getPreferencesRoute('me')}`,
{method: 'get'},
);
};
deletePreferences = async (userId: string, preferences: PreferenceType[]) => {
return this.doFetch(
`${this.getPreferencesRoute(userId)}/delete`,
{method: 'post', body: JSON.stringify(preferences)},
);
};
};
export default ClientPreferences;

177
app/client/rest/teams.ts Normal file
View file

@ -0,0 +1,177 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {analytics} from '@init/analytics';
import {Team, TeamMembership, TeamUnread} from '@mm-redux/types/teams';
import {buildQueryString} from '@mm-redux/utils/helpers';
import {PER_PAGE_DEFAULT} from './constants';
export interface ClientTeamsMix {
createTeam: (team: Team) => Promise<Team>;
deleteTeam: (teamId: string) => Promise<any>;
updateTeam: (team: Team) => Promise<Team>;
patchTeam: (team: Partial<Team> & {id: string}) => Promise<Team>;
getTeams: (page?: number, perPage?: number, includeTotalCount?: boolean) => Promise<any>;
getTeam: (teamId: string) => Promise<Team>;
getTeamByName: (teamName: string) => Promise<Team>;
getMyTeams: () => Promise<Team[]>;
getTeamsForUser: (userId: string) => Promise<Team[]>;
getMyTeamMembers: () => Promise<TeamMembership[]>;
getMyTeamUnreads: () => Promise<TeamUnread[]>;
getTeamMembers: (teamId: string, page?: number, perPage?: number) => Promise<TeamMembership[]>;
getTeamMember: (teamId: string, userId: string) => Promise<TeamMembership>;
addToTeam: (teamId: string, userId: string) => Promise<TeamMembership>;
joinTeam: (inviteId: string) => Promise<TeamMembership>;
removeFromTeam: (teamId: string, userId: string) => Promise<any>;
getTeamStats: (teamId: string) => Promise<any>;
getTeamIconUrl: (teamId: string, lastTeamIconUpdate: number) => string;
}
const ClientTeams = (superclass: any) => class extends superclass {
createTeam = async (team: Team) => {
analytics.trackAPI('api_teams_create');
return this.doFetch(
`${this.getTeamsRoute()}`,
{method: 'post', body: JSON.stringify(team)},
);
};
deleteTeam = async (teamId: string) => {
analytics.trackAPI('api_teams_delete');
return this.doFetch(
`${this.getTeamRoute(teamId)}`,
{method: 'delete'},
);
};
updateTeam = async (team: Team) => {
analytics.trackAPI('api_teams_update_name', {team_id: team.id});
return this.doFetch(
`${this.getTeamRoute(team.id)}`,
{method: 'put', body: JSON.stringify(team)},
);
};
patchTeam = async (team: Partial<Team> & {id: string}) => {
analytics.trackAPI('api_teams_patch_name', {team_id: team.id});
return this.doFetch(
`${this.getTeamRoute(team.id)}/patch`,
{method: 'put', body: JSON.stringify(team)},
);
};
getTeams = async (page = 0, perPage = PER_PAGE_DEFAULT, includeTotalCount = false) => {
return this.doFetch(
`${this.getTeamsRoute()}${buildQueryString({page, per_page: perPage, include_total_count: includeTotalCount})}`,
{method: 'get'},
);
};
getTeam = async (teamId: string) => {
return this.doFetch(
this.getTeamRoute(teamId),
{method: 'get'},
);
};
getTeamByName = async (teamName: string) => {
analytics.trackAPI('api_teams_get_team_by_name');
return this.doFetch(
this.getTeamNameRoute(teamName),
{method: 'get'},
);
};
getMyTeams = async () => {
return this.doFetch(
`${this.getUserRoute('me')}/teams`,
{method: 'get'},
);
};
getTeamsForUser = async (userId: string) => {
return this.doFetch(
`${this.getUserRoute(userId)}/teams`,
{method: 'get'},
);
};
getMyTeamMembers = async () => {
return this.doFetch(
`${this.getUserRoute('me')}/teams/members`,
{method: 'get'},
);
};
getMyTeamUnreads = async () => {
return this.doFetch(
`${this.getUserRoute('me')}/teams/unread`,
{method: 'get'},
);
};
getTeamMembers = async (teamId: string, page = 0, perPage = PER_PAGE_DEFAULT) => {
return this.doFetch(
`${this.getTeamMembersRoute(teamId)}${buildQueryString({page, per_page: perPage})}`,
{method: 'get'},
);
};
getTeamMember = async (teamId: string, userId: string) => {
return this.doFetch(
`${this.getTeamMemberRoute(teamId, userId)}`,
{method: 'get'},
);
};
addToTeam = async (teamId: string, userId: string) => {
analytics.trackAPI('api_teams_invite_members', {team_id: teamId});
const member = {user_id: userId, team_id: teamId};
return this.doFetch(
`${this.getTeamMembersRoute(teamId)}`,
{method: 'post', body: JSON.stringify(member)},
);
};
joinTeam = async (inviteId: string) => {
const query = buildQueryString({invite_id: inviteId});
return this.doFetch(
`${this.getTeamsRoute()}/members/invite${query}`,
{method: 'post'},
);
};
removeFromTeam = async (teamId: string, userId: string) => {
analytics.trackAPI('api_teams_remove_members', {team_id: teamId});
return this.doFetch(
`${this.getTeamMemberRoute(teamId, userId)}`,
{method: 'delete'},
);
};
getTeamStats = async (teamId: string) => {
return this.doFetch(
`${this.getTeamRoute(teamId)}/stats`,
{method: 'get'},
);
};
getTeamIconUrl = (teamId: string, lastTeamIconUpdate: number) => {
const params: any = {};
if (lastTeamIconUpdate) {
params._ = lastTeamIconUpdate;
}
return `${this.getTeamRoute(teamId)}/image${buildQueryString(params)}`;
};
};
export default ClientTeams;

25
app/client/rest/tos.ts Normal file
View file

@ -0,0 +1,25 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export interface ClientTosMix {
updateMyTermsOfServiceStatus: (termsOfServiceId: string, accepted: boolean) => Promise<any>;
getTermsOfService: () => Promise<any>;
}
const ClientTos = (superclass: any) => class extends superclass {
updateMyTermsOfServiceStatus = async (termsOfServiceId: string, accepted: boolean) => {
return this.doFetch(
`${this.getUserRoute('me')}/terms_of_service`,
{method: 'post', body: JSON.stringify({termsOfServiceId, accepted})},
);
}
getTermsOfService = async () => {
return this.doFetch(
`${this.getBaseRoute()}/terms_of_service`,
{method: 'get'},
);
}
};
export default ClientTos;

398
app/client/rest/users.ts Normal file
View file

@ -0,0 +1,398 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {analytics} from '@init/analytics';
import {General} from '@mm-redux/constants';
import {UserProfile, UserStatus} from '@mm-redux/types/users';
import {buildQueryString, isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {PER_PAGE_DEFAULT} from './constants';
export interface ClientUsersMix {
createUser: (user: UserProfile, token: string, inviteId: string) => Promise<UserProfile>;
patchMe: (userPatch: Partial<UserProfile>) => Promise<UserProfile>;
patchUser: (userPatch: Partial<UserProfile> & {id: string}) => Promise<UserProfile>;
updateUser: (user: UserProfile) => Promise<UserProfile>;
demoteUserToGuest: (userId: string) => Promise<any>;
getKnownUsers: () => Promise<string[]>;
sendPasswordResetEmail: (email: string) => Promise<any>;
setDefaultProfileImage: (userId: string) => Promise<any>;
login: (loginId: string, password: string, token?: string, deviceId?: string, ldapOnly?: boolean) => Promise<UserProfile>;
loginById: (id: string, password: string, token?: string, deviceId?: string) => Promise<UserProfile>;
logout: () => Promise<any>;
getProfiles: (page?: number, perPage?: number, options?: Record<string, any>) => Promise<UserProfile[]>;
getProfilesByIds: (userIds: string[], options?: Record<string, any>) => Promise<UserProfile[]>;
getProfilesByUsernames: (usernames: string[]) => Promise<UserProfile[]>;
getProfilesInTeam: (teamId: string, page?: number, perPage?: number, sort?: string, options?: Record<string, any>) => Promise<UserProfile[]>;
getProfilesNotInTeam: (teamId: string, groupConstrained: boolean, page?: number, perPage?: number) => Promise<UserProfile[]>;
getProfilesWithoutTeam: (page?: number, perPage?: number, options?: Record<string, any>) => Promise<UserProfile[]>;
getProfilesInChannel: (channelId: string, page?: number, perPage?: number, sort?: string) => Promise<UserProfile[]>;
getProfilesInGroupChannels: (channelsIds: string[]) => Promise<{[x: string]: UserProfile[]}>;
getProfilesNotInChannel: (teamId: string, channelId: string, groupConstrained: boolean, page?: number, perPage?: number) => Promise<UserProfile[]>;
getMe: () => Promise<UserProfile>;
getUser: (userId: string) => Promise<UserProfile>;
getUserByUsername: (username: string) => Promise<UserProfile>;
getUserByEmail: (email: string) => Promise<UserProfile>;
getProfilePictureUrl: (userId: string, lastPictureUpdate: number) => string;
getDefaultProfilePictureUrl: (userId: string) => string;
autocompleteUsers: (name: string, teamId: string, channelId: string, options?: Record<string, any>) => Promise<{users: UserProfile[], out_of_channel?: UserProfile[]}>;
getSessions: (userId: string) => Promise<any>;
checkUserMfa: (loginId: string) => Promise<{mfa_required: boolean}>;
attachDevice: (deviceId: string) => Promise<any>;
searchUsers: (term: string, options: any) => Promise<UserProfile[]>;
getStatusesByIds: (userIds: string[]) => Promise<UserStatus[]>;
getStatus: (userId: string) => Promise<UserStatus>;
updateStatus: (status: UserStatus) => Promise<UserStatus>;
}
const ClientUsers = (superclass: any) => class extends superclass {
createUser = async (user: UserProfile, token: string, inviteId: string) => {
analytics.trackAPI('api_users_create');
const queryParams: any = {};
if (token) {
queryParams.t = token;
}
if (inviteId) {
queryParams.iid = inviteId;
}
return this.doFetch(
`${this.getUsersRoute()}${buildQueryString(queryParams)}`,
{method: 'post', body: JSON.stringify(user)},
);
}
patchMe = async (userPatch: Partial<UserProfile>) => {
return this.doFetch(
`${this.getUserRoute('me')}/patch`,
{method: 'put', body: JSON.stringify(userPatch)},
);
}
patchUser = async (userPatch: Partial<UserProfile> & {id: string}) => {
analytics.trackAPI('api_users_patch');
return this.doFetch(
`${this.getUserRoute(userPatch.id)}/patch`,
{method: 'put', body: JSON.stringify(userPatch)},
);
}
updateUser = async (user: UserProfile) => {
analytics.trackAPI('api_users_update');
return this.doFetch(
`${this.getUserRoute(user.id)}`,
{method: 'put', body: JSON.stringify(user)},
);
}
demoteUserToGuest = async (userId: string) => {
analytics.trackAPI('api_users_demote_user_to_guest');
return this.doFetch(
`${this.getUserRoute(userId)}/demote`,
{method: 'post'},
);
}
getKnownUsers = async () => {
analytics.trackAPI('api_get_known_users');
return this.doFetch(
`${this.getUsersRoute()}/known`,
{method: 'get'},
);
}
sendPasswordResetEmail = async (email: string) => {
analytics.trackAPI('api_users_send_password_reset');
return this.doFetch(
`${this.getUsersRoute()}/password/reset/send`,
{method: 'post', body: JSON.stringify({email})},
);
}
setDefaultProfileImage = async (userId: string) => {
analytics.trackAPI('api_users_set_default_profile_picture');
return this.doFetch(
`${this.getUserRoute(userId)}/image`,
{method: 'delete'},
);
};
login = async (loginId: string, password: string, token = '', deviceId = '', ldapOnly = false) => {
analytics.trackAPI('api_users_login');
if (ldapOnly) {
analytics.trackAPI('api_users_login_ldap');
}
const body: any = {
device_id: deviceId,
login_id: loginId,
password,
token,
};
if (ldapOnly) {
body.ldap_only = 'true';
}
const {data} = await this.doFetchWithResponse(
`${this.getUsersRoute()}/login`,
{
method: 'post',
body: JSON.stringify(body),
headers: {'Cache-Control': 'no-store'},
},
);
return data;
};
loginById = async (id: string, password: string, token = '', deviceId = '') => {
analytics.trackAPI('api_users_login');
const body: any = {
device_id: deviceId,
id,
password,
token,
};
const {data} = await this.doFetchWithResponse(
`${this.getUsersRoute()}/login`,
{method: 'post', body: JSON.stringify(body)},
);
return data;
};
logout = async () => {
analytics.trackAPI('api_users_logout');
const {response} = await this.doFetchWithResponse(
`${this.getUsersRoute()}/logout`,
{method: 'post'},
);
if (response.ok) {
this.token = '';
}
this.serverVersion = '';
return response;
};
getProfiles = async (page = 0, perPage = PER_PAGE_DEFAULT, options = {}) => {
analytics.trackAPI('api_profiles_get');
return this.doFetch(
`${this.getUsersRoute()}${buildQueryString({page, per_page: perPage, ...options})}`,
{method: 'get'},
);
};
getProfilesByIds = async (userIds: string[], options = {}) => {
analytics.trackAPI('api_profiles_get_by_ids');
return this.doFetch(
`${this.getUsersRoute()}/ids${buildQueryString(options)}`,
{method: 'post', body: JSON.stringify(userIds)},
);
};
getProfilesByUsernames = async (usernames: string[]) => {
analytics.trackAPI('api_profiles_get_by_usernames');
return this.doFetch(
`${this.getUsersRoute()}/usernames`,
{method: 'post', body: JSON.stringify(usernames)},
);
};
getProfilesInTeam = async (teamId: string, page = 0, perPage = PER_PAGE_DEFAULT, sort = '', options = {}) => {
analytics.trackAPI('api_profiles_get_in_team', {team_id: teamId, sort});
return this.doFetch(
`${this.getUsersRoute()}${buildQueryString({...options, in_team: teamId, page, per_page: perPage, sort})}`,
{method: 'get'},
);
};
getProfilesNotInTeam = async (teamId: string, groupConstrained: boolean, page = 0, perPage = PER_PAGE_DEFAULT) => {
analytics.trackAPI('api_profiles_get_not_in_team', {team_id: teamId, group_constrained: groupConstrained});
const queryStringObj: any = {not_in_team: teamId, page, per_page: perPage};
if (groupConstrained) {
queryStringObj.group_constrained = true;
}
return this.doFetch(
`${this.getUsersRoute()}${buildQueryString(queryStringObj)}`,
{method: 'get'},
);
};
getProfilesWithoutTeam = async (page = 0, perPage = PER_PAGE_DEFAULT, options = {}) => {
analytics.trackAPI('api_profiles_get_without_team');
return this.doFetch(
`${this.getUsersRoute()}${buildQueryString({...options, without_team: 1, page, per_page: perPage})}`,
{method: 'get'},
);
};
getProfilesInChannel = async (channelId: string, page = 0, perPage = PER_PAGE_DEFAULT, sort = '') => {
analytics.trackAPI('api_profiles_get_in_channel', {channel_id: channelId});
const serverVersion = this.getServerVersion();
let queryStringObj;
if (isMinimumServerVersion(serverVersion, 4, 7)) {
queryStringObj = {in_channel: channelId, page, per_page: perPage, sort};
} else {
queryStringObj = {in_channel: channelId, page, per_page: perPage};
}
return this.doFetch(
`${this.getUsersRoute()}${buildQueryString(queryStringObj)}`,
{method: 'get'},
);
};
getProfilesInGroupChannels = async (channelsIds: string[]) => {
analytics.trackAPI('api_profiles_get_in_group_channels', {channelsIds});
return this.doFetch(
`${this.getUsersRoute()}/group_channels`,
{method: 'post', body: JSON.stringify(channelsIds)},
);
};
getProfilesNotInChannel = async (teamId: string, channelId: string, groupConstrained: boolean, page = 0, perPage = PER_PAGE_DEFAULT) => {
analytics.trackAPI('api_profiles_get_not_in_channel', {team_id: teamId, channel_id: channelId, group_constrained: groupConstrained});
const queryStringObj: any = {in_team: teamId, not_in_channel: channelId, page, per_page: perPage};
if (groupConstrained) {
queryStringObj.group_constrained = true;
}
return this.doFetch(
`${this.getUsersRoute()}${buildQueryString(queryStringObj)}`,
{method: 'get'},
);
};
getMe = async () => {
return this.doFetch(
`${this.getUserRoute('me')}`,
{method: 'get'},
);
};
getUser = async (userId: string) => {
return this.doFetch(
`${this.getUserRoute(userId)}`,
{method: 'get'},
);
};
getUserByUsername = async (username: string) => {
return this.doFetch(
`${this.getUsersRoute()}/username/${username}`,
{method: 'get'},
);
};
getUserByEmail = async (email: string) => {
return this.doFetch(
`${this.getUsersRoute()}/email/${email}`,
{method: 'get'},
);
};
getProfilePictureUrl = (userId: string, lastPictureUpdate: number) => {
const params: any = {};
if (lastPictureUpdate) {
params._ = lastPictureUpdate;
}
return `${this.getUserRoute(userId)}/image${buildQueryString(params)}`;
};
getDefaultProfilePictureUrl = (userId: string) => {
return `${this.getUserRoute(userId)}/image/default`;
};
autocompleteUsers = async (name: string, teamId: string, channelId: string, options = {
limit: General.AUTOCOMPLETE_LIMIT_DEFAULT,
}) => {
return this.doFetch(`${this.getUsersRoute()}/autocomplete${buildQueryString({
in_team: teamId,
in_channel: channelId,
name,
limit: options.limit,
})}`, {
method: 'get',
});
};
getSessions = async (userId: string) => {
return this.doFetch(
`${this.getUserRoute(userId)}/sessions`,
{method: 'get'},
);
};
checkUserMfa = async (loginId: string) => {
return this.doFetch(
`${this.getUsersRoute()}/mfa`,
{method: 'post', body: JSON.stringify({login_id: loginId})},
);
};
attachDevice = async (deviceId: string) => {
return this.doFetch(
`${this.getUsersRoute()}/sessions/device`,
{method: 'put', body: JSON.stringify({device_id: deviceId})},
);
};
searchUsers = async (term: string, options: any) => {
analytics.trackAPI('api_search_users');
return this.doFetch(
`${this.getUsersRoute()}/search`,
{method: 'post', body: JSON.stringify({term, ...options})},
);
};
getStatusesByIds = async (userIds: string[]) => {
return this.doFetch(
`${this.getUsersRoute()}/status/ids`,
{method: 'post', body: JSON.stringify(userIds)},
);
};
getStatus = async (userId: string) => {
return this.doFetch(
`${this.getUserRoute(userId)}/status`,
{method: 'get'},
);
};
updateStatus = async (status: UserStatus) => {
return this.doFetch(
`${this.getUserRoute(status.user_id)}/status`,
{method: 'put', body: JSON.stringify(status)},
);
};
};
export default ClientUsers;

View file

@ -6,7 +6,7 @@ export {thunk};
const configureStore = require('redux-mock-store').default;
export {configureStore};
export {Client4} from '@mm-redux/client';
export {Client4} from '@client/rest';
export type {AppBinding, AppForm} from '@mm-redux/types/apps';
export {AppFieldTypes} from '@mm-redux/constants/apps';

View file

@ -9,7 +9,7 @@ import {
} from 'react-native';
import {analytics} from '@init/analytics';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {Command, AutocompleteSuggestion, CommandArgs} from '@mm-redux/types/integrations';
import {Theme} from '@mm-redux/types/preferences';

View file

@ -6,7 +6,7 @@ import {connect} from 'react-redux';
import {getCustomEmojisByName} from '@mm-redux/selectors/entities/emojis';
import {getCurrentUserId} from '@mm-redux/selectors/entities/users';
import {getConfig} from '@mm-redux/selectors/entities/general';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {BuiltInEmojis, EmojiIndicesByAlias, Emojis} from 'app/utils/emojis';

View file

@ -9,7 +9,7 @@ import {
} from 'react-native';
import ProgressiveImage from '@components/progressive_image';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {changeOpacity} from '@utils/theme';
import FileAttachmentIcon from './file_attachment_icon';

View file

@ -3,7 +3,7 @@
import React from 'react';
import {shallow} from 'enzyme';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import FileAttachmentImage from './file_attachment_image.js';

View file

@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
import {StyleSheet, View, DeviceEventEmitter} from 'react-native';
import ImageViewPort from '@components/image_viewport';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {isDocument, isGif, isImage, isVideo} from '@utils/file';
import {getViewPortWidth, openGalleryAtIndex} from '@utils/images';
import {preventDoubleTap} from '@utils/tap';

View file

@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
import {StyleSheet, TouchableOpacity, View} from 'react-native';
import RNFetchBlob from 'rn-fetch-blob';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import mattermostBucket from 'app/mattermost_bucket';
import FileAttachmentImage from '@components/file_attachment_list/file_attachment_image';

View file

@ -4,7 +4,7 @@
import {connect} from 'react-redux';
import {isSystemMessage} from '@mm-redux/utils/post_utils';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {getTheme} from '@mm-redux/selectors/entities/preferences';
import {getConfig} from '@mm-redux/selectors/entities/general';

View file

@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
import {Platform, View} from 'react-native';
import FastImage from 'react-native-fast-image';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import CompassIcon from '@components/compass_icon';
import UserStatus from '@components/user_status';

View file

@ -4,7 +4,7 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {t} from 'app/utils/i18n';

View file

@ -7,7 +7,7 @@ import Preferences from '@mm-redux/constants/preferences';
import ProfilePictureButton from './profile_picture_button.js';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
describe('profile_picture_button', () => {
const baseProps = {

View file

@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
import {IntlProvider} from 'react-intl';
import {Platform} from 'react-native';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import EventEmitter from '@mm-redux/utils/event_emitter';
import {resetToTeams} from 'app/actions/navigation';

View file

@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
import {Text, View} from 'react-native';
import FastImage from 'react-native-fast-image';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {makeStyleSheetFromTheme} from 'app/utils/theme';

View file

@ -4,7 +4,7 @@
import AsyncStorage from '@react-native-community/async-storage';
import * as KeyChain from 'react-native-keychain';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import mattermostManaged from 'app/mattermost_managed';
import EphemeralStore from 'app/store/ephemeral_store';

View file

@ -7,8 +7,9 @@ import RNFetchBlob from 'rn-fetch-blob';
import urlParse from 'url-parse';
import LocalConfig from '@assets/config';
import {Client4} from '@mm-redux/client';
import {ClientError, HEADER_X_VERSION_ID} from '@mm-redux/client/client4';
import {Client4} from '@client/rest';
import {HEADER_TOKEN, HEADER_X_CLUSTER_ID, HEADER_X_VERSION_ID} from '@client/rest/constants';
import ClientError from '@client/rest/error';
import EventEmitter from '@mm-redux/utils/event_emitter';
import {General} from '@mm-redux/constants';
import {t} from '@utils/i18n';
@ -18,8 +19,6 @@ import mattermostManaged from 'app/mattermost_managed';
/* eslint-disable no-throw-literal */
export const HEADER_X_CLUSTER_ID = 'X-Cluster-Id';
export const HEADER_TOKEN = 'Token';
const DEFAULT_TIMEOUT = 10000;
let managedConfig;

View file

@ -1,16 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mm-redux/client';
import {HEADER_X_VERSION_ID} from '@mm-redux/client/client4';
import '@init/fetch';
import {Client4} from '@client/rest';
import {HEADER_X_CLUSTER_ID, HEADER_X_VERSION_ID, HEADER_TOKEN} from '@client/rest/constants';
import EventEmitter from '@mm-redux/utils/event_emitter';
import {General} from '@mm-redux/constants';
import {
HEADER_X_CLUSTER_ID,
HEADER_TOKEN,
} from 'app/init/fetch';
describe('Fetch', () => {
beforeAll(() => {
global.fetch = jest.fn(() => Promise.resolve());

View file

@ -23,7 +23,7 @@ import {setAppState, setServerVersion} from '@mm-redux/actions/general';
import {getTeams} from '@mm-redux/actions/teams';
import {autoUpdateTimezone} from '@mm-redux/actions/timezone';
import {close as closeWebSocket} from '@actions/websocket';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {General} from '@mm-redux/constants';
import {getConfig} from '@mm-redux/selectors/entities/general';
import {getCurrentChannelId} from '@mm-redux/selectors/entities/channels';

View file

@ -71,13 +71,6 @@ export default keyMirror({
INCREMENT_UNREAD_MENTION_COUNT: null,
DECREMENT_UNREAD_MENTION_COUNT: null,
UPDATED_CHANNEL_SCHEME: null,
UPDATED_CHANNEL_MEMBER_SCHEME_ROLES: null,
RECEIVED_CHANNEL_MEMBERS_MINUS_GROUP_MEMBERS: null,
RECEIVED_CHANNEL_MODERATIONS: null,
RECEIVED_CHANNEL_MEMBER_COUNTS_BY_GROUP: null,
RECEIVED_TOTAL_CHANNEL_COUNT: null,

View file

@ -12,10 +12,8 @@ import PreferenceTypes from './preferences';
import IntegrationTypes from './integrations';
import EmojiTypes from './emojis';
import GifTypes from './gifs';
import JobTypes from './jobs';
import SearchTypes from './search';
import RoleTypes from './roles';
import SchemeTypes from './schemes';
import GroupTypes from './groups';
import BotTypes from './bots';
import PluginTypes from './plugins';
@ -34,10 +32,8 @@ export {
IntegrationTypes,
EmojiTypes,
GifTypes,
JobTypes,
SearchTypes,
RoleTypes,
SchemeTypes,
GroupTypes,
BotTypes,
PluginTypes,

View file

@ -4,22 +4,12 @@ import keyMirror from '@mm-redux/utils/key_mirror';
export default keyMirror({
RECEIVED_INCOMING_HOOK: null,
RECEIVED_INCOMING_HOOKS: null,
DELETED_INCOMING_HOOK: null,
RECEIVED_OUTGOING_HOOK: null,
RECEIVED_OUTGOING_HOOKS: null,
DELETED_OUTGOING_HOOK: null,
RECEIVED_CUSTOM_TEAM_COMMANDS: null,
RECEIVED_COMMAND: null,
RECEIVED_COMMANDS: null,
RECEIVED_COMMAND_SUGGESTIONS: null,
RECEIVED_COMMAND_SUGGESTIONS_FAILURE: null,
RECEIVED_COMMAND_TOKEN: null,
DELETED_COMMAND: null,
RECEIVED_OAUTH_APP: null,
RECEIVED_OAUTH_APPS: null,
DELETED_OAUTH_APP: null,
RECEIVED_DIALOG_TRIGGER_ID: null,
RECEIVED_DIALOG: null,

View file

@ -1,25 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import keyMirror from '@mm-redux/utils/key_mirror';
export default keyMirror({
CREATE_JOB_REQUEST: null,
CREATE_JOB_SUCCESS: null,
CREATE_JOB_FAILURE: null,
CANCEL_JOB_REQUEST: null,
CANCEL_JOB_SUCCESS: null,
CANCEL_JOB_FAILURE: null,
GET_JOB_REQUEST: null,
GET_JOB_SUCCESS: null,
GET_JOB_FAILURE: null,
GET_JOBS_REQUEST: null,
GET_JOBS_SUCCESS: null,
GET_JOBS_FAILURE: null,
RECEIVED_JOB: null,
RECEIVED_JOBS: null,
RECEIVED_JOBS_BY_TYPE: null,
});

View file

@ -7,18 +7,6 @@ export default keyMirror({
ROLES_BY_NAMES_SUCCESS: null,
ROLES_BY_NAMES_FAILURE: null,
ROLE_BY_NAME_REQUEST: null,
ROLE_BY_NAME_SUCCESS: null,
ROLE_BY_NAME_FAILURE: null,
ROLE_BY_ID_REQUEST: null,
ROLE_BY_ID_SUCCESS: null,
ROLE_BY_ID_FAILURE: null,
EDIT_ROLE_REQUEST: null,
EDIT_ROLE_SUCCESS: null,
EDIT_ROLE_FAILURE: null,
RECEIVED_ROLES: null,
RECEIVED_ROLE: null,
ROLE_DELETED: null,

View file

@ -1,19 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import keyMirror from '@mm-redux/utils/key_mirror';
export default keyMirror({
RECEIVED_SCHEME: null,
RECEIVED_SCHEMES: null,
CREATED_SCHEME: null,
DELETED_SCHEME: null,
PATCHED_SCHEME: null,
RECEIVED_SCHEME_TEAMS: null,
RECEIVED_SCHEME_CHANNELS: null,
});

View file

@ -16,10 +16,6 @@ export default keyMirror({
CREATE_TEAM_SUCCESS: null,
CREATE_TEAM_FAILURE: null,
GET_TEAM_MEMBERS_REQUEST: null,
GET_TEAM_MEMBERS_SUCCESS: null,
GET_TEAM_MEMBERS_FAILURE: null,
JOIN_TEAM_REQUEST: null,
JOIN_TEAM_SUCCESS: null,
JOIN_TEAM_FAILURE: null,
@ -50,10 +46,6 @@ export default keyMirror({
RECEIVED_TEAM_STATS: null,
RECEIVED_MY_TEAM_UNREADS: null,
LEAVE_TEAM: null,
UPDATED_TEAM_SCHEME: null,
UPDATED_TEAM_MEMBER_SCHEME_ROLES: null,
RECEIVED_TEAM_MEMBERS_MINUS_GROUP_MEMBERS: null,
RECEIVED_TOTAL_TEAM_COUNT: null,
});

View file

@ -52,7 +52,6 @@ export default keyMirror({
RECEIVED_PROFILE_NOT_IN_CHANNEL: null,
RECEIVED_SESSIONS: null,
RECEIVED_REVOKED_SESSION: null,
RECEIVED_AUDITS: null,
RECEIVED_STATUS: null,
RECEIVED_STATUSES: null,
RECEIVED_AUTOCOMPLETE_IN_CHANNEL: null,

View file

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {AppsTypes} from '@mm-redux/action_types';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {ActionFunc} from '@mm-redux/types/actions';

View file

@ -5,7 +5,7 @@ import assert from 'assert';
import nock from 'nock';
import * as BotActions from '@mm-redux/actions/bots';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import TestHelper from 'test/test_helper';
import configureStore from 'test/test_store';
@ -51,82 +51,4 @@ describe('Actions.Bots', () => {
const botsResult = state.entities.bots.accounts[bot.user_id];
assert.equal(bot.username, botsResult.username);
});
it('createBot', async () => {
const bot = TestHelper.fakeBot();
nock(Client4.getBaseRoute()).
post('/bots').
reply(200, bot);
await store.dispatch(BotActions.createBot(bot));
const state = store.getState();
const botsResult = state.entities.bots.accounts[bot.user_id];
assert.equal(bot.username, botsResult.username);
});
it('patchBot', async () => {
const bot = TestHelper.fakeBot();
nock(Client4.getBaseRoute()).
post('/bots').
reply(200, bot);
await store.dispatch(BotActions.createBot(bot));
bot.username = 'mynewusername';
nock(Client4.getBaseRoute()).
put(`/bots/${bot.user_id}`).
reply(200, bot);
await store.dispatch(BotActions.patchBot(bot.user_id, bot));
const state = store.getState();
const botsResult = state.entities.bots.accounts[bot.user_id];
assert.equal(bot.username, botsResult.username);
});
it('disableBot', async () => {
const bot = TestHelper.fakeBot();
nock(Client4.getBaseRoute()).
post('/bots').
reply(200, bot);
await store.dispatch(BotActions.createBot(bot));
// Disable the bot by setting delete_at to a value > 0
bot.delete_at = 1507840900065;
nock(Client4.getBotRoute(bot.user_id)).
post('/disable').
reply(200, bot);
await store.dispatch(BotActions.disableBot(bot.user_id));
const state = store.getState();
const botsResult = state.entities.bots.accounts[bot.user_id];
assert.equal(bot.delete_at, botsResult.delete_at);
bot.delete_at = 0;
nock(Client4.getBotRoute(bot.user_id)).
post('/enable').
reply(200, bot);
await store.dispatch(BotActions.enableBot(bot.user_id));
const state2 = store.getState();
const botsResult2 = state2.entities.bots.accounts[bot.user_id];
assert.equal(bot.delete_at, botsResult2.delete_at);
});
it('assignBot', async () => {
const bot = TestHelper.fakeBot();
nock(Client4.getBaseRoute()).
post('/bots').
reply(200, bot);
await store.dispatch(BotActions.createBot(bot));
bot.owner_id = TestHelper.generateId();
nock(Client4.getBotRoute(bot.user_id)).
post('/assign/' + bot.owner_id).
reply(200, bot);
await store.dispatch(BotActions.assignBot(bot.user_id, bot.owner_id));
const state = store.getState();
const botsResult = state.entities.bots.accounts[bot.user_id];
assert.equal(bot.owner_id, botsResult.owner_id);
});
});

View file

@ -1,36 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {BotTypes} from '@mm-redux/action_types';
import {bindClientFunc} from './helpers';
import {ActionFunc} from '@mm-redux/types/actions';
import {Bot, BotPatch} from '@mm-redux/types/bots';
const BOTS_PER_PAGE_DEFAULT = 20;
export function createBot(bot: Bot): ActionFunc {
return bindClientFunc({
clientFunc: Client4.createBot,
onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT,
params: [
bot,
],
});
}
export function patchBot(botUserId: string, botPatch: BotPatch): ActionFunc {
return bindClientFunc({
clientFunc: Client4.patchBot,
onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT,
params: [
botUserId,
botPatch,
],
});
}
export function loadBot(botUserId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getBot,
@ -51,34 +29,3 @@ export function loadBots(page = 0, perPage = BOTS_PER_PAGE_DEFAULT): ActionFunc
],
});
}
export function disableBot(botUserId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.disableBot,
onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT,
params: [
botUserId,
],
});
}
export function enableBot(botUserId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.enableBot,
onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT,
params: [
botUserId,
],
});
}
export function assignBot(botUserId: string, newOwnerId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.assignBot,
onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT,
params: [
botUserId,
newOwnerId,
],
});
}

View file

@ -5,11 +5,9 @@ import assert from 'assert';
import nock from 'nock';
import * as Actions from '@mm-redux/actions/channels';
import {addUserToTeam} from '@mm-redux/actions/teams';
import {getProfilesByIds, login} from '@mm-redux/actions/users';
import {createIncomingHook, createOutgoingHook} from '@mm-redux/actions/integrations';
import {Client4} from '@mm-redux/client';
import {General, RequestStatus, Preferences, Permissions} from '../constants';
import {Client4} from '@client/rest';
import {General, RequestStatus, Preferences} from '../constants';
import {getPreferenceKey} from '@mm-redux/utils/preference_utils';
import TestHelper from 'test/test_helper';
import configureStore from 'test/test_store';
@ -450,57 +448,11 @@ describe('Actions.Channels', () => {
await store.dispatch(Actions.fetchMyChannelsAndMembers(TestHelper.basicTeam.id));
nock(Client4.getBaseRoute()).
post('/hooks/incoming').
reply(201, {
id: TestHelper.generateId(),
create_at: 1507840900004,
update_at: 1507840900004,
delete_at: 0,
user_id: TestHelper.basicUser.id,
channel_id: secondChannel.id,
team_id: TestHelper.basicTeam.id,
display_name: 'TestIncomingHook',
description: 'Some description.',
});
const incomingHook = await store.dispatch(createIncomingHook({channel_id: secondChannel.id, display_name: 'test', description: 'test'}));
nock(Client4.getBaseRoute()).
post('/hooks/outgoing').
reply(201, {
id: TestHelper.generateId(),
token: TestHelper.generateId(),
create_at: 1507841118796,
update_at: 1507841118796,
delete_at: 0,
creator_id: TestHelper.basicUser.id,
channel_id: secondChannel.id,
team_id: TestHelper.basicTeam.id,
trigger_words: ['testword'],
trigger_when: 0,
callback_urls: ['http://notarealurl'],
display_name: 'TestOutgoingHook',
description: '',
content_type: 'application/x-www-form-urlencoded',
});
const outgoingHook = await store.dispatch(createOutgoingHook({
channel_id: secondChannel.id,
team_id: TestHelper.basicTeam.id,
display_name: 'TestOutgoingHook',
trigger_words: [TestHelper.generateId()],
callback_urls: ['http://notarealurl']},
));
nock(Client4.getBaseRoute()).
delete(`/channels/${secondChannel.id}`).
reply(200, OK_RESPONSE);
await store.dispatch(Actions.deleteChannel(secondChannel.id));
const {incomingHooks, outgoingHooks} = store.getState().entities.integrations;
assert.ifError(incomingHooks[incomingHook.id]);
assert.ifError(outgoingHooks[outgoingHook.id]);
});
it('unarchiveChannel', async () => {
@ -550,57 +502,11 @@ describe('Actions.Channels', () => {
await store.dispatch(Actions.fetchMyChannelsAndMembers(TestHelper.basicTeam.id));
nock(Client4.getBaseRoute()).
post('/hooks/incoming').
reply(201, {
id: TestHelper.generateId(),
create_at: 1507840900004,
update_at: 1507840900004,
delete_at: 1609090954545,
user_id: TestHelper.basicUser.id,
channel_id: secondChannel.id,
team_id: TestHelper.basicTeam.id,
display_name: 'TestIncomingHook',
description: 'Some description.',
});
const incomingHook = await store.dispatch(createIncomingHook({channel_id: secondChannel.id, display_name: 'test', description: 'test'}));
nock(Client4.getBaseRoute()).
post('/hooks/outgoing').
reply(201, {
id: TestHelper.generateId(),
token: TestHelper.generateId(),
create_at: 1507841118796,
update_at: 1507841118796,
delete_at: 1609090954545,
creator_id: TestHelper.basicUser.id,
channel_id: secondChannel.id,
team_id: TestHelper.basicTeam.id,
trigger_words: ['testword'],
trigger_when: 0,
callback_urls: ['http://notarealurl'],
display_name: 'TestOutgoingHook',
description: '',
content_type: 'application/x-www-form-urlencoded',
});
const outgoingHook = await store.dispatch(createOutgoingHook({
channel_id: secondChannel.id,
team_id: TestHelper.basicTeam.id,
display_name: 'TestOutgoingHook',
trigger_words: [TestHelper.generateId()],
callback_urls: ['http://notarealurl']},
));
nock(Client4.getBaseRoute()).
delete(`/channels/${secondChannel.id}`).
reply(200, OK_RESPONSE);
await store.dispatch(Actions.unarchiveChannel(secondChannel.id));
const {incomingHooks, outgoingHooks} = store.getState().entities.integrations;
assert.ifError(incomingHooks[incomingHook.id]);
assert.ifError(outgoingHooks[outgoingHook.id]);
});
describe('viewChannel', () => {
@ -1615,60 +1521,6 @@ describe('Actions.Channels', () => {
assert.ok(store.getState().entities.channels.totalCount === mockTotalCount);
});
it('searchAllChannels', async () => {
const userClient = TestHelper.createClient();
nock(Client4.getBaseRoute()).
post('/users').
query(true).
reply(201, TestHelper.fakeUserWithId());
const user = await TestHelper.basicClient4.createUser(
TestHelper.fakeUser(),
null,
null,
TestHelper.basicTeam.invite_id,
);
nock(Client4.getBaseRoute()).
post('/users/login').
reply(200, user);
await userClient.login(user.email, 'password1');
nock(Client4.getBaseRoute()).
post('/channels').
reply(201, TestHelper.fakeChannelWithId(TestHelper.basicTeam.id));
const userChannel = await userClient.createChannel(
TestHelper.fakeChannel(TestHelper.basicTeam.id),
);
nock(Client4.getBaseRoute()).
post('/channels/search').
reply(200, [TestHelper.basicChannel, userChannel]);
await store.dispatch(Actions.searchAllChannels('test', 0));
const moreRequest = store.getState().requests.channels.getAllChannels;
if (moreRequest.status === RequestStatus.FAILURE) {
throw new Error(JSON.stringify(moreRequest.error));
}
nock(Client4.getBaseRoute()).
post('/channels/search').
reply(200, {channels: [TestHelper.basicChannel, userChannel], total_count: 2});
const response = await store.dispatch(Actions.searchAllChannels('test', '', false, 0, 100));
const paginatedRequest = store.getState().requests.channels.getAllChannels;
if (paginatedRequest.status === RequestStatus.FAILURE) {
throw new Error(JSON.stringify(paginatedRequest.error));
}
assert.ok(response.data.channels.length === 2);
});
it('searchArchivedChannels', async () => {
const userClient = TestHelper.createClient();
@ -1901,38 +1753,6 @@ describe('Actions.Channels', () => {
assert.ok(stats[channelId].member_count >= 1, 'incorrect member count for channel');
});
it('updateChannelMemberRoles', async () => {
nock(Client4.getBaseRoute()).
post('/users').
reply(201, TestHelper.fakeUserWithId());
const user = await TestHelper.basicClient4.createUser(TestHelper.fakeUser());
nock(Client4.getTeamsRoute()).
post(`/${TestHelper.basicChannel.id}/members`).
reply(201, {team_id: TestHelper.basicTeam.id, roles: 'channel_user', user_id: user.id});
await store.dispatch(addUserToTeam(TestHelper.basicTeam.id, user.id));
nock(Client4.getBaseRoute()).
post(`/channels/${TestHelper.basicChannel.id}/members`).
reply(201, {channel_id: TestHelper.basicChannel.id, roles: 'channel_user', user_id: user.id});
await store.dispatch(Actions.addChannelMember(TestHelper.basicChannel.id, user.id));
const roles = General.CHANNEL_USER_ROLE + ' ' + General.CHANNEL_ADMIN_ROLE;
nock(Client4.getBaseRoute()).
put(`/channels/${TestHelper.basicChannel.id}/members/${user.id}/roles`).
reply(200, {roles});
await store.dispatch(Actions.updateChannelMemberRoles(TestHelper.basicChannel.id, user.id, roles));
const members = store.getState().entities.channels.membersInChannel;
assert.ok(members[TestHelper.basicChannel.id]);
assert.ok(members[TestHelper.basicChannel.id][user.id]);
assert.ok(members[TestHelper.basicChannel.id][user.id].roles === roles);
});
it('updateChannelHeader', async () => {
nock(Client4.getBaseRoute()).
get(`/channels/${TestHelper.basicChannel.id}`).
@ -2158,73 +1978,6 @@ describe('Actions.Channels', () => {
assert.deepEqual(result, {data: [TestHelper.basicChannel]});
});
it('updateChannelScheme', async () => {
TestHelper.mockLogin();
await store.dispatch(login(TestHelper.basicChannelMember.email, 'password1'));
nock(Client4.getBaseRoute()).
post('/channels').
reply(201, TestHelper.fakeChannelWithId(TestHelper.basicTeam.id));
await store.dispatch(Actions.createChannel(TestHelper.fakeChannel(TestHelper.basicTeam.id), TestHelper.basicUser.id));
const createRequest = store.getState().requests.channels.createChannel;
if (createRequest.status === RequestStatus.FAILURE) {
throw new Error(JSON.stringify(createRequest.error));
}
const {channels} = store.getState().entities.channels;
const schemeId = 'xxxxxxxxxxxxxxxxxxxxxxxxxx';
const {id} = channels[Object.keys(channels)[0]];
nock(Client4.getBaseRoute()).
put('/channels/' + id + '/scheme').
reply(200, OK_RESPONSE);
await store.dispatch(Actions.updateChannelScheme(id, schemeId));
const updated = store.getState().entities.channels.channels[id];
assert.ok(updated);
assert.equal(updated.scheme_id, schemeId);
});
it('updateChannelMemberSchemeRoles', async () => {
TestHelper.mockLogin();
await store.dispatch(login(TestHelper.basicChannelMember.email, 'password1'));
nock(Client4.getBaseRoute()).
post('/channels').
reply(201, TestHelper.fakeChannelWithId(TestHelper.basicTeam.id));
const userId = TestHelper.basicChannelMember.id;
await store.dispatch(Actions.createChannel(TestHelper.fakeChannel(TestHelper.basicTeam.id), userId));
const {channels} = store.getState().entities.channels;
const channelId = channels[Object.keys(channels)[0]].id;
nock(Client4.getBaseRoute()).
put(`/channels/${channelId}/members/${userId}/schemeRoles`).
reply(200, OK_RESPONSE);
await store.dispatch(Actions.updateChannelMemberSchemeRoles(channelId, userId, true, true));
const update1 = store.getState().entities.channels.membersInChannel[channelId][userId];
assert.ok(update1);
assert.equal(update1.scheme_admin, true);
assert.equal(update1.scheme_user, true);
nock(Client4.getBaseRoute()).
put(`/channels/${channelId}/members/${userId}/schemeRoles`).
reply(200, OK_RESPONSE);
await store.dispatch(Actions.updateChannelMemberSchemeRoles(channelId, userId, false, false));
const update2 = store.getState().entities.channels.membersInChannel[channelId][userId];
assert.ok(update2);
assert.equal(update2.scheme_admin, false);
assert.equal(update2.scheme_user, false);
});
it('markGroupChannelOpen', async () => {
const channelId = TestHelper.generateId();
const now = new Date().getTime();
@ -2266,65 +2019,6 @@ describe('Actions.Channels', () => {
assert.deepEqual(response, data);
});
it('membersMinusGroupMembers', async () => {
const channelID = 'cid10000000000000000000000';
const groupIDs = ['gid10000000000000000000000', 'gid20000000000000000000000'];
const page = 7;
const perPage = 63;
nock(Client4.getBaseRoute()).get(
`/channels/${channelID}/members_minus_group_members?group_ids=${groupIDs.join(',')}&page=${page}&per_page=${perPage}`).
reply(200, {users: [], total_count: 0});
const {error} = await Actions.membersMinusGroupMembers(channelID, groupIDs, page, perPage)(store.dispatch, store.getState);
assert.equal(error, null);
});
it('getChannelModerations', async () => {
const channelID = 'cid10000000000000000000000';
nock(Client4.getBaseRoute()).get(
`/channels/${channelID}/moderations`).
reply(200, [{
name: Permissions.CHANNEL_MODERATED_PERMISSIONS.CREATE_POST,
roles: {
members: true,
guests: false,
},
}]);
const {error} = await store.dispatch(Actions.getChannelModerations(channelID));
const moderations = store.getState().entities.channels.channelModerations[channelID];
assert.equal(error, null);
assert.equal(moderations[0].name, Permissions.CHANNEL_MODERATED_PERMISSIONS.CREATE_POST);
assert.equal(moderations[0].roles.members, true);
assert.equal(moderations[0].roles.guests, false);
});
it('patchChannelModerations', async () => {
const channelID = 'cid10000000000000000000000';
nock(Client4.getBaseRoute()).put(
`/channels/${channelID}/moderations/patch`).
reply(200, [{
name: Permissions.CHANNEL_MODERATED_PERMISSIONS.CREATE_REACTIONS,
roles: {
members: true,
guests: false,
},
}]);
const {error} = await store.dispatch(Actions.patchChannelModerations(channelID, {}));
const moderations = store.getState().entities.channels.channelModerations[channelID];
assert.equal(error, null);
assert.equal(moderations[0].name, Permissions.CHANNEL_MODERATED_PERMISSIONS.CREATE_REACTIONS);
assert.equal(moderations[0].roles.members, true);
assert.equal(moderations[0].roles.guests, false);
});
it('getChannelMemberCountsByGroup', async () => {
const channelID = 'cid10000000000000000000000';

View file

@ -1,6 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {General, Preferences} from '../constants';
import {ChannelTypes, PreferenceTypes, TeamTypes, UserTypes} from '@mm-redux/action_types';
import {savePreferences, deletePreferences} from './preferences';
@ -17,7 +17,7 @@ import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {Action, ActionFunc, batchActions, DispatchFunc, GetStateFunc} from '@mm-redux/types/actions';
import {Channel, ChannelNotifyProps, ChannelMembership, ChannelModerationPatch} from '@mm-redux/types/channels';
import {Channel, ChannelNotifyProps, ChannelMembership} from '@mm-redux/types/channels';
import {PreferenceType} from '@mm-redux/types/preferences';
@ -1102,45 +1102,6 @@ export function searchChannels(teamId: string, term: string, archived?: boolean)
};
}
export function searchAllChannels(term: string, notAssociatedToGroup = '', excludeDefaultChannels = false, page?: number, perPage?: number): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
dispatch({type: ChannelTypes.GET_ALL_CHANNELS_REQUEST, data: null});
let response;
try {
response = await Client4.searchAllChannels(term, notAssociatedToGroup, excludeDefaultChannels, page, perPage);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(batchActions([
{type: ChannelTypes.GET_ALL_CHANNELS_FAILURE, error},
logError(error),
]));
return {error};
}
const channels = response.channels || response;
dispatch(batchActions([
{
type: ChannelTypes.RECEIVED_ALL_CHANNELS,
data: channels,
},
{
type: ChannelTypes.GET_ALL_CHANNELS_SUCCESS,
},
]));
return {data: response};
};
}
export function searchGroupChannels(term: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.searchGroupChannels,
params: [term],
});
}
export function getChannelStats(channelId: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
let stat;
@ -1220,28 +1181,6 @@ export function removeChannelMember(channelId: string, userId: string): ActionFu
};
}
export function updateChannelMemberRoles(channelId: string, userId: string, roles: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
try {
await Client4.updateChannelMemberRoles(channelId, userId, roles);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
const membersInChannel = getState().entities.channels.membersInChannel[channelId];
if (membersInChannel && membersInChannel[userId]) {
dispatch({
type: ChannelTypes.RECEIVED_CHANNEL_MEMBER,
data: {...membersInChannel[userId], roles},
});
}
return {data: true};
};
}
export function updateChannelHeader(channelId: string, header: string): ActionFunc {
return async (dispatch: DispatchFunc) => {
analytics.trackAction('action_channels_update_header', {channel_id: channelId});
@ -1461,65 +1400,6 @@ export function unfavoriteChannel(channelId: string): ActionFunc {
};
}
export function updateChannelScheme(channelId: string, schemeId: string) {
return bindClientFunc({
clientFunc: async () => {
await Client4.updateChannelScheme(channelId, schemeId);
return {channelId, schemeId};
},
onSuccess: ChannelTypes.UPDATED_CHANNEL_SCHEME,
});
}
export function updateChannelMemberSchemeRoles(channelId: string, userId: string, isSchemeUser: boolean, isSchemeAdmin: boolean) {
return bindClientFunc({
clientFunc: async () => {
await Client4.updateChannelMemberSchemeRoles(channelId, userId, isSchemeUser, isSchemeAdmin);
return {channelId, userId, isSchemeUser, isSchemeAdmin};
},
onSuccess: ChannelTypes.UPDATED_CHANNEL_MEMBER_SCHEME_ROLES,
});
}
export function membersMinusGroupMembers(channelID: string, groupIDs: Array<string>, page = 0, perPage: number = General.PROFILE_CHUNK_SIZE): ActionFunc {
return bindClientFunc({
clientFunc: Client4.channelMembersMinusGroupMembers,
onSuccess: ChannelTypes.RECEIVED_CHANNEL_MEMBERS_MINUS_GROUP_MEMBERS,
params: [
channelID,
groupIDs,
page,
perPage,
],
});
}
export function getChannelModerations(channelId: string): ActionFunc {
return bindClientFunc({
clientFunc: async () => {
const moderations = await Client4.getChannelModerations(channelId);
return {channelId, moderations};
},
onSuccess: ChannelTypes.RECEIVED_CHANNEL_MODERATIONS,
params: [
channelId,
],
});
}
export function patchChannelModerations(channelId: string, patch: Array<ChannelModerationPatch>): ActionFunc {
return bindClientFunc({
clientFunc: async () => {
const moderations = await Client4.patchChannelModerations(channelId, patch);
return {channelId, moderations};
},
onSuccess: ChannelTypes.RECEIVED_CHANNEL_MODERATIONS,
params: [
channelId,
],
});
}
export function getChannelMemberCountsByGroup(channelId: string, includeTimezones: boolean): ActionFunc {
return async (dispatch: DispatchFunc) => {
let channelMemberCountsByGroup;
@ -1562,7 +1442,6 @@ export default {
autocompleteChannels,
autocompleteChannelsForSearch,
searchChannels,
searchGroupChannels,
getChannelStats,
addChannelMember,
removeChannelMember,
@ -1572,6 +1451,4 @@ export default {
markChannelAsUnread,
favoriteChannel,
unfavoriteChannel,
membersMinusGroupMembers,
getChannelModerations,
};

View file

@ -6,7 +6,7 @@ import assert from 'assert';
import nock from 'nock';
import * as Actions from '@mm-redux/actions/emojis';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {GeneralTypes} from '@mm-redux/action_types';
import TestHelper from 'test/test_helper';

View file

@ -1,6 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {EmojiTypes} from '@mm-redux/action_types';
import {General, Emoji} from '../constants';
import {getProfilesByIds} from './users';

View file

@ -5,7 +5,7 @@ import assert from 'assert';
import nock from 'nock';
import {logError} from '@mm-redux/actions/errors';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import TestHelper from 'test/test_helper';
import configureStore from 'test/test_store';

View file

@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import {ErrorTypes} from '@mm-redux/action_types';
import {serializeError, ErrorObject} from 'serialize-error';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import EventEmitter from '@mm-redux/utils/event_emitter';
import {DispatchFunc, ActionFunc} from '@mm-redux/types/actions';
import {Error} from '@mm-redux/types/errors';

View file

@ -1,17 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import fs from 'fs';
import assert from 'assert';
import nock from 'nock';
import * as Actions from '@mm-redux/actions/files';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import TestHelper from 'test/test_helper';
import configureStore from 'test/test_store';
const FormData = require('form-data');
describe('Actions.Files', () => {
let store;
beforeAll(async () => {
@ -26,52 +23,6 @@ describe('Actions.Files', () => {
await TestHelper.tearDown();
});
it('getFilesForPost', async () => {
const {basicClient4, basicChannel} = TestHelper;
const testFileName = 'test.png';
const testImageData = fs.createReadStream(`test/assets/images/${testFileName}`);
const clientId = TestHelper.generateId();
const imageFormData = new FormData();
imageFormData.append('files', testImageData);
imageFormData.append('channel_id', basicChannel.id);
imageFormData.append('client_ids', clientId);
const formBoundary = imageFormData.getBoundary();
nock(Client4.getBaseRoute()).
post('/files').
reply(201, {file_infos: [{id: TestHelper.generateId(), user_id: TestHelper.basicUser.id, create_at: 1507921547541, update_at: 1507921547541, delete_at: 0, name: 'test.png', extension: 'png', size: 258428, mime_type: 'image/png', width: 600, height: 600, has_preview_image: true}], client_ids: [TestHelper.generateId()]});
const fileUploadResp = await basicClient4.
uploadFile(imageFormData, formBoundary);
const fileId = fileUploadResp.file_infos[0].id;
const fakePostForFile = TestHelper.fakePost(basicChannel.id);
fakePostForFile.file_ids = [fileId];
nock(Client4.getBaseRoute()).
post('/posts').
reply(201, {...TestHelper.fakePostWithId(), ...fakePostForFile});
const postForFile = await basicClient4.createPost(fakePostForFile);
nock(Client4.getBaseRoute()).
get(`/posts/${postForFile.id}/files/info`).
reply(200, [{id: fileId, user_id: TestHelper.basicUser.id, create_at: 1507921547541, update_at: 1507921547541, delete_at: 0, name: 'test.png', extension: 'png', size: 258428, mime_type: 'image/png', width: 600, height: 600, has_preview_image: true}]);
await Actions.getFilesForPost(postForFile.id)(store.dispatch, store.getState);
const {files: allFiles, fileIdsByPostId} = store.getState().entities.files;
assert.ok(allFiles);
assert.ok(allFiles[fileId]);
assert.equal(allFiles[fileId].id, fileId);
assert.equal(allFiles[fileId].name, testFileName);
assert.ok(fileIdsByPostId);
assert.ok(fileIdsByPostId[postForFile.id]);
assert.equal(fileIdsByPostId[postForFile.id][0], fileId);
});
it('getFilePublicLink', async () => {
const fileId = 't1izsr9uspgi3ynggqu6xxjn9y';
nock(Client4.getBaseRoute()).

View file

@ -1,6 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {FileTypes} from '@mm-redux/action_types';
import {DispatchFunc, GetStateFunc, ActionFunc} from '@mm-redux/types/actions';

View file

@ -7,7 +7,7 @@ import nock from 'nock';
import {FormattedError} from './helpers.ts';
import {GeneralTypes} from '@mm-redux/action_types';
import * as Actions from '@mm-redux/actions/general';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import TestHelper from 'test/test_helper';
import configureStore from 'test/test_store';

View file

@ -1,6 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {GeneralTypes} from '@mm-redux/action_types';

View file

@ -4,8 +4,8 @@
import assert from 'assert';
import {forceLogoutIfNecessary} from '@mm-redux/actions/helpers';
import {Client4} from '@mm-redux/client';
import {ClientError} from '@mm-redux/client/client4';
import {Client4} from '@client/rest';
import ClientError from '@client/rest/error';
import configureStore, {mockDispatch} from 'test/test_store';
describe('Actions.Helpers', () => {

View file

@ -3,7 +3,7 @@
import {logout} from '@actions/views/user';
import {UserTypes} from '@mm-redux/action_types';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {Client4Error} from '@mm-redux/types/client4';
import {getCurrentUserId, getUsers} from '@mm-redux/selectors/entities/users';
import {batchActions, Action, ActionFunc, GenericAction, DispatchFunc, GetStateFunc} from '@mm-redux/types/actions';

View file

@ -10,11 +10,9 @@ import * as general from './general';
import * as gifs from './gifs';
import * as helpers from './helpers';
import * as integrations from './integrations';
import * as jobs from './jobs';
import * as posts from './posts';
import * as preferences from './preferences';
import * as roles from './roles';
import * as schemes from './schemes';
import * as search from './search';
import * as teams from './teams';
import * as timezone from './timezone';
@ -30,11 +28,9 @@ export {
gifs,
integrations,
helpers,
jobs,
posts,
preferences,
roles,
schemes,
search,
teams,
timezone,

View file

@ -6,13 +6,11 @@ import nock from 'nock';
import * as Actions from '@mm-redux/actions/integrations';
import * as TeamsActions from '@mm-redux/actions/teams';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import TestHelper from 'test/test_helper';
import configureStore from 'test/test_store';
const OK_RESPONSE = {status: 'OK'};
describe('Actions.Integrations', () => {
let store;
beforeAll(async () => {
@ -27,294 +25,6 @@ describe('Actions.Integrations', () => {
await TestHelper.tearDown();
});
it('createIncomingHook', async () => {
nock(Client4.getBaseRoute()).
post('/hooks/incoming').
reply(201, TestHelper.testIncomingHook());
const {data: created} = await Actions.createIncomingHook(
{
channel_id: TestHelper.basicChannel.id,
display_name: 'test',
description: 'test',
},
)(store.dispatch, store.getState);
const state = store.getState();
const hooks = state.entities.integrations.incomingHooks;
assert.ok(hooks);
assert.ok(hooks[created.id]);
});
it('getIncomingWebhook', async () => {
nock(Client4.getBaseRoute()).
post('/hooks/incoming').
reply(201, TestHelper.testIncomingHook());
const {data: created} = await Actions.createIncomingHook(
{
channel_id: TestHelper.basicChannel.id,
display_name: 'test',
description: 'test',
},
)(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
get(`/hooks/incoming/${created.id}`).
reply(200, created);
await Actions.getIncomingHook(created.id)(store.dispatch, store.getState);
const state = store.getState();
const hooks = state.entities.integrations.incomingHooks;
assert.ok(hooks);
assert.ok(hooks[created.id]);
});
it('getIncomingWebhooks', async () => {
nock(Client4.getBaseRoute()).
post('/hooks/incoming').
reply(201, TestHelper.testIncomingHook());
const {data: created} = await Actions.createIncomingHook(
{
channel_id: TestHelper.basicChannel.id,
display_name: 'test',
description: 'test',
},
)(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
get('/hooks/incoming').
query(true).
reply(200, [created]);
await Actions.getIncomingHooks(TestHelper.basicTeam.id)(store.dispatch, store.getState);
const state = store.getState();
const hooks = state.entities.integrations.incomingHooks;
assert.ok(hooks);
assert.ok(hooks[created.id]);
});
it('removeIncomingHook', async () => {
nock(Client4.getBaseRoute()).
post('/hooks/incoming').
reply(201, TestHelper.testIncomingHook());
const {data: created} = await Actions.createIncomingHook(
{
channel_id: TestHelper.basicChannel.id,
display_name: 'test',
description: 'test',
},
)(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
delete(`/hooks/incoming/${created.id}`).
reply(200, OK_RESPONSE);
await Actions.removeIncomingHook(created.id)(store.dispatch, store.getState);
const state = store.getState();
const hooks = state.entities.integrations.incomingHooks;
assert.ok(!hooks[created.id]);
});
it('updateIncomingHook', async () => {
nock(Client4.getBaseRoute()).
post('/hooks/incoming').
reply(201, TestHelper.testIncomingHook());
const {data: created} = await Actions.createIncomingHook(
{
channel_id: TestHelper.basicChannel.id,
display_name: 'test',
description: 'test',
},
)(store.dispatch, store.getState);
const updated = {...created};
updated.display_name = 'test2';
nock(Client4.getBaseRoute()).
put(`/hooks/incoming/${created.id}`).
reply(200, updated);
await Actions.updateIncomingHook(updated)(store.dispatch, store.getState);
const state = store.getState();
const hooks = state.entities.integrations.incomingHooks;
assert.ok(hooks[created.id]);
assert.ok(hooks[created.id].display_name === updated.display_name);
});
it('createOutgoingHook', async () => {
nock(Client4.getBaseRoute()).
post('/hooks/outgoing').
reply(201, TestHelper.testOutgoingHook());
const {data: created} = await Actions.createOutgoingHook(
{
channel_id: TestHelper.basicChannel.id,
team_id: TestHelper.basicTeam.id,
display_name: 'test',
trigger_words: [TestHelper.generateId()],
callback_urls: ['http://localhost/notarealendpoint'],
},
)(store.dispatch, store.getState);
const state = store.getState();
const hooks = state.entities.integrations.outgoingHooks;
assert.ok(hooks);
assert.ok(hooks[created.id]);
});
it('getOutgoingWebhook', async () => {
nock(Client4.getBaseRoute()).
post('/hooks/outgoing').
reply(201, TestHelper.testOutgoingHook());
const {data: created} = await Actions.createOutgoingHook(
{
channel_id: TestHelper.basicChannel.id,
team_id: TestHelper.basicTeam.id,
display_name: 'test',
trigger_words: [TestHelper.generateId()],
callback_urls: ['http://localhost/notarealendpoint'],
},
)(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
get(`/hooks/outgoing/${created.id}`).
reply(200, TestHelper.testOutgoingHook());
await Actions.getOutgoingHook(created.id)(store.dispatch, store.getState);
const state = store.getState();
const hooks = state.entities.integrations.outgoingHooks;
assert.ok(hooks);
assert.ok(hooks[created.id]);
});
it('getOutgoingWebhooks', async () => {
nock(Client4.getBaseRoute()).
post('/hooks/outgoing').
reply(201, TestHelper.testOutgoingHook());
const {data: created} = await Actions.createOutgoingHook(
{
channel_id: TestHelper.basicChannel.id,
team_id: TestHelper.basicTeam.id,
display_name: 'test',
trigger_words: [TestHelper.generateId()],
callback_urls: ['http://localhost/notarealendpoint'],
},
)(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
get('/hooks/outgoing').
query(true).
reply(200, [TestHelper.testOutgoingHook()]);
await Actions.getOutgoingHooks(TestHelper.basicChannel.id)(store.dispatch, store.getState);
const state = store.getState();
const hooks = state.entities.integrations.outgoingHooks;
assert.ok(hooks);
assert.ok(hooks[created.id]);
});
it('removeOutgoingHook', async () => {
nock(Client4.getBaseRoute()).
post('/hooks/outgoing').
reply(201, TestHelper.testOutgoingHook());
const {data: created} = await Actions.createOutgoingHook(
{
channel_id: TestHelper.basicChannel.id,
team_id: TestHelper.basicTeam.id,
display_name: 'test',
trigger_words: [TestHelper.generateId()],
callback_urls: ['http://localhost/notarealendpoint'],
},
)(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
delete(`/hooks/outgoing/${created.id}`).
reply(200, OK_RESPONSE);
await Actions.removeOutgoingHook(created.id)(store.dispatch, store.getState);
const state = store.getState();
const hooks = state.entities.integrations.outgoingHooks;
assert.ok(!hooks[created.id]);
});
it('updateOutgoingHook', async () => {
nock(Client4.getBaseRoute()).
post('/hooks/outgoing').
reply(201, TestHelper.testOutgoingHook());
const {data: created} = await Actions.createOutgoingHook(
{
channel_id: TestHelper.basicChannel.id,
team_id: TestHelper.basicTeam.id,
display_name: 'test',
trigger_words: [TestHelper.generateId()],
callback_urls: ['http://localhost/notarealendpoint'],
},
)(store.dispatch, store.getState);
const updated = {...created};
updated.display_name = 'test2';
nock(Client4.getBaseRoute()).
put(`/hooks/outgoing/${created.id}`).
reply(200, updated);
await Actions.updateOutgoingHook(updated)(store.dispatch, store.getState);
const state = store.getState();
const hooks = state.entities.integrations.outgoingHooks;
assert.ok(hooks[created.id]);
assert.ok(hooks[created.id].display_name === updated.display_name);
});
it('regenOutgoingHookToken', async () => {
nock(Client4.getBaseRoute()).
post('/hooks/outgoing').
reply(201, TestHelper.testOutgoingHook());
const {data: created} = await Actions.createOutgoingHook(
{
channel_id: TestHelper.basicChannel.id,
team_id: TestHelper.basicTeam.id,
display_name: 'test',
trigger_words: [TestHelper.generateId()],
callback_urls: ['http://localhost/notarealendpoint'],
},
)(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
post(`/hooks/outgoing/${created.id}/regen_token`).
reply(200, {...created, token: TestHelper.generateId()});
await Actions.regenOutgoingHookToken(created.id)(store.dispatch, store.getState);
const state = store.getState();
const hooks = state.entities.integrations.outgoingHooks;
assert.ok(hooks[created.id]);
assert.ok(hooks[created.id].token !== created.token);
});
it('getCommands', async () => {
const noTeamCommands = store.getState().entities.integrations.commands;
const noSystemCommands = store.getState().entities.integrations.systemCommands;
@ -393,54 +103,6 @@ describe('Actions.Integrations', () => {
assert.equal(Object.keys({...teamCommands, ...systemCommands}).length, 2);
});
it('getCustomTeamCommands', async () => {
nock(Client4.getBaseRoute()).
post('/teams').
reply(201, TestHelper.fakeTeamWithId());
const {data: team} = await TeamsActions.createTeam(
TestHelper.fakeTeam(),
)(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
get('/commands').
query(true).
reply(200, []);
await Actions.getCustomTeamCommands(
team.id,
)(store.dispatch, store.getState);
const noCommands = store.getState().entities.integrations.commands;
assert.equal(Object.keys(noCommands).length, 0);
const command = TestHelper.testCommand(team.id);
nock(Client4.getBaseRoute()).
post('/commands').
reply(201, {...command, token: TestHelper.generateId(), id: TestHelper.generateId()});
const {data: created} = await Actions.addCommand(
command,
)(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
get('/commands').
query(true).
reply(200, []);
await Actions.getCustomTeamCommands(
team.id,
)(store.dispatch, store.getState);
const {commands} = store.getState().entities.integrations;
assert.ok(commands[created.id]);
assert.equal(Object.keys(commands).length, 1);
const actual = commands[created.id];
const expected = created;
assert.equal(JSON.stringify(actual), JSON.stringify(expected));
});
it('executeCommand', async () => {
nock(Client4.getBaseRoute()).
post('/teams').
@ -500,259 +162,6 @@ describe('Actions.Integrations', () => {
assert.equal(actual.url, expected.url);
});
it('regenCommandToken', async () => {
nock(Client4.getBaseRoute()).
post('/teams').
reply(201, TestHelper.fakeTeamWithId());
const {data: team} = await TeamsActions.createTeam(
TestHelper.fakeTeam(),
)(store.dispatch, store.getState);
const command = TestHelper.testCommand(team.id);
nock(Client4.getBaseRoute()).
post('/commands').
reply(201, {...command, token: TestHelper.generateId(), id: TestHelper.generateId()});
const {data: created} = await Actions.addCommand(
command,
)(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
put(`/commands/${created.id}/regen_token`).
reply(200, {...created, token: TestHelper.generateId()});
await Actions.regenCommandToken(
created.id,
)(store.dispatch, store.getState);
const {commands} = store.getState().entities.integrations;
assert.ok(commands[created.id]);
const updated = commands[created.id];
assert.equal(updated.id, created.id);
assert.notEqual(updated.token, created.token);
assert.equal(updated.create_at, created.create_at);
assert.equal(updated.update_at, created.update_at);
assert.equal(updated.delete_at, created.delete_at);
assert.equal(updated.creator_id, created.creator_id);
assert.equal(updated.team_id, created.team_id);
assert.equal(updated.trigger, created.trigger);
assert.equal(updated.method, created.method);
assert.equal(updated.username, created.username);
assert.equal(updated.icon_url, created.icon_url);
assert.equal(updated.auto_complete, created.auto_complete);
assert.equal(updated.auto_complete_desc, created.auto_complete_desc);
assert.equal(updated.auto_complete_hint, created.auto_complete_hint);
assert.equal(updated.display_name, created.display_name);
assert.equal(updated.description, created.description);
assert.equal(updated.url, created.url);
});
it('editCommand', async () => {
nock(Client4.getBaseRoute()).
post('/teams').
reply(201, TestHelper.fakeTeamWithId());
const {data: team} = await TeamsActions.createTeam(
TestHelper.fakeTeam(),
)(store.dispatch, store.getState);
const command = TestHelper.testCommand(team.id);
nock(Client4.getBaseRoute()).
post('/commands').
reply(201, {...command, token: TestHelper.generateId(), id: TestHelper.generateId()});
const {data: created} = await Actions.addCommand(
command,
)(store.dispatch, store.getState);
const expected = Object.assign({}, created);
expected.trigger = 'modified';
expected.method = 'G';
expected.username = 'modified';
expected.auto_complete = false;
nock(Client4.getBaseRoute()).
put(`/commands/${expected.id}`).
reply(200, {...expected, update_at: 123});
await Actions.editCommand(
expected,
)(store.dispatch, store.getState);
const {commands} = store.getState().entities.integrations;
assert.ok(commands[created.id]);
const actual = commands[created.id];
assert.notEqual(actual.update_at, expected.update_at);
expected.update_at = actual.update_at;
assert.equal(JSON.stringify(actual), JSON.stringify(expected));
});
it('deleteCommand', async () => {
nock(Client4.getBaseRoute()).
post('/teams').
reply(201, TestHelper.fakeTeamWithId());
const {data: team} = await TeamsActions.createTeam(
TestHelper.fakeTeam(),
)(store.dispatch, store.getState);
const command = TestHelper.testCommand(team.id);
nock(Client4.getBaseRoute()).
post('/commands').
reply(201, {...command, token: TestHelper.generateId(), id: TestHelper.generateId()});
const {data: created} = await Actions.addCommand(
command,
)(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
delete(`/commands/${created.id}`).
reply(200, OK_RESPONSE);
await Actions.deleteCommand(
created.id,
)(store.dispatch, store.getState);
const {commands} = store.getState().entities.integrations;
assert.ok(!commands[created.id]);
});
it('addOAuthApp', async () => {
nock(Client4.getBaseRoute()).
post('/oauth/apps').
reply(201, TestHelper.fakeOAuthAppWithId());
const {data: created} = await Actions.addOAuthApp(TestHelper.fakeOAuthApp())(store.dispatch, store.getState);
const {oauthApps} = store.getState().entities.integrations;
assert.ok(oauthApps[created.id]);
});
it('getOAuthApp', async () => {
nock(Client4.getBaseRoute()).
post('/oauth/apps').
reply(201, TestHelper.fakeOAuthAppWithId());
const {data: created} = await Actions.addOAuthApp(TestHelper.fakeOAuthApp())(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
get(`/oauth/apps/${created.id}`).
reply(200, created);
await Actions.getOAuthApp(created.id)(store.dispatch, store.getState);
const {oauthApps} = store.getState().entities.integrations;
assert.ok(oauthApps[created.id]);
});
it('editOAuthApp', async () => {
nock(Client4.getBaseRoute()).
post('/oauth/apps').
reply(201, TestHelper.fakeOAuthAppWithId());
const {data: created} = await Actions.addOAuthApp(TestHelper.fakeOAuthApp())(store.dispatch, store.getState);
const expected = Object.assign({}, created);
expected.name = 'modified';
expected.description = 'modified';
expected.homepage = 'https://modified.com';
expected.icon_url = 'https://modified.com/icon';
expected.callback_urls = ['https://modified.com/callback1', 'https://modified.com/callback2'];
expected.is_trusted = true;
const nockReply = Object.assign({}, expected);
nockReply.update_at += 1;
nock(Client4.getBaseRoute()).
put(`/oauth/apps/${created.id}`).reply(200, nockReply);
await Actions.editOAuthApp(expected)(store.dispatch, store.getState);
const {oauthApps} = store.getState().entities.integrations;
assert.ok(oauthApps[created.id]);
const actual = oauthApps[created.id];
assert.notEqual(actual.update_at, expected.update_at);
expected.update_at = actual.update_at;
assert.equal(JSON.stringify(actual), JSON.stringify(expected));
});
it('getOAuthApps', async () => {
nock(Client4.getBaseRoute()).
post('/oauth/apps').
reply(201, TestHelper.fakeOAuthAppWithId());
const {data: created} = await Actions.addOAuthApp(TestHelper.fakeOAuthApp())(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
get('/oauth/apps').
query(true).
reply(200, [created]);
await Actions.getOAuthApps()(store.dispatch, store.getState);
const {oauthApps} = store.getState().entities.integrations;
assert.ok(oauthApps);
});
it('getAuthorizedOAuthApps', async () => {
nock(Client4.getBaseRoute()).
post('/oauth/apps').
reply(201, TestHelper.fakeOAuthAppWithId());
const {data: created} = await Actions.addOAuthApp(TestHelper.fakeOAuthApp())(store.dispatch, store.getState);
const user = TestHelper.basicUser;
nock(Client4.getBaseRoute()).
get(`/users/${user.id}/oauth/apps/authorized`).
reply(200, [created]);
await Actions.getAuthorizedOAuthApps()(store.dispatch, store.getState);
const {oauthApps} = store.getState().entities.integrations;
assert.ok(oauthApps);
});
it('deleteOAuthApp', async () => {
nock(Client4.getBaseRoute()).
post('/oauth/apps').
reply(201, TestHelper.fakeOAuthAppWithId());
const {data: created} = await Actions.addOAuthApp(TestHelper.fakeOAuthApp())(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
delete(`/oauth/apps/${created.id}`).
reply(200, OK_RESPONSE);
await Actions.deleteOAuthApp(created.id)(store.dispatch, store.getState);
const {oauthApps} = store.getState().entities.integrations;
assert.ok(!oauthApps[created.id]);
});
it('regenOAuthAppSecret', async () => {
nock(Client4.getBaseRoute()).
post('/oauth/apps').
reply(201, TestHelper.fakeOAuthAppWithId());
const {data: created} = await Actions.addOAuthApp(TestHelper.fakeOAuthApp())(store.dispatch, store.getState);
nock(Client4.getBaseRoute()).
post(`/oauth/apps/${created.id}/regen_secret`).
reply(200, {...created, client_secret: TestHelper.generateId()});
await Actions.regenOAuthAppSecret(created.id)(store.dispatch, store.getState);
const {oauthApps} = store.getState().entities.integrations;
assert.ok(oauthApps[created.id].client_secret !== created.client_secret);
});
it('submitInteractiveDialog', async () => {
nock(Client4.getBaseRoute()).
post('/actions/dialogs/submit').

View file

@ -5,15 +5,14 @@ import {Alert} from 'react-native';
import {IntegrationTypes} from '@mm-redux/action_types';
import {General} from '../constants';
import {Client4} from '@mm-redux/client';
import {getCurrentUserId} from '@mm-redux/selectors/entities/users';
import {Client4} from '@client/rest';
import {getCurrentChannelId} from '@mm-redux/selectors/entities/channels';
import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams';
import {analytics} from '@init/analytics';
import {batchActions, DispatchFunc, GetStateFunc, ActionFunc} from '@mm-redux/types/actions';
import {Command, CommandArgs, DialogSubmission, IncomingWebhook, OAuthApp, OutgoingWebhook} from '@mm-redux/types/integrations';
import {Command, CommandArgs, DialogSubmission} from '@mm-redux/types/integrations';
import {logError} from './errors';
import {bindClientFunc, forceLogoutIfNecessary, requestSuccess, requestFailure} from './helpers';
@ -28,145 +27,6 @@ import * as DraftUtils from '@utils/draft';
import {permalinkBadTeam} from '@utils/general';
import {getURLAndMatch, tryOpenURL} from '@utils/url';
export function createIncomingHook(hook: IncomingWebhook): ActionFunc {
return bindClientFunc({
clientFunc: Client4.createIncomingWebhook,
onSuccess: [IntegrationTypes.RECEIVED_INCOMING_HOOK],
params: [
hook,
],
});
}
export function getIncomingHook(hookId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getIncomingWebhook,
onSuccess: [IntegrationTypes.RECEIVED_INCOMING_HOOK],
params: [
hookId,
],
});
}
export function getIncomingHooks(teamId = '', page = 0, perPage: number = General.PAGE_SIZE_DEFAULT): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getIncomingWebhooks,
onSuccess: [IntegrationTypes.RECEIVED_INCOMING_HOOKS],
params: [
teamId,
page,
perPage,
],
});
}
export function removeIncomingHook(hookId: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
try {
await Client4.removeIncomingWebhook(hookId);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
dispatch(batchActions([
{
type: IntegrationTypes.DELETED_INCOMING_HOOK,
data: {id: hookId},
},
]));
return {data: true};
};
}
export function updateIncomingHook(hook: IncomingWebhook): ActionFunc {
return bindClientFunc({
clientFunc: Client4.updateIncomingWebhook,
onSuccess: [IntegrationTypes.RECEIVED_INCOMING_HOOK],
params: [
hook,
],
});
}
export function createOutgoingHook(hook: OutgoingWebhook): ActionFunc {
return bindClientFunc({
clientFunc: Client4.createOutgoingWebhook,
onSuccess: [IntegrationTypes.RECEIVED_OUTGOING_HOOK],
params: [
hook,
],
});
}
export function getOutgoingHook(hookId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getOutgoingWebhook,
onSuccess: [IntegrationTypes.RECEIVED_OUTGOING_HOOK],
params: [
hookId,
],
});
}
export function getOutgoingHooks(channelId = '', teamId = '', page = 0, perPage: number = General.PAGE_SIZE_DEFAULT): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getOutgoingWebhooks,
onSuccess: [IntegrationTypes.RECEIVED_OUTGOING_HOOKS],
params: [
channelId,
teamId,
page,
perPage,
],
});
}
export function removeOutgoingHook(hookId: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
try {
await Client4.removeOutgoingWebhook(hookId);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
dispatch(batchActions([
{
type: IntegrationTypes.DELETED_OUTGOING_HOOK,
data: {id: hookId},
},
]));
return {data: true};
};
}
export function updateOutgoingHook(hook: OutgoingWebhook): ActionFunc {
return bindClientFunc({
clientFunc: Client4.updateOutgoingWebhook,
onSuccess: [IntegrationTypes.RECEIVED_OUTGOING_HOOK],
params: [
hook,
],
});
}
export function regenOutgoingHookToken(hookId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.regenOutgoingHookToken,
onSuccess: [IntegrationTypes.RECEIVED_OUTGOING_HOOK],
params: [
hookId,
],
});
}
export function getCommands(teamId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getCommandsList,
@ -206,16 +66,6 @@ export function getCommandAutocompleteSuggestions(userInput: string, teamId: str
};
}
export function getCustomTeamCommands(teamId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getCustomTeamCommands,
onSuccess: [IntegrationTypes.RECEIVED_CUSTOM_TEAM_COMMANDS],
params: [
teamId,
],
});
}
export function addCommand(command: Command): ActionFunc {
return bindClientFunc({
clientFunc: Client4.addCommand,
@ -226,16 +76,6 @@ export function addCommand(command: Command): ActionFunc {
});
}
export function editCommand(command: Command): ActionFunc {
return bindClientFunc({
clientFunc: Client4.editCommand,
onSuccess: [IntegrationTypes.RECEIVED_COMMAND],
params: [
command,
],
});
}
export function executeCommand(command: string, args: CommandArgs): ActionFunc {
return bindClientFunc({
clientFunc: Client4.executeCommand,
@ -246,154 +86,6 @@ export function executeCommand(command: string, args: CommandArgs): ActionFunc {
});
}
export function regenCommandToken(id: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
let res;
try {
res = await Client4.regenCommandToken(id);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
dispatch(batchActions([
{
type: IntegrationTypes.RECEIVED_COMMAND_TOKEN,
data: {
id,
token: res.token,
},
},
]));
return {data: true};
};
}
export function deleteCommand(id: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
try {
await Client4.deleteCommand(id);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
dispatch(batchActions([
{
type: IntegrationTypes.DELETED_COMMAND,
data: {id},
},
]));
return {data: true};
};
}
export function addOAuthApp(app: OAuthApp): ActionFunc {
return bindClientFunc({
clientFunc: Client4.createOAuthApp,
onSuccess: [IntegrationTypes.RECEIVED_OAUTH_APP],
params: [
app,
],
});
}
export function editOAuthApp(app: OAuthApp): ActionFunc {
return bindClientFunc({
clientFunc: Client4.editOAuthApp,
onSuccess: IntegrationTypes.RECEIVED_OAUTH_APP,
params: [
app,
],
});
}
export function getOAuthApps(page = 0, perPage: number = General.PAGE_SIZE_DEFAULT): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getOAuthApps,
onSuccess: [IntegrationTypes.RECEIVED_OAUTH_APPS],
params: [
page,
perPage,
],
});
}
export function getOAuthApp(appId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getOAuthApp,
onSuccess: [IntegrationTypes.RECEIVED_OAUTH_APP],
params: [
appId,
],
});
}
export function getAuthorizedOAuthApps(): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
const state = getState();
const currentUserId = getCurrentUserId(state);
let data;
try {
data = await Client4.getAuthorizedOAuthApps(currentUserId);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
return {data};
};
}
export function deauthorizeOAuthApp(clientId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.deauthorizeOAuthApp,
params: [clientId],
});
}
export function deleteOAuthApp(id: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
try {
await Client4.deleteOAuthApp(id);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
dispatch(batchActions([
{
type: IntegrationTypes.DELETED_OAUTH_APP,
data: {id},
},
]));
return {data: true};
};
}
export function regenOAuthAppSecret(appId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.regenOAuthAppSecret,
onSuccess: [IntegrationTypes.RECEIVED_OAUTH_APP],
params: [
appId,
],
});
}
export function submitInteractiveDialog(submission: DialogSubmission): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
const state = getState();

View file

@ -1,145 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import assert from 'assert';
import nock from 'nock';
import * as Actions from '@mm-redux/actions/jobs';
import {Client4} from '@mm-redux/client';
import {RequestStatus} from '../constants';
import TestHelper from 'test/test_helper';
import configureStore from 'test/test_store';
const OK_RESPONSE = {status: 'OK'};
describe('Actions.Jobs', () => {
let store;
beforeAll(async () => {
await TestHelper.initBasic(Client4);
});
beforeEach(async () => {
store = await configureStore();
});
afterAll(async () => {
await TestHelper.tearDown();
});
it('createJob', async () => {
const job = {
type: 'data_retention',
};
nock(Client4.getBaseRoute()).
post('/jobs').
reply(201, {
id: 'six4h67ja7ntdkek6g13dp3wka',
create_at: 1491399241953,
type: 'data_retention',
status: 'pending',
data: {},
});
await Actions.createJob(job)(store.dispatch, store.getState);
const state = store.getState();
const request = state.requests.jobs.createJob;
if (request.status === RequestStatus.FAILURE) {
throw new Error('createJob request failed');
}
const jobs = state.entities.jobs.jobs;
assert.ok(jobs.six4h67ja7ntdkek6g13dp3wka);
});
it('getJob', async () => {
nock(Client4.getBaseRoute()).
get('/jobs/six4h67ja7ntdkek6g13dp3wka').
reply(200, {
id: 'six4h67ja7ntdkek6g13dp3wka',
create_at: 1491399241953,
type: 'data_retention',
status: 'pending',
data: {},
});
await Actions.getJob('six4h67ja7ntdkek6g13dp3wka')(store.dispatch, store.getState);
const state = store.getState();
const request = state.requests.jobs.getJob;
if (request.status === RequestStatus.FAILURE) {
throw new Error('getJob request failed');
}
const jobs = state.entities.jobs.jobs;
assert.ok(jobs.six4h67ja7ntdkek6g13dp3wka);
});
it('cancelJob', async () => {
nock(Client4.getBaseRoute()).
post('/jobs/six4h67ja7ntdkek6g13dp3wka/cancel').
reply(200, OK_RESPONSE);
await Actions.cancelJob('six4h67ja7ntdkek6g13dp3wka')(store.dispatch, store.getState);
const state = store.getState();
const request = state.requests.jobs.cancelJob;
if (request.status === RequestStatus.FAILURE) {
throw new Error('cancelJob request failed');
}
});
it('getJobs', async () => {
nock(Client4.getBaseRoute()).
get('/jobs').
query(true).
reply(200, [{
id: 'six4h67ja7ntdkek6g13dp3wka',
create_at: 1491399241953,
type: 'data_retention',
status: 'pending',
data: {},
}]);
await Actions.getJobs()(store.dispatch, store.getState);
const state = store.getState();
const request = state.requests.jobs.getJobs;
if (request.status === RequestStatus.FAILURE) {
throw new Error('getJobs request failed');
}
const jobs = state.entities.jobs.jobs;
assert.ok(jobs.six4h67ja7ntdkek6g13dp3wka);
});
it('getJobsByType', async () => {
nock(Client4.getBaseRoute()).
get('/jobs/type/data_retention').
query(true).
reply(200, [{
id: 'six4h67ja7ntdkek6g13dp3wka',
create_at: 1491399241953,
type: 'data_retention',
status: 'pending',
data: {},
}]);
await Actions.getJobsByType('data_retention')(store.dispatch, store.getState);
const state = store.getState();
const request = state.requests.jobs.getJobs;
if (request.status === RequestStatus.FAILURE) {
throw new Error('getJobsByType request failed');
}
const jobs = state.entities.jobs.jobs;
assert.ok(jobs.six4h67ja7ntdkek6g13dp3wka);
const jobsByType = state.entities.jobs.jobsByTypeList;
assert.ok(jobsByType.data_retention);
assert.ok(jobsByType.data_retention.length === 1);
});
});

View file

@ -1,72 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {JobTypes} from '@mm-redux/action_types';
import {Client4} from '@mm-redux/client';
import {General} from '../constants';
import {ActionFunc} from '@mm-redux/types/actions';
import {JobType, Job} from '@mm-redux/types/jobs';
import {bindClientFunc} from './helpers';
export function createJob(job: Job): ActionFunc {
return bindClientFunc({
clientFunc: Client4.createJob,
onRequest: JobTypes.CREATE_JOB_REQUEST,
onSuccess: [JobTypes.RECEIVED_JOB, JobTypes.CREATE_JOB_SUCCESS],
onFailure: JobTypes.CREATE_JOB_FAILURE,
params: [
job,
],
});
}
export function getJob(id: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getJob,
onRequest: JobTypes.GET_JOB_REQUEST,
onSuccess: [JobTypes.RECEIVED_JOB, JobTypes.GET_JOB_SUCCESS],
onFailure: JobTypes.GET_JOB_FAILURE,
params: [
id,
],
});
}
export function getJobs(page = 0, perPage: number = General.JOBS_CHUNK_SIZE): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getJobs,
onRequest: JobTypes.GET_JOBS_REQUEST,
onSuccess: [JobTypes.RECEIVED_JOBS, JobTypes.GET_JOBS_SUCCESS],
onFailure: JobTypes.GET_JOBS_FAILURE,
params: [
page,
perPage,
],
});
}
export function getJobsByType(type: JobType, page = 0, perPage: number = General.JOBS_CHUNK_SIZE): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getJobsByType,
onRequest: JobTypes.GET_JOBS_REQUEST,
onSuccess: [JobTypes.RECEIVED_JOBS, JobTypes.RECEIVED_JOBS_BY_TYPE, JobTypes.GET_JOBS_SUCCESS],
onFailure: JobTypes.GET_JOBS_FAILURE,
params: [
type,
page,
perPage,
],
});
}
export function cancelJob(job: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.cancelJob,
onRequest: JobTypes.CANCEL_JOB_REQUEST,
onSuccess: JobTypes.CANCEL_JOB_SUCCESS,
onFailure: JobTypes.CANCEL_JOB_FAILURE,
params: [
job,
],
});
}

View file

@ -9,7 +9,7 @@ import * as Actions from '@mm-redux/actions/posts';
import {getChannelStats} from '@mm-redux/actions/channels';
import {login} from '@mm-redux/actions/users';
import {createCustomEmoji} from '@mm-redux/actions/emojis';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {Preferences, Posts, RequestStatus} from '../constants';
import {ChannelTypes, PostTypes} from '@mm-redux/action_types';
import TestHelper from 'test/test_helper';
@ -343,31 +343,6 @@ describe('Actions.Posts', () => {
assert.ok(!reactions[post1.id]);
});
it('getPostsUnread', async () => {
const {dispatch, getState} = store;
const channelId = TestHelper.basicChannel.id;
const post = TestHelper.fakePostWithId(channelId);
const userId = getState().entities.users.currentUserId;
const response = {
posts: {
[post.id]: post,
},
order: [post.id],
next_post_id: '',
prev_post_id: '',
};
nock(Client4.getUsersRoute()).
get(`/${userId}/channels/${channelId}/posts/unread`).
query(true).
reply(200, response);
await Actions.getPostsUnread(channelId)(dispatch, getState);
const {posts} = getState().entities.posts;
assert.ok(posts[post.id]);
});
it('getPostThread', async () => {
const channelId = TestHelper.basicChannel.id;
const post = {id: TestHelper.generateId(), channel_id: channelId, message: ''};

View file

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {General, Preferences, Posts} from '@mm-redux/constants';
import {WebsocketEvents} from '@constants';
import {PostTypes, ChannelTypes, FileTypes, IntegrationTypes} from '@mm-redux/action_types';
@ -728,33 +728,6 @@ export function getPosts(channelId: string, page = 0, perPage = Posts.POST_CHUNK
};
}
export function getPostsUnread(channelId: string) {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
const userId = getCurrentUserId(getState());
let posts;
try {
posts = await Client4.getPostsUnread(channelId, userId);
getProfilesAndStatusesForPosts(posts.posts, dispatch, getState);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
dispatch(batchActions([
receivedPosts(posts),
receivedPostsInChannel(posts, channelId, posts.next_post_id === '', posts.prev_post_id === ''),
]));
dispatch({
type: PostTypes.RECEIVED_POSTS,
data: posts,
channelId,
});
return {data: posts};
};
}
export function getPostsSince(channelId: string, since: number) {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
let posts;

View file

@ -6,7 +6,7 @@ import nock from 'nock';
import * as Actions from '@mm-redux/actions/preferences';
import {login} from '@mm-redux/actions/users';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {Preferences} from '../constants';
import TestHelper from 'test/test_helper';

View file

@ -1,6 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {Preferences} from '../constants';
import {PreferenceTypes} from '@mm-redux/action_types';
import {getMyPreferences as getMyPreferencesSelector, makeGetCategory} from '@mm-redux/selectors/entities/preferences';

View file

@ -5,7 +5,7 @@ import assert from 'assert';
import nock from 'nock';
import * as Actions from '@mm-redux/actions/roles';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {RequestStatus} from '../constants';
import TestHelper from 'test/test_helper';
@ -44,43 +44,6 @@ describe('Actions.Roles', () => {
assert.deepEqual(roles.system_admin.permissions, TestHelper.basicRoles.system_admin.permissions);
});
it('getRoleByName', async () => {
nock(Client4.getRolesRoute()).
get('/name/system_admin').
reply(200, TestHelper.basicRoles.system_admin);
await Actions.getRoleByName('system_admin')(store.dispatch, store.getState);
const state = store.getState();
const request = state.requests.roles.getRolesByNames;
const {roles} = state.entities.roles;
if (request.status === RequestStatus.FAILURE) {
throw new Error(JSON.stringify(request.error));
}
assert.equal(roles.system_admin.name, 'system_admin');
assert.deepEqual(roles.system_admin.permissions, TestHelper.basicRoles.system_admin.permissions);
});
it('getRole', async () => {
nock(Client4.getRolesRoute()).
get('/' + TestHelper.basicRoles.system_admin.id).
reply(200, TestHelper.basicRoles.system_admin);
await Actions.getRole(TestHelper.basicRoles.system_admin.id)(store.dispatch, store.getState);
const state = store.getState();
const request = state.requests.roles.getRole;
const {roles} = state.entities.roles;
if (request.status === RequestStatus.FAILURE) {
throw new Error(JSON.stringify(request.error));
}
assert.equal(roles.system_admin.name, 'system_admin');
assert.deepEqual(roles.system_admin.permissions, TestHelper.basicRoles.system_admin.permissions);
});
it('loadRolesIfNeeded', async () => {
const mock1 = nock(Client4.getRolesRoute()).
post('/names', JSON.stringify(['existing_role'])).
@ -106,14 +69,4 @@ describe('Actions.Roles', () => {
assert(!mock1.isDone());
assert(mock2.isDone());
});
it('editRole', async () => {
const roleId = TestHelper.basicRoles.system_admin.id;
const mock = nock(Client4.getRolesRoute()).
put('/' + roleId + '/patch', JSON.stringify({id: roleId, test: 'test'})).
reply(200, {});
await Actions.editRole({id: roleId, test: 'test'})(store.dispatch, store.state);
assert(mock.isDone());
});
});

View file

@ -1,62 +1,22 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {RoleTypes} from '@mm-redux/action_types';
import {getRoles} from '@mm-redux/selectors/entities/roles_helpers';
import {DispatchFunc, GetStateFunc, ActionFunc} from '@mm-redux/types/actions';
import {Role} from '@mm-redux/types/roles';
import {bindClientFunc} from './helpers';
export function getRolesByNames(rolesNames: Array<string>) {
return bindClientFunc({
clientFunc: Client4.getRolesByNames,
onRequest: RoleTypes.ROLES_BY_NAMES_REQUEST,
onSuccess: [RoleTypes.RECEIVED_ROLES, RoleTypes.ROLES_BY_NAMES_SUCCESS],
onFailure: RoleTypes.ROLES_BY_NAMES_FAILURE,
params: [
rolesNames,
],
});
}
export function getRoleByName(roleName: string) {
return bindClientFunc({
clientFunc: Client4.getRoleByName,
onRequest: RoleTypes.ROLE_BY_NAME_REQUEST,
onSuccess: [RoleTypes.RECEIVED_ROLE, RoleTypes.ROLE_BY_NAME_SUCCESS],
onFailure: RoleTypes.ROLE_BY_NAME_FAILURE,
params: [
roleName,
],
});
}
export function getRole(roleId: string) {
return bindClientFunc({
clientFunc: Client4.getRole,
onRequest: RoleTypes.ROLE_BY_ID_REQUEST,
onSuccess: [RoleTypes.RECEIVED_ROLE, RoleTypes.ROLE_BY_ID_SUCCESS],
onFailure: RoleTypes.ROLE_BY_ID_FAILURE,
params: [
roleId,
],
});
}
export function editRole(role: Role) {
return bindClientFunc({
clientFunc: Client4.patchRole,
onRequest: RoleTypes.EDIT_ROLE_REQUEST,
onSuccess: [RoleTypes.RECEIVED_ROLE, RoleTypes.EDIT_ROLE_SUCCESS],
onFailure: RoleTypes.EDIT_ROLE_FAILURE,
params: [
role.id,
role,
],
});
}
export function loadRolesIfNeeded(roles: Iterable<string>): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
const state = getState();

View file

@ -1,104 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import assert from 'assert';
import nock from 'nock';
import * as Actions from '@mm-redux/actions/schemes';
import {Client4} from '@mm-redux/client';
import TestHelper from 'test/test_helper';
import configureStore from 'test/test_store';
describe('Actions.Schemes', () => {
let store;
beforeAll(async () => {
await TestHelper.initBasic(Client4);
});
beforeEach(async () => {
store = await configureStore();
});
afterAll(async () => {
await TestHelper.tearDown();
});
it('getSchemes', async () => {
const mockScheme = TestHelper.basicScheme;
nock(Client4.getBaseRoute()).
get('/schemes').
query(true).
reply(200, [mockScheme]);
await Actions.getSchemes()(store.dispatch, store.getState);
const {schemes} = store.getState().entities.schemes;
assert.ok(Object.keys(schemes).length > 0);
});
it('createScheme', async () => {
const mockScheme = TestHelper.basicScheme;
nock(Client4.getBaseRoute()).
post('/schemes').
reply(201, mockScheme);
await Actions.createScheme(TestHelper.mockScheme())(store.dispatch, store.getState);
const {schemes} = store.getState().entities.schemes;
const schemeId = Object.keys(schemes)[0];
assert.strictEqual(Object.keys(schemes).length, 1);
assert.strictEqual(mockScheme.id, schemeId);
});
it('getScheme', async () => {
nock(Client4.getBaseRoute()).
get('/schemes/' + TestHelper.basicScheme.id).
reply(200, TestHelper.basicScheme);
await Actions.getScheme(TestHelper.basicScheme.id)(store.dispatch, store.getState);
const state = store.getState();
const {schemes} = state.entities.schemes;
assert.equal(schemes[TestHelper.basicScheme.id].name, TestHelper.basicScheme.name);
});
it('patchScheme', async () => {
const patchData = {name: 'The Updated Scheme', description: 'This is a scheme created by unit tests'};
const scheme = {
...TestHelper.basicScheme,
...patchData,
};
nock(Client4.getBaseRoute()).
put('/schemes/' + TestHelper.basicScheme.id + '/patch').
reply(200, scheme);
await Actions.patchScheme(TestHelper.basicScheme.id, scheme)(store.dispatch, store.getState);
const state = store.getState();
const {schemes} = state.entities.schemes;
const updated = schemes[TestHelper.basicScheme.id];
assert.ok(updated);
assert.strictEqual(updated.name, patchData.name);
assert.strictEqual(updated.description, patchData.description);
});
it('deleteScheme', async () => {
nock(Client4.getBaseRoute()).
delete('/schemes/' + TestHelper.basicScheme.id).
reply(200, {status: 'OK'});
await Actions.deleteScheme(TestHelper.basicScheme.id)(store.dispatch, store.getState);
const state = store.getState();
const {schemes} = state.entities.schemes;
assert.notStrictEqual(schemes, {});
});
});

View file

@ -1,95 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mm-redux/client';
import {SchemeTypes} from '@mm-redux/action_types';
import {General} from '../constants';
import {Scheme, SchemeScope, SchemePatch} from '@mm-redux/types/schemes';
import {ActionFunc, DispatchFunc, GetStateFunc} from '@mm-redux/types/actions';
import {bindClientFunc, forceLogoutIfNecessary} from './helpers';
import {logError} from './errors';
export function getScheme(schemeId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getScheme,
onSuccess: [SchemeTypes.RECEIVED_SCHEME],
params: [
schemeId,
],
});
}
export function getSchemes(scope: SchemeScope, page = 0, perPage: number = General.PAGE_SIZE_DEFAULT): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getSchemes,
onSuccess: [SchemeTypes.RECEIVED_SCHEMES],
params: [
scope,
page,
perPage,
],
});
}
export function createScheme(scheme: Scheme): ActionFunc {
return bindClientFunc({
clientFunc: Client4.createScheme,
onSuccess: [SchemeTypes.CREATED_SCHEME],
params: [
scheme,
],
});
}
export function deleteScheme(schemeId: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
let data = null;
try {
data = await Client4.deleteScheme(schemeId);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
dispatch({type: SchemeTypes.DELETED_SCHEME, data: {schemeId}});
return {data};
};
}
export function patchScheme(schemeId: string, scheme: SchemePatch): ActionFunc {
return bindClientFunc({
clientFunc: Client4.patchScheme,
onSuccess: [SchemeTypes.PATCHED_SCHEME],
params: [
schemeId,
scheme,
],
});
}
export function getSchemeTeams(schemeId: string, page = 0, perPage: number = General.PAGE_SIZE_DEFAULT): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getSchemeTeams,
onSuccess: [SchemeTypes.RECEIVED_SCHEME_TEAMS],
params: [
schemeId,
page,
perPage,
],
});
}
export function getSchemeChannels(schemeId: string, page = 0, perPage: number = General.PAGE_SIZE_DEFAULT): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getSchemeChannels,
onSuccess: [SchemeTypes.RECEIVED_SCHEME_CHANNELS],
params: [
schemeId,
page,
perPage,
],
});
}

View file

@ -5,7 +5,7 @@ import assert from 'assert';
import nock from 'nock';
import * as Actions from '@mm-redux/actions/search';
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import TestHelper from 'test/test_helper';
import configureStore from 'test/test_store';

View file

@ -1,6 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {SearchTypes} from '@mm-redux/action_types';
import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams';
import {getCurrentUserId, getCurrentUserMentionKeys} from '@mm-redux/selectors/entities/users';

View file

@ -3,12 +3,11 @@
import assert from 'assert';
import nock from 'nock';
import fs from 'fs';
import * as Actions from '@mm-redux/actions/teams';
import {login} from '@mm-redux/actions/users';
import {Client4} from '@mm-redux/client';
import {General, RequestStatus} from '../constants';
import {Client4} from '@client/rest';
import {RequestStatus} from '../constants';
import {GeneralTypes} from '@mm-redux/action_types';
import TestHelper from 'test/test_helper';
import configureStore from 'test/test_store';
@ -336,19 +335,6 @@ describe('Actions.Teams', () => {
assert.ok(Object.prototype.hasOwnProperty.call(member, 'mention_count'));
});
it('getTeamMembersForUser', async () => {
nock(Client4.getUserRoute(TestHelper.basicUser.id)).
get('/teams/members').
reply(200, [{user_id: TestHelper.basicUser.id, team_id: TestHelper.basicTeam.id}]);
await Actions.getTeamMembersForUser(TestHelper.basicUser.id)(store.dispatch, store.getState);
const membersInTeam = store.getState().entities.teams.membersInTeam;
assert.ok(membersInTeam);
assert.ok(membersInTeam[TestHelper.basicTeam.id]);
assert.ok(membersInTeam[TestHelper.basicTeam.id][TestHelper.basicUser.id]);
});
it('getTeamMember', async () => {
nock(Client4.getBaseRoute()).
post('/users').
@ -406,44 +392,6 @@ describe('Actions.Teams', () => {
assert.ok(membersInTeam[TestHelper.basicTeam.id][user2.id]);
});
it('getTeamMembersByIds', async () => {
nock(Client4.getBaseRoute()).
post('/users').
query(true).
reply(201, TestHelper.fakeUserWithId());
const user1 = await TestHelper.basicClient4.createUser(
TestHelper.fakeUser(),
null,
null,
TestHelper.basicTeam.invite_id,
);
nock(Client4.getBaseRoute()).
post('/users').
query(true).
reply(201, TestHelper.fakeUserWithId());
const user2 = await TestHelper.basicClient4.createUser(
TestHelper.fakeUser(),
null,
null,
TestHelper.basicTeam.invite_id,
);
nock(Client4.getBaseRoute()).
post(`/teams/${TestHelper.basicTeam.id}/members/ids`).
reply(200, [{user_id: user1.id, team_id: TestHelper.basicTeam.id}, {user_id: user2.id, team_id: TestHelper.basicTeam.id}]);
await Actions.getTeamMembersByIds(
TestHelper.basicTeam.id,
[user1.id, user2.id],
)(store.dispatch, store.getState);
const members = store.getState().entities.teams.membersInTeam;
assert.ok(members[TestHelper.basicTeam.id]);
assert.ok(members[TestHelper.basicTeam.id][user1.id]);
assert.ok(members[TestHelper.basicTeam.id][user2.id]);
});
it('getTeamStats', async () => {
nock(Client4.getTeamRoute(TestHelper.basicTeam.id)).
get('/stats').
@ -475,33 +423,6 @@ describe('Actions.Teams', () => {
assert.ok(members[TestHelper.basicTeam.id][user.id]);
});
it('addUsersToTeam', async () => {
nock(Client4.getBaseRoute()).
post('/users').
reply(201, TestHelper.fakeUserWithId());
const user = await TestHelper.basicClient4.createUser(TestHelper.fakeUser());
nock(Client4.getBaseRoute()).
post('/users').
reply(201, TestHelper.fakeUserWithId());
const user2 = await TestHelper.basicClient4.createUser(TestHelper.fakeUser());
nock(Client4.getTeamRoute(TestHelper.basicTeam.id)).
post('/members/batch').
reply(201, [{user_id: user.id, team_id: TestHelper.basicTeam.id}, {user_id: user2.id, team_id: TestHelper.basicTeam.id}]);
await Actions.addUsersToTeam(TestHelper.basicTeam.id, [user.id, user2.id])(store.dispatch, store.getState);
const members = store.getState().entities.teams.membersInTeam;
const profilesInTeam = store.getState().entities.users.profilesInTeam;
assert.ok(members[TestHelper.basicTeam.id]);
assert.ok(members[TestHelper.basicTeam.id][user.id]);
assert.ok(members[TestHelper.basicTeam.id][user2.id]);
assert.ok(profilesInTeam[TestHelper.basicTeam.id]);
assert.ok(profilesInTeam[TestHelper.basicTeam.id].has(user.id));
assert.ok(profilesInTeam[TestHelper.basicTeam.id].has(user2.id));
});
describe('removeUserFromTeam', () => {
const team = {id: 'team'};
const user = {id: 'user'};
@ -597,174 +518,4 @@ describe('Actions.Teams', () => {
expect(state.entities.channels.currentChannelId).toBe('');
});
});
it('updateTeamMemberRoles', async () => {
nock(Client4.getBaseRoute()).
post('/users').
reply(201, TestHelper.fakeUserWithId());
const user = await TestHelper.basicClient4.createUser(TestHelper.fakeUser());
nock(Client4.getTeamRoute(TestHelper.basicTeam.id)).
post('/members').
reply(201, {user_id: user.id, team_id: TestHelper.basicTeam.id});
await Actions.addUserToTeam(TestHelper.basicTeam.id, user.id)(store.dispatch, store.getState);
const roles = General.TEAM_USER_ROLE + ' ' + General.TEAM_ADMIN_ROLE;
nock(Client4.getBaseRoute()).
put(`/teams/${TestHelper.basicTeam.id}/members/${user.id}/roles`).
reply(200, {user_id: user.id, team_id: TestHelper.basicTeam.id, roles});
await Actions.updateTeamMemberRoles(TestHelper.basicTeam.id, user.id, roles)(store.dispatch, store.getState);
const members = store.getState().entities.teams.membersInTeam;
assert.ok(members[TestHelper.basicTeam.id]);
assert.ok(members[TestHelper.basicTeam.id][user.id]);
assert.ok(members[TestHelper.basicTeam.id][user.id].roles === roles);
});
it('sendEmailInvitesToTeam', async () => {
nock(Client4.getTeamRoute(TestHelper.basicTeam.id)).
post('/invite/email').
reply(200, OK_RESPONSE);
const {data} = await Actions.sendEmailInvitesToTeam(TestHelper.basicTeam.id, ['fakeemail1@example.com', 'fakeemail2@example.com'])(store.dispatch, store.getState);
assert.deepEqual(data, OK_RESPONSE);
});
it('checkIfTeamExists', async () => {
nock(Client4.getBaseRoute()).
get(`/teams/name/${TestHelper.basicTeam.name}/exists`).
reply(200, {exists: true});
let {data: exists} = await Actions.checkIfTeamExists(TestHelper.basicTeam.name)(store.dispatch, store.getState);
assert.ok(exists === true);
nock(Client4.getBaseRoute()).
get('/teams/name/junk/exists').
reply(200, {exists: false});
const {data} = await Actions.checkIfTeamExists('junk')(store.dispatch, store.getState);
exists = data;
assert.ok(exists === false);
});
it('setTeamIcon', async () => {
TestHelper.mockLogin();
await login(TestHelper.basicUser.email, 'password1')(store.dispatch, store.getState);
const team = TestHelper.basicTeam;
const imageData = fs.createReadStream('test/assets/images/test.png');
nock(Client4.getTeamRoute(team.id)).
post('/image').
reply(200, OK_RESPONSE);
const {data} = await Actions.setTeamIcon(team.id, imageData)(store.dispatch, store.getState);
assert.deepEqual(data, OK_RESPONSE);
});
it('removeTeamIcon', async () => {
TestHelper.mockLogin();
await login(TestHelper.basicUser.email, 'password1')(store.dispatch, store.getState);
const team = TestHelper.basicTeam;
nock(Client4.getTeamRoute(team.id)).
delete('/image').
reply(200, OK_RESPONSE);
const {data} = await Actions.removeTeamIcon(team.id)(store.dispatch, store.getState);
assert.deepEqual(data, OK_RESPONSE);
});
it('updateTeamScheme', async () => {
TestHelper.mockLogin();
await login(TestHelper.basicUser.email, 'password1')(store.dispatch, store.getState);
const schemeId = 'xxxxxxxxxxxxxxxxxxxxxxxxxx';
const {id} = TestHelper.basicTeam;
nock(Client4.getBaseRoute()).
put('/teams/' + id + '/scheme').
reply(200, OK_RESPONSE);
await Actions.updateTeamScheme(id, schemeId)(store.dispatch, store.getState);
const state = store.getState();
const {teams} = state.entities.teams;
const updated = teams[id];
assert.ok(updated);
assert.equal(updated.scheme_id, schemeId);
});
it('membersMinusGroupMembers', async () => {
const teamID = 'tid10000000000000000000000';
const groupIDs = ['gid10000000000000000000000', 'gid20000000000000000000000'];
const page = 4;
const perPage = 63;
nock(Client4.getBaseRoute()).get(
`/teams/${teamID}/members_minus_group_members?group_ids=${groupIDs.join(',')}&page=${page}&per_page=${perPage}`).
reply(200, {users: [], total_count: 0});
const {error} = await Actions.membersMinusGroupMembers(teamID, groupIDs, page, perPage)(store.dispatch, store.getState);
assert.equal(error, null);
});
it('searchTeams', async () => {
const userClient = TestHelper.createClient();
nock(Client4.getBaseRoute()).
post('/users').
query(true).
reply(201, TestHelper.fakeUserWithId());
const user = await TestHelper.basicClient4.createUser(
TestHelper.fakeUser(),
null,
null,
TestHelper.basicTeam.invite_id,
);
nock(Client4.getBaseRoute()).
post('/users/login').
reply(200, user);
await userClient.login(user.email, 'password1');
nock(Client4.getBaseRoute()).
post('/teams').
reply(201, TestHelper.fakeTeamWithId());
const userTeam = await userClient.createTeam(
TestHelper.fakeTeam(),
);
nock(Client4.getBaseRoute()).
post('/teams/search').
reply(200, [TestHelper.basicTeam, userTeam]);
await store.dispatch(Actions.searchTeams('test', 0));
const moreRequest = store.getState().requests.teams.getTeams;
if (moreRequest.status === RequestStatus.FAILURE) {
throw new Error(JSON.stringify(moreRequest.error));
}
nock(Client4.getBaseRoute()).
post('/teams/search').
reply(200, {teams: [TestHelper.basicTeam, userTeam], total_count: 2});
const response = await store.dispatch(Actions.searchTeams('test', '', false, true));
const paginatedRequest = store.getState().requests.teams.getTeams;
if (paginatedRequest.status === RequestStatus.FAILURE) {
throw new Error(JSON.stringify(paginatedRequest.error));
}
assert.ok(response.data.teams.length === 2);
});
});

View file

@ -1,6 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mm-redux/client';
import {Client4} from '@client/rest';
import {General} from '../constants';
import {ChannelTypes, RoleTypes, TeamTypes, UserTypes} from '@mm-redux/action_types';
import EventEmitter from '@mm-redux/utils/event_emitter';
@ -12,14 +12,13 @@ import {getCurrentUserId} from '@mm-redux/selectors/entities/users';
import {GetStateFunc, DispatchFunc, ActionFunc, ActionResult, batchActions, Action} from '@mm-redux/types/actions';
import {Team, TeamMembership, TeamMemberWithError} from '@mm-redux/types/teams';
import {Team} from '@mm-redux/types/teams';
import {selectChannel} from './channels';
import {logError} from './errors';
import {bindClientFunc, forceLogoutIfNecessary} from './helpers';
import {getProfilesByIds, getStatusesByIds} from './users';
import {loadRolesIfNeeded} from './roles';
import {UserProfile} from '@mm-redux/types/users';
async function getProfilesAndStatusesForMembers(userIds: string[], dispatch: DispatchFunc, getState: GetStateFunc) {
const {
@ -103,13 +102,10 @@ export function getTeams(page = 0, perPage: number = General.TEAMS_CHUNK_SIZE, i
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
let data;
dispatch({type: TeamTypes.GET_TEAMS_REQUEST, data});
try {
data = await Client4.getTeams(page, perPage, includeTotalCount);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch({type: TeamTypes.GET_TEAMS_FAILURE, data});
dispatch(logError(error));
return {error};
}
@ -117,7 +113,7 @@ export function getTeams(page = 0, perPage: number = General.TEAMS_CHUNK_SIZE, i
const actions: Action[] = [
{
type: TeamTypes.RECEIVED_TEAMS_LIST,
data: includeTotalCount ? data.teams : data,
data: (includeTotalCount ? data.teams : data),
},
{
type: TeamTypes.GET_TEAMS_SUCCESS,
@ -138,38 +134,6 @@ export function getTeams(page = 0, perPage: number = General.TEAMS_CHUNK_SIZE, i
};
}
export function searchTeams(term: string, page?: number, perPage?: number): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
dispatch({type: TeamTypes.GET_TEAMS_REQUEST, data: null});
let response;
try {
response = await Client4.searchTeams(term, page, perPage);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(batchActions([
{type: TeamTypes.GET_TEAMS_FAILURE, error},
logError(error),
]));
return {error};
}
const teams = response.teams || response;
dispatch(batchActions([
{
type: TeamTypes.RECEIVED_TEAMS_LIST,
data: teams,
},
{
type: TeamTypes.GET_TEAMS_SUCCESS,
},
], 'BATCH_SEARCH_TEAMS'));
return {data: response};
};
}
export function createTeam(team: Team): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
let created;
@ -263,16 +227,6 @@ export function patchTeam(team: Team): ActionFunc {
});
}
export function regenerateTeamInviteId(teamId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.regenerateTeamInviteId,
onSuccess: TeamTypes.REGENERATED_TEAM_INVITE_ID,
params: [
teamId,
],
});
}
export function getMyTeamMembers(): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
const getMyTeamMembersFunc = bindClientFunc({
@ -301,9 +255,7 @@ export function getMyTeamMembers(): ActionFunc {
export function getTeamMembers(teamId: string, page = 0, perPage: number = General.TEAMS_CHUNK_SIZE): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getTeamMembers,
onRequest: TeamTypes.GET_TEAM_MEMBERS_REQUEST,
onSuccess: [TeamTypes.RECEIVED_MEMBERS_IN_TEAM, TeamTypes.GET_TEAM_MEMBERS_SUCCESS],
onFailure: TeamTypes.GET_TEAM_MEMBERS_FAILURE,
onSuccess: TeamTypes.RECEIVED_MEMBERS_IN_TEAM,
params: [
teamId,
page,
@ -336,30 +288,6 @@ export function getTeamMember(teamId: string, userId: string): ActionFunc {
};
}
export function getTeamMembersByIds(teamId: string, userIds: Array<string>): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
let members;
try {
const membersRequest = Client4.getTeamMembersByIds(teamId, userIds);
getProfilesAndStatusesForMembers(userIds, dispatch, getState);
members = await membersRequest;
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
dispatch({
type: TeamTypes.RECEIVED_MEMBERS_IN_TEAM,
data: members,
});
return {data: members};
};
}
export function getTeamsForUser(userId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getTeamsForUser,
@ -372,16 +300,6 @@ export function getTeamsForUser(userId: string): ActionFunc {
});
}
export function getTeamMembersForUser(userId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getTeamMembersForUser,
onSuccess: TeamTypes.RECEIVED_TEAM_MEMBERS,
params: [
userId,
],
});
}
export function getTeamStats(teamId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getTeamStats,
@ -392,19 +310,6 @@ export function getTeamStats(teamId: string): ActionFunc {
});
}
export function addUserToTeamFromInvite(token: string, inviteId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.addToTeamFromInvite,
onRequest: TeamTypes.ADD_TO_TEAM_FROM_INVITE_REQUEST,
onSuccess: TeamTypes.ADD_TO_TEAM_FROM_INVITE_SUCCESS,
onFailure: TeamTypes.ADD_TO_TEAM_FROM_INVITE_FAILURE,
params: [
token,
inviteId,
],
});
}
export function addUserToTeam(teamId: string, userId: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
let member;
@ -458,66 +363,6 @@ export function addUserToTeam(teamId: string, userId: string): ActionFunc {
};
}
export function addUsersToTeam(teamId: string, userIds: Array<string>): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
let members;
try {
members = await Client4.addUsersToTeam(teamId, userIds);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
const profiles: Partial<UserProfile>[] = [];
members.forEach((m: TeamMembership) => profiles.push({id: m.user_id}));
dispatch(batchActions([
{
type: UserTypes.RECEIVED_PROFILES_LIST_IN_TEAM,
data: profiles,
id: teamId,
},
{
type: TeamTypes.RECEIVED_MEMBERS_IN_TEAM,
data: members,
},
], 'BATCH_ADD_USERS_TO_TEAM'));
return {data: members};
};
}
export function addUsersToTeamGracefully(teamId: string, userIds: Array<string>): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
let result: Array<TeamMemberWithError>;
try {
result = await Client4.addUsersToTeamGracefully(teamId, userIds);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
const addedMembers = result ? result.filter((m) => !m.error) : [];
const profiles: Partial<UserProfile>[] = addedMembers.map((m) => ({id: m.user_id}));
const members = addedMembers.map((m) => m.member);
dispatch(batchActions([
{
type: UserTypes.RECEIVED_PROFILES_LIST_IN_TEAM,
data: profiles,
id: teamId,
},
{
type: TeamTypes.RECEIVED_MEMBERS_IN_TEAM,
data: members,
},
], 'BATCH_ADD_USERS_TO_TEAM_GRACEFULLY'));
return {data: result};
};
}
export function removeUserFromTeam(teamId: string, userId: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
try {
@ -572,98 +417,6 @@ export function removeUserFromTeam(teamId: string, userId: string): ActionFunc {
};
}
export function updateTeamMemberRoles(teamId: string, userId: string, roles: string[]): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
try {
await Client4.updateTeamMemberRoles(teamId, userId, roles);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
const membersInTeam = getState().entities.teams.membersInTeam[teamId];
if (membersInTeam && membersInTeam[userId]) {
dispatch({
type: TeamTypes.RECEIVED_MEMBER_IN_TEAM,
data: {...membersInTeam[userId], roles},
});
}
return {data: true};
};
}
export function sendEmailInvitesToTeam(teamId: string, emails: Array<string>): ActionFunc {
return bindClientFunc({
clientFunc: Client4.sendEmailInvitesToTeam,
params: [
teamId,
emails,
],
});
}
export function sendEmailGuestInvitesToChannels(teamId: string, channelIds: Array<string>, emails: Array<string>, message: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.sendEmailGuestInvitesToChannels,
params: [
teamId,
channelIds,
emails,
message,
],
});
}
export function sendEmailInvitesToTeamGracefully(teamId: string, emails: Array<string>): ActionFunc {
return bindClientFunc({
clientFunc: Client4.sendEmailInvitesToTeamGracefully,
params: [
teamId,
emails,
],
});
}
export function sendEmailGuestInvitesToChannelsGracefully(teamId: string, channelIds: Array<string>, emails: Array<string>, message: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.sendEmailGuestInvitesToChannelsGracefully,
params: [
teamId,
channelIds,
emails,
message,
],
});
}
export function getTeamInviteInfo(inviteId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.getTeamInviteInfo,
onRequest: TeamTypes.TEAM_INVITE_INFO_REQUEST,
onSuccess: TeamTypes.TEAM_INVITE_INFO_SUCCESS,
onFailure: TeamTypes.TEAM_INVITE_INFO_FAILURE,
params: [
inviteId,
],
});
}
export function checkIfTeamExists(teamName: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
let data;
try {
data = await Client4.checkIfTeamExists(teamName);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(logError(error));
return {error};
}
return {data: data.exists};
};
}
export function joinTeam(inviteId: string, teamId: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
dispatch({type: TeamTypes.JOIN_TEAM_REQUEST, data: null});
@ -696,66 +449,3 @@ export function joinTeam(inviteId: string, teamId: string): ActionFunc {
return {data: true};
};
}
export function setTeamIcon(teamId: string, imageData: any): ActionFunc {
return bindClientFunc({
clientFunc: Client4.setTeamIcon,
params: [
teamId,
imageData,
],
});
}
export function removeTeamIcon(teamId: string): ActionFunc {
return bindClientFunc({
clientFunc: Client4.removeTeamIcon,
params: [
teamId,
],
});
}
export function updateTeamScheme(teamId: string, schemeId: string): ActionFunc {
return bindClientFunc({
clientFunc: async () => {
await Client4.updateTeamScheme(teamId, schemeId);
return {teamId, schemeId};
},
onSuccess: TeamTypes.UPDATED_TEAM_SCHEME,
});
}
export function updateTeamMemberSchemeRoles(
teamId: string,
userId: string,
isSchemeUser: boolean,
isSchemeAdmin: boolean,
): ActionFunc {
return bindClientFunc({
clientFunc: async () => {
await Client4.updateTeamMemberSchemeRoles(teamId, userId, isSchemeUser, isSchemeAdmin);
return {teamId, userId, isSchemeUser, isSchemeAdmin};
},
onSuccess: TeamTypes.UPDATED_TEAM_MEMBER_SCHEME_ROLES,
});
}
export function invalidateAllEmailInvites(): ActionFunc {
return bindClientFunc({
clientFunc: Client4.invalidateAllEmailInvites,
});
}
export function membersMinusGroupMembers(teamID: string, groupIDs: Array<string>, page = 0, perPage: number = General.PROFILE_CHUNK_SIZE): ActionFunc {
return bindClientFunc({
clientFunc: Client4.teamMembersMinusGroupMembers,
onSuccess: TeamTypes.RECEIVED_TEAM_MEMBERS_MINUS_GROUP_MEMBERS,
params: [
teamID,
groupIDs,
page,
perPage,
],
});
}

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