| /** |
| * @module module/alert-config-sk |
| * @description <h2><code>alert-config-sk</code></h2> |
| * |
| * Control that allows editing an alert.Config. |
| * |
| */ |
| import { define } from 'elements-sk/define' |
| import { html, render } from 'lit-html' |
| import { ElementSk } from '../../../infra-sk/modules/ElementSk' |
| |
| import 'elements-sk/checkbox-sk' |
| import 'elements-sk/multi-select-sk' |
| import 'elements-sk/select-sk' |
| import 'elements-sk/spinner-sk' |
| import 'elements-sk/styles/buttons' |
| import { errorMessage } from 'elements-sk/errorMessage' |
| import { jsonOrThrow } from 'common-sk/modules/jsonOrThrow' |
| |
| import '../algo-select-sk' |
| import '../query-chooser-sk' |
| |
| const _groupByChoices = (ele) => { |
| const groups = ele._config.group_by.split(','); |
| return ele._paramkeys.map((p) => html`<div ?selected=${groups.indexOf(p) != -1}>${p}</div>`); |
| } |
| |
| const template = (ele) => html` |
| <h3>Display Name</h3> |
| <label for=display-name>Display Name</label> |
| <input id=display-name type=text .value=${ele._config.display_name} @change=${(e) => ele._config.display_name = e.target.value}> |
| <h3>Category</h3> |
| <label for=category>Alerts will be grouped by category.</label> |
| <input id=category type=text .value=${ele._config.category} @input=${(e) => ele._config.category = e.target.value}> |
| <h3>Which traces should be monitored</h3> |
| <query-chooser-sk id=querychooser .paramset=${ele.paramset} .key_order=${ele.key_order} current_query=${ele._config.query} count_url='/_/count/' @query-change=${(e) => ele._config.query = e.detail.q}></query-chooser-sk> |
| <h3>What triggers an alert</h3> |
| <h4>Algorithm</h4> |
| <algo-select-sk algo=${ele._config.algo} @algo-change=${(e) => ele._config.algo = e.detail.algo}></algo-select-sk> |
| <h4>K</h4> |
| <label for=k>The number of clusters. Only used in kmeans. 0 = use a server chosen value. (For Tail algorithm, K is the jump percentage.)</label> |
| <input id=k type=number min=0 .value=${ele._config.k} @input=${(e) => ele._config.k = +e.target.value}> |
| <h4>Radius</h4> |
| <label for=radius>Number of commits on either side to consider. 0 = use a server chosen value. (For Tail algorithm, we only consider 2*Radius commits on the left side.)</label> |
| <input id=radius type=number min=0 .value=${ele._config.radius} @input=${(e) => ele._config.radius = +e.target.value}> |
| <h4>Step Direction</h4> |
| <select-sk @selection-changed=${(e) => ele._config.direction = e.target.children[e.detail.selection].getAttribute('value')}> |
| <!-- TODO(jcgregorio) Go back to using select-sk.selection once we've excised all Polymer. --> |
| <div value=BOTH ?selected=${ele._config.direction === 'BOTH'} >Either step up or step down trigger an alert.</div> |
| <div value=UP ?selected=${ele._config.direction === 'UP'}>Step up triggers an alert.</div> |
| <div value=DOWN ?selected=${ele._config.direction === 'DOWN'}>Step down triggers an alert.</div> |
| </select-sk> |
| <h4>Threshold</h4> |
| <label for=threshold>Interesting Threshold for clusters to be interesting. This value is based on the Algorithm chosen. It is a Regression factor for K-Means and Stepfit, an absolute value for Absolute, and a percentage value (0.0 -> 1.0) for Percent.</label> |
| <input id=threshold .value=${ele._config.interesting} @input=${(e) => ele._config.interesting = +e.target.value}> |
| <h4>Minimum</h4> |
| <label for=min>Minimum number of interesting traces to trigger an alert.</label> |
| <input id=min type=number .value=${ele._config.minimum_num} @input=${(e) => ele._config.minimum_num = +e.target.value}> |
| <h4>Sparse</h4> |
| <checkbox-sk ?checked=${ele._config.sparse} @input=${(e) => ele._config.sparse = e.target.checked} label='Data is sparse, so only include commits that have data.'></checkbox-sk> |
| <h3>Where are alerts sent</h3> |
| <label for=sent>Alert Destination: Comma separated list of email addresses.</label> |
| <input id=sent .value=${ele._config.alert} @input=${(e) => ele._config.alert = e.target.value}> |
| <button @click=${ele._testAlert}>Test</button> |
| <spinner-sk id=alertSpinner></spinner-sk> |
| <h3>Where are bugs filed</h3> |
| <label for=template>Bug URI Template: {cluster_url}, {commit_url}, and {message}.</label> |
| <input id=template .value=${ele._config.bug_uri_template} @input=${(e) => ele._config.bug_uri_template = e.target.value}> |
| <button @click=${ele._testBugTemplate}>Test</button> |
| <spinner-sk id=bugSpinner></spinner-sk> |
| <h3>Who owns this alert</h3> |
| <label for=owner>Email address of owner.</label> |
| <input id=owner .value=${ele._config.owner} @input=${(e) => ele._config.owner = e.target.value}> |
| <h3>Group By</h3> |
| <label for=groupby>Group clusters by these parameters. (Multiselect)</label> |
| <multi-select-sk |
| @selection-changed=${(e) => ele._config.group_by = e.detail.selection.map((i) => ele._paramkeys[i]).join(',')} |
| id=groupby |
| > |
| ${_groupByChoices(ele)} |
| </multi-select-sk> |
| <h3>Status</h3> |
| <select-sk @selection-changed=${(e) => ele._config.state = e.target.children[e.detail.selection].getAttribute('value')}> |
| <div value=ACTIVE ?selected=${ele._config.state === 'ACTIVE'} title='Clusters that match this will generate alerts.'>Active</div> |
| <div value=DELETED ?selected=${ele._config.state === 'DELETED'} title='Currently inactive.'>Deleted</div> |
| </select-sk> |
| `; |
| |
| define('alert-config-sk', class extends ElementSk { |
| constructor() { |
| super(template); |
| this._paramset = {}; |
| this._paramkeys = []; |
| this._config = { |
| id: -1, |
| display_name: 'Name', |
| query: '', |
| alert: '', |
| interesting: 0, |
| bug_uri_template: '', |
| algo: 'kmeans', |
| state: 'ACTIVE', |
| owner: '', |
| step_up_only: false, |
| direction: 'BOTH', |
| radius: 10, |
| k: 50, |
| group_by: '', |
| sparse: false, |
| minimum_num: 0, |
| category: 'Experimental' |
| }; |
| if (sk) { |
| this._key_order = sk.perf.key_order; |
| } |
| } |
| |
| connectedCallback() { |
| super.connectedCallback(); |
| this._upgradeProperty('config'); |
| this._upgradeProperty('paramset'); |
| this._render(); |
| this._bugSpinner = this.querySelector('#bugSpinner'); |
| this._alertSpinner = this.querySelector('#alertSpinner'); |
| } |
| |
| _testBugTemplate() { |
| this._bugSpinner.active = true; |
| const body = { |
| bug_uri_template: this.config.bug_uri_template, |
| }; |
| fetch('/_/alert/bug/try', { |
| method: 'POST', |
| body: JSON.stringify(body), |
| headers: { |
| 'Content-Type': 'application/json' |
| } |
| }).then(jsonOrThrow).then((json) => { |
| this._bugSpinner.active = false; |
| if (json.url) { |
| // Open the bug reporting page in a new window. |
| window.open(json.url, '_blank'); |
| } |
| }).catch((msg) => { |
| this._bugSpinner.active = false; |
| errorMessage(msg); |
| }); |
| } |
| |
| _testAlert() { |
| this._alertSpinner.active = true; |
| const body = { |
| alert: this.config.alert, |
| }; |
| fetch('/_/alert/notify/try', { |
| method: 'POST', |
| body: JSON.stringify(body), |
| headers: { |
| 'Content-Type': 'application/json' |
| } |
| }).then(jsonOrThrow).then((json) => { |
| this._alertSpinner.active = false; |
| }).catch((msg) => { |
| this._alertSpinner.active = false; |
| errorMessage(msg); |
| }); |
| } |
| |
| /** @prop paramset {string} A serialized paramtools.ParamSet. */ |
| get paramset() { return this._paramset } |
| set paramset(val) { |
| if (val === undefined) { |
| return |
| } |
| this._paramset = val; |
| this._paramkeys = Object.keys(val); |
| this._paramkeys.sort(); |
| this._render(); |
| } |
| |
| /** @prop config {Object} A serialized alerts.Config. */ |
| get config() { return this._config } |
| set config(val) { |
| if (val === undefined || Object.keys(val).length === 0) { |
| return |
| } |
| this._config = val; |
| if (this._config.interesting === 0) { |
| this._config.interesting = sk.perf.interesting; |
| } |
| if (this._config.radius === 0) { |
| this._config.radius = sk.perf.radius; |
| } |
| this._render(); |
| } |
| |
| /** @prop key_order {string} The order of keys, passed to query-sk. */ |
| get key_order() { return this._key_order } |
| set key_order(val) { |
| if (val === undefined) { |
| return |
| } |
| this._key_order = val; |
| this._render(); |
| } |
| |
| }); |