You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pinot.apache.org by ji...@apache.org on 2019/04/19 23:59:05 UTC
[incubator-pinot] branch master updated: [TE] frontend -
harleyjj/anomalies - make anomalies filterable by subscription group
(#4131)
This is an automated email from the ASF dual-hosted git repository.
jihao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-pinot.git
The following commit(s) were added to refs/heads/master by this push:
new d403a28 [TE] frontend - harleyjj/anomalies - make anomalies filterable by subscription group (#4131)
d403a28 is described below
commit d403a285a8b33b73640a8969d11aa10eb66955bd
Author: Harley Jackson <ha...@gmail.com>
AuthorDate: Fri Apr 19 16:59:00 2019 -0700
[TE] frontend - harleyjj/anomalies - make anomalies filterable by subscription group (#4131)
Makes anomalies on Anomalies Route filterable by subscription group
Adds "Clear All" button for filters
---
.../app/pods/anomalies/controller.js | 80 +++++++++++++++++++---
.../thirdeye-frontend/app/pods/anomalies/route.js | 26 ++++++-
.../app/pods/anomalies/template.hbs | 2 +
.../app/pods/components/entity-filter/component.js | 9 +++
.../app/pods/components/entity-filter/template.hbs | 8 ++-
5 files changed, 112 insertions(+), 13 deletions(-)
diff --git a/thirdeye/thirdeye-frontend/app/pods/anomalies/controller.js b/thirdeye/thirdeye-frontend/app/pods/anomalies/controller.js
index 5e8124d..13be4dd 100644
--- a/thirdeye/thirdeye-frontend/app/pods/anomalies/controller.js
+++ b/thirdeye/thirdeye-frontend/app/pods/anomalies/controller.js
@@ -12,9 +12,12 @@ import {
setProperties,
observer
} from '@ember/object';
-import { isPresent } from '@ember/utils';
+import { inject as service } from '@ember/service';
+import { isPresent, isEmpty } from '@ember/utils';
import Controller from '@ember/controller';
+import yamljs from 'yamljs';
import { reads } from '@ember/object/computed';
+import { toastOptions } from 'thirdeye-frontend/utils/constants';
import { setUpTimeRangeOptions, powerSort } from 'thirdeye-frontend/utils/manage-alert-utils';
import { anomalyResponseObjNew } from 'thirdeye-frontend/utils/anomaly';
import moment from 'moment';
@@ -28,6 +31,9 @@ const TIME_RANGE_OPTIONS = ['1d', '1w', '1m', '3m'];
export default Controller.extend({
queryParams: ['testMode'],
+ store: service('store'),
+
+ notifications: service('toast'),
/**
* One-way CP to store all sub groups
@@ -167,15 +173,13 @@ export default Controller.extend({
// no filter applied, just return all
return anomalyIds;
}
- let selectedAnomalies = [];
+ let selectedAnomalies = anomalyIds;
filterMaps.forEach(map => {
- if (anomalyFilters[map]) {
+ const selectedFilters = anomalyFilters[map];
+ // When a filter gets deleted, it leaves an empty array behind. We need to treat null and empty array the same here
+ if (!isEmpty(selectedFilters)) {
// a filter is selected, grab relevant anomalyIds
- if (selectedAnomalies.length === 0) {
- selectedAnomalies = this._unionOfArrays(anomaliesById, map, anomalyFilters[map]);
- } else {
- selectedAnomalies = this._intersectOfArrays(selectedAnomalies, this._unionOfArrays(anomaliesById, map, anomalyFilters[map]));
- }
+ selectedAnomalies = this._intersectOfArrays(selectedAnomalies, this._unionOfArrays(anomaliesById, map, anomalyFilters[map]));
}
});
return selectedAnomalies;
@@ -303,6 +307,14 @@ export default Controller.extend({
filterKeys = [...filterKeys, ...group];
});
Object.assign(filter, { filterKeys });
+ } else if (filter.name === "subscriptionFilterMap"){
+ const filterKeys = this.get('store')
+ .peekAll('subscription-groups')
+ .sortBy('name')
+ .filter(group => (group.get('active') && group.get('yaml')))
+ .map(group => group.get('name'));
+ // Add filterKeys prop to each facet or filter block
+ Object.assign(filter, { filterKeys });
} else if (filter.name === "statusFilterMap"){
let anomalyPropertyArray = Object.keys(anomaliesById.searchFilters[filter.name]);
anomalyPropertyArray = anomalyPropertyArray.map(prop => {
@@ -355,7 +367,10 @@ export default Controller.extend({
} else {
let addedIds = [];
selectedFilters.forEach(filter => {
- addedIds = [...addedIds, ...anomaliesById.searchFilters[filterType][filter]];
+ // If there are no anomalies from the time range with these filters, then the result will be null, so we handle that here
+ // It can happen for functionFilterMap only, because we are using subscription groups to map to alert names (function filters)
+ const anomalyIdsInResponse = anomaliesById.searchFilters[filterType][filter];
+ addedIds = anomalyIdsInResponse ? [...addedIds, ...anomaliesById.searchFilters[filterType][filter]] : addedIds;
});
return addedIds;
}
@@ -367,10 +382,57 @@ export default Controller.extend({
return existingArray.filter(anomalyId => incomingArray.includes(anomalyId));
},
+ /**
+ * This will retrieve the subscription groups from Ember Data and extract yaml configs
+ * The yaml configs are used to extract alert names and apply them as filters
+ * @method _subscriptionGroupFilter
+ * @param {Object} filterObj
+ * @returns {Object}
+ * @private
+ */
+ _subscriptionGroupFilter(filterObj) {
+ // get selected subscription groups, if any
+ const notifications = get(this, 'notifications');
+ const selectedSubGroups = filterObj['subscriptionFilterMap'];
+ if (Array.isArray(selectedSubGroups) && selectedSubGroups.length > 0) {
+ // extract selected subscription groups from Ember Data
+ const selectedSubGroupObjects = this.get('store')
+ .peekAll('subscription-groups')
+ .filter(group => {
+ return selectedSubGroups.includes(group.get('name'));
+ });
+ let additionalAlertNames = [];
+ // for each group, grab yaml, extract alert names for adding to filterObj
+ selectedSubGroupObjects.forEach(group => {
+ try {
+ const yamlAsObject = yamljs.parse(group.get('yaml'));
+ if (Array.isArray(yamlAsObject.subscribedDetections)) {
+ additionalAlertNames = [ ...additionalAlertNames, ...yamlAsObject.subscribedDetections];
+ }
+ }
+ catch(error){
+ notifications.error(`Failed to retrieve alert names for subscription group: ${group.get('name')}`, 'Error', toastOptions);
+ }
+ });
+ // add the alert names extracted from groups to any that are already present
+ let updatedFunctionFilterMap = Array.isArray(filterObj['functionFilterMap']) ? [ ...filterObj['functionFilterMap'], ...additionalAlertNames] : additionalAlertNames;
+ updatedFunctionFilterMap = [ ...new Set(powerSort(updatedFunctionFilterMap, null))];
+ set(filterObj, 'functionFilterMap', updatedFunctionFilterMap);
+ }
+ return filterObj;
+ },
+
actions: {
+ // Clears all selected filters at once
+ clearFilters() {
+ this._resetLocalFilters();
+ },
+
// Handles filter selections (receives array of filter options)
userDidSelectFilter(filterObj) {
const filterBlocksLocal = get(this, 'filterBlocksLocal');
+ // handle special case of subscription groups
+ filterObj = this._subscriptionGroupFilter(filterObj);
filterBlocksLocal.forEach(block => {
block.selected = filterObj[block.name];
});
diff --git a/thirdeye/thirdeye-frontend/app/pods/anomalies/route.js b/thirdeye/thirdeye-frontend/app/pods/anomalies/route.js
index 4a26050..9fb9583 100644
--- a/thirdeye/thirdeye-frontend/app/pods/anomalies/route.js
+++ b/thirdeye/thirdeye-frontend/app/pods/anomalies/route.js
@@ -12,12 +12,17 @@ export default Route.extend({
// Make duration service accessible
durationCache: service('services/duration'),
+ anomaliesApiService: service('services/api/anomalies'),
session: service(),
+ store: service('store'),
- model() {
+ async model() {
+ const anomaliesById = await getAnomalyIdsByTimeRange(start, end);
+ const subscriptionGroups = await this.get('anomaliesApiService').querySubscriptionGroups(); // Get all subscription groups available
return hash({
updateAnomalies: getAnomalyIdsByTimeRange,
- anomaliesById: getAnomalyIdsByTimeRange(start, end)
+ anomaliesById,
+ subscriptionGroups
});
},
@@ -56,6 +61,12 @@ export default Route.extend({
type: 'select',
matchWidth: true,
filterKeys: []
+ },
+ {
+ name: 'subscriptionFilterMap',
+ title: 'Subscription Groups',
+ type: 'select',
+ filterKeys: []
}
];
@@ -81,6 +92,14 @@ export default Route.extend({
const filterKeys = [ ...new Set(powerSort(anomalyPropertyArray, null))];
// Add filterKeys prop to each facet or filter block
Object.assign(filter, { filterKeys });
+ } else if (filter.name === "subscriptionFilterMap"){
+ const filterKeys = this.get('store')
+ .peekAll('subscription-groups')
+ .sortBy('name')
+ .filter(group => (group.get('active') && group.get('yaml')))
+ .map(group => group.get('name'));
+ // Add filterKeys prop to each facet or filter block
+ Object.assign(filter, { filterKeys });
} else {
const anomalyPropertyArray = Object.keys(model.anomaliesById.searchFilters[filter.name]);
const filterKeys = [ ...new Set(powerSort(anomalyPropertyArray, null))];
@@ -101,7 +120,8 @@ export default Route.extend({
updateAnomalies: model.updateAnomalies, //requires start and end time in epoch ex updateAnomalies(start, end)
filterBlocksLocal,
anomalyIds: model.anomaliesById.anomalyIds,
- anomaliesRange: [start, end]
+ anomaliesRange: [start, end],
+ subscriptionGroups: model.subscriptionGroups
});
},
diff --git a/thirdeye/thirdeye-frontend/app/pods/anomalies/template.hbs b/thirdeye/thirdeye-frontend/app/pods/anomalies/template.hbs
index 2655dee..fc1bdf5 100644
--- a/thirdeye/thirdeye-frontend/app/pods/anomalies/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/anomalies/template.hbs
@@ -8,6 +8,8 @@
resetFilters=resetFiltersLocal
filterBlocks=filterBlocksLocal
onSelectFilter=(action "userDidSelectFilter")
+ canClear=true
+ onClearFilters=(action "clearFilters")
}}
</div>
</aside>
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/entity-filter/component.js b/thirdeye/thirdeye-frontend/app/pods/components/entity-filter/component.js
index bfb43a6..80d5781 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/entity-filter/component.js
+++ b/thirdeye/thirdeye-frontend/app/pods/components/entity-filter/component.js
@@ -153,6 +153,15 @@ export default Component.extend({
toggleDisplay(clickedBlock) {
// Note: toggleProperty will not be able to find 'filterBlocks', as it is not an observed property
set(clickedBlock, 'isHidden', !clickedBlock.isHidden);
+ },
+
+ /**
+ * Bubbles clear filters action up to parent
+ * @method clearFilters
+ * @return {undefined}
+ */
+ clearFilters() {
+ this.get('onClearFilters')();
}
}
});
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/entity-filter/template.hbs b/thirdeye/thirdeye-frontend/app/pods/components/entity-filter/template.hbs
index 076ba60..40b551b 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/entity-filter/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/components/entity-filter/template.hbs
@@ -1,5 +1,11 @@
<header class="entity-filter__head">
- <div class="manage-alert-container__title">{{title}}</div>
+ <div class="manage-alert-container__title">{{title}}
+ {{#if canClear}}
+ <a href="#" {{action "clearFilters"}} >
+ <span class="pull-right">Clear All</span>
+ </a>
+ {{/if}}
+ </div>
</header>
{{#each filterBlocks as |block|}}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org
For additional commands, e-mail: commits-help@pinot.apache.org