From 9ea866c83336be9abfe25a3ad14e9f95cd724240 Mon Sep 17 00:00:00 2001 From: Haitao Yue Date: Sun, 24 Sep 2017 19:24:28 +0800 Subject: [PATCH] [CE-144] Support user management in vue In vue theme can manage users Add user search api for check new user Change-Id: I8d15dbdf3eea881fe25d222de215ca7003fac04b Signed-off-by: Haitao Yue --- src/modules/user/__init__.py | 2 +- src/modules/user/management/__init__.py | 1 + src/modules/user/management/search.py | 59 ++++++ src/resources/user_api.py | 3 +- src/themes/vue/static/package.json | 1 + src/themes/vue/static/src/App.vue | 3 + src/themes/vue/static/src/api/user.js | 37 ++++ .../src/pages/UserManagement/Operation.vue | 40 ++++ .../src/pages/UserManagement/UserModal.vue | 174 ++++++++++++++++ .../static/src/pages/UserManagement/index.vue | 190 ++++++++++++++++++ .../static/src/pages/UserManagementPage.vue | 18 -- src/themes/vue/static/src/router/index.js | 2 +- src/themes/vue/static/src/store/index.js | 4 +- src/themes/vue/static/src/store/users.js | 100 +++++++++ test/cases/test_user_management.py | 19 ++ 15 files changed, 631 insertions(+), 22 deletions(-) create mode 100644 src/modules/user/management/search.py create mode 100644 src/themes/vue/static/src/api/user.js create mode 100644 src/themes/vue/static/src/pages/UserManagement/Operation.vue create mode 100644 src/themes/vue/static/src/pages/UserManagement/UserModal.vue create mode 100644 src/themes/vue/static/src/pages/UserManagement/index.vue delete mode 100644 src/themes/vue/static/src/pages/UserManagementPage.vue create mode 100644 src/themes/vue/static/src/store/users.js diff --git a/src/modules/user/__init__.py b/src/modules/user/__init__.py index 1f0bc15b1..325380262 100644 --- a/src/modules/user/__init__.py +++ b/src/modules/user/__init__.py @@ -4,7 +4,7 @@ # SPDX-License-Identifier: Apache-2.0 # from .management import ListUser, CreateUser, UpdateUser, \ - DeleteUser, UserInfo + DeleteUser, UserInfo, UserSearch from .auth import Register, Login from .user import User from .profile import UserProfile diff --git a/src/modules/user/management/__init__.py b/src/modules/user/management/__init__.py index 156550458..0c5c0fd46 100644 --- a/src/modules/user/management/__init__.py +++ b/src/modules/user/management/__init__.py @@ -8,3 +8,4 @@ from .update import UpdateUser from .delete import DeleteUser from .info import UserInfo +from .search import UserSearch diff --git a/src/modules/user/management/search.py b/src/modules/user/management/search.py new file mode 100644 index 000000000..32552717c --- /dev/null +++ b/src/modules/user/management/search.py @@ -0,0 +1,59 @@ + +# Copyright IBM Corp, All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +from flask_restful import Resource, fields, marshal_with, reqparse +import logging +import sys +import os +from flask_login import login_required + +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..')) +from common import log_handler, LOG_LEVEL +from modules.user.user import User + +logger = logging.getLogger(__name__) +logger.setLevel(LOG_LEVEL) +logger.addHandler(log_handler) + +user_search_fields = { + "username": fields.String, + "apikey": fields.String, + "isActivated": fields.Boolean, + "balance": fields.Integer, + "user_exists": fields.Boolean +} + +user_search_parser = reqparse.RequestParser() +user_search_parser.add_argument('username', + location='args', + help='Username for create') + + +class UserSearch(Resource): + @login_required + @marshal_with(user_search_fields) + def get(self): + """ + search user with username + If user is existed return user info + else return user_exists False + :return: + """ + args = user_search_parser.parse_args() + username = args["username"] + user_obj = User() + user = user_obj.get_by_username(username) + if not user: + return {"user_exists": False}, 200 + + data = { + "username": user.username, + "apikey": str(user.id), + "isActivated": user.active, + "balance": user.balance, + "user_exists": True + } + + return data, 200 diff --git a/src/resources/user_api.py b/src/resources/user_api.py index 74a9fd2fa..f67e656ea 100644 --- a/src/resources/user_api.py +++ b/src/resources/user_api.py @@ -11,7 +11,7 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..')) from modules.user import ListUser, CreateUser, UpdateUser, \ - DeleteUser, Register, Login, UserInfo, UserProfile + DeleteUser, Register, Login, UserInfo, UserProfile, UserSearch bp_user_api = Blueprint('bp_user_api', __name__, @@ -39,6 +39,7 @@ def logout(): user_api.add_resource(UpdateUser, '/update/') user_api.add_resource(DeleteUser, '/delete/') user_api.add_resource(UserInfo, '/account/') +user_api.add_resource(UserSearch, '/search', endpoint="search") front_user_api = Api(front_rest_user_v2) front_user_api.add_resource(UserProfile, '/profile/') diff --git a/src/themes/vue/static/package.json b/src/themes/vue/static/package.json index f9f585eff..0af3a4720 100644 --- a/src/themes/vue/static/package.json +++ b/src/themes/vue/static/package.json @@ -20,6 +20,7 @@ "iview": "^2.3.2", "axios": "^0.16.2", "vue-echarts": "*", + "moment": "^2.18.1", "vuex-router-sync": "^4.1.2" }, "devDependencies": { diff --git a/src/themes/vue/static/src/App.vue b/src/themes/vue/static/src/App.vue index 3bac0ec93..98922f64b 100644 --- a/src/themes/vue/static/src/App.vue +++ b/src/themes/vue/static/src/App.vue @@ -130,4 +130,7 @@ export default { display:inline-block; border-bottom: 2px solid #5b6270; } + .main-content { + min-height: 100vh; + } diff --git a/src/themes/vue/static/src/api/user.js b/src/themes/vue/static/src/api/user.js new file mode 100644 index 000000000..ab0b4cd11 --- /dev/null +++ b/src/themes/vue/static/src/api/user.js @@ -0,0 +1,37 @@ + +/* Copyright IBM Corp, All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 + */ +import axios from 'axios' + +export default { + getUsers (params, cb) { + axios.get('/api/user/list', {params: params}).then(res => { + cb(res.data.users) + }) + }, + searchUser (params, cb) { + axios.get('/api/user/search', {params: params}).then(res => { + cb(res.data) + }) + }, + createUser (formData, cb) { + axios.post('/api/user/create', formData).then(res => { + cb(res.data) + }) + }, + updateUser (params, cb) { + const updateUrl = '/api/user/update/' + params.id + const formData = params.formData + axios.put(updateUrl, formData).then(res => { + cb(res.data) + }) + }, + deleteUser (params, cb) { + const deleteUrl = '/api/user/delete/' + params.id + axios.delete(deleteUrl).then(res => { + cb(res.data) + }) + } +} diff --git a/src/themes/vue/static/src/pages/UserManagement/Operation.vue b/src/themes/vue/static/src/pages/UserManagement/Operation.vue new file mode 100644 index 000000000..23d90788a --- /dev/null +++ b/src/themes/vue/static/src/pages/UserManagement/Operation.vue @@ -0,0 +1,40 @@ + + + + + + + diff --git a/src/themes/vue/static/src/pages/UserManagement/UserModal.vue b/src/themes/vue/static/src/pages/UserManagement/UserModal.vue new file mode 100644 index 000000000..07d9f6e88 --- /dev/null +++ b/src/themes/vue/static/src/pages/UserManagement/UserModal.vue @@ -0,0 +1,174 @@ + + + + + + + diff --git a/src/themes/vue/static/src/pages/UserManagement/index.vue b/src/themes/vue/static/src/pages/UserManagement/index.vue new file mode 100644 index 000000000..f861fff8f --- /dev/null +++ b/src/themes/vue/static/src/pages/UserManagement/index.vue @@ -0,0 +1,190 @@ + + + + + + + diff --git a/src/themes/vue/static/src/pages/UserManagementPage.vue b/src/themes/vue/static/src/pages/UserManagementPage.vue deleted file mode 100644 index 2c5a26989..000000000 --- a/src/themes/vue/static/src/pages/UserManagementPage.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/src/themes/vue/static/src/router/index.js b/src/themes/vue/static/src/router/index.js index d542d911a..dbd84517d 100644 --- a/src/themes/vue/static/src/router/index.js +++ b/src/themes/vue/static/src/router/index.js @@ -22,7 +22,7 @@ const ChainsPage = resolve => { } const UserManagementPage = resolve => { require.ensure([], () => { - resolve(require('@/pages/UserManagementPage.vue')) + resolve(require('@/pages/UserManagement/index.vue')) }) } diff --git a/src/themes/vue/static/src/store/index.js b/src/themes/vue/static/src/store/index.js index e44dc58a3..5d797aa9b 100644 --- a/src/themes/vue/static/src/store/index.js +++ b/src/themes/vue/static/src/store/index.js @@ -6,11 +6,13 @@ import Vuex from 'vuex' import Vue from 'vue' import stats from './stats' +import users from './users' Vue.use(Vuex) export default new Vuex.Store({ modules: { - stats + stats, + users } }) diff --git a/src/themes/vue/static/src/store/users.js b/src/themes/vue/static/src/store/users.js new file mode 100644 index 000000000..dfef06b05 --- /dev/null +++ b/src/themes/vue/static/src/store/users.js @@ -0,0 +1,100 @@ + +/* Copyright IBM Corp, All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 + */ +import user from '@/api/user' + +const state = { + users: [], + pageNo: 1, + pageSize: 10, + totalCount: 1, + loadingUsers: false, + modalVisible: false, + modalType: 'create', + currentUser: {} +} + +const actions = { + getUsers ({commit}, params) { + commit('show_loading') + user.getUsers(params, result => { + commit('receive_users', result) + }) + }, + showUserModal ({commit}, {modalType, user}) { + commit('show_modal', modalType, user) + }, + hideUserModal ({commit}) { + commit('hide_modal') + }, + deleteSingleUser ({dispatch}, params) { + user.deleteUser(params, result => { + if (result.status === 'OK') { + dispatch('getUsers', {}) + dispatch(params.deleteCallback({success: true})) + } else { + dispatch(params.deleteCallback({success: false})) + } + }) + } +} + +const mutations = { + show_loading (state) { + state.loadingUsers = true + }, + hide_loading (state) { + state.loadingUsers = false + }, + receive_users (state, result) { + state.users = result.result + state.pageNo = result.pageNo + state.pageSize = result.pageSize + state.totalCount = result.totalCount + state.loadingUsers = false + }, + show_modal (state, modalType, user) { + state.modalType = modalType + state.modalVisible = true + if (user) { + state.currentUser = user + } + }, + hide_modal (state) { + state.modalVisible = false + } +} + +const getters = { + isLoadingUsers (state) { + return state.loadingUsers + }, + users (state) { + return state.users + }, + userPagination (state) { + return { + current: state.pageNo, + pageSize: state.pageSize, + total: state.totalCount + } + }, + userModal (state) { + return { + visible: state.modalVisible, + type: state.modalType + } + }, + currentUser (state) { + return state.currentUser + } +} + +export default { + state, + actions, + mutations, + getters +} diff --git a/test/cases/test_user_management.py b/test/cases/test_user_management.py index 0fb5e4518..f28f4c577 100644 --- a/test/cases/test_user_management.py +++ b/test/cases/test_user_management.py @@ -5,6 +5,7 @@ # import unittest from flask_testing import TestCase +from flask import url_for import sys import os import logging @@ -58,6 +59,24 @@ def test_list_user(self): users = response.get("users", {}).get("result", []) self.assertTrue(len(users) >= 1) + def test_search_user(self): + """ + search admin will return admin user info, + search fake username will return user_exists False + """ + self._login("admin", "pass") + response = self.client.get(url_for("bp_user_api.search"), query_string={"username": "admin"}) + raw_json = response.data.decode("utf-8") + raw_json = json.loads(raw_json) + user_exists = raw_json.get("user_exists", False) + self.assertTrue(user_exists) + user_name = fake.user_name() + response = self.client.get(url_for("bp_user_api.search"), query_string={"username": user_name}) + raw_json = response.data.decode("utf-8") + raw_json = json.loads(raw_json) + user_exists = raw_json.get("user_exists", False) + self.assertTrue(not user_exists) + def test_create_update_delete_user(self): self._login("admin", "pass") user_name = fake.user_name()