From 3f9308c1e09085d66c8dbd18e87eb51269c2ef18 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic <jovanca.cvetkovic@gmail.com>
Date: Thu, 16 Feb 2023 04:12:29 +0100
Subject: [PATCH] Feature/update vertical domain (#372)

* [FEATURE] Detector must have at least one alert set #288

Signed-off-by: Jovan Cvetkovic <jovanca.cvetkovic@gmail.com>

* [FEATURE] Expand the chart's vertical domain so that top positioning elements are not clipped. #334

Signed-off-by: Jovan Cvetkovic <jovanca.cvetkovic@gmail.com>

* Feature/update vertical domain #638

Signed-off-by: Jovan Cvetkovic <jovanca.cvetkovic@gmail.com>

* Feature/update vertical domain #638

Signed-off-by: Jovan Cvetkovic <jovanca.cvetkovic@gmail.com>

* Feature/update vertical domain #638

Signed-off-by: Jovan Cvetkovic <jovanca.cvetkovic@gmail.com>

* testing github-action v5

Signed-off-by: Jovan Cvetkovic <jovanca.cvetkovic@gmail.com>

* Feature/update vertical domain #372

Signed-off-by: Jovan Cvetkovic <jovanca.cvetkovic@gmail.com>

* Unit tests for public components #383
[BUG] Detector Edit | Custom rule are not selected on update rules #406

Signed-off-by: Jovan Cvetkovic <jovanca.cvetkovic@gmail.com>

---------

Signed-off-by: Jovan Cvetkovic <jovanca.cvetkovic@gmail.com>
Co-authored-by: AWSHurneyt <hurneyt@amazon.com>
---
 package.json                                  |  7 ++--
 .../utils/__snapshots__/helper.test.ts.snap   |  6 +++
 public/pages/Overview/utils/helper.test.ts    |  8 ++--
 public/pages/Overview/utils/helpers.ts        | 41 +++++++++++++++----
 tsconfig.json                                 |  2 +-
 yarn.lock                                     | 12 ++++++
 6 files changed, 60 insertions(+), 16 deletions(-)

diff --git a/package.json b/package.json
index 348e93477..f5a4c536f 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,7 @@
   "resolutions": {
     "@types/react": "^16.9.8",
     "**/@types/angular": "1.6.50",
-    "**/@types/jest": "^24.0.9",
+    "**/@types/jest": "^29.3.1",
     "**/@types/react-dom": "^16.9.8",
     "eslint-utils": "^1.4.2",
     "path-parse": "^1.0.7",
@@ -55,14 +55,15 @@
     "@types/react-dom": "^16.9.8",
     "@types/react-router-dom": "^5.3.2",
     "cypress": "^6.0.0",
+    "cypress-real-events": "1.7.6",
+    "cypress-recurse": "^1.27.0",
     "eslint-plugin-no-unsanitized": "^3.0.2",
     "eslint-plugin-prefer-object-spread": "^1.2.1",
     "husky": "^3.0.0",
     "jest-cli": "^27.5.1",
     "jest-environment-jsdom": "^27.5.1",
     "lint-staged": "^10.2.0",
-    "ts-loader": "^6.2.1",
-    "cypress-real-events": "1.7.6"
+    "ts-loader": "^6.2.1"
   },
   "engines": {
     "yarn": "^1.21.1"
diff --git a/public/pages/Overview/utils/__snapshots__/helper.test.ts.snap b/public/pages/Overview/utils/__snapshots__/helper.test.ts.snap
index fbb4cf3fa..65b0e88f2 100644
--- a/public/pages/Overview/utils/__snapshots__/helper.test.ts.snap
+++ b/public/pages/Overview/utils/__snapshots__/helper.test.ts.snap
@@ -335,6 +335,12 @@ Object {
             "grid": true,
           },
           "field": "finding",
+          "scale": Object {
+            "domain": Array [
+              0,
+              0.1,
+            ],
+          },
           "title": "Count",
           "type": "quantitative",
         },
diff --git a/public/pages/Overview/utils/helper.test.ts b/public/pages/Overview/utils/helper.test.ts
index f5c7f413f..88ec7e93a 100644
--- a/public/pages/Overview/utils/helper.test.ts
+++ b/public/pages/Overview/utils/helper.test.ts
@@ -3,7 +3,6 @@ import {
   DateOpts,
   getAlertsVisualizationSpec,
   getChartTimeUnit,
-  getDomainRange,
   getFindingsVisualizationSpec,
   getOverviewVisualizationSpec,
   getTimeTooltip,
@@ -127,9 +126,10 @@ describe('helper utilities spec', () => {
 
   describe('tests parseDateString function', () => {
     it(' - function should return datetime in ms', () => {
-      const time = moment(10);
-      jest.spyOn(dateMath, 'parse').mockReturnValue(time);
-      expect(parseDateString(DEFAULT_DATE_RANGE.start)).toBe(time.milliseconds());
+      const mockTime = moment('2023-01-25T10:05:00');
+      jest.spyOn(dateMath, 'parse').mockReturnValue(mockTime);
+      jest.fn().mockImplementation('parseDateString', () => mockTime.milliseconds());
+      expect(parseDateString(DEFAULT_DATE_RANGE.start)).toBe(mockTime._d.getTime());
     });
   });
 
diff --git a/public/pages/Overview/utils/helpers.ts b/public/pages/Overview/utils/helpers.ts
index 497c8c1ff..b3307cfef 100644
--- a/public/pages/Overview/utils/helpers.ts
+++ b/public/pages/Overview/utils/helpers.ts
@@ -10,6 +10,7 @@ import dateMath from '@elastic/datemath';
 import _ from 'lodash';
 import { DEFAULT_DATE_RANGE } from '../../../utils/constants';
 import { severityOptions } from '../../Alerts/utils/constants';
+import moment from 'moment';
 
 export interface TimeUnit {
   unit: string;
@@ -67,13 +68,14 @@ export const defaultScaleDomain = [
   parseDateString(DEFAULT_DATE_RANGE.end),
 ];
 
-export const getYAxis = (field: string, title: string, axisGrid: boolean = true) => ({
-  aggregate: 'sum',
-  field: field,
-  type: 'quantitative',
-  title: title,
-  axis: { grid: axisGrid },
-});
+export const getYAxis = (field: string, title: string, axisGrid: boolean = true, opts: any = {}) =>
+  _.defaultsDeep(opts, {
+    aggregate: 'sum',
+    field: field,
+    type: 'quantitative',
+    title: title,
+    axis: { grid: axisGrid },
+  });
 
 export const getXAxis = (dateOpts: DateOpts, opts: any = {}) =>
   _.defaultsDeep(opts, {
@@ -121,6 +123,25 @@ export function getVisualizationSpec(description: string, data: any, layers: any
   };
 }
 
+/**
+ * Recalculates vertical domain range to add a bit of space
+ * so that the topmost items in the chart are not clipped
+ * @param {any} data
+ * @param {string} timeUnit
+ */
+export const getYDomainRange = (data: any[], timeUnit: string): number[] => {
+  data = data.filter((item) => item.finding === 1);
+
+  let dateFormat = 'mm';
+  const timeUnitSize = timeUnit.match(/.*(seconds|minutes|hours|date|month|year)$/);
+  if (timeUnitSize && timeUnitSize[1]) {
+    dateFormat = `${timeUnitSize[1][0]}${timeUnitSize[1][0]}`;
+  }
+  let dataGroups = _.groupBy(data, (item) => moment(item.time).format(dateFormat));
+  const domainMax = _.maxBy(Object.values(dataGroups), (group) => group.length) || [];
+  return [0, domainMax.length + 0.1];
+};
+
 export function getOverviewVisualizationSpec(
   visualizationData: SummaryData[],
   groupBy: string,
@@ -132,7 +153,11 @@ export function getOverviewVisualizationSpec(
 ): TopLevelSpec {
   const findingsEncoding: { [x: string]: any } = {
     x: getXAxis(dateOpts),
-    y: getYAxis('finding', 'Count'),
+    y: getYAxis('finding', 'Count', true, {
+      scale: {
+        domain: getYDomainRange(visualizationData, dateOpts.timeUnit.unit),
+      },
+    }),
     tooltip: [getYAxis('finding', 'Findings'), getTimeTooltip(dateOpts)],
     color: {
       field: 'fieldType',
diff --git a/tsconfig.json b/tsconfig.json
index 3115915e9..2bd480b10 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -9,6 +9,6 @@
     "skipLibCheck": true,
     "esModuleInterop": true,
     "outDir": "./target",
-    "types": ["cypress", "node", "cypress-real-events"]
+    "types": ["cypress", "node", "cypress-real-events", "jest"]
   }
 }
diff --git a/yarn.lock b/yarn.lock
index 3878fb46b..f1c965620 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2058,6 +2058,13 @@ cypress-real-events@1.7.6:
   resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.7.6.tgz#6f17e0b2ceea1d6dc60f6737d8f84cc517bbbb4c"
   integrity sha512-yP6GnRrbm6HK5q4DH6Nnupz37nOfZu/xn1xFYqsE2o4G73giPWQOdu6375QYpwfU1cvHNCgyD2bQ2hPH9D7NMw==
 
+cypress-recurse@^1.27.0:
+  version "1.27.0"
+  resolved "https://registry.yarnpkg.com/cypress-recurse/-/cypress-recurse-1.27.0.tgz#0c61e809c5f7740a7e907714614c49c72dcb5c1f"
+  integrity sha512-BCD83UqaxlD+JiqZn1PvIhHRXasgfCt57vLC1Fcyifvxh4QklELRcYUJV3MdhKamMkmajaErLfnCNbZ8VJ5SIg==
+  dependencies:
+    humanize-duration "^3.27.3"
+
 cypress@^6.0.0:
   version "6.9.1"
   resolved "https://registry.yarnpkg.com/cypress/-/cypress-6.9.1.tgz#ce1106bfdc47f8d76381dba63f943447883f864c"
@@ -3094,6 +3101,11 @@ human-signals@^2.1.0:
   resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
   integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
 
+humanize-duration@^3.27.3:
+  version "3.28.0"
+  resolved "https://registry.yarnpkg.com/humanize-duration/-/humanize-duration-3.28.0.tgz#f79770c0bec34d3bfd4899338cc40643bc04df72"
+  integrity sha512-jMAxraOOmHuPbffLVDKkEKi/NeG8dMqP8lGRd6Tbf7JgAeG33jjgPWDbXXU7ypCI0o+oNKJFgbSB9FKVdWNI2A==
+
 husky@^3.0.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/husky/-/husky-3.1.0.tgz#5faad520ab860582ed94f0c1a77f0f04c90b57c0"