Upgrade helix-front to Angular 9.1 (#2125)

Fix security vulnerabilities in helix-front dependencies.
Upgrade helix-front dependencies to improve contributor productivity.
diff --git a/helix-front/.nvmrc b/helix-front/.nvmrc
new file mode 100644
index 0000000..ceeec56
--- /dev/null
+++ b/helix-front/.nvmrc
@@ -0,0 +1 @@
+v10.13.0
diff --git a/helix-front/angular.json b/helix-front/angular.json
index 57f98cd..6700114 100644
--- a/helix-front/angular.json
+++ b/helix-front/angular.json
@@ -11,6 +11,7 @@
         "build": {
           "builder": "@angular-devkit/build-angular:browser",
           "options": {
+            "aot": true,
             "outputPath": "dist/public",
             "index": "client/index.html",
             "main": "client/main.ts",
@@ -28,6 +29,12 @@
           },
           "configurations": {
             "production": {
+              "budgets": [
+                {
+                  "type": "initial",
+                  "maximumWarning": "3503kb"
+                }
+              ],
               "optimization": true,
               "outputHashing": "all",
               "sourceMap": false,
diff --git a/helix-front/client/app/app-routing.module.ts b/helix-front/client/app/app-routing.module.ts
index 3e34485..7cb10c8 100644
--- a/helix-front/client/app/app-routing.module.ts
+++ b/helix-front/client/app/app-routing.module.ts
@@ -145,4 +145,4 @@
   }
 ];
 
-export const AppRoutingModule: ModuleWithProviders = RouterModule.forRoot(HELIX_ROUTES);
+export const AppRoutingModule: ModuleWithProviders<RouterModule> = RouterModule.forRoot(HELIX_ROUTES);
diff --git a/helix-front/client/app/app.component.spec.ts b/helix-front/client/app/app.component.spec.ts
index 2ac2d90..600dbb1 100644
--- a/helix-front/client/app/app.component.spec.ts
+++ b/helix-front/client/app/app.component.spec.ts
@@ -1,7 +1,8 @@
 import { TestBed, async } from '@angular/core/testing';
 import { NO_ERRORS_SCHEMA } from '@angular/core';
 
-import { Angulartics2, Angulartics2Piwik } from 'angulartics2';
+// import { Angulartics2 } from 'angulartics2';
+// import { Angulartics2Piwik } from 'angulartics2/piwik';
 
 import { TestingModule } from '../testing/testing.module';
 import { AppComponent } from './app.component';
@@ -10,7 +11,7 @@
   beforeEach(async(() => {
     TestBed.configureTestingModule({
       imports: [
-        TestingModule
+        TestingModule,
       ],
       declarations: [
         AppComponent
@@ -21,8 +22,8 @@
       ],
       // TODO vxu: I don't want to add the following two but ...
       providers: [
-        Angulartics2,
-        Angulartics2Piwik
+        // Angulartics2,
+        // Angulartics2Piwik,
       ]
     }).compileComponents();
   }));
diff --git a/helix-front/client/app/app.component.ts b/helix-front/client/app/app.component.ts
index 1b31f6c..a1403d7 100644
--- a/helix-front/client/app/app.component.ts
+++ b/helix-front/client/app/app.component.ts
@@ -7,9 +7,9 @@
   NavigationCancel,
   NavigationError
 } from '@angular/router';
-import { MatDialog } from '@angular/material';
+import { MatDialog } from '@angular/material/dialog';
 
-import { Angulartics2Piwik } from 'angulartics2';
+// import { Angulartics2Piwik } from 'angulartics2/piwik';
 
 import { UserService } from './core/user.service';
 import { InputDialogComponent } from './shared/dialog/input-dialog/input-dialog.component';
@@ -19,7 +19,7 @@
   selector: 'hi-root',
   templateUrl: './app.component.html',
   styleUrls: [ './app.component.scss' ],
-  providers: [ UserService ]
+  providers: [ UserService/*, Angulartics2Piwik */ ]
 })
 export class AppComponent implements OnInit {
 
@@ -29,9 +29,9 @@
   currentUser: any;
 
   constructor(
+    // protected angulartics2Piwik: Angulartics2Piwik,
     protected route: ActivatedRoute,
     protected router: Router,
-    protected angulartics: Angulartics2Piwik,
     protected dialog: MatDialog,
     protected service: UserService,
     protected helper: HelperService
@@ -50,6 +50,7 @@
         this.isLoading = false;
       }
     });
