mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge remote-tracking branch 'grafana/master' into influx-db-query2
* grafana/master: (35 commits) Update CHANGELOG.md Update CHANGELOG.md fixed some images (#9237) release: bumped version to v4.5.0 stable docs: minor update plugins: add styles for ol tags in markdown docs: minor fixes to images Docs image updates (#9225) fix: improve behavior when switching back and forth between x-axis modes, fixes #9229 fixes for metrics tab when data source was not found provide ace editor for external datasource plugin (#9224) ux: increased code editor auto complete width from 320px to 550px, fixes #9203 docs: windows - add note about ini comments prometheus: added completer unit test, #9208 docs: minor update docs: kiosk mode options add to playlist doc influxdb: small css fix for order by in query editor style: corrected indentation in sass file replaced old images and gifs with new ones (#9217) ux: success/error alerts refactoring, #9214 ...
This commit is contained in:
commit
bc03640d6b
@ -7,11 +7,13 @@
|
||||
- UX changes to nav & side menu
|
||||
- New dashboard grid layout system
|
||||
|
||||
# 4.5.0 (unreleased)
|
||||
|
||||
## Enhancements
|
||||
# 4.5.0 (2017-09-14)
|
||||
|
||||
## Fixes & Enhancements since beta1
|
||||
* **Security**: Security fix for api vulnerability (in multiple org setups).
|
||||
* **Shortcuts**: Adds shortcut for creating new dashboard [#8876](https://github.com/grafana/grafana/pull/8876) thx [@mtanda](https://github.com/mtanda)
|
||||
* **Graph**: Right Y-Axis label position fixed [#9172](https://github.com/grafana/grafana/pull/9172)
|
||||
* **General**: Improve rounding of time intervals [#9197](https://github.com/grafana/grafana/pull/9197), thx [@alin-amana](https://github.com/alin-amana)
|
||||
|
||||
# 4.5.0-beta1 (2017-09-05)
|
||||
|
||||
|
@ -41,7 +41,9 @@ Proxy access means that the Grafana backend will proxy all requests from the bro
|
||||
Click the ``Select metric`` link to start navigating the metric space. One you start you can continue using the mouse
|
||||
or keyboard arrow keys. You can select a wildcard and still continue.
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v45/graphite_query1_still.png"
|
||||
animated-gif="/img/docs/v45/graphite_query1.gif" >}}
|
||||
|
||||
|
||||
### Functions
|
||||
|
||||
@ -50,18 +52,26 @@ a function is selected it will be added and your focus will be in the text box o
|
||||
a parameter just click on it and it will turn into a text box. To delete a function click the function name followed
|
||||
by the x icon.
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v45/graphite_query2_still.png"
|
||||
animated-gif="/img/docs/v45/graphite_query2.gif" >}}
|
||||
|
||||
|
||||
### Optional parameters
|
||||
|
||||
Some functions like aliasByNode support an optional second argument. To add this parameter specify for example 3,-2 as the first parameter and the function editor will adapt and move the -2 to a second parameter. To remove the second optional parameter just click on it and leave it blank and the editor will remove it.
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v45/graphite_query3_still.png"
|
||||
animated-gif="/img/docs/v45/graphite_query3.gif" >}}
|
||||
|
||||
|
||||
### Nested Queries
|
||||
|
||||
You can reference queries by the row “letter” that they’re on (similar to Microsoft Excel). If you add a second query to a graph, you can reference the first query simply by typing in #A. This provides an easy and convenient way to build compounded queries.
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/graphite_nested_queries_still.png"
|
||||
animated-gif="/img/docs/v45/graphite_nested_queries.gif" >}}
|
||||
|
||||
|
||||
## Point consolidation
|
||||
|
||||
All Graphite metrics are consolidated so that Graphite doesn't return more data points than there are pixels in the graph. By default,
|
||||
|
@ -41,7 +41,7 @@ mode is also more secure as the username & password will never reach the browser
|
||||
|
||||
## Query Editor
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v45/influxdb_query_still.png" class="docs-image--no-shadow" animated-gif="/img/docs/v45/influxdb_query.gif" >}}
|
||||
|
||||
You find the InfluxDB editor in the metrics tab in Graph or Singlestat panel's edit mode. You enter edit mode by clicking the
|
||||
panel title, then edit. The editor allows you to select metrics and tags.
|
||||
@ -57,10 +57,8 @@ will automatically adjust the filter tag condition to use the InfluxDB regex mat
|
||||
|
||||
### Field & Aggregation functions
|
||||
In the `SELECT` row you can specify what fields and functions you want to use. If you have a
|
||||
group by time you need an aggregation function. Some functions like derivative require an aggregation function.
|
||||
|
||||
The editor tries simplify and unify this part of the query. For example:
|
||||

|
||||
group by time you need an aggregation function. Some functions like derivative require an aggregation function. The editor tries simplify and unify this part of the query. For example:<br>
|
||||
<br>
|
||||
|
||||
The above will generate the following InfluxDB `SELECT` clause:
|
||||
|
||||
|
@ -11,8 +11,7 @@ weight = 7
|
||||
|
||||
# Using MySQL in Grafana
|
||||
|
||||
> Only available in Grafana v4.3+. This data source is not ready for
|
||||
> production use, currently in development (alpha state).
|
||||
> Only available in Grafana v4.3+.
|
||||
|
||||
Grafana ships with a built-in MySQL data source plugin that allow you to query any visualize
|
||||
data from a MySQL compatible database.
|
||||
@ -58,8 +57,7 @@ If the `Format as` query option is set to `Table` then you can basically do any
|
||||
|
||||
Query editor with example query:
|
||||
|
||||

|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/mysql_table_query.png" >}}
|
||||
|
||||
The query:
|
||||
|
||||
|
@ -39,7 +39,8 @@ Name | Description
|
||||
|
||||
Open a graph in edit mode by click the title > Edit (or by pressing `e` key while hovering over panel).
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v45/prometheus_query_editor_still.png"
|
||||
animated-gif="/img/docs/v45/prometheus_query_editor.gif" >}}
|
||||
|
||||
Name | Description
|
||||
------- | --------
|
||||
|
@ -50,15 +50,11 @@ populate the template variable to a desired value from the link.
|
||||
The metrics tab defines what series data and sources to render. Each datasource provides different
|
||||
options.
|
||||
|
||||
## Axes & Grid
|
||||
## Axes
|
||||
|
||||

