apucher closed pull request #3617: [TE] frontend - harleyjj/create-alert - YAML 
autocomplete for create …
URL: https://github.com/apache/incubator-pinot/pull/3617
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git 
a/thirdeye/thirdeye-frontend/app/pods/self-serve/create-alert/controller.js 
b/thirdeye/thirdeye-frontend/app/pods/self-serve/create-alert/controller.js
index 3fd0819980..2f609cfeb4 100644
--- a/thirdeye/thirdeye-frontend/app/pods/self-serve/create-alert/controller.js
+++ b/thirdeye/thirdeye-frontend/app/pods/self-serve/create-alert/controller.js
@@ -6,10 +6,11 @@
 import { reads } from '@ember/object/computed';
 import { inject as service } from '@ember/service';
 import RSVP from "rsvp";
+import yamljs from 'yamljs';
 import fetch from 'fetch';
 import moment from 'moment';
 import Controller from '@ember/controller';
-import { computed, set, get, getWithDefault } from '@ember/object';
+import { computed, set, get, getWithDefault, getProperties } from 
'@ember/object';
 import { task, timeout } from 'ember-concurrency';
 import {
   isPresent,
@@ -29,7 +30,7 @@ import {
   getTopDimensions
 } from 'thirdeye-frontend/utils/manage-alert-utils';
 import config from 'thirdeye-frontend/config/environment';
-import { yamlAlertProps } from 'thirdeye-frontend/utils/constants';
+import { yamlAlertProps, yamIt } from 'thirdeye-frontend/utils/constants';
 
 export default Controller.extend({
 
@@ -62,10 +63,19 @@ export default Controller.extend({
   dimensionCount: 7,
   availableDimensions: 0,
   metricLookupCache: [],
+  filtersCache: {},
+  dimensionsCache: [],
+  noResultsArray: [{
+    value: 'abcdefghijklmnopqrstuvwxyz0123456789',
+    caption: 'No Results',
+    snippet: ''
+  }],
   metricHelpMailto: `mailto:${config.email}?subject=Metric Onboarding Request 
(non-additive UMP or derived)`,
   isForm: true,
   disableYamlSave: true,
   yamlAlertProps,
+  currentMetric: null,
+  isYamlParseable: true,
 
   /**
    * Component property initial settings
@@ -730,6 +740,102 @@ export default Controller.extend({
     }
   ),
 
+  /**
+   * Calls api's for specific metric's autocomplete
+   * @method _loadAutocompleteById
+   * @return Promise
+   */
+   _loadAutocompleteById(metricId) {
+     const promiseHash = {
+       filters: fetch(selfServeApiGraph.metricFilters(metricId)).then(res => 
checkStatus(res, 'get', true)),
+       dimensions: 
fetch(selfServeApiGraph.metricDimensions(metricId)).then(res => 
checkStatus(res, 'get', true))
+     };
+     return RSVP.hash(promiseHash);
+   },
+
+  /**
+   * Get autocomplete suggestions from relevant api
+   * @method _buildYamlSuggestions
+   * @return Promise
+   */
+   _buildYamlSuggestions(currentMetric, yamlAsObject, prefix, noResultsArray, 
filtersCache, dimensionsCache) {
+    // holds default result to return if all checks fail
+    let defaultReturn = Promise.resolve(noResultsArray);
+    // when metric is being autocompleted, entire text field will be replaced 
and metricId stored in editor
+    if (yamlAsObject.metric === prefix) {
+      return fetch(selfServeApiCommon.metricAutoComplete(prefix))
+        .then(checkStatus)
+        .then(metrics => {
+          if (metrics && metrics.length > 0) {
+            return metrics.map(metric => {
+              const [dataset, metricname] = metric.alias.split('::');
+              return {
+                value: metricname,
+                caption: metric.alias,
+                metricname,
+                dataset,
+                id: metric.id,
+                completer:{
+                insertMatch: (editor, data) => {
+                  editor.setValue(yamIt(data.metricname, data.dataset));
+                  editor.metricId = data.id;
+                  //editor.completer.insertMatch({value: data.value});
+                  // editor.insert('abc');
+                }
+              }}
+            })
+          }
+          return noResultsArray
+        })
+        .catch(() => {
+          return noResultsArray;
+        });
+    }
+    // if a currentMetric has been stored, we can check autocomplete filters 
and dimensions
+    if (currentMetric) {
+      const dimensionValues = yamlAsObject.dimensionExploration.dimensions;
+      const filterTypes = typeof yamlAsObject.filters === "object" ? 
Object.keys(yamlAsObject.filters) : [];
+      if (Array.isArray(dimensionValues) && dimensionValues.includes(prefix)) {
+        if (dimensionsCache.length > 0) {
+          // wraps result in Promise.resolve because return of Promise is 
expected by yamlSuggestions
+          return Promise.resolve(dimensionsCache.map(dimension => {
+            return {
+              value: dimension,
+            };
+          }));
+        }
+      }
+      let filterKey = '';
+      let i = 0;
+      while (i < filterTypes.length) {
+        if (filterTypes[i] === prefix){
+          i = filterTypes.length;
+          // wraps result in Promise.resolve because return of Promise is 
expected by yamlSuggestions
+          return Promise.resolve(Object.keys(filtersCache).map(filterType => {
+            return {
+              value: `${filterType}:`,
+              caption: `${filterType}:`,
+              snippet: filterType
+            };
+          }));
+        }
+        if (Array.isArray(yamlAsObject.filters[filterTypes[i]]) && 
yamlAsObject.filters[filterTypes[i]].includes(prefix)) {
+          filterKey = filterTypes[i];
+        }
+        i++;
+      }
+      if (filterKey) {
+        // wraps result in Promise.resolve because return of Promise is 
expected by yamlSuggestions
+        return Promise.resolve(filtersCache[filterKey].map(filterParam => {
+          return {
+            value: filterParam
+          }
+        }));
+      }
+    }
+    return defaultReturn;
+  },
+
   /**
    * Reset the form... clear all important fields
    * @method clearAll
@@ -774,6 +880,46 @@ export default Controller.extend({
    * Actions for create alert form view
    */
   actions: {
+    /**
+     * returns array of suggestions for Yaml editor autocompletion
+     */
+    yamlSuggestions(editor, session, position, prefix) {
+      const {
+        alertYamlContent,
+        noResultsArray
+      } = getProperties(this, 'alertYamlContent', 'noResultsArray');
+      let yamlAsObject = {}
+      try {
+        yamlAsObject = yamljs.parse(alertYamlContent);
+        set(this, 'isYamlParseable', true);
+      }
+      catch(err){
+        set(this, 'isYamlParseable', false);
+        return noResultsArray;
+      }
+      // if editor.metricId field contains a value, metric was just chosen.  
Populate caches for filters and dimensions
+      if(editor.metricId){
+        const currentMetric = set(this, 'currentMetric', editor.metricId);
+        editor.metricId = '';
+        return get(this, '_loadAutocompleteById')(currentMetric)
+          .then(resultObj => {
+            const { filters, dimensions } = resultObj;
+            this.setProperties({
+              dimensionsCache: dimensions,
+              filtersCache: filters
+            });
+          })
+          .then(() => {
+            return get(this, '_buildYamlSuggestions')(currentMetric, 
yamlAsObject, prefix, noResultsArray, get(this, 'filtersCache'), get(this, 
'dimensionsCache'))
+              .then(results => results);
+          })
+      }
+      const currentMetric = get(this, 'currentMetric');
+      // deals with no metricId, which could be autocomplete for metric or for 
filters and dimensions already cached
+      return get(this, '_buildYamlSuggestions')(currentMetric, yamlAsObject, 
prefix, noResultsArray, get(this, 'filtersCache'), get(this, 'dimensionsCache'))
+        .then(results => results);
+
+    },
 
     /**
      * Clears YAML content, disables 'save changes' button, and moves to form
@@ -782,6 +928,7 @@ export default Controller.extend({
       set(this, 'disableYamlSave', true);
       set(this, 'alertYamlContent', null);
       set(this, 'isForm', true);
+      set(this, 'currentMetric', null);
     },
 
     /**
diff --git 
a/thirdeye/thirdeye-frontend/app/pods/self-serve/create-alert/template.hbs 
b/thirdeye/thirdeye-frontend/app/pods/self-serve/create-alert/template.hbs
index 817ff0adfe..f7ad4816aa 100644
--- a/thirdeye/thirdeye-frontend/app/pods/self-serve/create-alert/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/self-serve/create-alert/template.hbs
@@ -10,9 +10,12 @@
     onToggle=(action (mut isForm))
     as |toggle|}}
       {{#toggle.label value=isForm}}
-        <span class="te-label te-label--flush">{{if isForm 'YAML' 
'Form'}}</span>
+        <span class="te-label te-label--flush">YAML</span>
       {{/toggle.label}}
       {{toggle.switch theme='ios' onLabel='diff on' offLabel='diff off'}}
+      {{#toggle.label value=isForm}}
+        <span class="te-label te-label--flush">Form</span>
+      {{/toggle.label}}
   {{/x-toggle}}
 </h1>
 <main class="alert-create card-container card-container--padded te-form">
@@ -420,12 +423,14 @@
   {{else}}
     <fieldset class="te-form__section te-form__section--first row">
       <div class="col-xs-12">
-        <legend class="te-form__section-title">Define alert in YAML</legend>
+        <legend class="te-form__section-title">Define anomaly detection in 
YAML</legend>
         {{ember-ace
           lines=35
           value=currentYamlValues
           update=(action 'onYMLSelector')
           mode="ace/mode/yaml"
+          suggestCompletions=(action 'yamlSuggestions')
+          enableLiveAutocompletion=true
         }}
       </div>
       {{!-- TOD: save for Alert settings
@@ -439,6 +444,12 @@
         }}
       </div> --}}
     </fieldset>
+    {{#unless isYamlParseable}}
+      <div class="yaml alert alert-warning fade in">
+        Could not parse YAML.  If you want autocomplete for filter name,
+        please insert a colon and type before the colon
+      </div>
+    {{/unless}}
     <fieldset class="te-form__section-submit">
       {{bs-button
         defaultText="Cancel"
diff --git 
a/thirdeye/thirdeye-frontend/app/styles/pods/self-serve/create-alert.scss 
b/thirdeye/thirdeye-frontend/app/styles/pods/self-serve/create-alert.scss
index 208e7294c2..34664e3c2f 100644
--- a/thirdeye/thirdeye-frontend/app/styles/pods/self-serve/create-alert.scss
+++ b/thirdeye/thirdeye-frontend/app/styles/pods/self-serve/create-alert.scss
@@ -51,3 +51,9 @@
     }
   }
 }
+
+.te-label {
+  &--flush {
+    margin-left: 10px;
+  }
+}
diff --git a/thirdeye/thirdeye-frontend/app/utils/constants.js 
b/thirdeye/thirdeye-frontend/app/utils/constants.js
index cc00fbcf89..5ec11291f8 100644
--- a/thirdeye/thirdeye-frontend/app/utils/constants.js
+++ b/thirdeye/thirdeye-frontend/app/utils/constants.js
@@ -11,7 +11,7 @@ export default {
 
 
 export const yamlAlertProps = `# give a name for this detection
-detectionName: name_of_the_detection 
+detectionName: name_of_the_detection
 # the metric to detect the anomalies
 metric: metric_name
 # the data set name for this metric
@@ -24,26 +24,26 @@ dimensionExploration:
   # Create an alert for each dimension value in the dimension
   dimensions:
     - dimensionName
-    
-  # (Optional) only create alert for the dimension value if the contribution 
to 
-  # overall metric is above the threshold 
+
+  # (Optional) only create alert for the dimension value if the contribution to
+  # overall metric is above the threshold
   minContribution: 0.05
-  
+
   # (Optional) only create alert for the top k dimension values
   k: 10
 
-# (Optional) Filter the metric by 
+# (Optional) Filter the metric by
 filters:
   dimensionName1:
      - dimensionValue1
      - dimensionValue2
   dimensionName2:
      - dimensionValue3
-     
+
 # configure rules of anomaly detection
 rules:
 # configure the first rule
-- # configure the detection rule. ThirdEye will detect anomalies based on the 
+- # configure the detection rule. ThirdEye will detect anomalies based on the
   # detection rules.
   detection:
       # give a name for the detection rule
@@ -51,11 +51,11 @@ rules:
       # ThirdEye rule type
       type: PERCENTAGE_RULE
       # parameters for this rule
-      params: 
+      params:
         offset: wo1w
         change: 0.1
- 
-  # (Optional) configure the exclusion rule. (Exclude the anomalies you don't 
+
+  # (Optional) configure the exclusion rule. (Exclude the anomalies you don't
   # want to see but detected by the detection rule above)
   filter:
     - name: filter_rule_1
@@ -73,3 +73,69 @@ rules:
         pattern: UP
 
 `;
+
+export const yamIt = function(metric, dataset){
+  return `# give a name for this detection
+detectionName: name_of_the_detection
+# the metric to detect the anomalies
+metric: ${metric}
+# the data set name for this metric
+dataset: ${dataset}
+# ThirdEye pipeline type. Just fill in Composite
+pipelineType: Composite
+
+# (Optional) Config dimension exploration
+dimensionExploration:
+  # Create an alert for each dimension value in the dimension
+  dimensions:
+    - dimensionName
+
+  # (Optional) only create alert for the dimension value if the contribution to
+  # overall metric is above the threshold
+  minContribution: 0.05
+
+  # (Optional) only create alert for the top k dimension values
+  k: 10
+
+# (Optional) Filter the metric by
+filters:
+  dimensionName1:
+     - dimensionValue1
+     - dimensionValue2
+  dimensionName2:
+     - dimensionValue3
+
+# configure rules of anomaly detection
+rules:
+# configure the first rule
+- # configure the detection rule. ThirdEye will detect anomalies based on the
+  # detection rules.
+  detection:
+      # give a name for the detection rule
+    - name: detection_rule_1
+      # ThirdEye rule type
+      type: PERCENTAGE_RULE
+      # parameters for this rule
+      params:
+        offset: wo1w
+        change: 0.1
+
+  # (Optional) configure the exclusion rule. (Exclude the anomalies you don't
+  # want to see but detected by the detection rule above)
+  filter:
+    - name: filter_rule_1
+      type: ABSOLUTE_CHANGE_FILTER
+      params:
+        threshold: 10000
+
+# configure more rule if you'd like to
+- detection:
+    - name: detection_rule_2
+      type: ABSOLUTE_CHANGE_RULE
+      params:
+        offset: wo1w
+        change: 1000000
+        pattern: UP
+
+`;
+};
diff --git a/thirdeye/thirdeye-frontend/ember-cli-build.js 
b/thirdeye/thirdeye-frontend/ember-cli-build.js
index b49b3ba5d0..6149e37fb6 100644
--- a/thirdeye/thirdeye-frontend/ember-cli-build.js
+++ b/thirdeye/thirdeye-frontend/ember-cli-build.js
@@ -68,6 +68,13 @@ module.exports = function(defaults) {
   // jspdf and html2canvas assets for PDF
   app.import('node_modules/jspdf/dist/jspdf.min.js');
 
+  // imports yamljs node module as commonjs
+  app.import('node_modules/yamljs/index.js', {
+    using: [
+      { transformation: 'cjs', as: 'yamljs'}
+    ]
+  });
+
   // Use `app.import` to add additional libraries to the generated
   // output files.
 
diff --git a/thirdeye/thirdeye-frontend/package.json 
b/thirdeye/thirdeye-frontend/package.json
index ef83479164..af2129d775 100644
--- a/thirdeye/thirdeye-frontend/package.json
+++ b/thirdeye/thirdeye-frontend/package.json
@@ -32,6 +32,7 @@
     "ember-cli": "~3.4.0",
     "ember-cli-app-version": "^3.0.0",
     "ember-cli-babel": "^6.6.0",
+    "ember-cli-cjs-transform": "^1.3.0",
     "ember-cli-daterangepicker": "^0.5.1",
     "ember-cli-dependency-checker": "^2.0.0",
     "ember-cli-eslint": "^4.2.1",
@@ -89,6 +90,7 @@
     "ember-cryptojs-shim": "^4.3.0",
     "ember-simple-tree": "^0.4.2",
     "ion-rangeslider": "^2.2.0",
-    "moment": "^2.18.1"
+    "moment": "^2.18.1",
+    "yamljs": "^0.3.0"
   }
 }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to