diff --git a/src/components/FormItem/index.jsx b/src/components/FormItem/index.jsx index f3ec7ef1..3249f137 100644 --- a/src/components/FormItem/index.jsx +++ b/src/components/FormItem/index.jsx @@ -245,7 +245,7 @@ export default class FormItem extends React.Component { case 'check-group': return { ...base, - wrapperCol: { + wrapperCol: wrapperCol || { xs: { span: 24, }, diff --git a/src/pages/identity/containers/Project/Detail/index.jsx b/src/pages/identity/containers/Project/Detail/index.jsx index 87fe2129..9b69e2f9 100644 --- a/src/pages/identity/containers/Project/Detail/index.jsx +++ b/src/pages/identity/containers/Project/Detail/index.jsx @@ -55,11 +55,11 @@ export class Detail extends Base { enabledColumn, { title: t('User Num'), - dataIndex: 'user_num', + dataIndex: 'userCount', }, { title: t('User Group Num'), - dataIndex: 'group_num', + dataIndex: 'groupCount', }, { title: t('Tags'), @@ -93,13 +93,6 @@ export class Detail extends Base { ]; return tabs; } - - goEdit = () => { - const { - params: { id }, - } = this.props.match; - this.routing.push(`${this.listUrl}/edit/${id}`); - }; } export default inject('rootStore')(observer(Detail)); diff --git a/src/pages/identity/containers/Project/actions/Create.jsx b/src/pages/identity/containers/Project/actions/Create.jsx index fade86ca..3c23408c 100644 --- a/src/pages/identity/containers/Project/actions/Create.jsx +++ b/src/pages/identity/containers/Project/actions/Create.jsx @@ -17,9 +17,9 @@ import { ModalAction } from 'containers/Action'; import globalDomainStore from 'stores/keystone/domain'; import globalProjectStore from 'stores/keystone/project'; import { regex } from 'utils/validate'; -import { statusTypes } from 'resources/keystone/domain'; +import { statusTypes, getDomainFormItem } from 'resources/keystone/domain'; -export class CreateForm extends ModalAction { +export class Create extends ModalAction { constructor(props) { super(props); this.state = { @@ -66,29 +66,6 @@ export class CreateForm extends ModalAction { return data; } - get domainList() { - const { - rootStore: { baseDomains }, - } = this.props; - const { domains } = this.domainStore; - const domainList = (domains || []).filter( - (it) => - baseDomains.indexOf(it.name) === -1 || it.id === this.item.domain_id - ); - return domainList.map((it) => ({ - label: it.name, - value: it.id, - })); - } - - get checkedList() { - const { domains } = this.domainStore; - return (domains || []).map((it) => ({ - label: it.name, - value: it.id, - })); - } - checkName = (rule, value) => { if (!value) { return Promise.reject(t('Please input')); @@ -108,6 +85,7 @@ export class CreateForm extends ModalAction { }; get formItems() { + const domainFormItem = getDomainFormItem(this); return [ { name: 'name', @@ -119,15 +97,7 @@ export class CreateForm extends ModalAction { extra: t('Project') + t('Name can not be duplicated'), maxLength: 30, }, - { - name: 'domain_id', - label: t('Affiliated Domain'), - type: 'select', - checkOptions: this.checkedList, - checkBoxInfo: t('Show All Domain'), - options: this.domainList, - required: true, - }, + domainFormItem, { name: 'enabled', label: t('Status'), @@ -154,4 +124,4 @@ export class CreateForm extends ModalAction { }; } -export default inject('rootStore')(observer(CreateForm)); +export default inject('rootStore')(observer(Create)); diff --git a/src/pages/identity/containers/Project/actions/Edit.jsx b/src/pages/identity/containers/Project/actions/Edit.jsx index f792fe32..1d6a2a4f 100644 --- a/src/pages/identity/containers/Project/actions/Edit.jsx +++ b/src/pages/identity/containers/Project/actions/Edit.jsx @@ -15,7 +15,6 @@ import { inject, observer } from 'mobx-react'; import { ModalAction } from 'containers/Action'; import globalProjectStore from 'stores/keystone/project'; -import { statusTypes } from 'resources/keystone/domain'; export class EditForm extends ModalAction { init() { @@ -73,17 +72,6 @@ export class EditForm extends ModalAction { validator: this.checkName, extra: t('Project') + t('Name can not be duplicated'), }, - { - name: 'enabled', - label: t('Status'), - type: 'radio', - optionType: 'default', - options: statusTypes, - disabled: true, - help: t( - 'Disabling the project will have a negative impact. If the users associated with the project are only assigned to the project, they will not be able to log in' - ), - }, { name: 'description', label: t('Description'), diff --git a/src/pages/identity/containers/Project/actions/Forbidden.jsx b/src/pages/identity/containers/Project/actions/Forbidden.jsx index 3cf37adc..36137724 100644 --- a/src/pages/identity/containers/Project/actions/Forbidden.jsx +++ b/src/pages/identity/containers/Project/actions/Forbidden.jsx @@ -33,6 +33,10 @@ export default class ForbiddenAction extends ConfirmAction { return t('Forbidden Project'); } + get isDanger() { + return true; + } + policy = 'identity:update_project'; allowedCheckFunc = (item) => { diff --git a/src/pages/identity/containers/Project/actions/QuotaManager.jsx b/src/pages/identity/containers/Project/actions/ManageQuota.jsx similarity index 98% rename from src/pages/identity/containers/Project/actions/QuotaManager.jsx rename to src/pages/identity/containers/Project/actions/ManageQuota.jsx index e20d62b0..ae59aae7 100644 --- a/src/pages/identity/containers/Project/actions/QuotaManager.jsx +++ b/src/pages/identity/containers/Project/actions/ManageQuota.jsx @@ -23,7 +23,7 @@ import { shareQuotaCard, } from 'pages/base/containers/Overview/components/QuotaOverview'; -export class QuotaManager extends ModalAction { +export class ManageQuota extends ModalAction { static id = 'quota-management'; static title = t('Edit Quota'); @@ -216,4 +216,4 @@ export class QuotaManager extends ModalAction { }; } -export default inject('rootStore')(observer(QuotaManager)); +export default inject('rootStore')(observer(ManageQuota)); diff --git a/src/pages/identity/containers/Project/actions/ManageUser.jsx b/src/pages/identity/containers/Project/actions/ManageUser.jsx new file mode 100644 index 00000000..b8c09df9 --- /dev/null +++ b/src/pages/identity/containers/Project/actions/ManageUser.jsx @@ -0,0 +1,240 @@ +// Copyright 2021 99cloud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from 'react'; +import { inject, observer } from 'mobx-react'; +import { Select } from 'antd'; +import globalProjectStore from 'stores/keystone/project'; +import { UserStore } from 'stores/keystone/user'; +import { RoleStore } from 'stores/keystone/role'; +import { ModalAction } from 'containers/Action'; +import { + nameDomainColumns, + transferFilterOption, +} from 'resources/keystone/domain'; + +export class ManagerUser extends ModalAction { + static id = 'management-user'; + + static title = t('Manage User'); + + get name() { + return t('Manage user'); + } + + async init() { + this.state.userRoles = this.getInitRoleMap(); + this.store = new RoleStore(); + this.userStore = new UserStore(); + this.getRoleList(); + this.getUser(); + } + + getRoleList() { + return this.store.fetchList(); + } + + getUser() { + this.userStore.fetchAllWithDomain(); + } + + getInitRoleMap() { + const { users = {} } = this.item; + return Object.keys(users).reduce((pre, cur) => { + pre[cur] = users[cur].map((it) => it.id); + return pre; + }, {}); + } + + static get modalSize() { + return 'large'; + } + + getModalSize() { + return 'large'; + } + + get labelCol() { + return { + xs: { span: 4 }, + sm: { span: 2 }, + }; + } + + get wrapperCol() { + return { + xs: { span: 20 }, + sm: { span: 20 }, + }; + } + + get userList() { + const users = this.userStore.list.data || []; + return users.map((it) => ({ + ...it, + key: it.id, + })); + } + + get projectRoleList() { + return this.store.list.data || []; + } + + userRolesList = (user_id) => + this.projectRoleList.map((it) => ({ + label: it.name, + value: it.id, + key: it.id, + user_id, + })); + + defaultRoles = (userId) => { + const { users } = this.item; + if (!users[userId]) { + return [this.projectRoleList[0].id]; + } + const usersProjectRole = users[userId].map((it) => { + return it.id; + }); + return usersProjectRole; + }; + + static policy = 'identity:update_project'; + + static allowed = () => Promise.resolve(true); + + get leftUserTable() { + return nameDomainColumns; + } + + get rightUserTable() { + return [ + ...nameDomainColumns, + { + title: t('Select Project Role'), + dataIndex: 'id', + render: (id) => this.renderSelect(id), + }, + ]; + } + + renderSelect = (id) => { + return ( + { + this.onSubChange(value, option, groupId); + }} + /> + ); + }; + + get rightGroupGroupTable() { + return [ + ...nameDomainColumns, + { + title: t('Select Project Role'), + dataIndex: 'id', + render: (id) => this.renderSelect(id), + }, + ]; + } + + onSubChange = (value, option, groupId) => { + const { groupRoles } = this.state; + if (value.length && option.length) { + groupRoles[groupId] = value; + } else { + groupRoles[groupId] = {}; + } + this.setState({ groupRoles }); + }; + + get defaultValue() { + const { name, domainName } = this.item; + const data = { + name, + domain: domainName, + }; + return data; + } + + get formItems() { + const { groups } = this.item; + return [ + { + name: 'name', + type: 'label', + label: t('Name'), + iconType: 'project', + }, + { + name: 'domain', + type: 'label', + label: t('Domain'), + }, + { + name: 'select_group', + label: t('User Group'), + type: 'transfer', + leftTableColumns: this.leftGroupGroupTable, + rightTableColumns: this.rightGroupGroupTable, + dataSource: this.groupList, + disabled: false, + showSearch: true, + oriTargetKeys: groups ? Object.keys(groups) : [], + filterOption: transferFilterOption, + wrapperCol: this.wrapperCol, + }, + ]; + } + + onSubmit = async () => { + const { groupRoles = {} } = this.state; + const { id } = this.item; + const oldGroupRoles = this.getInitRoleMap(); + const promiseList = []; + Object.keys(oldGroupRoles).forEach((groupId) => { + (oldGroupRoles[groupId] || []).forEach((roleId) => { + const newRoles = groupRoles[groupId] || []; + if (!newRoles.includes(roleId)) { + promiseList.push( + globalProjectStore.removeGroupRole({ id, groupId, roleId }) + ); + } + }); + }); + Object.keys(groupRoles).forEach((groupId) => { + const oldRoles = oldGroupRoles[groupId] || []; + groupRoles[groupId].forEach((roleId) => { + if (!oldRoles.includes(roleId)) { + promiseList.push( + globalProjectStore.assignGroupRole({ id, groupId, roleId }) + ); + } + }); + }); + const results = await Promise.all(promiseList); + return results; + }; +} + +export default inject('rootStore')(observer(ManageGroupGroup)); diff --git a/src/pages/identity/containers/Project/actions/UserGroupManager.jsx b/src/pages/identity/containers/Project/actions/UserGroupManager.jsx deleted file mode 100644 index 1e4f157b..00000000 --- a/src/pages/identity/containers/Project/actions/UserGroupManager.jsx +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2021 99cloud -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import React from 'react'; -import { inject, observer } from 'mobx-react'; -import { Select } from 'antd'; -import globalProjectStore from 'stores/keystone/project'; -import { GroupStore } from 'stores/keystone/user-group'; -import globalRoleStore from 'stores/keystone/role'; -import { ModalAction } from 'containers/Action'; - -export class UserGroupManager extends ModalAction { - static id = 'management-user-group'; - - static title = t('Manage User Group'); - - async init() { - const roles = JSON.stringify(this.item.groups); - this.state.domainDefault = this.item.domain_id; - this.state.groupRoles = JSON.parse(roles); - this.userGroupStore = new GroupStore(); - this.store = globalRoleStore; - await this.getRoleList(); - this.getUserGroup(); - } - - get name() { - return t('Manager user group'); - } - - get multipleMode() { - return 'multiple'; - } - - getUserGroup() { - this.userGroupStore.fetchList(); - } - - getRoleList() { - return new Promise((resolve) => { - this.store.fetchList().finally(() => { - resolve(); - }); - }); - } - - static get modalSize() { - return 'large'; - } - - getModalSize() { - return 'large'; - } - - get item() { - const { item } = this.props; - item.roles = {}; - return item; - } - - get groupList() { - return (this.userGroupStore.list.data || []).map((it) => ({ - ...it, - key: it.id, - })); - } - - get projectRoleList() { - const projectRole = this.store.list.data || []; - return projectRole; - } - - groupRolesList = (groupId) => - this.projectRoleList.map((it) => ({ - label: it.name, - value: it.id, - key: it.id, - groupId, - })); - - defaultRoles = (groupId) => { - const { groups, roles } = this.item; - const { groupRoles: projectGroups } = this.state; - const filterGroups = this.multipleMode ? groups : projectGroups; - if (!filterGroups[groupId]) { - roles[groupId] = [this.projectRoleList[0].id]; - } else { - const usersProjectRole = filterGroups[groupId].filter((it) => { - const projectRole = this.projectRoleList.find((role) => role.id === it); - return !!projectRole; - }); - return this.multipleMode - ? usersProjectRole - : usersProjectRole.slice(0, 1); - } - return roles[groupId]; - }; - - static policy = 'identity:update_project'; - - static allowed = () => Promise.resolve(true); - - get leftUserGroupTable() { - return [ - { - dataIndex: 'name', - title: t('Name'), - }, - ]; - } - - renderSelect = (groupId) => { - return ( - - ); - }; - - onSubChange = (value, option) => { - if ( - (this.multipleMode && value.length && option.length) || - (!this.multipleMode && value && option) - ) { - const { userRoles } = this.state; - const { user_id } = this.multipleMode ? option[0] : option; - userRoles[user_id] = this.multipleMode ? value : [value]; - this.setState({ userRoles }); - } else { - this.setState({ userRoles: {} }); - } - }; - - get defaultValue() { - const { domain_id: domain } = this.item; - const data = { - domain_id: domain || 'default', - }; - return data; - } - - get formItems() { - const { users } = this.item; - const { domainDefault } = this.state; - return [ - { - name: 'domain_id', - label: t('Affiliated Domain'), - type: 'select', - checkOptions: this.checkedList, - checkBoxInfo: t('Show All Domain'), - options: this.domainList, - onChange: (e) => { - this.setState({ - domainDefault: e, - }); - }, - required: true, - }, - { - name: 'select_user', - type: 'transfer', - label: t('User'), - leftTableColumns: this.leftUserTable, - rightTableColumns: this.rightUserTable, - dataSource: this.userList - ? this.userList.filter((it) => it.domain_id === domainDefault) - : [], - disabled: false, - showSearch: true, - oriTargetKeys: users ? Object.keys(users) : [], - }, - ]; - } - - onSubmit = async (values) => { - const { userRoles } = this.state; - if (!this.multipleMode) { - // If it is not multiple choices, role only takes the first item of the array - Object.keys(userRoles).forEach((key) => { - userRoles[key] = userRoles[key].slice(0, 1); - }); - } - const { id, users } = this.item; - const oldUserRoles = users; - const defaultUsers = Object.keys(oldUserRoles); - const promiseList = []; - defaultUsers.forEach((user_id) => { - if (values.select_user && !values.select_user.includes(user_id)) { - (oldUserRoles[user_id] || []).forEach((role_id) => { - promiseList.push( - globalProjectStore.removeUserRole({ id, user_id, role_id }) - ); - }); - } else { - (oldUserRoles[user_id] || []).forEach((role_id) => { - if (userRoles[user_id] && !userRoles[user_id].includes(role_id)) { - promiseList.push( - globalProjectStore.removeUserRole({ id, user_id, role_id }) - ); - } - }); - } - }); - (values.select_user || []).forEach((user_id) => { - if (defaultUsers && !defaultUsers.includes(user_id)) { - if (userRoles[user_id]) { - userRoles[user_id].forEach((role_id) => { - promiseList.push( - globalProjectStore.assignUserRole({ id, user_id, role_id }) - ); - }); - } else { - promiseList.push( - globalProjectStore.assignUserRole({ - id, - user_id, - role_id: this.projectRoleList[0].id, - }) - ); - } - } else { - (userRoles[user_id] || []).forEach((role_id) => { - if ( - (oldUserRoles[user_id] && - !oldUserRoles[user_id].includes(role_id)) || - !oldUserRoles[user_id] - ) { - promiseList.push( - globalProjectStore.assignUserRole({ id, user_id, role_id }) - ); - } - }); - } - }); - const results = await Promise.all(promiseList); - return results; - }; -} - -export default inject('rootStore')(observer(UserManager)); diff --git a/src/pages/identity/containers/Project/actions/index.jsx b/src/pages/identity/containers/Project/actions/index.jsx index 70d741c5..1c50e550 100644 --- a/src/pages/identity/containers/Project/actions/index.jsx +++ b/src/pages/identity/containers/Project/actions/index.jsx @@ -12,45 +12,45 @@ // See the License for the specific language governing permissions and // limitations under the License. -import deleteActionConfig from './Delete'; -import editActionConfig from './Edit'; -import enableActionConfig from './Enable'; -import forbiddenActionConfig from './Forbidden'; -import createActionConfig from './Create'; -import userActionConfig from './UserManager'; -import userGroupActionConfig from './UserGroupManager'; -import quotaActionConfig from './QuotaManager'; +import Delete from './Delete'; +import Edit from './Edit'; +import Enable from './Enable'; +import Forbidden from './Forbidden'; +import Create from './Create'; +import ManageUser from './ManageUser'; +import ManageUserGroup from './ManageUserGroup'; +import ManageQuota from './ManageQuota'; import ModifyTags from './ModifyTags'; const actionConfigs = { rowActions: { - firstAction: editActionConfig, + firstAction: Edit, moreActions: [ { - action: deleteActionConfig, + action: Delete, }, { - action: quotaActionConfig, + action: ManageQuota, }, { - action: userActionConfig, + action: ManageUser, }, { - action: userGroupActionConfig, + action: ManageUserGroup, }, { - action: enableActionConfig, + action: Enable, }, { - action: forbiddenActionConfig, + action: Forbidden, }, { action: ModifyTags, }, ], }, - batchActions: [deleteActionConfig], - primaryActions: [createActionConfig], + batchActions: [Delete], + primaryActions: [Create], }; export default actionConfigs; diff --git a/src/pages/identity/containers/Project/index.jsx b/src/pages/identity/containers/Project/index.jsx index 190e5062..f786e8fb 100644 --- a/src/pages/identity/containers/Project/index.jsx +++ b/src/pages/identity/containers/Project/index.jsx @@ -28,10 +28,6 @@ export class Projects extends Base { this.store = this.inDetailPage ? new ProjectStore() : globalProjectStore; } - get tabs() { - return []; - } - get policy() { return 'identity:list_projects'; } @@ -59,52 +55,163 @@ export class Projects extends Base { return this.inDetailPage && pathname.includes('user-group-admin/detail'); } - getColumns() { - const columns = [ + get inDomainDetail() { + const { pathname } = this.props.location; + return this.inDetailPage && pathname.includes('domain-admin/detail'); + } + + getUserProjectRole = (record) => { + // return [{role, groups}] + const { users = {}, groups = {} } = record || {}; + const userRoleIds = []; + const result = []; + Object.keys(users).forEach((id) => { + const roles = users[id]; + roles.forEach((r) => { + result.push({ + role: r, + }); + userRoleIds.push(r.id); + }); + }); + Object.keys(groups).forEach((groupId) => { + const { roles, group } = groups[groupId]; + const leftRoles = roles.filter((r) => !userRoleIds.includes(r.id)); + leftRoles.forEach((r) => { + const resultItem = result.find((it) => it.role.id === r.id); + if (resultItem) { + resultItem.groups.push(group); + } else { + result.push({ + role: r, + groups: [group], + }); + } + }); + }); + return result; + }; + + getBaseColumns() { + const userProjectRole = { + title: t('Role'), + dataIndex: 'userProjectRole', + render: (_, record) => { + const roles = this.getUserProjectRole(record); + const links = roles.map((it) => { + const { + role: { id, name }, + groups = [], + } = it; + if (!groups.length) { + const link = this.getLinkRender( + 'roleDetail', + name, + { id }, + { tab: 'user' } + ); + return
{link}
; + } + const roleGroupLink = this.getLinkRender( + 'roleDetail', + name, + { id }, + { tab: 'groups' } + ); + const groupLinks = groups.map((g) => { + const link = this.getLinkRender('groupDetail', g.name, { + id: g.id, + }); + return {link}; + }); + return ( +
+ {roleGroupLink} ({t('authorized by group ')} + {groupLinks}) +
+ ); + }); + return
{links}
; + }, + stringify: (_, record) => { + const roles = this.getUserProjectRole(record); + return roles + .map((it) => { + const { + role: { name }, + groups = [], + } = it; + if (!groups.length) { + return name; + } + const groupNames = groups.map((g) => g.name).join('; '); + return `${name} (${t('authorized by group ')}${groupNames})`; + }) + .join('; '); + }, + }; + + const groupProjectRole = { + title: t('Role'), + dataIndex: 'groupProjectRole', + render: (_, record) => { + const { groups: projectRole = {} } = record; + const tab = 'group'; + return Object.keys(projectRole).map((id) => { + const roles = projectRole[id]; + return roles.map((role) => { + const { id: roleId, name } = role; + const link = this.getLinkRender( + 'roleDetail', + name, + { id: roleId }, + { tab } + ); + return
{link}
; + }); + }); + }, + stringify: (_, record) => { + const { groups: projectRole = {} } = record; + return Object.keys(projectRole).map((id) => { + const roles = projectRole[id]; + return roles.map((role) => role.name).join(' ; '); + }); + }, + }; + return [ { title: t('Project ID/Name'), dataIndex: 'name', routeName: 'projectDetailAdmin', }, - { - title: t('Role'), - dataIndex: 'projectRole', - render: (roles, value) => { - const { groupProjectRole = [] } = value; - const rolesAll = [...(roles || []), ...(groupProjectRole || [])]; - return (rolesAll || []).map((it, idx) =>
{it}
); - }, - stringify: (roles, value) => { - const { groupProjectRole = [] } = value; - const rolesAll = [...(roles || []), ...(groupProjectRole || [])]; - return (rolesAll || []).join(';'); - }, - }, + userProjectRole, + groupProjectRole, { title: t('Member Num'), dataIndex: 'num', isHideable: true, render: (name, record) => { - const { user_num, group_num } = record; + const { userCount, groupCount } = record; return (
{t('User Num: ')} - {user_num} + {userCount} {t('User Group Num: ')} - {group_num} + {groupCount}
); }, stringify: (name, record) => { - const { user_num, group_num } = record; - return `${t('User Num: ')}${user_num} | ${t( + const { userCount, groupCount } = record; + return `${t('User Num: ')}${userCount} | ${t( 'User Group Num: ' - )}${group_num}`; + )}${groupCount}`; }, }, enabledColumn, @@ -120,16 +227,32 @@ export class Projects extends Base { isHideable: true, }, ]; + } - if (this.inProject) { - return columns.filter((it) => it.dataIndex !== 'projectRole'); + getColumns() { + const columns = this.getBaseColumns(); + + if (this.inProject || this.inDomainDetail) { + return columns.filter( + (it) => !['userProjectRole', 'groupProjectRole'].includes(it.dataIndex) + ); + } + if (this.inUserDetail) { + return columns.filter( + (it) => !['num', 'groupProjectRole'].includes(it.dataIndex) + ); + } + if (this.inUserGroupDetail) { + return columns.filter( + (it) => !['num', 'userProjectRole'].includes(it.dataIndex) + ); } return columns; } get actionConfigs() { - if (this.inUserDetail || this.inUserGroupDetail) { + if (this.inDetailPage) { return emptyActionConfig; } return actionConfigs; @@ -157,22 +280,19 @@ export class Projects extends Base { ]; } - async getData({ silent, ...params } = {}) { + updateFetchParams = (params) => { const { match } = this.props; const { id } = match.params || {}; const newParams = { ...params }; - silent && (this.list.silent = true); if (this.inUserDetail) { newParams.userId = id; - await this.store.fetchListInUserDetail(newParams); } else if (this.inUserGroupDetail) { newParams.groupId = id; - await this.store.fetchListInGroupDetail(newParams); - } else { - await this.store.fetchList(newParams); + } else if (this.inDomainDetail) { + newParams.domain_id = id; } - this.list.silent = false; - } + return newParams; + }; } export default inject('rootStore')(observer(Projects)); diff --git a/src/resources/keystone/project.js b/src/resources/keystone/project.js index bb88dbff..2c8b4c62 100644 --- a/src/resources/keystone/project.js +++ b/src/resources/keystone/project.js @@ -33,7 +33,7 @@ export const projectColumns = [ }, { title: t('User Num'), - dataIndex: 'user_num', + dataIndex: 'userCount', }, { title: t('Enabled'), diff --git a/src/stores/keystone/project.js b/src/stores/keystone/project.js index fbeca304..deb54249 100644 --- a/src/stores/keystone/project.js +++ b/src/stores/keystone/project.js @@ -14,7 +14,7 @@ import { action, observable } from 'mobx'; import { getGiBValue } from 'utils/index'; -import { get, isNil, isEmpty } from 'lodash'; +import { isNil, isEmpty } from 'lodash'; import client from 'client'; import Base from 'stores/base'; import globalRootStore from 'stores/root'; @@ -23,22 +23,17 @@ export class ProjectStore extends Base { @observable quota = {}; - @observable - userRoleList = []; - @observable groupRoleList = []; - @observable - domains = []; - - @observable - projectsOnly = []; - get client() { return client.keystone.projects; } + get domainClient() { + return client.keystone.domains; + } + get roleAssignmentClient() { return client.keystone.roleAssignments; } @@ -67,129 +62,125 @@ export class ProjectStore extends Base { return client.manila.quotaSets; } - async fetchProjects(filters) { - const { tags } = filters; - - const [roleAssignmentsResult, projectsResult, roleResult] = - await Promise.all([ - this.roleAssignmentClient.list(), - this.client.list(tags ? { tags } : {}), - this.roleClient.list(), - ]); - const { projects } = projectsResult; - const { roles } = roleResult; - const projectRoles = roles.filter( - (it) => - (it.name.indexOf('project_') !== -1 && - it.name.indexOf('_project_') === -1) || - it.name === 'admin' - ); - const projectRoleId = projectRoles.map((it) => it.id); - projects.map((project) => { - const userMapRole = {}; // all user include system role and project role: { user_id: [roles_id] } - const projectGroups = {}; - const userMapProjectRoles = {}; // { user_id: [projectRoles_id] } - roleAssignmentsResult.role_assignments.forEach((roleAssignment) => { - this.getUsersAndGroups( - project, - roleAssignment, - userMapRole, - projectGroups - ); - }); - this.getUsersProjectRole(userMapRole, userMapProjectRoles, projectRoleId); - project.users = userMapRole; - project.userMapProjectRoles = userMapProjectRoles; - project.groups = projectGroups; - project.user_num = Object.keys(userMapRole).length; - project.group_num = Object.keys(projectGroups).length; - return project; - }); - return projects; + listFetchByClient(params, originParams) { + const { userId } = originParams; + if (userId) { + return this.userClient.projects.list(userId, params); + } + return this.client.list(params); } - @action - async fetchList({ - limit, - page, - sortKey, - sortOrder, - conditions, - ...filters - } = {}) { - this.list.isLoading = true; - const items = await this.fetchProjects(filters); - const data = await this.listDidFetch(items, true, filters); - const newData = data.map(this.mapper); - this.list.update({ - data: newData, - total: newData.length || 0, - limit: Number(limit) || 10, - page: Number(page) || 1, - sortKey, - sortOrder, - filters, - isLoading: false, - ...(this.list.silent ? {} : { selectedRowKeys: [] }), - }); - return items; - } - - @action - getUsersProjectRole = (userMapRole, userMapProjectRoles, projectRoleId) => { - const projectUser = Object.keys(userMapRole); - projectUser.forEach((user_id) => { - const roles = userMapRole[user_id].filter( - (role_id) => projectRoleId.indexOf(role_id) !== -1 - ); - if (roles[0]) { - userMapProjectRoles[user_id] = roles; - } - }); - }; - - @action - getUsersAndGroups = (project, roleAssignment, userMapRole, projectGroups) => { - if (roleAssignment.user) { - const { - user: { id: user_id }, - role: { id: role_id }, - scope: { project: { id } = {} } = {}, - } = roleAssignment; - if (id && id === project.id) { - if (userMapRole[user_id]) { - userMapRole[user_id].push(role_id); - } else { - userMapRole[user_id] = [role_id]; - } - } - } - if (roleAssignment.group) { - const { - group: { id: group_id }, - role: { id: role_id }, - scope: { project: { id } = {} } = {}, - } = roleAssignment; - if (id && id === project.id) { - if (projectGroups[group_id]) { - projectGroups[group_id].push(role_id); - } else { - projectGroups[group_id] = [role_id]; - } - } - } - }; - - get mapper() { - return (item) => { - const domain = this.domains.filter((it) => it.id === item.domain_id); - if (domain[0]) { - item.domain_name = domain[0].name; - } - return item; + get paramsFunc() { + return (params) => { + const { id, userId, groupId, withRole, ...rest } = params; + return rest; }; } + updateProject = (project, roleAssignments, roles, domains) => { + const userMapRoles = {}; + const groupMapRoles = {}; + const { id } = project || {}; + roleAssignments.forEach((roleAssignment) => { + const { + scope: { project: { id: projectId } = {} } = {}, + user: { id: userId } = {}, + group: { id: groupId } = {}, + role: { id: roleId } = {}, + } = roleAssignment; + const roleItem = roles.find((it) => it.id === roleId); + if (projectId === id && roleId) { + if (userId) { + userMapRoles[userId] = userMapRoles[userId] + ? [...userMapRoles[userId], roleItem] + : [roleItem]; + } + if (groupId) { + groupMapRoles[groupId] = groupMapRoles[groupId] + ? [...groupMapRoles[groupId], roleItem] + : [roleItem]; + } + } + }); + const domain = domains.find((it) => it.id === project.domain_id); + return { + ...project, + users: userMapRoles, + groups: groupMapRoles, + userCount: Object.keys(userMapRoles).length, + groupCount: Object.keys(groupMapRoles).length, + domain, + domainName: (domain || {}).name || project.domain_id, + }; + }; + + async listDidFetch(items, allProjects, filters) { + if (!items.length) { + return items; + } + const { userId, groupId, withRole = true } = filters; + const params = {}; + if (groupId) { + params['group.id'] = groupId; + } + const [roleAssignmentResult, roleResult, domainResult] = await Promise.all([ + withRole ? this.roleAssignmentClient.list(params) : null, + withRole ? this.roleClient.list() : null, + this.domainClient.list(), + ]); + const { roles = [] } = roleResult || {}; + const { domains = [] } = domainResult; + const { role_assignments: assigns = [] } = roleAssignmentResult || {}; + const newItems = items.map((project) => { + return this.updateProject(project, assigns, roles, domains); + }); + if (userId) { + const { groups = [] } = await this.userClient.groups.list(userId); + return newItems.map((it) => { + const { users = {}, groups: groupMaps = {} } = it; + const currentUsers = users[userId] ? { [userId]: users[userId] } : {}; + const groupIds = groups.map((g) => g.id); + const currentGroups = Object.keys(groupMaps).reduce((pre, cur) => { + if (groupIds.includes(cur)) { + pre[cur] = { + roles: groupMaps[cur], + group: groups.find((g) => g.id === cur), + }; + } + return pre; + }, {}); + return { + ...it, + users: currentUsers, + userCount: Object.keys(currentUsers).length, + groups: currentGroups, + groupCount: Object.keys(currentGroups).length, + }; + }); + } + if (groupId) { + return newItems.filter((it) => !!it.groupCount); + } + return newItems; + } + + async detailDidFetch(item) { + const { id } = item; + const [roleAssignmentResult, roleResult, domainResult] = await Promise.all([ + this.roleAssignmentClient.list({ + 'scope.project.id': id, + }), + this.roleClient.list(), + this.domainClient.list(), + ]); + return this.updateProject( + item, + roleAssignmentResult.role_assignments, + roleResult.roles, + domainResult.domains + ); + } + get enableCinder() { return globalRootStore.checkEndpoint('cinder'); } @@ -214,12 +205,6 @@ export class ProjectStore extends Base { return this.submitting(this.client.patch(id, reqBody)); } - @action - async fetchDomain() { - const domainsResult = await this.skylineClient.domains(); - this.domains = domainsResult.domains; - } - @action async createProject(data) { const reqBody = { @@ -228,55 +213,6 @@ export class ProjectStore extends Base { return this.submitting(this.client.create(reqBody)); } - async fetchProject(id) { - const [roleAssignmentsResult, projectResult, roleResult] = - await Promise.all([ - this.roleAssignmentClient.list(), - this.client.show(id), - this.roleClient.list(), - ]); - const { roles } = roleResult; - const projectRoles = roles.filter( - (it) => - (it.name.indexOf('project_') !== -1 && - it.name.indexOf('_project_') === -1) || - it.name === 'admin' - ); - const projectRoleId = projectRoles.map((it) => it.id); - const { project } = projectResult; - const userMapRole = {}; - const projectGroups = {}; - const userMapProjectRoles = {}; - roleAssignmentsResult.role_assignments.forEach((roleAssignment) => { - this.getUsersAndGroups( - project, - roleAssignment, - userMapRole, - projectGroups - ); - }); - this.getUsersProjectRole(userMapRole, userMapProjectRoles, projectRoleId); - project.users = userMapRole; - project.userMapProjectRoles = userMapProjectRoles; - project.groups = projectGroups; - project.user_num = Object.keys(userMapRole).length; - project.group_num = Object.keys(projectGroups).length; - const newItem = await this.detailDidFetch(project); - return newItem; - } - - @action - async fetchDetail({ id, silent }) { - if (!silent) { - this.isLoading = true; - } - const item = await this.fetchProject(id); - const detail = this.mapper(item); - this.detail = detail; - this.isLoading = false; - return detail; - } - @action async edit({ id, description, name }) { const reqBody = { @@ -463,12 +399,6 @@ export class ProjectStore extends Base { return result; } - @action - async getUserRoleList({ id, user_id }) { - const result = await this.client.users.roles.list(id, user_id); - this.userRoleList = result.roles; - } - @action async create(data) { const body = {}; @@ -480,166 +410,31 @@ export class ProjectStore extends Base { } @action - async assignUserRole({ id, user_id, role_id }) { - const result = await this.client.users.roles.update(id, user_id, role_id); + async assignUserRole({ id, userId, roleId }) { + const result = await this.client.users.roles.update(id, userId, roleId); return result; } @action - async removeUserRole({ id, user_id, role_id }) { - const result = await this.client.users.roles.delete(id, user_id, role_id); + async removeUserRole({ id, userId, roleId }) { + const result = await this.client.users.roles.delete(id, userId, roleId); return result; } @action - async getGroupRoleList({ id, group_id }) { - const result = await this.client.groups.roles.list(id, group_id); + async getGroupRoleList({ id, groupId }) { + const result = await this.client.groups.roles.list(id, groupId); this.groupRoleList = result.roles; } @action - async assignGroupRole({ id, group_id, role_id }) { - const result = await this.client.groups.roles.update(id, group_id, role_id); + async assignGroupRole({ id, groupId, roleId }) { + const result = await this.client.groups.roles.update(id, groupId, roleId); return result; } - async removeGroupRole({ id, group_id, role_id }) { - const result = await this.client.groups.roles.delete(id, group_id, role_id); - return result; - } - - @action - async fetchListInUserDetail({ - limit, - page, - sortKey, - sortOrder, - conditions, - ...filters - } = {}) { - this.list.isLoading = true; - const { userId } = filters; - const [roleAssignmentsResult, projectsResult, roleResult, groupResult] = - await Promise.all([ - this.roleAssignmentClient.list(), - this.userClient.projects.list(userId), - this.roleClient.list(), - this.userClient.groups.list(userId), - ]); - const projects = get(projectsResult, this.listResponseKey, []); - projects.map((project) => { - const userMapRole = {}; - const projectGroups = {}; - roleAssignmentsResult.role_assignments.forEach((roleAssignment) => { - this.getUsersAndGroups( - project, - roleAssignment, - userMapRole, - projectGroups - ); - }); - project.users = userMapRole; - project.groups = projectGroups; - project.projectRole = []; - if (userMapRole[userId]) { - project.projectRole = userMapRole[userId].map( - (it) => roleResult.roles.filter((role) => role.id === it)[0].name - ); - } - groupResult.groups.forEach((group) => { - if (projectGroups[group.id]) { - project.groupProjectRole = projectGroups[group.id].map( - (it) => - `${roleResult.roles.filter((role) => role.id === it)[0].name}(${t( - 'user group' - )}: ${group.name})` - ); - } - }); - project.user_num = Object.keys(userMapRole).length; - project.group_num = Object.keys(projectGroups).length; - return project; - }); - - this.list.update({ - data: projects, - total: projects.length || 0, - limit: Number(limit) || 10, - page: Number(page) || 1, - sortKey, - sortOrder, - filters, - isLoading: false, - ...(this.list.silent ? {} : { selectedRowKeys: [] }), - }); - return projects; - } - - @action - async fetchListInGroupDetail({ - limit, - page, - sortKey, - sortOrder, - conditions, - ...filters - } = {}) { - this.list.isLoading = true; - const { groupId } = filters; - const [roleAssignmentsResult, projectsResult, roleResult] = - await Promise.all([ - this.roleAssignmentClient.list(), - this.client.list(), - this.roleClient.list(), - ]); - const projects = get(projectsResult, this.listResponseKey, []); - projects.map((project) => { - const userMapRole = {}; - const projectGroups = {}; - roleAssignmentsResult.role_assignments.forEach((roleAssignment) => { - this.getUsersAndGroups( - project, - roleAssignment, - userMapRole, - projectGroups - ); - }); - project.users = userMapRole; - project.groups = projectGroups; - project.projectRole = []; - if (projectGroups[groupId]) { - project.projectRole = projectGroups[groupId].map( - (it) => roleResult.roles.filter((role) => role.id === it)[0].name - ); - } - project.user_num = Object.keys(userMapRole).length; - project.group_num = Object.keys(projectGroups).length; - return project; - }); - - const groupProjects = projects.filter( - (it) => Object.keys(it.groups).indexOf(groupId) >= 0 - ); - this.list.update({ - data: groupProjects, - total: groupProjects.length || 0, - limit: Number(limit) || 10, - page: Number(page) || 1, - sortKey, - sortOrder, - filters, - isLoading: false, - ...(this.list.silent ? {} : { selectedRowKeys: [] }), - }); - return groupProjects; - } - - @action - async fetchProjectListOnly() { - this.list.isLoading = true; - const result = await this.client.list(); - this.projectsOnly = get(result, this.listResponseKey, []); - this.list.isLoading = false; + async removeGroupRole({ id, groupId, roleId }) { + const result = await this.client.groups.roles.delete(id, groupId, roleId); return result; } } diff --git a/src/stores/keystone/user.js b/src/stores/keystone/user.js index 8484ae62..cdb70a0c 100644 --- a/src/stores/keystone/user.js +++ b/src/stores/keystone/user.js @@ -102,16 +102,16 @@ export class UserStore extends Base { }); select_project.forEach((id) => { if (!newProjects.includes(id)) { - const role_id = defaultRole; + const roleId = defaultRole; promiseList.push( - globalProjectStore.assignUserRole({ id, user_id, role_id }) + globalProjectStore.assignUserRole({ id, userId: user_id, roleId }) ); } else { promiseList.push( globalProjectStore.assignUserRole({ id, - user_id, - role_id: newProjectRoles[id], + userId: user_id, + roleId: newProjectRoles[id], }) ); }