From b5b233a9f06ab3f930b3882162f38bce5dc4a5f8 Mon Sep 17 00:00:00 2001 From: Ahmed Abdelsalam Date: Thu, 7 Dec 2023 18:07:19 +0100 Subject: [PATCH] Change: Make use of GMP command get_resource_names to load tag resources. Show a loading indicator to the user when data is being loaded. --- src/gmp/commands/resourcenames.js | 85 +++++++++++++++++++++++++++++++ src/gmp/gmp.js | 1 + src/gmp/models/resourcename.js | 36 +++++++++++++ src/web/pages/tags/dialog.js | 64 ++++++++++++++++------- 4 files changed, 167 insertions(+), 19 deletions(-) create mode 100644 src/gmp/commands/resourcenames.js create mode 100644 src/gmp/models/resourcename.js diff --git a/src/gmp/commands/resourcenames.js b/src/gmp/commands/resourcenames.js new file mode 100644 index 0000000000..c7d233b975 --- /dev/null +++ b/src/gmp/commands/resourcenames.js @@ -0,0 +1,85 @@ +/* Copyright (C) 2023 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import registerCommand from 'gmp/command'; + +import ResourceName from 'gmp/models/resourcename'; +import EntitiesCommand from './entities'; + +export class ResourceNamesCommand extends EntitiesCommand { + constructor(http) { + super(http, 'resource_name', ResourceName); + this.name = 'resource'; + } + + getEntitiesResponse(root) { + return root.get_resource_names.get_resource_names_response; + } + + export(entities) { + throw new Error('export not implemented in ' + this.constructor.name); + } + + exportByIds(ids) { + throw new Error('exportByIds not implemented in ' + this.constructor.name); + } + + exportByFilter(filter) { + throw new Error( + 'exportByFilter not implemented in ' + this.constructor.name, + ); + } + + delete(entities, extraParams) { + throw new Error('delete not implemented in ' + this.constructor.name); + } + + deleteByIds(ids, extraParams = {}) { + throw new Error('deleteByIds not implemented in ' + this.constructor.name); + } + + deleteByFilter(filter, extraParams) { + throw new Error( + 'deleteByFilter not implemented in ' + this.constructor.name, + ); + } + + transformAggregates(response) { + throw new Error( + 'transformAggregates not implemented in ' + this.constructor.name, + ); + } + + getAggregates({ + dataColumns = [], + textColumns = [], + sort = [], + aggregateMode, + maxGroups, + subgroupColumn, + ...params + } = {}) { + throw new Error( + 'getAggregates not implemented in ' + this.constructor.name, + ); + } +} + +registerCommand('resourcenames', ResourceNamesCommand); + +// vim: set ts=2 sw=2 tw=80: diff --git a/src/gmp/gmp.js b/src/gmp/gmp.js index 6511092d67..844b0d5368 100644 --- a/src/gmp/gmp.js +++ b/src/gmp/gmp.js @@ -36,6 +36,7 @@ import 'gmp/commands/filters'; import 'gmp/commands/groups'; import 'gmp/commands/hosts'; import 'gmp/commands/license'; +import 'gmp/commands/resourcenames'; import 'gmp/commands/notes'; import 'gmp/commands/nvt'; import 'gmp/commands/nvtfamilies'; diff --git a/src/gmp/models/resourcename.js b/src/gmp/models/resourcename.js new file mode 100644 index 0000000000..a8fd61f8c0 --- /dev/null +++ b/src/gmp/models/resourcename.js @@ -0,0 +1,36 @@ +/* Copyright (C) 2023 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import {isDefined} from 'gmp/utils/identity'; + +export class ResourceName { + constructor({id, name}) { + this.id = id; + this.name = name; + } + + static fromElement(element) { + const {_id, name} = element; + + return new ResourceName({ + id: isDefined(_id) ? _id : '', + name: isDefined(name) ? name : '', + }); + } +} + +export default ResourceName; diff --git a/src/web/pages/tags/dialog.js b/src/web/pages/tags/dialog.js index 824af77222..c7574ea215 100644 --- a/src/web/pages/tags/dialog.js +++ b/src/web/pages/tags/dialog.js @@ -25,7 +25,6 @@ import _ from 'gmp/locale'; import {isDefined} from 'gmp/utils/identity'; import {map} from 'gmp/utils/array'; import {isEmpty} from 'gmp/utils/string'; -import {pluralizeType, normalizeType} from 'gmp/utils/entitytype'; import {YES_VALUE} from 'gmp/parser'; @@ -54,11 +53,30 @@ const ScrollableContent = styled.div` overflow: auto; `; +const types = { + operatingsystem: 'os', + certbund: 'cert_bund_adv', + dfncert: 'dfn_cert_adv', + portlist: 'port_list', + reportformat: 'report_format', + scanconfig: 'config', + tlscertificate: 'tls_certificate', +}; + +const convertType = type => { + const ctype = types[type]; + if (isDefined(ctype)) { + return ctype; + } + return type; +}; + class TagDialog extends React.Component { constructor(...args) { super(...args); const {resource_ids = []} = this.props; + this.isLoading = false; this.state = { resourceIdText: '', @@ -81,24 +99,27 @@ class TagDialog extends React.Component { return; } const {gmp} = this.props; - const plType = pluralizeType(normalizeType(type)); - gmp[plType].getAll().then(response => { - const {data} = response; - let id = this.state.resourceIdText; - const idPresent = data.includes(res => res.id === id); - if (!idPresent && !isEmpty(id)) { - data.push({ - name: '----', - id: id, + this.isLoading = true; + gmp.resourcenames + .getAll({resource_type: convertType(type)}) + .then(response => { + const {data} = response; + let id = this.state.resourceIdText; + const idPresent = data.includes(res => res.id === id); + if (!idPresent && !isEmpty(id)) { + data.push({ + name: '----', + id: id, + }); + } + if (isEmpty(id)) { + id = undefined; + } + this.isLoading = false; + this.setState({ + resourceOptions: data, }); - } - if (isEmpty(id)) { - id = undefined; - } - this.setState({ - resourceOptions: data, }); - }); } handleResourceTypeChange(type, onValueChange) { @@ -107,6 +128,7 @@ class TagDialog extends React.Component { this.loadResourcesByType(type); this.setState({ resourceIdsSelected: [], + resourceOptions: [], resourceType: type, }); } @@ -123,8 +145,8 @@ class TagDialog extends React.Component { const {gmp} = this.props; const {resourceIdsSelected, resourceType} = this.state; - gmp[pluralizeType(normalizeType(resourceType))] - .get({filter: 'uuid=' + id}) + gmp.resourcenames + .get({resource_type: convertType(resourceType), filter: 'uuid=' + id}) .then(response => { const ids = isDefined(resourceIdsSelected) ? resourceIdsSelected : []; if (response.data.length === 0) { @@ -265,6 +287,10 @@ class TagDialog extends React.Component { fixed || resourceTypesOptions.length === 0 } + isLoading={ + this.isLoading && + this.state.resourceOptions.length === 0 + } onChange={ids => this.handleIdChange(ids, onValueChange)} />