+    // angulartics2Piwik.startTracking();
   }
 
   ngOnInit() {
diff --git a/helix-front/client/app/app.module.ts b/helix-front/client/app/app.module.ts
index 1cedb00..085c39e 100644
--- a/helix-front/client/app/app.module.ts
+++ b/helix-front/client/app/app.module.ts
@@ -3,8 +3,8 @@
 import { FormsModule } from '@angular/forms';
 import { HttpClientModule } from '@angular/common/http';
 
-import { Angulartics2Module, Angulartics2Piwik } from 'angulartics2';
-
+// import { Angulartics2Module } from 'angulartics2';
+// import { Angulartics2Piwik } from 'angulartics2/piwik';
 import { AppRoutingModule } from './app-routing.module';
 import { CoreModule } from './core/core.module';
 import { SharedModule } from './shared/shared.module';
@@ -28,7 +28,7 @@
     FormsModule,
     HttpClientModule,
     AppRoutingModule,
-    Angulartics2Module.forRoot([ Angulartics2Piwik ]),
+    // Angulartics2Module.forRoot(),
     CoreModule,
     SharedModule,
     ClusterModule,
diff --git a/helix-front/client/app/cluster/cluster-detail/cluster-detail.component.ts b/helix-front/client/app/cluster/cluster-detail/cluster-detail.component.ts
index c7deca4..c7ccae8 100644
--- a/helix-front/client/app/cluster/cluster-detail/cluster-detail.component.ts
+++ b/helix-front/client/app/cluster/cluster-detail/cluster-detail.component.ts
@@ -2,7 +2,7 @@
 import {map} from 'rxjs/operators';
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
-import { MatDialog } from '@angular/material';
+import { MatDialog } from '@angular/material/dialog';
 
 import { Cluster } from '../shared/cluster.model';
 import { HelperService } from '../../shared/helper.service';
diff --git a/helix-front/client/app/cluster/cluster-list/cluster-list.component.ts b/helix-front/client/app/cluster/cluster-list/cluster-list.component.ts
index 2e1cb06..7144575 100644
--- a/helix-front/client/app/cluster/cluster-list/cluster-list.component.ts
+++ b/helix-front/client/app/cluster/cluster-list/cluster-list.component.ts
@@ -1,5 +1,6 @@
 import { Component, OnInit } from '@angular/core';
-import { MatDialog, MatSnackBar } from '@angular/material';
+import { MatDialog } from '@angular/material/dialog';
+import { MatSnackBar } from '@angular/material/snack-bar';
 import { Router } from '@angular/router';
 
 import { ClusterService } from '../shared/cluster.service';
diff --git a/helix-front/client/app/dashboard/dashboard.component.ts b/helix-front/client/app/dashboard/dashboard.component.ts
index 1e92246..9c45aa6 100644
--- a/helix-front/client/app/dashboard/dashboard.component.ts
+++ b/helix-front/client/app/dashboard/dashboard.component.ts
@@ -10,17 +10,11 @@
 import { map } from 'rxjs/operators';
 
 import * as _ from 'lodash';
-import { VisNode, VisNodes, VisEdges, VisNetworkService, VisNetworkData, VisNetworkOptions } from 'ngx-vis';
+import {Data, Edge, Node, Options, VisNetworkService } from 'ngx-vis';
 
 import { ResourceService } from '../resource/shared/resource.service';
 import { InstanceService } from '../instance/shared/instance.service';
 import { HelperService } from '../shared/helper.service';
-
-class DashboardNetworkData implements VisNetworkData {
-    public nodes: VisNodes;
-    public edges: VisEdges;
-}
-
 @Component({
   selector: 'hi-dashboard',
   templateUrl: './dashboard.component.html',
@@ -29,37 +23,40 @@
 })
 export class DashboardComponent implements OnInit, AfterViewInit, OnDestroy {
 
-  visNetwork = 'cluster-dashboard';
-  visNetworkData: DashboardNetworkData;
-  visNetworkOptions: VisNetworkOptions;
+  public visNetwork = 'cluster-dashboard';
+  public visNetworkData: Data;
+  public visNetworkOptions: Options;
+  public nodes: Node[];
+  public edges: Edge[]; 
 
-  clusterName: string;
-  isLoading = false;
-  resourceToId = {};
-  instanceToId = {};
-  selectedResource: any;
-  selectedInstance: any;
-  updateSubscription: Subscription;
-  updateInterval = 3000;
+  public clusterName: string;
+  public isLoading = false;
+  public resourceToId = {};
+  public instanceToId = {};
+  public selectedResource: any;
+  public selectedInstance: any;
+  public updateSubscription: Subscription;
+  public updateInterval = 3000;
 
-  constructor(
+  public constructor(
     private el:ElementRef,
     private route: ActivatedRoute,
-    protected visService: VisNetworkService,
+    protected visNetworkService: VisNetworkService,
     protected resourceService: ResourceService,
     protected instanceService: InstanceService,
     protected helper: HelperService
   ) { }
 
   networkInitialized() {
-    this.visService.on(this.visNetwork, 'click');
-    this.visService.on(this.visNetwork, 'zoom');
+    this.visNetworkService.on(this.visNetwork, 'click');
+    this.visNetworkService.on(this.visNetwork, 'zoom');
 
-    this.visService.click
+    this.visNetworkService.click
       .subscribe((eventData: any[]) => {
         if (eventData[0] === this.visNetwork) {
-          // clear the edges first
-          this.visNetworkData.edges.clear();
+
+          // clear the edges
+          this.visNetworkData.edges = []
           this.selectedResource = null;
           this.selectedInstance = null;
 
@@ -71,7 +68,7 @@
         }
       });
 
-    this.visService.zoom
+    this.visNetworkService.zoom
       .subscribe((eventData: any) => {
         if (eventData[0] === this.visNetwork) {
           const scale = eventData[1].scale;
@@ -85,10 +82,9 @@
   }
 
   ngOnInit() {
-    const nodes = new VisNodes();
-    const edges = new VisEdges();
-    this.visNetworkData = { nodes, edges };
-
+    this.nodes = 
+    this.edges = [];
+    this.visNetworkData = { nodes: this.nodes, edges: this.edges }
     this.visNetworkOptions = {
       interaction: {
         navigationButtons: true,
@@ -160,8 +156,8 @@
       this.updateSubscription.unsubscribe();
     }
 
-    this.visService.off(this.visNetwork, 'zoom');
-    this.visService.off(this.visNetwork, 'click');
+    this.visNetworkService.off(this.visNetwork, 'zoom');
+    this.visNetworkService.off(this.visNetwork, 'click');
   }
 
   protected fetchResources() {
@@ -172,17 +168,18 @@
       .subscribe(
         result => {
           _.forEach(result, (resource) => {
-            const newId = this.visNetworkData.nodes.getLength() + 1;
+            const lastId = this.nodes.length;
+            const newId = this.nodes.length + 1;
             this.resourceToId[resource.name] = newId;
-            this.visNetworkData.nodes.add({
+            (this.visNetworkData.nodes as Node[])[this.visNetworkData.nodes.length] = {
               id: newId,
               label: resource.name,
               group: 'resource',
               title: JSON.stringify(resource)
-            });
+            };
           });
 
-          this.visService.fit(this.visNetwork);
+          this.visNetworkService.fit(this.visNetwork);
         },
         error => this.helper.showError(error),
         () => this.isLoading = false
@@ -193,17 +190,17 @@
       .subscribe(
         result => {
           _.forEach(result, (instance) => {
-            const newId = this.visNetworkData.nodes.getLength() + 1;
+            const newId = this.visNetworkData.nodes.length + 1;
             this.instanceToId[instance.name] = newId;
-            this.visNetworkData.nodes.add({
+            (this.visNetworkData.nodes as Node[])[this.visNetworkData.nodes.length] = {
               id: newId,
               label: instance.name,
               group: instance.healthy ? 'instance' : 'instance_bad',
               title: JSON.stringify(instance),
-            });
+            };
           });
 
-          this.visService.fit(this.visNetwork);
+          this.visNetworkService.fit(this.visNetwork);
         },
         error => this.helper.showError(error),
         () => this.isLoading = false
@@ -218,12 +215,11 @@
     this.instanceService.getAll(this.clusterName)
       .subscribe(
         result => {
-          _.forEach(result, instance => {
-            const id = this.instanceToId[instance.name];
-            this.visNetworkData.nodes.update([{
-              id: id,
+          _.forEach(result, (instance, index, _collection) => {
+            (this.visNetworkData.nodes as Node[])[index] = {
+              id: this.instanceToId[instance.name],
               group: instance.healthy ? 'instance' : 'instance_bad'
-            }]);
+            };
           });
         }
       );
@@ -239,10 +235,10 @@
         .subscribe(
           resources => {
             _.forEach(resources, (resource) => {
-              this.visNetworkData.edges.add({
+              (this.visNetworkData.edges as Edge[])[this.visNetworkData.nodes.length] = {
                 from: id,
                 to: this.resourceToId[resource.name]
-              });
+              };
             });
           },
           error => this.helper.showError(error)
@@ -260,10 +256,10 @@
                 .unionBy('instanceName')
                 .map('instanceName')
                 .forEach((instanceName) => {
-                  this.visNetworkData.edges.add({
+                  (this.visNetworkData.edges as Edge[])[this.visNetworkData.nodes.length] = {
                     from: this.instanceToId[instanceName],
                     to: this.resourceToId[resourceName]
-                  });
+                  };
                 });
             },
             error => this.helper.showError(error)
diff --git a/helix-front/client/app/shared/data-table/data-table.component.ts b/helix-front/client/app/shared/data-table/data-table.component.ts
index dffae92..aa471d3 100644
--- a/helix-front/client/app/shared/data-table/data-table.component.ts
+++ b/helix-front/client/app/shared/data-table/data-table.component.ts
@@ -1,5 +1,5 @@
 import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
-import { MatDialog } from '@angular/material';
+import { MatDialog } from '@angular/material/dialog';
 
 import { Settings } from '../../core/settings';
 import { InputDialogComponent } from '../dialog/input-dialog/input-dialog.component';
diff --git a/helix-front/client/app/shared/dialog/alert-dialog/alert-dialog.component.ts b/helix-front/client/app/shared/dialog/alert-dialog/alert-dialog.component.ts
index d276e88..56cf480 100644
--- a/helix-front/client/app/shared/dialog/alert-dialog/alert-dialog.component.ts
+++ b/helix-front/client/app/shared/dialog/alert-dialog/alert-dialog.component.ts
@@ -1,5 +1,5 @@
 import { Component, OnInit, Inject } from '@angular/core';
-import { MAT_DIALOG_DATA } from '@angular/material';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
 
 @Component({
   selector: 'hi-alert-dialog',
diff --git a/helix-front/client/app/shared/dialog/confirm-dialog/confirm-dialog.component.ts b/helix-front/client/app/shared/dialog/confirm-dialog/confirm-dialog.component.ts
index 6f4ec8d..7d6f1d6 100644
--- a/helix-front/client/app/shared/dialog/confirm-dialog/confirm-dialog.component.ts
+++ b/helix-front/client/app/shared/dialog/confirm-dialog/confirm-dialog.component.ts
@@ -1,6 +1,6 @@
 import { Component, OnInit, Inject } from '@angular/core';
-import { MatDialogRef } from '@angular/material';
-import { MAT_DIALOG_DATA } from '@angular/material';
+import { MatDialogRef } from '@angular/material/dialog';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
 
 @Component({
   selector: 'hi-confirm-dialog',
diff --git a/helix-front/client/app/shared/dialog/input-dialog/input-dialog.component.ts b/helix-front/client/app/shared/dialog/input-dialog/input-dialog.component.ts
index d098008..64de0ab 100644
--- a/helix-front/client/app/shared/dialog/input-dialog/input-dialog.component.ts
+++ b/helix-front/client/app/shared/dialog/input-dialog/input-dialog.component.ts
@@ -1,6 +1,6 @@
 import { Component, OnInit, Inject } from '@angular/core';
-import { MatDialogRef } from '@angular/material';
-import { MAT_DIALOG_DATA } from '@angular/material';
+import { MatDialogRef } from '@angular/material/dialog';
+import { MAT_DIALOG_DATA } from '@angular/material/dialog';
 
 @Component({
   selector: 'hi-input-dialog',
diff --git a/helix-front/client/app/shared/helper.service.ts b/helix-front/client/app/shared/helper.service.ts
index 29c10ec..0eafd64 100644
--- a/helix-front/client/app/shared/helper.service.ts
+++ b/helix-front/client/app/shared/helper.service.ts
@@ -1,5 +1,6 @@
 import { Injectable } from '@angular/core';
-import { MatDialog, MatSnackBar } from '@angular/material';
+import { MatDialog } from '@angular/material/dialog';
+import { MatSnackBar } from '@angular/material/snack-bar';
 
 import { AlertDialogComponent } from './dialog/alert-dialog/alert-dialog.component';
 import { ConfirmDialogComponent } from './dialog/confirm-dialog/confirm-dialog.component';
diff --git a/helix-front/client/app/shared/material.module.ts b/helix-front/client/app/shared/material.module.ts
index cd630be..4800bd8 100644
--- a/helix-front/client/app/shared/material.module.ts
+++ b/helix-front/client/app/shared/material.module.ts
@@ -54,7 +54,6 @@
 import {
   MatExpansionModule
 } from '@angular/material/expansion';
-import 'hammerjs';
 
 @NgModule({
   imports: [
diff --git a/helix-front/client/app/shared/node-viewer/node-viewer.component.ts b/helix-front/client/app/shared/node-viewer/node-viewer.component.ts
index 3d78b2b..54f9b20 100644
--- a/helix-front/client/app/shared/node-viewer/node-viewer.component.ts
+++ b/helix-front/client/app/shared/node-viewer/node-viewer.component.ts
@@ -1,6 +1,6 @@
 import { Component, OnInit, Input, Output, ViewChild, ViewEncapsulation, EventEmitter } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
-import { MatDialog } from '@angular/material';
+import { MatDialog } from '@angular/material/dialog';
 
 import * as _ from 'lodash';
 
diff --git a/helix-front/client/tsconfig.app.json b/helix-front/client/tsconfig.app.json
index 5e2507d..5ea7521 100644
--- a/helix-front/client/tsconfig.app.json
+++ b/helix-front/client/tsconfig.app.json
@@ -2,12 +2,14 @@
   "extends": "../tsconfig.json",
   "compilerOptions": {
     "outDir": "../out-tsc/app",
-    "module": "es2015",
     "baseUrl": "",
     "types": []
   },
-  "exclude": [
-    "test.ts",
-    "**/*.spec.ts"
+  "files": [
+    "main.ts",
+    "polyfills.ts"
+  ],
+  "include": [
+    "client/**/*.d.ts"
   ]
 }
diff --git a/helix-front/client/tsconfig.spec.json b/helix-front/client/tsconfig.spec.json
index 15458ed..a6d21fd 100644
--- a/helix-front/client/tsconfig.spec.json
+++ b/helix-front/client/tsconfig.spec.json
@@ -2,7 +2,6 @@
   "extends": "../tsconfig.json",
   "compilerOptions": {
     "outDir": "../out-tsc/spec",
-    "module": "commonjs",
     "target": "es5",
     "baseUrl": "",
     "types": [
diff --git a/helix-front/package.json b/helix-front/package.json
index 9d14d33..eba5744 100644
--- a/helix-front/package.json
+++ b/helix-front/package.json
@@ -26,58 +26,64 @@
     "type:check:watch": "npm run type:check -- --watch"
   },
   "engines": {
-    "node": "10.9.0",
-    "npm": "6.2.0"
+    "node": "10.13.0",
+    "npm": "6.4.1"
   },
   "dependencies": {
-    "@angular/animations": "8.2.14",
-    "@angular/cdk": "8.2.3",
-    "@angular/common": "8.2.14",
-    "@angular/compiler": "8.2.14",
-    "@angular/core": "8.2.14",
-    "@angular/flex-layout": "^8.0.0-beta.27",
-    "@angular/forms": "8.2.14",
-    "@angular/material": "^8.2.3",
-    "@angular/platform-browser": "8.2.14",
-    "@angular/platform-browser-dynamic": "8.2.14",
-    "@angular/platform-server": "8.2.14",
-    "@angular/router": "8.2.14",
+    "@angular/animations": "9.1.13",
+    "@angular/cdk": "9.2.4",
+    "@angular/common": "9.1.13",
+    "@angular/compiler": "9.1.13",
+    "@angular/core": "9.1.13",
+    "@angular/flex-layout": "^9.0.0-beta.31",
+    "@angular/forms": "9.1.13",
+    "@angular/material": "^9.2.4",
+    "@angular/platform-browser": "9.1.13",
+    "@angular/platform-browser-dynamic": "9.1.13",
+    "@angular/platform-server": "9.1.13",
+    "@angular/router": "9.1.13",
+    "@egjs/hammerjs": "^2.0.17",
     "@swimlane/ngx-charts": "^13.0.4",
-    "@swimlane/ngx-datatable": "^16.1.1",
+    "@swimlane/ngx-datatable": "^17.1.0",
     "@swimlane/ngx-graph": "^7.0.0",
     "ajv": "^6.9.1",
-    "angulartics2": "^2.2.1",
+    "angulartics2": "^9.1.0",
     "body-parser": "^1.17.2",
+    "component-emitter": "^1.3.0",
     "core-js": "^2.4.1",
     "d3-shape": "^1.2.0",
     "dotenv": "^4.0.0",
     "express": "^4.15.3",
     "express-session": "^1.15.6",
-    "hammerjs": "^2.0.8",
+    "keycharm": "^0.3.0",
     "ldapjs": "^1.0.2",
     "lodash": "^4.17.12",
     "moment": "^2.29.2",
     "morgan": "^1.8.2",
     "ngx-clipboard": "^12.1.2",
     "ngx-json-viewer": "^2.3.0",
-    "ngx-vis": "^0.1.0",
+    "ngx-vis": "^3.1.0",
     "request": "^2.81.0",
-    "rxjs": "^6.6.7",
+    "rxjs": "^6.5.5",
     "rxjs-compat": "^6.0.0-rc.0",
     "sass": "^1.51.0",
-    "tsickle": "^0.37.0",
-    "tslib": "^1.9.0",
+    "tsickle": "^0.38.1",
+    "tslib": "^1.10.0",
+    "uuid": "^7.0.3",
     "vis": "^4.21.0",
-    "zone.js": "^0.9.1"
+    "vis-data": "^6.5.1",
+    "vis-util": "^4.0.0",
+    "xss": "^1.0.11",
+    "zone.js": "~0.10.2"
   },
   "devDependencies": {
-    "@angular-devkit/build-angular": "^0.803.29",
-    "@angular/cli": "8.3.29",
-    "@angular/compiler-cli": "8.2.14",
+    "@angular-devkit/build-angular": "~0.901.15",
+    "@angular/cli": "9.1.15",
+    "@angular/compiler-cli": "9.1.13",
     "@types/hammerjs": "^2.0.34",
     "@types/jasmine": "2.5.38",
     "@types/lodash": "4.14.120",
-    "@types/node": "~6.0.60",
+    "@types/node": "^12.11.1",
     "@types/request": "^2.0.0",
     "codelyzer": "^6.0.1",
     "concurrently": "^3.5.0",
@@ -95,6 +101,6 @@
     "rxjs-tslint": "^0.1.8",
     "ts-node": "~2.0.0",
     "tslint": "^5.8.0",
-    "typescript": "3.5.3"
+    "typescript": "3.8.3"
   }
 }