Commit a7162c4b authored by Christopher Speller's avatar Christopher Speller Committed by Harrison Healey

MM-13622 MM-13620 Adding client4 methods, reducers, actions, and selectors for bot accounts. (#782)

* Adding clinet4 methods, reducers, actions, and selectors for bot accounts.

* Fix and clarify tests.

* Adding logout handling and proper dispatch.
parent 7cdad4bd
......@@ -6248,7 +6248,8 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
......@@ -6269,12 +6270,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
......@@ -6289,17 +6292,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
......@@ -6416,7 +6422,8 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
......@@ -6428,6 +6435,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
......@@ -6442,6 +6450,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
......@@ -6449,12 +6458,14 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"minipass": {
"version": "2.2.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
......@@ -6473,6 +6484,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
......@@ -6553,7 +6565,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
......@@ -6565,6 +6578,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
......@@ -6650,7 +6664,8 @@
"safe-buffer": {
"version": "5.1.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
......@@ -6686,6 +6701,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
......@@ -6705,6 +6721,7 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
......@@ -6748,12 +6765,14 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"yallist": {
"version": "3.0.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
}
}
},
......
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import keyMirror from 'utils/key_mirror';
export default keyMirror({
RECEIVED_BOT_ACCOUNTS: null,
RECEIVED_BOT_ACCOUNT: null,
});
......@@ -20,6 +20,7 @@ import SearchTypes from './search';
import RoleTypes from './roles';
import SchemeTypes from './schemes';
import GroupTypes from './groups';
import BotTypes from './bots';
export {
ErrorTypes,
......@@ -40,4 +41,5 @@ export {
RoleTypes,
SchemeTypes,
GroupTypes,
BotTypes,
};
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from 'client';
import {BotTypes} from 'action_types';
import {bindClientFunc} from './helpers';
const BOTS_PER_PAGE_DEFAULT = 20;
export function createBot(bot) {
return bindClientFunc({
clientFunc: Client4.createBot,
onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT,
params: [
bot,
],
});
}
export function patchBot(botUserId, botPatch) {
return bindClientFunc({
clientFunc: Client4.patchBot,
onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT,
params: [
botUserId,
botPatch,
],
});
}
export function loadBot(botUserId) {
return bindClientFunc({
clientFunc: Client4.getBot,
onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT,
params: [
botUserId,
],
});
}
export function loadBots(page = 0, perPage = BOTS_PER_PAGE_DEFAULT) {
return bindClientFunc({
clientFunc: Client4.getBots,
onSuccess: BotTypes.RECEIVED_BOT_ACCOUNTS,
params: [
page,
perPage,
],
});
}
export function disableBot(botUserId) {
return bindClientFunc({
clientFunc: Client4.disableBot,
onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT,
params: [
botUserId,
],
});
}
export function enableBot(botUserId) {
return bindClientFunc({
clientFunc: Client4.enableBot,
onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT,
params: [
botUserId,
],
});
}
export function assignBot(botUserId, newOwnerId) {
return bindClientFunc({
clientFunc: Client4.assignBot,
onSuccess: BotTypes.RECEIVED_BOT_ACCOUNT,
params: [
botUserId,
newOwnerId,
],
});
}
// 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 BotActions from 'actions/bots';
import * as UserActions from 'actions/users';
import {Client4} from 'client';
import TestHelper from 'test/test_helper';
import configureStore from 'test/test_store';
describe('Actions.Bots', () => {
let store;
beforeAll(async () => {
await TestHelper.initBasic(Client4);
});
beforeEach(async () => {
store = await configureStore();
});
afterAll(async () => {
await TestHelper.tearDown();
});
it('loadBots', async () => {
const bots = [TestHelper.fakeBot(), TestHelper.fakeBot()];
nock(Client4.getBotsRoute()).
get('').
query(true).
reply(201, bots);
await store.dispatch(BotActions.loadBots());
const state = store.getState();
const botsResult = state.entities.bots.accounts;
assert.equal(bots.length, Object.values(botsResult).length);
});
it('loadBot', async () => {
const bot = TestHelper.fakeBot();
nock(Client4.getBotRoute(bot.user_id)).
get('').
query(true).
reply(201, bot);
await store.dispatch(BotActions.loadBot(bot.user_id));
const state = store.getState();
const botsResult = state.entities.bots.accounts[bot.user_id];
assert.equal(bot.username, botsResult.username);
});
it('createBot', async () => {
const bot = TestHelper.fakeBot();
nock(Client4.getBotRoute()).
post('').
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.getBotRoute()).
post('').
reply(200, bot);
await store.dispatch(BotActions.createBot(bot));
bot.username = 'mynewusername';
nock(Client4.getBotRoute(bot.user_id)).
put('').
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.getBotRoute()).
post('').
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.getBotRoute()).
post('').
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);
});
it('logout', async () => {
// Fill redux store with somthing
const bot = TestHelper.fakeBot();
nock(Client4.getBotRoute()).
post('').
reply(200, bot);
await store.dispatch(BotActions.createBot(bot));
// Should be cleared by logout
nock(Client4.getUsersRoute()).
post('/logout').
reply(200, {status: 'OK'});
await store.dispatch(UserActions.logout());
// Check is clear
const state = store.getState();
assert.equal(0, Object.keys(state.entities.bots.accounts).length);
});
});
......@@ -260,6 +260,14 @@ export default class Client4 {
return `${this.getBaseRoute()}/redirect_location`;
}
getBotsRoute() {
return `${this.getBaseRoute()}/bots`;
}
getBotRoute(botUserId) {
return `${this.getBotsRoute()}/${botUserId}`;
}
getOptions(options) {
const newOptions = Object.assign({}, options);
......@@ -2585,6 +2593,71 @@ export default class Client4 {
return this.doFetch(url, {method: 'get'});
};
// Bot Routes
createBot = async (bot) => {
return this.doFetch(
`${this.getBotRoute()}`,
{method: 'post', body: JSON.stringify(bot)}
);
}
patchBot = async (botUserId, botPatch) => {
return this.doFetch(
`${this.getBotRoute(botUserId)}`,
{method: 'put', body: JSON.stringify(botPatch)}
);
}
getBot = async (botUserId) => {
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'}
);
}
getBotsOrphaned = async (page = 0, perPage = PER_PAGE_DEFAULT) => {
return this.doFetch(
`${this.getBotsRoute()}${buildQueryString({only_orphaned: true, page, per_page: perPage})}`,
{method: 'get'}
);
}
disableBot = async (botUserId) => {
return this.doFetch(
`${this.getBotRoute(botUserId)}/disable`,
{method: 'post'}
);
}
enableBot = async (botUserId) => {
return this.doFetch(
`${this.getBotRoute(botUserId)}/enable`,
{method: 'post'}
);
}
assignBot = async (botUserId, newOwnerId) => {
return this.doFetch(
`${this.getBotRoute(botUserId)}/assign/${newOwnerId}`,
{method: 'post'}
);
}
// Client Helpers
doFetch = async (url, options) => {
......
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {combineReducers} from 'redux';
import {BotTypes, UserTypes} from 'action_types';
function accounts(state = {}, action) {
switch (action.type) {
case BotTypes.RECEIVED_BOT_ACCOUNTS: {
const newBots = action.data;
const nextState = {...state};
for (const bot of newBots) {
nextState[bot.user_id] = bot;
}
return nextState;
}
case BotTypes.RECEIVED_BOT_ACCOUNT: {
const bot = action.data;
const nextState = {...state};
nextState[bot.user_id] = bot;
return nextState;
}
case UserTypes.LOGOUT_SUCCESS:
return {};
default:
return state;
}
}
export default combineReducers({
accounts,
});
......@@ -21,6 +21,7 @@ import search from './search';
import roles from './roles';
import schemes from './schemes';
import groups from './groups';
import bots from './bots';
export default combineReducers({
general,
......@@ -41,4 +42,5 @@ export default combineReducers({
roles,
schemes,
groups,
bots,
});
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export function getBotAccounts(state) {
return state.entities.bots.accounts;
}
......@@ -301,6 +301,18 @@ class TestHelper {
};
};
fakeBot = () => {
return {
user_id: this.generateId(),
username: this.generateId(),
display_name: 'Fake bot',
owner_id: this.generateId(),
create_at: 1507840900004,
update_at: 1507840900004,
delete_at: 0,
};
}
mockLogin = () => {
nock(this.basicClient4.getUsersRoute()).
post('/login').
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment