diff --git a/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js b/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js index 730e2b535..cc695f837 100644 --- a/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js +++ b/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js @@ -47,7 +47,10 @@ import { DEFAULT_PAGE_SIZE_OPTIONS } from '../../../../pages/Monitors/containers import queryString from 'query-string'; import { MAX_ALERT_COUNT } from '../../../../pages/Dashboard/utils/constants'; import { SEVERITY_OPTIONS } from '../../../../pages/CreateTrigger/utils/constants'; -import { TABLE_TAB_IDS } from '../../../../pages/Dashboard/components/FindingsDashboard/utils'; +import { + ALERTS_FINDING_COLUMN, + TABLE_TAB_IDS, +} from '../../../../pages/Dashboard/components/FindingsDashboard/utils'; import FindingsDashboard from '../../../../pages/Dashboard/containers/FindingsDashboard'; export const DEFAULT_NUM_FLYOUT_ROWS = 10; @@ -146,11 +149,15 @@ export default class AlertsDashboardFlyoutComponent extends Component { } getBucketLevelGraphConditions = (trigger) => { - let conditions = _.get(trigger, 'condition.script.source', '-'); - conditions = _.replace(conditions, ' && ', '&AND&'); - conditions = _.replace(conditions, ' || ', '&OR&'); - conditions = conditions.split(/&/); - return conditions.join('\n'); + let conditions = _.get(trigger, 'condition.script.source'); + if (_.isEmpty(conditions)) { + return '-'; + } else { + conditions = conditions.replaceAll(' && ', '&AND&'); + conditions = conditions.replaceAll(' || ', '&OR&'); + conditions = conditions.split(/&/); + return conditions.join('\n'); + } }; getSeverityText = (severity) => { @@ -167,7 +174,7 @@ export default class AlertsDashboardFlyoutComponent extends Component { }; getAlerts = async () => { - this.setState({ loading: true }); + this.setState({ loading: true, tabContent: undefined }); const { from, search, @@ -263,8 +270,7 @@ export default class AlertsDashboardFlyoutComponent extends Component { alertState, monitorIds ); - this.setState({ selectedItems: [], tabContent: undefined }); - this.setState({ tabContent: this.renderAlertsTable() }); + this.setState({ selectedItems: [] }); this.props.refreshDashboard(); }; @@ -349,6 +355,10 @@ export default class AlertsDashboardFlyoutComponent extends Component { case MONITOR_TYPE.BUCKET_LEVEL: columns = insertGroupByColumn(groupBy); break; + case MONITOR_TYPE.DOC_LEVEL: + columns = _.cloneDeep(queryColumns); + columns.splice(0, 0, ALERTS_FINDING_COLUMN); + break; default: columns = queryColumns; break; diff --git a/public/pages/CreateMonitor/components/MonitorType/MonitorType.js b/public/pages/CreateMonitor/components/MonitorType/MonitorType.js index 03a8a39d9..983b3472f 100644 --- a/public/pages/CreateMonitor/components/MonitorType/MonitorType.js +++ b/public/pages/CreateMonitor/components/MonitorType/MonitorType.js @@ -60,7 +60,7 @@ const documentLevelDescription = ( // TODO DRAFT: confirm wording ); const MonitorType = ({ values }) => ( - +
( ( - Query duration + Monitor duration {`${_.get(response, 'took', DEFAULT_EMPTY_DATA)} ms`} diff --git a/public/pages/CreateMonitor/components/QueryPerformance/__snapshots__/QueryPerformance.test.js.snap b/public/pages/CreateMonitor/components/QueryPerformance/__snapshots__/QueryPerformance.test.js.snap index 6e89b5f99..e0117d4f0 100644 --- a/public/pages/CreateMonitor/components/QueryPerformance/__snapshots__/QueryPerformance.test.js.snap +++ b/public/pages/CreateMonitor/components/QueryPerformance/__snapshots__/QueryPerformance.test.js.snap @@ -15,7 +15,7 @@ exports[`QueryPerformance renders 1`] = `

- Query performance + Monitor performance

- Query duration + Monitor duration -
{message}
+
{_.isEmpty(message) ? : message}
); @@ -236,14 +237,18 @@ class DefineMonitor extends Component { } }; + let accordionTitle = 'Preview query and performance'; const previewContent = () => { switch (values.monitor_type) { case MONITOR_TYPE.BUCKET_LEVEL: return this.getBucketMonitorGraphs(aggregations, formikSnapshot, response); case MONITOR_TYPE.DOC_LEVEL: const { index, queries } = values; + accordionTitle = 'Preview findings and performance'; return _.isEmpty(response) ? ( - renderEmptyMessage('Loading findings...') + renderEmptyMessage( + validDocLevelGraphQueries(queries) ? '' : 'You must define at least one query.' + ) ) : ( - + @@ -286,9 +288,16 @@ class DefineMonitor extends Component { async onRunQuery() { this.setState({ loadingResponse: true }); const { httpClient, values, notifications } = this.props; - const formikSnapshot = _.cloneDeep(values); + const { monitor_type, searchType } = values; + + // Cancel execution criteria + switch (monitor_type) { + case MONITOR_TYPE.DOC_LEVEL: + const { queries } = values; + if (SEARCH_TYPE.GRAPH && !validDocLevelGraphQueries(queries)) return; + } - const searchType = values.searchType; + const formikSnapshot = _.cloneDeep(values); let requests; switch (searchType) { case SEARCH_TYPE.QUERY: @@ -341,20 +350,29 @@ class DefineMonitor extends Component { const endTime = moment(); const duration = moment.duration(endTime.diff(startTime)).milliseconds(); const response = _.get(queryResponse.resp, 'input_results.results[0]'); + // If there is an optionalResponse use it's results, otherwise use the original response const performanceResponse = optionalResponse ? _.get(optionalResponse, 'resp.input_results.results[0]', null) : response; - this.setState({ - response, - formikSnapshot, - // TODO FIXME: Doc level backend monitor run results don't include duration metric. Using this for now. - // This returns a much longer duration than other monitors, though. - performanceResponse: - values.monitor_type === MONITOR_TYPE.DOC_LEVEL - ? { ...performanceResponse, took: duration } - : performanceResponse, - }); + this.setState({ response, formikSnapshot, performanceResponse }); + + // TODO FIXME: Doc level backend monitor run results don't include duration metrics. Using this for now. + // This returns a much longer duration than other monitors, though. + if (monitor_type === MONITOR_TYPE.DOC_LEVEL) { + let hitsCount = 0; + _.keys(response).forEach( + (resultKey) => (hitsCount += _.values(performanceResponse[resultKey]).length) + ); + this.setState({ + performanceResponse: { + ...performanceResponse, + took: duration, + invalid: { path: duration }, + hits: { total: { value: hitsCount } }, + }, + }); + } } else { console.error('There was an error running the query', queryResponse.resp); backendErrorNotification(notifications, 'run', 'query', queryResponse.resp); diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js index 51fe4d72a..e84e30ecc 100644 --- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js +++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js @@ -109,7 +109,7 @@ export function getDocumentLevelScriptSource(conditions) { if (!_.isEmpty(query) && !_.isEmpty(query.queryName)) { const queryExpression = _.get(query, 'expression'); const operator = query.operator === '!=' ? '!' : ''; - scriptSourceContents.push(`(${operator}query[${queryExpression}])`); + scriptSourceContents.push(`${operator}query[${queryExpression}]`); } }); return scriptSourceContents.join(' '); diff --git a/public/pages/CreateTrigger/containers/DefineDocumentLevelTrigger/DefineDocumentLevelTrigger.js b/public/pages/CreateTrigger/containers/DefineDocumentLevelTrigger/DefineDocumentLevelTrigger.js index 6d0a932e4..7b749fd5a 100644 --- a/public/pages/CreateTrigger/containers/DefineDocumentLevelTrigger/DefineDocumentLevelTrigger.js +++ b/public/pages/CreateTrigger/containers/DefineDocumentLevelTrigger/DefineDocumentLevelTrigger.js @@ -31,7 +31,7 @@ import { backendErrorNotification, inputLimitText } from '../../../../utils/help import monitorToFormik from '../../../CreateMonitor/containers/CreateMonitor/utils/monitorToFormik'; import { buildRequest } from '../../../CreateMonitor/containers/DefineMonitor/utils/searchRequests'; -const MAX_TRIGGER_CONDITIONS = 5; // TODO DRAFT: Placeholder limit +const MAX_TRIGGER_CONDITIONS = 10; const defaultRowProps = { label: 'Trigger name', diff --git a/public/pages/Dashboard/components/DashboardEmptyPrompt/DashboardEmptyPrompt.js b/public/pages/Dashboard/components/DashboardEmptyPrompt/DashboardEmptyPrompt.js index c1ca17d80..ebc7b35c9 100644 --- a/public/pages/Dashboard/components/DashboardEmptyPrompt/DashboardEmptyPrompt.js +++ b/public/pages/Dashboard/components/DashboardEmptyPrompt/DashboardEmptyPrompt.js @@ -5,7 +5,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { EuiButton, EuiEmptyPrompt, EuiText } from '@elastic/eui'; +import { EuiButton, EuiButtonEmpty, EuiEmptyPrompt, EuiText } from '@elastic/eui'; import { APP_PATH } from '../../../../utils/constants'; import { PLUGIN_NAME } from '../../../../../utils/constants'; @@ -23,9 +23,7 @@ const createMonitorButton = ( ); const editMonitorButton = (onCreateTrigger) => ( - - Edit monitor - + Edit monitor ); const DashboardEmptyPrompt = ({ onCreateTrigger, isModal = false }) => { @@ -35,6 +33,11 @@ const DashboardEmptyPrompt = ({ onCreateTrigger, isModal = false }) => { : inMonitorDetails ? createTriggerText : createMonitorText; + const actions = inMonitorDetails + ? undefined + : isModal + ? editMonitorButton(onCreateTrigger) + : createMonitorButton; return ( {

{displayText}

} - actions={ - inMonitorDetails || isModal ? editMonitorButton(onCreateTrigger) : createMonitorButton - } + actions={actions} /> ); }; diff --git a/public/pages/Dashboard/components/FindingsDashboard/FindingFlyout.js b/public/pages/Dashboard/components/FindingsDashboard/FindingFlyout.js index 331be736e..b2e55e92c 100644 --- a/public/pages/Dashboard/components/FindingsDashboard/FindingFlyout.js +++ b/public/pages/Dashboard/components/FindingsDashboard/FindingFlyout.js @@ -64,8 +64,8 @@ export default class FindingFlyout extends Component { onClose={this.closeFlyout} ownFocus={false} hideCloseButton={true} - side={isAlertsFlyout ? 'left' : 'right'} - size={'s'} + side={'right'} + size={'m'} > @@ -119,13 +119,20 @@ export default class FindingFlyout extends Component { overflowHeight={600} inline={false} isCopyable + style={{ height: '400px' }} > {JSON.stringify(documentDisplay, null, 3)} - Close + + Close + ); diff --git a/public/pages/Dashboard/components/FindingsDashboard/QueriesPopover.js b/public/pages/Dashboard/components/FindingsDashboard/FindingsPopover.js similarity index 50% rename from public/pages/Dashboard/components/FindingsDashboard/QueriesPopover.js rename to public/pages/Dashboard/components/FindingsDashboard/FindingsPopover.js index ed2ad4f9b..4da4670fc 100644 --- a/public/pages/Dashboard/components/FindingsDashboard/QueriesPopover.js +++ b/public/pages/Dashboard/components/FindingsDashboard/FindingsPopover.js @@ -4,16 +4,33 @@ */ import React, { useState } from 'react'; +import _ from 'lodash'; import { EuiLink, EuiPopover, EuiSpacer, EuiText } from '@elastic/eui'; -export default function QueryPopover(queries) { +export default function FindingsPopover({ docIds = [], queries = [] }) { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const onButtonClick = () => setIsPopoverOpen(!isPopoverOpen); const closePopover = () => setIsPopoverOpen(false); - const popoverContent = queries.queries.map((query, index) => { - return ( + let header; + let popoverContent; + let count; + + if (!_.isEmpty(docIds)) { + header = 'Documents'; + popoverContent = docIds.map((docId, index) => ( +
+ {index > 0 && } + +

{docId}

+
+
+ )); + count = popoverContent.length; + } else { + header = 'Queries'; + popoverContent = queries.map((query, index) => (
{index > 0 && } @@ -21,12 +38,15 @@ export default function QueryPopover(queries) {

{query.query}

- ); - }); + )); + count = popoverContent.length; + } + + const buttonContent = `${count} ${header}`; return ( {`${queries.queries.length} Queries`}} + button={{buttonContent}} isOpen={isPopoverOpen} closePopover={closePopover} > diff --git a/public/pages/Dashboard/components/FindingsDashboard/utils.js b/public/pages/Dashboard/components/FindingsDashboard/utils.js index 7ec7a6478..796b59f81 100644 --- a/public/pages/Dashboard/components/FindingsDashboard/utils.js +++ b/public/pages/Dashboard/components/FindingsDashboard/utils.js @@ -7,13 +7,30 @@ import React from 'react'; import _ from 'lodash'; import { renderTime } from '../../utils/tableUtils'; import FindingFlyout from './FindingFlyout'; -import QueryPopover from './QueriesPopover'; +import FindingsPopover from './FindingsPopover'; +import { QUERY_OPERATORS } from '../../../CreateMonitor/components/DocumentLevelMonitorQueries/DocumentLevelQuery'; export const TABLE_TAB_IDS = { ALERTS: { id: 'alerts', name: 'Alerts' }, FINDINGS: { id: 'findings', name: 'Document findings' }, }; +export const ALERTS_FINDING_COLUMN = { + field: 'related_doc_ids', + name: 'Document', + sortable: true, + truncateText: true, + render: (related_doc_ids, alert) => { + if (_.isEmpty(related_doc_ids)) + console.log('Alerts index contains an entry with 0 related document IDs:', alert); + return related_doc_ids.length > 1 ? ( + + ) : ( + related_doc_ids[0] + ); + }, +}; + export const findingsColumnTypes = (isAlertsFlyout) => [ { field: 'document_list', @@ -39,11 +56,11 @@ export const findingsColumnTypes = (isAlertsFlyout) => [ name: 'Query', sortable: true, truncateText: false, - render: (queries) => { + render: (queries, finding) => { if (_.isEmpty(queries)) - console.log('Findings index contains an entry with 0 queries:', queries); + console.log('Findings index contains an entry with 0 queries:', finding); return queries.length > 1 ? ( - + ) : ( `${queries[0].name} (${queries[0].query})` ); @@ -74,31 +91,36 @@ export const getFindingsForMonitor = (findings, monitorId) => { export const parseFindingsForPreview = (previewResponse, index, queries = []) => { // TODO FIXME: ExecuteMonitor API currently only returns a list of query names/IDs and the relevant docIds. // As a result, the preview dashboard cannot display document contents. - const timestamp = Date.now(); const findings = []; - const docIdsToQueries = {}; - - _.keys(previewResponse).forEach((queryName) => { - _.get(previewResponse, queryName, []).forEach((id) => { - if (_.includes(_.keys(docIdsToQueries), id)) { - const query = _.find(queries, { queryName: queryName }); - docIdsToQueries[id].push({ name: queryName, query: query.query }); - } else { - const query = _.find(queries, { queryName: queryName }); - docIdsToQueries[id] = [{ name: queryName, query: query.query }]; + if (validDocLevelGraphQueries(queries)) { + const queryNames = queries.map((query) => query.queryName); + const timestamp = Date.now(); + const docIdsToQueries = {}; + _.keys(previewResponse).forEach((queryName) => { + if (_.includes(queryNames, queryName)) { + _.get(previewResponse, queryName, []).forEach((id) => { + if (_.includes(_.keys(docIdsToQueries), id)) { + const query = _.find(queries, { queryName: queryName }); + docIdsToQueries[id].push({ name: queryName, query: query.query }); + } else { + const query = _.find(queries, { queryName: queryName }); + const operator = _.find(QUERY_OPERATORS, { value: query.operator }).text; + const querySource = `${query.field} ${operator} ${query.query}`; + docIdsToQueries[id] = [{ name: queryName, query: querySource }]; + } + }); } }); - }); - - _.keys(docIdsToQueries).forEach((docId) => { - const finding = { - index: index, - related_doc_id: docId, - queries: docIdsToQueries[docId], - timestamp: timestamp, - }; - findings.push(finding); - }); + _.keys(docIdsToQueries).forEach((docId) => { + const finding = { + index: index, + related_doc_id: docId, + queries: docIdsToQueries[docId], + timestamp: timestamp, + }; + findings.push(finding); + }); + } return findings; }; @@ -112,3 +134,12 @@ export const getPreviewResponseDocIds = (response) => { }); return docIds; }; + +export const validDocLevelGraphQueries = (queries) => { + // The 'queryName' and 'query' fields are required to execute a doc level query. + // If either are undefined for any queries, the monitor cannot be executed. + const allQueriesDefined = queries.find( + (query) => !_.isEmpty(query.queryName) && !_.isEmpty(query.query) + ); + return !_.isEmpty(allQueriesDefined); +}; diff --git a/public/pages/Dashboard/containers/FindingsDashboard.js b/public/pages/Dashboard/containers/FindingsDashboard.js index d62d8593b..481f929e1 100644 --- a/public/pages/Dashboard/containers/FindingsDashboard.js +++ b/public/pages/Dashboard/containers/FindingsDashboard.js @@ -6,7 +6,7 @@ import React, { Component } from 'react'; import _ from 'lodash'; import queryString from 'query-string'; -import { EuiBasicTable } from '@elastic/eui'; +import { EuiBasicTable, EuiEmptyPrompt, EuiLoadingSpinner, EuiText } from '@elastic/eui'; import ContentPanel from '../../../components/ContentPanel'; import { backendErrorNotification } from '../../../utils/helpers'; import { DEFAULT_PAGE_SIZE_OPTIONS } from '../../Monitors/containers/Monitors/utils/constants'; @@ -29,6 +29,10 @@ export const GET_FINDINGS_PREVIEW_PARAMS = { sortField: GET_FINDINGS_SORT_FIELDS.TIMESTAMP, }; +export const NO_FINDINGS_TEXT = + 'There are no existing findings. Adjust document level queries to generate findings. Once a document is indexed that meets the query condition, the finding will show in this table.'; +export const MAX_FINDINGS_COUNT = 10000; + export default class FindingsDashboard extends Component { constructor(props) { super(props); @@ -54,18 +58,16 @@ export default class FindingsDashboard extends Component { componentDidMount() { const { isPreview = false } = this.props; - if (isPreview) { - this.getPreviewFindingsDocuments(); - } else { - const { id, from, size, search, sortField, sortDirection } = this.state; - this.getFindings(id, from, size, search, sortDirection, sortField); - } + if (isPreview) this.getPreviewFindingsDocuments(); + else this.getFindings(); } componentDidUpdate(prevProps, prevState) { const prevQuery = this.getQueryObjectFromState(prevState); const currQuery = this.getQueryObjectFromState(this.state); - if (!_.isEqual(prevQuery, currQuery)) this.componentDidMount(); + if (!_.isEqual(prevQuery, currQuery)) this.sortFindings(); + if (!_.isEqual(prevQuery.sortDirection, currQuery.sortDirection)) + this.setState({ findings: _.reverse(this.state.findings) }); } getURLQueryParams() { @@ -78,10 +80,15 @@ export default class FindingsDashboard extends Component { sortField = DEFAULT_GET_FINDINGS_PARAMS.sortField, sortDirection = DEFAULT_GET_FINDINGS_PARAMS.sortDirection, } = queryString.parse(location.search); + const parsedSize = isNaN(parseInt(size, 10)) + ? DEFAULT_GET_FINDINGS_PARAMS.size + : parseInt(size, 10); return { id, from: isNaN(parseInt(from, 10)) ? DEFAULT_GET_FINDINGS_PARAMS.from : parseInt(from, 10), - size: isNaN(parseInt(size, 10)) ? DEFAULT_GET_FINDINGS_PARAMS.size : parseInt(size, 10), + size: _.includes(DEFAULT_PAGE_SIZE_OPTIONS, parsedSize) + ? parsedSize + : DEFAULT_GET_FINDINGS_PARAMS.size, search, sortField: _.includes(_.values(GET_FINDINGS_SORT_FIELDS), sortField) ? sortField @@ -95,8 +102,9 @@ export default class FindingsDashboard extends Component { } getFindings = _.debounce( - (id, from, size, search, sortDirection, sortField) => { + () => { this.setState({ loadingFindings: true }); + const { id, from, size, search, sortField, sortDirection } = this.state; const params = { id, from, @@ -106,6 +114,11 @@ export default class FindingsDashboard extends Component { sortField, }; const queryParamsString = queryString.stringify(params); + + // TODO FIXME: Refactor 'size' logic to return all findings for a monitor + // once the backend supports retrieving findings for a monitorId. + params['size'] = Math.max(size, MAX_FINDINGS_COUNT); + location.search; const { httpClient, history, monitorId, notifications } = this.props; history.replace({ ...this.props.location, search: queryParamsString }); @@ -113,6 +126,7 @@ export default class FindingsDashboard extends Component { httpClient.get('../api/alerting/findings/_search', { query: params }).then((resp) => { if (resp.ok) { this.setState({ ...getFindingsForMonitor(resp.findings, monitorId) }); + this.sortFindings(); } else { console.log('Error getting findings:', resp); backendErrorNotification(notifications, 'get', 'findings', resp.err); @@ -124,6 +138,35 @@ export default class FindingsDashboard extends Component { { leading: true } ); + sortFindings() { + this.setState({ loadingFindings: true }); + const { findings, sortField } = this.state; + let sortedFindings; + switch (sortField) { + case 'document_list': + sortedFindings = _.orderBy(findings, (finding) => _.get(finding, 'document_list.0.id', '')); + break; + case GET_FINDINGS_SORT_FIELDS.INDEX: + sortedFindings = _.orderBy(findings, (finding) => + _.get(finding, GET_FINDINGS_SORT_FIELDS.INDEX, '') + ); + break; + case GET_FINDINGS_SORT_FIELDS.MONITOR_NAME: + sortedFindings = _.orderBy(findings, (finding) => + _.get(finding, GET_FINDINGS_SORT_FIELDS.MONITOR_NAME, '') + ); + break; + case 'queries': + sortedFindings = _.orderBy(findings, (finding) => _.get(finding, 'queries', []).length); + break; + default: + sortedFindings = _.orderBy(findings, (finding) => + _.get(finding, GET_FINDINGS_SORT_FIELDS.TIMESTAMP, '') + ); + } + this.setState({ findings: sortedFindings, loadingFindings: false }); + } + getPreviewFindingsDocuments() { this.setState({ loadingFindings: true }); const { index, queries, previewResponse } = this.props; @@ -154,7 +197,7 @@ export default class FindingsDashboard extends Component { const pagination = { pageIndex: page, pageSize: size, - totalItemCount: Math.min(size, totalFindings), + totalItemCount: totalFindings, pageSizeOptions: DEFAULT_PAGE_SIZE_OPTIONS, }; @@ -167,6 +210,7 @@ export default class FindingsDashboard extends Component { const getItemId = (item) => item.id; + const paginatedFindings = findings.slice(page * size, page * size + size); return ( + ) : ( + +

{NO_FINDINGS_TEXT}

+ + } + /> + ) + } />
); diff --git a/server/services/AlertService.js b/server/services/AlertService.js index cf2f1ba9a..80d0a1d8b 100644 --- a/server/services/AlertService.js +++ b/server/services/AlertService.js @@ -24,10 +24,7 @@ export default class AlertService { size = 20, search = '', sortDirection = 'desc', - // If the sortField parsed from the URL isn't a valid option for this API, use a default option. - sortField = _.includes(_.values(GET_ALERTS_SORT_FILTERS), req.query.sortField) - ? req.query.sortField - : GET_ALERTS_SORT_FILTERS.START_TIME, + sortField = GET_ALERTS_SORT_FILTERS.START_TIME, severityLevel = 'ALL', alertState = 'ALL', monitorIds = [], @@ -47,12 +44,6 @@ export default class AlertService { sortOrder: sortDirection, }; break; - case GET_ALERTS_SORT_FILTERS.START_TIME: - params = { - sortString: sortField, - sortOrder: sortDirection, - }; - break; case GET_ALERTS_SORT_FILTERS.END_TIME: params = { sortString: sortField, @@ -67,6 +58,12 @@ export default class AlertService { missing: '_last', }; break; + default: + // If the sortField parsed from the URL isn't a valid option for this API, use a default option. + params = { + sortString: GET_ALERTS_SORT_FILTERS.START_TIME, + sortOrder: sortDirection, + }; } params.startIndex = from; diff --git a/server/services/FindingService.js b/server/services/FindingService.js index 177c3be29..c9c1e568f 100644 --- a/server/services/FindingService.js +++ b/server/services/FindingService.js @@ -36,10 +36,7 @@ export default class FindingService { size = DEFAULT_GET_FINDINGS_PARAMS.size, search = DEFAULT_GET_FINDINGS_PARAMS.search, sortDirection = DEFAULT_GET_FINDINGS_PARAMS.sortDirection, - // If the sortField parsed from the URL isn't a valid option for this API, use a default option. - sortField = _.includes(_.values(GET_FINDINGS_SORT_FIELDS), req.query.sortField) - ? req.query.sortField - : DEFAULT_GET_FINDINGS_PARAMS.sortField, + sortField = DEFAULT_GET_FINDINGS_PARAMS.sortField, } = req.query; var params; @@ -56,12 +53,12 @@ export default class FindingService { sortOrder: sortDirection, }; break; - case GET_FINDINGS_SORT_FIELDS.TIMESTAMP: + default: + // If the sortField parsed from the URL isn't a valid option for this API, use a default option. params = { - sortString: sortField, + sortString: GET_FINDINGS_SORT_FIELDS.TIMESTAMP, sortOrder: sortDirection, }; - break; } if (!_.isEmpty(id)) params.findingId = id;