METRON-988 UI for viewing alerts generated by Metron  (iraghumitra via 
nickwallen) closes apache/metron#620


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/7d554444
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/7d554444
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/7d554444

Branch: refs/heads/master
Commit: 7d554444e7701eb4e8787d3c395f65b94db762c4
Parents: de154b0
Author: iraghumitra <raghumitra....@gmail.com>
Authored: Thu Aug 10 10:07:58 2017 -0400
Committer: nickallen <nickal...@apache.org>
Committed: Thu Aug 10 10:07:58 2017 -0400

----------------------------------------------------------------------
 metron-interface/metron-alerts/.gitignore       |    5 +
 metron-interface/metron-alerts/NOTICE           |   34 +
 metron-interface/metron-alerts/README.md        |   80 +
 .../metron-alerts/alerts-server-e2e.js          |  142 +
 metron-interface/metron-alerts/angular-cli.json |   63 +
 metron-interface/metron-alerts/assembly.xml     |   55 +
 .../e2e/alerts-list/alerts-list.e2e-spec.ts     |  136 +
 .../e2e/alerts-list/alerts-list.po.ts           |  244 +
 .../configure-table/configure-table.e2e-spec.ts |   48 +
 .../save-search/save-search.e2e-spec.ts         |   82 +
 .../e2e/matchers/custom-matchers.d.ts           |   23 +
 .../e2e/matchers/custom-matchers.ts             |   36 +
 .../metron-alerts/e2e/mock-data/alert-list.json | 8496 ++++++++++++++++
 .../e2e/mock-data/cluster-state.json            | 9261 ++++++++++++++++++
 .../metron-alerts/e2e/tsconfig.json             |   16 +
 metron-interface/metron-alerts/karma.conf.js    |   61 +
 .../metron-alerts/package-lock.json             | 6427 ++++++++++++
 metron-interface/metron-alerts/package.json     |   60 +
 metron-interface/metron-alerts/pom.xml          |  133 +
 .../metron-alerts/protractor.conf.js            |   61 +
 metron-interface/metron-alerts/proxy.conf.json  |   19 +
 .../metron-alerts/scripts/alerts-server.js      |   90 +
 .../metron-alerts/scripts/package.json          |   21 +
 .../scripts/prepend_license_header.sh           |   42 +
 .../metron-alerts/scripts/start-dev.sh          |   19 +
 .../scripts/start-server-for-e2e.sh             |   20 +
 .../metron-alerts/scripts/start_alerts_ui.sh    |   24 +
 .../metron-alerts/src/_variables.scss           |   99 +
 .../alert-details/alert-details.component.html  |   47 +
 .../alert-details/alert-details.component.scss  |   72 +
 .../alert-details/alert-details.component.ts    |  108 +
 .../alert-details/alerts-details.module.ts      |   29 +
 .../alert-details/alerts-details.routing.ts     |   24 +
 .../alerts-list/alerts-list.component.html      |  100 +
 .../alerts-list/alerts-list.component.scss      |  311 +
 .../alerts/alerts-list/alerts-list.component.ts |  399 +
 .../alerts/alerts-list/alerts-list.module.ts    |   38 +
 .../alerts/alerts-list/alerts-list.routing.ts   |   27 +
 .../src/app/alerts/alerts-list/query-builder.ts |  139 +
 .../configure-rows/configure-rows-enums.ts      |   37 +
 .../configure-rows.component.html               |   45 +
 .../configure-rows.component.scss               |   85 +
 .../configure-rows.component.spec.ts            |   39 +
 .../configure-rows/configure-rows.component.ts  |  112 +
 .../configure-rows/configure-rows.module.ts     |   28 +
 .../configure-table.component.html              |   72 +
 .../configure-table.component.scss              |   55 +
 .../configure-table.component.ts                |  174 +
 .../configure-table/configure-table.module.ts   |   30 +
 .../configure-table/configure-table.routing.ts  |   24 +
 .../save-search/save-search.component.html      |   34 +
 .../save-search/save-search.component.scss      |   22 +
 .../save-search/save-search.component.spec.ts   |   38 +
 .../alerts/save-search/save-search.component.ts |   77 +
 .../alerts/save-search/save-search.module.ts    |   29 +
 .../alerts/save-search/save-search.routing.ts   |   24 +
 .../saved-searches.component.html               |   30 +
 .../saved-searches.component.scss               |   22 +
 .../saved-searches.component.spec.ts            |   38 +
 .../saved-searches/saved-searches.component.ts  |  130 +
 .../saved-searches/saved-searches.module.ts     |   30 +
 .../saved-searches/saved-searches.routing.ts    |   24 +
 .../metron-alerts/src/app/app-routing.module.ts |   33 +
 .../metron-alerts/src/app/app.component.html    |   28 +
 .../metron-alerts/src/app/app.component.scss    |   25 +
 .../metron-alerts/src/app/app.component.spec.ts |   31 +
 .../metron-alerts/src/app/app.component.ts      |   26 +
 .../src/app/app.config.interface.ts             |   20 +
 .../metron-alerts/src/app/app.config.ts         |   17 +
 .../metron-alerts/src/app/app.module.ts         |   76 +
 metron-interface/metron-alerts/src/app/index.ts |   20 +
 .../metron-alerts/src/app/model/alert.ts        |   61 +
 .../src/app/model/alerts-search-response.ts     |   23 +
 .../src/app/model/column-metadata.ts            |   34 +
 .../metron-alerts/src/app/model/column-names.ts |   26 +
 .../metron-alerts/src/app/model/filter.ts       |   26 +
 .../metron-alerts/src/app/model/pagination.ts   |   22 +
 .../metron-alerts/src/app/model/rest-error.ts   |   22 +
 .../metron-alerts/src/app/model/save-search.ts  |   48 +
 .../src/app/model/search-request.ts             |   24 +
 .../src/app/model/table-metadata.ts             |   40 +
 .../src/app/service/alert.service.ts            |   69 +
 .../src/app/service/cluster-metadata.service.ts |   38 +
 .../src/app/service/column-names.service.ts     |   71 +
 .../src/app/service/configure-table.service.ts  |   50 +
 .../src/app/service/data-source.ts              |   62 +
 .../service/elasticsearch-localstorage-impl.ts  |  294 +
 .../src/app/service/save-search.service.ts      |   75 +
 .../src/app/service/workflow.service.ts         |   36 +
 .../shared/collapse/collapse-component-data.ts  |   32 +
 .../app/shared/collapse/collapse.component.html |   31 +
 .../app/shared/collapse/collapse.component.scss |   92 +
 .../shared/collapse/collapse.component.spec.ts  |   39 +
 .../app/shared/collapse/collapse.component.ts   |   58 +
 .../src/app/shared/collapse/collapse.module.ts  |   30 +
 .../directives/alert-search.directive.spec.ts   |   21 +
 .../shared/directives/alert-search.directive.ts |  208 +
 .../directives/alert-severity.directive.ts      |   53 +
 .../directives/nav-content.directive.spec.ts    |   25 +
 .../shared/directives/nav-content.directive.ts  |   35 +
 .../metron-alerts/src/app/shared/index.ts       |    0
 .../shared/list-group/list-group.component.html |   19 +
 .../shared/list-group/list-group.component.scss |   29 +
 .../list-group/list-group.component.spec.ts     |   42 +
 .../shared/list-group/list-group.component.ts   |   47 +
 .../app/shared/list-group/list-grup.module.ts   |   30 +
 .../src/app/shared/metron-dialog-box.ts         |   91 +
 .../shared/metron-table/metron-sorter/index.ts  |   18 +
 .../metron-sorter/metron-sorter.component.html  |   21 +
 .../metron-sorter/metron-sorter.component.scss  |   17 +
 .../metron-sorter.component.spec.ts             |   92 +
 .../metron-sorter/metron-sorter.component.ts    |   46 +
 .../metron-sorter/metron-sorter.module.ts       |   30 +
 .../metron-table-pagination.component.html      |   20 +
 .../metron-table-pagination.component.scss      |   30 +
 .../metron-table-pagination.component.spec.ts   |   42 +
 .../metron-table-pagination.component.ts        |   49 +
 .../metron-table-pagination.module.ts           |   30 +
 .../metron-table/metron-table.directive.ts      |  120 +
 .../shared/pipes/center-ellipses.pipe.spec.ts   |   25 +
 .../app/shared/pipes/center-ellipses.pipe.ts    |   45 +
 .../pipes/column-name-translate.pipe.spec.ts    |   25 +
 .../shared/pipes/column-name-translate.pipe.ts  |   36 +
 .../src/app/shared/shared.module.ts             |   51 +
 .../src/app/shared/switch/switch.component.html |   20 +
 .../src/app/shared/switch/switch.component.scss |   91 +
 .../app/shared/switch/switch.component.spec.ts  |   42 +
 .../src/app/shared/switch/switch.component.ts   |   33 +
 .../src/app/shared/switch/switch.module.ts      |   30 +
 .../metron-alerts/src/app/utils/constants.ts    |   22 +
 .../src/app/utils/elasticsearch-utils.ts        |   74 +
 .../metron-alerts/src/app/utils/enums.ts        |   21 +
 .../metron-alerts/src/app/utils/httpUtil.ts     |   45 +
 .../metron-alerts/src/assets/.gitkeep           |    0
 .../metron-alerts/src/assets/.npmignore         |    0
 .../metron-alerts/src/assets/ace/LICENSE        |   24 +
 .../metron-alerts/src/assets/ace/mode-lucene.js |   69 +
 .../src/assets/ace/theme-monokai.js             |  105 +
 .../src/assets/fonts/Roboto/LICENSE.txt         |  202 +
 .../src/assets/fonts/Roboto/Roboto-Black.ttf    |  Bin 0 -> 163488 bytes
 .../assets/fonts/Roboto/Roboto-BlackItalic.ttf  |  Bin 0 -> 165444 bytes
 .../src/assets/fonts/Roboto/Roboto-Bold.ttf     |  Bin 0 -> 162464 bytes
 .../assets/fonts/Roboto/Roboto-BoldItalic.ttf   |  Bin 0 -> 163644 bytes
 .../src/assets/fonts/Roboto/Roboto-Italic.ttf   |  Bin 0 -> 161484 bytes
 .../src/assets/fonts/Roboto/Roboto-Light.ttf    |  Bin 0 -> 162420 bytes
 .../assets/fonts/Roboto/Roboto-LightItalic.ttf  |  Bin 0 -> 166492 bytes
 .../src/assets/fonts/Roboto/Roboto-Medium.ttf   |  Bin 0 -> 162588 bytes
 .../assets/fonts/Roboto/Roboto-MediumItalic.ttf |  Bin 0 -> 165636 bytes
 .../src/assets/fonts/Roboto/Roboto-Regular.ttf  |  Bin 0 -> 162876 bytes
 .../src/assets/fonts/Roboto/Roboto-Thin.ttf     |  Bin 0 -> 163132 bytes
 .../assets/fonts/Roboto/Roboto-ThinItalic.ttf   |  Bin 0 -> 168276 bytes
 .../metron-alerts/src/assets/fonts/font.css     |  101 +
 .../metron-alerts/src/assets/images/login.jpg   |  Bin 0 -> 335521 bytes
 .../metron-alerts/src/assets/images/logo.png    |  Bin 0 -> 21186 bytes
 .../src/environments/environment.js             |   25 +
 .../src/environments/environment.prod.js        |   21 +
 .../src/environments/environment.prod.ts        |   21 +
 .../src/environments/environment.ts             |   25 +
 metron-interface/metron-alerts/src/favicon.ico  |  Bin 0 -> 1150 bytes
 metron-interface/metron-alerts/src/font.css     |  101 +
 metron-interface/metron-alerts/src/index.html   |   28 +
 metron-interface/metron-alerts/src/main.ts      |   28 +
 .../metron-alerts/src/metron-dialog.scss        |   56 +
 metron-interface/metron-alerts/src/polyfills.ts |   68 +
 metron-interface/metron-alerts/src/slider.scss  |  139 +
 metron-interface/metron-alerts/src/styles.scss  |  244 +
 metron-interface/metron-alerts/src/test.ts      |   49 +
 .../metron-alerts/src/tsconfig.app.json         |   16 +
 .../metron-alerts/src/tsconfig.spec.json        |   20 +
 metron-interface/metron-alerts/src/typings.d.ts |   22 +
 metron-interface/metron-alerts/src/vendor.scss  |   71 +
 metron-interface/metron-alerts/tsconfig.json    |   20 +
 metron-interface/metron-alerts/tslint.json      |  103 +
 metron-interface/pom.xml                        |    1 +
 174 files changed, 33103 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/7d554444/metron-interface/metron-alerts/.gitignore
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/.gitignore 
b/metron-interface/metron-alerts/.gitignore
new file mode 100644
index 0000000..0667762
--- /dev/null
+++ b/metron-interface/metron-alerts/.gitignore
@@ -0,0 +1,5 @@
+# Created by .ignore support plugin (hsz.mobi)
+.idea/
+metron-alerts.iml
+node_modules/
+/dist/

