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