|
||||
|
||||
The Axes & Grid tab controls the display of axes, grids and legend.
|
||||
|
||||
### Axes
|
||||
|
||||
The ``Left Y`` and ``Right Y`` can be customized using:
|
||||
The Axes tab controls the display of axes, grids and legend. The ``Left Y`` and ``Right Y`` can be customized using:
|
||||
|
||||
- ``Unit`` - The display unit for the Y value
|
||||
- ``Grid Max`` - The maximum Y value. (default auto)
|
||||
|
@ -12,7 +12,7 @@ weight = 2
|
||||
|
||||
# Table Panel
|
||||
|
||||
<img src="/assets/img/features/table-panel.png">
|
||||
<img class="screenshot" src="/assets/img/features/table-panel.png">
|
||||
|
||||
The new table panel is very flexible, supporting both multiple modes for time series as well as for
|
||||
table, annotation and raw JSON data. It also provides date formatting and value formatting and coloring options.
|
||||
@ -22,55 +22,63 @@ To view table panels in action and test different configurations with sample dat
|
||||
## Options overview
|
||||
|
||||
The table panel has many ways to manipulate your data for optimal presentation.
|
||||
{{< docs-imagebox img="/img/docs/v45/table_options.png" class="docs-image--no-shadow" max-width= "500px" >}}
|
||||
|
||||
<img class="no-shadow" src="/img/docs/v2/table-config2.png">
|
||||
|
||||
1. `Data`: Control how your query is transformed into a table.
|
||||
2. `Table Display`: Table display options.
|
||||
3. `Column Styles`: Column value formatting and display options.
|
||||
2. `Paging`: Table display options.
|
||||
|
||||
|
||||
## Data to Table
|
||||
|
||||
<img class="no-shadow" src="/img/docs/v2/table-data-options.png">
|
||||
{{< docs-imagebox img="/img/docs/v45/table_data_options.png" max-width="500px" class="docs-image--right">}}
|
||||
|
||||
The data section contains the **To Table Transform (1)**. This is the primary option for how your data/metric
|
||||
query should be transformed into a table format. The **Columns (2)** option allows you to select what columns
|
||||
you want in the table. Only applicable for some transforms.
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
### Time series to rows
|
||||
|
||||
<img src="/img/docs/v2/table_ts_to_rows2.png">
|
||||
{{< docs-imagebox img="/img/docs/v45/table_ts_to_rows.png" >}}
|
||||
|
||||
In the most simple mode you can turn time series to rows. This means you get a `Time`, `Metric` and a `Value` column. Where `Metric` is the name of the time series.
|
||||
|
||||
### Time series to columns
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v45/table_ts_to_columns.png" >}}
|
||||
|
||||
|
||||
This transform allows you to take multiple time series and group them by time. Which will result in the primary column being `Time` and a column for each time series.
|
||||
|
||||
### Time series aggregations
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v45/table_ts_to_aggregations.png" >}}
|
||||
|
||||
This table transformation will lay out your table into rows by metric, allowing columns of `Avg`, `Min`, `Max`, `Total`, `Current` and `Count`. More than one column can be added.
|
||||
|
||||
### Annotations
|
||||

|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/table_annotations.png" >}}
|
||||
|
||||
|
||||
If you have annotations enabled in the dashboard you can have the table show them. If you configure this
|
||||
mode then any queries you have in the metrics tab will be ignored.
|
||||
|
||||
### JSON Data
|
||||

|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/table_json_data.png" max-width="500px" >}}
|
||||
|
||||
If you have an Elasticsearch **Raw Document** query or an Elasticsearch query without a `date histogram` use this
|
||||
transform mode and pick the columns using the **Columns** section.
|
||||
|
||||

|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/elastic_raw_doc.png" >}}
|
||||
|
||||
## Table Display
|
||||
|
||||
<img class="no-shadow" src="/img/docs/v2/table-display.png">
|
||||
{{< docs-imagebox img="/img/docs/v45/table_paging.png" class="docs-image--no-shadow docs-image--right" max-width="350px" >}}
|
||||
|
||||
1. `Pagination (Page Size)`: The table display fields allow you to control The `Pagination` (page size) is the threshold at which the table rows will be broken into pages. For example, if your table had 95 records with a pagination value of 10, your table would be split across 9 pages.
|
||||
2. `Scroll`: The `scroll bar` checkbox toggles the ability to scroll within the panel, when unchecked, the panel height will grow to display all rows.
|
||||
@ -81,13 +89,11 @@ transform mode and pick the columns using the **Columns** section.
|
||||
|
||||
The column styles allow you control how dates and numbers are formatted.
|
||||
|
||||
<img class="no-shadow" src="/img/docs/v2/Column-Options.png">
|
||||
{{< docs-imagebox img="/img/docs/v45/table_column_styles.png" class="docs-image--no-shadow" >}}
|
||||
|
||||
1. `Name or regex`: The Name or Regex field controls what columns the rule should be applied to. The regex or name filter will be matched against the column name not against column values.
|
||||
2. `Type`: The three supported types of types are `Number`, `String` and `Date`.
|
||||
3. `Title`: Title for the column, when using a Regex the title can include replacement strings like `$1`.
|
||||
4. `Format`: Specify date format. Only available when `Type` is set to `Date`.
|
||||
5. `Coloring` and `Thresholds`: Specify color mode and thresholds limits.
|
||||
6. `Unit` and `Decimals`: Specify unit and decimal precision for numbers.
|
||||
7. `Add column style rule`: Add new column rule.
|
||||
2. `Column Header`: Title for the column, when using a Regex the title can include replacement strings like `$1`.
|
||||
3. `Add column style rule`: Add new column rule.
|
||||
4. `Thresholds` and `Coloring`: Specify color mode and thresholds limits.
|
||||
5. `Type`: The three supported types of types are `Number`, `String` and `Date`. `Unit` and `Decimals`: Specify unit and decimal precision for numbers.`Format`: Specify date format for dates.
|
||||
|
||||
|
@ -8,7 +8,7 @@ weight = 7
|
||||
|
||||
# Keyboard shortcuts
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v4/shortcuts.png" max-width="20rem" >}}
|
||||
{{< docs-imagebox img="/img/docs/v4/shortcuts.png" max-width="20rem" class="docs-image--right" >}}
|
||||
|
||||
Grafana v4 introduces a number of really powerful keyboard shortcuts. You can now focus a panel
|
||||
by hovering over it with your mouse. With a panel focused you can simple hit `e` to toggle panel
|
||||
|
@ -16,16 +16,13 @@ weight = -4
|
||||
|
||||
### New prometheus query editor
|
||||
|
||||
The new query editor has full syntax highlighting. As well as auto complete for metrics, functions, and range vectors.
|
||||
The new query editor has full syntax highlighting. As well as auto complete for metrics, functions, and range vectors. There is also integrated function docs right from the query editor!
|
||||
|
||||

|
||||
|
||||
There is also integrated function docs right from the query editor!
|
||||
|
||||

|
||||
{{< docs-imagebox img="/img/docs/v45/prometheus_query_editor_still.png" class="docs-image--block" animated-gif="/img/docs/v45/prometheus_query_editor.gif" >}}
|
||||
|
||||
### Elasticsearch: Add ad-hoc filters from the table panel
|
||||