http://git-wip-us.apache.org/repos/asf/metron/blob/7d554444/metron-interface/metron-alerts/NOTICE
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/NOTICE 
b/metron-interface/metron-alerts/NOTICE
new file mode 100644
index 0000000..68fa7b1
--- /dev/null
+++ b/metron-interface/metron-alerts/NOTICE
@@ -0,0 +1,34 @@
+metron-alerts
+Copyright 2006-2017 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+This product includes Font Awesome 4.7.0 
(http://fortawesome.github.com/Font-Awesome - SIL OFL 1.1)
+Copyright (c) 2013 Dave Gandy
+
+This product includes Angular Common/Compiler/Core/Forms/Http/Router 4.3.2 
(https://github.com/angular/angular - MIT)
+Copyright (c) 2014-2017 Google, Inc. http://angular.io
+
+This product includes Ace Builds 1.2.8 (https://github.com/ajaxorg/ace-builds 
- BSD)
+Copyright (c) 2010, Ajax.org B.V.
+
+This product includes Bootstrap 4.0.0 (https://github.com/twbs/bootstrap - MIT)
+Copyright (c) 2011-2017 Twitter, Inc.
+Copyright (c) 2011-2017 The Bootstrap Authors
+
+This product includes core-js 2.4.1 (https://github.com/zloirock/core-js - MIT)
+Copyright (c) 2014-2016 Denis Pushkarev
+
+This product includes jquery 3.2.1 (https://github.com/jquery/jquery - MIT)
+Copyright JS Foundation and other contributors, https://js.foundation/
+
+This product includes symbol-observable 1.0.4 
(https://github.com/blesh/symbol-observable - MIT)
+Copyright (c) Sindre Sorhus <sindresor...@gmail.com> (sindresorhus.com)
+Copyright (c) Ben Lesh <b...@benlesh.com>
+
+This product includes tether 1.4.0 (https://github.com/HubSpot/tether - MIT)
+Copyright (c) 2014-2017 HubSpot, Inc.
+
+This product includes zone.js 0.8.16 
(http://fortawesome.github.com/Font-Awesome - SIL OFL 1.1)
+Copyright (c) 2016 Google, Inc.

http://git-wip-us.apache.org/repos/asf/metron/blob/7d554444/metron-interface/metron-alerts/README.md
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/README.md 
b/metron-interface/metron-alerts/README.md
new file mode 100644
index 0000000..adba151
--- /dev/null
+++ b/metron-interface/metron-alerts/README.md
@@ -0,0 +1,80 @@
+- [Caveats](#caveats)
+- [Prerequisites](#prerequisites)
+- [Development Setup](#development-setup)
+- [E2E Tests](#e2e-tests)
+- [Mpack Integration](#mpack-integration)
+- [Installing on an existing Cluster](#installing-on-an-existing-cluster)
+
+## Caveats
+* UI doesn't have an authentication module yet
+* UI uses local storage to save all the data.  A middleware needs to be 
designed and developed for persisting the data
+
+## Prerequisites
+* Elastic search should be up and running and should have some alerts 
populated by metron topologies
+* The alerts can be populated using Quick Dev, Full Dev  or any other setup
+* UI is developed using angular4 and uses angular-cli
+* node.JS >= 7.8.0
+
+## Development Setup
+
+Install all the dependent node_modules using the following command
+```
+cd metron/metron-interface/metron-alerts
+npm install
+```
+UI can be run by using the following command
+```
+./scripts/start-dev.sh
+```
+**NOTE**: *In the development mode ui by default connects to ES at 
http://node1:9200 for fetching data. If you wish to change it you can change 
the ES url at metron/metron-interface/metron-alerts/proxy.conf.json*
+
+## E2E Tests
+
+An expressjs server is available for mocking the elastic search api.
+
+1. Run e2e webserver :
+    ```
+    cd metron/metron-interface/metron-alerts
+    sh ./scripts/start-server-for-e2e.sh
+    ```
+
+1. run e2e test using the following command
+    ```
+    cd metron/metron-interface/metron-alerts
+    npm run e2e
+    ```
+
+**NOTE**: *e2e tests covers all the general workflows and we will extend them 
as we need*
+
+## Mpack Integration
+Yet to come
+
+## Installing on an existing Cluster
+1. Build Metron:
+    ```
+    mvn clean package -DskipTests
+    ```
+
+1. Copy 
`metron/metron-interface/metron-alerts/target/metron-alerts-METRON_VERSION-archive.tar.gz`
 to the desired host.
+
+1. Untar the archive in the target directory.  The directory structure will 
look like:
+    ```
+    bin
+      start_alerts_ui.sh
+    web
+      alerts-ui
+        package.json
+        server.js
+        web assets (html, css, js, ...)
+    ```
+
+1. [Expressjs](https://github.com/expressjs/express) webserver script is 
included in the build that will serve the application. (The script has few 
rewrite rules and we can replace expressjs with any other webserver)
+
+1. Then start the application with the script:
+    ```
+    ./bin/start_alerts_ui.sh
+    Usage: server.js -p [port] -r [restUrl]
+    Options:
+      -p             Port to run metron alerts ui                [required]
+      -r, --resturl  Url where elastic search rest api is available  [required]
+    ```

http://git-wip-us.apache.org/repos/asf/metron/blob/7d554444/metron-interface/metron-alerts/alerts-server-e2e.js
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/alerts-server-e2e.js 
b/metron-interface/metron-alerts/alerts-server-e2e.js
new file mode 100644
index 0000000..e30ef44
--- /dev/null
+++ b/metron-interface/metron-alerts/alerts-server-e2e.js
@@ -0,0 +1,142 @@
+#!/usr/bin/env node
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+var os          = require('os');
+var jsonfile    = require('jsonfile');
+var bodyParser  = require('body-parser');
+var app         = require('express')();
+var path        = require('path');
+var compression = require('compression');
+var serveStatic = require('serve-static');
+var favicon     = require('serve-favicon');
+var proxy       = require('http-proxy-middleware');
+var argv        = require('optimist')
+                  .demand(['p'])
+                  .usage('Usage: server.js -p [port]')
+                  .describe('p', 'Port to run metron alerts ui')
+                  .argv;
+
+var port = argv.p;
+var metronUIAddress = '';
+var ifaces = os.networkInterfaces();
+var restUrl =  argv.r || argv.resturl;
+var conf = {
+  "elastic": {
+    "target": restUrl,
+    "secure": false
+  }
+};
+
+Object.keys(ifaces).forEach(function (dev) {
+  ifaces[dev].forEach(function (details) {
+    if (details.family === 'IPv4') {
+      metronUIAddress += '\n';
+      metronUIAddress += 'http://' + details.address + ':' + port;
+    }
+  });
+});
+
+function setCustomCacheControl (res, path) {
+  if (serveStatic.mime.lookup(path) === 'text/html') {
+    res.setHeader('Cache-Control', 'public, max-age=10')
+  }
+  res.setHeader("Expires", new Date(Date.now() + 2592000000).toUTCString());
+}
+
+var indexHTML = function(req, res){
+  res.sendFile(path.resolve('dist/index.html'));
+};
+
+var searchResult = function(req, res){
+  console.log('Serving ', req.originalUrl ,'from alert-list.json');
+  jsonfile.readFile('e2e/mock-data/alert-list.json', function(err, obj) {
+    if(err) {
+      res.json({status: 'error', reason: err.toString()});
+      return;
+    }
+
+    var filter = req.body.query.query_string.query;
+    if (filter !== '*') {
+      filter = filter.replace(/\\/g, '');
+      var lastIndex = filter.lastIndexOf(':');
+      var key = filter.substr(0, lastIndex);
+      var value = filter.substr(lastIndex+1);
+      obj.hits.hits =  obj.hits.hits.filter(function (hits) {
+        return hits._source[key] === value;
+      });
+    }
+
+    var sortField = req.body.sort && req.body.sort.length === 1 && 
req.body.sort[0];
+    if (sortField) {
+      var key = Object.keys(sortField)[0];
+      var order = sortField[key].order;
+      obj.hits.hits = obj.hits.hits.sort(function(o1, o2) {
+        if (!o1._source[key] || !o2._source[key]) {
+          return -1;
+        } 
+
+        if (typeof(o1._source[key]) === 'number' && typeof(o2._source[key]) 
=== 'number') {
+          return order === 'desc' ? o2._source[key]- (o1._source[key]) : 
o1._source[key] - (o2._source[key]);
+        } else {
+          return order === 'desc' ? 
o2._source[key].localeCompare(o1._source[key]) : 
o1._source[key].localeCompare(o2._source[key]);
+        }
+
+      });
+    }
+
+    obj.hits.total = obj.hits.hits.length;
+    obj.hits.hits = obj.hits.hits.splice(req.body.from, req.body.size);
+    res.json(obj);
+  });
+};
+
+var clusterState = function(req, res){
+  console.log('Serving ', req.originalUrl ,'from cluster-state.json');
+  jsonfile.readFile('e2e/mock-data/cluster-state.json', function(err, obj) {
+    if(err) {
+      res.json({status: 'error', reason: err.toString()});
+      return;
+    }
+    res.json(obj);
+  });
+};
+
+
+app.use(compression());
+app.use(bodyParser.json());
+app.use(favicon(path.join(__dirname, 'dist/favicon.ico')));
+app.use(serveStatic(path.join(__dirname, 'dist'), {
+  maxAge: '1d',
+  setHeaders: setCustomCacheControl
+}));
+
+app.post('^/search/*', searchResult);
+app.use('/_cluster', clusterState);
+app.get('/alerts-list', indexHTML);
+app.get('', indexHTML);
+app.use(function(req, res, next){
+  res.status(404).sendStatus(304);
+});
+
+
+app.listen(port, function(){
+  console.log("Metron alerts ui is listening on " + metronUIAddress);
+});

http://git-wip-us.apache.org/repos/asf/metron/blob/7d554444/metron-interface/metron-alerts/angular-cli.json
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/angular-cli.json 
b/metron-interface/metron-alerts/angular-cli.json
new file mode 100644
index 0000000..68922d7
--- /dev/null
+++ b/metron-interface/metron-alerts/angular-cli.json
@@ -0,0 +1,63 @@
+{
+  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+  "project": {
+    "name": "metron-alerts"
+  },
+  "apps": [
+    {
+      "root": "src",
+      "outDir": "dist",
+      "assets": [
+        "assets",
+        "favicon.ico"
+      ],
+      "index": "index.html",
+      "main": "main.ts",
+      "polyfills": "polyfills.ts",
+      "test": "test.ts",
+      "tsconfig": "tsconfig.app.json",
+      "testTsconfig": "tsconfig.spec.json",
+      "prefix": "app",
+      "styles": [
+        "../node_modules/font-awesome/css/font-awesome.css",
+        "vendor.scss",
+        "styles.scss"
+      ],
+      "scripts": [
+        "../node_modules/jquery/dist/jquery.js",
+        "../node_modules/tether/dist/js/tether.js",
+        "../node_modules/ace-builds/src-noconflict/ace.js"
+      ],
+      "environmentSource": "environments/environment.ts",
+      "environments": {
+        "dev": "environments/environment.ts",
+        "prod": "environments/environment.prod.ts"
+      }
+    }
+  ],
+  "e2e": {
+    "protractor": {
+      "config": "./protractor.conf.js"
+    }
+  },
+  "lint": [
+    {
+      "project": "src/tsconfig.app.json"
+    },
+    {
+      "project": "src/tsconfig.spec.json"
+    },
+    {
+      "project": "e2e/tsconfig.e2e.json"
+    }
+  ],
+  "test": {
+    "karma": {
+      "config": "./karma.conf.js"
+    }
+  },
+  "defaults": {
+    "styleExt": "scss",
+    "component": {}
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/7d554444/metron-interface/metron-alerts/assembly.xml
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/assembly.xml 
b/metron-interface/metron-alerts/assembly.xml
new file mode 100644
index 0000000..63e7380
--- /dev/null
+++ b/metron-interface/metron-alerts/assembly.xml
@@ -0,0 +1,55 @@
+<!--
+  Licensed to the Apache Software
+       Foundation (ASF) under one or more contributor license agreements. See 
the
+       NOTICE file distributed with this work for additional information 
regarding
+       copyright ownership. The ASF licenses this file to You under the Apache 
License,
+       Version 2.0 (the "License"); you may not use this file except in 
compliance
+       with the License. You may obtain a copy of the License at 
http://www.apache.org/licenses/LICENSE-2.0
+       Unless required by applicable law or agreed to in writing, software 
distributed
+       under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
+       OR CONDITIONS OF ANY KIND, either express or implied. See the License 
for
+  the specific language governing permissions and limitations under the 
License.
+  -->
+
+<assembly>
+  <id>archive</id>
+  <formats>
+    <format>tar.gz</format>
+  </formats>
+  <includeBaseDirectory>false</includeBaseDirectory>
+  <fileSets>
+    <fileSet>
+      <directory>${project.basedir}/dist</directory>
+      <outputDirectory>/web/alerts-ui</outputDirectory>
+      <excludes>
+      </excludes>
+      <fileMode>0644</fileMode>
+    </fileSet>
+    <fileSet>
+      <directory>${project.basedir}/scripts</directory>
+      <outputDirectory>/web/alerts-ui</outputDirectory>
+      <includes>
+        <include>package.json</include>
+        <include>alerts-server.js</include>
+      </includes>
+      <fileMode>0644</fileMode>
+    </fileSet>
+    <fileSet>
+      <directory>${project.basedir}/scripts</directory>
+      <outputDirectory>/bin</outputDirectory>
+      <useDefaultExcludes>true</useDefaultExcludes>
+      <includes>
+        <include>start_alerts_ui.sh</include>
+      </includes>
+      <fileMode>0755</fileMode>
+      <lineEnding>unix</lineEnding>
+      <filtered>true</filtered>
+    </fileSet>
+  </fileSets>
+  <files>
+    <file>
+      <source>src/favicon.ico</source>
+      <outputDirectory>/web/alerts-ui</outputDirectory>
+    </file>
+  </files>
+</assembly>

http://git-wip-us.apache.org/repos/asf/metron/blob/7d554444/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts
----------------------------------------------------------------------
diff --git 
a/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts 
b/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts
new file mode 100644
index 0000000..454ee6e
--- /dev/null
+++ b/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts
@@ -0,0 +1,136 @@
+/// <reference path="../matchers/custom-matchers.d.ts"/>
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { MetronAlertsPage } from './alerts-list.po';
+import { customMatchers } from  '../matchers/custom-matchers';
+
+describe('metron-alerts App', function() {
+  let page: MetronAlertsPage;
+  let columnNames = [ 'Score', '_id', 'timestamp', 'source:type', 
'ip_src_addr', 'enrichm...:country',
+                      'ip_dst_addr', 'host', 'alert_status', '', '' ];
+  let colNamesColumnConfig = [ 'score', '_id', 'timestamp', 'source:type', 
'ip_src_addr', 'enrichments:geo:ip_dst_addr:country',
+                                'ip_dst_addr', 'host', 'alert_status' ];
+
+  beforeEach(() => {
+    page = new MetronAlertsPage();
+    jasmine.addMatchers(customMatchers);
+  });
+
+  it('should have all the UI elements', () => {
+    page.navigateTo();
+    page.clearLocalStorage();
+
+    expect(page.isMetronLogoPresent()).toEqualBcoz(true, 'for Metron Logo');
+    expect(page.isSavedSearchButtonPresent()).toEqualBcoz(true, 'for 
SavedSearch Button');
+    expect(page.isClearSearchPresent()).toEqualBcoz(true, 'for Clear Search');
+    expect(page.isSearchButtonPresent()).toEqualBcoz(true, 'for Search 
Button');
+    expect(page.isSaveSearchButtonPresent()).toEqualBcoz(true, 'for Save 
Search Button');
+    expect(page.isTableSettingsButtonPresent()).toEqualBcoz(true, 'for table 
settings button');
+    expect(page.isPausePlayRefreshButtonPresent()).toEqualBcoz(true, 'for 
pause/play button');
+    expect(page.isConfigureTableColumnsPresent()).toEqualBcoz(true, 'for 
alerts table column configure button');
+
+    expect(page.getAlertTableTitle()).toEqualBcoz('Alerts (25 of 169)', 'for 
alerts title');
+    expect(page.getActionDropdownItems()).toEqualBcoz([ 'Open', 'Dismiss', 
'Escalate', 'Resolve' ], 'for default dropdown actions');
+    expect(page.getTableColumnNames()).toEqualBcoz(columnNames, 'for default 
column names for alert list table');
+  });
+
+  it('should have all pagination controls and they should be working', () => {
+    expect(page.isChevronLeftEnabled()).toEqualBcoz(false, 'for left chevron 
to be disabled for first page');
+    expect(page.getPaginationText()).toEqualBcoz('1 - 25 of 169', 'for 
pagination text');
+    expect(page.isChevronRightEnabled()).toEqualBcoz(true, 'for right chevron 
to be enabled for first page');
+
+    page.clickChevronRight();
+
+    expect(page.isChevronLeftEnabled()).toEqualBcoz(true, 'for left chevron to 
be enabled for second page');
+    expect(page.getPaginationText()).toEqualBcoz('26 - 50 of 169', 'for 
pagination text');
+    expect(page.isChevronRightEnabled()).toEqualBcoz(true, 'for right chevron 
to be enabled for second page');
+
+    page.clickChevronRight();
+
+    expect(page.isChevronLeftEnabled()).toEqualBcoz(true, 'for left chevron to 
be enabled for third page');
+    expect(page.getPaginationText()).toEqualBcoz('51 - 75 of 169', 'for 
pagination text');
+    expect(page.isChevronRightEnabled()).toEqualBcoz(true, 'for right chevron 
to be enabled for third page');
+
+    page.clickChevronRight(4);
+
+    expect(page.isChevronLeftEnabled()).toEqualBcoz(true, 'for left chevron to 
be enabled for last page');
+    expect(page.getPaginationText()).toEqualBcoz('151 - 169 of 169', 'for 
pagination text');
+    expect(page.isChevronRightEnabled()).toEqualBcoz(false, 'for right chevron 
to be disabled for last page');
+
+    page.clickChevronLeft(7);
+
+    expect(page.isChevronLeftEnabled()).toEqualBcoz(false, 'for left chevron 
to be disabled for first page again');
+    expect(page.getPaginationText()).toEqualBcoz('1 - 25 of 169', 'for 
pagination text');
+    expect(page.isChevronRightEnabled()).toEqualBcoz(true, 'for right chevron 
to be enabled for first page again');
+
+  });
+
+  it('should have all settings controls and they should be working', () => {
+    let settingsPaneLbelNames = [ 'REFRESH RATE', 'ROWS PER PAGE', 'HIDE 
Resolved Alerts', 'HIDE Dismissed Alerts' ];
+    let settingPaneRefreshIntervals = [ '5s', '10s', '15s', '30s', '1m', 
'10m', '1h' ];
+    let settingsPanePageSize = [ '10', '25', '50', '100', '250', '500', '1000' 
];
+
+    page.clickSettings();
+
+    expect(page.getSettingsLabels()).toEqualBcoz(settingsPaneLbelNames, 'for 
table settings labels');
+
+    
expect(page.getRefreshRateOptions()).toEqualBcoz(settingPaneRefreshIntervals, 
'for table settings refresh rate labels');
+    expect(page.getRefreshRateSelectedOption()).toEqualBcoz([ '1m' ], 'for 
table settings default refresh rate');
+
+    page.clickRefreshInterval('10s');
+    expect(page.getRefreshRateSelectedOption()).toEqualBcoz([ '10s' ], 'for 
refresh interval 10s');
+
+    page.clickRefreshInterval('1h');
+    expect(page.getRefreshRateSelectedOption()).toEqualBcoz([ '1h' ], 'for 
refresh interval 1h');
+
+    expect(page.getPageSizeOptions()).toEqualBcoz(settingsPanePageSize, 'for 
table settings refresh rate labels');
+    expect(page.getPageSizeSelectedOption()).toEqualBcoz([ '25' ], 'for table 
settings default page size');
+
+    page.clickPageSize('10');
+    expect(page.getPageSizeSelectedOption()).toEqualBcoz([ '10' ], 'for page 
size 10');
+
+    page.clickPageSize('100');
+    expect(page.getPageSizeSelectedOption()).toEqualBcoz([ '100' ], 'for page 
size 100');
+
+    page.clickSettings();
+  });
+
+  it('play pause should start polling and stop polling ', () => {
+    expect(page.getPlayPauseState()).toEqual('fa fa-pause', 'for default pause 
option');
+
+    page.clickPlayPause();
+    expect(page.getPlayPauseState()).toEqual('fa fa-play', 'for default pause 
option');
+
+    page.clickPlayPause();
+    expect(page.getPlayPauseState()).toEqual('fa fa-pause', 'for default pause 
option');
+  });
+
+  it('should select columns from table configuration', () => {
+    let newColNamesColumnConfig = [ 'score', 'timestamp', 'source:type', 
'ip_src_addr', 'enrichments:geo:ip_dst_addr:country',
+      'ip_dst_addr', 'host', 'alert_status', 'guid' ];
+
+    page.clickConfigureTable();
+    expect(page.getSelectedColumnNames()).toEqual(colNamesColumnConfig, 'for 
default selected column names');
+    page.toggleSelectCol('_id');
+    page.toggleSelectCol('guid', 'method');
+    expect(page.getSelectedColumnNames()).toEqual(newColNamesColumnConfig, 
'for guid added to selected column names');
+    page.saveConfigureColumns();
+
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/metron/blob/7d554444/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts 
b/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts
new file mode 100644
index 0000000..cefa137
--- /dev/null
+++ b/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts
@@ -0,0 +1,244 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {browser, element, by, protractor} from 'protractor';
+
+export class MetronAlertsPage {
+  navigateTo() {
+    browser.waitForAngularEnabled(false);
+    return browser.get('/alerts-list');
+  }
+
+  clearLocalStorage() {
+    browser.executeScript('window.localStorage.clear();');
+  }
+
+  isMetronLogoPresent() {
+    return element(by.css('img[src="../assets/images/logo.png"]')).isPresent();
+  }
+
+  isSavedSearchButtonPresent() {
+    return element(by.buttonText('Searches')).isPresent();
+  }
+
+  isClearSearchPresent() {
+    return element(by.css('.btn-search-clear')).isPresent();
+  }
+
+  isSearchButtonPresent() {
+    return element(by.css('.btn-search-clear')).isPresent();
+  }
+
+  isSaveSearchButtonPresent() {
+    return element(by.css('.save-button')).isPresent();
+  }
+
+  isTableSettingsButtonPresent() {
+    return element(by.css('.btn.settings')).isPresent();
+  }
+
+  isPausePlayRefreshButtonPresent() {
+    return element(by.css('.btn.pause-play')).isPresent();
+  }
+
+  isActionsButtonPresent() {
+    return element.all(by.buttonText('ACTIONS')).isPresent();
+  }
+
+  isConfigureTableColumnsPresent() {
+    return element(by.css('.fa.fa-cog.configure-table-icon')).isPresent();
+  }
+
+  getAlertTableTitle() {
+    return element(by.css('.col-form-label-lg')).getText();
+  }
+
+  getActionDropdownItems() {
+    return element(by.buttonText('ACTIONS')).click().then(() => 
element.all(by.css('.dropdown-menu .dropdown-item.disabled')).getText());
+  }
+
+  getTableColumnNames() {
+    return element.all(by.css('app-alerts-list .table th')).getText();
+  }
+
+  getPaginationText() {
+    return element(by.css('metron-table-pagination span')).getText();
+  }
+
+  isChevronLeftEnabled() {
+    return element(by.css('metron-table-pagination 
.fa.fa-chevron-left')).getAttribute('class').then((classes) => {
+      return classes.split(' ').indexOf('disabled') === -1;
+    });
+  }
+
+  isChevronRightEnabled() {
+    return element(by.css('metron-table-pagination 
.fa.fa-chevron-right')).getAttribute('class').then((classes) => {
+      return classes.split(' ').indexOf('disabled') === -1;
+    });
+  }
+
+  clickChevronRight(times = 1) {
+    for (let i = 0; i < times; i++) {
+      element(by.css('metron-table-pagination .fa.fa-chevron-right')).click();
+    }
+  }
+
+  clickChevronLeft(times = 1) {
+    for (let i = 0; i < times; i++) {
+      element(by.css('metron-table-pagination .fa.fa-chevron-left')).click();
+    }
+  }
+
+  clickSettings() {
+    return element(by.css('.btn.settings')).click();
+  }
+
+  getSettingsLabels() {
+    return element.all(by.css('form label:not(.switch)')).getText();
+  }
+
+  getRefreshRateOptions() {
+    return element.all(by.css('.preset-row.refresh-interval 
.preset-cell')).getText();
+  }
+
+  getRefreshRateSelectedOption() {
+    return element.all(by.css('.preset-row.refresh-interval 
.preset-cell.is-active')).getText();
+  }
+
+  getPageSizeOptions() {
+    return element.all(by.css('.preset-row.page-size .preset-cell')).getText();
+  }
+
+  getPageSizeSelectedOption() {
+    return element.all(by.css('.preset-row.page-size 
.preset-cell.is-active')).getText();
+  }
+
+  clickRefreshInterval(intervalText: string) {
+    return element(by.cssContainingText('.refresh-interval .preset-cell', 
intervalText)).click();
+  }
+
+  clickPageSize(pageSizeText: string) {
+    return element.all(by.cssContainingText('.page-size .preset-cell', 
pageSizeText)).first().click();
+  }
+
+  clickConfigureTable() {
+    element(by.css('app-alerts-list .fa.fa-cog.configure-table-icon')).click();
+    browser.sleep(1000);
+  }
+
+  clickCloseSavedSearch() {
+    element(by.css('app-saved-searches .close-button')).click();
+  }
+
+  clickSavedSearch() {
+    element(by.buttonText('Searches')).click();
+    browser.sleep(1000);
+  }
+
+  clickPlayPause() {
+    element(by.css('.btn.pause-play')).click();
+  }
+
+  clickTableText(name: string) {
+    element.all(by.linkText(name)).get(0).click();
+  }
+
+  clickClearSearch() {
+    element(by.css('.btn-search-clear')).click();
+  }
+
+  getSavedSearchTitle() {
+    return element(by.css('app-saved-searches .form-title')).getText();
+  }
+
+  getPlayPauseState() {
+    return element(by.css('.btn.pause-play i')).getAttribute('class');
+  }
+
+  getSearchText() {
+    return element(by.css('.ace_line')).getText();
+  }
+
+  getRecentSearchOptions() {
+    browser.sleep(1000);
+    let map = {};
+    let recentSearches = element.all(by.css('metron-collapse')).get(0);
+    return recentSearches.all(by.css('a')).getText().then(title => {
+       return 
recentSearches.all(by.css('.collapse.show')).getText().then(values => {
+         map[title] = values;
+        return map;
+      });
+    });
+  }
+
+  getSavedSearchOptions() {
+    browser.sleep(1000);
+    let map = {};
+    let recentSearches = element.all(by.css('metron-collapse')).get(1);
+    return recentSearches.all(by.css('a')).getText().then(title => {
+      return 
recentSearches.all(by.css('.collapse.show')).getText().then(values => {
+        map[title] = values;
+        return map;
+      });
+    });
+  }
+
+  getSelectedColumnNames() {
+    return element.all(by.css('app-configure-table 
input[type="checkbox"]:checked')).map(ele => {
+      return ele.getAttribute('id').then(id => id.replace(/select-deselect-/, 
''));
+    });
+  }
+
+  toggleSelectCol(name: string, scrollTo = '') {
+    scrollTo = scrollTo === '' ? name : scrollTo;
+    let ele = element(by.css('app-configure-table label[for="select-deselect-' 
+ name + '"]'));
+    let scrollToEle = element(by.css('app-configure-table 
label[for="select-deselect-' + scrollTo + '"]'));
+    browser.actions().mouseMove(scrollToEle).perform().then(() => ele.click());
+  }
+
+  saveSearch(name: string) {
+     return element(by.css('.save-button')).click().then(() => 
element(by.css('app-save-search #name')).sendKeys(name))
+      .then(() => element(by.css('app-save-search 
button[type="submit"]')).click());
+  }
+
+  saveConfigureColumns() {
+    
element(by.css('app-configure-table')).element(by.buttonText('SAVE')).click();
+  }
+
+  clickRemoveSearchChip() {
+    let aceLine = element.all(by.css('.ace_keyword')).get(0);
+    browser.actions().mouseMove(aceLine).perform().then(() => {
+      this.waitForElementPresence(element(by.css('.ace_value i'))).then(() => {
+        element.all(by.css('.ace_value i')).get(0).click();
+      });
+    });
+  }
+
+  setSearchText(search: string) {
+    this.clickClearSearch();
+    element(by.css('app-alerts-list 
.ace_text-input')).sendKeys(protractor.Key.BACK_SPACE);
+    element(by.css('app-alerts-list .ace_text-input')).sendKeys(search);
+    element(by.css('app-alerts-list 
.ace_text-input')).sendKeys(protractor.Key.ENTER);
+    browser.sleep(2000);
+  }
+
+  waitForElementPresence (element ) {
+    let EC = protractor.ExpectedConditions;
+    return browser.wait(EC.presenceOf(element));
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/7d554444/metron-interface/metron-alerts/e2e/alerts-list/configure-table/configure-table.e2e-spec.ts
----------------------------------------------------------------------
diff --git 
a/metron-interface/metron-alerts/e2e/alerts-list/configure-table/configure-table.e2e-spec.ts
 
b/metron-interface/metron-alerts/e2e/alerts-list/configure-table/configure-table.e2e-spec.ts
new file mode 100644
index 0000000..55d2a6e
--- /dev/null
+++ 
b/metron-interface/metron-alerts/e2e/alerts-list/configure-table/configure-table.e2e-spec.ts
@@ -0,0 +1,48 @@
+/// <reference path="../../matchers/custom-matchers.d.ts"/>
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { MetronAlertsPage } from '../alerts-list.po';
+import {customMatchers} from '../../matchers/custom-matchers';
+
+describe('metron-alerts configure table', function() {
+  let page: MetronAlertsPage;
+  let colNamesColumnConfig = [ 'score', '_id', 'timestamp', 'source:type', 
'ip_src_addr', 'enrichments:geo:ip_dst_addr:country',
+    'ip_dst_addr', 'host', 'alert_status' ];
+
+  beforeEach(() => {
+    page = new MetronAlertsPage();
+    jasmine.addMatchers(customMatchers);
+  });
+
+  it('should select columns from table configuration', () => {
+    let newColNamesColumnConfig = [ 'score', 'timestamp', 'source:type', 
'ip_src_addr', 'enrichments:geo:ip_dst_addr:country',
+      'ip_dst_addr', 'host', 'alert_status', 'guid' ];
+
+    page.clearLocalStorage();
+    page.navigateTo();
+
+    page.clickConfigureTable();
+    expect(page.getSelectedColumnNames()).toEqual(colNamesColumnConfig, 'for 
default selected column names');
+    page.toggleSelectCol('_id');
+    page.toggleSelectCol('guid', 'method');
+    expect(page.getSelectedColumnNames()).toEqual(newColNamesColumnConfig, 
'for guid added to selected column names');
+    page.saveConfigureColumns();
+
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/metron/blob/7d554444/metron-interface/metron-alerts/e2e/alerts-list/save-search/save-search.e2e-spec.ts
----------------------------------------------------------------------
diff --git 
a/metron-interface/metron-alerts/e2e/alerts-list/save-search/save-search.e2e-spec.ts
 
b/metron-interface/metron-alerts/e2e/alerts-list/save-search/save-search.e2e-spec.ts
new file mode 100644
index 0000000..fc02e12
--- /dev/null
+++ 
b/metron-interface/metron-alerts/e2e/alerts-list/save-search/save-search.e2e-spec.ts
@@ -0,0 +1,82 @@
+/// <reference path="../../matchers/custom-matchers.d.ts"/>
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { customMatchers } from  '../../matchers/custom-matchers';
+import {MetronAlertsPage} from '../alerts-list.po';
+
+describe('metron-alerts Search', function() {
+  let page: MetronAlertsPage;
+
+  beforeEach(() => {
+    page = new MetronAlertsPage();
+    jasmine.addMatchers(customMatchers);
+  });
+
+  it('should display all the default values for saved searches', () => {
+    page.clearLocalStorage();
+    page.navigateTo();
+
+    page.clickSavedSearch();
+    expect(page.getSavedSearchTitle()).toEqualBcoz('Searches', 'for saved 
searches title');
+    expect(page.getRecentSearchOptions()).toEqual({ 'Recent Searches': [ 'No 
Recent Searches yet' ] }, 'for recent search options');
+    expect(page.getSavedSearchOptions()).toEqual({ 'Saved Searches': [ 'No 
Saved Searches yet' ] }, 'for saved search options');
+    page.clickCloseSavedSearch();
+
+  });
+
+  it('should have all save search controls and they save search should be 
working', () => {
+    page.saveSearch('e2e-1');
+    page.clickSavedSearch();
+    expect(page.getSavedSearchOptions()).toEqual({ 'Saved Searches': [ 'e2e-1' 
] }, 'for saved search options e2e-1');
+    page.clickCloseSavedSearch();
+  });
+
+  it('should populate search items when selected on table', () => {
+    page.clickTableText('US');
+    
expect(page.getSearchText()).toEqual('enrichments:geo:ip_dst_addr:country:US', 
'for search text ip_dst_addr_country US');
+    page.clickClearSearch();
+    expect(page.getSearchText()).toEqual('*', 'for clear search');
+  });
+
+  it('should delete search items from search box', () => {
+    page.clickTableText('US');
+    
expect(page.getSearchText()).toEqual('enrichments:geo:ip_dst_addr:country:US', 
'for search text ip_dst_addr_country US');
+    page.clickRemoveSearchChip();
+    expect(page.getSearchText()).toEqual('*', 'for search chip remove');
+  });
+
+  it('should delete first search items from search box having multiple search 
fields', () => {
+    page.clickTableText('US');
+    page.clickTableText('bro');
+    
expect(page.getSearchText()).toEqual('enrichments:geo:ip_dst_addr:country:US 
AND source:type:bro', 'for search text US and bro');
+    page.clickRemoveSearchChip();
+    expect(page.getSearchText()).toEqual('source:type:bro', 'for search text 
bro after US is removed');
+    page.clickRemoveSearchChip();
+    expect(page.getSearchText()).toEqual('*', 'for search chip remove for two 
search texts');
+  });
+
+  it('manually entering search queries to search box and pressing enter key 
should search', () => {
+    page.setSearchText('enrichments:geo:ip_dst_addr:country:US');
+    expect(page.getPaginationText()).toEqualBcoz('1 - 22 of 22',
+                                                'for pagination text with 
search text enrichments:geo:ip_dst_addr:country:US');
+    page.setSearchText('enrichments:geo:ip_dst_addr:country:RU');
+    expect(page.getPaginationText()).toEqualBcoz('1 - 25 of 44',
+                                                  'for pagination text with 
search text enrichments:geo:ip_dst_addr:country:RU as text');
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/metron/blob/7d554444/metron-interface/metron-alerts/e2e/matchers/custom-matchers.d.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/matchers/custom-matchers.d.ts 
b/metron-interface/metron-alerts/e2e/matchers/custom-matchers.d.ts
new file mode 100644
index 0000000..fcaa8b6
--- /dev/null
+++ b/metron-interface/metron-alerts/e2e/matchers/custom-matchers.d.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+  
+declare module jasmine {
+  interface Matchers {
+    toEqualBcoz(expected: any, message: string): boolean;
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/7d554444/metron-interface/metron-alerts/e2e/matchers/custom-matchers.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/matchers/custom-matchers.ts 
b/metron-interface/metron-alerts/e2e/matchers/custom-matchers.ts
new file mode 100644
index 0000000..3c832c5
--- /dev/null
+++ b/metron-interface/metron-alerts/e2e/matchers/custom-matchers.ts
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+let customMatchers: jasmine.CustomMatcherFactories = {
+  toEqualBcoz: function (util: jasmine.MatchersUtil, customEqualityTesters: 
Array<jasmine.CustomEqualityTester>): jasmine.CustomMatcher {
+    return {
+      compare: function (actual: any, expected: any, message = ''): 
jasmine.CustomMatcherResult {
+        if (expected === undefined) {
+          expected = '';
+        }
+        let result = {pass: false, message: ''};
+        result.pass = util.equals(actual, expected, customEqualityTesters);
+        if (!result.pass) {
+          result.message = 'Expected \'' + actual + '\' to equal \'' + 
expected + '\' ' + message;
+        }
+        return result;
+      }
+    };
+  }
+};
+
+export {customMatchers};

Reply via email to