Skip to content
This repository has been archived by the owner on Mar 13, 2024. It is now read-only.

Commit

Permalink
Hide bots and apps UI when managed by Apps Framework (#7850)
Browse files Browse the repository at this point in the history
* Hide bots and apps UI when managed by Apps Framework

* Make OAuthAppsIDs into OAuthAppIDs

* don't hide bot account management controls
  • Loading branch information
larkox authored and hanzei committed Apr 30, 2021
1 parent ad70d39 commit 622c8f7
Show file tree
Hide file tree
Showing 21 changed files with 467 additions and 31 deletions.
6 changes: 5 additions & 1 deletion actions/integration_actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import request from 'superagent';
import * as IntegrationActions from 'mattermost-redux/actions/integrations';
import {getProfilesByIds} from 'mattermost-redux/actions/users';
import {getUser} from 'mattermost-redux/selectors/entities/users';
import {appsEnabled} from 'mattermost-redux/selectors/entities/apps';

const DEFAULT_PAGE_SIZE = 100;

Expand Down Expand Up @@ -97,7 +98,10 @@ export function loadProfilesForCommands(commands) {
}

export function loadOAuthAppsAndProfiles(page = 0, perPage = DEFAULT_PAGE_SIZE) {
return async (dispatch) => {
return async (dispatch, getState) => {
if (appsEnabled(getState())) {
dispatch(IntegrationActions.getAppsOAuthAppIDs());
}
const {data} = await dispatch(IntegrationActions.getOAuthApps(page, perPage));
if (data) {
dispatch(loadProfilesForOAuthApps(data));
Expand Down
51 changes: 51 additions & 0 deletions components/integrations/bots/bot.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ describe('components/integrations/bots/Bot', () => {
accessTokens={{}}
team={team}
actions={actions}
fromApp={false}
/>,
);

Expand Down Expand Up @@ -67,6 +68,52 @@ describe('components/integrations/bots/Bot', () => {
)).toEqual(false);
});

it('app bot', () => {
const bot = UtilsTestHelper.getBotMock({user_id: '1'});
const user = UtilsTestHelper.getUserMock({id: bot.user_id});
const wrapper = shallow(
<Bot
bot={bot}
user={user}
owner={undefined}
accessTokens={{}}
team={team}
actions={actions}
fromApp={true}
/>,
);

expect(wrapper.contains(bot.display_name + ' (@' + bot.username + ')')).toEqual(true);
expect(wrapper.contains(<Markdown message={bot.description}/>)).toEqual(true);
expect(wrapper.contains('Apps Framework')).toEqual(true);

// if bot managed by plugin, remove ability to edit from UI
expect(wrapper.contains(
<FormattedMessage
id='bot.manage.create_token'
defaultMessage='Create New Token'
/>,
)).toEqual(true);
expect(wrapper.contains(
<FormattedMessage
id='bots.manage.edit'
defaultMessage='Edit'
/>,
)).toEqual(true);
expect(wrapper.contains(
<FormattedMessage
id='bot.manage.disable'
defaultMessage='Disable'
/>,
)).toEqual(true);
expect(wrapper.contains(
<FormattedMessage
id='bot.manage.enable'
defaultMessage='Enable'
/>,
)).toEqual(false);
});

it('disabled bot', () => {
const bot = UtilsTestHelper.getBotMock({user_id: '1'});
bot.delete_at = 100; // disabled
Expand All @@ -79,6 +126,7 @@ describe('components/integrations/bots/Bot', () => {
accessTokens={{}}
team={team}
actions={actions}
fromApp={false}
/>,
);
expect(wrapper.contains(bot.display_name + ' (@' + bot.username + ')')).toEqual(true);
Expand Down Expand Up @@ -122,6 +170,7 @@ describe('components/integrations/bots/Bot', () => {
accessTokens={{}}
team={team}
actions={actions}
fromApp={false}
/>,
);
expect(wrapper.contains(owner.username)).toEqual(true);
Expand Down Expand Up @@ -167,6 +216,7 @@ describe('components/integrations/bots/Bot', () => {
accessTokens={accessTokens}
team={team}
actions={actions}
fromApp={false}
/>,
);

Expand Down Expand Up @@ -206,6 +256,7 @@ describe('components/integrations/bots/Bot', () => {
accessTokens={accessTokens}
team={team}
actions={actions}
fromApp={false}
/>,
);

Expand Down
9 changes: 8 additions & 1 deletion components/integrations/bots/bot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ type Props = {
*/
filter?: string;

/**
* Determine whether this bot is managed by the app framework
*/
fromApp: boolean;

actions: {

/**
Expand Down Expand Up @@ -193,7 +198,9 @@ export default class Bot extends React.PureComponent<Props, State> {
const displayName = this.props.bot.display_name || '';

let ownerUsername = 'plugin';
if (this.props.owner && this.props.owner.username) {
if (this.props.fromApp) {
ownerUsername = 'Apps Framework';
} else if (this.props.owner && this.props.owner.username) {
ownerUsername = this.props.owner.username;
}
const filter = this.props.filter ? this.props.filter.toLowerCase() : '';
Expand Down
77 changes: 77 additions & 0 deletions components/integrations/bots/bots.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('components/integrations/bots/Bots', () => {
getUser: jest.fn(),
disableBot: jest.fn(),
enableBot: jest.fn(),
fetchAppsBotIDs: jest.fn(),
};

it('bots', () => {
Expand All @@ -46,6 +47,8 @@ describe('components/integrations/bots/Bots', () => {
owners={{}}
users={users}
actions={actions}
appsEnabled={false}
appsBotIDs={[]}
/>,
);
wrapperFull.instance().setState({loading: false});
Expand All @@ -60,6 +63,7 @@ describe('components/integrations/bots/Bots', () => {
accessTokens={{}}
team={team}
actions={actions}
fromApp={false}
/>,
)).toEqual(true);
expect(wrapper.find('EnabledSection').shallow().contains(
Expand All @@ -71,6 +75,7 @@ describe('components/integrations/bots/Bots', () => {
accessTokens={{}}
team={team}
actions={actions}
fromApp={false}
/>,
)).toEqual(true);
expect(wrapper.find('EnabledSection').shallow().contains(
Expand All @@ -82,6 +87,75 @@ describe('components/integrations/bots/Bots', () => {
accessTokens={{}}
team={team}
actions={actions}
fromApp={false}
/>,
)).toEqual(true);
});

it('bots with bots from apps', () => {
const bot1 = TestHelper.getBotMock({user_id: '1'});
const bot2 = TestHelper.getBotMock({user_id: '2'});
const bot3 = TestHelper.getBotMock({user_id: '3'});
const bots = {
[bot1.user_id]: bot1,
[bot2.user_id]: bot2,
[bot3.user_id]: bot3,
};
const users = {
[bot1.user_id]: TestHelper.getUserMock({id: bot1.user_id}),
[bot2.user_id]: TestHelper.getUserMock({id: bot2.user_id}),
[bot3.user_id]: TestHelper.getUserMock({id: bot3.user_id}),
};

const wrapperFull = shallow(
<Bots
bots={bots}
team={team}
accessTokens={{}}
owners={{}}
users={users}
actions={actions}
appsEnabled={true}
appsBotIDs={[bot3.user_id]}
/>,
);
wrapperFull.instance().setState({loading: false});
const wrapper = shallow(<div>{(wrapperFull.instance() as Bots).bots()[0]}</div>);

expect(wrapper.find('EnabledSection').shallow().contains(
<Bot
key={bot1.user_id}
bot={bot1}
owner={undefined}
user={users[bot1.user_id]}
accessTokens={{}}
team={team}
actions={actions}
fromApp={false}
/>,
)).toEqual(true);
expect(wrapper.find('EnabledSection').shallow().contains(
<Bot
key={bot2.user_id}
bot={bot2}
owner={undefined}
user={users[bot2.user_id]}
accessTokens={{}}
team={team}
actions={actions}
fromApp={false}
/>,
)).toEqual(true);
expect(wrapper.find('EnabledSection').shallow().contains(
<Bot
key={bot3.user_id}
bot={bot3}
owner={undefined}
user={users[bot3.user_id]}
accessTokens={{}}
team={team}
actions={actions}
fromApp={true}
/>,
)).toEqual(true);
});
Expand Down Expand Up @@ -120,6 +194,8 @@ describe('components/integrations/bots/Bots', () => {
owners={owners}
users={users}
actions={actions}
appsEnabled={false}
appsBotIDs={[]}
/>,
);
wrapperFull.instance().setState({loading: false});
Expand All @@ -134,6 +210,7 @@ describe('components/integrations/bots/Bots', () => {
accessTokens={passedTokens}
team={team}
actions={actions}
fromApp={false}
/>,
)).toEqual(true);
});
Expand Down
19 changes: 19 additions & 0 deletions components/integrations/bots/bots.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ type Props = {
*/
bots: Record<string, BotType>;

/**
* List of bot IDs managed by the app framework
*/
appsBotIDs: string[];

/**
* Whether apps framework is enabled
*/
appsEnabled: boolean;

/**
* Map from botUserId to accessTokens.
*/
Expand Down Expand Up @@ -79,6 +89,11 @@ type Props = {
* Enable a bot
*/
enableBot: (userId: string) => Promise<ActionResult>;

/**
* Load bot IDs managed by the apps
*/
fetchAppsBotIDs: () => Promise<ActionResult>;
};

/**
Expand Down Expand Up @@ -124,6 +139,9 @@ export default class Bots extends React.PureComponent<Props, State> {
}
},
);
if (this.props.appsEnabled) {
this.props.actions.fetchAppsBotIDs();
}
}

DisabledSection(props: {hasDisabled: boolean; disabledBots: JSX.Element[]; filter?: string}): JSX.Element | null {
Expand Down Expand Up @@ -169,6 +187,7 @@ export default class Bots extends React.PureComponent<Props, State> {
accessTokens={(this.props.accessTokens && this.props.accessTokens[bot.user_id]) || {}}
actions={this.props.actions}
team={this.props.team}
fromApp={this.props.appsBotIDs.includes(bot.user_id)}
/>
);
};
Expand Down
8 changes: 8 additions & 0 deletions components/integrations/bots/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ import {bindActionCreators, Dispatch, ActionCreatorsMapObject} from 'redux';
import {getExternalBotAccounts} from 'mattermost-redux/selectors/entities/bots';
import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {loadBots, disableBot, enableBot} from 'mattermost-redux/actions/bots';
import {getAppsBotIDs as fetchAppsBotIDs} from 'mattermost-redux/actions/integrations';
import {getAppsBotIDs} from 'mattermost-redux/selectors/entities/integrations';
import {createUserAccessToken, revokeUserAccessToken, enableUserAccessToken, disableUserAccessToken, getUserAccessTokensForUser, getUser} from 'mattermost-redux/actions/users';
import * as UserSelectors from 'mattermost-redux/selectors/entities/users';
import {GlobalState} from 'mattermost-redux/types/store';
import {GenericAction, ActionResult, ActionFunc} from 'mattermost-redux/types/actions';
import {Bot as BotType} from 'mattermost-redux/types/bots';
import {UserProfile} from 'mattermost-redux/types/users';

import {appsEnabled} from 'mattermost-redux/selectors/entities/apps';

import Bots from './bots';

function mapStateToProps(state: GlobalState) {
Expand All @@ -38,10 +42,13 @@ function mapStateToProps(state: GlobalState) {
accessTokens: state.entities.admin.userAccessTokensByUser,
owners,
users,
appsBotIDs: getAppsBotIDs(state),
appsEnabled: appsEnabled(state),
};
}

type Actions = {
fetchAppsBotIDs: () => Promise<{data: string[]}>;
loadBots: (page?: number, perPage?: number) => Promise<{data: BotType[]; error?: Error}>;
getUserAccessTokensForUser: (userId: string, page?: number, perPage?: number) => void;
createUserAccessToken: (userId: string, description: string) => Promise<{
Expand All @@ -59,6 +66,7 @@ type Actions = {
function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
return {
actions: bindActionCreators<ActionCreatorsMapObject<ActionFunc>, Actions>({
fetchAppsBotIDs,
loadBots,
getUserAccessTokensForUser,
createUserAccessToken,
Expand Down
Loading

0 comments on commit 622c8f7

Please sign in to comment.