|
||||
|
||||
{{< docs-imagebox img="/img/docs/v45/elastic_ad_hoc_filters.png" class="docs-image--block" >}}
|
||||
|
||||
### Table cell links!
|
||||
Create column styles that turn cells into links that use the value in the cell (or other other row values) to generate a url to another dashboard or system:
|
||||
|
@ -27,7 +27,7 @@ this folder to anywhere you want Grafana to run from. Go into the
|
||||
|
||||
The default Grafana port is `3000`, this port requires extra permissions
|
||||
on windows. Edit `custom.ini` and uncomment the `http_port`
|
||||
configuration option and change it to something like `8080` or similar.
|
||||
configuration option (`;` is the comment character in ini files) and change it to something like `8080` or similar.
|
||||
That port should not require extra Windows privileges.
|
||||
|
||||
Start Grafana by executing `grafana-server.exe`, preferably from the
|
||||
|
@ -16,7 +16,7 @@ Since Grafana automatically scales Dashboards to any resolution they're perfect
|
||||
|
||||
## Creating a Playlist
|
||||
|
||||
{{< docs-imagebox img="/img/docs/v3/playlist.png" max-width="25rem" >}}
|
||||
{{< docs-imagebox img="/img/docs/v3/playlist.png" max-width="25rem" class="docs-image--right">}}
|
||||
|
||||
The Playlist feature can be accessed from Grafana's sidemenu, in the Dashboard submenu.
|
||||
|
||||
@ -43,3 +43,25 @@ Playlists can also be manually controlled utilizing the Playlist controls at the
|
||||
Click the stop button to stop the Playlist, and exit to the current Dashboard.
|
||||
Click the next button to advance to the next Dashboard in the Playlist.
|
||||
Click the back button to rewind to the previous Dashboard in the Playlist.
|
||||
|
||||
## TV or Kiosk Mode
|
||||
|
||||
In TV mode the top navbar, row & panel controls will all fade to transparent.
|
||||
|
||||
This happens automatically after one minute of user inactivity but can also be toggled manually
|
||||
with the `d v` sequence shortcut. Any mouse movement or keyboard action will
|
||||
restore navbar & controls.
|
||||
|
||||
Another feature is the kiosk mode - in kiosk mode the navbar is completely hidden/removed from view. This can be enabled with the `d k`
|
||||
shortcut.
|
||||
|
||||
To put a playlist into kiosk mode, use the `d k` shortcut after the playlist has started. The same shortcut will toggle the playlist out of kiosk mode.
|
||||
|
||||
### Linking to the Playlist in Kiosk Mode
|
||||
|
||||
If you want to create a link to the playlist with kiosk mode enabled:
|
||||
|
||||
1. Copy the Start Url (by right clicking on the Play button and choosing Copy link address).
|
||||
2. Add the `?kiosk` parameter to the url.
|
||||
|
||||
For example, to open the first playlist on the Grafana Play site in kiosk mode: [http://play.grafana.org/playlists/play/1?kiosk](http://play.grafana.org/playlists/play/1?kiosk)
|
||||
|
@ -74,7 +74,9 @@ If you do not get an image when opening this link verify that the required font
|
||||
|
||||
### Grafana API Key
|
||||
|
||||
<img src="/img/docs/v2/orgdropdown_api_keys.png" style="width: 150px" class="right"></img>
|
||||
<!--<img src="/img/docs/v2/orgdropdown_api_keys.png" style="width: 150px" class="right"></img>-->
|
||||
{{< docs-imagebox img="/img/docs/v2/orgdropdown_api_keys.png" max-width="150px" class="docs-image--right">}}
|
||||
|
||||
You need to set the environment variable `HUBOT_GRAFANA_API_KEY` to a Grafana API Key.
|
||||
You can add these from the API Keys page which you find in the Organization dropdown.
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
"company": "Grafana Labs"
|
||||
},
|
||||
"name": "grafana",
|
||||
"version": "4.5.0-beta1",
|
||||
"version": "4.5.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/grafana/grafana.git"
|
||||
@ -53,10 +53,6 @@
|
||||
"systemjs": "0.19.41",
|
||||
"zone.js": "^0.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x",
|
||||
"npm": "2.14.x"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "./node_modules/grunt-cli/bin/grunt",
|
||||
"test": "./node_modules/grunt-cli/bin/grunt test",
|
||||
|
@ -29,7 +29,7 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
|
||||
return ApiError(400, "Query missing datasourceId", nil)
|
||||
}
|
||||
|
||||
dsQuery := models.GetDataSourceByIdQuery{Id: dsId}
|
||||
dsQuery := models.GetDataSourceByIdQuery{Id: dsId, OrgId: c.OrgId}
|
||||
if err := bus.Dispatch(&dsQuery); err != nil {
|
||||
return ApiError(500, "failed to fetch data source", err)
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
gocontext "context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/null"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
@ -112,6 +114,10 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *
|
||||
|
||||
resp, err := c.HandleRequest(context.Ctx, req)
|
||||
if err != nil {
|
||||
if err == gocontext.DeadlineExceeded {
|
||||
return nil, fmt.Errorf("Alert execution exceeded the timeout")
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("tsdb.HandleRequest() error %v", err)
|
||||
}
|
||||
|
||||
|
@ -27,12 +27,13 @@ func Decrypt(payload []byte, secret string) ([]byte, error) {
|
||||
}
|
||||
iv := payload[saltLength : saltLength+aes.BlockSize]
|
||||
payload = payload[saltLength+aes.BlockSize:]
|
||||
payloadDst := make([]byte, len(payload))
|
||||
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
|
||||
// XORKeyStream can work in-place if the two arguments are the same.
|
||||
stream.XORKeyStream(payload, payload)
|
||||
return payload, nil
|
||||
stream.XORKeyStream(payloadDst, payload)
|
||||
return payloadDst, nil
|
||||
}
|
||||
|
||||
func Encrypt(payload []byte, secret string) ([]byte, error) {
|
||||
|
@ -40,11 +40,11 @@ const DEFAULT_MAX_LINES = 10;
|
||||
const DEFAULT_TAB_SIZE = 2;
|
||||
const DEFAULT_BEHAVIOURS = true;
|
||||
|
||||
const GRAFANA_MODULES = ['mode-prometheus', 'snippets-prometheus', 'theme-grafana-dark'];
|
||||
const GRAFANA_MODULES = ['theme-grafana-dark'];
|
||||
const GRAFANA_MODULE_BASE = "public/app/core/components/code_editor/";
|
||||
|
||||
// Trick for loading additional modules
|
||||
function setModuleUrl(moduleType, name) {
|
||||
function setModuleUrl(moduleType, name, pluginBaseUrl = null) {
|
||||
let baseUrl = ACE_SRC_BASE;
|
||||
let aceModeName = `ace/${moduleType}/${name}`;
|
||||
let moduleName = `${moduleType}-${name}`;
|
||||
@ -54,6 +54,10 @@ function setModuleUrl(moduleType, name) {
|
||||
baseUrl = GRAFANA_MODULE_BASE;
|
||||
}
|
||||
|
||||
if (pluginBaseUrl) {
|
||||
baseUrl = pluginBaseUrl + '/';
|
||||
}
|
||||
|
||||
if (moduleType === 'snippets') {
|
||||
componentName = `${moduleType}/${name}.js`;
|
||||
}
|
||||
@ -111,6 +115,17 @@ function link(scope, elem, attrs) {
|
||||
let textarea = elem.find("textarea");
|
||||
textarea.addClass('gf-form-input');
|
||||
|
||||
if (scope.codeEditorFocus) {
|
||||
setTimeout(function () {
|
||||
textarea.focus();
|
||||
var domEl = textarea[0];
|
||||
if (domEl.setSelectionRange) {
|
||||
var pos = textarea.val().length * 2;
|
||||
domEl.setSelectionRange(pos, pos);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
editorSession.on('change', (e) => {
|
||||
scope.$apply(() => {
|
||||
@ -148,8 +163,8 @@ function link(scope, elem, attrs) {
|
||||
|
||||
function setLangMode(lang) {
|
||||
let aceModeName = `ace/mode/${lang}`;
|
||||
setModuleUrl("mode", lang);
|
||||
setModuleUrl("snippets", lang);
|
||||
setModuleUrl("mode", lang, scope.datasource.meta.baseUrl || null);
|
||||
setModuleUrl("snippets", lang, scope.datasource.meta.baseUrl || null);
|
||||
editorSession.setMode(aceModeName);
|
||||
|
||||
ace.config.loadModule("ace/ext/language_tools", (language_tools) => {
|
||||
@ -199,6 +214,8 @@ export function codeEditorDirective() {
|
||||
template: editorTemplate,
|
||||
scope: {
|
||||
content: "=",
|
||||
datasource: "=",
|
||||
codeEditorFocus: "<",
|
||||
onChange: "&",
|
||||
getCompleter: "&"
|
||||
},
|
||||
|
@ -16,7 +16,7 @@ export class AlertSrv {
|
||||
|
||||
init() {
|
||||
this.$rootScope.onAppEvent('alert-error', (e, alert) => {
|
||||
this.set(alert[0], alert[1], 'error', 7000);
|
||||
this.set(alert[0], alert[1], 'error', 12000);
|
||||
}, this.$rootScope);
|
||||
|
||||
this.$rootScope.onAppEvent('alert-warning', (e, alert) => {
|
||||
@ -33,6 +33,14 @@ export class AlertSrv {
|
||||
appEvents.on('confirm-modal', this.showConfirmModal.bind(this));
|
||||
}
|
||||
|
||||
getIconForSeverity(severity) {
|
||||
switch (severity) {
|
||||
case 'success': return 'fa fa-check';
|
||||
case 'error': return 'fa fa-exclamation-triangle';
|
||||
default: return 'fa fa-exclamation';
|
||||
}
|
||||
}
|
||||
|
||||
set(title, text, severity, timeout) {
|
||||
if (_.isObject(text)) {
|
||||
console.log('alert error', text);
|
||||
@ -45,6 +53,7 @@ export class AlertSrv {
|
||||
title: title || '',
|
||||
text: text || '',
|
||||
severity: severity || 'info',
|
||||
icon: this.getIconForSeverity(severity)
|
||||
};
|
||||
|
||||
var newAlertJson = angular.toJson(newAlert);
|
||||
|
@ -64,7 +64,13 @@ export class BackendSrv {
|
||||
}
|
||||
|
||||
if (data.message) {
|
||||
this.alertSrv.set("Problem!", data.message, data.severity, 10000);
|
||||
let description = "";
|
||||
let message = data.message;
|
||||
if (message.length > 80) {
|
||||
description = message;
|
||||
message = "Error";
|
||||
}
|
||||
this.alertSrv.set(message, description, data.severity, 10000);
|
||||
}
|
||||
|
||||
throw data;
|
||||
@ -97,7 +103,7 @@ export class BackendSrv {
|
||||
return results.data;
|
||||
}, err => {
|
||||
// handle unauthorized
|
||||
if (err.status === 401 && firstAttempt) {
|
||||
if (err.status === 401 && this.contextSrv.user.isSignedIn && firstAttempt) {
|
||||
return this.loginPing().then(() => {
|
||||
options.retry = 1;
|
||||
return this.request(options);
|
||||
|
@ -17,90 +17,87 @@ function($, _) {
|
||||
kbn.round_interval = function(interval) {
|
||||
switch (true) {
|
||||
// 0.015s
|
||||
case (interval <= 15):
|
||||
case (interval < 15):
|
||||
return 10; // 0.01s
|
||||
// 0.035s
|
||||
case (interval <= 35):
|
||||
case (interval < 35):
|
||||
return 20; // 0.02s
|
||||
// 0.075s
|
||||
case (interval <= 75):
|
||||
case (interval < 75):
|
||||
return 50; // 0.05s
|
||||
// 0.15s
|
||||
case (interval <= 150):
|
||||
case (interval < 150):
|
||||
return 100; // 0.1s
|
||||
// 0.35s
|
||||
case (interval <= 350):
|
||||
case (interval < 350):
|
||||
return 200; // 0.2s
|
||||
// 0.75s
|
||||
case (interval <= 750):
|
||||
case (interval < 750):
|
||||
return 500; // 0.5s
|
||||
// 1.5s
|
||||
case (interval <= 1500):
|
||||
case (interval < 1500):
|
||||
return 1000; // 1s
|
||||
// 3.5s
|
||||
case (interval <= 3500):
|
||||
case (interval < 3500):
|
||||
return 2000; // 2s
|
||||
// 7.5s
|
||||
case (interval <= 7500):
|
||||
case (interval < 7500):
|
||||
return 5000; // 5s
|
||||
// 12.5s
|
||||
case (interval <= 12500):
|
||||
case (interval < 12500):
|
||||
return 10000; // 10s
|
||||
// 17.5s
|
||||
case (interval <= 17500):
|
||||
case (interval < 17500):
|
||||
return 15000; // 15s
|
||||
// 25s
|
||||
case (interval <= 25000):
|
||||
case (interval < 25000):
|
||||
return 20000; // 20s
|
||||
// 45s
|
||||
case (interval <= 45000):
|
||||
case (interval < 45000):
|
||||
return 30000; // 30s
|
||||
// 1.5m
|
||||
case (interval <= 90000):
|
||||
case (interval < 90000):
|
||||
return 60000; // 1m
|
||||
// 3.5m
|
||||
case (interval <= 210000):
|
||||
case (interval < 210000):
|
||||
return 120000; // 2m
|
||||
// 7.5m
|
||||
case (interval <= 450000):
|
||||
case (interval < 450000):
|
||||
return 300000; // 5m
|
||||
// 12.5m
|
||||
case (interval <= 750000):
|
||||
case (interval < 750000):
|
||||
return 600000; // 10m
|
||||
// 12.5m
|
||||
case (interval <= 1050000):
|
||||
case (interval < 1050000):
|
||||
return 900000; // 15m
|
||||
// 25m
|
||||
case (interval <= 1500000):
|
||||
case (interval < 1500000):
|
||||
return 1200000; // 20m
|
||||
// 45m
|
||||
case (interval <= 2700000):
|
||||
case (interval < 2700000):
|
||||
return 1800000; // 30m
|
||||
// 1.5h
|
||||
case (interval <= 5400000):
|
||||
case (interval < 5400000):
|
||||
return 3600000; // 1h
|
||||
// 2.5h
|
||||
case (interval <= 9000000):
|
||||
case (interval < 9000000):
|
||||
return 7200000; // 2h
|
||||
// 4.5h
|
||||
case (interval <= 16200000):
|
||||
case (interval < 16200000):
|
||||
return 10800000; // 3h
|
||||
// 9h
|
||||
case (interval <= 32400000):
|
||||
case (interval < 32400000):
|
||||
return 21600000; // 6h
|
||||
// 24h
|
||||
case (interval <= 86400000):
|
||||
// 1d
|
||||
case (interval < 86400000):
|
||||
return 43200000; // 12h
|
||||
// 48h
|
||||
case (interval <= 172800000):
|
||||
return 86400000; // 24h
|
||||
// 1w
|
||||
case (interval <= 604800000):
|
||||
return 86400000; // 24h
|
||||
case (interval < 604800000):
|
||||
return 86400000; // 1d
|
||||
// 3w
|
||||
case (interval <= 1814400000):
|
||||
case (interval < 1814400000):
|
||||
return 604800000; // 1w
|
||||
// 2y
|
||||
// 6w
|
||||
case (interval < 3628800000):
|
||||
return 2592000000; // 30d
|
||||
default:
|
||||
@ -134,7 +131,7 @@ function($, _) {
|
||||
return nummilliseconds + 'ms';
|
||||
}
|
||||
|
||||
return 'less then a millisecond'; //'just now' //or other string you like;
|
||||
return 'less than a millisecond'; //'just now' //or other string you like;
|
||||
};
|
||||
|
||||
kbn.to_percent = function(number,outof) {
|
||||
|
@ -83,7 +83,7 @@ export class DashboardSrv {
|
||||
}
|
||||
|
||||
this.$rootScope.appEvent('dashboard-saved', this.dash);
|
||||
this.$rootScope.appEvent('alert-success', ['Dashboard saved', 'Saved as ' + clone.title]);
|
||||
this.$rootScope.appEvent('alert-success', ['Dashboard saved']);
|
||||
}
|
||||
|
||||
save(clone, options) {
|
||||
|
@ -116,16 +116,14 @@ class TimeSrv {
|
||||
|
||||
setAutoRefresh(interval) {
|
||||
this.dashboard.refresh = interval;
|
||||
this.cancelNextRefresh();
|
||||
if (interval) {
|
||||
var intervalMs = kbn.interval_to_ms(interval);
|
||||
|
||||
this.$timeout(() => {
|
||||
this.refreshTimer = this.timer.register(this.$timeout(() => {
|
||||
this.startNextRefreshTimer(intervalMs);
|
||||
this.refreshDashboard();
|
||||
}, intervalMs);
|
||||
|
||||
} else {
|
||||
this.cancelNextRefresh();
|
||||
}, intervalMs));
|
||||
}
|
||||
|
||||
// update url
|
||||
|
@ -9,7 +9,7 @@ export class MetricsTabCtrl {
|
||||
panel: any;
|
||||
panelCtrl: any;
|
||||
datasources: any[];
|
||||
current: any;
|
||||
datasourceInstance: any;
|
||||
nextRefId: string;
|
||||
dashboard: DashboardModel;
|
||||
panelDsValue: any;
|
||||
@ -29,23 +29,26 @@ export class MetricsTabCtrl {
|
||||
this.panel = this.panelCtrl.panel;
|
||||
this.dashboard = this.panelCtrl.dashboard;
|
||||
this.datasources = datasourceSrv.getMetricSources();
|
||||
this.panelDsValue = this.panelCtrl.panel.datasource || null;
|
||||
this.panelDsValue = this.panelCtrl.panel.datasource;
|
||||
|
||||
for (let ds of this.datasources) {
|
||||
if (ds.value === this.panelDsValue) {
|
||||
this.current = ds;
|
||||
this.datasourceInstance = ds;
|
||||
}
|
||||
}
|
||||
|
||||
this.addQueryDropdown = {text: 'Add Query', value: null, fake: true};
|
||||
|
||||
// update next ref id
|
||||
this.panelCtrl.nextRefId = this.dashboard.getNextQueryLetter(this.panel);
|
||||
this.updateDatasourceOptions();
|
||||
}
|
||||
|
||||
updateDatasourceOptions() {
|
||||
this.hasQueryHelp = this.current.meta.hasQueryHelp;
|
||||
this.queryOptions = this.current.meta.queryOptions;
|
||||
if (this.datasourceInstance) {
|
||||
this.hasQueryHelp = this.datasourceInstance.meta.hasQueryHelp;
|
||||
this.queryOptions = this.datasourceInstance.meta.queryOptions;
|
||||
}
|
||||
}
|
||||
|
||||
getOptions(includeBuiltin) {
|
||||
@ -61,7 +64,7 @@ export class MetricsTabCtrl {
|
||||
return;
|
||||
}
|
||||
|
||||
this.current = option.datasource;
|
||||
this.datasourceInstance = option.datasource;
|
||||
this.panelCtrl.setDatasource(option.datasource);
|
||||
this.updateDatasourceOptions();
|
||||
}
|
||||
@ -85,7 +88,7 @@ export class MetricsTabCtrl {
|
||||
this.queryTroubleshooterOpen = false;
|
||||
this.helpOpen = !this.helpOpen;
|
||||
|
||||
this.backendSrv.get(`/api/plugins/${this.current.meta.id}/markdown/query_help`).then(res => {
|
||||
this.backendSrv.get(`/api/plugins/${this.datasourceInstance.meta.id}/markdown/query_help`).then(res => {
|
||||
var md = new Remarkable();
|
||||
this.helpHtml = this.$sce.trustAsHtml(md.render(res));
|
||||
});
|
||||
|
@ -73,7 +73,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="query-editor-rows gf-form-group">
|
||||
<div class="query-editor-rows gf-form-group" ng-if="ctrl.datasourceInstance">
|
||||
<div ng-repeat="target in ctrl.panel.targets" ng-class="{'gf-form-disabled': target.hide}">
|
||||
<rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
|
||||
<plugin-component type="query-ctrl">
|
||||
@ -89,11 +89,11 @@
|
||||
</span>
|
||||
<span class="gf-form-query-letter-cell-letter">{{ctrl.panelCtrl.nextRefId}}</span>
|
||||
</label>
|
||||
<button class="btn btn-secondary gf-form-btn" ng-click="ctrl.addQuery()" ng-hide="ctrl.current.meta.mixed">
|
||||
<button class="btn btn-secondary gf-form-btn" ng-click="ctrl.addQuery()" ng-hide="ctrl.datasourceInstance.meta.mixed">
|
||||
Add Query
|
||||
</button>
|
||||
|
||||
<div class="dropdown" ng-if="ctrl.current.meta.mixed">
|
||||
<div class="dropdown" ng-if="ctrl.datasourceInstance.meta.mixed">
|
||||
<gf-form-dropdown model="ctrl.addQueryDropdown" get-options="ctrl.getOptions(false)" on-change="ctrl.addMixedQuery($option)">
|
||||
</gf-form-dropdown>
|
||||
</div>
|
||||
|
@ -10,9 +10,11 @@ export class QueryCtrl {
|
||||
panel: any;
|
||||
hasRawMode: boolean;
|
||||
error: string;
|
||||
isLastQuery: boolean;
|
||||
|
||||
constructor(public $scope, private $injector) {
|
||||
this.panel = this.panelCtrl.panel;
|
||||
this.isLastQuery = _.indexOf(this.panel.targets, this.target) === (this.panel.targets.length - 1);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
|
@ -126,21 +126,18 @@ export class DataSourceEditCtrl {
|
||||
return;
|
||||
}
|
||||
|
||||
this.testing = {done: false};
|
||||
this.testing = {done: false, status: 'error'};
|
||||
|
||||
// make test call in no backend cache context
|
||||
this.backendSrv.withNoBackendCache(() => {
|
||||
return datasource.testDatasource().then(result => {
|
||||
this.testing.message = result.message;
|
||||
this.testing.status = result.status;
|
||||
this.testing.title = result.title;
|
||||
}).catch(err => {
|
||||
if (err.statusText) {
|
||||
this.testing.message = err.statusText;
|
||||
this.testing.title = "HTTP Error";
|
||||
this.testing.message = 'HTTP Error ' + err.statusText;
|
||||
} else {
|
||||
this.testing.message = err.message;
|
||||
this.testing.title = "Unknown error";
|
||||
}
|
||||
});
|
||||
}).finally(() => {
|
||||
|
@ -59,9 +59,14 @@
|
||||
|
||||
<div ng-if="ctrl.testing" class="gf-form-group">
|
||||
<h5 ng-show="!ctrl.testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>
|
||||
<div class="alert-{{ctrl.testing.status}} alert">
|
||||
<div class="alert-title">{{ctrl.testing.title}}</div>
|
||||
<div ng-bind='ctrl.testing.message'></div>
|
||||
<div class="alert-{{ctrl.testing.status}} alert" ng-show="ctrl.testing.done">
|
||||
<div class="alert-icon">
|
||||
<i class="fa fa-exclamation-triangle" ng-show="ctrl.testing.status === 'error'"></i>
|
||||
<i class="fa fa-check" ng-show="ctrl.testing.status !== 'error'"></i>
|
||||
</div>
|
||||
<div class="alert-body">
|
||||
<div class="alert-title">{{ctrl.testing.message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -335,7 +335,7 @@ function (angular, _, moment, dateMath, kbn, templatingVariable, CloudWatchAnnot
|
||||
var dimensions = {};
|
||||
|
||||
return this.getDimensionValues(region, namespace, metricName, 'ServiceName', dimensions).then(function () {
|
||||
return { status: 'success', message: 'Data source is working', title: 'Success' };
|
||||
return { status: 'success', message: 'Data source is working' };
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -175,9 +175,9 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
|
||||
return this.getFields({type: 'date'}).then(function(dateFields) {
|
||||
var timeField = _.find(dateFields, {text: this.timeField});
|
||||
if (!timeField) {
|
||||
return { status: "error", message: "No date field named " + this.timeField + ' found', title: "Error" };
|
||||
return { status: "error", message: "No date field named " + this.timeField + ' found' };
|
||||
}
|
||||
return { status: "success", message: "Index OK. Time field name OK.", title: "Success" };
|
||||
return { status: "success", message: "Index OK. Time field name OK." };
|
||||
}.bind(this), function(err) {
|
||||
console.log(err);
|
||||
if (err.data && err.data.error) {
|
||||
@ -185,9 +185,9 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
|
||||
if (err.data.error.reason) {
|
||||
message = err.data.error.reason;
|
||||
}
|
||||
return { status: "error", message: message, title: "Error" };
|
||||
return { status: "error", message: message };
|
||||
} else {
|
||||
return { status: "error", message: err.status, title: "Error" };
|
||||
return { status: "error", message: err.status };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -205,7 +205,7 @@ export function GraphiteDatasource(instanceSettings, $q, backendSrv, templateSrv
|
||||
|
||||
this.testDatasource = function() {
|
||||
return this.metricFindQuery('*').then(function () {
|
||||
return { status: "success", message: "Data source is working", title: "Success" };
|
||||
return { status: "success", message: "Data source is working"};
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -24,6 +24,11 @@ describe('GraphiteQueryCtrl', function() {
|
||||
ctx.target = {target: 'aliasByNode(scaleToSeconds(test.prod.*,1),2)'};
|
||||
ctx.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
|
||||
ctx.panelCtrl = {panel: {}};
|
||||
ctx.panelCtrl = {
|
||||
panel: {
|
||||
targets: [ctx.target]
|
||||
}
|
||||
};
|
||||
ctx.panelCtrl.refresh = sinon.spy();
|
||||
|
||||
ctx.ctrl = $controller(GraphiteQueryCtrl, {$scope: ctx.scope}, {
|
||||
|
@ -196,11 +196,11 @@ export default class InfluxDatasource {
|
||||
return this.metricFindQuery('SHOW DATABASES').then(res => {
|
||||
let found = _.find(res, {text: this.database});
|
||||
if (!found) {
|
||||
return { status: "error", message: "Could not find the specified database name.", title: "DB Not found" };
|
||||
return { status: "error", message: "Could not find the specified database name." };
|
||||
}
|
||||
return { status: "success", message: "Data source is working", title: "Success" };
|
||||
return { status: "success", message: "Data source is working" };
|
||||
}).catch(err => {
|
||||
return { status: "error", message: err.message, title: "Test Failed" };
|
||||
return { status: "error", message: err.message };
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@
|
||||
<div class="gf-form-inline" ng-if="ctrl.target.orderByTime === 'DESC'">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">ORDER BY</label>
|
||||
<label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()">time <span classs="query-keyword">DESC</span> <i class="fa fa-remove"></i></label>
|
||||
<label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()">time <span class="query-keyword">DESC</span> <i class="fa fa-remove"></i></label>
|
||||
</div>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
|
@ -19,9 +19,13 @@ describe('InfluxDBQueryCtrl', function() {
|
||||
ctx.$q = $q;
|
||||
ctx.scope = $rootScope.$new();
|
||||
ctx.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
|
||||
ctx.panelCtrl = {panel: {}};
|
||||
ctx.panelCtrl.refresh = sinon.spy();
|
||||
ctx.target = {target: {}};
|
||||
ctx.panelCtrl = {
|
||||
panel: {
|
||||
targets: [ctx.target]
|
||||
}
|
||||
};
|
||||
ctx.panelCtrl.refresh = sinon.spy();
|
||||
ctx.ctrl = $controller(InfluxQueryCtrl, {$scope: ctx.scope}, {
|
||||
panelCtrl: ctx.panelCtrl,
|
||||
target: ctx.target,
|
||||
|
@ -118,13 +118,13 @@ export class MysqlDatasource {
|
||||
}],
|
||||
}
|
||||
}).then(res => {
|
||||
return { status: "success", message: "Database Connection OK", title: "Success" };
|
||||
return { status: "success", message: "Database Connection OK"};
|
||||
}).catch(err => {
|
||||
console.log(err);
|
||||
if (err.data && err.data.message) {
|
||||
return { status: "error", message: err.data.message, title: "Error" };
|
||||
return { status: "error", message: err.data.message };
|
||||
} else {
|
||||
return { status: "error", message: err.status, title: "Error" };
|
||||
return { status: "error", message: err.status };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -296,7 +296,7 @@ function (angular, _, dateMath) {
|
||||
|
||||
this.testDatasource = function() {
|
||||
return this._performSuggestQuery('cpu', 'metrics').then(function () {
|
||||
return { status: "success", message: "Data source is working", title: "Success" };
|
||||
return { status: "success", message: "Data source is working" };
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -18,7 +18,11 @@ describe('OpenTsQueryCtrl', function() {
|
||||
ctx.$q = $q;
|
||||
ctx.scope = $rootScope.$new();
|
||||
ctx.target = {target: ''};
|
||||
ctx.panelCtrl = {panel: {}};
|
||||
ctx.panelCtrl = {
|
||||
panel: {
|
||||
targets: [ctx.target]
|
||||
}
|
||||
};
|
||||
ctx.panelCtrl.refresh = sinon.spy();
|
||||
ctx.datasource.getAggregators = sinon.stub().returns(ctx.$q.when([]));
|
||||
ctx.datasource.getFilterTypes = sinon.stub().returns(ctx.$q.when([]));
|
||||
|
@ -241,7 +241,7 @@ export class PrometheusDatasource {
|
||||
|
||||
testDatasource() {
|
||||
return this.metricFindQuery('metrics(.*)').then(function() {
|
||||
return { status: 'success', message: 'Data source is working', title: 'Success' };
|
||||
return { status: 'success', message: 'Data source is working'};
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -65,20 +65,20 @@ var PrometheusHighlightRules = function() {
|
||||
regex : "\\s+"
|
||||
} ],
|
||||
"start-label-matcher" : [ {
|
||||
token : "label.name",
|
||||
token : "keyword",
|
||||
regex : '[a-zA-Z_][a-zA-Z0-9_]*'
|
||||
}, {
|
||||
token : "label.matching_operator",
|
||||
regex : '=|!=|=~|!~'
|
||||
token : "keyword.operator",
|
||||
regex : '=~|=|!~|!='
|
||||
}, {
|
||||
token : "label.value",
|
||||
token : "string",
|
||||
regex : '"[^"]*"|\'[^\']*\''
|
||||
}, {
|
||||
token : "label.matching_delimiter",
|
||||
token : "punctuation.operator",
|
||||
regex : ",",
|
||||
push : 'start-label-matcher'
|
||||
}, {
|
||||
token : "label.matching_end",
|
||||
token : "paren.rparen",
|
||||
regex : "}",
|
||||
next : "start"
|
||||
} ]
|
@ -1,8 +1,8 @@
|
||||
<query-editor-row query-ctrl="ctrl" can-collapse="true" has-text-edit-mode="false">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form gf-form--grow">
|
||||
<code-editor content="ctrl.target.expr" on-change="ctrl.refreshMetricData()"
|
||||
get-completer="ctrl.getCompleter()" data-mode="prometheus">
|
||||
<code-editor content="ctrl.target.expr" datasource="ctrl.datasource" on-change="ctrl.refreshMetricData()"
|
||||
get-completer="ctrl.getCompleter()" data-mode="prometheus" code-editor-focus="ctrl.isLastQuery">
|
||||
</code-editor>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,27 @@
|
||||
import {describe, beforeEach, it, sinon, expect} from 'test/lib/common';
|
||||
|
||||
import {PromCompleter} from '../completer';
|
||||
import {PrometheusDatasource} from '../datasource';
|
||||
|
||||
describe('Prometheus editor completer', function() {
|
||||
|
||||
let editor = {};
|
||||
let session = {
|
||||
getTokenAt: sinon.stub().returns({}),
|
||||
getLine: sinon.stub().returns(""),
|
||||
};
|
||||
|
||||
let datasourceStub = <PrometheusDatasource>{};
|
||||
let completer = new PromCompleter(datasourceStub);
|
||||
|
||||
describe("When inside brackets", () => {
|
||||
|
||||
it("Should return range vectors", () => {
|
||||
completer.getCompletions(editor, session, 10, "[", (s, res) => {
|
||||
expect(res[0]).to.eql({caption: '1s', value: '[1s', meta: 'range vector'});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -48,26 +48,14 @@
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-6">Mode</label>
|
||||
<div class="gf-form-select-wrapper max-width-15">
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.xaxis.mode" ng-options="v as k for (k, v) in ctrl.xAxisModes" ng-change="ctrl.xAxisOptionChanged()"> </select>
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.xaxis.mode" ng-options="v as k for (k, v) in ctrl.xAxisModes" ng-change="ctrl.xAxisModeChanged()"> </select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Table mode -->
|
||||
<div class="gf-form" ng-if="ctrl.panel.xaxis.mode === 'field'">
|
||||
<label class="gf-form-label width-6">Name</label>
|
||||
<metric-segment-model property="ctrl.panel.xaxis.name" get-options="ctrl.getDataFieldNames(false)" on-change="ctrl.xAxisOptionChanged()" custom="false" css-class="width-10" select-mode="true"></metric-segment-model>
|
||||
</div>
|
||||
|
||||
<!-- Series mode -->
|
||||
<div class="gf-form" ng-if="ctrl.panel.xaxis.mode === 'field'">
|
||||
<label class="gf-form-label width-6">Value</label>
|
||||
<metric-segment-model property="ctrl.panel.xaxis.values[0]" get-options="ctrl.getDataFieldNames(true)" on-change="ctrl.xAxisOptionChanged()" custom="false" css-class="width-10" select-mode="true"></metric-segment-model>
|
||||
</div>
|
||||
|
||||
<!-- Series mode -->
|
||||
<div class="gf-form" ng-if="ctrl.panel.xaxis.mode === 'series'">
|
||||
<label class="gf-form-label width-6">Value</label>
|
||||
<metric-segment-model property="ctrl.panel.xaxis.values[0]" options="ctrl.xAxisStatOptions" on-change="ctrl.xAxisOptionChanged()" custom="false" css-class="width-10" select-mode="true"></metric-segment-model>
|
||||
<metric-segment-model property="ctrl.panel.xaxis.values[0]" options="ctrl.xAxisStatOptions" on-change="ctrl.xAxisValueChanged()" custom="false" css-class="width-10" select-mode="true"></metric-segment-model>
|
||||
</div>
|
||||
|
||||
<!-- Histogram mode -->
|
||||
|
@ -59,10 +59,12 @@ export class AxesEditorCtrl {
|
||||
this.panelCtrl.render();
|
||||
}
|
||||
|
||||
xAxisOptionChanged() {
|
||||
if (!this.panel.xaxis.values || !this.panel.xaxis.values[0]){
|
||||
this.panelCtrl.processor.setPanelDefaultsForNewXAxisMode();
|
||||
}
|
||||
xAxisModeChanged() {
|
||||
this.panelCtrl.processor.setPanelDefaultsForNewXAxisMode();
|
||||
this.panelCtrl.onDataReceived(this.panelCtrl.dataList);
|
||||
}
|
||||
|
||||
xAxisValueChanged() {
|
||||
this.panelCtrl.onDataReceived(this.panelCtrl.dataList);
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">Mode</span>
|
||||
<span class="gf-form-select-wrapper">
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.mode" ng-options="f for f in ['html','markdown','text']"></select>
|
||||
<select class="gf-form-input" ng-model="ctrl.panel.mode" ng-options="f for f in ['html','markdown']"></select>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -103,7 +103,7 @@ $tight-form-func-bg: #333;
|
||||
$tight-form-func-highlight-bg: #444;
|
||||
|
||||
$modal-background: $black;
|
||||
$code-tag-bg: $dark-5;
|
||||
$code-tag-bg: $gray-1;
|
||||
$code-tag-border: lighten($code-tag-bg, 2%);
|
||||
|
||||
|
||||
@ -238,17 +238,15 @@ $paginationActiveBackground: $blue;
|
||||
|
||||
// Form states and alerts
|
||||
// -------------------------
|
||||
$state-warning-text: $warn;
|
||||
$state-warning-bg: $brand-warning;
|
||||
$warning-text-color: $warn;
|
||||
$error-text-color: #E84D4D;
|
||||
$success-text-color: #12D95A;
|
||||
$info-text-color: $blue-dark;
|
||||
|
||||
$errorText: #E84D4D;
|
||||
$errorBackground: $btn-danger-bg;
|
||||
|
||||
$successText: #12D95A;
|
||||
$successBackground: $btn-success-bg;
|
||||
|
||||
$infoText: $blue-dark;
|
||||
$infoBackground: $blue-dark;
|
||||
$alert-error-bg: linear-gradient(90deg, #d44939, #e0603d);
|
||||
$alert-success-bg: linear-gradient(90deg, #3aa655, #47b274);
|
||||
$alert-warning-bg: linear-gradient(90deg, #d44939, #e0603d);
|
||||
$alert-info-bg: linear-gradient(100deg, #1a4552, #00374a);
|
||||
|
||||
// popover
|
||||
$popover-bg: $panel-bg;
|
||||
@ -258,6 +256,8 @@ $popover-border-color: $gray-1;
|
||||
$popover-help-bg: $btn-secondary-bg;
|
||||
$popover-help-color: $text-color;
|
||||
|
||||
$popover-error-bg: $btn-danger-bg;
|
||||
|
||||
// Tooltips and popovers
|
||||
// -------------------------
|
||||
$tooltipColor: $popover-help-color;
|
||||
@ -276,7 +276,7 @@ $card-background-hover: linear-gradient(135deg, #343434, #262626);
|
||||
$card-shadow: -1px -1px 0 0 hsla(0, 0%, 100%, .1), 1px 1px 0 0 rgba(0, 0, 0, .3);
|
||||
|
||||
// info box
|
||||
$info-box-background: linear-gradient(100deg, #1a4552, #0b2127);
|
||||
$info-box-background: linear-gradient(100deg, #1a4552, #00374a);
|
||||
|
||||
// footer
|
||||
$footer-link-color: $gray-1;
|
||||
|
@ -259,29 +259,23 @@ $paginationActiveBackground: $blue;
|
||||
|
||||
// Form states and alerts
|
||||
// -------------------------
|
||||
$state-warning-text: lighten($orange, 10%);
|
||||
$state-warning-bg: $orange;
|
||||
$warningBorder: transparent;
|
||||
$warning-text-color: lighten($orange, 10%);
|
||||
$error-text-color: lighten($red, 10%);
|
||||
$success-text-color: lighten($green, 10%);
|
||||
$info-text-color: $blue;
|
||||
|
||||
$errorText: lighten($red, 10%);
|
||||
$errorBackground: $red;
|
||||
$errorBorder: transparent;
|
||||
|
||||
$successText: lighten($green, 10%);
|
||||
$successBackground: $green;
|
||||
$successBorder: transparent;
|
||||
|
||||
$infoText: $blue;
|
||||
$infoBackground: $blue-dark;
|
||||
$infoBorder: transparent;
|
||||
$alert-error-bg: linear-gradient(90deg, #d44939, #e0603d);
|
||||
$alert-success-bg: linear-gradient(90deg, #3aa655, #47b274);
|
||||
$alert-warning-bg: linear-gradient(90deg, #d44939, #e0603d);
|
||||
$alert-info-bg: $blue-dark;
|
||||
|
||||
// popover
|
||||
$popover-bg: $gray-5;
|
||||
$popover-color: $text-color;
|
||||
$popover-border-color: $gray-3;
|
||||
|
||||
$popover-help-bg: $blue-dark;
|
||||
$popover-help-color: $gray-6;
|
||||
$popover-error-bg: $btn-danger-bg;
|
||||
|
||||
// Tooltips and popovers
|
||||
// -------------------------
|
||||
|
@ -31,21 +31,21 @@ cite { font-style: normal; }
|
||||
a.muted:hover,
|
||||
a.muted:focus { color: darken($text-muted, 10%); }
|
||||
|
||||
.text-warning { color: $state-warning-text; }
|
||||
.text-warning { color: $warning-text-color; }
|
||||
a.text-warning:hover,
|
||||
a.text-warning:focus { color: darken($state-warning-text, 10%); }
|
||||
a.text-warning:focus { color: darken($warning-text-color, 10%); }
|
||||
|
||||
.text-error { color: $errorText; }
|
||||
.text-error { color: $error-text-color; }
|
||||
a.text-error:hover,
|
||||
a.text-error:focus { color: darken($errorText, 10%); }
|
||||
a.text-error:focus { color: darken($error-text-color, 10%); }
|
||||
|
||||
.text-info { color: $infoText; }
|
||||
.text-info { color: $info-text-color; }
|
||||
a.text-info:hover,
|
||||
a.text-info:focus { color: darken($infoText, 10%); }
|
||||
a.text-info:focus { color: darken($info-text-color, 10%); }
|
||||
|
||||
.text-success { color: $successText; }
|
||||
.text-success { color: $success-text-color; }
|
||||
a.text-success:hover,
|
||||
a.text-success:focus { color: darken($successText, 10%); }
|
||||
a.text-success:focus { color: darken($success-text-color, 10%); }
|
||||
a { cursor: pointer; }
|
||||
|
||||
a[disabled] {
|
||||
@ -130,7 +130,7 @@ small,
|
||||
mark,
|
||||
.mark {
|
||||
padding: .2em;
|
||||
background-color: $state-warning-bg;
|
||||
background: $alert-warning-bg;
|
||||
}
|
||||
|
||||
|
||||
@ -296,7 +296,7 @@ a.external-link {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
ul {
|
||||
ul, ol {
|
||||
padding-left: $spacer*1.5;
|
||||
margin-bottom: $spacer;
|
||||
}
|
||||
@ -328,7 +328,7 @@ a.external-link {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
ul:last-child {
|
||||
ul:last-child, ol:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
@ -7,60 +7,57 @@
|
||||
// -------------------------
|
||||
|
||||
.alert {
|
||||
padding: 0.5rem 2rem 0.5rem 1rem;
|
||||
padding: 1.25rem 2rem 1.25rem 1.5rem;
|
||||
margin-bottom: $line-height-base;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,.5);
|
||||
background-color: $state-warning-bg;
|
||||
text-shadow: 0 2px 0 rgba(255,255,255,.5);
|
||||
background: $alert-error-bg;
|
||||
position: relative;
|
||||
color: $white;
|
||||
text-shadow: 0 1px 0 rgba(0,0,0,.5);
|
||||
text-shadow: 0 1px 0 rgba(0,0,0,.2);
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
// Alternate styles
|
||||
// -------------------------
|
||||
|
||||
.alert-success {
|
||||
background-color: $successBackground;
|
||||
background: $alert-success-bg;
|
||||
}
|
||||
|
||||
.alert-danger,
|
||||
.alert-error {
|
||||
background-color: $errorBackground;
|
||||
background: $alert-error-bg;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background-color: $infoBackground;
|
||||
background: $alert-info-bg;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background-color: $state-warning-bg;
|
||||
background: $alert-warning-bg;
|
||||
}
|
||||
|
||||
.page-alert-list {
|
||||
z-index: 8000;
|
||||
min-width: 300px;
|
||||
max-width: 300px;
|
||||
min-width: 400px;
|
||||
max-width: 600px;
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
top: 56px;
|
||||
right: 10px;
|
||||
top: 60px;
|
||||
}
|
||||
|
||||
.alert-close {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
right: -2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0;
|
||||
background: $white;
|
||||
border-radius: 50%;
|
||||
padding: 0 0 0 1rem;
|
||||
border: none;
|
||||
font-size: 1.1rem;
|
||||
color: $dark-4;
|
||||
background: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.fa {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
align-self: flex-end;
|
||||
font-size: 1.5rem;
|
||||
color: rgba(255,255,255,.75)
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,3 +65,18 @@
|
||||
font-weight: $font-weight-semi-bold;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.alert-icon {
|
||||
padding: 0 1rem 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.5rem;
|
||||
.fa {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.alert-body {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@
|
||||
background-color: $dropdownBackground !important;
|
||||
color: $dropdownLinkColor !important;
|
||||
border: 1px solid $dropdownBorder !important;
|
||||
width: 320px !important;
|
||||
width: 550px !important;
|
||||
|
||||
.ace_scroller {
|
||||
.ace_selected, .ace_active-line, .ace_line-hover {
|
||||
@ -77,3 +77,7 @@ $doc-font-size: $font-size-sm;
|
||||
.ace_tooltip {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.ace_hidden-cursors .ace_cursor {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ $useDropShadow: false;
|
||||
$attachmentOffset: 0%;
|
||||
$easing: cubic-bezier(0, 0, 0.265, 1.00);
|
||||
|
||||
@include drop-theme("error", $errorBackground, $popover-color);
|
||||
@include drop-theme("error", $popover-error-bg, $popover-color);
|
||||
@include drop-theme("popover", $popover-bg, $popover-color, $popover-border-color);
|
||||
@include drop-theme("help", $popover-help-bg, $popover-help-color);
|
||||
|
||||
|
@ -138,7 +138,7 @@ div.flot-text {
|
||||
&--error {
|
||||
display: block;
|
||||
color: $text-color;
|
||||
@include panel-corner-color($errorBackground);
|
||||
@include panel-corner-color($popover-error-bg);
|
||||
.fa:before {
|
||||
content: "\f12a";
|
||||
}
|
||||
|
@ -167,6 +167,20 @@ define([
|
||||
var res = kbn.calculateInterval(range, 900, '>15ms');
|
||||
expect(res.interval).to.be('15ms');
|
||||
});
|
||||
|
||||
it('1d 1 resolution', function() {
|
||||
var range = { from: dateMath.parse('now-1d'), to: dateMath.parse('now') };
|
||||
var res = kbn.calculateInterval(range, 1, null);
|
||||
expect(res.interval).to.be('1d');
|
||||
expect(res.intervalMs).to.be(86400000);
|
||||
});
|
||||
|
||||
it('86399s 1 resolution', function() {
|
||||
var range = { from: dateMath.parse('now-86390s'), to: dateMath.parse('now') };
|
||||
var res = kbn.calculateInterval(range, 1, null);
|
||||
expect(res.interval).to.be('12h');
|
||||
expect(res.intervalMs).to.be(43200000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hex', function() {
|
||||
|
@ -31,11 +31,14 @@
|
||||
|
||||
<div class="page-alert-list">
|
||||
<div ng-repeat='alert in dashAlerts.list' class="alert-{{alert.severity}} alert">
|
||||
<div class="alert-icon"><i class="{{alert.icon}}"></i></div>
|
||||
<div class="alert-body">
|
||||
<div class="alert-title">{{alert.title}}</div>
|
||||
<div class="alert-text" ng-bind='alert.text'></div>
|
||||
</div>
|
||||
<button type="button" class="alert-close" ng-click="dashAlerts.clear(alert)">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
<i class="fa fa fa-remove"></i>
|
||||
</button>
|
||||
<div class="alert-title">{{alert.title}}</div>
|
||||
<div ng-bind='alert.text'></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user