You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pinot.apache.org by jl...@apache.org on 2019/03/19 23:04:58 UTC
[incubator-pinot] 01/01: [TE] frontend - harleyjj/report-anomaly -
adds back report-anomaly modal to alert overview (#3985)
This is an automated email from the ASF dual-hosted git repository.
jlli pushed a commit to branch add-doc-experiment-with-pinot
in repository https://gitbox.apache.org/repos/asf/incubator-pinot.git
commit f456b46186b719c2dbfcb43c77e924cfff36517e
Author: Harley Jackson <ha...@gmail.com>
AuthorDate: Tue Mar 19 10:24:01 2019 -0700
[TE] frontend - harleyjj/report-anomaly - adds back report-anomaly modal to alert overview (#3985)
---
docs/getting_started.rst | 138 +++++++++++++++++++
.../app/pods/components/alert-details/component.js | 146 +++++++++++++++++++--
.../app/pods/components/alert-details/template.hbs | 50 +++++++
.../components/alert-report-modal/component.js | 55 ++++----
.../components/alert-report-modal/template.hbs | 61 ++++-----
.../app/pods/components/yaml-editor/component.js | 8 +-
.../app/pods/manage/explore/template.hbs | 1 +
thirdeye/thirdeye-frontend/app/utils/anomaly.js | 4 +-
.../thirdeye-frontend/app/utils/api/anomaly.js | 4 +-
.../tests/unit/utils/api/anomaly-test.js | 2 +-
10 files changed, 391 insertions(+), 78 deletions(-)
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index e3d2416..78a9250 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -94,3 +94,141 @@ show up in Pinot.
To show new events appearing, one can run :sql:`SELECT * FROM meetupRsvp ORDER BY mtime DESC LIMIT 50` repeatedly, which shows the
last events that were ingested by Pinot.
+Experimenting with Pinot
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now we have a quick start Pinot cluster running locally. The below shows a step-by-step instruction on
+how to add a simple table to the Pinot system, how to upload segments, and how to query it.
+
+Suppose we have a transcript in CSV format containing students' basic info and their scores of each subject.
+
++------------+------------+-----------+-----------+-----------+-----------+
+| StudentID | firstName | lastName | gender | subject | score |
++============+============+===========+===========+===========+===========+
+| 200 | Lucy | Smith | Female | Maths | 3.8 |
++------------+------------+-----------+-----------+-----------+-----------+
+| 200 | Lucy | Smith | Female | English | 3.5 |
++------------+------------+-----------+-----------+-----------+-----------+
+| 201 | Bob | King | Male | Maths | 3.2 |
++------------+------------+-----------+-----------+-----------+-----------+
+| 202 | Nick | Young | Male | Physics | 3.6 |
++------------+------------+-----------+-----------+-----------+-----------+
+
+Firstly in order to set up a table, we need to specify the schema of this transcript.
+
+.. code-block:: none
+
+ {
+ "schemaName": "transcript",
+ "dimensionFieldSpecs": [
+ {
+ "name": "studentID",
+ "dataType": "STRING"
+ },
+ {
+ "name": "firstName",
+ "dataType": "STRING"
+ },
+ {
+ "name": "lastName",
+ "dataType": "STRING"
+ },
+ {
+ "name": "gender",
+ "dataType": "STRING"
+ },
+ {
+ "name": "subject",
+ "dataType": "STRING"
+ }
+ ],
+ "metricFieldSpecs": [
+ {
+ "name": "score",
+ "dataType": "INT"
+ }
+ ]
+ }
+
+To upload the schema, we can use the command below:
+
+.. code-block:: none
+
+ $ ./pinot-distribution/target/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/bin/pinot-admin.sh AddSchema -schemaFile /Users/jlli/transcript-schema.json -exec
+ Executing command: AddSchema -controllerHost 172.25.119.20 -controllerPort 9000 -schemaFilePath /Users/jlli/transcript-schema.json -exec
+ Sending request: http://172.25.119.20:9000/schemas to controller: jlli-mn2.linkedin.biz, version: 0.1.0-SNAPSHOT-2c5d42a908213122ab0ad8b7ac9524fcf390e4cb
+
+Then, we need to specify the table config which links the schema to this table:
+
+.. code-block:: none
+
+ {
+ "tableName": "transcript",
+ "segmentsConfig" : {
+ "replication" : "1",
+ "schemaName" : "transcript",
+ "segmentAssignmentStrategy" : "BalanceNumSegmentAssignmentStrategy"
+ },
+ "tenants" : {
+ "broker":"DefaultTenant",
+ "server":"DefaultTenant"
+ },
+ "tableIndexConfig" : {
+ "invertedIndexColumns" : [],
+ "loadMode" : "HEAP",
+ "lazyLoad" : "false"
+ },
+ "tableType":"OFFLINE",
+ "metadata": {}
+ }
+
+And upload the table config to Pinot cluster:
+
+.. code-block:: none
+
+ $ ./pinot-distribution/target/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/bin/pinot-admin.sh AddTable -filePath /Users/jlli/transcript-table-config.json -exec
+ Executing command: AddTable -filePath /Users/jlli/transcript-table-config.json -controllerHost 172.25.119.20 -controllerPort 9000 -exec
+ {"status":"Table transcript_OFFLINE successfully added"}
+
+In order to upload our data to Pinot cluster, we need to convert our CSV file to Pinot Segment:
+
+.. code-block:: none
+
+ $ ./pinot-distribution/target/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/bin/pinot-admin.sh CreateSegment -dataDir /Users/jlli/Desktop/test/ -format CSV -outDir /Users/jlli/Desktop/test2/ -tableName transcript -segmentName transcript_0 -overwrite -schemaFile /Users/jlli/transcript-schema.json
+ Executing command: CreateSegment -generatorConfigFile null -dataDir /Users/jlli/Desktop/test/ -format CSV -outDir /Users/jlli/Desktop/test2/ -overwrite true -tableName transcript -segmentName transcript_0 -timeColumnName null -schemaFile /Users/jlli/transcript-schema.json -readerConfigFile null -enableStarTreeIndex false -starTreeIndexSpecFile null -hllSize 9 -hllColumns null -hllSuffix _hll -numThreads 1
+ Accepted files: [/Users/jlli/Desktop/test/Transcript.csv]
+ Finished building StatsCollector!
+ Collected stats for 4 documents
+ Created dictionary for STRING column: studentID with cardinality: 1, max length in bytes: 4, range: null to null
+ Created dictionary for STRING column: firstName with cardinality: 3, max length in bytes: 4, range: Bob to Nick
+ Created dictionary for STRING column: lastName with cardinality: 3, max length in bytes: 5, range: King to Young
+ Created dictionary for INT column: score with cardinality: 1, range: 0 to 0
+ Created dictionary for STRING column: gender with cardinality: 1, max length in bytes: 4, range: null to null
+ Created dictionary for STRING column: subject with cardinality: 1, max length in bytes: 4, range: null to null
+ Start building IndexCreator!
+ Finished records indexing in IndexCreator!
+ Finished segment seal!
+ Converting segment: /Users/jlli/Desktop/test2/transcript_0_0 to v3 format
+ v3 segment location for segment: transcript_0_0 is /Users/jlli/Desktop/test2/transcript_0_0/v3
+ Deleting files in v1 segment directory: /Users/jlli/Desktop/test2/transcript_0_0
+ Driver, record read time : 0
+ Driver, stats collector time : 0
+ Driver, indexing time : 1
+
+Once we have the Pinot segment, we can upload this segment to our cluster:
+
+.. code-block:: none
+
+ $ ./pinot-distribution/target/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/bin/pinot-admin.sh UploadSegment -segmentDir /Users/jlli/Desktop/test2/
+ Executing command: UploadSegment -controllerHost 172.25.119.20 -controllerPort 9000 -segmentDir /Users/jlli/Desktop/test2/
+ Compressing segment transcript_0_0
+ Uploading segment transcript_0_0.tar.gz
+ Sending request: http://172.25.119.20:9000/v2/segments to controller: jlli-mn2.linkedin.biz, version: 0.1.0-SNAPSHOT-2c5d42a908213122ab0ad8b7ac9524fcf390e4cb
+
+Now we can query the data we just uploaded to Pinot:
+
+.. code-block:: none
+
+ $ ./pinot-distribution/target/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/apache-pinot-incubating-0.1.0-SNAPSHOT-bin/bin/pinot-admin.sh PostQuery -brokerPort 8000 -query "select count(*) from transcript"
+ Executing command: PostQuery -brokerHost 172.25.119.20 -brokerPort 8000 -query select count(*) from transcript
+ Result: {"aggregationResults":[{"function":"count_star","value":"4"}],"exceptions":[],"numServersQueried":1,"numServersResponded":1,"numSegmentsQueried":1,"numSegmentsProcessed":1,"numSegmentsMatched":1,"numDocsScanned":4,"numEntriesScannedInFilter":0,"numEntriesScannedPostFilter":0,"numGroupsLimitReached":false,"totalDocs":4,"timeUsedMs":7,"segmentStatistics":[],"traceInfo":{}}
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/alert-details/component.js b/thirdeye/thirdeye-frontend/app/pods/components/alert-details/component.js
index b70b5d5..f93e432 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/alert-details/component.js
+++ b/thirdeye/thirdeye-frontend/app/pods/components/alert-details/component.js
@@ -14,9 +14,9 @@
*/
import Component from '@ember/component';
-import { computed, observer, set, get, getProperties, getWithDefault } from '@ember/object';
+import { computed, observer, set, get, getProperties } from '@ember/object';
import { later } from '@ember/runloop';
-import { checkStatus, humanizeFloat } from 'thirdeye-frontend/utils/utils';
+import { checkStatus, humanizeFloat, postProps } from 'thirdeye-frontend/utils/utils';
import { colorMapping, toColor, makeTime, toMetricLabel, extractTail } from 'thirdeye-frontend/utils/rca-utils';
import { getYamlPreviewAnomalies,
getAnomaliesByAlertId,
@@ -90,10 +90,16 @@ export default Component.extend({
currentPage: 1,
isPreviewMode: false,
alertId: null,
+ alertData: null,
feedbackOptions: ['Not reviewed yet', 'Yes - unexpected', 'Expected temporary change', 'Expected permanent change', 'No change observed'],
labelMap: anomalyResponseMapNew,
labelResponse: {},
selectedDimension: null,
+ isReportSuccess: false,
+ isReportFailure: false,
+ openReportModal: false,
+ missingAnomalyProps: {},
+
updateVisuals: observer(
@@ -170,6 +176,24 @@ export default Component.extend({
),
/**
+ * date-time-picker: indicates the date format to be used based on granularity
+ * @type {String}
+ */
+ uiDateFormat: computed('alertData.windowUnit', function() {
+ const rawGranularity = this.get('alertData.bucketUnit');
+ const granularity = rawGranularity ? rawGranularity.toLowerCase() : '';
+
+ switch(granularity) {
+ case 'days':
+ return 'MMM D, YYYY';
+ case 'hours':
+ return 'MMM D, YYYY h a';
+ default:
+ return 'MMM D, YYYY hh:mm a';
+ }
+ }),
+
+ /**
* Table pagination: creates the page Array for view
* @type {Array}
*/
@@ -645,6 +669,54 @@ export default Component.extend({
}
},
+ /**
+ * Send a POST request to the report anomaly API (2-step process)
+ * http://go/te-ss-alert-flow-api
+ * @method reportAnomaly
+ * @param {String} id - The alert id
+ * @param {Object} data - The input values from 'report new anomaly' modal
+ * @return {Promise}
+ */
+ _reportAnomaly(id, metricUrn, data) {
+ const reportUrl = `/detection/report-anomaly/${id}?metricUrn=${metricUrn}`;
+ const requiredProps = ['startTime', 'endTime', 'feedbackType'];
+ let missingData = false;
+ requiredProps.forEach(prop => {
+ if (!data[prop]) {
+ missingData = true;
+ }
+ });
+ let queryStringUrl = reportUrl;
+
+ if (missingData) {
+ return Promise.reject(new Error('missing data'));
+ } else {
+ Object.entries(data).forEach(([key, value]) => {
+ queryStringUrl += `&${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
+ });
+ // Step 1: Report the anomaly
+ return fetch(queryStringUrl, postProps('')).then((res) => checkStatus(res, 'post'));
+ }
+ },
+
+ /**
+ * Modal opener for "report missing anomaly".
+ * @method _triggerOpenReportModal
+ * @return {undefined}
+ */
+ _triggerOpenReportModal() {
+ this.setProperties({
+ isReportSuccess: false,
+ isReportFailure: false,
+ openReportModal: true
+ });
+ // We need the C3/D3 graph to render after its containing parent elements are rendered
+ // in order to avoid strange overflow effects.
+ later(() => {
+ this.set('renderModalContent', true);
+ });
+ },
+
actions: {
/**
* Handle dynamically saving anomaly feedback responses
@@ -664,11 +736,9 @@ export default Component.extend({
// Save anomaly feedback
await updateAnomalyFeedback(anomalyRecord.anomalyId, responseObj.value);
// We make a call to ensure our new response got saved
- const anomaly = await verifyAnomalyFeedback(anomalyRecord.anomalyId, responseObj.status);
- const filterMap = getWithDefault(anomaly, 'searchFilters.statusFilterMap', null);
- // This verifies that the status change got saved as key in the anomaly statusFilterMap property
- const keyPresent = filterMap && Object.keys(filterMap).find(key => responseObj.status.includes(key));
- if (keyPresent) {
+ const anomaly = await verifyAnomalyFeedback(anomalyRecord.anomalyId);
+
+ if (anomaly.feedback && responseObj.value === anomaly.feedback.feedbackType) {
this.set('labelResponse', {
anomalyId: anomalyRecord.anomalyId,
showResponseSaved: true,
@@ -680,7 +750,13 @@ export default Component.extend({
let found = false;
while (i < anomalies.length && !found) {
if (anomalies[i].id === anomalyRecord.anomalyId) {
- anomalies[i].feedback.feedbackType = newFeedbackValue;
+ if (anomalies[i].feedback) {
+ anomalies[i].feedback.feedbackType = newFeedbackValue;
+ } else {
+ anomalies[i].feedback = {
+ feedbackType: newFeedbackValue
+ };
+ }
found = true;
}
i++;
@@ -700,6 +776,60 @@ export default Component.extend({
set(this, 'renderStatusIcon', true);
},
+ /**
+ * Handle missing anomaly modal cancel
+ */
+ onCancel() {
+ this.setProperties({
+ isReportSuccess: false,
+ isReportFailure: false,
+ openReportModal: false,
+ renderModalContent: false
+ });
+ },
+
+ /**
+ * Open modal for missing anomalies
+ */
+ onClickReportAnomaly() {
+ this._triggerOpenReportModal();
+ },
+
+ /**
+ * Received bubbled-up action from modal
+ * @param {Object} all input field values
+ */
+ onInputMissingAnomaly(inputObj) {
+ this.set('missingAnomalyProps', inputObj);
+ },
+
+ /**
+ * Handle submission of missing anomaly form from alert-report-modal
+ */
+ onSave() {
+ const { alertId, missingAnomalyProps, metricUrn } = this.getProperties('alertId', 'missingAnomalyProps', 'metricUrn');
+ this._reportAnomaly(alertId, metricUrn, missingAnomalyProps)
+ .then(() => {
+ const rangeFormat = 'YYYY-MM-DD HH:mm';
+ const startStr = moment(missingAnomalyProps.startTime).format(rangeFormat);
+ const endStr = moment(missingAnomalyProps.endTime).format(rangeFormat);
+ this.setProperties({
+ isReportSuccess: true,
+ isReportFailure: false,
+ openReportModal: false,
+ reportedRange: `${startStr} - ${endStr}`
+ });
+ })
+ // If failure, leave modal open and report
+ .catch(() => {
+ this.setProperties({
+ missingAnomalyProps: {},
+ isReportFailure: true,
+ isReportSuccess: false
+ });
+ });
+ },
+
onSelectDimension(selected) {
const metricUrnList = get(this, 'metricUrnList');
const newMetricUrn = metricUrnList.find(urn => {
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/alert-details/template.hbs b/thirdeye/thirdeye-frontend/app/pods/components/alert-details/template.hbs
index 287fc98..c0f479d 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/alert-details/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/components/alert-details/template.hbs
@@ -49,12 +49,30 @@
{{/if}}
</div>
+ {{#if isReportSuccess}}
+ {{#bs-alert type="success" class="te-form__banner te-form__banner--success"}}
+ <strong>Success:</strong> Anomaly reported for dates <strong>{{reportedRange}}</strong>. Refresh page to see new anomalies...
+ {{/bs-alert}}
+ {{/if}}
+
+ {{#if isReportFailure}}
+ {{#bs-alert type="danger" class="te-form__banner te-form__banner--failure"}}
+ <strong>Error:</strong> Failed to save reported anomaly.
+ {{/bs-alert}}
+ {{/if}}
+
<div class="te-content-block">
{{#if alertHasDimensions}}
<h4 class="te-self-serve__block-title">Anomalies over time for dimension {{selectedDimension}}</h4>
{{else}}
<h4 class="te-self-serve__block-title">Anomalies over time </h4>
{{/if}}
+
+ {{#unless isPreviewMode}}
+ <a class="te-self-serve__side-link te-self-serve__side-link--high" {{action "onClickReportAnomaly" this}}>Report missing anomaly</a>
+ {{/unless}}
+
+ {{!-- Dimension selector --}}
{{#if alertHasDimensions}}
<div class="te-form__select te-form__select--wider col-md-3">
{{#power-select
@@ -73,6 +91,38 @@
{{/power-select}}
</div>
{{/if}}
+
+ {{!-- Missing anomaly modal --}}
+ {{#te-modal
+ cancelButtonText="Cancel"
+ submitButtonText="Report"
+ submitAction=(action "onSave")
+ cancelAction=(action "onCancel")
+ isShowingModal=openReportModal
+ headerText="Report Undetected Anomaly"
+ }}
+ {{#if renderModalContent}}
+ {{alert-report-modal
+ series=series
+ colorMapping=colorMapping
+ axis=axis
+ zoom=zoom
+ legend=legend
+ metricName=alertData.metric
+ alertName=alertData.detectionName
+ predefinedRanges=predefinedRanges
+ dimensionOptions=dimensionOptions
+ selectedDimension=selectedDimension
+ alertHasDimensions=alertHasDimensions
+ isMetricDataLoading=isMetricDataLoading
+ isMetricDataInvalid=isMetricDataInvalid
+ inputAction=(action "onInputMissingAnomaly")
+ }}
+ {{else}}
+ {{ember-spinner}}
+ {{/if}}
+ {{/te-modal}}
+
<div class="col-xs-12 te-graph-container">
{{timeseries-chart
series=series
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/component.js b/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/component.js
index 8ca639a..df87958 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/component.js
+++ b/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/component.js
@@ -3,26 +3,22 @@
* @module components/alert-report-modal
* @property {String} metricName - text for read-only metric field
* @property {String} alertName - text for read-only alert field
- * @property {Array} dimensionOptions - options for dimension select field
* @property {Number} timePickerIncrement - config for time-range-picker
- * @property {String} maxTime - timestamp for loading anomaly graph
* @property {String} viewRegionStart - range start timestamp
* @property {String} viewRegionEnd - range end timestamp
* @property {Object} predefinedRanges - needed for time-range-picker
* @property {String} uiDateFormat - date format desired for time-range-picker
- * @property {String} graphMessageText - text for graph in loading state
* @example
{{alert-report-modal
metricName="mobile_notification_errors"
alertName="notification_sessions_mobile"
- dimensionOptions=['dimension 1', 'dimension 2']
+ selectedDimension='dimension'
+ alertHasDimensions=true
timePickerIncrement=200
- maxTime="1513137100914"
viewRegionStart="2017-10-12 23:59"
viewRegionEnd="2017-12-11 23:59"
predefinedRanges=predefinedRanges
uiDateFormat="MMM D, YYYY hh:mm a"
- graphMessageText="Loading graph"
inputAction=(action "onInputMissingAnomaly")
}}
* @exports alert-report-modal
@@ -37,19 +33,7 @@ export default Component.extend({
containerClassNames: 'alert-report-modal',
isNewTrend: false,
showTimePicker: true,
- timePickerIncrement: 30,
-
- /**
- * Pre-select all dimensions option if none else available
- * TODO: use this.get('dimensionOptions.firstObject')
- */
- init() {
- this._super(...arguments);
- const dimensionOptions = this.get('dimensionOptions');
- if (dimensionOptions && dimensionOptions.length === 1 && dimensionOptions[0] === 'All Dimensions') {
- this.set('selectedDimension', dimensionOptions[0]);
- }
- },
+ timePickerIncrement: 5,
/**
* Collects all input data for the post request
@@ -68,7 +52,7 @@ export default Component.extend({
startTime: moment(this.get('viewAnomalyStart')).utc().valueOf(),
endTime: moment(this.get('viewAnomalyEnd')).utc().valueOf(),
feedbackType: this.get('isNewTrend') ? 'ANOMALY_NEW_TREND' : 'ANOMALY',
- dimension: this.get('selectedDimension') || null,
+ dimension: this.get('showDimension') ? this.get('selectedDimension') || null : null,
comment: this.get('anomalyComments') || null,
externalURL: this.get('anomalyLinks') || null
};
@@ -78,6 +62,24 @@ export default Component.extend({
),
/**
+ * Collects all input data for the post request
+ * @method reportAnomalyPayload
+ * @return {Object} Post data
+ */
+ showDimension: computed(
+ 'alertHasDimensions',
+ 'selectedDimension',
+ function() {
+ const {
+ alertHasDimensions,
+ selectedDimension
+ } = this.getProperties('alertHasDimensions', 'selectedDimension');
+
+ return (alertHasDimensions && selectedDimension !== 'Choose a dimension');
+ }
+ ),
+
+ /**
* Sends post object as is to parent
* @method bubbleModalInput
*/
@@ -98,9 +100,9 @@ export default Component.extend({
},
/**
- * Handle selected dimension filter
- * @method onSelectDimension
- * @param {Object} selectedObj - the user-selected dimension to filter by
+ * Handle selected feedback type
+ * @method onFeedbackTypeSelection
+ * @param {Object} selectedObj - the user-selected feedback type
*/
onFeedbackTypeSelection(trendSelection) {
this.set('isNewTrend', trendSelection);
@@ -108,11 +110,10 @@ export default Component.extend({
},
/**
- * Handle selected dimension filter
- * @method onFeedbackComments
- * @param {String} comment field value
+ * Handle changes to anomaly input
+ * @method onAnomalyInput
*/
- onAnomalyInput(value) {
+ onAnomalyInput() {
this.bubbleModalInput();
}
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/template.hbs b/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/template.hbs
index 76c12c6..4520f01 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/components/alert-report-modal/template.hbs
@@ -1,9 +1,15 @@
<main class="te-form alert-report-modal__body">
<fieldset class="te-form__section te-form__section--first row" id="select-target1">
- <div class="col-xs-12">
- <legend class="te-report-title">Specify Metric, Alert, and Dimensions</legend>
- </div>
+ {{#if showDimension}}
+ <div class="col-xs-12">
+ <legend class="te-report-title">Specify Metric, Alert, and Dimensions</legend>
+ </div>
+ {{else}}
+ <div class="col-xs-12">
+ <legend class="te-report-title">Specify Metric and Alert</legend>
+ </div>
+ {{/if}}
<div class="form-group col-xs-5">
<label for="select-metric" class="control-label te-label required">Metric</label>
@@ -27,23 +33,18 @@
}}
</div>
- <div class="form-group col-xs-7">
- <label for="report-select-dimension" class="control-label te-label">Dimensions</label>
- {{#power-select
- triggerId="report-select-dimension"
- triggerClass="te-form__select"
- verticalPosition="below"
- placeholder="Select a dimension"
- renderInPlace=true
- options=dimensionOptions
- searchEnabled=false
- selected=selectedDimension
- onchange=(action "onSelectDimension")
- as |dimension|
- }}
- {{dimension}}
- {{/power-select}}
- </div>
+ {{#if showDimension}}
+ <div class="form-group col-xs-7">
+ <label for="report-select-dimension" class="control-label te-label">Dimensions</label>
+ {{input
+ type="text"
+ id="report-select-dimension"
+ class="form-control te-input"
+ value=selectedDimension
+ disabled=true
+ }}
+ </div>
+ {{/if}}
</fieldset>
<fieldset class="te-form__section row">
@@ -69,20 +70,12 @@
</fieldset>
<fieldset class="te-form__section te-form__section--graph">
- {{self-serve-graph
- isMetricDataLoading=isMetricDataLoading
- isMetricDataInvalid=isMetricDataInvalid
- isFetchingDimensions=isFetchingDimensions
- isDimensionFetchDone=isDimensionFetchDone
- availableDimensions=availableDimensions
- selectedDimension=alertDimension
- graphMailtoLink=graphMailtoLink
- isTopDimensionsAllowed=false
- isDimensionError=false
- isMetricSelected=true
- metricData=metricData
- topDimensions=topDimensions
- componentId='report-anomaly'
+ {{timeseries-chart
+ series=series
+ colorMapping=colorMapping
+ axis=axis
+ zoom=zoom
+ legend=legend
}}
</fieldset>
diff --git a/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js b/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js
index 07f384a..1777b90 100644
--- a/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js
+++ b/thirdeye/thirdeye-frontend/app/pods/components/yaml-editor/component.js
@@ -305,6 +305,7 @@ export default Component.extend({
if(value.yaml) {
set(this, 'currentYamlSettings', value.yaml);
set(this, 'groupName', value);
+ set(this, 'subscriptionGroupId', value.id);
}
},
@@ -419,12 +420,11 @@ export default Component.extend({
async saveEditYamlAction() {
const {
alertYaml,
- detectionSettingsYaml,
+ currentYamlSettings,
notifications,
alertId,
subscriptionGroupId
- } = getProperties(this, 'alertYaml', 'detectionSettingsYaml', 'notifications', 'alertId', 'subscriptionGroupId');
-
+ } = getProperties(this, 'alertYaml', 'currentYamlSettings', 'notifications', 'alertId', 'subscriptionGroupId');
//PUT alert
const alert_url = `/yaml/${alertId}`;
const alertPostProps = {
@@ -450,7 +450,7 @@ export default Component.extend({
const setting_url = `/yaml/subscription/${subscriptionGroupId}`;
const settingsPostProps = {
method: 'PUT',
- body: detectionSettingsYaml,
+ body: currentYamlSettings,
headers: { 'content-type': 'text/plain' }
};
try {
diff --git a/thirdeye/thirdeye-frontend/app/pods/manage/explore/template.hbs b/thirdeye/thirdeye-frontend/app/pods/manage/explore/template.hbs
index d0f178b..c5698d8 100644
--- a/thirdeye/thirdeye-frontend/app/pods/manage/explore/template.hbs
+++ b/thirdeye/thirdeye-frontend/app/pods/manage/explore/template.hbs
@@ -29,6 +29,7 @@
showDetails=true
dataIsCurrent=true
alertId=model.alertId
+ alertData=model.alertData
metricUrn=model.metricUrn
metricUrnList=model.metricUrnList
}}
diff --git a/thirdeye/thirdeye-frontend/app/utils/anomaly.js b/thirdeye/thirdeye-frontend/app/utils/anomaly.js
index 84c44a4..ed74925 100644
--- a/thirdeye/thirdeye-frontend/app/utils/anomaly.js
+++ b/thirdeye/thirdeye-frontend/app/utils/anomaly.js
@@ -89,7 +89,7 @@ anomalyResponseObjNew.forEach((obj) => {
* @return {Ember.RSVP.Promise}
*/
export function updateAnomalyFeedback(anomalyId, feedbackType) {
- const url = `/anomalies/updateFeedback/${anomalyId}`;
+ const url = `/dashboard/anomaly-merged-result/feedback/${anomalyId}`;
const data = { feedbackType, comment: '' };
return fetch(url, postProps(data)).then((res) => checkStatus(res, 'post'));
}
@@ -127,7 +127,7 @@ export function getAnomaliesByAlertId(alertId, startTime, endTime) {
* @return {Ember.RSVP.Promise}
*/
export function verifyAnomalyFeedback(anomalyId) {
- const url = `${anomalyApiUrls.getAnomalyDataUrl()}${anomalyId}`;
+ const url = `${anomalyApiUrls.getAnomalyDataUrl(anomalyId)}`;
return fetch(url).then(checkStatus);
}
diff --git a/thirdeye/thirdeye-frontend/app/utils/api/anomaly.js b/thirdeye/thirdeye-frontend/app/utils/api/anomaly.js
index 77e7905..de4aa5a 100644
--- a/thirdeye/thirdeye-frontend/app/utils/api/anomaly.js
+++ b/thirdeye/thirdeye-frontend/app/utils/api/anomaly.js
@@ -5,8 +5,8 @@
* @returns {String} the complete anomaly data url
* @example getAnomalyDataUrl(1491804013000, 1491890413000) // yields => /anomalies/search/anomalyIds/1491804013000/1491890413000/1?anomalyIds=
*/
-export function getAnomalyDataUrl(startStamp = 0, endStamp = 0) {
- return `/anomalies/search/anomalyIds/${startStamp}/${endStamp}/1?anomalyIds=`;
+export function getAnomalyDataUrl(anomalyId) {
+ return `/dashboard/anomalies/view/${anomalyId}`;
}
/**
diff --git a/thirdeye/thirdeye-frontend/tests/unit/utils/api/anomaly-test.js b/thirdeye/thirdeye-frontend/tests/unit/utils/api/anomaly-test.js
index c841f24..2e7e57c 100644
--- a/thirdeye/thirdeye-frontend/tests/unit/utils/api/anomaly-test.js
+++ b/thirdeye/thirdeye-frontend/tests/unit/utils/api/anomaly-test.js
@@ -4,5 +4,5 @@ import { getAnomalyDataUrl } from 'thirdeye-frontend/utils/api/anomaly';
module('Unit | Utility | api/anomaly');
test('it returns anomaly data url correctly', function(assert) {
- assert.equal(getAnomalyDataUrl(0, 0), '/anomalies/search/anomalyIds/0/0/1?anomalyIds=', 'it returns anomaly data url duration ok');
+ assert.equal(getAnomalyDataUrl(0), '/dashboard/anomalies/view/0', 'it returns anomaly data url duration ok');
});
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org
For additional commands, e-mail: commits-help@pinot.apache.org