From 8a16fa84f86f3adfba8ea149c86b1e022d07d9dc Mon Sep 17 00:00:00 2001 From: Ahmed Abdelsalam Date: Mon, 4 Mar 2024 11:39:11 +0100 Subject: [PATCH] Apply review remarks - Rewrite auditdeltadetailspage, auditdetailspage, auditreportlistpage, compliancelevelsgroup into functional components. - Use camel case instead of snake case - Use absolute imports without .js suffix --- public/locales/gsa-de.json | 2 +- src/gmp/commands/__tests__/auditreport.js | 8 +- src/gmp/commands/__tests__/auditreports.js | 2 +- src/gmp/models/auditreport.js | 6 +- src/gmp/models/report/auditreport.js | 55 +- src/gmp/models/report/host.js | 10 +- src/web/components/bar/compliancebar.js | 14 +- src/web/components/label/compliancestate.js | 84 +- .../__tests__/compliancelevelsgroup.js | 4 +- .../powerfilter/compliancelevelsgroup.js | 108 +- .../reports/__mocks__/mockauditdeltareport.js | 21 +- .../reports/__mocks__/mockauditreport.js | 2 +- .../__tests__/auditdeltadetailspage.js | 2 +- .../reports/__tests__/auditdetailscontent.js | 2 +- .../reports/__tests__/auditfilterdialog.js | 2 +- .../reports/__tests__/detailsfilterdialog.js | 2 +- src/web/pages/reports/auditdashboard/index.js | 4 +- .../pages/reports/auditdashboard/loaders.js | 2 - .../pages/reports/auditdeltadetailspage.js | 742 ++++++-------- src/web/pages/reports/auditdetailscontent.js | 19 +- src/web/pages/reports/auditdetailspage.js | 920 ++++++++---------- src/web/pages/reports/auditfilterdialog.js | 10 +- src/web/pages/reports/auditreportrow.js | 6 +- src/web/pages/reports/auditreportslistpage.js | 136 ++- src/web/pages/reports/auditreportstable.js | 16 +- src/web/pages/reports/deltadetailscontent.js | 10 +- src/web/pages/reports/deltadetailspage.js | 17 - .../reports/details/emptyresultsreport.js | 8 +- src/web/pages/reports/details/hoststab.js | 12 +- src/web/pages/reports/details/hoststable.js | 22 +- src/web/pages/reports/details/resultstab.js | 4 +- src/web/pages/tasks/row.js | 2 +- src/web/utils/theme.js | 8 +- 33 files changed, 1010 insertions(+), 1252 deletions(-) diff --git a/public/locales/gsa-de.json b/public/locales/gsa-de.json index b0215e660d..ab1701b290 100644 --- a/public/locales/gsa-de.json +++ b/public/locales/gsa-de.json @@ -824,7 +824,7 @@ "Include log messages in your filter settings.": "Log-Nachrichten in die Filtereinstellungen einbeziehen.", "Include report": "Bericht einfügen", "Included": "Beinhaltet", - "Incomplete": "Inkomplett", + "Incomplete": "Unvollständig", "Info": "Info", "Information": "Informationen", "Inheriting user": "Erbender Benutzer", diff --git a/src/gmp/commands/__tests__/auditreport.js b/src/gmp/commands/__tests__/auditreport.js index e6a78a2765..ce7091ee06 100644 --- a/src/gmp/commands/__tests__/auditreport.js +++ b/src/gmp/commands/__tests__/auditreport.js @@ -15,11 +15,11 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import {createHttp, createEntityResponse} from '../testing'; -import {AuditReportCommand} from '../auditreports'; +import {createHttp, createEntityResponse} from 'gmp/commands/testing'; +import {AuditReportCommand} from 'gmp/commands/auditreports'; -describe('ReportCommand tests', () => { - test('should request single report', () => { +describe('AuditReportCommand tests', () => { + test('should request single audit report', () => { const response = createEntityResponse('report', {_id: 'foo'}); const fakeHttp = createHttp(response); diff --git a/src/gmp/commands/__tests__/auditreports.js b/src/gmp/commands/__tests__/auditreports.js index 7e6920e0b7..d6fdb42885 100644 --- a/src/gmp/commands/__tests__/auditreports.js +++ b/src/gmp/commands/__tests__/auditreports.js @@ -22,7 +22,7 @@ import { createEntitiesResponse, createAggregatesResponse, } from '../testing'; -import {AuditReportsCommand} from '../auditreports'; +import {AuditReportsCommand} from 'gmp/commands/auditreports'; describe('AuditReportsCommand tests', () => { test('should return all audit reports', () => { diff --git a/src/gmp/models/auditreport.js b/src/gmp/models/auditreport.js index f666f75496..40f1bd0c9d 100644 --- a/src/gmp/models/auditreport.js +++ b/src/gmp/models/auditreport.js @@ -57,11 +57,11 @@ class AuditReport extends Model { copy.report = AuditReportReport.fromElement(report); } - copy.report_format = parseModelFromElement(report_format, 'reportformat'); + copy.reportFormat = parseModelFromElement(report_format, 'reportformat'); copy.task = parseModelFromElement(task, 'task'); - copy.report_type = type; - copy.content_type = content_type; + copy.reportType = type; + copy.contentType = content_type; // revert ? copy.scan_start = parseDate(scan_start); copy.timestamp = parseDate(timestamp); diff --git a/src/gmp/models/report/auditreport.js b/src/gmp/models/report/auditreport.js index 8c1c559280..cc5f503c7b 100644 --- a/src/gmp/models/report/auditreport.js +++ b/src/gmp/models/report/auditreport.js @@ -15,14 +15,14 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import {isDefined} from '../../utils/identity'; -import {isEmpty} from '../../utils/string'; +import {isDefined} from 'gmp/utils/identity'; +import {isEmpty} from 'gmp/utils/string'; -import {parseDate} from '../../parser'; +import {parseDate} from 'gmp/parser'; -import {parseFilter} from '../../collection/parser'; +import {parseFilter} from 'gmp/collection/parser'; -import Model from '../../model'; +import Model from 'gmp/model'; import ReportTask from './task'; @@ -44,14 +44,24 @@ class AuditReportReport extends Model { static parseElement(element) { const copy = super.parseElement(element); - const {delta, compliance, scan_start, scan_end, task, scan, timestamp} = - element; + const { + delta, + compliance, + compliance_count, + scan_start, + scan_end, + task, + scan, + timestamp, + } = element; const filter = parseFilter(element); copy.filter = filter; - copy.report_type = element._type; + copy.reportType = element._type; + + // copy.scanRunStatus = element.scan_run_status; delete copy.filters; @@ -62,6 +72,31 @@ class AuditReportReport extends Model { }; } + if (isDefined(compliance_count)) { + copy.complianceCounts = { + filtered: parseInt(compliance_count.filtered), + full: parseInt(compliance_count.full), + incomplete: { + filtered: parseInt(compliance_count.incomplete.filtered), + full: parseInt(compliance_count.incomplete.full), + }, + no: { + filtered: parseInt(compliance_count.no.filtered), + full: parseInt(compliance_count.no.full), + }, + undefined: { + filtered: parseInt(compliance_count.undefined.filtered), + full: parseInt(compliance_count.undefined.full), + }, + yes: { + filtered: parseInt(compliance_count.yes.filtered), + full: parseInt(compliance_count.yes.full), + }, + }; + } + + delete copy.compliance_count; + copy.task = ReportTask.fromElement(task); copy.results = parseResults(element, filter); @@ -72,7 +107,7 @@ class AuditReportReport extends Model { delete copy.host; - copy.operatingsystems = parseOperatingSystems(element, filter); + copy.operatingSystems = parseOperatingSystems(element, filter); copy.errors = parse_errors(element, filter); @@ -112,7 +147,7 @@ class AuditReportReport extends Model { } isDeltaReport() { - return this.report_type === 'delta'; + return this.reportType === 'delta'; } } diff --git a/src/gmp/models/report/host.js b/src/gmp/models/report/host.js index 4563e0a7c8..9034d8effa 100644 --- a/src/gmp/models/report/host.js +++ b/src/gmp/models/report/host.js @@ -50,7 +50,7 @@ class Host { warning: 0, total: 0, }; - this.compliance_counts = { + this.complianceCounts = { yes: 0, no: 0, incomplete: 0, @@ -77,10 +77,12 @@ class Host { host_compliance, } = element; - copy.host_compliance = isDefined(host_compliance) + copy.hostCompliance = isDefined(host_compliance) ? host_compliance : 'undefined'; + delete copy.host_compliance; + if (isEmpty(asset._asset_id)) { delete copy.asset; } else { @@ -111,7 +113,7 @@ class Host { } if (isDefined(compliance_count)) { - copy.compliance_counts = { + copy.complianceCounts = { yes: parse_page_count(compliance_count.yes), no: parse_page_count(compliance_count.no), incomplete: parse_page_count(compliance_count.incomplete), @@ -119,7 +121,7 @@ class Host { total: parse_page_count(compliance_count), }; } else { - copy.compliance_counts = { + copy.complianceCounts = { yes: 0, no: 0, incomplete: 0, diff --git a/src/web/components/bar/compliancebar.js b/src/web/components/bar/compliancebar.js index 2c08f44707..e4d35fc30d 100644 --- a/src/web/components/bar/compliancebar.js +++ b/src/web/components/bar/compliancebar.js @@ -19,10 +19,10 @@ import React from 'react'; import {isDefined} from 'gmp/utils/identity'; -import PropTypes from '../../utils/proptypes.js'; -import Theme from 'web/utils/theme.js'; +import PropTypes from 'web/utils/proptypes'; +import Theme from 'web/utils/theme'; -import ProgressBar from './progressbar.js'; +import ProgressBar from './progressbar'; import {getTranslatableReportCompliance} from 'gmp/models/auditreport'; const ComplianceBar = ({compliance, toolTip}) => { @@ -30,13 +30,13 @@ const ComplianceBar = ({compliance, toolTip}) => { let background; if (compliance === 'no') { - background = Theme.compliance_no; + background = Theme.complianceNo; } else if (compliance === 'incomplete') { - background = Theme.compliance_incomplete; + background = Theme.complianceIncomplete; } else if (compliance === 'yes') { - background = Theme.compliance_yes; + background = Theme.complianceYes; } else { - background = Theme.compliance_undefined; + background = Theme.complianceUndefined; } const toolTipText = isDefined(toolTip) ? toolTip : title; diff --git a/src/web/components/label/compliancestate.js b/src/web/components/label/compliancestate.js index 9e9ba5badf..ca6cb43c78 100644 --- a/src/web/components/label/compliancestate.js +++ b/src/web/components/label/compliancestate.js @@ -15,13 +15,11 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import React from 'react'; - import styled from 'styled-components'; import _ from 'gmp/locale'; import {styledExcludeProps} from 'web/utils/styledConfig'; -import Theme from 'web/utils/theme.js'; +import Theme from 'web/utils/theme'; const Label = styledExcludeProps(styled.div, [ 'backgroundColor', @@ -40,53 +38,45 @@ const Label = styledExcludeProps(styled.div, [ border-color: ${props => props.borderColor}; `; -const YesLabel = props => { - return ( - - ); -}; +const YesLabel = props => ( + +); -const NoLabel = props => { - return ( - - ); -}; +const NoLabel = props => ( + +); -const IncompleteLabel = props => { - return ( - - ); -}; +const IncompleteLabel = props => ( + +); -const UndefinedLabel = props => { - return ( - - ); -}; +const UndefinedLabel = props => ( + +); export const ComplianceStateLabels = { Yes: YesLabel, diff --git a/src/web/components/powerfilter/__tests__/compliancelevelsgroup.js b/src/web/components/powerfilter/__tests__/compliancelevelsgroup.js index 0f873cc3bf..d1ac6990fe 100644 --- a/src/web/components/powerfilter/__tests__/compliancelevelsgroup.js +++ b/src/web/components/powerfilter/__tests__/compliancelevelsgroup.js @@ -15,11 +15,9 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import React from 'react'; - import {render, fireEvent} from 'web/utils/testing'; -import ComplianceLevelsFilterGroup from '../compliancelevelsgroup'; +import ComplianceLevelsFilterGroup from 'web/components/powerfilter/compliancelevelsgroup'; import Filter from 'gmp/models/filter'; diff --git a/src/web/components/powerfilter/compliancelevelsgroup.js b/src/web/components/powerfilter/compliancelevelsgroup.js index 2d16d5ae2c..3000f17c14 100644 --- a/src/web/components/powerfilter/compliancelevelsgroup.js +++ b/src/web/components/powerfilter/compliancelevelsgroup.js @@ -22,24 +22,18 @@ import _ from 'gmp/locale'; import {isDefined} from 'gmp/utils/identity'; -import PropTypes from '../../utils/proptypes.js'; +import PropTypes from 'web/utils/proptypes'; -import Checkbox from '../form/checkbox.js'; -import FormGroup from '../form/formgroup.js'; +import Checkbox from 'web/components/form/checkbox'; +import FormGroup from 'web/components/form/formgroup'; -import IconDivider from '../layout/icondivider.js'; +import IconDivider from 'web/components/layout/icondivider'; -import ComplianceStateLabels from '../label/compliancestate.js'; +import ComplianceStateLabels from 'web/components/label/compliancestate'; -class ComplianceLevelsFilterGroup extends React.Component { - constructor(...args) { - super(...args); - - this.handleComplianceChange = this.handleComplianceChange.bind(this); - } - - handleComplianceChange(value, level) { - const {filter, onChange, onRemove, isResult = false} = this.props; +const ComplianceLevelsFilterGroup = props => { + const handleComplianceChange = (value, level) => { + const {filter, onChange, onRemove, isResult = false} = props; const filter_name = isResult ? 'compliance_levels' @@ -63,54 +57,52 @@ class ComplianceLevelsFilterGroup extends React.Component { onChange(compliance, filter_name); } } - } + }; - render() { - const {filter, isResult} = this.props; + const {filter, isResult} = props; - let compliance_levels = filter.get( - isResult ? 'compliance_levels' : 'report_compliance_levels', - ); + let compliance_levels = filter.get( + isResult ? 'compliance_levels' : 'report_compliance_levels', + ); - if (!isDefined(compliance_levels)) { - compliance_levels = ''; - } - return ( - - - - - - - - - - - - - - - - - ); + if (!isDefined(compliance_levels)) { + compliance_levels = ''; } -} + return ( + + + + + + + + + + + + + + + + + ); +}; ComplianceLevelsFilterGroup.propTypes = { filter: PropTypes.filter.isRequired, diff --git a/src/web/pages/reports/__mocks__/mockauditdeltareport.js b/src/web/pages/reports/__mocks__/mockauditdeltareport.js index 5f5de621c2..b6e04c4860 100644 --- a/src/web/pages/reports/__mocks__/mockauditdeltareport.js +++ b/src/web/pages/reports/__mocks__/mockauditdeltareport.js @@ -350,7 +350,26 @@ export const getMockAuditDeltaReport = () => { os: {count: 2}, ssl_certs: {count: 2}, compliance: {filtered: 'no', full: 'no'}, - compliance_count: {__text: 3, full: 3, filtered: 2}, + compliance_count: { + full: 3, + filtered: 2, + incomplete: { + full: 5, + filtered: 1, + }, + yes: { + full: 2, + filtered: 3, + }, + no: { + full: 1, + filtered: 2, + }, + undefined: { + full: 2, + filtered: 0, + }, + }, results: {result: [result1, result2, result3]}, hosts: {count: 3}, host: [host1, host2, host3], diff --git a/src/web/pages/reports/__mocks__/mockauditreport.js b/src/web/pages/reports/__mocks__/mockauditreport.js index 8126f50271..409e52e283 100644 --- a/src/web/pages/reports/__mocks__/mockauditreport.js +++ b/src/web/pages/reports/__mocks__/mockauditreport.js @@ -378,7 +378,7 @@ export const getMockAuditReport = () => { report: entity.report, results: entity.report.results, hosts: entity.report.hosts, - operatingsystems: entity.report.operatingsystems, + operatingsystems: entity.report.operatingSystems, tlsCertificates: entity.report.tlsCertificates, errors: entity.report.errors, task: entity.report.task, diff --git a/src/web/pages/reports/__tests__/auditdeltadetailspage.js b/src/web/pages/reports/__tests__/auditdeltadetailspage.js index 2bb53f403c..294ff3e2c6 100644 --- a/src/web/pages/reports/__tests__/auditdeltadetailspage.js +++ b/src/web/pages/reports/__tests__/auditdeltadetailspage.js @@ -28,7 +28,7 @@ import {rendererWith} from 'web/utils/testing'; import {getMockAuditDeltaReport} from 'web/pages/reports/__mocks__/mockauditdeltareport'; -import DeltaDetailsContent from '../deltadetailscontent'; +import DeltaDetailsContent from 'web/pages/reports/deltadetailscontent'; setLocale('en'); diff --git a/src/web/pages/reports/__tests__/auditdetailscontent.js b/src/web/pages/reports/__tests__/auditdetailscontent.js index 43c5250f9f..dc22c73dcd 100644 --- a/src/web/pages/reports/__tests__/auditdetailscontent.js +++ b/src/web/pages/reports/__tests__/auditdetailscontent.js @@ -28,7 +28,7 @@ import {rendererWith} from 'web/utils/testing'; import {getMockAuditReport} from 'web/pages/reports/__mocks__/mockauditreport'; -import DetailsContent from '../auditdetailscontent'; +import DetailsContent from 'web/pages/reports/auditdetailscontent'; setLocale('en'); diff --git a/src/web/pages/reports/__tests__/auditfilterdialog.js b/src/web/pages/reports/__tests__/auditfilterdialog.js index a6da627062..f1de966899 100644 --- a/src/web/pages/reports/__tests__/auditfilterdialog.js +++ b/src/web/pages/reports/__tests__/auditfilterdialog.js @@ -24,7 +24,7 @@ import Filter from 'gmp/models/filter'; import {rendererWith} from 'web/utils/testing'; -import AuditReportFilter from '../auditfilterdialog'; +import AuditReportFilter from 'web/pages/reports/auditfilterdialog'; setLocale('en'); diff --git a/src/web/pages/reports/__tests__/detailsfilterdialog.js b/src/web/pages/reports/__tests__/detailsfilterdialog.js index 65f7382ba9..35ea2209b8 100644 --- a/src/web/pages/reports/__tests__/detailsfilterdialog.js +++ b/src/web/pages/reports/__tests__/detailsfilterdialog.js @@ -24,7 +24,7 @@ import Filter from 'gmp/models/filter'; import {rendererWith} from 'web/utils/testing'; -import FilterDialog from '../detailsfilterdialog'; +import FilterDialog from 'web/pages/reports/detailsfilterdialog'; setLocale('en'); diff --git a/src/web/pages/reports/auditdashboard/index.js b/src/web/pages/reports/auditdashboard/index.js index 3118683ac4..c88606ec8c 100644 --- a/src/web/pages/reports/auditdashboard/index.js +++ b/src/web/pages/reports/auditdashboard/index.js @@ -16,9 +16,7 @@ * along with this program. If not, see . */ -import React from 'react'; - -import Dashboard from '../../../components/dashboard/dashboard'; +import Dashboard from 'web/components/dashboard/dashboard'; import { ReportComplianceDisplay, diff --git a/src/web/pages/reports/auditdashboard/loaders.js b/src/web/pages/reports/auditdashboard/loaders.js index d8ed201bb3..792148413f 100644 --- a/src/web/pages/reports/auditdashboard/loaders.js +++ b/src/web/pages/reports/auditdashboard/loaders.js @@ -15,8 +15,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import React from 'react'; - import Loader, { loadFunc, loaderPropTypes, diff --git a/src/web/pages/reports/auditdeltadetailspage.js b/src/web/pages/reports/auditdeltadetailspage.js index c406a98796..cefaabd255 100644 --- a/src/web/pages/reports/auditdeltadetailspage.js +++ b/src/web/pages/reports/auditdeltadetailspage.js @@ -16,9 +16,11 @@ * along with this program. If not, see . */ -import React from 'react'; +import React, {useEffect, useState} from 'react'; -import {connect} from 'react-redux'; +import {useDispatch, useSelector, shallowEqual} from 'react-redux'; + +import {useRouteMatch} from 'react-router-dom'; import _ from 'gmp/locale'; @@ -63,7 +65,6 @@ import { import {loadUserSettingDefaults} from 'web/store/usersettings/defaults/actions'; import {getUserSettingsDefaults} from 'web/store/usersettings/defaults/selectors'; -import {loadUserSettingsDefaultFilter} from 'web/store/usersettings/defaultfilters/actions'; import {getUserSettingsDefaultFilter} from 'web/store/usersettings/defaultfilters/selectors'; import { @@ -71,11 +72,10 @@ import { getUsername, } from 'web/store/usersettings/selectors'; -import {create_pem_certificate} from 'web/utils/cert'; import compose from 'web/utils/compose'; import {generateFilename} from 'web/utils/render'; import PropTypes from 'web/utils/proptypes'; -import withGmp from 'web/utils/withGmp'; +import useGmp from 'web/utils/useGmp'; import TargetComponent from '../targets/component'; @@ -101,140 +101,161 @@ const getFilter = (entity = {}) => { return report.filter; }; -class DeltaAuditReportDetails extends React.Component { - constructor(...args) { - super(...args); - - this.state = { - activeTab: 0, - showFilterDialog: false, - showDownloadReportDialog: false, - sorting: { - results: { - sortField: 'compliant', - sortReverse: true, - }, - errors: { - sortField: 'error', - sortReverse: false, - }, - }, - }; - - this.handleActivateTab = this.handleActivateTab.bind(this); - this.handleAddToAssets = this.handleAddToAssets.bind(this); - this.handleChanged = this.handleChanged.bind(this); - this.handleError = this.handleError.bind(this); - this.handleFilterAddLogLevel = this.handleFilterAddLogLevel.bind(this); - this.handleFilterChange = this.handleFilterChange.bind(this); - this.handleFilterDecreaseMinQoD = - this.handleFilterDecreaseMinQoD.bind(this); - this.handleFilterCreated = this.handleFilterCreated.bind(this); - this.handleFilterEditClick = this.handleFilterEditClick.bind(this); - this.handleFilterRemoveSeverity = - this.handleFilterRemoveSeverity.bind(this); - this.handleFilterRemoveClick = this.handleFilterRemoveClick.bind(this); - this.handleFilterResetClick = this.handleFilterResetClick.bind(this); - this.handleRemoveFromAssets = this.handleRemoveFromAssets.bind(this); - this.handleReportDownload = this.handleReportDownload.bind(this); - this.handleTlsCertificateDownload = - this.handleTlsCertificateDownload.bind(this); - this.handleFilterDialogClose = this.handleFilterDialogClose.bind(this); - this.handleSortChange = this.handleSortChange.bind(this); - - this.loadTarget = this.loadTarget.bind(this); - this.handleOpenDownloadReportDialog = - this.handleOpenDownloadReportDialog.bind(this); - this.handleCloseDownloadReportDialog = - this.handleCloseDownloadReportDialog.bind(this); - } - - componentDidMount() { - this.props.loadSettings(); - this.props.loadFilters(); - this.props.loadReportFormats(); - this.props.loadReportComposerDefaults(); - } - - componentDidUpdate(prevProps) { - const {reportFormats} = this.props; +const DeltaAuditReportDetails = props => { + const [activeTab, setActiveTab] = useState(0); + const [showFilterDialog, setShowFilterDialog] = useState(false); + const [showDownloadReportDialog, setShowDownloadReportDialog] = + useState(false); + const [reportFormatId, setReportFormatId] = useState(undefined); + const [isUpdating, setIsUpdating] = useState(false); + // storeAsDefault is set in SaveDialogContent + // eslint-disable-next-line no-unused-vars + const [storeAsDefault, setStoreAsDefault] = useState(undefined); + + const [sorting, setSorting] = useState({ + results: { + sortField: 'compliant', + sortReverse: true, + }, + errors: { + sortField: 'error', + sortReverse: false, + }, + }); + + const gmp = useGmp(); + const dispatch = useDispatch(); + const match = useRouteMatch(); + const {id: reportId, deltaid: deltaReportId} = match.params; + + const reportFormatsSel = useSelector(reportFormatsSelector); + const reportFormats = reportFormatsSel?.getAllEntities(REPORT_FORMATS_FILTER); + const userDefaultFilterSel = useSelector( + rootState => getUserSettingsDefaultFilter(rootState, 'result'), + shallowEqual, + ); + const resultDefaultFilter = userDefaultFilterSel?.getFilter('result'); + const reportComposerDefaults = useSelector(getReportComposerDefaults); + const userDefaultsSelector = useSelector( + getUserSettingsDefaults, + shallowEqual, + ); + const reportExportFileName = userDefaultsSelector?.getValueByName( + 'reportexportfilename', + ); + const username = useSelector(getUsername); + const filterSel = useSelector(filterSelector); + const filters = filterSel?.getAllEntities(RESULTS_FILTER_FILTER); + const [entity, entityError] = useSelector(state => { + const deltaSel = deltaAuditReportSelector(state); + return [ + deltaSel?.getEntity(reportId, deltaReportId), + deltaSel?.getError(reportId, deltaReportId), + ]; + }); + const isLoading = !isDefined(entity); + + useEffect(() => { + dispatch(loadUserSettingDefaults(gmp)()); + dispatch(loadFilters(gmp)(RESULTS_FILTER_FILTER)); + dispatch(loadReportFormats(gmp)(REPORT_FORMATS_FILTER)); + dispatch(loadReportComposerDefaults(gmp)()); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { if ( - !isDefined(this.state.reportFormatId) && + !isDefined(reportFormatId) && isDefined(reportFormats) && reportFormats.length > 0 ) { // set initial report format id if available - const reportFormatId = first(reportFormats).id; - if (isDefined(reportFormatId)) { + const initialReportFormatId = first(reportFormats).id; + if (isDefined(initialReportFormatId)) { // ensure the report format id is only set if we really have one // if no report format id is available we would create an infinite // render loop here - this.setState({reportFormatId}); + setReportFormatId({initialReportFormatId}); + } else { + // if there is no report format at all, throw a proper error message + // instead of just showing x is undefined JS stacktrace + const noReportFormatError = _( + 'The report cannot be displayed because' + + ' no Greenbone Vulnerability Manager report format is available.' + + ' This could be due to a missing gvmd data feed. Please update' + + ' the gvmd data feed, check the "feed import owner" setting, or' + + ' contact your system administrator.', + ); + throw new Error(noReportFormatError); } } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [reportFormats, reportFormatId]); - if ( - prevProps.reportId !== this.props.reportId || - prevProps.deltaReportId !== this.props.deltaReportId - ) { - this.load(); - } - } + useEffect(() => { + load(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [reportId, deltaReportId]); - load(filter) { - log.debug('Loading deleta report', { + const load = filter => { + log.debug('Loading report', { filter, }); + const {reportFilter: prevFilter} = props; - this.setState(({lastFilter}) => ({ - isUpdating: isDefined(lastFilter) && !lastFilter.equals(filter), // show update indicator if filter has changed - lastFilter: filter, - })); + setIsUpdating(!isDefined(prevFilter) || !prevFilter.equals(filter)); - this.props.reload(filter).then(() => { - this.setState({isUpdating: false}); - }); - } + props + .reload(filter) + .then(() => { + setIsUpdating(false); + }) + .catch(() => { + setIsUpdating(false); + }); + }; - reload() { + const reload = () => { // reload data from backend - this.load(this.state.lastFilter); - } + load(props.reportFilter); + }; - handleChanged() { - this.reload(); - } + const handleChanged = () => { + reload(); + }; - handleError(error) { - const {showError} = this.props; + const handleError = error => { + const {showError} = props; log.error(error); showError(error); - } - - handleFilterChange(filter) { - this.handleInteraction(); - - this.load(filter); - } + }; - handleFilterRemoveClick() { - this.handleFilterChange(RESET_FILTER); - } + const handleFilterChange = filter => { + handleInteraction(); + load(filter); + }; - handleFilterResetClick() { - this.handleFilterChange(this.props.resultDefaultFilter); - } + const handleFilterRemoveClick = () => { + handleFilterChange(RESET_FILTER); + }; - handleActivateTab(index) { - this.handleInteraction(); + const handleFilterResetClick = () => { + if (hasValue(resultDefaultFilter)) { + handleFilterChange(resultDefaultFilter); + } else { + handleFilterChange(DEFAULT_FILTER); + } + }; - this.setState({activeTab: index}); - } + const handleActivateTab = index => { + handleInteraction(); + setActiveTab(index); + }; - handleAddToAssets() { - const {gmp, showSuccessMessage, entity, reportFilter: filter} = this.props; + const handleAddToAssets = () => { + const {showSuccessMessage, reportFilter: filter} = props; - this.handleInteraction(); + handleInteraction(); gmp.auditreport.addAssets(entity, {filter}).then(() => { showSuccessMessage( @@ -242,55 +263,43 @@ class DeltaAuditReportDetails extends React.Component { 'Report content added to Assets with QoD>=70% and Overrides enabled.', ), ); - this.reload(); - }, this.handleError); - } + reload(); + }, handleError); + }; - handleRemoveFromAssets() { - const {gmp, showSuccessMessage, entity, reportFilter: filter} = this.props; + const handleRemoveFromAssets = () => { + const {showSuccessMessage, reportFilter: filter} = props; - this.handleInteraction(); + handleInteraction(); gmp.auditreport.removeAssets(entity, {filter}).then(() => { showSuccessMessage(_('Report content removed from Assets.')); - this.reload(); - }, this.handleError); - } + reload(); + }, handleError); + }; - handleFilterEditClick() { - this.handleInteraction(); + const handleFilterEditClick = () => { + handleInteraction(); - this.setState({showFilterDialog: true}); - } + setShowFilterDialog(true); + }; - handleFilterDialogClose() { - this.handleInteraction(); + const handleFilterDialogClose = () => { + handleInteraction(); + setShowFilterDialog(false); + }; - this.setState({showFilterDialog: false}); - } + const handleOpenDownloadReportDialog = () => { + setShowDownloadReportDialog(true); + }; - handleOpenDownloadReportDialog() { - this.setState({ - showDownloadReportDialog: true, - }); - } - - handleCloseDownloadReportDialog() { - this.setState({showDownloadReportDialog: false}); - } - - handleReportDownload(state) { - const { - deltaReportId, - entity, - gmp, - reportComposerDefaults, - reportExportFileName, - reportFilter, - reportFormats = [], - username, - onDownload, - } = this.props; + const handleCloseDownloadReportDialog = () => { + setShowDownloadReportDialog(false); + }; + + const handleReportDownload = state => { + const {reportFilter, onDownload} = props; + // eslint-disable-next-line no-shadow const {includeNotes, includeOverrides, reportFormatId, storeAsDefault} = state; @@ -305,18 +314,18 @@ class DeltaAuditReportDetails extends React.Component { includeNotes, includeOverrides, }; - this.props.saveReportComposerDefaults(defaults); + dispatch(saveReportComposerDefaults(gmp)(defaults)); } - const report_format = reportFormats.find( - format => reportFormatId === format.id, - ); + const report_format = reportFormats + ? reportFormats.find(format => reportFormatId === format.id) + : undefined; const extension = isDefined(report_format) ? report_format.extension : 'unknown'; // unknown should never happen but we should be save here - this.handleInteraction(); + handleInteraction(); return gmp.auditreport .download(entity, { @@ -325,7 +334,7 @@ class DeltaAuditReportDetails extends React.Component { filter: newFilter, }) .then(response => { - this.setState({showDownloadReportDialog: false}); + setShowDownloadReportDialog(false); const {data} = response; const filename = generateFilename({ creationTime: entity.creationTime, @@ -333,290 +342,140 @@ class DeltaAuditReportDetails extends React.Component { fileNameFormat: reportExportFileName, id: entity.id, modificationTime: entity.modificationTime, - reportFormat: report_format.name, + reportFormat: report_format?.name, resourceName: entity.task.name, resourceType: 'report', username, }); onDownload({filename, data}); - }, this.handleError); - } - - handleTlsCertificateDownload(cert) { - const {onDownload} = this.props; - - const {data, serial} = cert; - - this.handleInteraction(); - - onDownload({ - filename: 'tls-cert-' + serial + '.pem', - data: create_pem_certificate(data), - }); - } - - handleFilterCreated(filter) { - this.handleInteraction(); - this.load(filter); - this.props.loadFilters(); - } - - handleFilterAddLogLevel() { - const {reportFilter} = this.props; - let levels = reportFilter.get('levels', ''); - - this.handleInteraction(); - - if (!levels.includes('g')) { - levels += 'g'; - const lfilter = reportFilter.copy(); - lfilter.set('levels', levels); - this.load(lfilter); - } - } - - handleFilterRemoveSeverity() { - const {reportFilter} = this.props; - - this.handleInteraction(); + }, handleError); + }; - if (reportFilter.has('severity')) { - const lfilter = reportFilter.copy(); - lfilter.delete('severity'); - this.load(lfilter); - } - } + const handleFilterCreated = filter => { + handleInteraction(); + load(filter); + dispatch(loadFilters(gmp)(RESULTS_FILTER_FILTER)); + }; - handleFilterDecreaseMinQoD() { - const {reportFilter} = this.props; + const handleFilterDecreaseMinQoD = () => { + const {reportFilter} = props; - this.handleInteraction(); + handleInteraction(); if (reportFilter.has('min_qod')) { const lfilter = reportFilter.copy(); lfilter.set('min_qod', 30); - this.load(lfilter); + load(lfilter); } - } + }; - handleSortChange(name, sortField) { - this.handleInteraction(); + const handleSortChange = (name, sortField) => { + handleInteraction(); - const prev = this.state.sorting[name]; + const prev = sorting[name]; const sortReverse = sortField === prev.sortField ? !prev.sortReverse : false; - this.setState({ - sorting: { - ...this.state.sorting, - [name]: { - sortField, - sortReverse, - }, + const newSorting = { + ...sorting, + [name]: { + sortField, + sortReverse, }, - }); - } + }; + setSorting(newSorting); + }; - handleInteraction() { - const {onInteraction} = this.props; - if (isDefined(onInteraction)) { - onInteraction(); - } - } + const handleInteraction = () => dispatch(renewSessionTimeout(gmp)()); - loadTarget() { - const {entity} = this.props; + const loadTarget = () => { const target = getTarget(entity); + return gmp.target.get({id: target.id}); + }; + + const {reportFilter, showError, showErrorMessage, showSuccessMessage} = props; + + const {report} = entity || {}; - return this.props.loadTarget(target.id); - } - - render() { - const { - entity, - entityError, - filters = [], - isLoading, - reportFilter, - reportFormats, - reportId, - onInteraction, - reportComposerDefaults, - showError, - showErrorMessage, - showSuccessMessage, - } = this.props; - const { - activeTab, - isUpdating = false, - showFilterDialog, - showDownloadReportDialog, - sorting, - storeAsDefault, - } = this.state; - - const {report} = entity || {}; - - return ( - - - {({edit}) => ( - - this.loadTarget().then(response => edit(response.data)) - } - onTlsCertificateDownloadClick={this.handleTlsCertificateDownload} - showError={showError} - showErrorMessage={showErrorMessage} - showSuccessMessage={showSuccessMessage} - /> - )} - - {showFilterDialog && ( - + + {({edit}) => ( + + loadTarget().then(response => edit(response.data)) + } + showError={showError} + showErrorMessage={showErrorMessage} + showSuccessMessage={showSuccessMessage} /> )} - {showDownloadReportDialog && ( - - )} - - ); - } -} + + {showFilterDialog && ( + + )} + {showDownloadReportDialog && ( + + )} + + ); +}; DeltaAuditReportDetails.propTypes = { defaultFilter: PropTypes.filter, - deltaReportId: PropTypes.id, - entity: PropTypes.model, - entityError: PropTypes.object, - filters: PropTypes.array, - gmp: PropTypes.gmp.isRequired, - isLoading: PropTypes.bool.isRequired, - loadFilters: PropTypes.func.isRequired, - loadReport: PropTypes.func.isRequired, - loadReportComposerDefaults: PropTypes.func.isRequired, - loadReportFormats: PropTypes.func.isRequired, - loadReportIfNeeded: PropTypes.func.isRequired, - loadSettings: PropTypes.func.isRequired, - loadTarget: PropTypes.func.isRequired, location: PropTypes.object.isRequired, - match: PropTypes.object.isRequired, reload: PropTypes.func.isRequired, - reportComposerDefaults: PropTypes.object, - reportExportFileName: PropTypes.string, reportFilter: PropTypes.filter, - reportFormats: PropTypes.array, - reportId: PropTypes.id, - resultDefaultFilter: PropTypes.filter, - saveReportComposerDefaults: PropTypes.func.isRequired, showError: PropTypes.func.isRequired, showErrorMessage: PropTypes.func.isRequired, showSuccessMessage: PropTypes.func.isRequired, target: PropTypes.model, username: PropTypes.string, onDownload: PropTypes.func.isRequired, - onInteraction: PropTypes.func.isRequired, -}; - -const mapDispatchToProps = (dispatch, {gmp}) => { - return { - onInteraction: () => dispatch(renewSessionTimeout(gmp)()), - loadFilters: () => dispatch(loadFilters(gmp)(RESULTS_FILTER_FILTER)), - loadSettings: () => dispatch(loadUserSettingDefaults(gmp)()), - loadTarget: targetId => gmp.target.get({id: targetId}), - loadReportFormats: () => - dispatch(loadReportFormats(gmp)(REPORT_FORMATS_FILTER)), - loadReport: (id, deltaId, filter) => - dispatch(loadDeltaAuditReport(gmp)(id, deltaId, filter)), - loadReportIfNeeded: (id, deltaId, filter) => - dispatch(loadDeltaAuditReport(gmp)(id, deltaId, filter)), - loadReportComposerDefaults: () => - dispatch(loadReportComposerDefaults(gmp)()), - loadUserSettingDefaultFilter: () => - dispatch(loadUserSettingsDefaultFilter(gmp)('result')), - saveReportComposerDefaults: reportComposerDefaults => - dispatch(saveReportComposerDefaults(gmp)(reportComposerDefaults)), - }; -}; - -const mapStateToProps = (rootState, {match}) => { - const {id, deltaid} = match.params; - const filterSel = filterSelector(rootState); - const deltaSel = deltaAuditReportSelector(rootState); - const reportFormatsSel = reportFormatsSelector(rootState); - const userDefaultsSelector = getUserSettingsDefaults(rootState); - const userDefaultFilterSel = getUserSettingsDefaultFilter( - rootState, - 'result', - ); - const username = getUsername(rootState); - const entity = deltaSel.getEntity(id, deltaid); - const entityError = deltaSel.getError(id, deltaid); - - return { - deltaReportId: deltaid, - entity, - entityError, - filters: filterSel.getAllEntities(RESULTS_FILTER_FILTER), - isLoading: !isDefined(entity), - reportExportFileName: userDefaultsSelector.getValueByName( - 'reportexportfilename', - ), - reportFilter: getFilter(entity), - reportFormats: reportFormatsSel.getAllEntities(REPORT_FORMATS_FILTER), - reportId: id, - reportComposerDefaults: getReportComposerDefaults(rootState), - resultDefaultFilter: userDefaultFilterSel.getFilter('result'), - username, - }; }; const reloadInterval = report => @@ -625,14 +484,7 @@ const reloadInterval = report => : NO_RELOAD; // report doesn't change anymore. no need to reload const load = - ({ - defaultFilter, - reportId, - deltaReportId, - loadReport, - loadReportIfNeeded, - reportFilter, - }) => + ({defaultFilter, reportId, deltaReportId, dispatch, gmp, reportFilter}) => filter => { if (!hasValue(filter)) { // use loaded filter after initial loading @@ -654,45 +506,65 @@ const load = // sort term will be handled by GSA in the browser) filter.delete('sort-reverse'); filter.set('sort', 'name'); - return loadReportIfNeeded(reportId, deltaReportId, filter).then(() => - loadReport(reportId, deltaReportId, filter), + return dispatch( + loadDeltaAuditReport(gmp)(reportId, deltaReportId, filter), + ).then(() => + dispatch(loadDeltaAuditReport(gmp)(reportId, deltaReportId, filter)), ); }; -const DeltaAuditReportDetailsWrapper = ({ - defaultFilter, - reportFilter, - ...props -}) => ( - reloadInterval(props.entity)} - > - {({reload}) => ( - - )} - -); +const DeltaAuditReportDetailsWrapper = ({defaultFilter, ...props}) => { + const gmp = useGmp(); + const dispatch = useDispatch(); + const match = useRouteMatch(); + + const {id: reportId, deltaid: deltaReportId} = match.params; + const deltaSel = useSelector(deltaAuditReportSelector, shallowEqual); + const entity = deltaSel.getEntity(reportId, deltaReportId); + const reportFilter = getFilter(entity); + + return ( + reloadInterval(entity)} + > + {({reload}) => ( + + )} + + ); +}; DeltaAuditReportDetailsWrapper.propTypes = { defaultFilter: PropTypes.filter, - entity: PropTypes.model, - gmp: PropTypes.gmp.isRequired, - reportFilter: PropTypes.filter, }; export default compose( - withGmp, withDialogNotification, withDownload, - connect(mapStateToProps, mapDispatchToProps), )(DeltaAuditReportDetailsWrapper); // vim: set ts=2 sw=2 tw=80: diff --git a/src/web/pages/reports/auditdetailscontent.js b/src/web/pages/reports/auditdetailscontent.js index 9447230fd3..6e2f4d8772 100644 --- a/src/web/pages/reports/auditdetailscontent.js +++ b/src/web/pages/reports/auditdetailscontent.js @@ -56,7 +56,7 @@ import EntityInfo from 'web/entity/info'; import EntityTags from 'web/entity/tags'; import PropTypes from 'web/utils/proptypes'; -import withGmp from 'web/utils/withGmp'; +import useGmp from 'web/utils/useGmp'; import ErrorsTab from './details/errorstab'; import HostsTab from './details/hoststab'; @@ -77,7 +77,6 @@ const PageContent = ({ entity, errorsCounts, filters, - gmp, hostsCounts, isLoading = true, isLoadingFilters = true, @@ -99,12 +98,10 @@ const PageContent = ({ onAddToAssetsClick, onTlsCertificateDownloadClick, onError, - onFilterAddLogLevelClick, onFilterChanged, onFilterCreated, onFilterDecreaseMinQoDClick, onFilterEditClick, - onFilterRemoveSeverityClick, onFilterRemoveClick, onFilterResetClick, onInteraction, @@ -120,11 +117,12 @@ const PageContent = ({ const userTags = hasReport ? report.userTags : undefined; const userTagsCount = isDefined(userTags) ? userTags.length : 0; + const gmp = useGmp(); const { errors = {}, hosts = {}, - operatingsystems = {}, + operatingSystems = {}, results = {}, tlsCertificates = {}, timestamp, @@ -284,9 +282,7 @@ const PageContent = ({ reportResultsCounts={resultsCounts} sortField={sorting.results.sortField} sortReverse={sorting.results.sortReverse} - onFilterAddLogLevelClick={onFilterAddLogLevelClick} onFilterDecreaseMinQoDClick={onFilterDecreaseMinQoDClick} - onFilterRemoveSeverityClick={onFilterRemoveSeverityClick} onFilterEditClick={onFilterEditClick} onFilterRemoveClick={onFilterRemoveClick} onInteraction={onInteraction} @@ -339,8 +335,8 @@ const PageContent = ({ ) : ( . */ -import React from 'react'; +import React, {useEffect, useState} from 'react'; -import {connect} from 'react-redux'; +import {useDispatch, useSelector, shallowEqual} from 'react-redux'; + +import {useRouteMatch} from 'react-router-dom'; import _ from 'gmp/locale'; @@ -85,6 +87,7 @@ import Page from './auditdetailscontent'; import FilterDialog from './detailsfilterdialog'; import {pageFilter as setPageFilter} from 'web/store/pages/actions'; import getPage from 'web/store/pages/selectors'; +import useGmp from 'web/utils/useGmp'; const log = logger.getLogger('web.pages.auditreport.detailspage'); @@ -98,6 +101,8 @@ export const AUDIT_REPORT_RESET_FILTER = RESET_FILTER.copy() const REPORT_FORMATS_FILTER = Filter.fromString('active=1 and trust=1 rows=-1'); +const getReportPageName = id => `report-${id}`; + const getTarget = (entity = {}) => { const {report = {}} = entity; const {task = {}} = report; @@ -109,127 +114,127 @@ const getFilter = (entity = {}) => { return report.filter; }; -class ReportDetails extends React.Component { - constructor(...args) { - super(...args); - - this.state = { - activeTab: 0, - showFilterDialog: false, - showDownloadReportDialog: false, - sorting: { - results: { - sortField: 'compliant', - sortReverse: true, - }, - hosts: { - sortField: 'compliant', - sortReverse: true, - }, - os: { - sortField: 'compliant', - sortReverse: true, - }, - tlscerts: { - sortField: 'dn', - sortReverse: false, - }, - errors: { - sortField: 'error', - sortReverse: false, - }, - }, - }; +const ReportDetails = props => { + const [activeTab, setActiveTab] = useState(0); + const [showFilterDialog, setShowFilterDialog] = useState(false); + const [showDownloadReportDialog, setShowDownloadReportDialog] = + useState(false); + const [sorting, setSorting] = useState({ + results: { + sortField: 'compliant', + sortReverse: true, + }, + hosts: { + sortField: 'compliant', + sortReverse: true, + }, + os: { + sortField: 'compliant', + sortReverse: true, + }, + tlscerts: { + sortField: 'dn', + sortReverse: false, + }, + errors: { + sortField: 'error', + sortReverse: false, + }, + }); + + const [entity, setEntity] = useState(undefined); + const [resultsCounts, setResultsCounts] = useState(undefined); + const [hostsCounts, setHostsCounts] = useState(undefined); + const [operatingSystemsCounts, setOperatingSystemsCounts] = + useState(undefined); + const [tlsCertificatesCounts, setTlsCertificatesCounts] = useState(undefined); + const [reportFormatId, setReportFormatId] = useState(undefined); + const [errorsCounts, setErrorsCounts] = useState(undefined); + const [reportFilter, setReportFilter] = useState(undefined); + const [isUpdating, setIsUpdating] = useState(false); + // storeAsDefault is set in SaveDialogContent + // eslint-disable-next-line no-unused-vars + const [storeAsDefault, setStoreAsDefault] = useState(undefined); + + const gmp = useGmp(); + const dispatch = useDispatch(); + const match = useRouteMatch(); + const {id: reportId} = match.params; + + const pSelector = useSelector(getPage, shallowEqual); + const pageFilter = pSelector?.getFilter(getReportPageName(reportId)); + + const [selectedEntity, reportError, isLoading] = useSelector(state => { + const reportSel = auditReportSelector(state); + return [ + reportSel?.getEntity(reportId, pageFilter), + reportSel?.getEntityError(reportId, pageFilter), + reportSel?.isLoadingEntity(reportId, pageFilter), + ]; + }, shallowEqual); + + const userDefaultsSelector = useSelector( + getUserSettingsDefaults, + shallowEqual, + ); + const reportExportFileName = userDefaultsSelector?.getValueByName( + 'reportexportfilename', + ); + + const reportFormatsSel = useSelector(reportFormatsSelector); + const reportFormats = reportFormatsSel?.getAllEntities(REPORT_FORMATS_FILTER); + const reportComposerDefaults = useSelector(getReportComposerDefaults); + const userDefaultFilterSel = useSelector( + rootState => getUserSettingsDefaultFilter(rootState, 'result'), + shallowEqual, + ); + const resultDefaultFilter = userDefaultFilterSel?.getFilter(); + const username = useSelector(getUsername); + + useEffect(() => { + dispatch(loadUserSettingDefaults(gmp)()); + dispatch(loadFilters(gmp)(RESULTS_FILTER_FILTER)); + dispatch(loadReportFormats(gmp)(REPORT_FORMATS_FILTER)); + dispatch(loadReportComposerDefaults(gmp)()); + + if (isDefined(selectedEntity)) { + setEntity(entity); + updateReportCounts(selectedEntity); + setReportFilter(props.reportFilter); + setIsUpdating(false); + } else { + setIsUpdating(true); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - this.handleActivateTab = this.handleActivateTab.bind(this); - this.handleAddToAssets = this.handleAddToAssets.bind(this); - this.handleChanged = this.handleChanged.bind(this); - this.handleError = this.handleError.bind(this); - this.handleFilterAddLogLevel = this.handleFilterAddLogLevel.bind(this); - this.handleFilterChange = this.handleFilterChange.bind(this); - this.handleFilterDecreaseMinQoD = - this.handleFilterDecreaseMinQoD.bind(this); - this.handleFilterCreated = this.handleFilterCreated.bind(this); - this.handleFilterEditClick = this.handleFilterEditClick.bind(this); - this.handleFilterRemoveSeverity = - this.handleFilterRemoveSeverity.bind(this); - this.handleFilterRemoveClick = this.handleFilterRemoveClick.bind(this); - this.handleFilterResetClick = this.handleFilterResetClick.bind(this); - this.handleRemoveFromAssets = this.handleRemoveFromAssets.bind(this); - this.handleReportDownload = this.handleReportDownload.bind(this); - this.handleTlsCertificateDownload = - this.handleTlsCertificateDownload.bind(this); - this.handleFilterDialogClose = this.handleFilterDialogClose.bind(this); - this.handleSortChange = this.handleSortChange.bind(this); - - this.loadTarget = this.loadTarget.bind(this); - this.handleOpenDownloadReportDialog = - this.handleOpenDownloadReportDialog.bind(this); - this.handleCloseDownloadReportDialog = - this.handleCloseDownloadReportDialog.bind(this); - } - - static getDerivedStateFromProps(props, state) { - if (isDefined(props.entity)) { + useEffect(() => { + if (isDefined(selectedEntity)) { // update only if a new report is available to avoid having no report // when the filter changes - - const {report = {}} = props.entity; - const { - results = {}, - hosts = {}, - operatingsystems = {}, - tlsCertificates = {}, - errors = {}, - } = report; - - return { - entity: props.entity, - - resultsCounts: isDefined(results.counts) - ? results.counts - : state.resultsCounts, - hostsCounts: isDefined(hosts.counts) ? hosts.counts : state.hostsCounts, - operatingSystemsCounts: isDefined(operatingsystems.counts) - ? operatingsystems.counts - : state.operatingSystemsCounts, - tlsCertificatesCounts: isDefined(tlsCertificates.counts) - ? tlsCertificates.counts - : state.tlsCertificatesCounts, - errorsCounts: isDefined(errors.counts) - ? errors.counts - : state.errorsCounts, - reportFilter: props.reportFilter, - isUpdating: false, - }; + setEntity(selectedEntity); + updateReportCounts(selectedEntity); + setReportFilter(props.reportFilter); + setIsUpdating(false); + } else { + // report is not in the store and is currently loaded + setIsUpdating(true); } - // report is not in the store and is currently loaded - return { - isUpdating: true, - }; - } + }, [selectedEntity, props.reportFilter]); - componentDidMount() { - this.props.loadSettings(); - this.props.loadFilters(); - this.props.loadReportFormats(); - this.props.loadReportComposerDefaults(); - } - - componentDidUpdate(prevProps) { - const {reportFormats} = this.props; + useEffect(() => { if ( - !isDefined(this.state.reportFormatId) && + !isDefined(reportFormatId) && isDefined(reportFormats) && reportFormats.length > 0 ) { // set initial report format id if available - const reportFormatId = first(reportFormats).id; - if (isDefined(reportFormatId)) { + const initialReportFormatId = first(reportFormats).id; + if (isDefined(initialReportFormatId)) { // ensure the report format id is only set if we really have one // if no report format id is available we would create an infinite // render loop here - this.setState({reportFormatId}); + setReportFormatId({initialReportFormatId}); } else { // if there is no report format at all, throw a proper error message // instead of just showing x is undefined JS stacktrace @@ -243,134 +248,153 @@ class ReportDetails extends React.Component { throw new Error(noReportFormatError); } } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [reportFormats, reportFormatId]); + + useEffect(() => { + load(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [reportId]); - if (prevProps.reportId !== this.props.reportId) { - this.load(); + const updateReportCounts = reportEntity => { + const {report = {}} = reportEntity; + const { + results = {}, + hosts = {}, + operatingSystems = {}, + tlsCertificates = {}, + errors = {}, + } = report; + + if (isDefined(results.counts)) { + setResultsCounts(results.counts); } - } + if (isDefined(hosts.counts)) { + setHostsCounts(hosts.counts); + } + if (isDefined(operatingSystems.counts)) { + setOperatingSystemsCounts(operatingSystems.counts); + } + if (isDefined(tlsCertificates.counts)) { + setTlsCertificatesCounts(tlsCertificates.counts); + } + if (isDefined(errors.counts)) { + setErrorsCounts(errors.counts); + } + }; - load(filter) { + const load = newFilter => { log.debug('Loading report', { - filter, + newFilter, }); - const {reportFilter} = this.props; + const {reportFilter: filter} = props; - this.setState({ - isUpdating: !isDefined(reportFilter) || !reportFilter.equals(filter), // show update indicator if filter has changed - }); + setIsUpdating(!isDefined(filter) || !filter.equals(newFilter)); // show update indicator if filter has changed - this.props - .reload(filter) + props + .reload(newFilter) .then(() => { - this.setState({isUpdating: false}); + setIsUpdating(false); }) .catch(() => { - this.setState({isUpdating: false}); + setIsUpdating(false); }); - } + }; - reload() { + const reload = () => { // reload data from backend - this.load(this.props.reportFilter); - } + load(props.reportFilter); + }; - handleChanged() { - this.reload(); - } + const handleChanged = () => { + reload(); + }; - handleError(error) { - const {showError} = this.props; + const handleError = error => { + const {showError} = props; log.error(error); showError(error); - } - - handleFilterChange(filter) { - this.handleInteraction(); + }; - this.load(filter); - } + const handleFilterChange = filter => { + handleInteraction(); + load(filter); + }; - handleFilterRemoveClick() { - this.handleFilterChange(AUDIT_REPORT_RESET_FILTER); - } + const handleFilterRemoveClick = () => { + handleFilterChange(AUDIT_REPORT_RESET_FILTER); + }; - handleFilterResetClick() { - if (hasValue(this.props.resultDefaultFilter)) { - this.handleFilterChange(this.props.resultDefaultFilter); + const handleFilterResetClick = () => { + if (hasValue(resultDefaultFilter)) { + handleFilterChange(resultDefaultFilter); } else { - this.handleFilterChange(DEFAULT_FILTER); + handleFilterChange(DEFAULT_FILTER); } - } - - handleActivateTab(index) { - this.handleInteraction(); + }; - this.setState({activeTab: index}); - } + const handleActivateTab = index => { + handleInteraction(); + setActiveTab(index); + }; - handleAddToAssets() { - const {gmp, showSuccessMessage, entity, reportFilter: filter} = this.props; + const handleAddToAssets = () => { + const {showSuccessMessage, reportFilter: filter} = props; - this.handleInteraction(); + handleInteraction(); - gmp.auditreport.addAssets(entity, {filter}).then(() => { + gmp.auditreport.addAssets(selectedEntity, {filter}).then(() => { showSuccessMessage( _( 'Report content added to Assets with QoD>=70% and Overrides enabled.', ), ); - this.reload(); - }, this.handleError); - } + reload(); + }, handleError); + }; - handleRemoveFromAssets() { - const {gmp, showSuccessMessage, entity, reportFilter: filter} = this.props; + const handleRemoveFromAssets = () => { + const {showSuccessMessage, reportFilter: filter} = props; - this.handleInteraction(); + handleInteraction(); - gmp.auditreport.removeAssets(entity, {filter}).then(() => { + gmp.auditreport.removeAssets(selectedEntity, {filter}).then(() => { showSuccessMessage(_('Report content removed from Assets.')); - this.reload(); - }, this.handleError); - } - - handleFilterEditClick() { - this.handleInteraction(); + reload(); + }, handleError); + }; - this.setState({showFilterDialog: true}); - } + const handleFilterEditClick = () => { + handleInteraction(); + setShowFilterDialog(true); + }; - handleFilterDialogClose() { - this.handleInteraction(); + const handleFilterDialogClose = () => { + handleInteraction(); + setShowFilterDialog(false); + }; - this.setState({showFilterDialog: false}); - } + const handleOpenDownloadReportDialog = () => { + setShowDownloadReportDialog(true); + }; - handleOpenDownloadReportDialog() { - this.setState({ - showDownloadReportDialog: true, - }); - } + const handleCloseDownloadReportDialog = () => { + setShowDownloadReportDialog(false); + }; - handleCloseDownloadReportDialog() { - this.setState({showDownloadReportDialog: false}); - } + const handleReportDownload = state => { + const {reportFilter: filter, onDownload} = props; - handleReportDownload(state) { const { - entity, - gmp, - reportComposerDefaults, - reportExportFileName, - reportFilter, - reportFormats = [], - username, - onDownload, - } = this.props; - const {includeNotes, includeOverrides, reportFormatId, storeAsDefault} = - state; - - const newFilter = reportFilter.copy(); + includeNotes, + includeOverrides, + // eslint-disable-next-line no-shadow + reportFormatId, + // eslint-disable-next-line no-shadow + storeAsDefault, + } = state; + + const newFilter = filter.copy(); newFilter.set('notes', includeNotes); newFilter.set('overrides', includeOverrides); @@ -381,288 +405,205 @@ class ReportDetails extends React.Component { includeNotes, includeOverrides, }; - this.props.saveReportComposerDefaults(defaults); + dispatch(saveReportComposerDefaults(gmp)(defaults)); } - const report_format = reportFormats.find( - format => reportFormatId === format.id, - ); + const report_format = reportFormats + ? reportFormats.find(format => reportFormatId === format.id) + : undefined; const extension = isDefined(report_format) ? report_format.extension : 'unknown'; // unknown should never happen but we should be save here - this.handleInteraction(); + handleInteraction(); return gmp.auditreport - .download(entity, { + .download(selectedEntity, { reportFormatId, filter: newFilter, }) .then(response => { - this.setState({showDownloadReportDialog: false}); + setShowDownloadReportDialog(false); const {data} = response; const filename = generateFilename({ - creationTime: entity.creationTime, + creationTime: selectedEntity.creationTime, extension, fileNameFormat: reportExportFileName, - id: entity.id, - modificationTime: entity.modificationTime, - reportFormat: report_format.name, - resourceName: entity.task.name, + id: selectedEntity.id, + modificationTime: selectedEntity.modificationTime, + reportFormat: report_format?.name, + resourceName: selectedEntity.task.name, resourceType: 'report', username, }); onDownload({filename, data}); - }, this.handleError); - } + }, handleError); + }; - handleTlsCertificateDownload(cert) { - const {onDownload} = this.props; + const handleTlsCertificateDownload = cert => { + const {onDownload} = props; const {data, serial} = cert; - this.handleInteraction(); + handleInteraction(); onDownload({ filename: 'tls-cert-' + serial + '.pem', mimetype: 'application/x-x509-ca-cert', data: create_pem_certificate(data), }); - } - - handleFilterCreated(filter) { - this.handleInteraction(); - this.load(filter); - this.props.loadFilters(); - } - - handleFilterAddLogLevel() { - const {reportFilter} = this.props; - let levels = reportFilter.get('levels', ''); - - this.handleInteraction(); - - if (!levels.includes('g')) { - levels += 'g'; - const lfilter = reportFilter.copy(); - lfilter.set('levels', levels); - this.load(lfilter); - } - } - - handleFilterRemoveSeverity() { - const {reportFilter} = this.props; - - this.handleInteraction(); + }; - if (reportFilter.has('severity')) { - const lfilter = reportFilter.copy(); - lfilter.delete('severity'); - this.load(lfilter); - } - } + const handleFilterCreated = filter => { + handleInteraction(); + load(filter); + dispatch(loadFilters(gmp)(RESULTS_FILTER_FILTER)); + }; - handleFilterDecreaseMinQoD() { - const {reportFilter} = this.props; + const handleFilterDecreaseMinQoD = () => { + const {reportFilter: filter} = props; - this.handleInteraction(); + handleInteraction(); - if (reportFilter.has('min_qod')) { - const lfilter = reportFilter.copy(); + if (filter.has('min_qod')) { + const lfilter = filter.copy(); lfilter.set('min_qod', 30); - this.load(lfilter); + load(lfilter); } - } + }; - handleSortChange(name, sortField) { - this.handleInteraction(); + const handleSortChange = (name, sortField) => { + handleInteraction(); - const prev = this.state.sorting[name]; + const prev = sorting[name]; const sortReverse = sortField === prev.sortField ? !prev.sortReverse : false; - this.setState({ - sorting: { - ...this.state.sorting, - [name]: { - sortField, - sortReverse, - }, + const newSort = { + ...sorting, + [name]: { + sortField, + sortReverse, }, - }); - } + }; + setSorting(newSort); + }; - handleInteraction() { - const {onInteraction} = this.props; - if (isDefined(onInteraction)) { - onInteraction(); - } - } + const handleInteraction = () => dispatch(renewSessionTimeout(gmp)()); - loadTarget() { - const {entity} = this.props; - const target = getTarget(entity); + const loadTarget = () => { + const target = getTarget(selectedEntity); + return gmp.target.get({id: target.id}); + }; - return this.props.loadTarget(target.id); - } + const {showError, showErrorMessage, showSuccessMessage} = props; - render() { - const { - filters = [], - gmp, - isLoading, - isLoadingFilters, - pageFilter, - reportError, - reportFormats, - reportId, - onInteraction, - reportComposerDefaults, - showError, - showErrorMessage, - showSuccessMessage, - } = this.props; - const { - activeTab, - entity, - errorsCounts, - hostsCounts, - isUpdating = false, - operatingSystemsCounts, - reportFilter, - resultsCounts, - showFilterDialog, - showDownloadReportDialog, - sorting, - storeAsDefault, - tlsCertificatesCounts, - } = this.state; + const report = isDefined(entity) ? entity.report : undefined; - const report = isDefined(entity) ? entity.report : undefined; + const threshold = gmp.settings.reportResultsThreshold; - const threshold = gmp.settings.reportResultsThreshold; + const showThresholdMessage = + isDefined(report) && report.results.counts.filtered > threshold; - const showThresholdMessage = - isDefined(report) && report.results.counts.filtered > threshold; + const [filters, isLoadingFilters] = useSelector(state => { + const filterSel = filterSelector(state); + return [ + filterSel?.getAllEntities(RESULTS_FILTER_FILTER), + filterSel?.isLoadingAllEntities(RESULTS_FILTER_FILTER), + ]; + }); - return ( - - - - {({edit}) => ( - - this.loadTarget().then(response => edit(response.data)) - } - onTlsCertificateDownloadClick={this.handleTlsCertificateDownload} - showError={showError} - showErrorMessage={showErrorMessage} - showSuccessMessage={showSuccessMessage} - /> - )} - - {showFilterDialog && ( - + + + {({edit}) => ( + - )} - {showDownloadReportDialog && ( - + loadTarget().then(response => edit(response.data)) + } + onTlsCertificateDownloadClick={handleTlsCertificateDownload} + showError={showError} + showErrorMessage={showErrorMessage} + showSuccessMessage={showSuccessMessage} /> )} - - ); - } -} + + {showFilterDialog && ( + + )} + {showDownloadReportDialog && ( + + )} + + ); +}; ReportDetails.propTypes = { - entity: PropTypes.model, - filter: PropTypes.filter, - filters: PropTypes.array, - gmp: PropTypes.gmp.isRequired, - isLoading: PropTypes.bool, - isLoadingFilters: PropTypes.bool, - loadFilters: PropTypes.func.isRequired, - loadReportComposerDefaults: PropTypes.func.isRequired, - loadReportFormats: PropTypes.func.isRequired, - loadSettings: PropTypes.func.isRequired, - loadTarget: PropTypes.func.isRequired, location: PropTypes.object.isRequired, - match: PropTypes.object.isRequired, - pageFilter: PropTypes.filter, reload: PropTypes.func.isRequired, - reportComposerDefaults: PropTypes.object, - reportError: PropTypes.error, - reportExportFileName: PropTypes.string, reportFilter: PropTypes.filter, - reportFormats: PropTypes.array, - reportId: PropTypes.id, - resultDefaultFilter: PropTypes.filter, - saveReportComposerDefaults: PropTypes.func.isRequired, showError: PropTypes.func.isRequired, showErrorMessage: PropTypes.func.isRequired, showSuccessMessage: PropTypes.func.isRequired, target: PropTypes.model, username: PropTypes.string, onDownload: PropTypes.func.isRequired, - onInteraction: PropTypes.func.isRequired, }; const reloadInterval = report => @@ -675,10 +616,11 @@ const load = defaultFilter, reportId, // eslint-disable-next-line no-shadow - loadReportWithThreshold, + dispatch, + gmp, + match, pageFilter, reportFilter, - updateFilter, }) => filter => { if (!hasValue(filter)) { @@ -700,110 +642,72 @@ const load = // use fallback filter filter = DEFAULT_FILTER; } - - updateFilter(filter); - return loadReportWithThreshold(reportId, {filter}); + dispatch(setPageFilter(getReportPageName(match.params.id), filter)); + return dispatch(loadAuditReportWithThreshold(gmp)(reportId, {filter})); }; -const ReportDetailsWrapper = ({reportFilter, ...props}) => ( - - {({filter}) => ( - reloadInterval(props.entity)} - > - {({reload}) => ( - - )} - - )} - -); - -ReportDetailsWrapper.propTypes = { - entity: PropTypes.model, - gmp: PropTypes.gmp.isRequired, - reportFilter: PropTypes.filter, - reportId: PropTypes.id.isRequired, -}; - -const getReportPageName = id => `report-${id}`; - -const mapDispatchToProps = (dispatch, {gmp, entity, match}) => ({ - onInteraction: () => dispatch(renewSessionTimeout(gmp)()), - loadFilters: () => dispatch(loadFilters(gmp)(RESULTS_FILTER_FILTER)), - loadSettings: () => dispatch(loadUserSettingDefaults(gmp)()), - loadTarget: targetId => gmp.target.get({id: targetId}), - loadReportFormats: () => - dispatch(loadReportFormats(gmp)(REPORT_FORMATS_FILTER)), - loadReportWithThreshold: (id, options) => - dispatch(loadAuditReportWithThreshold(gmp)(id, options)), - loadReportComposerDefaults: () => dispatch(loadReportComposerDefaults(gmp)()), - saveReportComposerDefaults: reportComposerDefaults => - dispatch(saveReportComposerDefaults(gmp)(reportComposerDefaults)), - updateFilter: f => - dispatch(setPageFilter(getReportPageName(match.params.id), f)), -}); - -const mapStateToProps = (rootState, {match}) => { - const {id} = match.params; - const filterSel = filterSelector(rootState); - const reportSel = auditReportSelector(rootState); - const reportFormatsSel = reportFormatsSelector(rootState); - const userDefaultsSelector = getUserSettingsDefaults(rootState); - const userDefaultFilterSel = getUserSettingsDefaultFilter( - rootState, - 'result', - ); - const username = getUsername(rootState); - - const pSelector = getPage(rootState); - const pageFilter = pSelector.getFilter(getReportPageName(id)); - - const entity = reportSel.getEntity(id, pageFilter); - const isLoading = reportSel.isLoadingEntity(id, pageFilter); - const reportError = reportSel.getEntityError(id, pageFilter); - - const filters = filterSel.getAllEntities(RESULTS_FILTER_FILTER); - const isLoadingFilters = filterSel.isLoadingAllEntities( - RESULTS_FILTER_FILTER, +const ReportDetailsWrapper = props => { + const dispatch = useDispatch(); + const gmp = useGmp(); + const match = useRouteMatch(); + + const {id: reportId} = match.params; + const reportSel = useSelector(auditReportSelector, shallowEqual); + const pSelector = useSelector(getPage, shallowEqual); + + const pageFilter = pSelector.getFilter(getReportPageName(reportId)); + const entity = reportSel.getEntity(reportId, pageFilter); + const reportFilter = getFilter(entity); + + return ( + + {({filter}) => ( + reloadInterval(entity)} + > + {({reload}) => ( + + )} + + )} + ); - - return { - entity, - filters, - reportError, - pageFilter, - isLoading, - isLoadingFilters, - reportExportFileName: userDefaultsSelector.getValueByName( - 'reportexportfilename', - ), - reportFilter: getFilter(entity), - reportFormats: reportFormatsSel.getAllEntities(REPORT_FORMATS_FILTER), - reportId: id, - reportComposerDefaults: getReportComposerDefaults(rootState), - resultDefaultFilter: userDefaultFilterSel.getFilter(), - username, - }; }; export default compose( withGmp, withDialogNotification, withDownload, - connect(mapStateToProps, mapDispatchToProps), )(ReportDetailsWrapper); // vim: set ts=2 sw=2 tw=80: diff --git a/src/web/pages/reports/auditfilterdialog.js b/src/web/pages/reports/auditfilterdialog.js index e9618b1367..b7ee5a41be 100644 --- a/src/web/pages/reports/auditfilterdialog.js +++ b/src/web/pages/reports/auditfilterdialog.js @@ -22,7 +22,7 @@ import {_l, _} from 'gmp/locale/lang'; import Layout from 'web/components/layout/layout'; import compose from 'web/utils/compose'; -import withCapabilities from 'web/utils/withCapabilities'; +import useCapabilities from 'web/utils/useCapabilities'; /* eslint-disable max-len */ @@ -71,7 +71,6 @@ const SORT_FIELDS = [ ]; const AuditReportFilterDialogComponent = ({ - capabilities, filter, filterName, filterstring, @@ -87,6 +86,8 @@ const AuditReportFilterDialogComponent = ({ const handleRemoveCompliance = () => onFilterChange(filter.delete('report_compliance_levels')); + const capabilities = useCapabilities(); + if (!filter) { return null; } @@ -144,9 +145,6 @@ const AuditReportFilterDialogComponent = ({ AuditReportFilterDialogComponent.propTypes = FilterDialogPropTypes; -export default compose( - withCapabilities, - withFilterDialog(), -)(AuditReportFilterDialogComponent); +export default compose(withFilterDialog())(AuditReportFilterDialogComponent); // vim: set ts=2 sw=2 tw=80: diff --git a/src/web/pages/reports/auditreportrow.js b/src/web/pages/reports/auditreportrow.js index b922244eb1..de5684d7e4 100644 --- a/src/web/pages/reports/auditreportrow.js +++ b/src/web/pages/reports/auditreportrow.js @@ -139,10 +139,10 @@ const AuditRow = ({ - {report.compliance_count.yes.filtered} - {report.compliance_count.no.filtered} + {report.complianceCounts.yes.filtered} + {report.complianceCounts.no.filtered} - {report.compliance_count.incomplete.filtered} + {report.complianceCounts.incomplete.filtered} diff --git a/src/web/pages/reports/auditreportslistpage.js b/src/web/pages/reports/auditreportslistpage.js index 51f81fb81b..f7d64e24b4 100644 --- a/src/web/pages/reports/auditreportslistpage.js +++ b/src/web/pages/reports/auditreportslistpage.js @@ -16,7 +16,9 @@ * along with this program. If not, see . */ -import React from 'react'; +import React, {useEffect, useState} from 'react'; + +import {useHistory} from 'react-router-dom'; import _ from 'gmp/locale'; @@ -68,46 +70,36 @@ const ToolBarIcons = () => ( ); -class Page extends React.Component { - constructor(...args) { - super(...args); - - this.state = {}; - - this.handleReportDeltaSelect = this.handleReportDeltaSelect.bind(this); - this.handleReportDeleteClick = this.handleReportDeleteClick.bind(this); - this.handleTaskChange = this.handleTaskChange.bind(this); - } - - static getDerivedStateFromProps(props, state) { - const {filter} = props; - const {selectedDeltaReport} = state; +const AuditReportsPage = props => { + const [selectedDeltaReport, setSelectedDeltaReport] = useState(undefined); + const [beforeSelectFilter, setBeforeSelectFilter] = useState(undefined); + const history = useHistory(); + useEffect(() => { if ( isDefined(selectedDeltaReport) && - (!isDefined(filter) || - filter.get('task_id') !== selectedDeltaReport.task.id) + (!isDefined(props.filter) || + props.filter.get('task_id') !== selectedDeltaReport.task.id) ) { // filter has changed. reset delta report selection - return {selectedDeltaReport: undefined}; + setSelectedDeltaReport(undefined); } - return null; - } + }, [props.filter, selectedDeltaReport]); - handleReportDeltaSelect(report) { - const {onFilterChanged} = this.props; - const {selectedDeltaReport, beforeSelectFilter} = this.state; + const handleReportDeleteClick = report => { + const {onDelete} = props; + return onDelete(report); + }; + const handleReportDeltaSelect = report => { if (isDefined(selectedDeltaReport)) { - const {history} = this.props; - onFilterChanged(beforeSelectFilter); history.push( '/auditreport/delta/' + selectedDeltaReport.id + '/' + report.id, ); } else { - const {filter = new Filter()} = this.props; + const {filter = new Filter()} = props; onFilterChanged( filter @@ -115,60 +107,46 @@ class Page extends React.Component { .set('first', 1) // reset to first page .set('task_id', report.task.id), ); - - this.setState({ - beforeSelectFilter: filter, - selectedDeltaReport: report, - }); + setSelectedDeltaReport(report); + setBeforeSelectFilter(filter); } - } + }; + + const {filter, onFilterChanged, onInteraction} = props; + return ( + + + ( + + )} + dashboardControls={() => ( + + )} + filtersFilter={AUDIT_REPORTS_FILTER_FILTER} + filterEditDialog={AuditFilterDialog} + table={AuditReportsTable} + toolBarIcons={ToolBarIcons} + title={_('Audit Reports')} + sectionIcon={} + onInteraction={onInteraction} + onReportDeltaSelect={handleReportDeltaSelect} + onReportDeleteClick={handleReportDeleteClick} + /> + + ); +}; - handleReportDeleteClick(report) { - const {onDelete} = this.props; - return onDelete(report); - } - - handleTaskChange(task_id) { - this.setState({task_id}); - } - - render() { - const {filter, onFilterChanged, onInteraction} = this.props; - return ( - - - ( - - )} - dashboardControls={() => ( - - )} - filtersFilter={AUDIT_REPORTS_FILTER_FILTER} - filterEditDialog={AuditFilterDialog} - table={AuditReportsTable} - toolBarIcons={ToolBarIcons} - title={_('Audit Reports')} - sectionIcon={} - onInteraction={onInteraction} - onReportDeltaSelect={this.handleReportDeltaSelect} - onReportDeleteClick={this.handleReportDeleteClick} - /> - - ); - } -} - -Page.propTypes = { +AuditReportsPage.propTypes = { filter: PropTypes.filter, gmp: PropTypes.gmp.isRequired, history: PropTypes.object.isRequired, @@ -196,6 +174,6 @@ export default compose( loadEntities, reloadInterval: reportsReloadInterval, }), -)(Page); +)(AuditReportsPage); // vim: set ts=2 sw=2 tw=80: diff --git a/src/web/pages/reports/auditreportstable.js b/src/web/pages/reports/auditreportstable.js index a4660d3c2e..3ccbddaaad 100644 --- a/src/web/pages/reports/auditreportstable.js +++ b/src/web/pages/reports/auditreportstable.js @@ -21,18 +21,18 @@ import {_, _l} from 'gmp/locale/lang'; import {isDefined} from 'gmp/utils/identity'; -import PropTypes from '../../utils/proptypes.js'; +import PropTypes from 'web/utils/proptypes'; -import {createEntitiesFooter} from '../../entities/footer.js'; -import {createEntitiesTable} from '../../entities/table.js'; +import {createEntitiesFooter} from 'web/entities/footer'; +import {createEntitiesTable} from 'web/entities/table'; -import ComplianceState from '../../components/label/compliancestate.js'; +import ComplianceState from 'web/components/label/compliancestate'; -import TableHead from '../../components/table/head.js'; -import TableHeader from '../../components/table/header.js'; -import TableRow from '../../components/table/row.js'; +import TableHead from 'web/components/table/head'; +import TableHeader from 'web/components/table/header'; +import TableRow from 'web/components/table/row'; -import AuditReportRow from './auditreportrow.js'; +import AuditReportRow from './auditreportrow'; const Header = ({ actionsColumn, diff --git a/src/web/pages/reports/deltadetailscontent.js b/src/web/pages/reports/deltadetailscontent.js index 1c95f2e478..405c9af10d 100644 --- a/src/web/pages/reports/deltadetailscontent.js +++ b/src/web/pages/reports/deltadetailscontent.js @@ -83,7 +83,6 @@ const PageContent = ({ task, onActivateTab, onAddToAssetsClick, - onTlsCertificateDownloadClick, onError, onFilterAddLogLevelClick, onFilterChanged, @@ -107,7 +106,7 @@ const PageContent = ({ const { results = {}, - compliance_count = {}, + complianceCounts = {}, result_count = {}, timestamp, scan_run_status, @@ -145,7 +144,7 @@ const PageContent = ({ ); - const {filtered} = audit ? compliance_count : result_count; + const {filtered} = audit ? complianceCounts : result_count; return ( @@ -276,13 +275,13 @@ PageContent.propTypes = { onActivateTab: PropTypes.func.isRequired, onAddToAssetsClick: PropTypes.func.isRequired, onError: PropTypes.func.isRequired, - onFilterAddLogLevelClick: PropTypes.func.isRequired, + onFilterAddLogLevelClick: PropTypes.func, onFilterChanged: PropTypes.func.isRequired, onFilterCreated: PropTypes.func.isRequired, onFilterDecreaseMinQoDClick: PropTypes.func.isRequired, onFilterEditClick: PropTypes.func.isRequired, onFilterRemoveClick: PropTypes.func.isRequired, - onFilterRemoveSeverityClick: PropTypes.func.isRequired, + onFilterRemoveSeverityClick: PropTypes.func, onFilterResetClick: PropTypes.func.isRequired, onInteraction: PropTypes.func.isRequired, onRemoveFromAssetsClick: PropTypes.func.isRequired, @@ -290,7 +289,6 @@ PageContent.propTypes = { onSortChange: PropTypes.func.isRequired, onTagSuccess: PropTypes.func.isRequired, onTargetEditClick: PropTypes.func.isRequired, - onTlsCertificateDownloadClick: PropTypes.func.isRequired, }; export default PageContent; diff --git a/src/web/pages/reports/deltadetailspage.js b/src/web/pages/reports/deltadetailspage.js index f7f96e73ad..c0b59e598d 100644 --- a/src/web/pages/reports/deltadetailspage.js +++ b/src/web/pages/reports/deltadetailspage.js @@ -71,7 +71,6 @@ import { getUsername, } from 'web/store/usersettings/selectors'; -import {create_pem_certificate} from 'web/utils/cert'; import compose from 'web/utils/compose'; import {generateFilename} from 'web/utils/render'; import PropTypes from 'web/utils/proptypes'; @@ -165,8 +164,6 @@ class DeltaReportDetails extends React.Component { this.handleFilterResetClick = this.handleFilterResetClick.bind(this); this.handleRemoveFromAssets = this.handleRemoveFromAssets.bind(this); this.handleReportDownload = this.handleReportDownload.bind(this); - this.handleTlsCertificateDownload = - this.handleTlsCertificateDownload.bind(this); this.handleFilterDialogClose = this.handleFilterDialogClose.bind(this); this.handleSortChange = this.handleSortChange.bind(this); @@ -371,19 +368,6 @@ class DeltaReportDetails extends React.Component { }, this.handleError); } - handleTlsCertificateDownload(cert) { - const {onDownload} = this.props; - - const {data, serial} = cert; - - this.handleInteraction(); - - onDownload({ - filename: 'tls-cert-' + serial + '.pem', - data: create_pem_certificate(data), - }); - } - handleFilterCreated(filter) { this.handleInteraction(); this.load(filter); @@ -523,7 +507,6 @@ class DeltaReportDetails extends React.Component { onTargetEditClick={() => this.loadTarget().then(response => edit(response.data)) } - onTlsCertificateDownloadClick={this.handleTlsCertificateDownload} showError={showError} showErrorMessage={showErrorMessage} showSuccessMessage={showSuccessMessage} diff --git a/src/web/pages/reports/details/emptyresultsreport.js b/src/web/pages/reports/details/emptyresultsreport.js index 0d44c9bd6c..1ae0e94b73 100644 --- a/src/web/pages/reports/details/emptyresultsreport.js +++ b/src/web/pages/reports/details/emptyresultsreport.js @@ -68,7 +68,7 @@ const EmptyResultsReport = ({ - {!levels.includes('g') && ( + {!levels.includes('g') && isDefined(onFilterAddLogLevelClick) && ( } title={_('Log messages are currently excluded.')} @@ -78,7 +78,7 @@ const EmptyResultsReport = ({ )} - {has_severity_filter && ( + {has_severity_filter && isDefined(onFilterRemoveSeverityClick) && ( } title={_( @@ -129,11 +129,11 @@ const EmptyResultsReport = ({ EmptyResultsReport.propTypes = { all: PropTypes.number.isRequired, filter: PropTypes.filter.isRequired, - onFilterAddLogLevelClick: PropTypes.func.isRequired, + onFilterAddLogLevelClick: PropTypes.func, onFilterDecreaseMinQoDClick: PropTypes.func.isRequired, onFilterEditClick: PropTypes.func.isRequired, onFilterRemoveClick: PropTypes.func.isRequired, - onFilterRemoveSeverityClick: PropTypes.func.isRequired, + onFilterRemoveSeverityClick: PropTypes.func, }; export default EmptyResultsReport; diff --git a/src/web/pages/reports/details/hoststab.js b/src/web/pages/reports/details/hoststab.js index 9f0fefacd0..9a8052619a 100644 --- a/src/web/pages/reports/details/hoststab.js +++ b/src/web/pages/reports/details/hoststab.js @@ -49,13 +49,13 @@ const hostsSortFunctions = { start: makeCompareDate(entity => entity.start), end: makeCompareDate(entity => entity.end), total: makeCompareNumber(entity => entity.result_counts.total), - compliance_yes: makeCompareNumber(entity => entity.compliance_counts.yes), - compliance_no: makeCompareNumber(entity => entity.compliance_counts.no), - compliance_incomplete: makeCompareNumber( - entity => entity.compliance_counts.incomplete, + complianceYes: makeCompareNumber(entity => entity.complianceCounts.yes), + complianceNo: makeCompareNumber(entity => entity.complianceCounts.no), + complianceIncomplete: makeCompareNumber( + entity => entity.complianceCounts.incomplete, ), - compliance_total: makeCompareNumber(entity => entity.compliance_counts.total), - compliant: makeCompareString('host_compliance'), + complianceTotal: makeCompareNumber(entity => entity.complianceCounts.total), + compliant: makeCompareString('hostCompliance'), }; const HostsTab = ({ diff --git a/src/web/pages/reports/details/hoststable.js b/src/web/pages/reports/details/hoststable.js index e6e8a65a9d..02de322e39 100644 --- a/src/web/pages/reports/details/hoststable.js +++ b/src/web/pages/reports/details/hoststable.js @@ -122,7 +122,7 @@ const Header = ({ { authSuccess, details = {}, end, - host_compliance, + hostCompliance, ip, result_counts = {}, - compliance_counts = {}, + complianceCounts = {}, severity, start, portsCount, @@ -333,30 +333,30 @@ const Row = ({entity, links = true, audit = false}) => { {audit ? ( - {compliance_counts.yes} + {complianceCounts.yes} ) : ( {result_counts.high} )} {audit ? ( - {compliance_counts.no} + {complianceCounts.no} ) : ( {result_counts.warning} )} {audit ? ( - {compliance_counts.incomplete} + {complianceCounts.incomplete} ) : ( {result_counts.info} )} {!audit && {result_counts.log}} {!audit && {result_counts.false_positive}} {audit ? ( - {compliance_counts.total} + {complianceCounts.total} ) : ( {result_counts.total} )} {audit ? ( - + ) : ( diff --git a/src/web/pages/reports/details/resultstab.js b/src/web/pages/reports/details/resultstab.js index 5a36bed723..6a759de2ae 100644 --- a/src/web/pages/reports/details/resultstab.js +++ b/src/web/pages/reports/details/resultstab.js @@ -272,11 +272,11 @@ ResultsTab.propTypes = { resultsError: PropTypes.error, resultsFilter: PropTypes.filter, status: PropTypes.string.isRequired, - onFilterAddLogLevelClick: PropTypes.func.isRequired, + onFilterAddLogLevelClick: PropTypes.func, onFilterDecreaseMinQoDClick: PropTypes.func.isRequired, onFilterEditClick: PropTypes.func.isRequired, onFilterRemoveClick: PropTypes.func.isRequired, - onFilterRemoveSeverityClick: PropTypes.func.isRequired, + onFilterRemoveSeverityClick: PropTypes.func, onTargetEditClick: PropTypes.func.isRequired, }; diff --git a/src/web/pages/tasks/row.js b/src/web/pages/tasks/row.js index 37496c76db..842b8c7b96 100644 --- a/src/web/pages/tasks/row.js +++ b/src/web/pages/tasks/row.js @@ -54,7 +54,7 @@ import Trend from './trend'; import {GREENBONE_SENSOR_SCANNER_TYPE} from 'gmp/models/scanner'; -const renderReport = (report, links) => { +export const renderReport = (report, links) => { if (!isDefined(report)) { return null; } diff --git a/src/web/utils/theme.js b/src/web/utils/theme.js index 6cbf026bcc..f7d572db79 100644 --- a/src/web/utils/theme.js +++ b/src/web/utils/theme.js @@ -37,10 +37,10 @@ const Theme = { darkRed: '#c12c30', // used by: dialog errors font errorRed: '#c83814', // used by: progressbar - compliance_yes: '#4cb045', - compliance_no: '#D80000', - compliance_incomplete: 'orange', - compliance_undefined: 'silver', + complianceYes: '#4cb045', + complianceNo: '#D80000', + complianceIncomplete: 'orange', + complianceUndefined: 'silver', lightBlue: '#d6e6fd', // used by InfoPanel and dashboard hovering mediumBlue: '#77acf7', // used by active/hovered items in Select