You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pinot.apache.org by GitBox <gi...@apache.org> on 2019/01/15 00:34:28 UTC

[incubator-pinot] Diff for: [GitHub] aaronucsd merged pull request #3636: [TE] frontend - aaronucsd/Basic yaml editor with alert and subscripti…

diff --git a/thirdeye/thirdeye-frontend/app/pods/components/self-serve-alert-details/template.hbs b/thirdeye/thirdeye-frontend/app/pods/components/self-serve-alert-details/template.hbs
index f5abe905a5..64a2580655 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/self-serve-alert-details/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/components/self-serve-alert-details/template.hbs
@@ -12,9 +12,16 @@
         {{/if}}
       </div>
       {{#if (eq displayMode "list")}}
-        {{#link-to "manage.alert" alertData.id}}
-          <div class="te-search-results__title-name" title={{alertData.functionName}}>{{alertData.functionName}}</div>
-        {{/link-to}}
+      {{alertData.isNewPipeline}}
+        {{#if alertData.isNewPipeline}}
+          {{#link-to "manage.yaml" alertData.id}}
+            <div class="te-search-results__title-name" title={{alertData.functionName}}>{{alertData.functionName}}</div>
+          {{/link-to}}
+        {{else}}
+          {{#link-to "manage.alert" alertData.id}}
+            <div class="te-search-results__title-name" title={{alertData.functionName}}>{{alertData.functionName}}</div>
+          {{/link-to}}
+        {{/if}}
       {{/if}}
     </div>
   </div>
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/self-serve-alert-yaml-details/component.js b/thirdeye/thirdeye-frontend/app/pods/components/self-serve-alert-yaml-details/component.js
new file mode 100644
index 0000000000..06806696d1
--- /dev/null
+++ b/thirdeye/thirdeye-frontend/app/pods/components/self-serve-alert-yaml-details/component.js
@@ -0,0 +1,38 @@
+/**
+ * This component displays an alert summary section for users to see alert properties at a glance.
+ * Initially used for consistency in both alert index page and single alert details page.
+ * We use slightly different sub class names for positioning based on use case.
+ * @module components/self-serve-alert-details
+ * @property {Object} alertData    - alert properties
+ * @property {Boolean} isLoadError - was there an error loading the data
+ * @property {String} displayMode  - is the use case part of a list or standalone? 'list' || 'single'
+ * @example
+    {{#self-serve-alert-details
+      alertData=alertData
+      isLoadError=false
+      displayMode="list"
+    }}
+      ...additional case-specific header content
+    {{/self-serve-alert-details}}
+ * @exports self-serve-alert-details
+ * @author smcclung
+ */
+
+import Component from '@ember/component';
+import { setProperties, get } from '@ember/object';
+
+export default Component.extend({
+  valueClassSuffix: '',
+  modeSubClass: 'list',
+
+  init() {
+    this._super(...arguments);
+    const mode = get(this, 'displayMode');
+    if (mode === 'single') {
+      setProperties(this, {
+        valueClassSuffix: '-solo',
+        modeSubClass: 'solo'
+      });
+    }
+  }
+});
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/self-serve-alert-yaml-details/template.hbs b/thirdeye/thirdeye-frontend/app/pods/components/self-serve-alert-yaml-details/template.hbs
new file mode 100644
index 0000000000..e499995b15
--- /dev/null
+++ b/thirdeye/thirdeye-frontend/app/pods/components/self-serve-alert-yaml-details/template.hbs
@@ -0,0 +1,97 @@
+<div class="te-search-results__header">
+  <div class="te-search-results__title-group">
+    <div class="te-search-results__title">
+      {{#if (eq displayMode "single")}}
+        <span title={{alertData.functionName}}>new {{alertData.functionName}}</span>
+      {{/if}}
+      <div class="te-search-results__tag {{if (eq displayMode "list") "te-search-results__tag--list"}} {{if alertData.isActive "te-search-results__tag--active"}}">
+        {{#if isLoadError}}
+          Error
+        {{else}}
+          {{if alertData.isActive "Active" "Inactive"}}
+        {{/if}}
+      </div>
+      {{#if (eq displayMode "list")}}
+        {{#link-to "manage.yaml" alertData.id}}
+          <div class="te-search-results__title-name" title={{alertData.functionName}}>{{alertData.functionName}}</div>
+        {{/link-to}}
+      {{/if}}
+    </div>
+  </div>
+  {{yield}}
+</div>
+
+{{#if (not isLoadError)}}
+  <ul class="te-search-results__list te-search-results__list--details-block row">
+    <div class="col-xs-12 col-sm-5">
+      <li class="te-search-results__row">
+        <div class="te-search-results__option te-search-results__option--{{modeSubClass}} te-search-results__option--left">Metric</div>
+        <div class="te-search-results__value{{valueClassSuffix}}" title={{alertData.metric}}>{{alertData.metric}}</div>
+      </li>
+      <li class="te-search-results__row">
+        <div class="te-search-results__option te-search-results__option--{{modeSubClass}} te-search-results__option--left">Dataset</div>
+        <div class="te-search-results__value{{valueClassSuffix}}" title={{alertData.collection}}>
+          <span class="{{unless alertData.collection 'te-search-results__prop--missing' 'te-search-results__prop'}}">
+            {{if alertData.collection alertData.collection 'N/A'}}
+          </span>
+        </div>
+      </li>
+      {{#if (eq displayMode "single")}}
+        <li class="te-search-results__row">
+          <div class="te-search-results__option te-search-results__option--{{modeSubClass}} te-search-results__option--left">Granularity</div>
+          <div class="te-search-results__value{{valueClassSuffix}}" title={{alertData.granularity}}>
+            <span class="{{unless alertData.granularity 'te-search-results__prop--missing' 'te-search-results__prop'}}">
+              {{if alertData.granularity alertData.granularity 'N/A'}}
+            </span>
+          </div>
+        </li>
+      {{/if}}
+      <li class="te-search-results__row">
+        <div class="te-search-results__option te-search-results__option--{{modeSubClass}} te-search-results__option--left">Application</div>
+        <div class="te-search-results__value{{valueClassSuffix}}" title={{alertData.application}}>
+          <span class="{{unless alertData.application 'te-search-results__prop--missing' 'te-search-results__prop'}}">
+            {{if alertData.application alertData.application 'N/A'}}
+          </span>
+        </div>
+      </li>
+      <li class="te-search-results__row">
+        <div class="te-search-results__option te-search-results__option--{{modeSubClass}} te-search-results__option--left">Alert Owner</div>
+        <div class="te-search-results__value{{valueClassSuffix}}" title={{alertData.createdBy}}>
+          <span class="{{unless alertData.createdBy 'te-search-results__prop--missing' 'te-search-results__prop'}}">
+            {{if alertData.createdBy alertData.createdBy 'N/A'}}
+          </span>
+        </div>
+      </li>
+    </div>
+    <div class="col-xs-12 col-sm-7">
+      <li class="te-search-results__row">
+        <div class="te-search-results__option te-search-results__option--{{modeSubClass}}">Data Filter</div>
+        <div class="te-search-results__value{{valueClassSuffix}}" title={{alertData.filters}}>
+          <span class="{{unless alertData.filters 'te-search-results__prop--missing' 'te-search-results__prop'}}">
+            {{if alertData.filters alertData.filters 'N/A'}}
+          </span>
+        </div>
+      </li>
+      <li class="te-search-results__row">
+        <div class="te-search-results__option te-search-results__option--{{modeSubClass}}">Dimensions</div>
+        <div class="te-search-results__value{{valueClassSuffix}}" title={{alertData.exploreDimensions}}>
+          <span class="{{unless alertData.exploreDimensions 'te-search-results__prop--missing' 'te-search-results__prop'}}">
+            {{if alertData.exploreDimensions alertData.exploreDimensions 'N/A'}}
+          </span>
+        </div>
+      </li>
+      <li class="te-search-results__row">
+        <div class="te-search-results__option te-search-results__option--{{modeSubClass}}">Detection Type</div>
+        <div class="te-search-results__value{{valueClassSuffix}}" title={{alertData.type}}>{{alertData.type}}</div>
+      </li>
+      <li class="te-search-results__row">
+        <div class="te-search-results__option te-search-results__option--{{modeSubClass}}">Subscription Group</div>
+        <div class="te-search-results__value{{valueClassSuffix}}" title={{alertData.group}}>
+          <span class="{{unless alertData.group 'te-search-results__prop--missing' 'te-search-results__prop'}}">
+            {{if alertData.group alertData.group 'N/A'}}
+          </span>
+        </div>
+      </li>
+    </div>
+  </ul>
+{{/if}}
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js b/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js
new file mode 100644
index 0000000000..10d9331679
--- /dev/null
+++ b/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js
@@ -0,0 +1,323 @@
+/**
+ * Component to render pre-set time range selection pills and a 'custom' one using date-range-picker.
+ * @module components/range-pill-selectors
+ * @property {Object} timeRangeOptions - object containing our range options
+ * @property {Number} timePickerIncrement - determines selectable time increment in date-range-picker
+ * @property {Date} activeRangeStart - default start date for range picker
+ * @property {Date} activeRangeEnd - default end date for range picker
+ * @property {String} uiDateFormat - date format specified by parent route (often specific to metric granularity)
+ * @property {Action} selectAction - closure action from parent
+ * @example
+  {{yaml-editor
+    alertTitle="New Editor Title"
+    isEditMode=true
+    showSettings=true
+    onYMLSelector=(action "onYMLSelector")
+    saveAlertYaml=(action "saveAlertYaml")
+    cancelAlertYaml=(action "cancelAlertYaml")
+  }}
+ * @author
+ */
+
+import Component from '@ember/component';
+import { computed, set, get, getProperties } from '@ember/object';
+import { checkStatus } from 'thirdeye-frontend/utils/utils';
+import { yamlAlertProps, yamlAlertSettings, yamIt } from 'thirdeye-frontend/utils/constants';
+import yamljs from 'yamljs';
+import RSVP from "rsvp";
+import fetch from 'fetch';
+import {
+  selfServeApiGraph,
+  selfServeApiCommon
+} from 'thirdeye-frontend/utils/api/self-serve';
+import { inject as service } from '@ember/service';
+import { task } from 'ember-concurrency';
+
+export default Component.extend({
+  classNames: ['yaml-editor'],
+  notifications: service('toast'),
+  /**
+   * Properties we expect to receive for the yaml-editor
+   */
+   //isForm: true,
+  currentMetric: null,
+  isYamlParseable: true,
+  alertTitle: 'Define anomaly detection in YAML',
+  alertSettingsTitle: 'Define notification settings',
+  isEditMode: false,
+  showSettings: true,
+  disableYamlSave: true,
+  errorMsg: '',
+  alertYaml: null,           // The YAML for the anomaly alert detection
+  detectionSettingsYaml:  null,   // The YAML for the subscription group
+  yamlAlertProps: yamlAlertProps,
+  yamlAlertSettings: yamlAlertSettings,
+  subscriptionGroupNames: [],
+
+  /**
+   * params passed to yaml-editor component
+   */
+  async didReceiveAttrs() {
+    this._super(...arguments);
+
+    // fetch the subscription group list
+    await get(this, '_fetchSubscriptionGroups').perform();
+  },
+
+  /**
+   * sets Yaml value displayed to contents of alertYaml or yamlAlertProps
+   * @method currentYamlValues
+   * @return {String}
+   */
+  currentYamlValues: computed(
+    'alertYaml',
+    function() {
+      const inputYaml = this.get('alertYaml');
+      return inputYaml || this.get('yamlAlertProps');
+    }
+  ),
+
+  /**
+   * sets Yaml value displayed to contents of alertYaml or yamlAlertProps
+   * @method currentYamlValues
+   * @return {String}
+   */
+  currentYamlSettings: computed(
+    'detectionSettingsYaml',
+    function() {
+      const inputYaml = this.get('detectionSettingsYaml');
+      return inputYaml || this.get('yamlAlertSettings');
+    }
+  ),
+
+
+  isErrorMsg: computed(
+    'errorMsg',
+    function() {
+      const errorMsg = this.get('errorMsg');
+      return errorMsg !== '';
+    }
+  ),
+
+  _fetchSubscriptionGroups: task(function* () {
+    // /detection/subscription-groups
+    const url2 = `/detection/subscription-groups`;//dropdown of subscription groups
+    const postProps2 = {
+      method: 'get',
+      headers: { 'content-type': 'application/json' }
+    };
+    const notifications = get(this, 'notifications');
+
+    try {
+      const response = yield fetch(url2, postProps2);
+      const json = yield response.json();
+      //filter subscription groups with yaml
+      set(this, 'subscriptionGroupNames', json.filterBy('yaml'));
+    } catch (error) {
+      notifications.error('Failed to retrieve subscription groups.', 'Error');
+    }
+  }).drop(),
+
+  /**
+   * 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;
+  },
+
+  actions: {
+    /**
+     * Updates the notification settings yaml with user section
+     */
+    onYAMLGroupSelectionAction(value) {
+      if(value.yaml) {
+        set(this, 'currentYamlSettings', value.yaml);
+        set(this, 'groupName', value);
+      }
+    },
+
+    /**
+     * returns array of suggestions for Yaml editor autocompletion
+     */
+    yamlSuggestions(editor, session, position, prefix) {
+      const {
+        alertYaml,
+        noResultsArray
+      } = getProperties(this, 'alertYaml', 'noResultsArray');
+      let yamlAsObject = {}
+      try {
+        yamlAsObject = yamljs.parse(alertYaml);
+        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);
+
+    },
+
+    /**
+     * Activates 'save changes' button and stores YAML content in alertYaml
+     */
+    onYMLSelectorAction(value) {
+      set(this, 'disableYamlSave', false);
+      set(this, 'alertYaml', value);
+      set(this, 'errorMsg', '');
+    },
+
+    /**
+     * Activates 'save changes' button and stores YAML content in alertYaml
+     */
+    onYMLSettingsSelectorAction(value) {
+      set(this, 'disableYamlSave', false);
+      set(this, 'currentYamlSettings', value);
+    },
+
+    cancelAlertYamlAction() {
+      //call the onConfirm property to invoke the passed in action
+      get(this, 'cancelAlertYaml')();
+    },
+
+    /**
+     * Fired by save button in YAML UI
+     * Grabs YAML content and sends it
+     */
+    saveAlertYamlAction() {
+      const content = {
+        detection: get(this, 'alertYaml'),
+        notification: get(this, 'currentYamlSettings')
+      }
+      const url = '/yaml/create-alert';
+      const postProps = {
+        method: 'post',
+        body: JSON.stringify(content),
+        headers: { 'content-type': 'application/json' }
+      };
+      const notifications = this.get('notifications');
+
+      fetch(url, postProps).then((res) => {
+        res.json().then((result) => {
+          if (result && result.message) {
+            set(this, 'errorMsg', result.message);
+          }
+          if (result.detectionAlertConfigId && result.detectionConfigId) {
+            notifications.success('Save alert yaml successfully.', 'Saved');
+          }
+        });
+
+      }).catch((error) => {
+        notifications.error('Save alert yaml file failed.', error);
+      });
+    }
+  }
+});
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/template.hbs b/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/template.hbs
new file mode 100644
index 0000000000..33919ba935
--- /dev/null
+++ b/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/template.hbs
@@ -0,0 +1,82 @@
+<fieldset class="te-form__section te-form__section--first row">
+  <div class="col-xs-12">
+    <legend class="te-form__section-title">{{alertTitle}}</legend>
+    {{ember-ace
+      lines=35
+      value=currentYamlValues
+      suggestCompletions=(action 'yamlSuggestions')
+      enableLiveAutocompletion=true
+      update=(action "onYMLSelectorAction")
+      mode="ace/mode/yaml"
+    }}
+  </div>
+
+  <div class="col-xs-12">
+    <hr/>
+  </div>
+  {{!-- TOD: save for Alert settings --}}
+  {{#if showSettings}}
+    <div class="col-xs-12">
+      <legend class="te-form__section-title">{{alertSettingsTitle}}</legend>
+    </div>
+    <div class="col-xs-4">
+      <label class="te-label te-label--small">Add this alert to a subscription group</label>
+      {{!--  subscription group --}}
+      {{#power-select
+        options=subscriptionGroupNames
+        selected=groupName
+        searchField="name"
+        onchange=(action 'onYAMLGroupSelectionAction')
+        as |groupName|
+      }}
+        {{groupName.name}} ({{groupName.id}})
+      {{/power-select}}
+    </div>
+    <div class="col-xs-12">
+      {{!-- notification settings editor --}}
+      {{ember-ace
+        lines=20
+        value=currentYamlSettings
+        update=(action "onYMLSettingsSelectorAction")
+        mode="ace/mode/yaml"
+      }}
+    </div>
+  {{/if}}
+  <div class="col-xs-12">
+    {{#if isErrorMsg}}
+      <div class="yaml-editor-msg">
+        <p class="yaml-editor-msg__icon"><i class="yaml-editor-msg__icon--error glyphicon glyphicon-remove-circle"></i>Error in the yaml file</p>
+        <p>Message: {{errorMsg}}</p>
+      </div>
+    {{/if}}
+  </div>
+</fieldset>
+
+<fieldset class="te-form__section-submit">
+  {{#if isEditMode}}
+    {{bs-button
+      defaultText="Cancel"
+      type="outline-primary"
+      buttonType="cancel"
+      onClick=(action "cancelAlertYamlAction")
+      class="te-button te-button--cancel"
+    }}
+    {{bs-button
+      defaultText="Save changes"
+      type="primary"
+      buttonType="submit"
+      disabled=disableYamlSave
+      onClick=(action "saveAlertYamlAction")
+      class="te-button te-button--submit"
+    }}
+  {{else}}
+    {{bs-button
+      defaultText="Create alert"
+      type="primary"
+      buttonType="submit"
+      disabled=disableYamlSave
+      onClick=(action "saveAlertYamlAction")
+      class="te-button te-button--submit"
+    }}
+  {{/if}}
+</fieldset>
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/alerts/index/route.js b/thirdeye/thirdeye-frontend/app/pods/manage/alerts/index/route.js
index d5c036a1d9..01d136fcf8 100644
--- a/thirdeye/thirdeye-frontend/app/pods/manage/alerts/index/route.js
+++ b/thirdeye/thirdeye-frontend/app/pods/manage/alerts/index/route.js
@@ -59,7 +59,8 @@ export default Route.extend({
         collection: yamlAlert.dataset,
         type: yamlAlert.pipelineType,
         exploreDimensions: yamlAlert.dimensions,
-        filters: this._formatYamlFilter(yamlAlert.filters)
+        filters: this._formatYamlFilter(yamlAlert.filters),
+        isNewPipeline: true
       });
     }
 
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/yaml/controller.js b/thirdeye/thirdeye-frontend/app/pods/manage/yaml/controller.js
new file mode 100644
index 0000000000..96ef85fb28
--- /dev/null
+++ b/thirdeye/thirdeye-frontend/app/pods/manage/yaml/controller.js
@@ -0,0 +1,9 @@
+/**
+ * Controller for Alert Landing and Details Page
+ * @module manage/alert
+ * @exports manage/alert
+ */
+import Controller from '@ember/controller';
+
+export default Controller.extend({
+});
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/yaml/route.js b/thirdeye/thirdeye-frontend/app/pods/manage/yaml/route.js
new file mode 100644
index 0000000000..9101711da2
--- /dev/null
+++ b/thirdeye/thirdeye-frontend/app/pods/manage/yaml/route.js
@@ -0,0 +1,66 @@
+/**
+ * Handles the 'alert details' route.
+ * @module manage/alert/route
+ * @exports manage alert model
+ */
+import Route from '@ember/routing/route';
+import RSVP from 'rsvp';
+import { set, get } from '@ember/object';
+import { inject as service } from '@ember/service';
+
+export default Route.extend({
+  notifications: service('toast'),
+
+  async model(params) {
+    const id = params.alert_id;
+
+    //detection alert fetch
+    const url = `/detection/${id}`;
+    const postProps = {
+      method: 'get',
+      headers: { 'content-type': 'application/json' }
+    };
+    const notifications = this.get('notifications');
+
+    await fetch(url, postProps).then((res) => {
+      res.json().then((result) => {
+        if (result && result.yaml) {
+          set(this, 'detectionYaml', result);
+        }
+      });
+
+      if (res && res.active) {
+        notifications.success('Save alert yaml successfully.', 'Saved');
+      }
+    }).catch((ex) => {
+      notifications.error('Save alert yaml file failed.', 'Error');
+    });
+
+    //subscription group fetch
+    const url2 = `/detection/subscription-groups/${id}`;//dropdown of subscription groups
+    const postProps2 = {
+      method: 'get',
+      headers: { 'content-type': 'application/json' }
+    };
+
+    await fetch(url2, postProps2).then((res) => {
+      res.json().then((result) => {
+        if (result && result.yaml) {
+          set(this, 'detectionSettingsYaml', result);
+        }
+      });
+
+      if (res && res.active) {
+        notifications.success('Save alert yaml successfully.', 'Saved');
+      }
+    }).catch((ex) => {
+      notifications.error('Save alert yaml file failed.', 'Error');
+    });
+
+    return RSVP.hash({
+      id,
+      detectionYaml: get(this, 'detectionYaml'),
+      detectionSettingsYaml: get(this, 'detectionSettingsYaml')
+    });
+  }
+});
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/yaml/template.hbs b/thirdeye/thirdeye-frontend/app/pods/manage/yaml/template.hbs
new file mode 100644
index 0000000000..9c7d1858fa
--- /dev/null
+++ b/thirdeye/thirdeye-frontend/app/pods/manage/yaml/template.hbs
@@ -0,0 +1,30 @@
+<section class="te-page__top te-search-results {{if isEditModeActive "te-search-results--slim"}}">
+  <div class="container">
+    {{#self-serve-alert-yaml-details
+      detectionYaml=model.detectionYaml
+      isLoadError=isLoadError
+      displayMode="single"
+    }}
+    {{/self-serve-alert-yaml-details}}
+  </div>
+</section>
+
+<section class="te-page__bottom">
+  <div class="container">
+    {{#if isLoadError}}
+      <div class="te-alert-page-pending">
+        <img src="{{rootURL}}assets/images/te-alert-error.png" class="te-alert-page-pending__image te-alert-page-pending__image--error" alt="error">
+        <h2 class="te-alert-page-pending__title">Oops, something went wrong</h2>
+        <p class="te-alert-page-pending__text">{{errorText}}</p>
+      </div>
+    {{else}}
+    {{model.detectionSettingsYaml.yaml}}
+      {{yaml-editor
+        isEditMode=true
+        showSettings=true
+        alertYaml=model.detectionYaml.yaml
+        detectionSettingsYaml=model.detectionSettingsYaml.yaml
+      }}
+    {{/if}}
+  </div>
+</section>
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 2f609cfeb4..df503e93d6 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,7 +6,6 @@
 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';
@@ -30,7 +29,6 @@ import {
   getTopDimensions
 } from 'thirdeye-frontend/utils/manage-alert-utils';
 import config from 'thirdeye-frontend/config/environment';
-import { yamlAlertProps, yamIt } from 'thirdeye-frontend/utils/constants';
 
 export default Controller.extend({
 
@@ -71,11 +69,7 @@ export default Controller.extend({
     snippet: ''
   }],
   metricHelpMailto: `mailto:${config.email}?subject=Metric Onboarding Request (non-additive UMP or derived)`,
-  isForm: true,
-  disableYamlSave: true,
-  yamlAlertProps,
-  currentMetric: null,
-  isYamlParseable: true,
+  isDevEnv: config.environment === 'development',
 
   /**
    * Component property initial settings
@@ -84,7 +78,7 @@ export default Controller.extend({
   graphConfig: {},
   selectedFilters: JSON.stringify({}),
   selectedWeeklyEffect: true,
-  alertYamlContent: null,
+  isForm: true,
 
   /**
    * Object to cover basic ield 'presence' validation
@@ -415,19 +409,7 @@ export default Controller.extend({
     }
   ),
 
-  /**
-   * sets Yaml value displayed to contents of alertYamlContent or yamlAlertProps
-   * @method currentYamlValues
-   * @return {String}
-   */
-  currentYamlValues: computed(
-    'yamlAlertProps',
-    'alertYamlContent',
-    function() {
-      const inputYaml = this.get('alertYamlContent');
-      return (inputYaml ? inputYaml : this.get('yamlAlertProps'));
-    }
-  ),
+
 
   /**
    * Enables the submit button when all required fields are filled
@@ -740,102 +722,6 @@ 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
@@ -880,89 +766,15 @@ 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
      */
     cancelAlertYaml() {
-      set(this, 'disableYamlSave', true);
-      set(this, 'alertYamlContent', null);
       set(this, 'isForm', true);
       set(this, 'currentMetric', null);
     },
 
-    /**
-     * Activates 'save changes' button and stores YAML content in alertYamlContent
-     */
-    onYMLSelector(value) {
-      set(this, 'disableYamlSave', false);
-      set(this, 'alertYamlContent', value);
-    },
-
-    /**
-     * Fired by save button in YAML UI
-     * Grabs YAML content and sends it
-     */
-    saveAlertYaml() {
-      set(this, 'disableYamlSave', true);
-      const content = get(this, 'alertYamlContent');
-      const url = '/yaml';
-      const postProps = {
-        method: 'post',
-        body: content,
-        headers: { 'content-type': 'text/plain' }
-      };
-      const notifications = this.get('notifications');
-
-      fetch(url, postProps).then(checkStatus).then((res) => {
-        if (res && res.active) {
-          notifications.success('Save alert yaml successfully.', 'Saved');
-        }
-      }).catch(() => {
-        notifications.error('Save alert yaml file failed.', 'Error');
-      });
-    },
-
     /**
      * When a metric is selected, fetch its props, and send them to the graph builder
      * TODO: if 'hash.dimensions' is not needed, lets refactor the RSVP object instead of renaming
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 f7ad4816aa..c85dafcaff 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
@@ -1,22 +1,23 @@
 <h1 class="te-title">Create Alert
-
-  {{#x-toggle
-    value=isForm
-    classNames="te-toggle te-toggle--form te-toggle--left report-toggle pull-right"
-    theme="ios"
-    id="label-toggle"
-    showLabels=true
-    name="activeToggle"
-    onToggle=(action (mut isForm))
-    as |toggle|}}
-      {{#toggle.label value=isForm}}
-        <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}}
+  {{#if isDevEnv}}
+    {{#x-toggle
+      value=isForm
+      classNames="te-toggle te-toggle--form te-toggle--left report-toggle pull-right"
+      theme="ios"
+      id="label-toggle"
+      showLabels=true
+      name="activeToggle"
+      onToggle=(action (mut isForm))
+      as |toggle|}}
+        {{#toggle.label value=isForm}}
+          <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}}
+  {{/if}}
 </h1>
 <main class="alert-create card-container card-container--padded te-form">
   {{#if isForm}}
@@ -421,52 +422,10 @@
       {{/if}}
     </fieldset>
   {{else}}
-    <fieldset class="te-form__section te-form__section--first row">
-      <div class="col-xs-12">
-        <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
-      <div class="col-xs-12">
-        <legend class="te-form__section-title">Define alert settings</legend>
-        {{ember-ace
-          lines=15
-          value="value"
-          theme="ace/theme/chaos"
-          mode="ace/mode/yaml"
-        }}
-      </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"
-        type="outline-primary"
-        buttonType="cancel"
-        onClick=(action "cancelAlertYaml")
-        class="te-button te-button--cancel"
-      }}
-      {{bs-button
-        defaultText="Save changes"
-        type="primary"
-        buttonType="submit"
-        disabled=disableYamlSave
-        onClick=(action "saveAlertYaml")
-        class="te-button te-button--submit"
-      }}
-    </fieldset>
+    {{yaml-editor
+      isEditMode=false
+      showSettings=true
+    }}
   {{/if}}
   {{outlet}}
 </main>
diff --git a/thirdeye/thirdeye-frontend/app/router.js b/thirdeye/thirdeye-frontend/app/router.js
index edad2ff78a..b84491bc54 100644
--- a/thirdeye/thirdeye-frontend/app/router.js
+++ b/thirdeye/thirdeye-frontend/app/router.js
@@ -27,6 +27,7 @@ Router.map(function() {
     this.route('alerts', function() {
       this.route('performance');
     });
+    this.route('yaml', { path: 'yaml/:alert_id' });
   });
 
   this.route('self-serve', function() {
diff --git a/thirdeye/thirdeye-frontend/app/styles/app.scss b/thirdeye/thirdeye-frontend/app/styles/app.scss
index 1bbf74fb34..fd476c34c6 100644
--- a/thirdeye/thirdeye-frontend/app/styles/app.scss
+++ b/thirdeye/thirdeye-frontend/app/styles/app.scss
@@ -64,6 +64,7 @@ body {
 @import 'components/te-toggle';
 @import 'components/range-pill-selectors';
 @import 'components/shared/common-tabs';
+@import 'components/yaml-editor';
 
 // Pod Pages
 @import 'ember-power-select/themes/bootstrap';
diff --git a/thirdeye/thirdeye-frontend/app/styles/components/yaml-editor.scss b/thirdeye/thirdeye-frontend/app/styles/components/yaml-editor.scss
new file mode 100644
index 0000000000..a12cafa5db
--- /dev/null
+++ b/thirdeye/thirdeye-frontend/app/styles/components/yaml-editor.scss
@@ -0,0 +1,16 @@
+.yaml-editor {
+  .yaml-editor-msg {
+    color: #e75858;
+
+    &__icon {
+      font-size: 20px;
+      &--error {
+        padding-right: 5px;
+      }
+    }
+  }
+
+  .col-xs-4 {
+    margin-bottom: 20px;
+  }
+}
diff --git a/thirdeye/thirdeye-frontend/app/utils/constants.js b/thirdeye/thirdeye-frontend/app/utils/constants.js
index 5ec11291f8..9e96bfe35c 100644
--- a/thirdeye/thirdeye-frontend/app/utils/constants.js
+++ b/thirdeye/thirdeye-frontend/app/utils/constants.js
@@ -139,3 +139,25 @@ rules:
 
 `;
 };
+
+export const yamlAlertSettings = `# Below are all dummy example. Please update accordingly.
+subscriptionGroupName: test_subscription_group
+cron: "0 0/5 * 1/1 * ? *"
+application: "parity-check"
+active: true
+
+detectionConfigIds:
+ - 5773069
+
+fromAddress: your_from@company.com
+recipients:
+ to:
+  - "me@company.com"
+ cc:
+  - "cc_email@company.com"
+
+alertSchemes:
+- type: EMAIL
+
+referenceLinks:
+ "My Company FAQs": "http://www.company.com/faq"`;
diff --git a/thirdeye/thirdeye-frontend/yarn.lock b/thirdeye/thirdeye-frontend/yarn.lock
index aaba52436a..16a4763b0d 100644
--- a/thirdeye/thirdeye-frontend/yarn.lock
+++ b/thirdeye/thirdeye-frontend/yarn.lock
@@ -27,14 +27,20 @@
   dependencies:
     "@types/estree" "*"
 
-"@types/estree@*":
+"@types/estree@*", "@types/estree@0.0.39":
   version "0.0.39"
   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
+  integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
 
 "@types/estree@0.0.38":
   version "0.0.38"
   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.38.tgz#c1be40aa933723c608820a99a373a16d215a1ca2"
 
+"@types/node@*":
+  version "10.12.18"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67"
+  integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==
+
 "@types/node@^9.6.0":
   version "9.6.35"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.35.tgz#197dd535c094362a7c95f0b78f07583d6681ed26"
@@ -1701,6 +1707,11 @@ builtin-modules@^1.0.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
 
+builtin-modules@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-2.0.0.tgz#60b7ef5ae6546bd7deefa74b08b62a43a232648e"
+  integrity sha512-3U5kUA5VPsRUA3nofm/BXX7GVHKfxz0hOBAPxXrIvHzlDRkQVqEn6yi8QJegxl4LzOHLdvb7XF5dVawa/VVYBg==
+
 builtins@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88"
@@ -2545,6 +2556,23 @@ ember-cli-build-config-editor@0.5.0:
   dependencies:
     recast "^0.12.0"
 
+ember-cli-cjs-transform@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/ember-cli-cjs-transform/-/ember-cli-cjs-transform-1.3.0.tgz#73a24a9335067b83a4acac9c5fe4deb51a62391a"
+  integrity sha1-c6JKkzUGe4OkrKycX+TetRpiORo=
+  dependencies:
+    broccoli-debug "^0.6.4"
+    broccoli-plugin "^1.3.0"
+    ember-cli-babel "^6.6.0"
+    fs-extra "^5.0.0"
+    hash-for-dep "^1.2.3"
+    pkg-dir "^2.0.0"
+    resolve "^1.7.1"
+    rollup "^0.59.0"
+    rollup-plugin-commonjs "^9.1.0"
+    rollup-plugin-node-resolve "^3.3.0"
+    username "^3.0.0"
+
 ember-cli-daterangepicker@^0.5.1:
   version "0.5.1"
   resolved "https://registry.yarnpkg.com/ember-cli-daterangepicker/-/ember-cli-daterangepicker-0.5.1.tgz#34b116861eff13aee11b03c619164172fc2b504c"
@@ -4769,6 +4797,11 @@ is-integer@^1.0.4:
   dependencies:
     is-finite "^1.0.0"
 
+is-module@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
+  integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
+
 is-number@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
@@ -5600,6 +5633,13 @@ magic-string@^0.24.0:
   dependencies:
     sourcemap-codec "^1.4.1"
 
+magic-string@^0.25.1:
+  version "0.25.1"
+  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.1.tgz#b1c248b399cd7485da0fe7385c2fc7011843266e"
+  integrity sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg==
+  dependencies:
+    sourcemap-codec "^1.4.1"
+
 make-dir@^1.0.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
@@ -6361,7 +6401,7 @@ path-key@^2.0.0, path-key@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
 
-path-parse@^1.0.5:
+path-parse@^1.0.5, path-parse@^1.0.6:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
 
@@ -6409,6 +6449,13 @@ pinkie@^2.0.0:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
 
+pkg-dir@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
+  integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=
+  dependencies:
+    find-up "^2.1.0"
+
 pluralize@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
@@ -6832,6 +6879,13 @@ resolve@^1.1.2, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.3, resolve@^1.4.0,
   dependencies:
     path-parse "^1.0.5"
 
+resolve@^1.7.1:
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.9.0.tgz#a14c6fdfa8f92a7df1d996cb7105fa744658ea06"
+  integrity sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==
+  dependencies:
+    path-parse "^1.0.6"
+
 restore-cursor@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
@@ -6870,9 +6924,29 @@ rimraf@~2.2.6:
   version "2.2.8"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582"
 
-rollup-pluginutils@^2.0.1:
+rollup-plugin-commonjs@^9.1.0:
+  version "9.2.0"
+  resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.2.0.tgz#4604e25069e0c78a09e08faa95dc32dec27f7c89"
+  integrity sha512-0RM5U4Vd6iHjL6rLvr3lKBwnPsaVml+qxOGaaNUWN1lSq6S33KhITOfHmvxV3z2vy9Mk4t0g4rNlVaJJsNQPWA==
+  dependencies:
+    estree-walker "^0.5.2"
+    magic-string "^0.25.1"
+    resolve "^1.8.1"
+    rollup-pluginutils "^2.3.3"
+
+rollup-plugin-node-resolve@^3.3.0:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.4.0.tgz#908585eda12e393caac7498715a01e08606abc89"
+  integrity sha512-PJcd85dxfSBWih84ozRtBkB731OjXk0KnzN0oGp7WOWcarAFkVa71cV5hTJg2qpVsV2U8EUwrzHP3tvy9vS3qg==
+  dependencies:
+    builtin-modules "^2.0.0"
+    is-module "^1.0.0"
+    resolve "^1.1.6"
+
+rollup-pluginutils@^2.0.1, rollup-pluginutils@^2.3.3:
   version "2.3.3"
   resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.3.3.tgz#3aad9b1eb3e7fe8262820818840bf091e5ae6794"
+  integrity sha512-2XZwja7b6P5q4RZ5FhyX1+f46xi1Z3qBKigLRZ6VTZjwbN0K1IFGMlwm06Uu0Emcre2Z63l77nq/pzn+KxIEoA==
   dependencies:
     estree-walker "^0.5.2"
     micromatch "^2.3.11"
@@ -6899,6 +6973,14 @@ rollup@^0.57.1:
     signal-exit "^3.0.2"
     sourcemap-codec "^1.4.1"
 
+rollup@^0.59.0:
+  version "0.59.4"
+  resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.59.4.tgz#6f80f7017c22667ff1bf3e62adf8624a44cc44aa"
+  integrity sha512-ISiMqq/aJa+57QxX2MRcvLESHdJ7wSavmr6U1euMr+6UgFe6KM+3QANrYy8LQofwhTC1I7BcAdlLnDiaODs1BA==
+  dependencies:
+    "@types/estree" "0.0.39"
+    "@types/node" "*"
+
 route-recognizer@^0.2.3:
   version "0.2.10"
   resolved "https://registry.yarnpkg.com/route-recognizer/-/route-recognizer-0.2.10.tgz#024b2283c2e68d13a7c7f5173a5924645e8902df"
@@ -7816,6 +7898,14 @@ username@^1.0.1:
   dependencies:
     meow "^3.4.0"
 
+username@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/username/-/username-3.0.0.tgz#b3dba982a72b4ce59d52f159fa1aeba266af5fc8"
+  integrity sha1-s9upgqcrTOWdUvFZ+hrromavX8g=
+  dependencies:
+    execa "^0.7.0"
+    mem "^1.1.0"
+
 util-deprecate@^1.0.2, util-deprecate@~1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@@ -8051,6 +8141,14 @@ yam@^0.0.24:
     fs-extra "^4.0.2"
     lodash.merge "^4.6.0"
 
+yamljs@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/yamljs/-/yamljs-0.3.0.tgz#dc060bf267447b39f7304e9b2bfbe8b5a7ddb03b"
+  integrity sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==
+  dependencies:
+    argparse "^1.0.7"
+    glob "^7.0.5"
+
 yargs-parser@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org
For additional commands, e-mail: commits-help@pinot.apache.org