fix: show project id and filter data by it

Show project id and filter data by it when create nework,subnet,fip,qos in management platform

Closes-Bug: #2037275
Change-Id: I4e12b423be7662ba83f8efbf55e1b6aa0e31105d
This commit is contained in:
xusongfu 2023-09-25 18:27:22 +08:00
parent 7662ae6a09
commit ec21bd17ab
25 changed files with 279 additions and 93 deletions

View File

@ -166,7 +166,7 @@
}
.magic-input-checks {
min-width: 120px;
min-width: 158px;
margin-left: 8px;
line-height: 32px;
}

View File

@ -56,7 +56,7 @@ export class ManageAccess extends ModalAction {
}
async getProjects() {
await this.projectStore.fetchList();
await this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}

View File

@ -48,8 +48,9 @@ export class AccessTypeSetting extends Base {
];
}
getProjects() {
this.projectStore.fetchList();
async getProjects() {
this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}
get formItems() {

View File

@ -31,7 +31,7 @@ export class CreateForm extends FormAction {
init() {
this.store = globalImageStore;
this.projectStore = new ProjectStore();
this.getProjects();
this.isAdminPage && this.getProjects();
}
static id = 'image-create';
@ -72,10 +72,9 @@ export class CreateForm extends FormAction {
return Promise.resolve(true);
}
getProjects() {
if (this.isAdminPage) {
this.projectStore.fetchList();
}
async getProjects() {
await this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}
get projects() {

View File

@ -53,7 +53,7 @@ export class ManageAccess extends ModalAction {
}
async getProjects() {
await this.projectStore.fetchList();
await this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}

View File

@ -257,6 +257,10 @@ export class Instance extends Base {
label: t('Project Name'),
name: 'project_name',
},
{
label: t('Project ID'),
name: 'project_id',
},
]
: []),
...(this.isAdminPage && !this.inHostDetailPage

View File

@ -284,6 +284,10 @@ export class Projects extends Base {
label: t('Project Name'),
name: 'name',
},
{
label: t('Project ID'),
name: 'id',
},
{
label: t('Enabled'),
name: 'enabled',

View File

@ -21,6 +21,10 @@ import parsePhoneNumberFromString from 'libphonenumber-js';
export class EditForm extends ModalAction {
init() {
this.store = globalUserStore;
const {
list: { data },
} = this.store;
data.length === 0 && this.store.fetchList();
}
static id = 'user-edit';

View File

@ -22,6 +22,9 @@ import globalSubnetStore from 'stores/neutron/subnet';
import { QoSPolicyStore } from 'stores/neutron/qos-policy';
import { getQoSPolicyTabs } from 'resources/neutron/qos-policy';
import { qosEndpoint } from 'client/client/constants';
import { projectTableOptions } from 'resources/keystone/project';
import { isAdminPage } from 'utils';
import { toJS } from 'mobx';
export class Allocate extends ModalAction {
static id = 'allocate';
@ -33,11 +36,12 @@ export class Allocate extends ModalAction {
}
static get modalSize() {
return qosEndpoint() ? 'large' : 'small';
const { pathname } = window.location;
return qosEndpoint() || isAdminPage(pathname) ? 'large' : 'small';
}
getModalSize() {
return qosEndpoint() ? 'large' : 'small';
return qosEndpoint() || this.isAdminPage ? 'large' : 'small';
}
get qosEndpoint() {
@ -63,10 +67,15 @@ export class Allocate extends ModalAction {
maxCount: 2,
};
this.getExternalNetworks();
this.isAdminPage && globalProjectStore.fetchList();
this.isAdminPage && this.fetchProjectList();
this.getQuota();
}
async fetchProjectList() {
await this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}
async getExternalNetworks() {
const networks = await this.networkStore.pureFetchList({
'router:external': true,
@ -80,6 +89,10 @@ export class Allocate extends ModalAction {
return false;
}
get projects() {
return toJS(this.projectStore.list.data) || [];
}
static policy = 'create_floatingip';
static allowed = () => Promise.resolve(true);
@ -151,10 +164,15 @@ export class Allocate extends ModalAction {
}
get defaultValue() {
return {
project_id: this.currentProjectId,
const values = {
count: 2,
};
if (this.isAdminPage) {
values.project_id = {
selectedRowKeys: [this.currentProjectId],
};
}
return values;
}
handleNetworkChange = async (networkId) => {
@ -181,7 +199,14 @@ export class Allocate extends ModalAction {
});
};
onSubmit = ({ subnet_id, batch_allocate, count, qos_policy_id, ...rest }) => {
onSubmit = ({
subnet_id,
batch_allocate,
count,
qos_policy_id,
project_id,
...rest
}) => {
const data = rest;
if (subnet_id) {
data.subnet_id = subnet_id.value;
@ -197,7 +222,12 @@ export class Allocate extends ModalAction {
}
return Promise.all(promises);
}
return this.store.create(data);
return this.store.create({
...data,
project_id: project_id
? project_id.selectedRowKeys[0]
: this.currentProjectId,
});
};
onCountChange = (value) => {
@ -207,9 +237,10 @@ export class Allocate extends ModalAction {
};
onProjectChange = (value) => {
const { selectedRowKeys } = value;
this.setState(
{
projectId: value,
projectId: selectedRowKeys[0],
},
() => {
this.getQuota();
@ -230,10 +261,6 @@ export class Allocate extends ModalAction {
label: item.name,
value: item.id,
}));
const projectOptions = globalProjectStore.list.data.map((project) => ({
label: project.name,
value: project.id,
}));
return [
{
name: 'floating_network_id',
@ -246,12 +273,13 @@ export class Allocate extends ModalAction {
{
name: 'project_id',
label: t('Project'),
type: 'select',
showSearch: true,
type: 'select-table',
hidden: !this.isAdminPage,
required: this.isAdminPage,
options: projectOptions,
isLoading: this.projectStore.list.isLoading,
data: this.projects,
onChange: this.onProjectChange,
...projectTableOptions,
},
{
name: 'subnet_id',

View File

@ -22,6 +22,9 @@ import Notify from 'components/Notify';
import { checkSystemAdmin } from 'resources/skyline/policy';
import globalNeutronStore from 'stores/neutron/neutron';
import { subnetIpv6Tip } from 'resources/neutron/network';
import { projectTableOptions } from 'resources/keystone/project';
import { isAdminPage } from 'utils';
import { toJS } from 'mobx';
import networkUtil from './networkUtil';
const {
@ -59,6 +62,15 @@ export class CreateNetwork extends ModalAction {
return t('create network');
}
static get modalSize() {
const { pathname } = window.location;
return isAdminPage(pathname) ? 'large' : 'small';
}
getModalSize() {
return this.isAdminPage ? 'large' : 'small';
}
init() {
globalNetworkStore.updateCreateWithSubnet(false);
this.state.networkQuota = {};
@ -68,7 +80,7 @@ export class CreateNetwork extends ModalAction {
this.state.projectId = this.currentProjectId;
this.projectStore = globalProjectStore;
globalNeutronStore.fetchAvailableZones();
this.isAdminPage && globalProjectStore.fetchList();
this.isAdminPage && this.fetchProjectList();
this.getQuota();
}
@ -89,6 +101,15 @@ export class CreateNetwork extends ModalAction {
return true;
}
async fetchProjectList() {
await this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}
get projects() {
return toJS(this.projectStore.list.data) || [];
}
get showQuota() {
return true;
}
@ -136,8 +157,7 @@ export class CreateNetwork extends ModalAction {
}
get defaultValue() {
return {
project_id: this.currentProjectId,
const values = {
enable_dhcp: true,
provider_network_type: 'vxlan',
ip_version: 'ipv4',
@ -147,6 +167,12 @@ export class CreateNetwork extends ModalAction {
ipv6_ra_mode: 'slaac',
ipv6_address_mode: 'slaac',
};
if (this.isAdminPage) {
values.project_id = {
selectedRowKeys: [this.currentProjectId],
};
}
return values;
}
onSubmit = (values) => {
@ -188,7 +214,9 @@ export class CreateNetwork extends ModalAction {
const networkAdminPageData = {
'router:external': external_network,
project_id,
project_id: project_id
? project_id.selectedRowKeys[0]
: this.currentProjectId,
'provider:network_type': provider_network_type,
'provider:physical_network': provider_physical_network,
'provider:segmentation_id': provider_segmentation_id,
@ -314,9 +342,10 @@ export class CreateNetwork extends ModalAction {
};
onProjectChange = (value) => {
const { selectedRowKeys } = value;
this.setState(
{
projectId: value,
projectId: selectedRowKeys[0],
},
() => {
this.getQuota();
@ -339,10 +368,6 @@ export class CreateNetwork extends ModalAction {
ip_version = 'ipv4',
disable_gateway = false,
} = this.state;
const projectOptions = globalProjectStore.list.data.map((project) => ({
label: project.name,
value: project.id,
}));
const hiddenPhysicalNetwork =
this.isAdminPage &&
@ -426,13 +451,13 @@ export class CreateNetwork extends ModalAction {
{
name: 'project_id',
label: t('Project'),
type: 'select',
showSearch: true,
type: 'select-table',
hidden: !this.isAdminPage,
required: this.isAdminPage,
options: projectOptions,
isLoading: this.projectStore.list.isLoading,
data: this.projects,
onChange: this.onProjectChange,
allowClear: false,
...projectTableOptions,
},
{
name: 'provider_network_type',

View File

@ -20,6 +20,8 @@ import { isEmpty } from 'lodash';
import globalProjectStore from 'stores/keystone/project';
import globalRootStore from 'stores/root';
import { subnetIpv6Tip } from 'resources/neutron/network';
import { projectTableOptions } from 'resources/keystone/project';
import { toJS } from 'mobx';
import networkUtil from './networkUtil';
const {
@ -43,6 +45,14 @@ export class CreateSubnet extends ModalAction {
return t('Create Subnet');
}
static get modalSize() {
return globalRootStore.hasAdminRole ? 'large' : 'small';
}
getModalSize() {
return this.isSystemAdmin ? 'large' : 'small';
}
get network() {
return this.props.containerProps.detail || this.item || {};
}
@ -52,15 +62,20 @@ export class CreateSubnet extends ModalAction {
}
get defaultValue() {
return {
const values = {
enable_dhcp: true,
ip_version: 'ipv4',
project_id: this.currentProjectId,
disable_gateway: false,
more: false,
ipv6_ra_mode: 'slaac',
ipv6_address_mode: 'slaac',
};
if (this.isSystemAdmin) {
values.project_id = {
selectedRowKeys: [this.currentProjectId],
};
}
return values;
}
init() {
@ -72,8 +87,13 @@ export class CreateSubnet extends ModalAction {
this.getQuota();
}
getProjects() {
this.projectStore.fetchList();
async getProjects() {
await this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}
get projects() {
return toJS(this.projectStore.list.data) || [];
}
static get disableSubmit() {
@ -139,7 +159,7 @@ export class CreateSubnet extends ModalAction {
};
onSubmit = (values) => {
const { allocation_pools, host_routes, ...rest } = values;
const { allocation_pools, host_routes, project_id, ...rest } = values;
const allocationPools = getAllocationPools(allocation_pools);
@ -147,6 +167,9 @@ export class CreateSubnet extends ModalAction {
return globalNetworkStore.createSubnet({
...rest,
project_id: project_id
? project_id.selectedRowKeys[0]
: this.currentProjectId,
network_id: this.network.id,
allocation_pools: allocationPools,
host_routes: hostRouters,
@ -180,9 +203,10 @@ export class CreateSubnet extends ModalAction {
};
onProjectChange = (value) => {
const { selectedRowKeys } = value;
this.setState(
{
projectId: value,
projectId: selectedRowKeys[0],
},
() => {
this.getQuota();
@ -207,10 +231,6 @@ export class CreateSubnet extends ModalAction {
projectId,
} = this.state;
const isIpv4 = ip_version === 'ipv4';
const projectOptions = globalProjectStore.list.data.map((project) => ({
label: project.name,
value: project.id,
}));
return [
{
@ -223,17 +243,18 @@ export class CreateSubnet extends ModalAction {
{
name: 'project_id',
label: t('Project'),
type: 'select',
type: 'select-table',
required: true,
hidden: !this.isSystemAdmin,
showSearch: true,
extra:
projectId !== this.networkProjectId &&
t(
'The selected project is different from the project to which the network belongs. That is, the subnet to be created is not under the same project as the network. Please do not continue unless you are quit sure what you are doing.'
),
options: projectOptions,
isLoading: this.projectStore.list.isLoading,
data: this.projects,
onChange: this.onProjectChange,
...projectTableOptions,
},
{
name: 'ip_version',

View File

@ -16,6 +16,9 @@ import { inject, observer } from 'mobx-react';
import { ModalAction } from 'containers/Action';
import globalProjectStore from 'stores/keystone/project';
import { QoSPolicyStore } from 'stores/neutron/qos-policy';
import { projectTableOptions } from 'resources/keystone/project';
import { isAdminPage } from 'utils';
import { toJS } from 'mobx';
export class Create extends ModalAction {
static id = 'create_qos_policy';
@ -26,6 +29,15 @@ export class Create extends ModalAction {
return t('Create QoS Policy');
}
static get modalSize() {
const { pathname } = window.location;
return isAdminPage(pathname) ? 'large' : 'small';
}
getModalSize() {
return this.isAdminPage ? 'large' : 'small';
}
static policy = 'create_policy';
static aliasPolicy = 'neutron:create_policy';
@ -34,13 +46,28 @@ export class Create extends ModalAction {
init() {
this.store = new QoSPolicyStore();
this.isAdminPage && globalProjectStore.fetchList();
this.projectStore = globalProjectStore;
this.isAdminPage && this.fetchProjectList();
}
async fetchProjectList() {
await this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}
get projects() {
return toJS(this.projectStore.list.data) || [];
}
get defaultValue() {
return {
project_id: this.props.rootStore.user.project.id,
};
if (this.isAdminPage) {
return {
project_id: {
selectedRowKeys: [this.props.rootStore.user.project.id],
},
};
}
return {};
}
onSubmit = (values) => {
@ -50,16 +77,13 @@ export class Create extends ModalAction {
description,
shared,
is_default,
project_id,
project_id: project_id
? project_id.selectedRowKeys[0]
: this.props.rootStore.user.project.id,
});
};
get formItems() {
const projects = globalProjectStore.list.data.map((item) => ({
label: item.name,
value: item.id,
}));
return [
{
name: 'name',
@ -71,11 +95,12 @@ export class Create extends ModalAction {
{
name: 'project_id',
label: t('Project'),
type: 'select',
showSearch: true,
required: true,
options: projects,
type: 'select-table',
required: this.isAdminPage,
isLoading: globalProjectStore.list.isLoading,
data: this.projects,
hidden: !this.isAdminPage,
...projectTableOptions,
},
{
name: 'description',

View File

@ -42,8 +42,9 @@ export class Create extends ModalAction {
this.typeStore.fetchList({ is_public: 'all' });
}
getProjects() {
this.projectStore.fetchList();
async getProjects() {
await this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}
get projects() {

View File

@ -57,7 +57,7 @@ export class ManageAccess extends ModalAction {
}
async getProjects() {
await this.projectStore.fetchList();
await this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}

View File

@ -60,8 +60,9 @@ export class Create extends ModalAction {
this.getProjects();
}
getProjects() {
this.projectStore.fetchList();
async getProjects() {
await this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}
get projects() {

View File

@ -55,7 +55,7 @@ export class ManageAccess extends ModalAction {
}
async getProjects() {
await this.projectStore.fetchList();
await this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}

View File

@ -38,8 +38,9 @@ export class Create extends ModalAction {
// this.getServices();
}
getProjects() {
this.projectStore.fetchList();
async getProjects() {
await this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}
get projects() {

View File

@ -57,7 +57,7 @@ export class ManageAccess extends ModalAction {
}
async getProjects() {
await this.projectStore.fetchList();
await this.projectStore.fetchProjectsWithDomain();
this.updateDefaultValue();
}

View File

@ -1,5 +1,6 @@
import React from 'react';
import { Badge } from 'antd';
import { getIdRender } from 'utils/table';
import globalDomainStore from 'stores/keystone/domain';
import globalRootStore from 'src/stores/root';
@ -73,15 +74,35 @@ export const enabledColumn = {
export const nameDomainColumns = [
{
dataIndex: 'name',
title: t('Name'),
title: t('Project ID/Name'),
render: (value, record) => {
return (
<>
<div>{getIdRender(record.id, true, false)}</div>
<div>{value}</div>
</>
);
},
},
{
dataIndex: 'domainName',
title: t('Domain'),
title: t('Domain ID/Name'),
render: (value, record) => {
return (
<>
<div>{getIdRender(record.domain_id, true, false)}</div>
<div>{value}</div>
</>
);
},
},
];
export const transferFilterOption = (inputValue, record) => {
const { domainName, name } = record;
return name.includes(inputValue) || domainName.includes(inputValue);
const { domainName, name, id } = record;
return (
id.includes(inputValue) ||
name.includes(inputValue) ||
domainName.includes(inputValue)
);
};

View File

@ -12,13 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import React from 'react';
import { yesNoOptions } from 'utils/constants';
import { getIdRender } from 'utils/table';
export const projectFilter = [
{
label: t('Project Name'),
name: 'name',
},
{
label: t('Project ID'),
name: 'id',
},
{
label: t('Domain Name'),
name: 'domainName',
},
{
label: t('Domain ID'),
name: 'domain_id',
},
{
label: t('Enabled'),
name: 'enabled',
@ -28,12 +42,30 @@ export const projectFilter = [
export const projectColumns = [
{
title: t('Project Name'),
title: t('Project ID/Name'),
dataIndex: 'name',
render: (value, record) => {
const idRender = getIdRender(record.id, true, false);
return (
<>
<div>{idRender}</div>
<div>{value}</div>
</>
);
},
},
{
title: t('User Num'),
dataIndex: 'userCount',
title: t('Domain ID/Name'),
dataIndex: 'domainName',
render: (value, record) => {
const idRender = getIdRender(record.domain_id, true, false);
return (
<>
<div>{idRender}</div>
<div>{value}</div>
</>
);
},
},
{
title: t('Enabled'),

View File

@ -218,6 +218,24 @@ export class ProjectStore extends Base {
);
}
async fetchProjectsWithDomain() {
this.list.isLoading = true;
const [{ projects = [] }, { domains = [] }] = await Promise.all([
this.client.list(),
this.domainClient.list(),
]);
const newProjects = projects.map((pro) => {
const domain = domains.find((it) => it.id === pro.domain_id);
pro.domainName = domain?.name;
return pro;
});
this.list.update({
data: newProjects,
total: newProjects.length || 0,
isLoading: false,
});
}
get enableCinder() {
return globalRootStore.checkEndpoint('cinder');
}

View File

@ -50,6 +50,7 @@ describe('The Project Page', () => {
it('successfully edit quota', () => {
cy.tableSearchText(name)
.clickActionInMore('Edit Quota')
.wait(5000)
.formInput('instances', 11)
.formButtonClick('more')
.wait(2000)

View File

@ -153,7 +153,7 @@ Cypress.Commands.add('setLanguageByPage', () => {
.find('.ant-col')
.last()
.find('button')
.trigger('mouseover');
.trigger('mouseover', { force: true });
cy.get('.ant-dropdown-menu-light')
.find('li')
.eq(2)

View File

@ -39,7 +39,7 @@ Cypress.Commands.add('clickDetailActionInMore', (title, waitTime = 2000) => {
cy.get('.detail-main')
.first()
.find('.ant-dropdown-trigger')
.trigger('mouseover');
.trigger('mouseover', { force: true });
const realTitle = getTitle(title);
cy.get('ul.ant-dropdown-menu-light')
.contains(realTitle)

View File

@ -56,7 +56,7 @@ Cypress.Commands.add(
cy.get('.table-header-btns')
.find('.ant-dropdown-trigger')
.contains(moreTitle)
.trigger('mouseover');
.trigger('mouseover', { force: true });
cy.get('ul.ant-dropdown-menu-light')
.last()
.find('button')
@ -95,7 +95,7 @@ Cypress.Commands.add('clickMoreActionButton', (buttonIndex) => {
cy.get('.ant-table-row')
.first()
.find('.ant-dropdown-trigger')
.trigger('mouseover');
.trigger('mouseover', { force: true });
cy.get('ul.ant-dropdown-menu-light')
.find('li')
.eq(buttonIndex)
@ -106,7 +106,7 @@ Cypress.Commands.add('clickActionInMore', (title, waitTime = 2000) => {
cy.get('.ant-table-row')
.first()
.find('.ant-dropdown-trigger')
.trigger('mouseover');
.trigger('mouseover', { force: true });
const realTitle = getTitle(title);
cy.get('ul.ant-dropdown-menu-light')
.contains(realTitle)
@ -118,12 +118,12 @@ Cypress.Commands.add('clickActionInMoreSub', (title, subMenu) => {
cy.get('.ant-table-row')
.first()
.find('.ant-dropdown-trigger')
.trigger('mouseover');
.trigger('mouseover', { force: true });
const realTitle = getTitle(title);
const realMenu = getTitle(subMenu);
cy.get('.ant-dropdown-menu-submenu-title')
.contains(realMenu)
.trigger('mouseover');
.trigger('mouseover', { force: true });
cy.get('.ant-dropdown-menu-submenu-popup')
.last()
.find('button')
@ -190,7 +190,7 @@ Cypress.Commands.add('checkActionDisabled', (title) => {
cy.get('.ant-table-row')
.first()
.find('.ant-dropdown-trigger')
.trigger('mouseover');
.trigger('mouseover', { force: true });
cy.get('ul.ant-dropdown-menu-light').contains(realTitle).should('not.exist');
});
@ -204,7 +204,7 @@ Cypress.Commands.add('checkActionDisabledInFirstRow', (title, name) => {
.get('.ant-table-row')
.first()
.find('.ant-dropdown-trigger')
.trigger('mouseover')
.trigger('mouseover', { force: true })
.get('ul.ant-dropdown-menu-light')
.contains(realTitle)
.should('not.exist');
@ -228,7 +228,7 @@ Cypress.Commands.add('clickConfirmActionInMore', (title, waitTime) => {
cy.get('.ant-table-row')
.first()
.find('.ant-dropdown-trigger')
.trigger('mouseover');
.trigger('mouseover', { force: true });
const realTitle = getTitle(title);
cy.get('ul.ant-dropdown-menu-light')
.contains(realTitle)
@ -254,12 +254,12 @@ Cypress.Commands.add(
cy.get('.ant-table-row')
.first()
.find('.ant-dropdown-trigger')
.trigger('mouseover');
.trigger('mouseover', { force: true });
const realTitle = getTitle(title);
const realMenu = getTitle(subMenu);
cy.get('.ant-dropdown-menu-submenu-title')
.contains(realMenu)
.trigger('mouseover');
.trigger('mouseover', { force: true });
cy.get('.ant-dropdown-menu-submenu-popup')
.last()
.find('button')