Merge branch 'master' into backend_plugins
* master: (48 commits) fix: unit test fixed prettier: change to single quoting ux: minor name change to search sections db: fix postgres regression when comparing boolean columns/values (#10303) dashboard: delete row improvements fix missing comma in documentation output example fix broken link (#10291) minor fixes and formatting after review dashfolders: use validation service for folder creation and dashboard import. #10197 dashfolders: support creating new folder when moving dashboards. #10197 dashfolders: support creating new folder when saving a dashboard. #10197 dashfolders: support creating new folder in dashboard settings. #10197 dashfolders: support creating new folder from the folder picker. #10197 tech: ran prettier on all scss files tech: ran pretttier on all typescript files search: closes dash search when selecting current dashboard (#10285) fix: Original dashboard link from snapshot should be an a-tag, not a button (#10269) (#10283) dashboard: fixes #10262 added new to new dahsboard and folder test: Update test with new component signature ...
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
# 5.0.0 (unreleased / master branch)
|
# 5.0.0 (unreleased / master branch)
|
||||||
|
|
||||||
|
Grafana v5.0 is going to be the biggest and most foundational release Grafana has ever had, coming with a ton of UX improvements, a new dashboard grid engine, dashboard folders, user teams and permissions. Checkout out this [video preview](https://www.youtube.com/watch?v=BC_YRNpqj5k) of Grafana v5.
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
- **Dashboards** Dashboard folders, [#1611](https://github.com/grafana/grafana/issues/1611)
|
- **Dashboards** Dashboard folders, [#1611](https://github.com/grafana/grafana/issues/1611)
|
||||||
- **Teams** User groups (teams) implemented. Can be used in folder & dashboard permission list.
|
- **Teams** User groups (teams) implemented. Can be used in folder & dashboard permission list.
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ Graphite, Elasticsearch, OpenTSDB, Prometheus and InfluxDB.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Grafana v5 Alpha Preview
|
||||||
|
Grafana master is now v5.0 alpha. This is going to be the biggest and most foundational release Grafana has ever had, coming with a ton of UX improvements, a new dashboard grid engine, dashboard folders, user teams and permissions. Checkout out this [video preview](https://www.youtube.com/watch?v=BC_YRNpqj5k) of Grafana v5.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Head to [docs.grafana.org](http://docs.grafana.org/installation/) and [download](https://grafana.com/get)
|
Head to [docs.grafana.org](http://docs.grafana.org/installation/) and [download](https://grafana.com/get)
|
||||||
the latest release.
|
the latest release.
|
||||||
|
|||||||
@@ -45,7 +45,14 @@ To simplify syntax and to allow for dynamic parts, like date range filters, the
|
|||||||
|
|
||||||
Macro example | Description
|
Macro example | Description
|
||||||
------------ | -------------
|
------------ | -------------
|
||||||
|
*$__time(dateColumn)* | Will be replaced by an expression to convert to a UNIX timestamp and rename the column to `time_sec`. For example, *UNIX_TIMESTAMP(dateColumn) as time_sec*
|
||||||
*$__timeFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name. For example, *dateColumn > FROM_UNIXTIME(1494410783) AND dateColumn < FROM_UNIXTIME(1494497183)*
|
*$__timeFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name. For example, *dateColumn > FROM_UNIXTIME(1494410783) AND dateColumn < FROM_UNIXTIME(1494497183)*
|
||||||
|
*$__timeFrom()* | Will be replaced by the start of the currently active time selection. For example, *FROM_UNIXTIME(1494410783)*
|
||||||
|
*$__timeTo()* | Will be replaced by the end of the currently active time selection. For example, *FROM_UNIXTIME(1494497183)*
|
||||||
|
*$__timeGroup(dateColumn,'5m')* | Will be replaced by an expression usable in GROUP BY clause. For example, *cast(cast(UNIX_TIMESTAMP(dateColumn)/(300) as signed)*300 as signed) as time_sec,*
|
||||||
|
*$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamp. For example, *dateColumn > 1494410783 AND dateColumn < 1494497183*
|
||||||
|
*$__unixEpochFrom()* | Will be replaced by the start of the currently active time selection as unix timestamp. For example, *1494410783*
|
||||||
|
*$__unixEpochTo()* | Will be replaced by the end of the currently active time selection as unix timestamp. For example, *1494497183*
|
||||||
|
|
||||||
We plan to add many more macros. If you have suggestions for what macros you would like to see, please [open an issue](https://github.com/grafana/grafana) in our GitHub repo.
|
We plan to add many more macros. If you have suggestions for what macros you would like to see, please [open an issue](https://github.com/grafana/grafana) in our GitHub repo.
|
||||||
|
|
||||||
@@ -99,6 +106,19 @@ GROUP BY metric1, UNIX_TIMESTAMP(time_date_time) DIV 300
|
|||||||
ORDER BY time_sec asc
|
ORDER BY time_sec asc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Example with $__timeGroup macro:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
$__timeGroup(time_date_time,'5m') as time_sec,
|
||||||
|
min(value_double) as value,
|
||||||
|
metric_name as metric
|
||||||
|
FROM test_data
|
||||||
|
WHERE $__timeFilter(time_date_time)
|
||||||
|
GROUP BY 1, metric_name
|
||||||
|
ORDER BY 1
|
||||||
|
```
|
||||||
|
|
||||||
Currently, there is no support for a dynamic group by time based on time range & panel width.
|
Currently, there is no support for a dynamic group by time based on time range & panel width.
|
||||||
This is something we plan to add.
|
This is something we plan to add.
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ The coloring options of the Singlestat Panel config allow you to dynamically cha
|
|||||||
2. **Thresholds**: Change the background and value colors dynamically within the panel, depending on the Singlestat value. The threshold field accepts **2 comma-separated** values which represent 3 ranges that correspond to the three colors directly to the right. For example: if the thresholds are 70, 90 then the first color represents < 70, the second color represents between 70 and 90 and the third color represents > 90.
|
2. **Thresholds**: Change the background and value colors dynamically within the panel, depending on the Singlestat value. The threshold field accepts **2 comma-separated** values which represent 3 ranges that correspond to the three colors directly to the right. For example: if the thresholds are 70, 90 then the first color represents < 70, the second color represents between 70 and 90 and the third color represents > 90.
|
||||||
3. **Colors**: Select a color and opacity
|
3. **Colors**: Select a color and opacity
|
||||||
4. **Value**: This checkbox applies the configured thresholds and colors to the summary stat.
|
4. **Value**: This checkbox applies the configured thresholds and colors to the summary stat.
|
||||||
5. **Invert order**: This link toggles the threshold color order.</br>For example: Green, Orange, Red (<img class="no-shadow" src="/img/docs(v1/gyr.png">) will become Red, Orange, Green (<img class="no-shadow" src="/img/docs/v1/ryg.png">).
|
5. **Invert order**: This link toggles the threshold color order.</br>For example: Green, Orange, Red (<img class="no-shadow" src="/img/docs/v1/gyr.png">) will become Red, Orange, Green (<img class="no-shadow" src="/img/docs/v1/ryg.png">).
|
||||||
|
|
||||||
### Spark Lines
|
### Spark Lines
|
||||||
|
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ HTTP/1.1 200
|
|||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"email": "user@mygraf.com"
|
"email": "user@mygraf.com",
|
||||||
"name": "admin",
|
"name": "admin",
|
||||||
"login": "admin",
|
"login": "admin",
|
||||||
"theme": "light",
|
"theme": "light",
|
||||||
@@ -409,4 +409,4 @@ HTTP/1.1 200
|
|||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{"message":"Dashboard unstarred"}
|
{"message":"Dashboard unstarred"}
|
||||||
```
|
```
|
||||||
|
|||||||
+13
-3
@@ -65,7 +65,7 @@
|
|||||||
"karma-sinon": "^1.0.5",
|
"karma-sinon": "^1.0.5",
|
||||||
"karma-sourcemap-loader": "^0.3.7",
|
"karma-sourcemap-loader": "^0.3.7",
|
||||||
"karma-webpack": "^2.0.4",
|
"karma-webpack": "^2.0.4",
|
||||||
"lint-staged": "^4.2.3",
|
"lint-staged": "^6.0.0",
|
||||||
"load-grunt-tasks": "3.5.2",
|
"load-grunt-tasks": "3.5.2",
|
||||||
"mocha": "^4.0.1",
|
"mocha": "^4.0.1",
|
||||||
"ng-annotate-loader": "^0.6.1",
|
"ng-annotate-loader": "^0.6.1",
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
"postcss-browser-reporter": "^0.5.0",
|
"postcss-browser-reporter": "^0.5.0",
|
||||||
"postcss-loader": "^2.0.6",
|
"postcss-loader": "^2.0.6",
|
||||||
"postcss-reporter": "^5.0.0",
|
"postcss-reporter": "^5.0.0",
|
||||||
"prettier": "1.7.3",
|
"prettier": "1.9.2",
|
||||||
"react-test-renderer": "^16.0.0",
|
"react-test-renderer": "^16.0.0",
|
||||||
"sass-lint": "^1.10.2",
|
"sass-lint": "^1.10.2",
|
||||||
"sass-loader": "^6.0.6",
|
"sass-loader": "^6.0.6",
|
||||||
@@ -103,7 +103,17 @@
|
|||||||
"lint": "tslint -c tslint.json --project tsconfig.json --type-check",
|
"lint": "tslint -c tslint.json --project tsconfig.json --type-check",
|
||||||
"karma": "node ./node_modules/grunt-cli/bin/grunt karma:dev",
|
"karma": "node ./node_modules/grunt-cli/bin/grunt karma:dev",
|
||||||
"jest": "node ./node_modules/jest-cli/bin/jest.js --notify --watch",
|
"jest": "node ./node_modules/jest-cli/bin/jest.js --notify --watch",
|
||||||
"precommit": "node ./node_modules/grunt-cli/bin/grunt precommit"
|
"precommit": "lint-staged && node ./node_modules/grunt-cli/bin/grunt precommit"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{ts,tsx}": [
|
||||||
|
"prettier --single-quote --trailing-comma es5 --write",
|
||||||
|
"git add"
|
||||||
|
],
|
||||||
|
"*.scss": [
|
||||||
|
"prettier --single-quote --write",
|
||||||
|
"git add"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+1
-1
@@ -92,7 +92,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
|||||||
Text: "Create",
|
Text: "Create",
|
||||||
Id: "create",
|
Id: "create",
|
||||||
Icon: "fa fa-fw fa-plus",
|
Icon: "fa fa-fw fa-plus",
|
||||||
Url: setting.AppSubUrl + "dashboard/new",
|
Url: setting.AppSubUrl + "/dashboard/new",
|
||||||
Children: []*dtos.NavLink{
|
Children: []*dtos.NavLink{
|
||||||
{Text: "Dashboard", Icon: "gicon gicon-dashboard-new", Url: setting.AppSubUrl + "/dashboard/new"},
|
{Text: "Dashboard", Icon: "gicon gicon-dashboard-new", Url: setting.AppSubUrl + "/dashboard/new"},
|
||||||
{Text: "Folder", SubTitle: "Create a new folder to organize your dashboards", Id: "folder", Icon: "gicon gicon-folder-new", Url: setting.AppSubUrl + "/dashboards/folder/new"},
|
{Text: "Folder", SubTitle: "Create a new folder to organize your dashboards", Id: "folder", Icon: "gicon gicon-folder-new", Url: setting.AppSubUrl + "/dashboards/folder/new"},
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
@@ -41,7 +42,7 @@ func OrgRedirect() macaron.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
newUrl := setting.ToAbsUrl(fmt.Sprintf("%s?%s", c.Req.URL.Path, c.Req.URL.Query().Encode()))
|
newURL := setting.ToAbsUrl(fmt.Sprintf("%s?%s", strings.TrimPrefix(c.Req.URL.Path, "/"), c.Req.URL.Query().Encode()))
|
||||||
c.Redirect(newUrl, 302)
|
c.Redirect(newURL, 302)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,90 @@ func copyJson(in *simplejson.Json) (*simplejson.Json, error) {
|
|||||||
return simplejson.NewJson(rawJson)
|
return simplejson.NewJson(rawJson)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *DashAlertExtractor) GetAlertFromPanels(jsonWithPanels *simplejson.Json) ([]*m.Alert, error) {
|
||||||
|
alerts := make([]*m.Alert, 0)
|
||||||
|
|
||||||
|
for _, panelObj := range jsonWithPanels.Get("panels").MustArray() {
|
||||||
|
panel := simplejson.NewFromAny(panelObj)
|
||||||
|
jsonAlert, hasAlert := panel.CheckGet("alert")
|
||||||
|
|
||||||
|
if !hasAlert {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
panelId, err := panel.Get("id").Int64()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("panel id is required. err %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// backward compatibility check, can be removed later
|
||||||
|
enabled, hasEnabled := jsonAlert.CheckGet("enabled")
|
||||||
|
if hasEnabled && enabled.MustBool() == false {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
frequency, err := getTimeDurationStringToSeconds(jsonAlert.Get("frequency").MustString())
|
||||||
|
if err != nil {
|
||||||
|
return nil, ValidationError{Reason: "Could not parse frequency"}
|
||||||
|
}
|
||||||
|
|
||||||
|
alert := &m.Alert{
|
||||||
|
DashboardId: e.Dash.Id,
|
||||||
|
OrgId: e.OrgId,
|
||||||
|
PanelId: panelId,
|
||||||
|
Id: jsonAlert.Get("id").MustInt64(),
|
||||||
|
Name: jsonAlert.Get("name").MustString(),
|
||||||
|
Handler: jsonAlert.Get("handler").MustInt64(),
|
||||||
|
Message: jsonAlert.Get("message").MustString(),
|
||||||
|
Frequency: frequency,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, condition := range jsonAlert.Get("conditions").MustArray() {
|
||||||
|
jsonCondition := simplejson.NewFromAny(condition)
|
||||||
|
|
||||||
|
jsonQuery := jsonCondition.Get("query")
|
||||||
|
queryRefId := jsonQuery.Get("params").MustArray()[0].(string)
|
||||||
|
panelQuery := findPanelQueryByRefId(panel, queryRefId)
|
||||||
|
|
||||||
|
if panelQuery == nil {
|
||||||
|
reason := fmt.Sprintf("Alert on PanelId: %v refers to query(%s) that cannot be found", alert.PanelId, queryRefId)
|
||||||
|
return nil, ValidationError{Reason: reason}
|
||||||
|
}
|
||||||
|
|
||||||
|
dsName := ""
|
||||||
|
if panelQuery.Get("datasource").MustString() != "" {
|
||||||
|
dsName = panelQuery.Get("datasource").MustString()
|
||||||
|
} else if panel.Get("datasource").MustString() != "" {
|
||||||
|
dsName = panel.Get("datasource").MustString()
|
||||||
|
}
|
||||||
|
|
||||||
|
if datasource, err := e.lookupDatasourceId(dsName); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
jsonQuery.SetPath([]string{"datasourceId"}, datasource.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if interval, err := panel.Get("interval").String(); err == nil {
|
||||||
|
panelQuery.Set("interval", interval)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonQuery.Set("model", panelQuery.Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
alert.Settings = jsonAlert
|
||||||
|
|
||||||
|
// validate
|
||||||
|
_, err = NewRuleFromDBAlert(alert)
|
||||||
|
if err == nil && alert.ValidToSave() {
|
||||||
|
alerts = append(alerts, alert)
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return alerts, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) {
|
func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) {
|
||||||
e.log.Debug("GetAlerts")
|
e.log.Debug("GetAlerts")
|
||||||
|
|
||||||
@@ -78,86 +162,27 @@ func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
alerts := make([]*m.Alert, 0)
|
alerts := make([]*m.Alert, 0)
|
||||||
for _, rowObj := range dashboardJson.Get("rows").MustArray() {
|
|
||||||
row := simplejson.NewFromAny(rowObj)
|
|
||||||
|
|
||||||
for _, panelObj := range row.Get("panels").MustArray() {
|
// We extract alerts from rows to be backwards compatible
|
||||||
panel := simplejson.NewFromAny(panelObj)
|
// with the old dashboard json model.
|
||||||
jsonAlert, hasAlert := panel.CheckGet("alert")
|
rows := dashboardJson.Get("rows").MustArray()
|
||||||
|
if len(rows) > 0 {
|
||||||
if !hasAlert {
|
for _, rowObj := range rows {
|
||||||
continue
|
row := simplejson.NewFromAny(rowObj)
|
||||||
}
|
a, err := e.GetAlertFromPanels(row)
|
||||||
|
|
||||||
panelId, err := panel.Get("id").Int64()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("panel id is required. err %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// backward compatibility check, can be removed later
|
|
||||||
enabled, hasEnabled := jsonAlert.CheckGet("enabled")
|
|
||||||
if hasEnabled && enabled.MustBool() == false {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
frequency, err := getTimeDurationStringToSeconds(jsonAlert.Get("frequency").MustString())
|
|
||||||
if err != nil {
|
|
||||||
return nil, ValidationError{Reason: "Could not parse frequency"}
|
|
||||||
}
|
|
||||||
|
|
||||||
alert := &m.Alert{
|
|
||||||
DashboardId: e.Dash.Id,
|
|
||||||
OrgId: e.OrgId,
|
|
||||||
PanelId: panelId,
|
|
||||||
Id: jsonAlert.Get("id").MustInt64(),
|
|
||||||
Name: jsonAlert.Get("name").MustString(),
|
|
||||||
Handler: jsonAlert.Get("handler").MustInt64(),
|
|
||||||
Message: jsonAlert.Get("message").MustString(),
|
|
||||||
Frequency: frequency,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, condition := range jsonAlert.Get("conditions").MustArray() {
|
|
||||||
jsonCondition := simplejson.NewFromAny(condition)
|
|
||||||
|
|
||||||
jsonQuery := jsonCondition.Get("query")
|
|
||||||
queryRefId := jsonQuery.Get("params").MustArray()[0].(string)
|
|
||||||
panelQuery := findPanelQueryByRefId(panel, queryRefId)
|
|
||||||
|
|
||||||
if panelQuery == nil {
|
|
||||||
reason := fmt.Sprintf("Alert on PanelId: %v refers to query(%s) that cannot be found", alert.PanelId, queryRefId)
|
|
||||||
return nil, ValidationError{Reason: reason}
|
|
||||||
}
|
|
||||||
|
|
||||||
dsName := ""
|
|
||||||
if panelQuery.Get("datasource").MustString() != "" {
|
|
||||||
dsName = panelQuery.Get("datasource").MustString()
|
|
||||||
} else if panel.Get("datasource").MustString() != "" {
|
|
||||||
dsName = panel.Get("datasource").MustString()
|
|
||||||
}
|
|
||||||
|
|
||||||
if datasource, err := e.lookupDatasourceId(dsName); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
jsonQuery.SetPath([]string{"datasourceId"}, datasource.Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
if interval, err := panel.Get("interval").String(); err == nil {
|
|
||||||
panelQuery.Set("interval", interval)
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonQuery.Set("model", panelQuery.Interface())
|
|
||||||
}
|
|
||||||
|
|
||||||
alert.Settings = jsonAlert
|
|
||||||
|
|
||||||
// validate
|
|
||||||
_, err = NewRuleFromDBAlert(alert)
|
|
||||||
if err == nil && alert.ValidToSave() {
|
|
||||||
alerts = append(alerts, alert)
|
|
||||||
} else {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alerts = append(alerts, a...)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
a, err := e.GetAlertFromPanels(dashboardJson)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
alerts = append(alerts, a...)
|
||||||
}
|
}
|
||||||
|
|
||||||
e.log.Debug("Extracted alerts from dashboard", "alertCount", len(alerts))
|
e.log.Debug("Extracted alerts from dashboard", "alertCount", len(alerts))
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package alerting
|
package alerting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,10 +18,6 @@ func TestAlertRuleExtraction(t *testing.T) {
|
|||||||
return &FakeCondition{}, nil
|
return &FakeCondition{}, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
setting.NewConfigContext(&setting.CommandLineArgs{
|
|
||||||
HomePath: "../../../",
|
|
||||||
})
|
|
||||||
|
|
||||||
// mock data
|
// mock data
|
||||||
defaultDs := &m.DataSource{Id: 12, OrgId: 1, Name: "I am default", IsDefault: true}
|
defaultDs := &m.DataSource{Id: 12, OrgId: 1, Name: "I am default", IsDefault: true}
|
||||||
graphite2Ds := &m.DataSource{Id: 15, OrgId: 1, Name: "graphite2"}
|
graphite2Ds := &m.DataSource{Id: 15, OrgId: 1, Name: "graphite2"}
|
||||||
@@ -45,70 +41,8 @@ func TestAlertRuleExtraction(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
json := `
|
json, err := ioutil.ReadFile("./test-data/graphite-alert.json")
|
||||||
{
|
So(err, ShouldBeNil)
|
||||||
"id": 57,
|
|
||||||
"title": "Graphite 4",
|
|
||||||
"originalTitle": "Graphite 4",
|
|
||||||
"tags": ["graphite"],
|
|
||||||
"rows": [
|
|
||||||
{
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"title": "Active desktop users",
|
|
||||||
"editable": true,
|
|
||||||
"type": "graph",
|
|
||||||
"id": 3,
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"refId": "A",
|
|
||||||
"target": "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"datasource": null,
|
|
||||||
"alert": {
|
|
||||||
"name": "name1",
|
|
||||||
"message": "desc1",
|
|
||||||
"handler": 1,
|
|
||||||
"frequency": "60s",
|
|
||||||
"conditions": [
|
|
||||||
{
|
|
||||||
"type": "query",
|
|
||||||
"query": {"params": ["A", "5m", "now"]},
|
|
||||||
"reducer": {"type": "avg", "params": []},
|
|
||||||
"evaluator": {"type": ">", "params": [100]}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Active mobile users",
|
|
||||||
"id": 4,
|
|
||||||
"targets": [
|
|
||||||
{"refId": "A", "target": ""},
|
|
||||||
{"refId": "B", "target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}
|
|
||||||
],
|
|
||||||
"datasource": "graphite2",
|
|
||||||
"alert": {
|
|
||||||
"name": "name2",
|
|
||||||
"message": "desc2",
|
|
||||||
"handler": 0,
|
|
||||||
"frequency": "60s",
|
|
||||||
"severity": "warning",
|
|
||||||
"conditions": [
|
|
||||||
{
|
|
||||||
"type": "query",
|
|
||||||
"query": {"params": ["B", "5m", "now"]},
|
|
||||||
"reducer": {"type": "avg", "params": []},
|
|
||||||
"evaluator": {"type": ">", "params": [100]}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`
|
|
||||||
|
|
||||||
Convey("Extractor should not modify the original json", func() {
|
Convey("Extractor should not modify the original json", func() {
|
||||||
dashJson, err := simplejson.NewJson([]byte(json))
|
dashJson, err := simplejson.NewJson([]byte(json))
|
||||||
@@ -201,69 +135,8 @@ func TestAlertRuleExtraction(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Panels missing id should return error", func() {
|
Convey("Panels missing id should return error", func() {
|
||||||
panelWithoutId := `
|
panelWithoutId, err := ioutil.ReadFile("./test-data/panels-missing-id.json")
|
||||||
{
|
So(err, ShouldBeNil)
|
||||||
"id": 57,
|
|
||||||
"title": "Graphite 4",
|
|
||||||
"originalTitle": "Graphite 4",
|
|
||||||
"tags": ["graphite"],
|
|
||||||
"rows": [
|
|
||||||
{
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"title": "Active desktop users",
|
|
||||||
"editable": true,
|
|
||||||
"type": "graph",
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"refId": "A",
|
|
||||||
"target": "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"datasource": null,
|
|
||||||
"alert": {
|
|
||||||
"name": "name1",
|
|
||||||
"message": "desc1",
|
|
||||||
"handler": 1,
|
|
||||||
"frequency": "60s",
|
|
||||||
"conditions": [
|
|
||||||
{
|
|
||||||
"type": "query",
|
|
||||||
"query": {"params": ["A", "5m", "now"]},
|
|
||||||
"reducer": {"type": "avg", "params": []},
|
|
||||||
"evaluator": {"type": ">", "params": [100]}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Active mobile users",
|
|
||||||
"id": 4,
|
|
||||||
"targets": [
|
|
||||||
{"refId": "A", "target": ""},
|
|
||||||
{"refId": "B", "target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}
|
|
||||||
],
|
|
||||||
"datasource": "graphite2",
|
|
||||||
"alert": {
|
|
||||||
"name": "name2",
|
|
||||||
"message": "desc2",
|
|
||||||
"handler": 0,
|
|
||||||
"frequency": "60s",
|
|
||||||
"severity": "warning",
|
|
||||||
"conditions": [
|
|
||||||
{
|
|
||||||
"type": "query",
|
|
||||||
"query": {"params": ["B", "5m", "now"]},
|
|
||||||
"reducer": {"type": "avg", "params": []},
|
|
||||||
"evaluator": {"type": ">", "params": [100]}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`
|
|
||||||
|
|
||||||
dashJson, err := simplejson.NewJson([]byte(panelWithoutId))
|
dashJson, err := simplejson.NewJson([]byte(panelWithoutId))
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
@@ -277,292 +150,31 @@ func TestAlertRuleExtraction(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("Parse alerts from dashboard without rows", func() {
|
||||||
|
json, err := ioutil.ReadFile("./test-data/v5-dashboard.json")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
dashJson, err := simplejson.NewJson(json)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
dash := m.NewDashboardFromJson(dashJson)
|
||||||
|
extractor := NewDashAlertExtractor(dash, 1)
|
||||||
|
|
||||||
|
alerts, err := extractor.GetAlerts()
|
||||||
|
|
||||||
|
Convey("Get rules without error", func() {
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Should have 2 alert rule", func() {
|
||||||
|
So(len(alerts), ShouldEqual, 2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Convey("Parse and validate dashboard containing influxdb alert", func() {
|
Convey("Parse and validate dashboard containing influxdb alert", func() {
|
||||||
|
json, err := ioutil.ReadFile("./test-data/influxdb-alert.json")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
json2 := `{
|
dashJson, err := simplejson.NewJson(json)
|
||||||
"id": 4,
|
|
||||||
"title": "Influxdb",
|
|
||||||
"tags": [
|
|
||||||
"apa"
|
|
||||||
],
|
|
||||||
"style": "dark",
|
|
||||||
"timezone": "browser",
|
|
||||||
"editable": true,
|
|
||||||
"hideControls": false,
|
|
||||||
"sharedCrosshair": false,
|
|
||||||
"rows": [
|
|
||||||
{
|
|
||||||
"collapse": false,
|
|
||||||
"editable": true,
|
|
||||||
"height": "450px",
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"alert": {
|
|
||||||
"conditions": [
|
|
||||||
{
|
|
||||||
"evaluator": {
|
|
||||||
"params": [
|
|
||||||
10
|
|
||||||
],
|
|
||||||
"type": "gt"
|
|
||||||
},
|
|
||||||
"query": {
|
|
||||||
"params": [
|
|
||||||
"B",
|
|
||||||
"5m",
|
|
||||||
"now"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"reducer": {
|
|
||||||
"params": [],
|
|
||||||
"type": "avg"
|
|
||||||
},
|
|
||||||
"type": "query"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"frequency": "3s",
|
|
||||||
"handler": 1,
|
|
||||||
"name": "Influxdb",
|
|
||||||
"noDataState": "no_data",
|
|
||||||
"notifications": [
|
|
||||||
{
|
|
||||||
"id": 6
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"alerting": {},
|
|
||||||
"aliasColors": {
|
|
||||||
"logins.count.count": "#890F02"
|
|
||||||
},
|
|
||||||
"bars": false,
|
|
||||||
"datasource": "InfluxDB",
|
|
||||||
"editable": true,
|
|
||||||
"error": false,
|
|
||||||
"fill": 1,
|
|
||||||
"grid": {},
|
|
||||||
"id": 1,
|
|
||||||
"interval": ">10s",
|
|
||||||
"isNew": true,
|
|
||||||
"legend": {
|
|
||||||
"avg": false,
|
|
||||||
"current": false,
|
|
||||||
"max": false,
|
|
||||||
"min": false,
|
|
||||||
"show": true,
|
|
||||||
"total": false,
|
|
||||||
"values": false
|
|
||||||
},
|
|
||||||
"lines": true,
|
|
||||||
"linewidth": 2,
|
|
||||||
"links": [],
|
|
||||||
"nullPointMode": "connected",
|
|
||||||
"percentage": false,
|
|
||||||
"pointradius": 5,
|
|
||||||
"points": false,
|
|
||||||
"renderer": "flot",
|
|
||||||
"seriesOverrides": [],
|
|
||||||
"span": 10,
|
|
||||||
"stack": false,
|
|
||||||
"steppedLine": false,
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"groupBy": [
|
|
||||||
{
|
|
||||||
"params": [
|
|
||||||
"$interval"
|
|
||||||
],
|
|
||||||
"type": "time"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"params": [
|
|
||||||
"datacenter"
|
|
||||||
],
|
|
||||||
"type": "tag"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"params": [
|
|
||||||
"none"
|
|
||||||
],
|
|
||||||
"type": "fill"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"hide": false,
|
|
||||||
"measurement": "logins.count",
|
|
||||||
"policy": "default",
|
|
||||||
"query": "SELECT 8 * count(\"value\") FROM \"logins.count\" WHERE $timeFilter GROUP BY time($interval), \"datacenter\" fill(none)",
|
|
||||||
"rawQuery": true,
|
|
||||||
"refId": "B",
|
|
||||||
"resultFormat": "time_series",
|
|
||||||
"select": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"params": [
|
|
||||||
"value"
|
|
||||||
],
|
|
||||||
"type": "field"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"params": [],
|
|
||||||
"type": "count"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"groupBy": [
|
|
||||||
{
|
|
||||||
"params": [
|
|
||||||
"$interval"
|
|
||||||
],
|
|
||||||
"type": "time"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"params": [
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"type": "fill"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"hide": true,
|
|
||||||
"measurement": "cpu",
|
|
||||||
"policy": "default",
|
|
||||||
"refId": "A",
|
|
||||||
"resultFormat": "time_series",
|
|
||||||
"select": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"params": [
|
|
||||||
"value"
|
|
||||||
],
|
|
||||||
"type": "field"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"params": [],
|
|
||||||
"type": "mean"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"params": [
|
|
||||||
"value"
|
|
||||||
],
|
|
||||||
"type": "field"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"params": [],
|
|
||||||
"type": "sum"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"tags": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"thresholds": [
|
|
||||||
{
|
|
||||||
"colorMode": "critical",
|
|
||||||
"fill": true,
|
|
||||||
"line": true,
|
|
||||||
"op": "gt",
|
|
||||||
"value": 10
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timeFrom": null,
|
|
||||||
"timeShift": null,
|
|
||||||
"title": "Panel Title",
|
|
||||||
"tooltip": {
|
|
||||||
"msResolution": false,
|
|
||||||
"ordering": "alphabetical",
|
|
||||||
"shared": true,
|
|
||||||
"sort": 0,
|
|
||||||
"value_type": "cumulative"
|
|
||||||
},
|
|
||||||
"type": "graph",
|
|
||||||
"xaxis": {
|
|
||||||
"mode": "time",
|
|
||||||
"name": null,
|
|
||||||
"show": true,
|
|
||||||
"values": []
|
|
||||||
},
|
|
||||||
"yaxes": [
|
|
||||||
{
|
|
||||||
"format": "short",
|
|
||||||
"logBase": 1,
|
|
||||||
"max": null,
|
|
||||||
"min": null,
|
|
||||||
"show": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"format": "short",
|
|
||||||
"logBase": 1,
|
|
||||||
"max": null,
|
|
||||||
"min": null,
|
|
||||||
"show": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"editable": true,
|
|
||||||
"error": false,
|
|
||||||
"id": 2,
|
|
||||||
"isNew": true,
|
|
||||||
"limit": 10,
|
|
||||||
"links": [],
|
|
||||||
"show": "current",
|
|
||||||
"span": 2,
|
|
||||||
"stateFilter": [
|
|
||||||
"alerting"
|
|
||||||
],
|
|
||||||
"title": "Alert status",
|
|
||||||
"type": "alertlist"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"title": "Row"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": {
|
|
||||||
"from": "now-5m",
|
|
||||||
"to": "now"
|
|
||||||
},
|
|
||||||
"timepicker": {
|
|
||||||
"now": true,
|
|
||||||
"refresh_intervals": [
|
|
||||||
"5s",
|
|
||||||
"10s",
|
|
||||||
"30s",
|
|
||||||
"1m",
|
|
||||||
"5m",
|
|
||||||
"15m",
|
|
||||||
"30m",
|
|
||||||
"1h",
|
|
||||||
"2h",
|
|
||||||
"1d"
|
|
||||||
],
|
|
||||||
"time_options": [
|
|
||||||
"5m",
|
|
||||||
"15m",
|
|
||||||
"1h",
|
|
||||||
"6h",
|
|
||||||
"12h",
|
|
||||||
"24h",
|
|
||||||
"2d",
|
|
||||||
"7d",
|
|
||||||
"30d"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"templating": {
|
|
||||||
"list": []
|
|
||||||
},
|
|
||||||
"annotations": {
|
|
||||||
"list": []
|
|
||||||
},
|
|
||||||
"schemaVersion": 13,
|
|
||||||
"version": 120,
|
|
||||||
"links": [],
|
|
||||||
"gnetId": null
|
|
||||||
}`
|
|
||||||
|
|
||||||
dashJson, err := simplejson.NewJson([]byte(json2))
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
dash := m.NewDashboardFromJson(dashJson)
|
dash := m.NewDashboardFromJson(dashJson)
|
||||||
extractor := NewDashAlertExtractor(dash, 1)
|
extractor := NewDashAlertExtractor(dash, 1)
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
|
|||||||
this.log.Error("Failed get rule link", "error", err)
|
this.log.Error("Failed get rule link", "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
message := evalContext.Rule.Message
|
message := evalContext.Rule.Message
|
||||||
for idx, evt := range evalContext.EvalMatches {
|
for idx, evt := range evalContext.EvalMatches {
|
||||||
message += fmt.Sprintf("\n<b>%s</b>: %v", evt.Metric, evt.Value)
|
message += fmt.Sprintf("\n<b>%s</b>: %v", evt.Metric, evt.Value)
|
||||||
@@ -146,6 +147,9 @@ func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
|
|||||||
if evalContext.ImagePublicUrl != "" {
|
if evalContext.ImagePublicUrl != "" {
|
||||||
message += fmt.Sprintf("\n<a href=\"%s\">Show graph image</a>", evalContext.ImagePublicUrl)
|
message += fmt.Sprintf("\n<a href=\"%s\">Show graph image</a>", evalContext.ImagePublicUrl)
|
||||||
}
|
}
|
||||||
|
if message == "" {
|
||||||
|
message = "Notification message missing (Set a notification message to replace this text.)"
|
||||||
|
}
|
||||||
|
|
||||||
q := url.Values{}
|
q := url.Values{}
|
||||||
q.Add("user", this.UserKey)
|
q.Add("user", this.UserKey)
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"id": 57,
|
||||||
|
"title": "Graphite 4",
|
||||||
|
"originalTitle": "Graphite 4",
|
||||||
|
"tags": ["graphite"],
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"title": "Active desktop users",
|
||||||
|
"editable": true,
|
||||||
|
"type": "graph",
|
||||||
|
"id": 3,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"refId": "A",
|
||||||
|
"target": "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"datasource": null,
|
||||||
|
"alert": {
|
||||||
|
"name": "name1",
|
||||||
|
"message": "desc1",
|
||||||
|
"handler": 1,
|
||||||
|
"frequency": "60s",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"type": "query",
|
||||||
|
"query": {"params": ["A", "5m", "now"]},
|
||||||
|
"reducer": {"type": "avg", "params": []},
|
||||||
|
"evaluator": {"type": ">", "params": [100]}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Active mobile users",
|
||||||
|
"id": 4,
|
||||||
|
"targets": [
|
||||||
|
{"refId": "A", "target": ""},
|
||||||
|
{"refId": "B", "target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}
|
||||||
|
],
|
||||||
|
"datasource": "graphite2",
|
||||||
|
"alert": {
|
||||||
|
"name": "name2",
|
||||||
|
"message": "desc2",
|
||||||
|
"handler": 0,
|
||||||
|
"frequency": "60s",
|
||||||
|
"severity": "warning",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"type": "query",
|
||||||
|
"query": {"params": ["B", "5m", "now"]},
|
||||||
|
"reducer": {"type": "avg", "params": []},
|
||||||
|
"evaluator": {"type": ">", "params": [100]}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"title": "Influxdb",
|
||||||
|
"tags": [
|
||||||
|
"apa"
|
||||||
|
],
|
||||||
|
"style": "dark",
|
||||||
|
"timezone": "browser",
|
||||||
|
"editable": true,
|
||||||
|
"hideControls": false,
|
||||||
|
"sharedCrosshair": false,
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"collapse": false,
|
||||||
|
"editable": true,
|
||||||
|
"height": "450px",
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"alert": {
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"evaluator": {
|
||||||
|
"params": [
|
||||||
|
10
|
||||||
|
],
|
||||||
|
"type": "gt"
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"params": [
|
||||||
|
"B",
|
||||||
|
"5m",
|
||||||
|
"now"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"reducer": {
|
||||||
|
"params": [],
|
||||||
|
"type": "avg"
|
||||||
|
},
|
||||||
|
"type": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frequency": "3s",
|
||||||
|
"handler": 1,
|
||||||
|
"name": "Influxdb",
|
||||||
|
"noDataState": "no_data",
|
||||||
|
"notifications": [
|
||||||
|
{
|
||||||
|
"id": 6
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"alerting": {},
|
||||||
|
"aliasColors": {
|
||||||
|
"logins.count.count": "#890F02"
|
||||||
|
},
|
||||||
|
"bars": false,
|
||||||
|
"datasource": "InfluxDB",
|
||||||
|
"editable": true,
|
||||||
|
"error": false,
|
||||||
|
"fill": 1,
|
||||||
|
"grid": {},
|
||||||
|
"id": 1,
|
||||||
|
"interval": ">10s",
|
||||||
|
"isNew": true,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 2,
|
||||||
|
"links": [],
|
||||||
|
"nullPointMode": "connected",
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"span": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"groupBy": [
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"$interval"
|
||||||
|
],
|
||||||
|
"type": "time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"datacenter"
|
||||||
|
],
|
||||||
|
"type": "tag"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"none"
|
||||||
|
],
|
||||||
|
"type": "fill"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hide": false,
|
||||||
|
"measurement": "logins.count",
|
||||||
|
"policy": "default",
|
||||||
|
"query": "SELECT 8 * count(\"value\") FROM \"logins.count\" WHERE $timeFilter GROUP BY time($interval), \"datacenter\" fill(none)",
|
||||||
|
"rawQuery": true,
|
||||||
|
"refId": "B",
|
||||||
|
"resultFormat": "time_series",
|
||||||
|
"select": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"type": "field"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": [],
|
||||||
|
"type": "count"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"tags": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupBy": [
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"$interval"
|
||||||
|
],
|
||||||
|
"type": "time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"type": "fill"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hide": true,
|
||||||
|
"measurement": "cpu",
|
||||||
|
"policy": "default",
|
||||||
|
"refId": "A",
|
||||||
|
"resultFormat": "time_series",
|
||||||
|
"select": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"type": "field"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": [],
|
||||||
|
"type": "mean"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"type": "field"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": [],
|
||||||
|
"type": "sum"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [
|
||||||
|
{
|
||||||
|
"colorMode": "critical",
|
||||||
|
"fill": true,
|
||||||
|
"line": true,
|
||||||
|
"op": "gt",
|
||||||
|
"value": 10
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Panel Title",
|
||||||
|
"tooltip": {
|
||||||
|
"msResolution": false,
|
||||||
|
"ordering": "alphabetical",
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "cumulative"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"editable": true,
|
||||||
|
"error": false,
|
||||||
|
"id": 2,
|
||||||
|
"isNew": true,
|
||||||
|
"limit": 10,
|
||||||
|
"links": [],
|
||||||
|
"show": "current",
|
||||||
|
"span": 2,
|
||||||
|
"stateFilter": [
|
||||||
|
"alerting"
|
||||||
|
],
|
||||||
|
"title": "Alert status",
|
||||||
|
"type": "alertlist"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Row"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": {
|
||||||
|
"from": "now-5m",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {
|
||||||
|
"now": true,
|
||||||
|
"refresh_intervals": [
|
||||||
|
"5s",
|
||||||
|
"10s",
|
||||||
|
"30s",
|
||||||
|
"1m",
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"30m",
|
||||||
|
"1h",
|
||||||
|
"2h",
|
||||||
|
"1d"
|
||||||
|
],
|
||||||
|
"time_options": [
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"1h",
|
||||||
|
"6h",
|
||||||
|
"12h",
|
||||||
|
"24h",
|
||||||
|
"2d",
|
||||||
|
"7d",
|
||||||
|
"30d"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"annotations": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"schemaVersion": 13,
|
||||||
|
"version": 120,
|
||||||
|
"links": [],
|
||||||
|
"gnetId": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"id": 57,
|
||||||
|
"title": "Graphite 4",
|
||||||
|
"originalTitle": "Graphite 4",
|
||||||
|
"tags": ["graphite"],
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"title": "Active desktop users",
|
||||||
|
"editable": true,
|
||||||
|
"type": "graph",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"refId": "A",
|
||||||
|
"target": "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"datasource": null,
|
||||||
|
"alert": {
|
||||||
|
"name": "name1",
|
||||||
|
"message": "desc1",
|
||||||
|
"handler": 1,
|
||||||
|
"frequency": "60s",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"type": "query",
|
||||||
|
"query": {"params": ["A", "5m", "now"]},
|
||||||
|
"reducer": {"type": "avg", "params": []},
|
||||||
|
"evaluator": {"type": ">", "params": [100]}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Active mobile users",
|
||||||
|
"id": 4,
|
||||||
|
"targets": [
|
||||||
|
{"refId": "A", "target": ""},
|
||||||
|
{"refId": "B", "target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}
|
||||||
|
],
|
||||||
|
"datasource": "graphite2",
|
||||||
|
"alert": {
|
||||||
|
"name": "name2",
|
||||||
|
"message": "desc2",
|
||||||
|
"handler": 0,
|
||||||
|
"frequency": "60s",
|
||||||
|
"severity": "warning",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"type": "query",
|
||||||
|
"query": {"params": ["B", "5m", "now"]},
|
||||||
|
"reducer": {"type": "avg", "params": []},
|
||||||
|
"evaluator": {"type": ">", "params": [100]}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"id": 57,
|
||||||
|
"title": "Graphite 4",
|
||||||
|
"originalTitle": "Graphite 4",
|
||||||
|
"tags": ["graphite"],
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"title": "Active desktop users",
|
||||||
|
"editable": true,
|
||||||
|
"type": "graph",
|
||||||
|
"id": 3,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"refId": "A",
|
||||||
|
"target": "aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"datasource": null,
|
||||||
|
"alert": {
|
||||||
|
"name": "name1",
|
||||||
|
"message": "desc1",
|
||||||
|
"handler": 1,
|
||||||
|
"frequency": "60s",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"type": "query",
|
||||||
|
"query": {"params": ["A", "5m", "now"]},
|
||||||
|
"reducer": {"type": "avg", "params": []},
|
||||||
|
"evaluator": {"type": ">", "params": [100]}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Active mobile users",
|
||||||
|
"id": 4,
|
||||||
|
"targets": [
|
||||||
|
{"refId": "A", "target": ""},
|
||||||
|
{"refId": "B", "target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}
|
||||||
|
],
|
||||||
|
"datasource": "graphite2",
|
||||||
|
"alert": {
|
||||||
|
"name": "name2",
|
||||||
|
"message": "desc2",
|
||||||
|
"handler": 0,
|
||||||
|
"frequency": "60s",
|
||||||
|
"severity": "warning",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"type": "query",
|
||||||
|
"query": {"params": ["B", "5m", "now"]},
|
||||||
|
"reducer": {"type": "avg", "params": []},
|
||||||
|
"evaluator": {"type": ">", "params": [100]}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -345,8 +345,9 @@ func GetDashboards(query *m.GetDashboardsQuery) error {
|
|||||||
|
|
||||||
func GetDashboardsByPluginId(query *m.GetDashboardsByPluginIdQuery) error {
|
func GetDashboardsByPluginId(query *m.GetDashboardsByPluginIdQuery) error {
|
||||||
var dashboards = make([]*m.Dashboard, 0)
|
var dashboards = make([]*m.Dashboard, 0)
|
||||||
|
whereExpr := "org_id=? AND plugin_id=? AND is_folder=" + dialect.BooleanStr(false)
|
||||||
|
|
||||||
err := x.Where("org_id=? AND plugin_id=? AND is_folder=0", query.OrgId, query.PluginId).Find(&dashboards)
|
err := x.Where(whereExpr, query.OrgId, query.PluginId).Find(&dashboards)
|
||||||
query.Result = dashboards
|
query.Result = dashboards
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -170,7 +170,12 @@ func GetDashboardAclInfoList(query *m.GetDashboardAclInfoListQuery) error {
|
|||||||
FROM dashboard_acl as da,
|
FROM dashboard_acl as da,
|
||||||
dashboard as dash
|
dashboard as dash
|
||||||
LEFT JOIN dashboard folder on dash.folder_id = folder.id
|
LEFT JOIN dashboard folder on dash.folder_id = folder.id
|
||||||
WHERE dash.id = ? AND (dash.has_acl = 0 or folder.has_acl = 0) AND da.dashboard_id = -1
|
WHERE
|
||||||
|
dash.id = ? AND (
|
||||||
|
dash.has_acl = ` + dialect.BooleanStr(false) + ` or
|
||||||
|
folder.has_acl = ` + dialect.BooleanStr(false) + `
|
||||||
|
) AND
|
||||||
|
da.dashboard_id = -1
|
||||||
`
|
`
|
||||||
|
|
||||||
query.Result = make([]*m.DashboardAclInfoDTO, 0)
|
query.Result = make([]*m.DashboardAclInfoDTO, 0)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ func addTeamMigrations(mg *Migrator) {
|
|||||||
Name: "team",
|
Name: "team",
|
||||||
Columns: []*Column{
|
Columns: []*Column{
|
||||||
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||||
{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
|
{Name: "name", Type: DB_NVarchar, Length: 190, Nullable: false},
|
||||||
{Name: "org_id", Type: DB_BigInt},
|
{Name: "org_id", Type: DB_BigInt},
|
||||||
{Name: "created", Type: DB_DateTime, Nullable: false},
|
{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||||
{Name: "updated", Type: DB_DateTime, Nullable: false},
|
{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||||
|
|||||||
@@ -175,14 +175,14 @@ func (sb *SearchBuilder) buildSearchWhereClause() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sb.signedInUser.OrgRole != m.ROLE_ADMIN {
|
if sb.signedInUser.OrgRole != m.ROLE_ADMIN {
|
||||||
allowedDashboardsSubQuery := ` AND (dashboard.has_acl = 0 OR dashboard.id in (
|
allowedDashboardsSubQuery := ` AND (dashboard.has_acl = ` + dialect.BooleanStr(false) + ` OR dashboard.id in (
|
||||||
SELECT distinct d.id AS DashboardId
|
SELECT distinct d.id AS DashboardId
|
||||||
FROM dashboard AS d
|
FROM dashboard AS d
|
||||||
LEFT JOIN dashboard_acl as da on d.folder_id = da.dashboard_id or d.id = da.dashboard_id
|
LEFT JOIN dashboard_acl as da on d.folder_id = da.dashboard_id or d.id = da.dashboard_id
|
||||||
LEFT JOIN team_member as ugm on ugm.team_id = da.team_id
|
LEFT JOIN team_member as ugm on ugm.team_id = da.team_id
|
||||||
LEFT JOIN org_user ou on ou.role = da.role
|
LEFT JOIN org_user ou on ou.role = da.role
|
||||||
WHERE
|
WHERE
|
||||||
d.has_acl = 1 and
|
d.has_acl = ` + dialect.BooleanStr(true) + ` and
|
||||||
(da.user_id = ? or ugm.user_id = ? or ou.id is not null)
|
(da.user_id = ? or ugm.user_id = ? or ou.id is not null)
|
||||||
and d.org_id = ?
|
and d.org_id = ?
|
||||||
)
|
)
|
||||||
@@ -198,11 +198,11 @@ func (sb *SearchBuilder) buildSearchWhereClause() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sb.whereTypeFolder {
|
if sb.whereTypeFolder {
|
||||||
sb.sql.WriteString(" AND dashboard.is_folder = 1")
|
sb.sql.WriteString(" AND dashboard.is_folder = " + dialect.BooleanStr(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
if sb.whereTypeDash {
|
if sb.whereTypeDash {
|
||||||
sb.sql.WriteString(" AND dashboard.is_folder = 0")
|
sb.sql.WriteString(" AND dashboard.is_folder = " + dialect.BooleanStr(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sb.whereFolderIds) > 0 {
|
if len(sb.whereFolderIds) > 0 {
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ func init() {
|
|||||||
"AWS/Events": {"RuleName"},
|
"AWS/Events": {"RuleName"},
|
||||||
"AWS/Firehose": {"DeliveryStreamName"},
|
"AWS/Firehose": {"DeliveryStreamName"},
|
||||||
"AWS/IoT": {"Protocol"},
|
"AWS/IoT": {"Protocol"},
|
||||||
"AWS/Kinesis": {"StreamName", "ShardID"},
|
"AWS/Kinesis": {"StreamName", "ShardId"},
|
||||||
"AWS/KinesisAnalytics": {"Flow", "Id", "Application"},
|
"AWS/KinesisAnalytics": {"Flow", "Id", "Application"},
|
||||||
"AWS/Lambda": {"FunctionName", "Resource", "Version", "Alias"},
|
"AWS/Lambda": {"FunctionName", "Resource", "Version", "Alias"},
|
||||||
"AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"},
|
"AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"},
|
||||||
|
|||||||
+66
-44
@@ -21,12 +21,12 @@ import _ from 'lodash';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
// add move to lodash for backward compatabiltiy
|
// add move to lodash for backward compatabiltiy
|
||||||
_.move = function (array, fromIndex, toIndex) {
|
_.move = function(array, fromIndex, toIndex) {
|
||||||
array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]);
|
array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]);
|
||||||
return array;
|
return array;
|
||||||
};
|
};
|
||||||
|
|
||||||
import {coreModule, registerAngularDirectives} from './core/core';
|
import { coreModule, registerAngularDirectives } from './core/core';
|
||||||
|
|
||||||
export class GrafanaApp {
|
export class GrafanaApp {
|
||||||
registerFunctions: any;
|
registerFunctions: any;
|
||||||
@@ -54,36 +54,49 @@ export class GrafanaApp {
|
|||||||
|
|
||||||
moment.locale(config.bootData.user.locale);
|
moment.locale(config.bootData.user.locale);
|
||||||
|
|
||||||
app.config(($locationProvider, $controllerProvider, $compileProvider, $filterProvider, $httpProvider, $provide) => {
|
app.config(
|
||||||
// pre assing bindings before constructor calls
|
(
|
||||||
$compileProvider.preAssignBindingsEnabled(true);
|
$locationProvider,
|
||||||
|
$controllerProvider,
|
||||||
|
$compileProvider,
|
||||||
|
$filterProvider,
|
||||||
|
$httpProvider,
|
||||||
|
$provide
|
||||||
|
) => {
|
||||||
|
// pre assing bindings before constructor calls
|
||||||
|
$compileProvider.preAssignBindingsEnabled(true);
|
||||||
|
|
||||||
if (config.buildInfo.env !== 'development') {
|
if (config.buildInfo.env !== 'development') {
|
||||||
$compileProvider.debugInfoEnabled(false);
|
$compileProvider.debugInfoEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
$httpProvider.useApplyAsync(true);
|
||||||
|
|
||||||
|
this.registerFunctions.controller = $controllerProvider.register;
|
||||||
|
this.registerFunctions.directive = $compileProvider.directive;
|
||||||
|
this.registerFunctions.factory = $provide.factory;
|
||||||
|
this.registerFunctions.service = $provide.service;
|
||||||
|
this.registerFunctions.filter = $filterProvider.register;
|
||||||
|
|
||||||
|
$provide.decorator('$http', [
|
||||||
|
'$delegate',
|
||||||
|
'$templateCache',
|
||||||
|
function($delegate, $templateCache) {
|
||||||
|
var get = $delegate.get;
|
||||||
|
$delegate.get = function(url, config) {
|
||||||
|
if (url.match(/\.html$/)) {
|
||||||
|
// some template's already exist in the cache
|
||||||
|
if (!$templateCache.get(url)) {
|
||||||
|
url += '?v=' + new Date().getTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return get(url, config);
|
||||||
|
};
|
||||||
|
return $delegate;
|
||||||
|
},
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
);
|
||||||
$httpProvider.useApplyAsync(true);
|
|
||||||
|
|
||||||
this.registerFunctions.controller = $controllerProvider.register;
|
|
||||||
this.registerFunctions.directive = $compileProvider.directive;
|
|
||||||
this.registerFunctions.factory = $provide.factory;
|
|
||||||
this.registerFunctions.service = $provide.service;
|
|
||||||
this.registerFunctions.filter = $filterProvider.register;
|
|
||||||
|
|
||||||
$provide.decorator("$http", ["$delegate", "$templateCache", function($delegate, $templateCache) {
|
|
||||||
var get = $delegate.get;
|
|
||||||
$delegate.get = function(url, config) {
|
|
||||||
if (url.match(/\.html$/)) {
|
|
||||||
// some template's already exist in the cache
|
|
||||||
if (!$templateCache.get(url)) {
|
|
||||||
url += "?v=" + new Date().getTime();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return get(url, config);
|
|
||||||
};
|
|
||||||
return $delegate;
|
|
||||||
}]);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.ngModuleDependencies = [
|
this.ngModuleDependencies = [
|
||||||
'grafana.core',
|
'grafana.core',
|
||||||
@@ -95,10 +108,17 @@ export class GrafanaApp {
|
|||||||
'pasvaz.bindonce',
|
'pasvaz.bindonce',
|
||||||
'ui.bootstrap',
|
'ui.bootstrap',
|
||||||
'ui.bootstrap.tpls',
|
'ui.bootstrap.tpls',
|
||||||
'react'
|
'react',
|
||||||
];
|
];
|
||||||
|
|
||||||
var module_types = ['controllers', 'directives', 'factories', 'services', 'filters', 'routes'];
|
var module_types = [
|
||||||
|
'controllers',
|
||||||
|
'directives',
|
||||||
|
'factories',
|
||||||
|
'services',
|
||||||
|
'filters',
|
||||||
|
'routes',
|
||||||
|
];
|
||||||
|
|
||||||
_.each(module_types, type => {
|
_.each(module_types, type => {
|
||||||
var moduleName = 'grafana.' + type;
|
var moduleName = 'grafana.' + type;
|
||||||
@@ -113,20 +133,22 @@ export class GrafanaApp {
|
|||||||
|
|
||||||
var preBootRequires = [System.import('app/features/all')];
|
var preBootRequires = [System.import('app/features/all')];
|
||||||
|
|
||||||
Promise.all(preBootRequires).then(() => {
|
Promise.all(preBootRequires)
|
||||||
// disable tool tip animation
|
.then(() => {
|
||||||
$.fn.tooltip.defaults.animation = false;
|
// disable tool tip animation
|
||||||
// bootstrap the app
|
$.fn.tooltip.defaults.animation = false;
|
||||||
angular.bootstrap(document, this.ngModuleDependencies).invoke(() => {
|
// bootstrap the app
|
||||||
_.each(this.preBootModules, module => {
|
angular.bootstrap(document, this.ngModuleDependencies).invoke(() => {
|
||||||
_.extend(module, this.registerFunctions);
|
_.each(this.preBootModules, module => {
|
||||||
});
|
_.extend(module, this.registerFunctions);
|
||||||
|
});
|
||||||
|
|
||||||
this.preBootModules = null;
|
this.preBootModules = null;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
console.log('Application boot failed:', err);
|
||||||
});
|
});
|
||||||
}).catch(function(err) {
|
|
||||||
console.log('Application boot failed:', err);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {Emitter} from './utils/emitter';
|
import { Emitter } from './utils/emitter';
|
||||||
|
|
||||||
var appEvents = new Emitter();
|
var appEvents = new Emitter();
|
||||||
export default appEvents;
|
export default appEvents;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { NavModel, NavModelItem } from '../../nav_model_srv';
|
import { NavModel, NavModelItem } from "../../nav_model_srv";
|
||||||
import classNames from 'classnames';
|
import classNames from "classnames";
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from "app/core/app_events";
|
||||||
|
|
||||||
export interface IProps {
|
export interface IProps {
|
||||||
model: NavModel;
|
model: NavModel;
|
||||||
@@ -9,12 +9,12 @@ export interface IProps {
|
|||||||
|
|
||||||
function TabItem(tab: NavModelItem) {
|
function TabItem(tab: NavModelItem) {
|
||||||
if (tab.hideFromTabs) {
|
if (tab.hideFromTabs) {
|
||||||
return (null);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tabClasses = classNames({
|
let tabClasses = classNames({
|
||||||
'gf-tabs-link': true,
|
"gf-tabs-link": true,
|
||||||
active: tab.active,
|
active: tab.active
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -28,8 +28,9 @@ function TabItem(tab: NavModelItem) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SelectOption(navItem: NavModelItem) {
|
function SelectOption(navItem: NavModelItem) {
|
||||||
if (navItem.hideFromTabs) { // TODO: Rename hideFromTabs => hideFromNav
|
if (navItem.hideFromTabs) {
|
||||||
return (null);
|
// TODO: Rename hideFromTabs => hideFromNav
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -39,14 +40,22 @@ function SelectOption(navItem: NavModelItem) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Navigation({main}: {main: NavModelItem}) {
|
function Navigation({ main }: { main: NavModelItem }) {
|
||||||
return (<nav>
|
return (
|
||||||
<SelectNav customCss="page-header__select_nav" main={main} />
|
<nav>
|
||||||
<Tabs customCss="page-header__tabs" main={main} />
|
<SelectNav customCss="page-header__select-nav" main={main} />
|
||||||
</nav>);
|
<Tabs customCss="page-header__tabs" main={main} />
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectNav({main, customCss}: {main: NavModelItem, customCss: string}) {
|
function SelectNav({
|
||||||
|
main,
|
||||||
|
customCss
|
||||||
|
}: {
|
||||||
|
main: NavModelItem;
|
||||||
|
customCss: string;
|
||||||
|
}) {
|
||||||
const defaultSelectedItem = main.children.find(navItem => {
|
const defaultSelectedItem = main.children.find(navItem => {
|
||||||
return navItem.active === true;
|
return navItem.active === true;
|
||||||
});
|
});
|
||||||
@@ -54,17 +63,32 @@ function SelectNav({main, customCss}: {main: NavModelItem, customCss: string}) {
|
|||||||
const gotoUrl = evt => {
|
const gotoUrl = evt => {
|
||||||
var element = evt.target;
|
var element = evt.target;
|
||||||
var url = element.options[element.selectedIndex].value;
|
var url = element.options[element.selectedIndex].value;
|
||||||
appEvents.emit('location-change', {href: url});
|
appEvents.emit("location-change", { href: url });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (<select
|
return (
|
||||||
className={`gf-select-nav ${customCss}`}
|
<div className={`gf-form-select-wrapper width-20 ${customCss}`}>
|
||||||
defaultValue={defaultSelectedItem.url}
|
<label
|
||||||
onChange={gotoUrl}>{main.children.map(SelectOption)}</select>);
|
className={`gf-form-select-icon ${defaultSelectedItem.icon}`}
|
||||||
|
htmlFor="page-header-select-nav"
|
||||||
|
/>
|
||||||
|
{/* Label to make it clickable */}
|
||||||
|
<select
|
||||||
|
className="gf-select-nav gf-form-input"
|
||||||
|
defaultValue={defaultSelectedItem.url}
|
||||||
|
onChange={gotoUrl}
|
||||||
|
id="page-header-select-nav"
|
||||||
|
>
|
||||||
|
{main.children.map(SelectOption)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Tabs({main, customCss}: {main: NavModelItem, customCss: string}) {
|
function Tabs({ main, customCss }: { main: NavModelItem; customCss: string }) {
|
||||||
return <ul className={`gf-tabs ${customCss}`}>{main.children.map(TabItem)}</ul>;
|
return (
|
||||||
|
<ul className={`gf-tabs ${customCss}`}>{main.children.map(TabItem)}</ul>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class PageHeader extends React.Component<IProps, any> {
|
export default class PageHeader extends React.Component<IProps, any> {
|
||||||
@@ -77,7 +101,11 @@ export default class PageHeader extends React.Component<IProps, any> {
|
|||||||
for (let i = 0; i < breadcrumbs.length; i++) {
|
for (let i = 0; i < breadcrumbs.length; i++) {
|
||||||
const bc = breadcrumbs[i];
|
const bc = breadcrumbs[i];
|
||||||
if (bc.url) {
|
if (bc.url) {
|
||||||
breadcrumbsResult.push(<a className="text-link" key={i} href={bc.url}>{bc.title}</a>);
|
breadcrumbsResult.push(
|
||||||
|
<a className="text-link" key={i} href={bc.url}>
|
||||||
|
{bc.title}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
breadcrumbsResult.push(<span key={i}> / {bc.title}</span>);
|
breadcrumbsResult.push(<span key={i}> / {bc.title}</span>);
|
||||||
}
|
}
|
||||||
@@ -95,12 +123,15 @@ export default class PageHeader extends React.Component<IProps, any> {
|
|||||||
|
|
||||||
<div className="page-header__info-block">
|
<div className="page-header__info-block">
|
||||||
{main.text && <h1 className="page-header__title">{main.text}</h1>}
|
{main.text && <h1 className="page-header__title">{main.text}</h1>}
|
||||||
{main.breadcrumbs && main.breadcrumbs.length > 0 && (
|
{main.breadcrumbs &&
|
||||||
<h1 className="page-header__title">
|
main.breadcrumbs.length > 0 && (
|
||||||
{this.renderBreadcrumb(main.breadcrumbs)}
|
<h1 className="page-header__title">
|
||||||
</h1>)
|
{this.renderBreadcrumb(main.breadcrumbs)}
|
||||||
}
|
</h1>
|
||||||
{main.subTitle && <div className="page-header__sub-title">{main.subTitle}</div>}
|
)}
|
||||||
|
{main.subTitle && (
|
||||||
|
<div className="page-header__sub-title">{main.subTitle}</div>
|
||||||
|
)}
|
||||||
{main.subType && (
|
{main.subType && (
|
||||||
<div className="page-header__stamps">
|
<div className="page-header__stamps">
|
||||||
<i className={main.subType.icon} />
|
<i className={main.subType.icon} />
|
||||||
|
|||||||
@@ -38,10 +38,12 @@ import 'brace/mode/sql';
|
|||||||
import 'brace/snippets/sql';
|
import 'brace/snippets/sql';
|
||||||
import 'brace/mode/markdown';
|
import 'brace/mode/markdown';
|
||||||
import 'brace/snippets/markdown';
|
import 'brace/snippets/markdown';
|
||||||
|
import 'brace/mode/json';
|
||||||
|
import 'brace/snippets/json';
|
||||||
|
|
||||||
const DEFAULT_THEME_DARK = "ace/theme/grafana-dark";
|
const DEFAULT_THEME_DARK = 'ace/theme/grafana-dark';
|
||||||
const DEFAULT_THEME_LIGHT = "ace/theme/textmate";
|
const DEFAULT_THEME_LIGHT = 'ace/theme/textmate';
|
||||||
const DEFAULT_MODE = "text";
|
const DEFAULT_MODE = 'text';
|
||||||
const DEFAULT_MAX_LINES = 10;
|
const DEFAULT_MAX_LINES = 10;
|
||||||
const DEFAULT_TAB_SIZE = 2;
|
const DEFAULT_TAB_SIZE = 2;
|
||||||
const DEFAULT_BEHAVIOURS = true;
|
const DEFAULT_BEHAVIOURS = true;
|
||||||
@@ -54,7 +56,9 @@ function link(scope, elem, attrs) {
|
|||||||
let maxLines = attrs.maxLines || DEFAULT_MAX_LINES;
|
let maxLines = attrs.maxLines || DEFAULT_MAX_LINES;
|
||||||
let showGutter = attrs.showGutter !== undefined;
|
let showGutter = attrs.showGutter !== undefined;
|
||||||
let tabSize = attrs.tabSize || DEFAULT_TAB_SIZE;
|
let tabSize = attrs.tabSize || DEFAULT_TAB_SIZE;
|
||||||
let behavioursEnabled = attrs.behavioursEnabled ? attrs.behavioursEnabled === 'true' : DEFAULT_BEHAVIOURS;
|
let behavioursEnabled = attrs.behavioursEnabled
|
||||||
|
? attrs.behavioursEnabled === 'true'
|
||||||
|
: DEFAULT_BEHAVIOURS;
|
||||||
|
|
||||||
// Initialize editor
|
// Initialize editor
|
||||||
let aceElem = elem.get(0);
|
let aceElem = elem.get(0);
|
||||||
@@ -68,7 +72,7 @@ function link(scope, elem, attrs) {
|
|||||||
behavioursEnabled: behavioursEnabled,
|
behavioursEnabled: behavioursEnabled,
|
||||||
highlightActiveLine: false,
|
highlightActiveLine: false,
|
||||||
showPrintMargin: false,
|
showPrintMargin: false,
|
||||||
autoScrollEditorIntoView: true // this is needed if editor is inside scrollable page
|
autoScrollEditorIntoView: true, // this is needed if editor is inside scrollable page
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set options
|
// Set options
|
||||||
@@ -84,12 +88,12 @@ function link(scope, elem, attrs) {
|
|||||||
setEditorContent(scope.content);
|
setEditorContent(scope.content);
|
||||||
|
|
||||||
// Add classes
|
// Add classes
|
||||||
elem.addClass("gf-code-editor");
|
elem.addClass('gf-code-editor');
|
||||||
let textarea = elem.find("textarea");
|
let textarea = elem.find('textarea');
|
||||||
textarea.addClass('gf-form-input');
|
textarea.addClass('gf-form-input');
|
||||||
|
|
||||||
if (scope.codeEditorFocus) {
|
if (scope.codeEditorFocus) {
|
||||||
setTimeout(function () {
|
setTimeout(function() {
|
||||||
textarea.focus();
|
textarea.focus();
|
||||||
var domEl = textarea[0];
|
var domEl = textarea[0];
|
||||||
if (domEl.setSelectionRange) {
|
if (domEl.setSelectionRange) {
|
||||||
@@ -100,7 +104,7 @@ function link(scope, elem, attrs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Event handlers
|
// Event handlers
|
||||||
editorSession.on('change', (e) => {
|
editorSession.on('change', e => {
|
||||||
scope.$apply(() => {
|
scope.$apply(() => {
|
||||||
let newValue = codeEditor.getValue();
|
let newValue = codeEditor.getValue();
|
||||||
scope.content = newValue;
|
scope.content = newValue;
|
||||||
@@ -121,25 +125,25 @@ function link(scope, elem, attrs) {
|
|||||||
scope.onChange();
|
scope.onChange();
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.$on("$destroy", () => {
|
scope.$on('$destroy', () => {
|
||||||
codeEditor.destroy();
|
codeEditor.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Keybindings
|
// Keybindings
|
||||||
codeEditor.commands.addCommand({
|
codeEditor.commands.addCommand({
|
||||||
name: 'executeQuery',
|
name: 'executeQuery',
|
||||||
bindKey: {win: 'Ctrl-Enter', mac: 'Command-Enter'},
|
bindKey: { win: 'Ctrl-Enter', mac: 'Command-Enter' },
|
||||||
exec: () => {
|
exec: () => {
|
||||||
scope.onChange();
|
scope.onChange();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function setLangMode(lang) {
|
function setLangMode(lang) {
|
||||||
ace.acequire("ace/ext/language_tools");
|
ace.acequire('ace/ext/language_tools');
|
||||||
codeEditor.setOptions({
|
codeEditor.setOptions({
|
||||||
enableBasicAutocompletion: true,
|
enableBasicAutocompletion: true,
|
||||||
enableLiveAutocompletion: true,
|
enableLiveAutocompletion: true,
|
||||||
enableSnippets: true
|
enableSnippets: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (scope.getCompleter()) {
|
if (scope.getCompleter()) {
|
||||||
@@ -173,13 +177,13 @@ export function codeEditorDirective() {
|
|||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
template: editorTemplate,
|
template: editorTemplate,
|
||||||
scope: {
|
scope: {
|
||||||
content: "=",
|
content: '=',
|
||||||
datasource: "=",
|
datasource: '=',
|
||||||
codeEditorFocus: "<",
|
codeEditorFocus: '<',
|
||||||
onChange: "&",
|
onChange: '&',
|
||||||
getCompleter: "&"
|
getCompleter: '&',
|
||||||
},
|
},
|
||||||
link: link
|
link: link,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,14 @@ export function spectrumPicker() {
|
|||||||
require: 'ngModel',
|
require: 'ngModel',
|
||||||
scope: true,
|
scope: true,
|
||||||
replace: true,
|
replace: true,
|
||||||
template: '<color-picker color="ngModel.$viewValue" onChange="onColorChange"></color-picker>',
|
template:
|
||||||
|
'<color-picker color="ngModel.$viewValue" onChange="onColorChange"></color-picker>',
|
||||||
link: function(scope, element, attrs, ngModel) {
|
link: function(scope, element, attrs, ngModel) {
|
||||||
scope.ngModel = ngModel;
|
scope.ngModel = ngModel;
|
||||||
scope.onColorChange = (color) => {
|
scope.onColorChange = color => {
|
||||||
ngModel.$setViewValue(color);
|
ngModel.$setViewValue(color);
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
coreModule.directive('spectrumPicker', spectrumPicker);
|
coreModule.directive('spectrumPicker', spectrumPicker);
|
||||||
|
|||||||
@@ -9,15 +9,14 @@ export class DashboardSelectorCtrl {
|
|||||||
options: any;
|
options: any;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private backendSrv) {
|
constructor(private backendSrv) {}
|
||||||
}
|
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
this.options = [{value: 0, text: 'Default'}];
|
this.options = [{ value: 0, text: 'Default' }];
|
||||||
|
|
||||||
return this.backendSrv.search({starred: true}).then(res => {
|
return this.backendSrv.search({ starred: true }).then(res => {
|
||||||
res.forEach(dash => {
|
res.forEach(dash => {
|
||||||
this.options.push({value: dash.id, text: dash.title});
|
this.options.push({ value: dash.id, text: dash.title });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -31,8 +30,8 @@ export function dashboardSelector() {
|
|||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
template: template,
|
template: template,
|
||||||
scope: {
|
scope: {
|
||||||
model: '='
|
model: '=',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ import coreModule from '../../core_module';
|
|||||||
|
|
||||||
function typeaheadMatcher(item) {
|
function typeaheadMatcher(item) {
|
||||||
var str = this.query;
|
var str = this.query;
|
||||||
if (str[0] === '/') { str = str.substring(1); }
|
if (str[0] === '/') {
|
||||||
if (str[str.length - 1] === '/') { str = str.substring(0, str.length-1); }
|
str = str.substring(1);
|
||||||
|
}
|
||||||
|
if (str[str.length - 1] === '/') {
|
||||||
|
str = str.substring(0, str.length - 1);
|
||||||
|
}
|
||||||
return item.toLowerCase().match(str.toLowerCase());
|
return item.toLowerCase().match(str.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,19 +32,26 @@ export class FormDropdownCtrl {
|
|||||||
lookupText: boolean;
|
lookupText: boolean;
|
||||||
|
|
||||||
/** @ngInject **/
|
/** @ngInject **/
|
||||||
constructor(private $scope, $element, private $sce, private templateSrv, private $q) {
|
constructor(
|
||||||
|
private $scope,
|
||||||
|
$element,
|
||||||
|
private $sce,
|
||||||
|
private templateSrv,
|
||||||
|
private $q
|
||||||
|
) {
|
||||||
this.inputElement = $element.find('input').first();
|
this.inputElement = $element.find('input').first();
|
||||||
this.linkElement = $element.find('a').first();
|
this.linkElement = $element.find('a').first();
|
||||||
this.linkMode = true;
|
this.linkMode = true;
|
||||||
this.cancelBlur = null;
|
this.cancelBlur = null;
|
||||||
|
|
||||||
// listen to model changes
|
// listen to model changes
|
||||||
$scope.$watch("ctrl.model", this.modelChanged.bind(this));
|
$scope.$watch('ctrl.model', this.modelChanged.bind(this));
|
||||||
|
|
||||||
if (this.labelMode) {
|
if (this.labelMode) {
|
||||||
this.cssClasses = 'gf-form-label ' + this.cssClass;
|
this.cssClasses = 'gf-form-label ' + this.cssClass;
|
||||||
} else {
|
} else {
|
||||||
this.cssClasses = 'gf-form-input gf-form-input--dropdown ' + this.cssClass;
|
this.cssClasses =
|
||||||
|
'gf-form-input gf-form-input--dropdown ' + this.cssClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inputElement.attr('data-provide', 'typeahead');
|
this.inputElement.attr('data-provide', 'typeahead');
|
||||||
@@ -55,7 +66,7 @@ export class FormDropdownCtrl {
|
|||||||
// modify typeahead lookup
|
// modify typeahead lookup
|
||||||
// this = typeahead
|
// this = typeahead
|
||||||
var typeahead = this.inputElement.data('typeahead');
|
var typeahead = this.inputElement.data('typeahead');
|
||||||
typeahead.lookup = function () {
|
typeahead.lookup = function() {
|
||||||
this.query = this.$element.val() || '';
|
this.query = this.$element.val() || '';
|
||||||
var items = this.source(this.query, $.proxy(this.process, this));
|
var items = this.source(this.query, $.proxy(this.process, this));
|
||||||
return items ? this.process(items) : items;
|
return items ? this.process(items) : items;
|
||||||
@@ -80,7 +91,7 @@ export class FormDropdownCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getOptionsInternal(query) {
|
getOptionsInternal(query) {
|
||||||
var result = this.getOptions({$query: query});
|
var result = this.getOptions({ $query: query });
|
||||||
if (this.isPromiseLike(result)) {
|
if (this.isPromiseLike(result)) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -88,7 +99,7 @@ export class FormDropdownCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isPromiseLike(obj) {
|
isPromiseLike(obj) {
|
||||||
return obj && (typeof obj.then === 'function');
|
return obj && typeof obj.then === 'function';
|
||||||
}
|
}
|
||||||
|
|
||||||
modelChanged() {
|
modelChanged() {
|
||||||
@@ -97,8 +108,8 @@ export class FormDropdownCtrl {
|
|||||||
} else {
|
} else {
|
||||||
// if we have text use it
|
// if we have text use it
|
||||||
if (this.lookupText) {
|
if (this.lookupText) {
|
||||||
this.getOptionsInternal("").then(options => {
|
this.getOptionsInternal('').then(options => {
|
||||||
var item = _.find(options, {value: this.model});
|
var item = _.find(options, { value: this.model });
|
||||||
this.updateDisplay(item ? item.text : this.model);
|
this.updateDisplay(item ? item.text : this.model);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -140,7 +151,9 @@ export class FormDropdownCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switchToLink(fromClick) {
|
switchToLink(fromClick) {
|
||||||
if (this.linkMode && !fromClick) { return; }
|
if (this.linkMode && !fromClick) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
clearTimeout(this.cancelBlur);
|
clearTimeout(this.cancelBlur);
|
||||||
this.cancelBlur = null;
|
this.cancelBlur = null;
|
||||||
@@ -164,7 +177,7 @@ export class FormDropdownCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.$scope.$apply(() => {
|
this.$scope.$apply(() => {
|
||||||
var option = _.find(this.optionCache, {text: text});
|
var option = _.find(this.optionCache, { text: text });
|
||||||
|
|
||||||
if (option) {
|
if (option) {
|
||||||
if (_.isObject(this.model)) {
|
if (_.isObject(this.model)) {
|
||||||
@@ -186,20 +199,24 @@ export class FormDropdownCtrl {
|
|||||||
// property is synced with outerscope
|
// property is synced with outerscope
|
||||||
this.$scope.$$postDigest(() => {
|
this.$scope.$$postDigest(() => {
|
||||||
this.$scope.$apply(() => {
|
this.$scope.$apply(() => {
|
||||||
this.onChange({$option: option});
|
this.onChange({ $option: option });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDisplay(text) {
|
updateDisplay(text) {
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.display = this.$sce.trustAsHtml(this.templateSrv.highlightVariablesAsHtml(text));
|
this.display = this.$sce.trustAsHtml(
|
||||||
|
this.templateSrv.highlightVariablesAsHtml(text)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
open() {
|
open() {
|
||||||
this.inputElement.css('width', (Math.max(this.linkElement.width(), 80) + 16) + 'px');
|
this.inputElement.css(
|
||||||
|
'width',
|
||||||
|
Math.max(this.linkElement.width(), 80) + 16 + 'px'
|
||||||
|
);
|
||||||
|
|
||||||
this.inputElement.show();
|
this.inputElement.show();
|
||||||
this.inputElement.focus();
|
this.inputElement.focus();
|
||||||
@@ -215,7 +232,7 @@ export class FormDropdownCtrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
<input type="text"
|
<input type="text"
|
||||||
data-provide="typeahead"
|
data-provide="typeahead"
|
||||||
class="gf-form-input"
|
class="gf-form-input"
|
||||||
@@ -238,13 +255,13 @@ export function formDropdownDirective() {
|
|||||||
bindToController: true,
|
bindToController: true,
|
||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
scope: {
|
scope: {
|
||||||
model: "=",
|
model: '=',
|
||||||
getOptions: "&",
|
getOptions: '&',
|
||||||
onChange: "&",
|
onChange: '&',
|
||||||
cssClass: "@",
|
cssClass: '@',
|
||||||
allowCustom: "@",
|
allowCustom: '@',
|
||||||
labelMode: "@",
|
labelMode: '@',
|
||||||
lookupText: "@",
|
lookupText: '@',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,15 +27,15 @@ export function gfPageDirective() {
|
|||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
template: template,
|
template: template,
|
||||||
scope: {
|
scope: {
|
||||||
"model": "=",
|
model: '=',
|
||||||
},
|
},
|
||||||
transclude: {
|
transclude: {
|
||||||
'header': '?gfPageHeader',
|
header: '?gfPageHeader',
|
||||||
'body': 'gfPageBody',
|
body: 'gfPageBody',
|
||||||
},
|
},
|
||||||
link: function(scope, elem, attrs) {
|
link: function(scope, elem, attrs) {
|
||||||
console.log(scope);
|
console.log(scope);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,21 @@ import _ from 'lodash';
|
|||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
|
|
||||||
import coreModule from 'app/core/core_module';
|
import coreModule from 'app/core/core_module';
|
||||||
import {profiler} from 'app/core/profiler';
|
import { profiler } from 'app/core/profiler';
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
import Drop from 'tether-drop';
|
import Drop from 'tether-drop';
|
||||||
|
|
||||||
export class GrafanaCtrl {
|
export class GrafanaCtrl {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, alertSrv, utilSrv, $rootScope, $controller, contextSrv, globalEventSrv) {
|
constructor(
|
||||||
|
$scope,
|
||||||
|
alertSrv,
|
||||||
|
utilSrv,
|
||||||
|
$rootScope,
|
||||||
|
$controller,
|
||||||
|
contextSrv,
|
||||||
|
globalEventSrv
|
||||||
|
) {
|
||||||
$scope.init = function() {
|
$scope.init = function() {
|
||||||
$scope.contextSrv = contextSrv;
|
$scope.contextSrv = contextSrv;
|
||||||
|
|
||||||
@@ -27,7 +33,7 @@ export class GrafanaCtrl {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.initDashboard = function(dashboardData, viewScope) {
|
$scope.initDashboard = function(dashboardData, viewScope) {
|
||||||
$scope.appEvent("dashboard-fetch-end", dashboardData);
|
$scope.appEvent('dashboard-fetch-end', dashboardData);
|
||||||
$controller('DashboardCtrl', { $scope: viewScope }).init(dashboardData);
|
$controller('DashboardCtrl', { $scope: viewScope }).init(dashboardData);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -49,13 +55,62 @@ export class GrafanaCtrl {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$rootScope.colors = [
|
$rootScope.colors = [
|
||||||
"#7EB26D","#EAB839","#6ED0E0","#EF843C","#E24D42","#1F78C1","#BA43A9","#705DA0",
|
'#7EB26D',
|
||||||
"#508642","#CCA300","#447EBC","#C15C17","#890F02","#0A437C","#6D1F62","#584477",
|
'#EAB839',
|
||||||
"#B7DBAB","#F4D598","#70DBED","#F9BA8F","#F29191","#82B5D8","#E5A8E2","#AEA2E0",
|
'#6ED0E0',
|
||||||
"#629E51","#E5AC0E","#64B0C8","#E0752D","#BF1B00","#0A50A1","#962D82","#614D93",
|
'#EF843C',
|
||||||
"#9AC48A","#F2C96D","#65C5DB","#F9934E","#EA6460","#5195CE","#D683CE","#806EB7",
|
'#E24D42',
|
||||||
"#3F6833","#967302","#2F575E","#99440A","#58140C","#052B51","#511749","#3F2B5B",
|
'#1F78C1',
|
||||||
"#E0F9D7","#FCEACA","#CFFAFF","#F9E2D2","#FCE2DE","#BADFF4","#F9D9F9","#DEDAF7"
|
'#BA43A9',
|
||||||
|
'#705DA0',
|
||||||
|
'#508642',
|
||||||
|
'#CCA300',
|
||||||
|
'#447EBC',
|
||||||
|
'#C15C17',
|
||||||
|
'#890F02',
|
||||||
|
'#0A437C',
|
||||||
|
'#6D1F62',
|
||||||
|
'#584477',
|
||||||
|
'#B7DBAB',
|
||||||
|
'#F4D598',
|
||||||
|
'#70DBED',
|
||||||
|
'#F9BA8F',
|
||||||
|
'#F29191',
|
||||||
|
'#82B5D8',
|
||||||
|
'#E5A8E2',
|
||||||
|
'#AEA2E0',
|
||||||
|
'#629E51',
|
||||||
|
'#E5AC0E',
|
||||||
|
'#64B0C8',
|
||||||
|
'#E0752D',
|
||||||
|
'#BF1B00',
|
||||||
|
'#0A50A1',
|
||||||
|
'#962D82',
|
||||||
|
'#614D93',
|
||||||
|
'#9AC48A',
|
||||||
|
'#F2C96D',
|
||||||
|
'#65C5DB',
|
||||||
|
'#F9934E',
|
||||||
|
'#EA6460',
|
||||||
|
'#5195CE',
|
||||||
|
'#D683CE',
|
||||||
|
'#806EB7',
|
||||||
|
'#3F6833',
|
||||||
|
'#967302',
|
||||||
|
'#2F575E',
|
||||||
|
'#99440A',
|
||||||
|
'#58140C',
|
||||||
|
'#052B51',
|
||||||
|
'#511749',
|
||||||
|
'#3F2B5B',
|
||||||
|
'#E0F9D7',
|
||||||
|
'#FCEACA',
|
||||||
|
'#CFFAFF',
|
||||||
|
'#F9E2D2',
|
||||||
|
'#FCE2DE',
|
||||||
|
'#BADFF4',
|
||||||
|
'#F9D9F9',
|
||||||
|
'#DEDAF7',
|
||||||
];
|
];
|
||||||
|
|
||||||
$scope.init();
|
$scope.init();
|
||||||
@@ -63,7 +118,12 @@ export class GrafanaCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScope) {
|
export function grafanaAppDirective(
|
||||||
|
playlistSrv,
|
||||||
|
contextSrv,
|
||||||
|
$timeout,
|
||||||
|
$rootScope
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
controller: GrafanaCtrl,
|
controller: GrafanaCtrl,
|
||||||
@@ -92,7 +152,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
|||||||
// tooltip removal fix
|
// tooltip removal fix
|
||||||
// manage page classes
|
// manage page classes
|
||||||
var pageClass;
|
var pageClass;
|
||||||
scope.$on("$routeChangeSuccess", function(evt, data) {
|
scope.$on('$routeChangeSuccess', function(evt, data) {
|
||||||
if (pageClass) {
|
if (pageClass) {
|
||||||
body.removeClass(pageClass);
|
body.removeClass(pageClass);
|
||||||
}
|
}
|
||||||
@@ -107,7 +167,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
|||||||
// clear body class sidemenu states
|
// clear body class sidemenu states
|
||||||
body.removeClass('sidemenu-open--xs');
|
body.removeClass('sidemenu-open--xs');
|
||||||
|
|
||||||
$("#tooltip, .tooltip").remove();
|
$('#tooltip, .tooltip').remove();
|
||||||
|
|
||||||
// check for kiosk url param
|
// check for kiosk url param
|
||||||
if (data.params.kiosk) {
|
if (data.params.kiosk) {
|
||||||
@@ -140,7 +200,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((new Date().getTime() - lastActivity) > inActiveTimeLimit) {
|
if (new Date().getTime() - lastActivity > inActiveTimeLimit) {
|
||||||
activeUser = false;
|
activeUser = false;
|
||||||
body.addClass('user-activity-low');
|
body.addClass('user-activity-low');
|
||||||
// hide sidemenu
|
// hide sidemenu
|
||||||
@@ -148,7 +208,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
|||||||
sidemenuHidden = true;
|
sidemenuHidden = true;
|
||||||
body.removeClass('sidemenu-open');
|
body.removeClass('sidemenu-open');
|
||||||
$timeout(function() {
|
$timeout(function() {
|
||||||
$rootScope.$broadcast("render");
|
$rootScope.$broadcast('render');
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,7 +225,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
|||||||
sidemenuHidden = false;
|
sidemenuHidden = false;
|
||||||
body.addClass('sidemenu-open');
|
body.addClass('sidemenu-open');
|
||||||
$timeout(function() {
|
$timeout(function() {
|
||||||
$rootScope.$broadcast("render");
|
$rootScope.$broadcast('render');
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,7 +269,10 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
|||||||
|
|
||||||
// hide search
|
// hide search
|
||||||
if (body.find('.search-container').length > 0) {
|
if (body.find('.search-container').length > 0) {
|
||||||
if (target.parents('.search-results-container, .search-field-wrapper').length === 0) {
|
if (
|
||||||
|
target.parents('.search-results-container, .search-field-wrapper')
|
||||||
|
.length === 0
|
||||||
|
) {
|
||||||
scope.$apply(function() {
|
scope.$apply(function() {
|
||||||
scope.appEvent('hide-dash-search');
|
scope.appEvent('hide-dash-search');
|
||||||
});
|
});
|
||||||
@@ -218,11 +281,14 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
|||||||
|
|
||||||
// hide popovers
|
// hide popovers
|
||||||
var popover = elem.find('.popover');
|
var popover = elem.find('.popover');
|
||||||
if (popover.length > 0 && target.parents('.graph-legend').length === 0) {
|
if (
|
||||||
|
popover.length > 0 &&
|
||||||
|
target.parents('.graph-legend').length === 0
|
||||||
|
) {
|
||||||
popover.hide();
|
popover.hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,39 +11,45 @@ export class HelpCtrl {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.tabIndex = 0;
|
this.tabIndex = 0;
|
||||||
this.shortcuts = {
|
this.shortcuts = {
|
||||||
'Global': [
|
Global: [
|
||||||
{keys: ['g', 'h'], description: 'Go to Home Dashboard'},
|
{ keys: ['g', 'h'], description: 'Go to Home Dashboard' },
|
||||||
{keys: ['g', 'p'], description: 'Go to Profile'},
|
{ keys: ['g', 'p'], description: 'Go to Profile' },
|
||||||
{keys: ['s', 'o'], description: 'Open search'},
|
{ keys: ['s', 'o'], description: 'Open search' },
|
||||||
{keys: ['s', 's'], description: 'Open search with starred filter'},
|
{ keys: ['s', 's'], description: 'Open search with starred filter' },
|
||||||
{keys: ['s', 't'], description: 'Open search in tags view'},
|
{ keys: ['s', 't'], description: 'Open search in tags view' },
|
||||||
{keys: ['esc'], description: 'Exit edit/setting views'},
|
{ keys: ['esc'], description: 'Exit edit/setting views' },
|
||||||
],
|
],
|
||||||
'Dashboard': [
|
Dashboard: [
|
||||||
{keys: ['mod+s'], description: 'Save dashboard'},
|
{ keys: ['mod+s'], description: 'Save dashboard' },
|
||||||
{keys: ['mod+h'], description: 'Hide row controls'},
|
{ keys: ['mod+h'], description: 'Hide row controls' },
|
||||||
{keys: ['d', 'r'], description: 'Refresh all panels'},
|
{ keys: ['d', 'r'], description: 'Refresh all panels' },
|
||||||
{keys: ['d', 's'], description: 'Dashboard settings'},
|
{ keys: ['d', 's'], description: 'Dashboard settings' },
|
||||||
{keys: ['d', 'v'], description: 'Toggle in-active / view mode'},
|
{ keys: ['d', 'v'], description: 'Toggle in-active / view mode' },
|
||||||
{keys: ['d', 'k'], description: 'Toggle kiosk mode (hides top nav)'},
|
{ keys: ['d', 'k'], description: 'Toggle kiosk mode (hides top nav)' },
|
||||||
{keys: ['d', 'E'], description: 'Expand all rows'},
|
{ keys: ['d', 'E'], description: 'Expand all rows' },
|
||||||
{keys: ['d', 'C'], description: 'Collapse all rows'},
|
{ keys: ['d', 'C'], description: 'Collapse all rows' },
|
||||||
{keys: ['mod+o'], description: 'Toggle shared graph crosshair'},
|
{ keys: ['mod+o'], description: 'Toggle shared graph crosshair' },
|
||||||
],
|
],
|
||||||
'Focused Panel': [
|
'Focused Panel': [
|
||||||
{keys: ['e'], description: 'Toggle panel edit view'},
|
{ keys: ['e'], description: 'Toggle panel edit view' },
|
||||||
{keys: ['v'], description: 'Toggle panel fullscreen view'},
|
{ keys: ['v'], description: 'Toggle panel fullscreen view' },
|
||||||
{keys: ['p', 's'], description: 'Open Panel Share Modal'},
|
{ keys: ['p', 's'], description: 'Open Panel Share Modal' },
|
||||||
{keys: ['p', 'r'], description: 'Remove Panel'},
|
{ keys: ['p', 'r'], description: 'Remove Panel' },
|
||||||
],
|
],
|
||||||
'Focused Row': [
|
'Focused Row': [
|
||||||
{keys: ['r', 'c'], description: 'Collapse Row'},
|
{ keys: ['r', 'c'], description: 'Collapse Row' },
|
||||||
{keys: ['r', 'r'], description: 'Remove Row'},
|
{ keys: ['r', 'r'], description: 'Remove Row' },
|
||||||
],
|
],
|
||||||
'Time Range': [
|
'Time Range': [
|
||||||
{keys: ['t', 'z'], description: 'Zoom out time range'},
|
{ keys: ['t', 'z'], description: 'Zoom out time range' },
|
||||||
{keys: ['t', '<i class="fa fa-long-arrow-left"></i>'], description: 'Move time range back'},
|
{
|
||||||
{keys: ['t', '<i class="fa fa-long-arrow-right"></i>'], description: 'Move time range forward'},
|
keys: ['t', '<i class="fa fa-long-arrow-left"></i>'],
|
||||||
|
description: 'Move time range back',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keys: ['t', '<i class="fa fa-long-arrow-right"></i>'],
|
||||||
|
description: 'Move time range forward',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ export function infoPopover() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
transclude(function(clone, newScope) {
|
transclude(function(clone, newScope) {
|
||||||
var content = document.createElement("div");
|
var content = document.createElement('div');
|
||||||
content.className = 'markdown-html';
|
content.className = 'markdown-html';
|
||||||
|
|
||||||
_.each(clone, (node) => {
|
_.each(clone, node => {
|
||||||
content.appendChild(node);
|
content.appendChild(node);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -43,22 +43,21 @@ export function infoPopover() {
|
|||||||
tetherOptions: {
|
tetherOptions: {
|
||||||
offset: offset,
|
offset: offset,
|
||||||
constraints: [
|
constraints: [
|
||||||
{
|
{
|
||||||
to: 'window',
|
to: 'window',
|
||||||
attachment: 'together',
|
attachment: 'together',
|
||||||
pin: true
|
pin: true,
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var unbind = scope.$on('$destroy', function() {
|
var unbind = scope.$on('$destroy', function() {
|
||||||
drop.destroy();
|
drop.destroy();
|
||||||
unbind();
|
unbind();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* Escapes `"` charachters from string
|
* Escapes `"` charachters from string
|
||||||
*/
|
*/
|
||||||
function escapeString(str: string): string {
|
function escapeString(str: string): string {
|
||||||
return str.replace('"', '\"');
|
return str.replace('"', '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -13,7 +13,7 @@ function escapeString(str: string): string {
|
|||||||
*/
|
*/
|
||||||
export function isObject(value: any): boolean {
|
export function isObject(value: any): boolean {
|
||||||
var type = typeof value;
|
var type = typeof value;
|
||||||
return !!value && (type === 'object');
|
return !!value && type === 'object';
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -29,11 +29,11 @@ export function getObjectName(object: Object): string {
|
|||||||
return 'Object';
|
return 'Object';
|
||||||
}
|
}
|
||||||
if (typeof object === 'object' && !object.constructor) {
|
if (typeof object === 'object' && !object.constructor) {
|
||||||
return 'Object';
|
return 'Object';
|
||||||
}
|
}
|
||||||
|
|
||||||
const funcNameRegex = /function ([^(]*)/;
|
const funcNameRegex = /function ([^(]*)/;
|
||||||
const results = (funcNameRegex).exec((object).constructor.toString());
|
const results = funcNameRegex.exec(object.constructor.toString());
|
||||||
if (results && results.length > 1) {
|
if (results && results.length > 1) {
|
||||||
return results[1];
|
return results[1];
|
||||||
} else {
|
} else {
|
||||||
@@ -45,27 +45,33 @@ export function getObjectName(object: Object): string {
|
|||||||
* Gets type of an object. Returns "null" for null objects
|
* Gets type of an object. Returns "null" for null objects
|
||||||
*/
|
*/
|
||||||
export function getType(object: Object): string {
|
export function getType(object: Object): string {
|
||||||
if (object === null) { return 'null'; }
|
if (object === null) {
|
||||||
|
return 'null';
|
||||||
|
}
|
||||||
return typeof object;
|
return typeof object;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generates inline preview for a JavaScript object based on a value
|
* Generates inline preview for a JavaScript object based on a value
|
||||||
*/
|
*/
|
||||||
export function getValuePreview (object: Object, value: string): string {
|
export function getValuePreview(object: Object, value: string): string {
|
||||||
var type = getType(object);
|
var type = getType(object);
|
||||||
|
|
||||||
if (type === 'null' || type === 'undefined') { return type; }
|
if (type === 'null' || type === 'undefined') {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
if (type === 'string') {
|
if (type === 'string') {
|
||||||
value = '"' + escapeString(value) + '"';
|
value = '"' + escapeString(value) + '"';
|
||||||
}
|
}
|
||||||
if (type === 'function') {
|
if (type === 'function') {
|
||||||
|
|
||||||
// Remove content of the function
|
// Remove content of the function
|
||||||
return object.toString()
|
return (
|
||||||
|
object
|
||||||
|
.toString()
|
||||||
.replace(/[\r\n]/g, '')
|
.replace(/[\r\n]/g, '')
|
||||||
.replace(/\{.*\}/, '') + '{…}';
|
.replace(/\{.*\}/, '') + '{…}'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -97,7 +103,11 @@ export function cssClass(className: string): string {
|
|||||||
* Creates a new DOM element wiht given type and class
|
* Creates a new DOM element wiht given type and class
|
||||||
* TODO: move me to helpers
|
* TODO: move me to helpers
|
||||||
*/
|
*/
|
||||||
export function createElement(type: string, className?: string, content?: Element|string): Element {
|
export function createElement(
|
||||||
|
type: string,
|
||||||
|
className?: string,
|
||||||
|
content?: Element | string
|
||||||
|
): Element {
|
||||||
const el = document.createElement(type);
|
const el = document.createElement(type);
|
||||||
if (className) {
|
if (className) {
|
||||||
el.classList.add(cssClass(className));
|
el.classList.add(cssClass(className));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
getType,
|
getType,
|
||||||
getValuePreview,
|
getValuePreview,
|
||||||
cssClass,
|
cssClass,
|
||||||
createElement
|
createElement,
|
||||||
} from './helpers';
|
} from './helpers';
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@@ -19,7 +19,12 @@ const JSON_DATE_REGEX = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/;
|
|||||||
// When toggleing, don't animated removal or addition of more than a few items
|
// When toggleing, don't animated removal or addition of more than a few items
|
||||||
const MAX_ANIMATED_TOGGLE_ITEMS = 10;
|
const MAX_ANIMATED_TOGGLE_ITEMS = 10;
|
||||||
|
|
||||||
const requestAnimationFrame = window.requestAnimationFrame || function(cb: ()=>void) { cb(); return 0; };
|
const requestAnimationFrame =
|
||||||
|
window.requestAnimationFrame ||
|
||||||
|
function(cb: () => void) {
|
||||||
|
cb();
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
export interface JsonExplorerConfig {
|
export interface JsonExplorerConfig {
|
||||||
animateOpen?: boolean;
|
animateOpen?: boolean;
|
||||||
@@ -30,18 +35,16 @@ export interface JsonExplorerConfig {
|
|||||||
const _defaultConfig: JsonExplorerConfig = {
|
const _defaultConfig: JsonExplorerConfig = {
|
||||||
animateOpen: true,
|
animateOpen: true,
|
||||||
animateClose: true,
|
animateClose: true,
|
||||||
theme: null
|
theme: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class JsonExplorer
|
* @class JsonExplorer
|
||||||
*
|
*
|
||||||
* JsonExplorer allows you to render JSON objects in HTML with a
|
* JsonExplorer allows you to render JSON objects in HTML with a
|
||||||
* **collapsible** navigation.
|
* **collapsible** navigation.
|
||||||
*/
|
*/
|
||||||
export class JsonExplorer {
|
export class JsonExplorer {
|
||||||
|
|
||||||
// Hold the open state after the toggler is used
|
// Hold the open state after the toggler is used
|
||||||
private _isOpen: boolean = null;
|
private _isOpen: boolean = null;
|
||||||
|
|
||||||
@@ -77,9 +80,13 @@ export class JsonExplorer {
|
|||||||
*
|
*
|
||||||
* @param {string} [key=undefined] The key that this object in it's parent
|
* @param {string} [key=undefined] The key that this object in it's parent
|
||||||
* context
|
* context
|
||||||
*/
|
*/
|
||||||
constructor(public json: any, private open = 1, private config: JsonExplorerConfig = _defaultConfig, private key?: string) {
|
constructor(
|
||||||
}
|
public json: any,
|
||||||
|
private open = 1,
|
||||||
|
private config: JsonExplorerConfig = _defaultConfig,
|
||||||
|
private key?: string
|
||||||
|
) {}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* is formatter open?
|
* is formatter open?
|
||||||
@@ -103,17 +110,19 @@ export class JsonExplorer {
|
|||||||
* is this a date string?
|
* is this a date string?
|
||||||
*/
|
*/
|
||||||
private get isDate(): boolean {
|
private get isDate(): boolean {
|
||||||
return (this.type === 'string') &&
|
return (
|
||||||
|
this.type === 'string' &&
|
||||||
(DATE_STRING_REGEX.test(this.json) ||
|
(DATE_STRING_REGEX.test(this.json) ||
|
||||||
JSON_DATE_REGEX.test(this.json) ||
|
JSON_DATE_REGEX.test(this.json) ||
|
||||||
PARTIAL_DATE_REGEX.test(this.json));
|
PARTIAL_DATE_REGEX.test(this.json))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* is this a URL string?
|
* is this a URL string?
|
||||||
*/
|
*/
|
||||||
private get isUrl(): boolean {
|
private get isUrl(): boolean {
|
||||||
return this.type === 'string' && (this.json.indexOf('http') === 0);
|
return this.type === 'string' && this.json.indexOf('http') === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -142,7 +151,9 @@ export class JsonExplorer {
|
|||||||
* is this an empty object or array?
|
* is this an empty object or array?
|
||||||
*/
|
*/
|
||||||
private get isEmpty(): boolean {
|
private get isEmpty(): boolean {
|
||||||
return this.isEmptyObject || (this.keys && !this.keys.length && this.isArray);
|
return (
|
||||||
|
this.isEmptyObject || (this.keys && !this.keys.length && this.isArray)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -174,7 +185,7 @@ export class JsonExplorer {
|
|||||||
*/
|
*/
|
||||||
private get keys(): string[] {
|
private get keys(): string[] {
|
||||||
if (this.isObject) {
|
if (this.isObject) {
|
||||||
return Object.keys(this.json).map((key)=> key ? key : '""');
|
return Object.keys(this.json).map(key => (key ? key : '""'));
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -183,7 +194,7 @@ export class JsonExplorer {
|
|||||||
/**
|
/**
|
||||||
* Toggles `isOpen` state
|
* Toggles `isOpen` state
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
toggleOpen() {
|
toggleOpen() {
|
||||||
this.isOpen = !this.isOpen;
|
this.isOpen = !this.isOpen;
|
||||||
|
|
||||||
@@ -198,17 +209,17 @@ export class JsonExplorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open all children up to a certain depth.
|
* Open all children up to a certain depth.
|
||||||
* Allows actions such as expand all/collapse all
|
* Allows actions such as expand all/collapse all
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
openAtDepth(depth = 1) {
|
openAtDepth(depth = 1) {
|
||||||
if (depth < 0) {
|
if (depth < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.open = depth;
|
this.open = depth;
|
||||||
this.isOpen = (depth !== 0);
|
this.isOpen = depth !== 0;
|
||||||
|
|
||||||
if (this.element) {
|
if (this.element) {
|
||||||
this.removeChildren(false);
|
this.removeChildren(false);
|
||||||
@@ -223,8 +234,11 @@ export class JsonExplorer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isNumberArray() {
|
isNumberArray() {
|
||||||
return (this.json.length > 0 && this.json.length < 4) &&
|
return (
|
||||||
(_.isNumber(this.json[0]) || _.isNumber(this.json[1]));
|
this.json.length > 0 &&
|
||||||
|
this.json.length < 4 &&
|
||||||
|
(_.isNumber(this.json[0]) || _.isNumber(this.json[1]))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderArray() {
|
renderArray() {
|
||||||
@@ -235,13 +249,17 @@ export class JsonExplorer {
|
|||||||
if (this.isNumberArray()) {
|
if (this.isNumberArray()) {
|
||||||
this.json.forEach((val, index) => {
|
this.json.forEach((val, index) => {
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
arrayWrapperSpan.appendChild(createElement('span', 'array-comma', ','));
|
arrayWrapperSpan.appendChild(
|
||||||
|
createElement('span', 'array-comma', ',')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
arrayWrapperSpan.appendChild(createElement('span', 'number', val));
|
arrayWrapperSpan.appendChild(createElement('span', 'number', val));
|
||||||
});
|
});
|
||||||
this.skipChildren = true;
|
this.skipChildren = true;
|
||||||
} else {
|
} else {
|
||||||
arrayWrapperSpan.appendChild(createElement('span', 'number', (this.json.length)));
|
arrayWrapperSpan.appendChild(
|
||||||
|
createElement('span', 'number', this.json.length)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
arrayWrapperSpan.appendChild(createElement('span', 'bracket', ']'));
|
arrayWrapperSpan.appendChild(createElement('span', 'bracket', ']'));
|
||||||
@@ -280,7 +298,11 @@ export class JsonExplorer {
|
|||||||
const objectWrapperSpan = createElement('span');
|
const objectWrapperSpan = createElement('span');
|
||||||
|
|
||||||
// get constructor name and append it to wrapper span
|
// get constructor name and append it to wrapper span
|
||||||
var constructorName = createElement('span', 'constructor-name', this.constructorName);
|
var constructorName = createElement(
|
||||||
|
'span',
|
||||||
|
'constructor-name',
|
||||||
|
this.constructorName
|
||||||
|
);
|
||||||
objectWrapperSpan.appendChild(constructorName);
|
objectWrapperSpan.appendChild(constructorName);
|
||||||
|
|
||||||
// if it's an array append the array specific elements like brackets and length
|
// if it's an array append the array specific elements like brackets and length
|
||||||
@@ -294,7 +316,6 @@ export class JsonExplorer {
|
|||||||
togglerLink.appendChild(value);
|
togglerLink.appendChild(value);
|
||||||
// Primitive values
|
// Primitive values
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// make a value holder element
|
// make a value holder element
|
||||||
const value = this.isUrl ? createElement('a') : createElement('span');
|
const value = this.isUrl ? createElement('a') : createElement('span');
|
||||||
|
|
||||||
@@ -366,17 +387,24 @@ export class JsonExplorer {
|
|||||||
/**
|
/**
|
||||||
* Appends all the children to children element
|
* Appends all the children to children element
|
||||||
* Animated option is used when user triggers this via a click
|
* Animated option is used when user triggers this via a click
|
||||||
*/
|
*/
|
||||||
appendChildren(animated = false) {
|
appendChildren(animated = false) {
|
||||||
const children = this.element.querySelector(`div.${cssClass('children')}`);
|
const children = this.element.querySelector(`div.${cssClass('children')}`);
|
||||||
|
|
||||||
if (!children || this.isEmpty) { return; }
|
if (!children || this.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (animated) {
|
if (animated) {
|
||||||
let index = 0;
|
let index = 0;
|
||||||
const addAChild = ()=> {
|
const addAChild = () => {
|
||||||
const key = this.keys[index];
|
const key = this.keys[index];
|
||||||
const formatter = new JsonExplorer(this.json[key], this.open - 1, this.config, key);
|
const formatter = new JsonExplorer(
|
||||||
|
this.json[key],
|
||||||
|
this.open - 1,
|
||||||
|
this.config,
|
||||||
|
key
|
||||||
|
);
|
||||||
children.appendChild(formatter.render());
|
children.appendChild(formatter.render());
|
||||||
|
|
||||||
index += 1;
|
index += 1;
|
||||||
@@ -391,10 +419,14 @@ export class JsonExplorer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
requestAnimationFrame(addAChild);
|
requestAnimationFrame(addAChild);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.keys.forEach(key => {
|
this.keys.forEach(key => {
|
||||||
const formatter = new JsonExplorer(this.json[key], this.open - 1, this.config, key);
|
const formatter = new JsonExplorer(
|
||||||
|
this.json[key],
|
||||||
|
this.open - 1,
|
||||||
|
this.config,
|
||||||
|
key
|
||||||
|
);
|
||||||
children.appendChild(formatter.render());
|
children.appendChild(formatter.render());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -403,13 +435,15 @@ export class JsonExplorer {
|
|||||||
/**
|
/**
|
||||||
* Removes all the children from children element
|
* Removes all the children from children element
|
||||||
* Animated option is used when user triggers this via a click
|
* Animated option is used when user triggers this via a click
|
||||||
*/
|
*/
|
||||||
removeChildren(animated = false) {
|
removeChildren(animated = false) {
|
||||||
const childrenElement = this.element.querySelector(`div.${cssClass('children')}`) as HTMLDivElement;
|
const childrenElement = this.element.querySelector(
|
||||||
|
`div.${cssClass('children')}`
|
||||||
|
) as HTMLDivElement;
|
||||||
|
|
||||||
if (animated) {
|
if (animated) {
|
||||||
let childrenRemoved = 0;
|
let childrenRemoved = 0;
|
||||||
const removeAChild = ()=> {
|
const removeAChild = () => {
|
||||||
if (childrenElement && childrenElement.children.length) {
|
if (childrenElement && childrenElement.children.length) {
|
||||||
childrenElement.removeChild(childrenElement.children[0]);
|
childrenElement.removeChild(childrenElement.children[0]);
|
||||||
childrenRemoved += 1;
|
childrenRemoved += 1;
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
import coreModule from 'app/core/core_module';
|
import coreModule from 'app/core/core_module';
|
||||||
import {JsonExplorer} from '../json_explorer/json_explorer';
|
import { JsonExplorer } from '../json_explorer/json_explorer';
|
||||||
|
|
||||||
coreModule.directive('jsonTree', [function jsonTreeDirective() {
|
coreModule.directive('jsonTree', [
|
||||||
return{
|
function jsonTreeDirective() {
|
||||||
restrict: 'E',
|
return {
|
||||||
scope: {
|
restrict: 'E',
|
||||||
object: '=',
|
scope: {
|
||||||
startExpanded: '@',
|
object: '=',
|
||||||
rootName: '@',
|
startExpanded: '@',
|
||||||
},
|
rootName: '@',
|
||||||
link: function(scope, elem) {
|
},
|
||||||
|
link: function(scope, elem) {
|
||||||
|
var jsonExp = new JsonExplorer(scope.object, 3, {
|
||||||
|
animateOpen: true,
|
||||||
|
});
|
||||||
|
|
||||||
var jsonExp = new JsonExplorer(scope.object, 3, {
|
const html = jsonExp.render(true);
|
||||||
animateOpen: true
|
elem.html(html);
|
||||||
});
|
},
|
||||||
|
};
|
||||||
const html = jsonExp.render(true);
|
},
|
||||||
elem.html(html);
|
]);
|
||||||
}
|
|
||||||
};
|
|
||||||
}]);
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ export class LayoutSelectorCtrl {
|
|||||||
store.set('grafana.list.layout.mode', 'grid');
|
store.set('grafana.list.layout.mode', 'grid');
|
||||||
this.$rootScope.appEvent('layout-mode-changed', 'grid');
|
this.$rootScope.appEvent('layout-mode-changed', 'grid');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @ngInject **/
|
/** @ngInject **/
|
||||||
@@ -56,12 +55,16 @@ export function layoutMode($rootScope) {
|
|||||||
var className = 'card-list-layout-' + layout;
|
var className = 'card-list-layout-' + layout;
|
||||||
elem.addClass(className);
|
elem.addClass(className);
|
||||||
|
|
||||||
$rootScope.onAppEvent('layout-mode-changed', (evt, newLayout) => {
|
$rootScope.onAppEvent(
|
||||||
elem.removeClass(className);
|
'layout-mode-changed',
|
||||||
className = 'card-list-layout-' + newLayout;
|
(evt, newLayout) => {
|
||||||
elem.addClass(className);
|
elem.removeClass(className);
|
||||||
}, scope);
|
className = 'card-list-layout-' + newLayout;
|
||||||
}
|
elem.addClass(className);
|
||||||
|
},
|
||||||
|
scope
|
||||||
|
);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,20 +60,24 @@
|
|||||||
switch-class="gf-form-switch--transparent gf-form-switch--search-result-filter-row__checkbox"
|
switch-class="gf-form-switch--transparent gf-form-switch--search-result-filter-row__checkbox"
|
||||||
/>
|
/>
|
||||||
<div class="search-results-filter-row__filters">
|
<div class="search-results-filter-row__filters">
|
||||||
<select
|
<div class="gf-form-select-wrapper">
|
||||||
class="search-results-filter-row__filters-item gf-form-input"
|
<select
|
||||||
ng-model="ctrl.selectedStarredFilter"
|
class="search-results-filter-row__filters-item gf-form-input"
|
||||||
ng-options="t.text disable when t.disabled for t in ctrl.starredFilterOptions"
|
ng-model="ctrl.selectedStarredFilter"
|
||||||
ng-change="ctrl.onStarredFilterChange()"
|
ng-options="t.text disable when t.disabled for t in ctrl.starredFilterOptions"
|
||||||
ng-show="!(ctrl.canMove || ctrl.canDelete)"
|
ng-change="ctrl.onStarredFilterChange()"
|
||||||
/>
|
ng-show="!(ctrl.canMove || ctrl.canDelete)"
|
||||||
<select
|
/>
|
||||||
class="search-results-filter-row__filters-item gf-form-input"
|
</div>
|
||||||
ng-model="ctrl.selectedTagFilter"
|
<div class="gf-form-select-wrapper">
|
||||||
ng-options="t.term disable when t.disabled for t in ctrl.tagFilterOptions"
|
<select
|
||||||
ng-change="ctrl.onTagFilterChange()"
|
class="search-results-filter-row__filters-item gf-form-input"
|
||||||
ng-show="!(ctrl.canMove || ctrl.canDelete)"
|
ng-model="ctrl.selectedTagFilter"
|
||||||
/>
|
ng-options="t.term disable when t.disabled for t in ctrl.tagFilterOptions"
|
||||||
|
ng-change="ctrl.onTagFilterChange()"
|
||||||
|
ng-show="!(ctrl.canMove || ctrl.canDelete)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="gf-form-button-row" ng-show="ctrl.canMove || ctrl.canDelete">
|
<div class="gf-form-button-row" ng-show="ctrl.canMove || ctrl.canDelete">
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn gf-form-button btn-inverse"
|
class="btn gf-form-button btn-inverse"
|
||||||
|
|||||||
@@ -13,13 +13,24 @@ export class ManageDashboardsCtrl {
|
|||||||
canMove = false;
|
canMove = false;
|
||||||
hasFilters = false;
|
hasFilters = false;
|
||||||
selectAllChecked = false;
|
selectAllChecked = false;
|
||||||
starredFilterOptions = [{ text: 'Filter by Starred', disabled: true }, { text: 'Yes' }, { text: 'No' }];
|
starredFilterOptions = [
|
||||||
|
{ text: 'Filter by Starred', disabled: true },
|
||||||
|
{ text: 'Yes' },
|
||||||
|
{ text: 'No' },
|
||||||
|
];
|
||||||
selectedStarredFilter: any;
|
selectedStarredFilter: any;
|
||||||
folderId?: number;
|
folderId?: number;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private backendSrv, navModelSrv, private searchSrv: SearchSrv) {
|
constructor(private backendSrv, navModelSrv, private searchSrv: SearchSrv) {
|
||||||
this.query = { query: '', mode: 'tree', tag: [], starred: false, skipRecent: true, skipStarred: true };
|
this.query = {
|
||||||
|
query: '',
|
||||||
|
mode: 'tree',
|
||||||
|
tag: [],
|
||||||
|
starred: false,
|
||||||
|
skipRecent: true,
|
||||||
|
skipStarred: true,
|
||||||
|
};
|
||||||
|
|
||||||
if (this.folderId) {
|
if (this.folderId) {
|
||||||
this.query.folderIds = [this.folderId];
|
this.query.folderIds = [this.folderId];
|
||||||
@@ -33,7 +44,7 @@ export class ManageDashboardsCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDashboards() {
|
getDashboards() {
|
||||||
return this.searchSrv.search(this.query).then((result) => {
|
return this.searchSrv.search(this.query).then(result => {
|
||||||
return this.initDashboardList(result);
|
return this.initDashboardList(result);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -42,7 +53,10 @@ export class ManageDashboardsCtrl {
|
|||||||
this.canMove = false;
|
this.canMove = false;
|
||||||
this.canDelete = false;
|
this.canDelete = false;
|
||||||
this.selectAllChecked = false;
|
this.selectAllChecked = false;
|
||||||
this.hasFilters = this.query.query.length > 0 || this.query.tag.length > 0 || this.query.starred;
|
this.hasFilters =
|
||||||
|
this.query.query.length > 0 ||
|
||||||
|
this.query.tag.length > 0 ||
|
||||||
|
this.query.starred;
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
this.sections = [];
|
this.sections = [];
|
||||||
@@ -79,7 +93,7 @@ export class ManageDashboardsCtrl {
|
|||||||
getFoldersAndDashboardsToDelete() {
|
getFoldersAndDashboardsToDelete() {
|
||||||
let selectedDashboards = {
|
let selectedDashboards = {
|
||||||
folders: [],
|
folders: [],
|
||||||
dashboards: []
|
dashboards: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const section of this.sections) {
|
for (const section of this.sections) {
|
||||||
@@ -112,10 +126,16 @@ export class ManageDashboardsCtrl {
|
|||||||
let text2;
|
let text2;
|
||||||
|
|
||||||
if (folderCount > 0 && dashCount > 0) {
|
if (folderCount > 0 && dashCount > 0) {
|
||||||
text += `selected folder${folderCount === 1 ? '' : 's'} and dashboard${dashCount === 1 ? '' : 's'}?`;
|
text += `selected folder${folderCount === 1 ? '' : 's'} and dashboard${
|
||||||
text2 = `All dashboards of the selected folder${folderCount === 1 ? '' : 's'} will also be deleted`;
|
dashCount === 1 ? '' : 's'
|
||||||
|
}?`;
|
||||||
|
text2 = `All dashboards of the selected folder${
|
||||||
|
folderCount === 1 ? '' : 's'
|
||||||
|
} will also be deleted`;
|
||||||
} else if (folderCount > 0) {
|
} else if (folderCount > 0) {
|
||||||
text += `selected folder${folderCount === 1 ? '' : 's'} and all its dashboards?`;
|
text += `selected folder${
|
||||||
|
folderCount === 1 ? '' : 's'
|
||||||
|
} and all its dashboards?`;
|
||||||
} else {
|
} else {
|
||||||
text += `selected dashboard${dashCount === 1 ? '' : 's'}?`;
|
text += `selected dashboard${dashCount === 1 ? '' : 's'}?`;
|
||||||
}
|
}
|
||||||
@@ -129,7 +149,7 @@ export class ManageDashboardsCtrl {
|
|||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
const foldersAndDashboards = data.folders.concat(data.dashboards);
|
const foldersAndDashboards = data.folders.concat(data.dashboards);
|
||||||
this.deleteFoldersAndDashboards(foldersAndDashboards);
|
this.deleteFoldersAndDashboards(foldersAndDashboards);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,16 +165,22 @@ export class ManageDashboardsCtrl {
|
|||||||
let msg;
|
let msg;
|
||||||
|
|
||||||
if (folderCount > 0 && dashCount > 0) {
|
if (folderCount > 0 && dashCount > 0) {
|
||||||
header = `Folder${folderCount === 1 ? '' : 's'} And Dashboard${dashCount === 1 ? '' : 's'} Deleted`;
|
header = `Folder${folderCount === 1 ? '' : 's'} And Dashboard${
|
||||||
|
dashCount === 1 ? '' : 's'
|
||||||
|
} Deleted`;
|
||||||
msg = `${folderCount} folder${folderCount === 1 ? '' : 's'} `;
|
msg = `${folderCount} folder${folderCount === 1 ? '' : 's'} `;
|
||||||
msg += `and ${dashCount} dashboard${dashCount === 1 ? '' : 's'} has been deleted`;
|
msg += `and ${dashCount} dashboard${
|
||||||
|
dashCount === 1 ? '' : 's'
|
||||||
|
} has been deleted`;
|
||||||
} else if (folderCount > 0) {
|
} else if (folderCount > 0) {
|
||||||
header = `Folder${folderCount === 1 ? '' : 's'} Deleted`;
|
header = `Folder${folderCount === 1 ? '' : 's'} Deleted`;
|
||||||
|
|
||||||
if (folderCount === 1) {
|
if (folderCount === 1) {
|
||||||
msg = `${folders[0].dashboard.title} has been deleted`;
|
msg = `${folders[0].dashboard.title} has been deleted`;
|
||||||
} else {
|
} else {
|
||||||
msg = `${folderCount} folder${folderCount === 1 ? '' : 's'} has been deleted`;
|
msg = `${folderCount} folder${
|
||||||
|
folderCount === 1 ? '' : 's'
|
||||||
|
} has been deleted`;
|
||||||
}
|
}
|
||||||
} else if (dashCount > 0) {
|
} else if (dashCount > 0) {
|
||||||
header = `Dashboard${dashCount === 1 ? '' : 's'} Deleted`;
|
header = `Dashboard${dashCount === 1 ? '' : 's'} Deleted`;
|
||||||
@@ -162,7 +188,9 @@ export class ManageDashboardsCtrl {
|
|||||||
if (dashCount === 1) {
|
if (dashCount === 1) {
|
||||||
msg = `${dashboards[0].dashboard.title} has been deleted`;
|
msg = `${dashboards[0].dashboard.title} has been deleted`;
|
||||||
} else {
|
} else {
|
||||||
msg = `${dashCount} dashboard${dashCount === 1 ? '' : 's'} has been deleted`;
|
msg = `${dashCount} dashboard${
|
||||||
|
dashCount === 1 ? '' : 's'
|
||||||
|
} has been deleted`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,19 +215,25 @@ export class ManageDashboardsCtrl {
|
|||||||
moveTo() {
|
moveTo() {
|
||||||
const selectedDashboards = this.getDashboardsToMove();
|
const selectedDashboards = this.getDashboardsToMove();
|
||||||
|
|
||||||
const template = '<move-to-folder-modal dismiss="dismiss()" ' +
|
const template =
|
||||||
|
'<move-to-folder-modal dismiss="dismiss()" ' +
|
||||||
'dashboards="model.dashboards" after-save="model.afterSave()">' +
|
'dashboards="model.dashboards" after-save="model.afterSave()">' +
|
||||||
'</move-to-folder-modal>`';
|
'</move-to-folder-modal>`';
|
||||||
appEvents.emit('show-modal', {
|
appEvents.emit('show-modal', {
|
||||||
templateHtml: template,
|
templateHtml: template,
|
||||||
modalClass: 'modal--narrow',
|
modalClass: 'modal--narrow',
|
||||||
model: { dashboards: selectedDashboards, afterSave: this.getDashboards.bind(this) }
|
model: {
|
||||||
|
dashboards: selectedDashboards,
|
||||||
|
afterSave: this.getDashboards.bind(this),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getTags() {
|
getTags() {
|
||||||
return this.searchSrv.getDashboardTags().then((results) => {
|
return this.searchSrv.getDashboardTags().then(results => {
|
||||||
this.tagFilterOptions = [{ term: 'Filter By Tag', disabled: true }].concat(results);
|
this.tagFilterOptions = [
|
||||||
|
{ term: 'Filter By Tag', disabled: true },
|
||||||
|
].concat(results);
|
||||||
this.selectedTagFilter = this.tagFilterOptions[0];
|
this.selectedTagFilter = this.tagFilterOptions[0];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -248,7 +282,7 @@ export class ManageDashboardsCtrl {
|
|||||||
section.checked = this.selectAllChecked;
|
section.checked = this.selectAllChecked;
|
||||||
}
|
}
|
||||||
|
|
||||||
section.items = _.map(section.items, (item) => {
|
section.items = _.map(section.items, item => {
|
||||||
item.checked = this.selectAllChecked;
|
item.checked = this.selectAllChecked;
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
@@ -268,13 +302,14 @@ export class ManageDashboardsCtrl {
|
|||||||
export function manageDashboardsDirective() {
|
export function manageDashboardsDirective() {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
templateUrl: 'public/app/core/components/manage_dashboards/manage_dashboards.html',
|
templateUrl:
|
||||||
|
'public/app/core/components/manage_dashboards/manage_dashboards.html',
|
||||||
controller: ManageDashboardsCtrl,
|
controller: ManageDashboardsCtrl,
|
||||||
bindToController: true,
|
bindToController: true,
|
||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
scope: {
|
scope: {
|
||||||
folderId: '='
|
folderId: '=',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import coreModule from '../../core_module';
|
import coreModule from '../../core_module';
|
||||||
import {NavModel} from '../../nav_model_srv';
|
import { NavModel } from '../../nav_model_srv';
|
||||||
|
import appEvents from 'app/core/app_events';
|
||||||
|
|
||||||
export class NavbarCtrl {
|
export class NavbarCtrl {
|
||||||
model: NavModel;
|
model: NavModel;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $rootScope) {
|
constructor() {}
|
||||||
}
|
|
||||||
|
|
||||||
showSearch() {
|
showSearch() {
|
||||||
this.$rootScope.appEvent('show-dash-search');
|
appEvents.emit('show-dash-search');
|
||||||
}
|
}
|
||||||
|
|
||||||
navItemClicked(navItem, evt) {
|
navItemClicked(navItem, evt) {
|
||||||
@@ -28,10 +28,9 @@ export function navbarDirective() {
|
|||||||
bindToController: true,
|
bindToController: true,
|
||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
scope: {
|
scope: {
|
||||||
model: "=",
|
model: '=',
|
||||||
},
|
},
|
||||||
link: function(scope, elem) {
|
link: function(scope, elem) {},
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,11 +45,10 @@ export function pageH1() {
|
|||||||
</h1>
|
</h1>
|
||||||
`,
|
`,
|
||||||
scope: {
|
scope: {
|
||||||
model: "=",
|
model: '=',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
coreModule.directive('pageH1', pageH1);
|
coreModule.directive('pageH1', pageH1);
|
||||||
coreModule.directive('navbar', navbarDirective);
|
coreModule.directive('navbar', navbarDirective);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
///<reference path="../../headers/common.d.ts" />
|
///<reference path="../../headers/common.d.ts" />
|
||||||
|
|
||||||
import coreModule from 'app/core/core_module';
|
import coreModule from 'app/core/core_module';
|
||||||
import {contextSrv} from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
@@ -63,7 +63,9 @@ export class OrgSwitchCtrl {
|
|||||||
setUsingOrg(org) {
|
setUsingOrg(org) {
|
||||||
return this.backendSrv.post('/api/user/using/' + org.orgId).then(() => {
|
return this.backendSrv.post('/api/user/using/' + org.orgId).then(() => {
|
||||||
const re = /orgId=\d+/gi;
|
const re = /orgId=\d+/gi;
|
||||||
this.setWindowLocationHref(this.getWindowLocationHref().replace(re, 'orgId=' + org.orgId));
|
this.setWindowLocationHref(
|
||||||
|
this.getWindowLocationHref().replace(re, 'orgId=' + org.orgId)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +85,7 @@ export function orgSwitcher() {
|
|||||||
controller: OrgSwitchCtrl,
|
controller: OrgSwitchCtrl,
|
||||||
bindToController: true,
|
bindToController: true,
|
||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
scope: {dismiss: "&"},
|
scope: { dismiss: '&' },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export class QueryPart {
|
|||||||
this.part = part;
|
this.part = part;
|
||||||
this.def = def;
|
this.def = def;
|
||||||
if (!this.def) {
|
if (!this.def) {
|
||||||
throw {message: 'Could not find query part ' + part.type};
|
throw { message: 'Could not find query part ' + part.type };
|
||||||
}
|
}
|
||||||
|
|
||||||
part.params = part.params || _.clone(this.def.defaultParams);
|
part.params = part.params || _.clone(this.def.defaultParams);
|
||||||
@@ -42,7 +42,7 @@ export class QueryPart {
|
|||||||
return this.def.renderer(this, innerExpr);
|
return this.def.renderer(this, innerExpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasMultipleParamsInString (strValue, index) {
|
hasMultipleParamsInString(strValue, index) {
|
||||||
if (strValue.indexOf(',') === -1) {
|
if (strValue.indexOf(',') === -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,7 @@ export class QueryPart {
|
|||||||
return this.def.params[index + 1] && this.def.params[index + 1].optional;
|
return this.def.params[index + 1] && this.def.params[index + 1].optional;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParam (strValue, index) {
|
updateParam(strValue, index) {
|
||||||
// handle optional parameters
|
// handle optional parameters
|
||||||
// if string contains ',' and next param is optional, split and update both
|
// if string contains ',' and next param is optional, split and update both
|
||||||
if (this.hasMultipleParamsInString(strValue, index)) {
|
if (this.hasMultipleParamsInString(strValue, index)) {
|
||||||
@@ -107,7 +107,6 @@ export function functionRenderer(part, innerExpr) {
|
|||||||
return str + parameters.join(', ') + ')';
|
return str + parameters.join(', ') + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function suffixRenderer(part, innerExpr) {
|
export function suffixRenderer(part, innerExpr) {
|
||||||
return innerExpr + ' ' + part.params[0];
|
return innerExpr + ' ' + part.params[0];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,17 +15,17 @@ var template = `
|
|||||||
</ul>
|
</ul>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
export function queryPartEditorDirective($compile, templateSrv) {
|
export function queryPartEditorDirective($compile, templateSrv) {
|
||||||
|
var paramTemplate =
|
||||||
var paramTemplate = '<input type="text" class="hide input-mini tight-form-func-param"></input>';
|
'<input type="text" class="hide input-mini tight-form-func-param"></input>';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
template: template,
|
template: template,
|
||||||
scope: {
|
scope: {
|
||||||
part: "=",
|
part: '=',
|
||||||
handleEvent: "&",
|
handleEvent: '&',
|
||||||
},
|
},
|
||||||
link: function postLink($scope, elem) {
|
link: function postLink($scope, elem) {
|
||||||
var part = $scope.part;
|
var part = $scope.part;
|
||||||
@@ -40,7 +40,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
|||||||
var $input = $link.next();
|
var $input = $link.next();
|
||||||
|
|
||||||
$input.val(part.params[paramIndex]);
|
$input.val(part.params[paramIndex]);
|
||||||
$input.css('width', ($link.width() + 16) + 'px');
|
$input.css('width', $link.width() + 16 + 'px');
|
||||||
|
|
||||||
$link.hide();
|
$link.hide();
|
||||||
$input.show();
|
$input.show();
|
||||||
@@ -65,7 +65,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
|||||||
|
|
||||||
part.updateParam($input.val(), paramIndex);
|
part.updateParam($input.val(), paramIndex);
|
||||||
$scope.$apply(() => {
|
$scope.$apply(() => {
|
||||||
$scope.handleEvent({$event: {name: 'part-param-changed'}});
|
$scope.handleEvent({ $event: { name: 'part-param-changed' } });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,20 +90,26 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var typeaheadSource = function (query, callback) {
|
var typeaheadSource = function(query, callback) {
|
||||||
if (param.options) {
|
if (param.options) {
|
||||||
var options = param.options;
|
var options = param.options;
|
||||||
if (param.type === 'int') {
|
if (param.type === 'int') {
|
||||||
options = _.map(options, function(val) { return val.toString(); });
|
options = _.map(options, function(val) {
|
||||||
|
return val.toString();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.$apply(function() {
|
$scope.$apply(function() {
|
||||||
$scope.handleEvent({$event: {name: 'get-param-options'}}).then(function(result) {
|
$scope
|
||||||
var dynamicOptions = _.map(result, function(op) { return op.value; });
|
.handleEvent({ $event: { name: 'get-param-options' } })
|
||||||
callback(dynamicOptions);
|
.then(function(result) {
|
||||||
});
|
var dynamicOptions = _.map(result, function(op) {
|
||||||
|
return op.value;
|
||||||
|
});
|
||||||
|
callback(dynamicOptions);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -113,16 +119,16 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
|||||||
source: typeaheadSource,
|
source: typeaheadSource,
|
||||||
minLength: 0,
|
minLength: 0,
|
||||||
items: 1000,
|
items: 1000,
|
||||||
updater: function (value) {
|
updater: function(value) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
inputBlur.call($input[0], paramIndex);
|
inputBlur.call($input[0], paramIndex);
|
||||||
}, 0);
|
}, 0);
|
||||||
return value;
|
return value;
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var typeahead = $input.data('typeahead');
|
var typeahead = $input.data('typeahead');
|
||||||
typeahead.lookup = function () {
|
typeahead.lookup = function() {
|
||||||
this.query = this.$element.val() || '';
|
this.query = this.$element.val() || '';
|
||||||
var items = this.source(this.query, $.proxy(this.process, this));
|
var items = this.source(this.query, $.proxy(this.process, this));
|
||||||
return items ? this.process(items) : items;
|
return items ? this.process(items) : items;
|
||||||
@@ -130,13 +136,15 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$scope.showActionsMenu = function() {
|
$scope.showActionsMenu = function() {
|
||||||
$scope.handleEvent({$event: {name: 'get-part-actions'}}).then(res => {
|
$scope
|
||||||
$scope.partActions = res;
|
.handleEvent({ $event: { name: 'get-part-actions' } })
|
||||||
});
|
.then(res => {
|
||||||
|
$scope.partActions = res;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.triggerPartAction = function(action) {
|
$scope.triggerPartAction = function(action) {
|
||||||
$scope.handleEvent({$event: {name: 'action', action: action}});
|
$scope.handleEvent({ $event: { name: 'action', action: action } });
|
||||||
};
|
};
|
||||||
|
|
||||||
function addElementsAndCompile() {
|
function addElementsAndCompile() {
|
||||||
@@ -149,8 +157,12 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
|||||||
$('<span>, </span>').appendTo($paramsContainer);
|
$('<span>, </span>').appendTo($paramsContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
|
var paramValue = templateSrv.highlightVariablesAsHtml(
|
||||||
var $paramLink = $('<a class="graphite-func-param-link pointer">' + paramValue + '</a>');
|
part.params[index]
|
||||||
|
);
|
||||||
|
var $paramLink = $(
|
||||||
|
'<a class="graphite-func-param-link pointer">' + paramValue + '</a>'
|
||||||
|
);
|
||||||
var $input = $(paramTemplate);
|
var $input = $(paramTemplate);
|
||||||
|
|
||||||
$paramLink.appendTo($paramsContainer);
|
$paramLink.appendTo($paramsContainer);
|
||||||
@@ -171,7 +183,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
relink();
|
relink();
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export function geminiScrollbar() {
|
|||||||
return {
|
return {
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
link: function(scope, elem, attrs) {
|
link: function(scope, elem, attrs) {
|
||||||
|
|
||||||
let scrollbar = new PerfectScrollbar(elem[0]);
|
let scrollbar = new PerfectScrollbar(elem[0]);
|
||||||
|
|
||||||
scope.$on('$routeChangeSuccess', () => {
|
scope.$on('$routeChangeSuccess', () => {
|
||||||
@@ -19,8 +18,7 @@ export function geminiScrollbar() {
|
|||||||
scope.$on('$destroy', () => {
|
scope.$on('$destroy', () => {
|
||||||
scrollbar.destroy();
|
scrollbar.destroy();
|
||||||
});
|
});
|
||||||
|
},
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,11 +57,11 @@
|
|||||||
<div class="search-filter-box">
|
<div class="search-filter-box">
|
||||||
<a href="dashboard/new" class="search-filter-box-link">
|
<a href="dashboard/new" class="search-filter-box-link">
|
||||||
<i class="gicon gicon-dashboard-new"></i>
|
<i class="gicon gicon-dashboard-new"></i>
|
||||||
Dashboard
|
New dashboard
|
||||||
</a>
|
</a>
|
||||||
<a href="dashboards/folder/new" class="search-filter-box-link">
|
<a href="dashboards/folder/new" class="search-filter-box-link">
|
||||||
<i class="gicon gicon-folder-new"></i>
|
<i class="gicon gicon-folder-new"></i>
|
||||||
Folder
|
New folder
|
||||||
</a>
|
</a>
|
||||||
<a class="search-filter-box-link" target="_blank" href="https://grafana.com/dashboards?utm_source=grafana_search">
|
<a class="search-filter-box-link" target="_blank" href="https://grafana.com/dashboards?utm_source=grafana_search">
|
||||||
<img src="public/img/icn-dashboard-tiny.svg" width="20" /> Find dashboards on Grafana.com
|
<img src="public/img/icn-dashboard-tiny.svg" width="20" /> Find dashboards on Grafana.com
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import coreModule from '../../core_module';
|
import coreModule from '../../core_module';
|
||||||
import { SearchSrv } from 'app/core/services/search_srv';
|
import { SearchSrv } from 'app/core/services/search_srv';
|
||||||
|
import appEvents from 'app/core/app_events';
|
||||||
|
|
||||||
export class SearchCtrl {
|
export class SearchCtrl {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -16,11 +17,16 @@ export class SearchCtrl {
|
|||||||
initialFolderFilterTitle: string;
|
initialFolderFilterTitle: string;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, private $location, private $timeout, private searchSrv: SearchSrv, $rootScope) {
|
constructor(
|
||||||
$rootScope.onAppEvent('show-dash-search', this.openSearch.bind(this), $scope);
|
$scope,
|
||||||
$rootScope.onAppEvent('hide-dash-search', this.closeSearch.bind(this), $scope);
|
private $location,
|
||||||
|
private $timeout,
|
||||||
|
private searchSrv: SearchSrv
|
||||||
|
) {
|
||||||
|
appEvents.on('show-dash-search', this.openSearch.bind(this), $scope);
|
||||||
|
appEvents.on('hide-dash-search', this.closeSearch.bind(this), $scope);
|
||||||
|
|
||||||
this.initialFolderFilterTitle = "All";
|
this.initialFolderFilterTitle = 'All';
|
||||||
}
|
}
|
||||||
|
|
||||||
closeSearch() {
|
closeSearch() {
|
||||||
@@ -69,11 +75,14 @@ export class SearchCtrl {
|
|||||||
|
|
||||||
if (currentItem) {
|
if (currentItem) {
|
||||||
if (currentItem.dashboardIndex !== undefined) {
|
if (currentItem.dashboardIndex !== undefined) {
|
||||||
const selectedDash = this.results[currentItem.folderIndex].items[currentItem.dashboardIndex];
|
const selectedDash = this.results[currentItem.folderIndex].items[
|
||||||
|
currentItem.dashboardIndex
|
||||||
|
];
|
||||||
|
|
||||||
if (selectedDash) {
|
if (selectedDash) {
|
||||||
this.$location.search({});
|
this.$location.search({});
|
||||||
this.$location.path(selectedDash.url);
|
this.$location.path(selectedDash.url);
|
||||||
|
this.closeSearch();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const selectedFolder = this.results[currentItem.folderIndex];
|
const selectedFolder = this.results[currentItem.folderIndex];
|
||||||
@@ -96,7 +105,9 @@ export class SearchCtrl {
|
|||||||
|
|
||||||
if (currentItem) {
|
if (currentItem) {
|
||||||
if (currentItem.dashboardIndex !== undefined) {
|
if (currentItem.dashboardIndex !== undefined) {
|
||||||
this.results[currentItem.folderIndex].items[currentItem.dashboardIndex].selected = false;
|
this.results[currentItem.folderIndex].items[
|
||||||
|
currentItem.dashboardIndex
|
||||||
|
].selected = false;
|
||||||
} else {
|
} else {
|
||||||
this.results[currentItem.folderIndex].selected = false;
|
this.results[currentItem.folderIndex].selected = false;
|
||||||
}
|
}
|
||||||
@@ -109,10 +120,13 @@ export class SearchCtrl {
|
|||||||
|
|
||||||
const max = flattenedResult.length;
|
const max = flattenedResult.length;
|
||||||
let newIndex = this.selectedIndex + direction;
|
let newIndex = this.selectedIndex + direction;
|
||||||
this.selectedIndex = ((newIndex %= max) < 0) ? newIndex + max : newIndex;
|
this.selectedIndex = (newIndex %= max) < 0 ? newIndex + max : newIndex;
|
||||||
const selectedItem = flattenedResult[this.selectedIndex];
|
const selectedItem = flattenedResult[this.selectedIndex];
|
||||||
|
|
||||||
if (selectedItem.dashboardIndex === undefined && this.results[selectedItem.folderIndex].id === 0) {
|
if (
|
||||||
|
selectedItem.dashboardIndex === undefined &&
|
||||||
|
this.results[selectedItem.folderIndex].id === 0
|
||||||
|
) {
|
||||||
this.moveSelection(direction);
|
this.moveSelection(direction);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -123,7 +137,9 @@ export class SearchCtrl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.results[selectedItem.folderIndex].items[selectedItem.dashboardIndex].selected = true;
|
this.results[selectedItem.folderIndex].items[
|
||||||
|
selectedItem.dashboardIndex
|
||||||
|
].selected = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +156,9 @@ export class SearchCtrl {
|
|||||||
var localSearchId = this.currentSearchId;
|
var localSearchId = this.currentSearchId;
|
||||||
|
|
||||||
return this.searchSrv.search(this.query).then(results => {
|
return this.searchSrv.search(this.query).then(results => {
|
||||||
if (localSearchId < this.currentSearchId) { return; }
|
if (localSearchId < this.currentSearchId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.results = results || [];
|
this.results = results || [];
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
this.moveSelection(1);
|
this.moveSelection(1);
|
||||||
@@ -149,7 +167,9 @@ export class SearchCtrl {
|
|||||||
|
|
||||||
queryHasNoFilters() {
|
queryHasNoFilters() {
|
||||||
var query = this.query;
|
var query = this.query;
|
||||||
return query.query === '' && query.starred === false && query.tag.length === 0;
|
return (
|
||||||
|
query.query === '' && query.starred === false && query.tag.length === 0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
filterByTag(tag) {
|
filterByTag(tag) {
|
||||||
@@ -169,7 +189,7 @@ export class SearchCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTags() {
|
getTags() {
|
||||||
return this.searchSrv.getDashboardTags().then((results) => {
|
return this.searchSrv.getDashboardTags().then(results => {
|
||||||
this.results = results;
|
this.results = results;
|
||||||
this.giveSearchFocus = this.giveSearchFocus + 1;
|
this.giveSearchFocus = this.giveSearchFocus + 1;
|
||||||
});
|
});
|
||||||
@@ -194,21 +214,23 @@ export class SearchCtrl {
|
|||||||
private getFlattenedResultForNavigation() {
|
private getFlattenedResultForNavigation() {
|
||||||
let folderIndex = 0;
|
let folderIndex = 0;
|
||||||
|
|
||||||
return _.flatMap(this.results, (s) => {
|
return _.flatMap(this.results, s => {
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
folderIndex: folderIndex
|
folderIndex: folderIndex,
|
||||||
});
|
});
|
||||||
|
|
||||||
let dashboardIndex = 0;
|
let dashboardIndex = 0;
|
||||||
|
|
||||||
result = result.concat(_.map(s.items || [], (i) => {
|
result = result.concat(
|
||||||
return {
|
_.map(s.items || [], i => {
|
||||||
folderIndex: folderIndex,
|
return {
|
||||||
dashboardIndex: dashboardIndex++
|
folderIndex: folderIndex,
|
||||||
};
|
dashboardIndex: dashboardIndex++,
|
||||||
}));
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
folderIndex++;
|
folderIndex++;
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
<span class="search-item__icon">
|
<span class="search-item__icon">
|
||||||
<i class="gicon mini gicon-dashboard-list"></i>
|
<i class="gicon mini gicon-dashboard-list"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="search-item__body">
|
<span class="search-item__body" ng-click="ctrl.onItemClick(item)">
|
||||||
<div class="search-item__body-title">{{::item.title}}</div>
|
<div class="search-item__body-title">{{::item.title}}</div>
|
||||||
</span>
|
</span>
|
||||||
<span class="search-item__tags">
|
<span class="search-item__tags">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import coreModule from '../../core_module';
|
import coreModule from '../../core_module';
|
||||||
|
import appEvents from 'app/core/app_events';
|
||||||
|
|
||||||
export class SearchResultsCtrl {
|
export class SearchResultsCtrl {
|
||||||
results: any;
|
results: any;
|
||||||
@@ -61,9 +62,15 @@ export class SearchResultsCtrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onItemClick(item) {
|
||||||
|
if (this.$location.path().indexOf(item.url) > -1) {
|
||||||
|
appEvents.emit('hide-dash-search');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
selectTag(tag, evt) {
|
selectTag(tag, evt) {
|
||||||
if (this.onTagSelected) {
|
if (this.onTagSelected) {
|
||||||
this.onTagSelected({$tag: tag});
|
this.onTagSelected({ $tag: tag });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evt) {
|
if (evt) {
|
||||||
@@ -85,7 +92,7 @@ export function searchResultsDirective() {
|
|||||||
results: '=',
|
results: '=',
|
||||||
onSelectionChanged: '&',
|
onSelectionChanged: '&',
|
||||||
onTagSelected: '&',
|
onTagSelected: '&',
|
||||||
onFolderExpanding: '&'
|
onFolderExpanding: '&',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,22 +13,36 @@ export class SideMenuCtrl {
|
|||||||
isOpenMobile: boolean;
|
isOpenMobile: boolean;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $scope, private $rootScope, private $location, private contextSrv, private $timeout) {
|
constructor(
|
||||||
|
private $scope,
|
||||||
|
private $rootScope,
|
||||||
|
private $location,
|
||||||
|
private contextSrv,
|
||||||
|
private $timeout
|
||||||
|
) {
|
||||||
this.isSignedIn = contextSrv.isSignedIn;
|
this.isSignedIn = contextSrv.isSignedIn;
|
||||||
this.user = contextSrv.user;
|
this.user = contextSrv.user;
|
||||||
this.mainLinks = _.filter(config.bootData.navTree, item => !item.hideFromMenu);
|
this.mainLinks = _.filter(
|
||||||
this.bottomNav = _.filter(config.bootData.navTree, item => item.hideFromMenu);
|
config.bootData.navTree,
|
||||||
this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path());
|
item => !item.hideFromMenu
|
||||||
|
);
|
||||||
|
this.bottomNav = _.filter(
|
||||||
|
config.bootData.navTree,
|
||||||
|
item => item.hideFromMenu
|
||||||
|
);
|
||||||
|
this.loginUrl =
|
||||||
|
'login?redirect=' + encodeURIComponent(this.$location.path());
|
||||||
|
|
||||||
if (contextSrv.user.orgCount > 1) {
|
if (contextSrv.user.orgCount > 1) {
|
||||||
let profileNode = _.find(this.bottomNav, {id: 'profile'});
|
let profileNode = _.find(this.bottomNav, { id: 'profile' });
|
||||||
if (profileNode) {
|
if (profileNode) {
|
||||||
profileNode.showOrgSwitcher = true;
|
profileNode.showOrgSwitcher = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$scope.$on('$routeChangeSuccess', () => {
|
this.$scope.$on('$routeChangeSuccess', () => {
|
||||||
this.loginUrl = 'login?redirect=' + encodeURIComponent(this.$location.path());
|
this.loginUrl =
|
||||||
|
'login?redirect=' + encodeURIComponent(this.$location.path());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +67,9 @@ export class SideMenuCtrl {
|
|||||||
|
|
||||||
itemClicked(item, evt) {
|
itemClicked(item, evt) {
|
||||||
if (item.url === '/shortcuts') {
|
if (item.url === '/shortcuts') {
|
||||||
appEvents.emit('show-modal', {templateHtml: '<help-modal></help-modal>'});
|
appEvents.emit('show-modal', {
|
||||||
|
templateHtml: '<help-modal></help-modal>',
|
||||||
|
});
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,7 +94,7 @@ export function sideMenuDirective() {
|
|||||||
parent.append(menu);
|
parent.append(menu);
|
||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ export class SwitchCtrl {
|
|||||||
return this.onChange();
|
return this.onChange();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function switchDirective() {
|
export function switchDirective() {
|
||||||
@@ -43,12 +42,12 @@ export function switchDirective() {
|
|||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
bindToController: true,
|
bindToController: true,
|
||||||
scope: {
|
scope: {
|
||||||
checked: "=",
|
checked: '=',
|
||||||
label: "@",
|
label: '@',
|
||||||
labelClass: "@",
|
labelClass: '@',
|
||||||
tooltip: "@",
|
tooltip: '@',
|
||||||
switchClass: "@",
|
switchClass: '@',
|
||||||
onChange: "&",
|
onChange: '&',
|
||||||
},
|
},
|
||||||
template: template,
|
template: template,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,24 +17,31 @@ export class TeamPickerCtrl {
|
|||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private backendSrv) {
|
constructor(private backendSrv) {
|
||||||
this.debouncedSearchGroups = _.debounce(this.searchGroups, 500, {'leading': true, 'trailing': false});
|
this.debouncedSearchGroups = _.debounce(this.searchGroups, 500, {
|
||||||
|
leading: true,
|
||||||
|
trailing: false,
|
||||||
|
});
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this.group = {text: 'Choose', value: null};
|
this.group = { text: 'Choose', value: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
searchGroups(query: string) {
|
searchGroups(query: string) {
|
||||||
return Promise.resolve(this.backendSrv.get('/api/teams/search?perpage=10&page=1&query=' + query).then(result => {
|
return Promise.resolve(
|
||||||
return _.map(result.teams, ug => {
|
this.backendSrv
|
||||||
return {text: ug.name, value: ug};
|
.get('/api/teams/search?perpage=10&page=1&query=' + query)
|
||||||
});
|
.then(result => {
|
||||||
}));
|
return _.map(result.teams, ug => {
|
||||||
|
return { text: ug.name, value: ug };
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(option) {
|
onChange(option) {
|
||||||
this.teamPicked({$group: option.value});
|
this.teamPicked({ $group: option.value });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,10 +56,10 @@ export function teamPicker() {
|
|||||||
teamPicked: '&',
|
teamPicked: '&',
|
||||||
},
|
},
|
||||||
link: function(scope, elem, attrs, ctrl) {
|
link: function(scope, elem, attrs, ctrl) {
|
||||||
scope.$on("team-picker-reset", () => {
|
scope.$on('team-picker-reset', () => {
|
||||||
ctrl.reset();
|
ctrl.reset();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,23 +18,30 @@ export class UserPickerCtrl {
|
|||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private backendSrv) {
|
constructor(private backendSrv) {
|
||||||
this.reset();
|
this.reset();
|
||||||
this.debouncedSearchUsers = _.debounce(this.searchUsers, 500, {'leading': true, 'trailing': false});
|
this.debouncedSearchUsers = _.debounce(this.searchUsers, 500, {
|
||||||
|
leading: true,
|
||||||
|
trailing: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
searchUsers(query: string) {
|
searchUsers(query: string) {
|
||||||
return Promise.resolve(this.backendSrv.get('/api/users/search?perpage=10&page=1&query=' + query).then(result => {
|
return Promise.resolve(
|
||||||
return _.map(result.users, user => {
|
this.backendSrv
|
||||||
return {text: user.login + ' - ' + user.email, value: user};
|
.get('/api/users/search?perpage=10&page=1&query=' + query)
|
||||||
});
|
.then(result => {
|
||||||
}));
|
return _.map(result.users, user => {
|
||||||
|
return { text: user.login + ' - ' + user.email, value: user };
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(option) {
|
onChange(option) {
|
||||||
this.userPicked({$user: option.value});
|
this.userPicked({ $user: option.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this.user = {text: 'Choose', value: null};
|
this.user = { text: 'Choose', value: null };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,10 +63,10 @@ export function userPicker() {
|
|||||||
userPicked: '&',
|
userPicked: '&',
|
||||||
},
|
},
|
||||||
link: function(scope, elem, attrs, ctrl) {
|
link: function(scope, elem, attrs, ctrl) {
|
||||||
scope.$on("user-picker-reset", () => {
|
scope.$on('user-picker-reset', () => {
|
||||||
ctrl.reset();
|
ctrl.reset();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+32
-32
@@ -1,39 +1,39 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
class Settings {
|
class Settings {
|
||||||
datasources: any;
|
datasources: any;
|
||||||
panels: any;
|
panels: any;
|
||||||
appSubUrl: string;
|
appSubUrl: string;
|
||||||
window_title_prefix: string;
|
window_title_prefix: string;
|
||||||
buildInfo: any;
|
buildInfo: any;
|
||||||
new_panel_title: string;
|
new_panel_title: string;
|
||||||
bootData: any;
|
bootData: any;
|
||||||
externalUserMngLinkUrl: string;
|
externalUserMngLinkUrl: string;
|
||||||
externalUserMngLinkName: string;
|
externalUserMngLinkName: string;
|
||||||
externalUserMngInfo: string;
|
externalUserMngInfo: string;
|
||||||
allowOrgCreate: boolean;
|
allowOrgCreate: boolean;
|
||||||
disableLoginForm: boolean;
|
disableLoginForm: boolean;
|
||||||
defaultDatasource: string;
|
defaultDatasource: string;
|
||||||
alertingEnabled: boolean;
|
alertingEnabled: boolean;
|
||||||
authProxyEnabled: boolean;
|
authProxyEnabled: boolean;
|
||||||
ldapEnabled: boolean;
|
ldapEnabled: boolean;
|
||||||
oauth: any;
|
oauth: any;
|
||||||
disableUserSignUp: boolean;
|
disableUserSignUp: boolean;
|
||||||
loginHint: any;
|
loginHint: any;
|
||||||
loginError: any;
|
loginError: any;
|
||||||
|
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
var defaults = {
|
var defaults = {
|
||||||
datasources: {},
|
datasources: {},
|
||||||
window_title_prefix: 'Grafana - ',
|
window_title_prefix: 'Grafana - ',
|
||||||
panels: {},
|
panels: {},
|
||||||
new_panel_title: 'Panel Title',
|
new_panel_title: 'Panel Title',
|
||||||
playlist_timespan: "1m",
|
playlist_timespan: '1m',
|
||||||
unsaved_changes_warning: true,
|
unsaved_changes_warning: true,
|
||||||
appSubUrl: ""
|
appSubUrl: '',
|
||||||
};
|
};
|
||||||
_.extend(this, defaults, options);
|
_.extend(this, defaults, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var bootData = (<any>window).grafanaBootData || { settings: {} };
|
var bootData = (<any>window).grafanaBootData || { settings: {} };
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export const GRID_CELL_HEIGHT = 30;
|
export const GRID_CELL_HEIGHT = 30;
|
||||||
export const GRID_CELL_VMARGIN = 10;
|
export const GRID_CELL_VMARGIN = 10;
|
||||||
export const GRID_COLUMN_COUNT = 24;
|
export const GRID_COLUMN_COUNT = 24;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import coreModule from '../core_module';
|
|||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
|
|
||||||
export class ErrorCtrl {
|
export class ErrorCtrl {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, contextSrv, navModelSrv) {
|
constructor($scope, contextSrv, navModelSrv) {
|
||||||
$scope.navModel = navModelSrv.getNotFoundNav();
|
$scope.navModel = navModelSrv.getNotFoundNav();
|
||||||
@@ -13,7 +12,7 @@ export class ErrorCtrl {
|
|||||||
appEvents.emit('toggle-sidemenu-hidden');
|
appEvents.emit('toggle-sidemenu-hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.$on("destroy", () => {
|
$scope.$on('destroy', () => {
|
||||||
if (!contextSrv.isSignedIn) {
|
if (!contextSrv.isSignedIn) {
|
||||||
appEvents.emit('toggle-sidemenu-hidden');
|
appEvents.emit('toggle-sidemenu-hidden');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,20 +4,19 @@ import $ from 'jquery';
|
|||||||
import coreModule from '../core_module';
|
import coreModule from '../core_module';
|
||||||
|
|
||||||
export class InspectCtrl {
|
export class InspectCtrl {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, $sanitize) {
|
constructor($scope, $sanitize) {
|
||||||
var model = $scope.inspector;
|
var model = $scope.inspector;
|
||||||
|
|
||||||
$scope.init = function () {
|
$scope.init = function() {
|
||||||
$scope.editor = { index: 0 };
|
$scope.editor = { index: 0 };
|
||||||
|
|
||||||
if (!model.error) {
|
if (!model.error) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_.isString(model.error.data)) {
|
if (_.isString(model.error.data)) {
|
||||||
$scope.response = $("<div>" + model.error.data + "</div>").text();
|
$scope.response = $('<div>' + model.error.data + '</div>').text();
|
||||||
} else if (model.error.data) {
|
} else if (model.error.data) {
|
||||||
if (model.error.data.response) {
|
if (model.error.data.response) {
|
||||||
$scope.response = $sanitize(model.error.data.response);
|
$scope.response = $sanitize(model.error.data.response);
|
||||||
@@ -29,8 +28,11 @@ export class InspectCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (model.error.config && model.error.config.params) {
|
if (model.error.config && model.error.config.params) {
|
||||||
$scope.request_parameters = _.map(model.error.config.params, function(value, key) {
|
$scope.request_parameters = _.map(model.error.config.params, function(
|
||||||
return { key: key, value: value};
|
value,
|
||||||
|
key
|
||||||
|
) {
|
||||||
|
return { key: key, value: value };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,10 +46,15 @@ export class InspectCtrl {
|
|||||||
$scope.editor.index = 2;
|
$scope.editor.index = 2;
|
||||||
|
|
||||||
if (_.isString(model.error.config.data)) {
|
if (_.isString(model.error.config.data)) {
|
||||||
$scope.request_parameters = this.getParametersFromQueryString(model.error.config.data);
|
$scope.request_parameters = this.getParametersFromQueryString(
|
||||||
} else {
|
model.error.config.data
|
||||||
$scope.request_parameters = _.map(model.error.config.data, function(value, key) {
|
);
|
||||||
return {key: key, value: angular.toJson(value, true)};
|
} else {
|
||||||
|
$scope.request_parameters = _.map(model.error.config.data, function(
|
||||||
|
value,
|
||||||
|
key
|
||||||
|
) {
|
||||||
|
return { key: key, value: angular.toJson(value, true) };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,11 +62,14 @@ export class InspectCtrl {
|
|||||||
}
|
}
|
||||||
getParametersFromQueryString(queryString) {
|
getParametersFromQueryString(queryString) {
|
||||||
var result = [];
|
var result = [];
|
||||||
var parameters = queryString.split("&");
|
var parameters = queryString.split('&');
|
||||||
for (var i = 0; i < parameters.length; i++) {
|
for (var i = 0; i < parameters.length; i++) {
|
||||||
var keyValue = parameters[i].split("=");
|
var keyValue = parameters[i].split('=');
|
||||||
if (keyValue[1].length > 0) {
|
if (keyValue[1].length > 0) {
|
||||||
result.push({ key: keyValue[0], value: (<any>window).unescape(keyValue[1]) });
|
result.push({
|
||||||
|
key: keyValue[0],
|
||||||
|
value: (<any>window).unescape(keyValue[1]),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import coreModule from '../core_module';
|
|||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
|
|
||||||
export class InvitedCtrl {
|
export class InvitedCtrl {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, $routeParams, contextSrv, backendSrv) {
|
constructor($scope, $routeParams, contextSrv, backendSrv) {
|
||||||
contextSrv.sidemenu = false;
|
contextSrv.sidemenu = false;
|
||||||
@@ -12,23 +11,22 @@ export class InvitedCtrl {
|
|||||||
main: {
|
main: {
|
||||||
icon: 'gicon gicon-branding',
|
icon: 'gicon gicon-branding',
|
||||||
subTitle: 'Register your Grafana account',
|
subTitle: 'Register your Grafana account',
|
||||||
breadcrumbs: [
|
breadcrumbs: [{ title: 'Login', url: '/login' }, { title: 'Invite' }],
|
||||||
{ title: 'Login', url: '/login' },
|
},
|
||||||
{ title: 'Invite' },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.init = function() {
|
$scope.init = function() {
|
||||||
backendSrv.get('/api/user/invite/' + $routeParams.code).then(function(invite) {
|
backendSrv
|
||||||
$scope.formModel.name = invite.name;
|
.get('/api/user/invite/' + $routeParams.code)
|
||||||
$scope.formModel.email = invite.email;
|
.then(function(invite) {
|
||||||
$scope.formModel.username = invite.email;
|
$scope.formModel.name = invite.name;
|
||||||
$scope.formModel.inviteCode = $routeParams.code;
|
$scope.formModel.email = invite.email;
|
||||||
|
$scope.formModel.username = invite.email;
|
||||||
|
$scope.formModel.inviteCode = $routeParams.code;
|
||||||
|
|
||||||
$scope.greeting = invite.name || invite.email || invite.username;
|
$scope.greeting = invite.name || invite.email || invite.username;
|
||||||
$scope.invitedBy = invite.invitedBy;
|
$scope.invitedBy = invite.invitedBy;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.submit = function() {
|
$scope.submit = function() {
|
||||||
@@ -36,9 +34,11 @@ export class InvitedCtrl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
backendSrv.post('/api/user/invite/complete', $scope.formModel).then(function() {
|
backendSrv
|
||||||
window.location.href = config.appSubUrl + '/';
|
.post('/api/user/invite/complete', $scope.formModel)
|
||||||
});
|
.then(function() {
|
||||||
|
window.location.href = config.appSubUrl + '/';
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.init();
|
$scope.init();
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import angular from 'angular';
|
|||||||
import coreModule from '../core_module';
|
import coreModule from '../core_module';
|
||||||
|
|
||||||
export class JsonEditorCtrl {
|
export class JsonEditorCtrl {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope) {
|
constructor($scope) {
|
||||||
$scope.json = angular.toJson($scope.object, true);
|
$scope.json = angular.toJson($scope.object, true);
|
||||||
$scope.canUpdate = $scope.updateHandler !== void 0 && $scope.contextSrv.isEditor;
|
$scope.canUpdate =
|
||||||
|
$scope.updateHandler !== void 0 && $scope.contextSrv.isEditor;
|
||||||
|
|
||||||
$scope.update = function () {
|
$scope.update = function() {
|
||||||
var newObject = angular.fromJson($scope.json);
|
var newObject = angular.fromJson($scope.json);
|
||||||
$scope.updateHandler(newObject, $scope.object);
|
$scope.updateHandler(newObject, $scope.object);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import coreModule from '../core_module';
|
|||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
|
|
||||||
export class LoginCtrl {
|
export class LoginCtrl {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, backendSrv, contextSrv, $location) {
|
constructor($scope, backendSrv, contextSrv, $location) {
|
||||||
$scope.formModel = {
|
$scope.formModel = {
|
||||||
@@ -19,13 +18,13 @@ export class LoginCtrl {
|
|||||||
|
|
||||||
$scope.disableLoginForm = config.disableLoginForm;
|
$scope.disableLoginForm = config.disableLoginForm;
|
||||||
$scope.disableUserSignUp = config.disableUserSignUp;
|
$scope.disableUserSignUp = config.disableUserSignUp;
|
||||||
$scope.loginHint = config.loginHint;
|
$scope.loginHint = config.loginHint;
|
||||||
|
|
||||||
$scope.loginMode = true;
|
$scope.loginMode = true;
|
||||||
$scope.submitBtnText = 'Log in';
|
$scope.submitBtnText = 'Log in';
|
||||||
|
|
||||||
$scope.init = function() {
|
$scope.init = function() {
|
||||||
$scope.$watch("loginMode", $scope.loginModeChanged);
|
$scope.$watch('loginMode', $scope.loginModeChanged);
|
||||||
|
|
||||||
if (config.loginError) {
|
if (config.loginError) {
|
||||||
$scope.appEvent('alert-warning', ['Login Failed', config.loginError]);
|
$scope.appEvent('alert-warning', ['Login Failed', config.loginError]);
|
||||||
@@ -49,13 +48,15 @@ export class LoginCtrl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
backendSrv.post('/api/user/signup', $scope.formModel).then(function(result) {
|
backendSrv
|
||||||
if (result.status === 'SignUpCreated') {
|
.post('/api/user/signup', $scope.formModel)
|
||||||
$location.path('/signup').search({email: $scope.formModel.email});
|
.then(function(result) {
|
||||||
} else {
|
if (result.status === 'SignUpCreated') {
|
||||||
window.location.href = config.appSubUrl + '/';
|
$location.path('/signup').search({ email: $scope.formModel.email });
|
||||||
}
|
} else {
|
||||||
});
|
window.location.href = config.appSubUrl + '/';
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.login = function() {
|
$scope.login = function() {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import coreModule from '../core_module';
|
import coreModule from '../core_module';
|
||||||
|
|
||||||
export class ResetPasswordCtrl {
|
export class ResetPasswordCtrl {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, contextSrv, backendSrv, $location) {
|
constructor($scope, contextSrv, backendSrv, $location) {
|
||||||
contextSrv.sidemenu = false;
|
contextSrv.sidemenu = false;
|
||||||
@@ -21,30 +20,36 @@ export class ResetPasswordCtrl {
|
|||||||
breadcrumbs: [
|
breadcrumbs: [
|
||||||
{ title: 'Login', url: '/login' },
|
{ title: 'Login', url: '/login' },
|
||||||
{ title: 'Reset Password' },
|
{ title: 'Reset Password' },
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.sendResetEmail = function() {
|
$scope.sendResetEmail = function() {
|
||||||
if (!$scope.sendResetForm.$valid) {
|
if (!$scope.sendResetForm.$valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
backendSrv.post('/api/user/password/send-reset-email', $scope.formModel).then(function() {
|
backendSrv
|
||||||
$scope.mode = 'email-sent';
|
.post('/api/user/password/send-reset-email', $scope.formModel)
|
||||||
});
|
.then(function() {
|
||||||
|
$scope.mode = 'email-sent';
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.submitReset = function() {
|
$scope.submitReset = function() {
|
||||||
if (!$scope.resetForm.$valid) { return; }
|
if (!$scope.resetForm.$valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ($scope.formModel.newPassword !== $scope.formModel.confirmPassword) {
|
if ($scope.formModel.newPassword !== $scope.formModel.confirmPassword) {
|
||||||
$scope.appEvent('alert-warning', ['New passwords do not match', '']);
|
$scope.appEvent('alert-warning', ['New passwords do not match', '']);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
backendSrv.post('/api/user/password/reset', $scope.formModel).then(function() {
|
backendSrv
|
||||||
$location.path('login');
|
.post('/api/user/password/reset', $scope.formModel)
|
||||||
});
|
.then(function() {
|
||||||
|
$location.path('login');
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,13 @@ import config from 'app/core/config';
|
|||||||
import coreModule from '../core_module';
|
import coreModule from '../core_module';
|
||||||
|
|
||||||
export class SignUpCtrl {
|
export class SignUpCtrl {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(
|
constructor(
|
||||||
private $scope: any,
|
private $scope: any,
|
||||||
private backendSrv: any,
|
private backendSrv: any,
|
||||||
$location: any,
|
$location: any,
|
||||||
contextSrv: any) {
|
contextSrv: any
|
||||||
|
) {
|
||||||
contextSrv.sidemenu = false;
|
contextSrv.sidemenu = false;
|
||||||
$scope.ctrl = this;
|
$scope.ctrl = this;
|
||||||
|
|
||||||
@@ -30,11 +29,8 @@ export class SignUpCtrl {
|
|||||||
main: {
|
main: {
|
||||||
icon: 'gicon gicon-branding',
|
icon: 'gicon gicon-branding',
|
||||||
subTitle: 'Register your Grafana account',
|
subTitle: 'Register your Grafana account',
|
||||||
breadcrumbs: [
|
breadcrumbs: [{ title: 'Login', url: '/login' }, { title: 'Sign Up' }],
|
||||||
{ title: 'Login', url: '/login' },
|
},
|
||||||
{ title: 'Sign Up' },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
backendSrv.get('/api/user/signup/options').then(options => {
|
backendSrv.get('/api/user/signup/options').then(options => {
|
||||||
@@ -43,20 +39,22 @@ export class SignUpCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
submit () {
|
submit() {
|
||||||
if (!this.$scope.signUpForm.$valid) {
|
if (!this.$scope.signUpForm.$valid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.backendSrv.post('/api/user/signup/step2', this.$scope.formModel).then(rsp => {
|
this.backendSrv
|
||||||
if (rsp.code === 'redirect-to-select-org') {
|
.post('/api/user/signup/step2', this.$scope.formModel)
|
||||||
window.location.href = config.appSubUrl + '/profile/select-org?signup=1';
|
.then(rsp => {
|
||||||
} else {
|
if (rsp.code === 'redirect-to-select-org') {
|
||||||
window.location.href = config.appSubUrl + '/';
|
window.location.href =
|
||||||
}
|
config.appSubUrl + '/profile/select-org?signup=1';
|
||||||
});
|
} else {
|
||||||
|
window.location.href = config.appSubUrl + '/';
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coreModule.controller('SignUpCtrl', SignUpCtrl);
|
coreModule.controller('SignUpCtrl', SignUpCtrl);
|
||||||
|
|
||||||
|
|||||||
+41
-41
@@ -1,14 +1,14 @@
|
|||||||
import "./directives/dash_class";
|
import './directives/dash_class';
|
||||||
import "./directives/dash_edit_link";
|
import './directives/dash_edit_link';
|
||||||
import "./directives/dropdown_typeahead";
|
import './directives/dropdown_typeahead';
|
||||||
import "./directives/metric_segment";
|
import './directives/metric_segment';
|
||||||
import "./directives/misc";
|
import './directives/misc';
|
||||||
import "./directives/ng_model_on_blur";
|
import './directives/ng_model_on_blur';
|
||||||
import "./directives/tags";
|
import './directives/tags';
|
||||||
import "./directives/value_select_dropdown";
|
import './directives/value_select_dropdown';
|
||||||
import "./directives/rebuild_on_change";
|
import './directives/rebuild_on_change';
|
||||||
import "./directives/give_focus";
|
import './directives/give_focus';
|
||||||
import "./directives/diff-view";
|
import './directives/diff-view';
|
||||||
import './jquery_extended';
|
import './jquery_extended';
|
||||||
import './partials';
|
import './partials';
|
||||||
import './components/jsontree/jsontree';
|
import './components/jsontree/jsontree';
|
||||||
@@ -20,19 +20,19 @@ import './components/colorpicker/spectrum_picker';
|
|||||||
import './services/search_srv';
|
import './services/search_srv';
|
||||||
import './services/ng_react';
|
import './services/ng_react';
|
||||||
|
|
||||||
import {grafanaAppDirective} from './components/grafana_app';
|
import { grafanaAppDirective } from './components/grafana_app';
|
||||||
import {sideMenuDirective} from './components/sidemenu/sidemenu';
|
import { sideMenuDirective } from './components/sidemenu/sidemenu';
|
||||||
import {searchDirective} from './components/search/search';
|
import { searchDirective } from './components/search/search';
|
||||||
import {infoPopover} from './components/info_popover';
|
import { infoPopover } from './components/info_popover';
|
||||||
import {navbarDirective} from './components/navbar/navbar';
|
import { navbarDirective } from './components/navbar/navbar';
|
||||||
import {arrayJoin} from './directives/array_join';
|
import { arrayJoin } from './directives/array_join';
|
||||||
import {liveSrv} from './live/live_srv';
|
import { liveSrv } from './live/live_srv';
|
||||||
import {Emitter} from './utils/emitter';
|
import { Emitter } from './utils/emitter';
|
||||||
import {layoutSelector} from './components/layout_selector/layout_selector';
|
import { layoutSelector } from './components/layout_selector/layout_selector';
|
||||||
import {switchDirective} from './components/switch';
|
import { switchDirective } from './components/switch';
|
||||||
import {dashboardSelector} from './components/dashboard_selector';
|
import { dashboardSelector } from './components/dashboard_selector';
|
||||||
import {queryPartEditorDirective} from './components/query_part/query_part_editor';
|
import { queryPartEditorDirective } from './components/query_part/query_part_editor';
|
||||||
import {formDropdownDirective} from './components/form_dropdown/form_dropdown';
|
import { formDropdownDirective } from './components/form_dropdown/form_dropdown';
|
||||||
import 'app/core/controllers/all';
|
import 'app/core/controllers/all';
|
||||||
import 'app/core/services/all';
|
import 'app/core/services/all';
|
||||||
import 'app/core/routes/routes';
|
import 'app/core/routes/routes';
|
||||||
@@ -40,23 +40,23 @@ import './filters/filters';
|
|||||||
import coreModule from './core_module';
|
import coreModule from './core_module';
|
||||||
import appEvents from './app_events';
|
import appEvents from './app_events';
|
||||||
import colors from './utils/colors';
|
import colors from './utils/colors';
|
||||||
import {assignModelProperties} from './utils/model_utils';
|
import { assignModelProperties } from './utils/model_utils';
|
||||||
import {contextSrv} from './services/context_srv';
|
import { contextSrv } from './services/context_srv';
|
||||||
import {KeybindingSrv} from './services/keybindingSrv';
|
import { KeybindingSrv } from './services/keybindingSrv';
|
||||||
import {helpModal} from './components/help/help';
|
import { helpModal } from './components/help/help';
|
||||||
import {JsonExplorer} from './components/json_explorer/json_explorer';
|
import { JsonExplorer } from './components/json_explorer/json_explorer';
|
||||||
import {NavModelSrv, NavModel} from './nav_model_srv';
|
import { NavModelSrv, NavModel } from './nav_model_srv';
|
||||||
import {userPicker} from './components/user_picker';
|
import { userPicker } from './components/user_picker';
|
||||||
import {teamPicker} from './components/team_picker';
|
import { teamPicker } from './components/team_picker';
|
||||||
import {geminiScrollbar} from './components/scroll/scroll';
|
import { geminiScrollbar } from './components/scroll/scroll';
|
||||||
import {gfPageDirective} from './components/gf_page';
|
import { gfPageDirective } from './components/gf_page';
|
||||||
import {orgSwitcher} from './components/org_switcher';
|
import { orgSwitcher } from './components/org_switcher';
|
||||||
import {profiler} from './profiler';
|
import { profiler } from './profiler';
|
||||||
import {registerAngularDirectives} from './angular_wrappers';
|
import { registerAngularDirectives } from './angular_wrappers';
|
||||||
import {updateLegendValues} from './time_series2';
|
import { updateLegendValues } from './time_series2';
|
||||||
import TimeSeries from './time_series2';
|
import TimeSeries from './time_series2';
|
||||||
import {searchResultsDirective} from './components/search/search_results';
|
import { searchResultsDirective } from './components/search/search_results';
|
||||||
import {manageDashboardsDirective} from './components/manage_dashboards/manage_dashboards';
|
import { manageDashboardsDirective } from './components/manage_dashboards/manage_dashboards';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
profiler,
|
profiler,
|
||||||
@@ -92,5 +92,5 @@ export {
|
|||||||
manageDashboardsDirective,
|
manageDashboardsDirective,
|
||||||
TimeSeries,
|
TimeSeries,
|
||||||
updateLegendValues,
|
updateLegendValues,
|
||||||
searchResultsDirective
|
searchResultsDirective,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ export function arrayJoin() {
|
|||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
require: 'ngModel',
|
require: 'ngModel',
|
||||||
link: function(scope, element, attr, ngModel) {
|
link: function(scope, element, attr, ngModel) {
|
||||||
|
|
||||||
function split_array(text) {
|
function split_array(text) {
|
||||||
return (text || '').split(',');
|
return (text || '').split(',');
|
||||||
}
|
}
|
||||||
@@ -25,9 +24,8 @@ export function arrayJoin() {
|
|||||||
|
|
||||||
ngModel.$parsers.push(split_array);
|
ngModel.$parsers.push(split_array);
|
||||||
ngModel.$formatters.push(join_array);
|
ngModel.$formatters.push(join_array);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
coreModule.directive('arrayJoin', arrayJoin);
|
coreModule.directive('arrayJoin', arrayJoin);
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ export class DeltaCtrl {
|
|||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $rootScope) {
|
constructor(private $rootScope) {
|
||||||
|
const waitForCompile = mutations => {
|
||||||
const waitForCompile = (mutations) => {
|
|
||||||
if (mutations.length === 1) {
|
if (mutations.length === 1) {
|
||||||
this.$rootScope.appEvent('json-diff-ready');
|
this.$rootScope.appEvent('json-diff-ready');
|
||||||
}
|
}
|
||||||
@@ -72,7 +71,7 @@ export function linkJson() {
|
|||||||
link: '@lineLink',
|
link: '@lineLink',
|
||||||
switchView: '&',
|
switchView: '&',
|
||||||
},
|
},
|
||||||
template: `<a class="diff-linenum btn btn-inverse btn-small" ng-click="ctrl.goToLine(link)">Line {{ line }}</a>`
|
template: `<a class="diff-linenum btn btn-inverse btn-small" ng-click="ctrl.goToLine(link)">Line {{ line }}</a>`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
coreModule.directive('diffLinkJson', linkJson);
|
coreModule.directive('diffLinkJson', linkJson);
|
||||||
|
|||||||
@@ -8,19 +8,23 @@ coreModule.directive('giveFocus', function() {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.$watch(attrs.giveFocus, function (newValue) {
|
scope.$watch(
|
||||||
if (!newValue) {
|
attrs.giveFocus,
|
||||||
return;
|
function(newValue) {
|
||||||
}
|
if (!newValue) {
|
||||||
setTimeout(function() {
|
return;
|
||||||
element.focus();
|
|
||||||
var domEl = element[0];
|
|
||||||
if (domEl.setSelectionRange) {
|
|
||||||
var pos = element.val().length * 2;
|
|
||||||
domEl.setSelectionRange(pos, pos);
|
|
||||||
}
|
}
|
||||||
}, 200);
|
setTimeout(function() {
|
||||||
}, true);
|
element.focus();
|
||||||
|
var domEl = element[0];
|
||||||
|
if (domEl.setSelectionRange) {
|
||||||
|
var pos = element.val().length * 2;
|
||||||
|
domEl.setSelectionRange(pos, pos);
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
},
|
||||||
|
true
|
||||||
|
);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
import angular from "angular";
|
import angular from 'angular';
|
||||||
import Clipboard from "clipboard";
|
import Clipboard from 'clipboard';
|
||||||
import coreModule from "../core_module";
|
import coreModule from '../core_module';
|
||||||
import kbn from "app/core/utils/kbn";
|
import kbn from 'app/core/utils/kbn';
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
function tip($compile) {
|
function tip($compile) {
|
||||||
return {
|
return {
|
||||||
restrict: "E",
|
restrict: 'E',
|
||||||
link: function(scope, elem, attrs) {
|
link: function(scope, elem, attrs) {
|
||||||
var _t =
|
var _t =
|
||||||
'<i class="grafana-tip fa fa-' +
|
'<i class="grafana-tip fa fa-' +
|
||||||
(attrs.icon || "question-circle") +
|
(attrs.icon || 'question-circle') +
|
||||||
'" bs-tooltip="\'' +
|
'" bs-tooltip="\'' +
|
||||||
kbn.addslashes(elem.text()) +
|
kbn.addslashes(elem.text()) +
|
||||||
"'\"></i>";
|
'\'"></i>';
|
||||||
_t = _t.replace(/{/g, "\\{").replace(/}/g, "\\}");
|
_t = _t.replace(/{/g, '\\{').replace(/}/g, '\\}');
|
||||||
elem.replaceWith($compile(angular.element(_t))(scope));
|
elem.replaceWith($compile(angular.element(_t))(scope));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function clipboardButton() {
|
function clipboardButton() {
|
||||||
return {
|
return {
|
||||||
scope: {
|
scope: {
|
||||||
getText: "&clipboardButton"
|
getText: '&clipboardButton',
|
||||||
},
|
},
|
||||||
link: function(scope, elem) {
|
link: function(scope, elem) {
|
||||||
scope.clipboard = new Clipboard(elem[0], {
|
scope.clipboard = new Clipboard(elem[0], {
|
||||||
text: function() {
|
text: function() {
|
||||||
return scope.getText();
|
return scope.getText();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.$on("$destroy", function() {
|
scope.$on('$destroy', function() {
|
||||||
if (scope.clipboard) {
|
if (scope.clipboard) {
|
||||||
scope.clipboard.destroy();
|
scope.clipboard.destroy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
function compile($compile) {
|
function compile($compile) {
|
||||||
return {
|
return {
|
||||||
restrict: "A",
|
restrict: 'A',
|
||||||
link: function(scope, element, attrs) {
|
link: function(scope, element, attrs) {
|
||||||
scope.$watch(
|
scope.$watch(
|
||||||
function(scope) {
|
function(scope) {
|
||||||
@@ -55,42 +55,42 @@ function compile($compile) {
|
|||||||
$compile(element.contents())(scope);
|
$compile(element.contents())(scope);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function watchChange() {
|
function watchChange() {
|
||||||
return {
|
return {
|
||||||
scope: { onchange: "&watchChange" },
|
scope: { onchange: '&watchChange' },
|
||||||
link: function(scope, element) {
|
link: function(scope, element) {
|
||||||
element.on("input", function() {
|
element.on('input', function() {
|
||||||
scope.$apply(function() {
|
scope.$apply(function() {
|
||||||
scope.onchange({ inputValue: element.val() });
|
scope.onchange({ inputValue: element.val() });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
function editorOptBool($compile) {
|
function editorOptBool($compile) {
|
||||||
return {
|
return {
|
||||||
restrict: "E",
|
restrict: 'E',
|
||||||
link: function(scope, elem, attrs) {
|
link: function(scope, elem, attrs) {
|
||||||
var ngchange = attrs.change ? ' ng-change="' + attrs.change + '"' : "";
|
var ngchange = attrs.change ? ' ng-change="' + attrs.change + '"' : '';
|
||||||
var tip = attrs.tip ? " <tip>" + attrs.tip + "</tip>" : "";
|
var tip = attrs.tip ? ' <tip>' + attrs.tip + '</tip>' : '';
|
||||||
var showIf = attrs.showIf ? ' ng-show="' + attrs.showIf + '" ' : "";
|
var showIf = attrs.showIf ? ' ng-show="' + attrs.showIf + '" ' : '';
|
||||||
|
|
||||||
var template =
|
var template =
|
||||||
'<div class="editor-option gf-form-checkbox text-center"' +
|
'<div class="editor-option gf-form-checkbox text-center"' +
|
||||||
showIf +
|
showIf +
|
||||||
">" +
|
'>' +
|
||||||
' <label for="' +
|
' <label for="' +
|
||||||
attrs.model +
|
attrs.model +
|
||||||
'" class="small">' +
|
'" class="small">' +
|
||||||
attrs.text +
|
attrs.text +
|
||||||
tip +
|
tip +
|
||||||
"</label>" +
|
'</label>' +
|
||||||
'<input class="cr1" id="' +
|
'<input class="cr1" id="' +
|
||||||
attrs.model +
|
attrs.model +
|
||||||
'" type="checkbox" ' +
|
'" type="checkbox" ' +
|
||||||
@@ -105,19 +105,19 @@ function editorOptBool($compile) {
|
|||||||
attrs.model +
|
attrs.model +
|
||||||
'" class="cr1"></label>';
|
'" class="cr1"></label>';
|
||||||
elem.replaceWith($compile(angular.element(template))(scope));
|
elem.replaceWith($compile(angular.element(template))(scope));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
function editorCheckbox($compile, $interpolate) {
|
function editorCheckbox($compile, $interpolate) {
|
||||||
return {
|
return {
|
||||||
restrict: "E",
|
restrict: 'E',
|
||||||
link: function(scope, elem, attrs) {
|
link: function(scope, elem, attrs) {
|
||||||
var text = $interpolate(attrs.text)(scope);
|
var text = $interpolate(attrs.text)(scope);
|
||||||
var model = $interpolate(attrs.model)(scope);
|
var model = $interpolate(attrs.model)(scope);
|
||||||
var ngchange = attrs.change ? ' ng-change="' + attrs.change + '"' : "";
|
var ngchange = attrs.change ? ' ng-change="' + attrs.change + '"' : '';
|
||||||
var tip = attrs.tip ? " <tip>" + attrs.tip + "</tip>" : "";
|
var tip = attrs.tip ? ' <tip>' + attrs.tip + '</tip>' : '';
|
||||||
var label =
|
var label =
|
||||||
'<label for="' +
|
'<label for="' +
|
||||||
scope.$id +
|
scope.$id +
|
||||||
@@ -125,7 +125,7 @@ function editorCheckbox($compile, $interpolate) {
|
|||||||
'" class="checkbox-label">' +
|
'" class="checkbox-label">' +
|
||||||
text +
|
text +
|
||||||
tip +
|
tip +
|
||||||
"</label>";
|
'</label>';
|
||||||
|
|
||||||
var template =
|
var template =
|
||||||
'<input class="cr1" id="' +
|
'<input class="cr1" id="' +
|
||||||
@@ -145,21 +145,21 @@ function editorCheckbox($compile, $interpolate) {
|
|||||||
'" class="cr1"></label>';
|
'" class="cr1"></label>';
|
||||||
|
|
||||||
template = template + label;
|
template = template + label;
|
||||||
elem.addClass("gf-form-checkbox");
|
elem.addClass('gf-form-checkbox');
|
||||||
elem.html($compile(angular.element(template))(scope));
|
elem.html($compile(angular.element(template))(scope));
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
function gfDropdown($parse, $compile, $timeout) {
|
function gfDropdown($parse, $compile, $timeout) {
|
||||||
function buildTemplate(items, placement?) {
|
function buildTemplate(items, placement?) {
|
||||||
var upclass = placement === "top" ? "dropup" : "";
|
var upclass = placement === 'top' ? 'dropup' : '';
|
||||||
var ul = [
|
var ul = [
|
||||||
'<ul class="dropdown-menu ' +
|
'<ul class="dropdown-menu ' +
|
||||||
upclass +
|
upclass +
|
||||||
'" role="menu" aria-labelledby="drop1">',
|
'" role="menu" aria-labelledby="drop1">',
|
||||||
"</ul>"
|
'</ul>',
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let index = 0; index < items.length; index++) {
|
for (let index = 0; index < items.length; index++) {
|
||||||
@@ -171,26 +171,26 @@ function gfDropdown($parse, $compile, $timeout) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var li =
|
var li =
|
||||||
"<li" +
|
'<li' +
|
||||||
(item.submenu && item.submenu.length
|
(item.submenu && item.submenu.length
|
||||||
? ' class="dropdown-submenu"'
|
? ' class="dropdown-submenu"'
|
||||||
: "") +
|
: '') +
|
||||||
">" +
|
'>' +
|
||||||
'<a tabindex="-1" ng-href="' +
|
'<a tabindex="-1" ng-href="' +
|
||||||
(item.href || "") +
|
(item.href || '') +
|
||||||
'"' +
|
'"' +
|
||||||
(item.click ? ' ng-click="' + item.click + '"' : "") +
|
(item.click ? ' ng-click="' + item.click + '"' : '') +
|
||||||
(item.target ? ' target="' + item.target + '"' : "") +
|
(item.target ? ' target="' + item.target + '"' : '') +
|
||||||
(item.method ? ' data-method="' + item.method + '"' : "") +
|
(item.method ? ' data-method="' + item.method + '"' : '') +
|
||||||
">" +
|
'>' +
|
||||||
(item.text || "") +
|
(item.text || '') +
|
||||||
"</a>";
|
'</a>';
|
||||||
|
|
||||||
if (item.submenu && item.submenu.length) {
|
if (item.submenu && item.submenu.length) {
|
||||||
li += buildTemplate(item.submenu).join("\n");
|
li += buildTemplate(item.submenu).join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
li += "</li>";
|
li += '</li>';
|
||||||
ul.splice(index + 1, 0, li);
|
ul.splice(index + 1, 0, li);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,29 +198,29 @@ function gfDropdown($parse, $compile, $timeout) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
restrict: "EA",
|
restrict: 'EA',
|
||||||
scope: true,
|
scope: true,
|
||||||
link: function postLink(scope, iElement, iAttrs) {
|
link: function postLink(scope, iElement, iAttrs) {
|
||||||
var getter = $parse(iAttrs.gfDropdown),
|
var getter = $parse(iAttrs.gfDropdown),
|
||||||
items = getter(scope);
|
items = getter(scope);
|
||||||
$timeout(function() {
|
$timeout(function() {
|
||||||
var placement = iElement.data("placement");
|
var placement = iElement.data('placement');
|
||||||
var dropdown = angular.element(
|
var dropdown = angular.element(
|
||||||
buildTemplate(items, placement).join("")
|
buildTemplate(items, placement).join('')
|
||||||
);
|
);
|
||||||
dropdown.insertAfter(iElement);
|
dropdown.insertAfter(iElement);
|
||||||
$compile(iElement.next("ul.dropdown-menu"))(scope);
|
$compile(iElement.next('ul.dropdown-menu'))(scope);
|
||||||
});
|
});
|
||||||
|
|
||||||
iElement.addClass("dropdown-toggle").attr("data-toggle", "dropdown");
|
iElement.addClass('dropdown-toggle').attr('data-toggle', 'dropdown');
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
coreModule.directive("tip", tip);
|
coreModule.directive('tip', tip);
|
||||||
coreModule.directive("clipboardButton", clipboardButton);
|
coreModule.directive('clipboardButton', clipboardButton);
|
||||||
coreModule.directive("compile", compile);
|
coreModule.directive('compile', compile);
|
||||||
coreModule.directive("watchChange", watchChange);
|
coreModule.directive('watchChange', watchChange);
|
||||||
coreModule.directive("editorOptBool", editorOptBool);
|
coreModule.directive('editorOptBool', editorOptBool);
|
||||||
coreModule.directive("editorCheckbox", editorCheckbox);
|
coreModule.directive('editorCheckbox', editorCheckbox);
|
||||||
coreModule.directive("gfDropdown", gfDropdown);
|
coreModule.directive('gfDropdown', gfDropdown);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ function ngModelOnBlur() {
|
|||||||
ngModelCtrl.$setViewValue(elm.val());
|
ngModelCtrl.$setViewValue(elm.val());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,12 +25,14 @@ function emptyToNull() {
|
|||||||
return {
|
return {
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
require: 'ngModel',
|
require: 'ngModel',
|
||||||
link: function (scope, elm, attrs, ctrl) {
|
link: function(scope, elm, attrs, ctrl) {
|
||||||
ctrl.$parsers.push(function (viewValue) {
|
ctrl.$parsers.push(function(viewValue) {
|
||||||
if (viewValue === "") { return null; }
|
if (viewValue === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return viewValue;
|
return viewValue;
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +50,7 @@ function validTimeSpan() {
|
|||||||
var info = rangeUtil.describeTextRange(viewValue);
|
var info = rangeUtil.describeTextRange(viewValue);
|
||||||
return info.invalid !== true;
|
return info.invalid !== true;
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ function getBlockNodes(nodes) {
|
|||||||
|
|
||||||
/** @ngInject **/
|
/** @ngInject **/
|
||||||
function rebuildOnChange($animate) {
|
function rebuildOnChange($animate) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
multiElement: true,
|
multiElement: true,
|
||||||
terminal: true,
|
terminal: true,
|
||||||
@@ -48,7 +47,10 @@ function rebuildOnChange($animate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.$watch(attrs.property, function rebuildOnChangeAction(value, oldValue) {
|
scope.$watch(attrs.property, function rebuildOnChangeAction(
|
||||||
|
value,
|
||||||
|
oldValue
|
||||||
|
) {
|
||||||
if (childScope && value !== oldValue) {
|
if (childScope && value !== oldValue) {
|
||||||
cleanUp();
|
cleanUp();
|
||||||
}
|
}
|
||||||
@@ -56,15 +58,17 @@ function rebuildOnChange($animate) {
|
|||||||
if (!childScope && (value || attrs.showNull)) {
|
if (!childScope && (value || attrs.showNull)) {
|
||||||
transclude(function(clone, newScope) {
|
transclude(function(clone, newScope) {
|
||||||
childScope = newScope;
|
childScope = newScope;
|
||||||
clone[clone.length++] = document.createComment(' end rebuild on change ');
|
clone[clone.length++] = document.createComment(
|
||||||
block = {clone: clone};
|
' end rebuild on change '
|
||||||
|
);
|
||||||
|
block = { clone: clone };
|
||||||
$animate.enter(clone, elem.parent(), elem);
|
$animate.enter(clone, elem.parent(), elem);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
cleanUp();
|
cleanUp();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'vendor/tagsinput/bootstrap-tagsinput.js';
|
|||||||
function djb2(str) {
|
function djb2(str) {
|
||||||
var hash = 5381;
|
var hash = 5381;
|
||||||
for (var i = 0; i < str.length; i++) {
|
for (var i = 0; i < str.length; i++) {
|
||||||
hash = ((hash << 5) + hash) + str.charCodeAt(i); /* hash * 33 + c */
|
hash = (hash << 5) + hash + str.charCodeAt(i); /* hash * 33 + c */
|
||||||
}
|
}
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
@@ -14,33 +14,79 @@ function djb2(str) {
|
|||||||
function setColor(name, element) {
|
function setColor(name, element) {
|
||||||
var hash = djb2(name.toLowerCase());
|
var hash = djb2(name.toLowerCase());
|
||||||
var colors = [
|
var colors = [
|
||||||
"#E24D42","#1F78C1","#BA43A9","#705DA0","#466803",
|
'#E24D42',
|
||||||
"#508642","#447EBC","#C15C17","#890F02","#757575",
|
'#1F78C1',
|
||||||
"#0A437C","#6D1F62","#584477","#629E51","#2F4F4F",
|
'#BA43A9',
|
||||||
"#BF1B00","#806EB7","#8a2eb8", "#699e00","#000000",
|
'#705DA0',
|
||||||
"#3F6833","#2F575E","#99440A","#E0752D","#0E4AB4",
|
'#466803',
|
||||||
"#58140C","#052B51","#511749","#3F2B5B",
|
'#508642',
|
||||||
|
'#447EBC',
|
||||||
|
'#C15C17',
|
||||||
|
'#890F02',
|
||||||
|
'#757575',
|
||||||
|
'#0A437C',
|
||||||
|
'#6D1F62',
|
||||||
|
'#584477',
|
||||||
|
'#629E51',
|
||||||
|
'#2F4F4F',
|
||||||
|
'#BF1B00',
|
||||||
|
'#806EB7',
|
||||||
|
'#8a2eb8',
|
||||||
|
'#699e00',
|
||||||
|
'#000000',
|
||||||
|
'#3F6833',
|
||||||
|
'#2F575E',
|
||||||
|
'#99440A',
|
||||||
|
'#E0752D',
|
||||||
|
'#0E4AB4',
|
||||||
|
'#58140C',
|
||||||
|
'#052B51',
|
||||||
|
'#511749',
|
||||||
|
'#3F2B5B',
|
||||||
];
|
];
|
||||||
var borderColors = [
|
var borderColors = [
|
||||||
"#FF7368","#459EE7","#E069CF","#9683C6","#6C8E29",
|
'#FF7368',
|
||||||
"#76AC68","#6AA4E2","#E7823D","#AF3528","#9B9B9B",
|
'#459EE7',
|
||||||
"#3069A2","#934588","#7E6A9D","#88C477","#557575",
|
'#E069CF',
|
||||||
"#E54126","#A694DD","#B054DE", "#8FC426","#262626",
|
'#9683C6',
|
||||||
"#658E59","#557D84","#BF6A30","#FF9B53","#3470DA",
|
'#6C8E29',
|
||||||
"#7E3A32","#2B5177","#773D6F","#655181",
|
'#76AC68',
|
||||||
|
'#6AA4E2',
|
||||||
|
'#E7823D',
|
||||||
|
'#AF3528',
|
||||||
|
'#9B9B9B',
|
||||||
|
'#3069A2',
|
||||||
|
'#934588',
|
||||||
|
'#7E6A9D',
|
||||||
|
'#88C477',
|
||||||
|
'#557575',
|
||||||
|
'#E54126',
|
||||||
|
'#A694DD',
|
||||||
|
'#B054DE',
|
||||||
|
'#8FC426',
|
||||||
|
'#262626',
|
||||||
|
'#658E59',
|
||||||
|
'#557D84',
|
||||||
|
'#BF6A30',
|
||||||
|
'#FF9B53',
|
||||||
|
'#3470DA',
|
||||||
|
'#7E3A32',
|
||||||
|
'#2B5177',
|
||||||
|
'#773D6F',
|
||||||
|
'#655181',
|
||||||
];
|
];
|
||||||
var color = colors[Math.abs(hash % colors.length)];
|
var color = colors[Math.abs(hash % colors.length)];
|
||||||
var borderColor = borderColors[Math.abs(hash % borderColors.length)];
|
var borderColor = borderColors[Math.abs(hash % borderColors.length)];
|
||||||
element.css("background-color", color);
|
element.css('background-color', color);
|
||||||
element.css("border-color", borderColor);
|
element.css('border-color', borderColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
function tagColorFromName() {
|
function tagColorFromName() {
|
||||||
return {
|
return {
|
||||||
scope: { tagColorFromName: "=" },
|
scope: { tagColorFromName: '=' },
|
||||||
link: function (scope, element) {
|
link: function(scope, element) {
|
||||||
setColor(scope.tagColorFromName, element);
|
setColor(scope.tagColorFromName, element);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,12 +109,11 @@ function bootstrapTagsinput() {
|
|||||||
restrict: 'EA',
|
restrict: 'EA',
|
||||||
scope: {
|
scope: {
|
||||||
model: '=ngModel',
|
model: '=ngModel',
|
||||||
onTagsUpdated: "&",
|
onTagsUpdated: '&',
|
||||||
},
|
},
|
||||||
template: '<select multiple></select>',
|
template: '<select multiple></select>',
|
||||||
replace: false,
|
replace: false,
|
||||||
link: function(scope, element, attrs) {
|
link: function(scope, element, attrs) {
|
||||||
|
|
||||||
if (!angular.isArray(scope.model)) {
|
if (!angular.isArray(scope.model)) {
|
||||||
scope.model = [];
|
scope.model = [];
|
||||||
}
|
}
|
||||||
@@ -81,13 +126,18 @@ function bootstrapTagsinput() {
|
|||||||
|
|
||||||
select.tagsinput({
|
select.tagsinput({
|
||||||
typeahead: {
|
typeahead: {
|
||||||
source: angular.isFunction(scope.$parent[attrs.typeaheadSource]) ? scope.$parent[attrs.typeaheadSource] : null
|
source: angular.isFunction(scope.$parent[attrs.typeaheadSource])
|
||||||
|
? scope.$parent[attrs.typeaheadSource]
|
||||||
|
: null,
|
||||||
},
|
},
|
||||||
widthClass: attrs.widthClass,
|
widthClass: attrs.widthClass,
|
||||||
itemValue: getItemProperty(scope, attrs.itemvalue),
|
itemValue: getItemProperty(scope, attrs.itemvalue),
|
||||||
itemText : getItemProperty(scope, attrs.itemtext),
|
itemText: getItemProperty(scope, attrs.itemtext),
|
||||||
tagClass : angular.isFunction(scope.$parent[attrs.tagclass]) ?
|
tagClass: angular.isFunction(scope.$parent[attrs.tagclass])
|
||||||
scope.$parent[attrs.tagclass] : function() { return attrs.tagclass; }
|
? scope.$parent[attrs.tagclass]
|
||||||
|
: function() {
|
||||||
|
return attrs.tagclass;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
select.on('itemAdded', function(event) {
|
select.on('itemAdded', function(event) {
|
||||||
@@ -97,7 +147,12 @@ function bootstrapTagsinput() {
|
|||||||
scope.onTagsUpdated();
|
scope.onTagsUpdated();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var tagElement = select.next().children("span").filter(function() { return $(this).text() === event.item; });
|
var tagElement = select
|
||||||
|
.next()
|
||||||
|
.children('span')
|
||||||
|
.filter(function() {
|
||||||
|
return $(this).text() === event.item;
|
||||||
|
});
|
||||||
setColor(event.item, tagElement);
|
setColor(event.item, tagElement);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -111,19 +166,22 @@ function bootstrapTagsinput() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.$watch("model", function() {
|
scope.$watch(
|
||||||
if (!angular.isArray(scope.model)) {
|
'model',
|
||||||
scope.model = [];
|
function() {
|
||||||
}
|
if (!angular.isArray(scope.model)) {
|
||||||
|
scope.model = [];
|
||||||
|
}
|
||||||
|
|
||||||
select.tagsinput('removeAll');
|
select.tagsinput('removeAll');
|
||||||
|
|
||||||
for (var i = 0; i < scope.model.length; i++) {
|
for (var i = 0; i < scope.model.length; i++) {
|
||||||
select.tagsinput('add', scope.model[i]);
|
select.tagsinput('add', scope.model[i]);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
}, true);
|
true
|
||||||
}
|
);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,19 +41,17 @@ coreModule.filter('moment', function() {
|
|||||||
|
|
||||||
coreModule.filter('noXml', function() {
|
coreModule.filter('noXml', function() {
|
||||||
var noXml = function(text) {
|
var noXml = function(text) {
|
||||||
return _.isString(text)
|
return _.isString(text)
|
||||||
? text
|
? text
|
||||||
.replace(/&/g, '&')
|
.replace(/&/g, '&')
|
||||||
.replace(/</g, '<')
|
.replace(/</g, '<')
|
||||||
.replace(/>/g, '>')
|
.replace(/>/g, '>')
|
||||||
.replace(/'/g, ''')
|
.replace(/'/g, ''')
|
||||||
.replace(/"/g, '"')
|
.replace(/"/g, '"')
|
||||||
: text;
|
: text;
|
||||||
};
|
};
|
||||||
return function(text) {
|
return function(text) {
|
||||||
return _.isArray(text)
|
return _.isArray(text) ? _.map(text, noXml) : noXml(text);
|
||||||
? _.map(text, noXml)
|
|
||||||
: noXml(text);
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
|
|
||||||
import {Observable} from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
export class LiveSrv {
|
export class LiveSrv {
|
||||||
conn: any;
|
conn: any;
|
||||||
@@ -14,7 +14,12 @@ export class LiveSrv {
|
|||||||
|
|
||||||
getWebSocketUrl() {
|
getWebSocketUrl() {
|
||||||
var l = window.location;
|
var l = window.location;
|
||||||
return ((l.protocol === "https:") ? "wss://" : "ws://") + l.host + config.appSubUrl + '/ws';
|
return (
|
||||||
|
(l.protocol === 'https:' ? 'wss://' : 'ws://') +
|
||||||
|
l.host +
|
||||||
|
config.appSubUrl +
|
||||||
|
'/ws'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getConnection() {
|
getConnection() {
|
||||||
@@ -30,25 +35,25 @@ export class LiveSrv {
|
|||||||
console.log('Live: connecting...');
|
console.log('Live: connecting...');
|
||||||
this.conn = new WebSocket(this.getWebSocketUrl());
|
this.conn = new WebSocket(this.getWebSocketUrl());
|
||||||
|
|
||||||
this.conn.onclose = (evt) => {
|
this.conn.onclose = evt => {
|
||||||
console.log("Live: websocket onclose", evt);
|
console.log('Live: websocket onclose', evt);
|
||||||
reject({message: 'Connection closed'});
|
reject({ message: 'Connection closed' });
|
||||||
|
|
||||||
this.initPromise = null;
|
this.initPromise = null;
|
||||||
setTimeout(this.reconnect.bind(this), 2000);
|
setTimeout(this.reconnect.bind(this), 2000);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.conn.onmessage = (evt) => {
|
this.conn.onmessage = evt => {
|
||||||
this.handleMessage(evt.data);
|
this.handleMessage(evt.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.conn.onerror = (evt) => {
|
this.conn.onerror = evt => {
|
||||||
this.initPromise = null;
|
this.initPromise = null;
|
||||||
reject({message: 'Connection error'});
|
reject({ message: 'Connection error' });
|
||||||
console.log("Live: websocket error", evt);
|
console.log('Live: websocket error', evt);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.conn.onopen = (evt) => {
|
this.conn.onopen = evt => {
|
||||||
console.log('opened');
|
console.log('opened');
|
||||||
this.initPromise = null;
|
this.initPromise = null;
|
||||||
resolve(this.conn);
|
resolve(this.conn);
|
||||||
@@ -62,7 +67,7 @@ export class LiveSrv {
|
|||||||
message = JSON.parse(message);
|
message = JSON.parse(message);
|
||||||
|
|
||||||
if (!message.stream) {
|
if (!message.stream) {
|
||||||
console.log("Error: stream message without stream!", message);
|
console.log('Error: stream message without stream!', message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +90,7 @@ export class LiveSrv {
|
|||||||
|
|
||||||
this.getConnection().then(conn => {
|
this.getConnection().then(conn => {
|
||||||
_.each(this.observers, (value, key) => {
|
_.each(this.observers, (value, key) => {
|
||||||
this.send({action: 'subscribe', stream: key});
|
this.send({ action: 'subscribe', stream: key });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -98,7 +103,7 @@ export class LiveSrv {
|
|||||||
this.observers[stream] = observer;
|
this.observers[stream] = observer;
|
||||||
|
|
||||||
this.getConnection().then(conn => {
|
this.getConnection().then(conn => {
|
||||||
this.send({action: 'subscribe', stream: stream});
|
this.send({ action: 'subscribe', stream: stream });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +112,7 @@ export class LiveSrv {
|
|||||||
delete this.observers[stream];
|
delete this.observers[stream];
|
||||||
|
|
||||||
this.getConnection().then(conn => {
|
this.getConnection().then(conn => {
|
||||||
this.send({action: 'unsubscribe', stream: stream});
|
this.send({ action: 'unsubscribe', stream: stream });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,8 +131,7 @@ export class LiveSrv {
|
|||||||
// this.send({action: 'subscribe', stream: name});
|
// this.send({action: 'subscribe', stream: name});
|
||||||
// });
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var instance = new LiveSrv();
|
var instance = new LiveSrv();
|
||||||
export {instance as liveSrv};
|
export { instance as liveSrv };
|
||||||
|
|||||||
Vendored
+5
-9
@@ -1,18 +1,14 @@
|
|||||||
declare module "app/core/controllers/all" {
|
declare module 'app/core/controllers/all' {
|
||||||
let json: any;
|
let json: any;
|
||||||
export {json};
|
export { json };
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module "app/core/routes/all" {
|
declare module 'app/core/routes/all' {
|
||||||
let json: any;
|
let json: any;
|
||||||
export {json};
|
export { json };
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module "app/core/services/all" {
|
declare module 'app/core/services/all' {
|
||||||
let json: any;
|
let json: any;
|
||||||
export default json;
|
export default json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export class NavModelSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getCfgNode() {
|
getCfgNode() {
|
||||||
return _.find(this.navItems, {id: 'cfg'});
|
return _.find(this.navItems, { id: 'cfg' });
|
||||||
}
|
}
|
||||||
|
|
||||||
getNav(...args) {
|
getNav(...args) {
|
||||||
@@ -48,7 +48,7 @@ export class NavModelSrv {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = _.find(children, {id: id});
|
let node = _.find(children, { id: id });
|
||||||
nav.breadcrumbs.push(node);
|
nav.breadcrumbs.push(node);
|
||||||
nav.node = node;
|
nav.node = node;
|
||||||
nav.main = node;
|
nav.main = node;
|
||||||
@@ -70,15 +70,15 @@ export class NavModelSrv {
|
|||||||
|
|
||||||
getNotFoundNav() {
|
getNotFoundNav() {
|
||||||
var node = {
|
var node = {
|
||||||
text: "Page not found",
|
text: 'Page not found',
|
||||||
icon: "fa fa-fw fa-warning",
|
icon: 'fa fa-fw fa-warning',
|
||||||
subTitle: "404 Error"
|
subTitle: '404 Error',
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
breadcrumbs: [node],
|
breadcrumbs: [node],
|
||||||
node: node,
|
node: node,
|
||||||
main: node
|
main: node,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-18
@@ -20,15 +20,30 @@ export class Profiler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$rootScope.$watch(() => {
|
$rootScope.$watch(
|
||||||
this.digestCounter++;
|
() => {
|
||||||
return false;
|
this.digestCounter++;
|
||||||
}, () => {});
|
return false;
|
||||||
|
},
|
||||||
|
() => {}
|
||||||
|
);
|
||||||
|
|
||||||
$rootScope.onAppEvent('refresh', this.refresh.bind(this), $rootScope);
|
$rootScope.onAppEvent('refresh', this.refresh.bind(this), $rootScope);
|
||||||
$rootScope.onAppEvent('dashboard-fetch-end', this.dashboardFetched.bind(this), $rootScope);
|
$rootScope.onAppEvent(
|
||||||
$rootScope.onAppEvent('dashboard-initialized', this.dashboardInitialized.bind(this), $rootScope);
|
'dashboard-fetch-end',
|
||||||
$rootScope.onAppEvent('panel-initialized', this.panelInitialized.bind(this), $rootScope);
|
this.dashboardFetched.bind(this),
|
||||||
|
$rootScope
|
||||||
|
);
|
||||||
|
$rootScope.onAppEvent(
|
||||||
|
'dashboard-initialized',
|
||||||
|
this.dashboardInitialized.bind(this),
|
||||||
|
$rootScope
|
||||||
|
);
|
||||||
|
$rootScope.onAppEvent(
|
||||||
|
'panel-initialized',
|
||||||
|
this.panelInitialized.bind(this),
|
||||||
|
$rootScope
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
refresh() {
|
||||||
@@ -55,12 +70,21 @@ export class Profiler {
|
|||||||
|
|
||||||
dashboardInitialized() {
|
dashboardInitialized() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log("Dashboard::Performance Total Digests: " + this.digestCounter);
|
console.log(
|
||||||
console.log("Dashboard::Performance Total Watchers: " + this.getTotalWatcherCount());
|
'Dashboard::Performance Total Digests: ' + this.digestCounter
|
||||||
console.log("Dashboard::Performance Total ScopeCount: " + this.scopeCount);
|
);
|
||||||
|
console.log(
|
||||||
|
'Dashboard::Performance Total Watchers: ' + this.getTotalWatcherCount()
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
'Dashboard::Performance Total ScopeCount: ' + this.scopeCount
|
||||||
|
);
|
||||||
|
|
||||||
var timeTaken = this.timings.lastPanelInitializedAt - this.timings.dashboardLoadStart;
|
var timeTaken =
|
||||||
console.log("Dashboard::Performance All panels initialized in " + timeTaken + " ms");
|
this.timings.lastPanelInitializedAt - this.timings.dashboardLoadStart;
|
||||||
|
console.log(
|
||||||
|
'Dashboard::Performance All panels initialized in ' + timeTaken + ' ms'
|
||||||
|
);
|
||||||
|
|
||||||
// measure digest performance
|
// measure digest performance
|
||||||
var rootDigestStart = window.performance.now();
|
var rootDigestStart = window.performance.now();
|
||||||
@@ -68,7 +92,10 @@ export class Profiler {
|
|||||||
this.$rootScope.$apply();
|
this.$rootScope.$apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Dashboard::Performance Root Digest " + ((window.performance.now() - rootDigestStart) / 30));
|
console.log(
|
||||||
|
'Dashboard::Performance Root Digest ' +
|
||||||
|
(window.performance.now() - rootDigestStart) / 30
|
||||||
|
);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,15 +104,15 @@ export class Profiler {
|
|||||||
var scopes = 0;
|
var scopes = 0;
|
||||||
var root = $(document.getElementsByTagName('body'));
|
var root = $(document.getElementsByTagName('body'));
|
||||||
|
|
||||||
var f = function (element) {
|
var f = function(element) {
|
||||||
if (element.data().hasOwnProperty('$scope')) {
|
if (element.data().hasOwnProperty('$scope')) {
|
||||||
scopes++;
|
scopes++;
|
||||||
angular.forEach(element.data().$scope.$$watchers, function () {
|
angular.forEach(element.data().$scope.$$watchers, function() {
|
||||||
count++;
|
count++;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
angular.forEach(element.children(), function (childElement) {
|
angular.forEach(element.children(), function(childElement) {
|
||||||
f($(childElement));
|
f($(childElement));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -116,8 +143,7 @@ export class Profiler {
|
|||||||
this.panelsInitCount++;
|
this.panelsInitCount++;
|
||||||
this.timings.lastPanelInitializedAt = new Date().getTime();
|
this.timings.lastPanelInitializedAt = new Date().getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var profiler = new Profiler();
|
var profiler = new Profiler();
|
||||||
export {profiler};
|
export { profiler };
|
||||||
|
|||||||
@@ -4,19 +4,23 @@ export class BundleLoader {
|
|||||||
constructor(bundleName) {
|
constructor(bundleName) {
|
||||||
var defer = null;
|
var defer = null;
|
||||||
|
|
||||||
this.lazy = ["$q", "$route", "$rootScope", ($q, $route, $rootScope) => {
|
this.lazy = [
|
||||||
if (defer) {
|
'$q',
|
||||||
|
'$route',
|
||||||
|
'$rootScope',
|
||||||
|
($q, $route, $rootScope) => {
|
||||||
|
if (defer) {
|
||||||
|
return defer.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
defer = $q.defer();
|
||||||
|
|
||||||
|
System.import(bundleName).then(() => {
|
||||||
|
defer.resolve();
|
||||||
|
});
|
||||||
|
|
||||||
return defer.promise;
|
return defer.promise;
|
||||||
}
|
},
|
||||||
|
];
|
||||||
defer = $q.defer();
|
|
||||||
|
|
||||||
System.import(bundleName).then(() => {
|
|
||||||
defer.resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
return defer.promise;
|
|
||||||
}];
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import coreModule from '../core_module';
|
import coreModule from '../core_module';
|
||||||
|
|
||||||
export class LoadDashboardCtrl {
|
export class LoadDashboardCtrl {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, $routeParams, dashboardLoaderSrv, backendSrv, $location) {
|
constructor($scope, $routeParams, dashboardLoaderSrv, backendSrv, $location) {
|
||||||
$scope.appEvent("dashboard-fetch-start");
|
$scope.appEvent('dashboard-fetch-start');
|
||||||
|
|
||||||
if (!$routeParams.slug) {
|
if (!$routeParams.slug) {
|
||||||
backendSrv.get('/api/dashboards/home').then(function(homeDash) {
|
backendSrv.get('/api/dashboards/home').then(function(homeDash) {
|
||||||
@@ -19,33 +18,37 @@ export class LoadDashboardCtrl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboardLoaderSrv.loadDashboard($routeParams.type, $routeParams.slug).then(function(result) {
|
dashboardLoaderSrv
|
||||||
if ($routeParams.keepRows) {
|
.loadDashboard($routeParams.type, $routeParams.slug)
|
||||||
result.meta.keepRows = true;
|
.then(function(result) {
|
||||||
}
|
if ($routeParams.keepRows) {
|
||||||
$scope.initDashboard(result, $scope);
|
result.meta.keepRows = true;
|
||||||
});
|
}
|
||||||
|
$scope.initDashboard(result, $scope);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NewDashboardCtrl {
|
export class NewDashboardCtrl {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, $routeParams) {
|
constructor($scope, $routeParams) {
|
||||||
$scope.initDashboard({
|
$scope.initDashboard(
|
||||||
meta: { canStar: false, canShare: false, isNew: true },
|
{
|
||||||
dashboard: {
|
meta: { canStar: false, canShare: false, isNew: true },
|
||||||
title: "New dashboard",
|
dashboard: {
|
||||||
panels: [
|
title: 'New dashboard',
|
||||||
{
|
panels: [
|
||||||
type: 'add-panel',
|
{
|
||||||
gridPos: {x: 0, y: 0, w: 12, h: 9},
|
type: 'add-panel',
|
||||||
title: 'Panel Title',
|
gridPos: { x: 0, y: 0, w: 12, h: 9 },
|
||||||
}
|
title: 'Panel Title',
|
||||||
],
|
},
|
||||||
folderId: Number($routeParams.folderId)
|
],
|
||||||
|
folderId: Number($routeParams.folderId),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}, $scope);
|
$scope
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+284
-262
@@ -6,277 +6,299 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
|
|||||||
$locationProvider.html5Mode(true);
|
$locationProvider.html5Mode(true);
|
||||||
|
|
||||||
var loadOrgBundle = {
|
var loadOrgBundle = {
|
||||||
lazy: ["$q", "$route", "$rootScope", ($q, $route, $rootScope) => {
|
lazy: [
|
||||||
return System.import('app/features/org/all');
|
'$q',
|
||||||
}]
|
'$route',
|
||||||
|
'$rootScope',
|
||||||
|
($q, $route, $rootScope) => {
|
||||||
|
return System.import('app/features/org/all');
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
var loadAdminBundle = {
|
var loadAdminBundle = {
|
||||||
lazy: ["$q", "$route", "$rootScope", ($q, $route, $rootScope) => {
|
lazy: [
|
||||||
return System.import('app/features/admin/admin');
|
'$q',
|
||||||
}]
|
'$route',
|
||||||
|
'$rootScope',
|
||||||
|
($q, $route, $rootScope) => {
|
||||||
|
return System.import('app/features/admin/admin');
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
var loadAlertingBundle = {
|
var loadAlertingBundle = {
|
||||||
lazy: ["$q", "$route", "$rootScope", ($q, $route, $rootScope) => {
|
lazy: [
|
||||||
return System.import('app/features/alerting/all');
|
'$q',
|
||||||
}]
|
'$route',
|
||||||
|
'$rootScope',
|
||||||
|
($q, $route, $rootScope) => {
|
||||||
|
return System.import('app/features/alerting/all');
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
$routeProvider
|
$routeProvider
|
||||||
.when('/', {
|
.when('/', {
|
||||||
templateUrl: 'public/app/partials/dashboard.html',
|
templateUrl: 'public/app/partials/dashboard.html',
|
||||||
controller : 'LoadDashboardCtrl',
|
controller: 'LoadDashboardCtrl',
|
||||||
reloadOnSearch: false,
|
reloadOnSearch: false,
|
||||||
pageClass: 'page-dashboard',
|
pageClass: 'page-dashboard',
|
||||||
})
|
})
|
||||||
.when('/dashboard/:type/:slug', {
|
.when('/dashboard/:type/:slug', {
|
||||||
templateUrl: 'public/app/partials/dashboard.html',
|
templateUrl: 'public/app/partials/dashboard.html',
|
||||||
controller : 'LoadDashboardCtrl',
|
controller: 'LoadDashboardCtrl',
|
||||||
reloadOnSearch: false,
|
reloadOnSearch: false,
|
||||||
pageClass: 'page-dashboard',
|
pageClass: 'page-dashboard',
|
||||||
})
|
})
|
||||||
.when('/dashboard-solo/:type/:slug', {
|
.when('/dashboard-solo/:type/:slug', {
|
||||||
templateUrl: 'public/app/features/panel/partials/soloPanel.html',
|
templateUrl: 'public/app/features/panel/partials/soloPanel.html',
|
||||||
controller : 'SoloPanelCtrl',
|
controller: 'SoloPanelCtrl',
|
||||||
reloadOnSearch: false,
|
reloadOnSearch: false,
|
||||||
pageClass: 'page-dashboard',
|
pageClass: 'page-dashboard',
|
||||||
})
|
})
|
||||||
.when('/dashboard/new', {
|
.when('/dashboard/new', {
|
||||||
templateUrl: 'public/app/partials/dashboard.html',
|
templateUrl: 'public/app/partials/dashboard.html',
|
||||||
controller : 'NewDashboardCtrl',
|
controller: 'NewDashboardCtrl',
|
||||||
reloadOnSearch: false,
|
reloadOnSearch: false,
|
||||||
pageClass: 'page-dashboard',
|
pageClass: 'page-dashboard',
|
||||||
})
|
})
|
||||||
.when('/dashboard/import', {
|
.when('/dashboard/import', {
|
||||||
templateUrl: 'public/app/features/dashboard/partials/dashboardImport.html',
|
templateUrl:
|
||||||
controller : 'DashboardImportCtrl',
|
'public/app/features/dashboard/partials/dashboard_import.html',
|
||||||
controllerAs: 'ctrl',
|
controller: 'DashboardImportCtrl',
|
||||||
})
|
controllerAs: 'ctrl',
|
||||||
.when('/datasources', {
|
})
|
||||||
templateUrl: 'public/app/features/plugins/partials/ds_list.html',
|
.when('/datasources', {
|
||||||
controller : 'DataSourcesCtrl',
|
templateUrl: 'public/app/features/plugins/partials/ds_list.html',
|
||||||
controllerAs: 'ctrl',
|
controller: 'DataSourcesCtrl',
|
||||||
})
|
controllerAs: 'ctrl',
|
||||||
.when('/datasources/edit/:id', {
|
})
|
||||||
templateUrl: 'public/app/features/plugins/partials/ds_edit.html',
|
.when('/datasources/edit/:id', {
|
||||||
controller : 'DataSourceEditCtrl',
|
templateUrl: 'public/app/features/plugins/partials/ds_edit.html',
|
||||||
controllerAs: 'ctrl',
|
controller: 'DataSourceEditCtrl',
|
||||||
})
|
controllerAs: 'ctrl',
|
||||||
.when('/datasources/new', {
|
})
|
||||||
templateUrl: 'public/app/features/plugins/partials/ds_edit.html',
|
.when('/datasources/new', {
|
||||||
controller : 'DataSourceEditCtrl',
|
templateUrl: 'public/app/features/plugins/partials/ds_edit.html',
|
||||||
controllerAs: 'ctrl',
|
controller: 'DataSourceEditCtrl',
|
||||||
})
|
controllerAs: 'ctrl',
|
||||||
.when('/dashboards', {
|
})
|
||||||
templateUrl: 'public/app/features/dashboard/partials/dashboard_list.html',
|
.when('/dashboards', {
|
||||||
controller : 'DashboardListCtrl',
|
templateUrl: 'public/app/features/dashboard/partials/dashboard_list.html',
|
||||||
controllerAs: 'ctrl',
|
controller: 'DashboardListCtrl',
|
||||||
})
|
controllerAs: 'ctrl',
|
||||||
.when('/dashboards/folder/new', {
|
})
|
||||||
templateUrl: 'public/app/features/dashboard/partials/create_folder.html',
|
.when('/dashboards/folder/new', {
|
||||||
controller : 'CreateFolderCtrl',
|
templateUrl: 'public/app/features/dashboard/partials/create_folder.html',
|
||||||
controllerAs: 'ctrl',
|
controller: 'CreateFolderCtrl',
|
||||||
})
|
controllerAs: 'ctrl',
|
||||||
.when('/dashboards/folder/:folderId/:slug/permissions', {
|
})
|
||||||
templateUrl: 'public/app/features/dashboard/partials/folder_permissions.html',
|
.when('/dashboards/folder/:folderId/:slug/permissions', {
|
||||||
controller : 'FolderPermissionsCtrl',
|
templateUrl:
|
||||||
controllerAs: 'ctrl',
|
'public/app/features/dashboard/partials/folder_permissions.html',
|
||||||
})
|
controller: 'FolderPermissionsCtrl',
|
||||||
.when('/dashboards/folder/:folderId/:slug/settings', {
|
controllerAs: 'ctrl',
|
||||||
templateUrl: 'public/app/features/dashboard/partials/folder_settings.html',
|
})
|
||||||
controller : 'FolderSettingsCtrl',
|
.when('/dashboards/folder/:folderId/:slug/settings', {
|
||||||
controllerAs: 'ctrl',
|
templateUrl:
|
||||||
})
|
'public/app/features/dashboard/partials/folder_settings.html',
|
||||||
.when('/dashboards/folder/:folderId/:slug', {
|
controller: 'FolderSettingsCtrl',
|
||||||
templateUrl: 'public/app/features/dashboard/partials/folder_dashboards.html',
|
controllerAs: 'ctrl',
|
||||||
controller : 'FolderDashboardsCtrl',
|
})
|
||||||
controllerAs: 'ctrl',
|
.when('/dashboards/folder/:folderId/:slug', {
|
||||||
})
|
templateUrl:
|
||||||
.when('/org', {
|
'public/app/features/dashboard/partials/folder_dashboards.html',
|
||||||
templateUrl: 'public/app/features/org/partials/orgDetails.html',
|
controller: 'FolderDashboardsCtrl',
|
||||||
controller : 'OrgDetailsCtrl',
|
controllerAs: 'ctrl',
|
||||||
resolve: loadOrgBundle,
|
})
|
||||||
})
|
.when('/org', {
|
||||||
.when('/org/new', {
|
templateUrl: 'public/app/features/org/partials/orgDetails.html',
|
||||||
templateUrl: 'public/app/features/org/partials/newOrg.html',
|
controller: 'OrgDetailsCtrl',
|
||||||
controller : 'NewOrgCtrl',
|
resolve: loadOrgBundle,
|
||||||
resolve: loadOrgBundle,
|
})
|
||||||
})
|
.when('/org/new', {
|
||||||
.when('/org/users', {
|
templateUrl: 'public/app/features/org/partials/newOrg.html',
|
||||||
templateUrl: 'public/app/features/org/partials/orgUsers.html',
|
controller: 'NewOrgCtrl',
|
||||||
controller : 'OrgUsersCtrl',
|
resolve: loadOrgBundle,
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
resolve: loadOrgBundle,
|
.when('/org/users', {
|
||||||
})
|
templateUrl: 'public/app/features/org/partials/orgUsers.html',
|
||||||
.when('/org/users/invite', {
|
controller: 'OrgUsersCtrl',
|
||||||
templateUrl: 'public/app/features/org/partials/invite.html',
|
controllerAs: 'ctrl',
|
||||||
controller : 'UserInviteCtrl',
|
resolve: loadOrgBundle,
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
resolve: loadOrgBundle,
|
.when('/org/users/invite', {
|
||||||
})
|
templateUrl: 'public/app/features/org/partials/invite.html',
|
||||||
.when('/org/apikeys', {
|
controller: 'UserInviteCtrl',
|
||||||
templateUrl: 'public/app/features/org/partials/orgApiKeys.html',
|
controllerAs: 'ctrl',
|
||||||
controller : 'OrgApiKeysCtrl',
|
resolve: loadOrgBundle,
|
||||||
resolve: loadOrgBundle,
|
})
|
||||||
})
|
.when('/org/apikeys', {
|
||||||
.when('/org/teams', {
|
templateUrl: 'public/app/features/org/partials/orgApiKeys.html',
|
||||||
templateUrl: 'public/app/features/org/partials/teams.html',
|
controller: 'OrgApiKeysCtrl',
|
||||||
controller : 'TeamsCtrl',
|
resolve: loadOrgBundle,
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
resolve: loadOrgBundle,
|
.when('/org/teams', {
|
||||||
})
|
templateUrl: 'public/app/features/org/partials/teams.html',
|
||||||
.when('/org/teams/edit/:id', {
|
controller: 'TeamsCtrl',
|
||||||
templateUrl: 'public/app/features/org/partials/team_details.html',
|
controllerAs: 'ctrl',
|
||||||
controller : 'TeamDetailsCtrl',
|
resolve: loadOrgBundle,
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
resolve: loadOrgBundle,
|
.when('/org/teams/edit/:id', {
|
||||||
})
|
templateUrl: 'public/app/features/org/partials/team_details.html',
|
||||||
.when('/profile', {
|
controller: 'TeamDetailsCtrl',
|
||||||
templateUrl: 'public/app/features/org/partials/profile.html',
|
controllerAs: 'ctrl',
|
||||||
controller : 'ProfileCtrl',
|
resolve: loadOrgBundle,
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
resolve: loadOrgBundle,
|
.when('/profile', {
|
||||||
})
|
templateUrl: 'public/app/features/org/partials/profile.html',
|
||||||
.when('/profile/password', {
|
controller: 'ProfileCtrl',
|
||||||
templateUrl: 'public/app/features/org/partials/change_password.html',
|
controllerAs: 'ctrl',
|
||||||
controller : 'ChangePasswordCtrl',
|
resolve: loadOrgBundle,
|
||||||
resolve: loadOrgBundle,
|
})
|
||||||
})
|
.when('/profile/password', {
|
||||||
.when('/profile/select-org', {
|
templateUrl: 'public/app/features/org/partials/change_password.html',
|
||||||
templateUrl: 'public/app/features/org/partials/select_org.html',
|
controller: 'ChangePasswordCtrl',
|
||||||
controller : 'SelectOrgCtrl',
|
resolve: loadOrgBundle,
|
||||||
resolve: loadOrgBundle,
|
})
|
||||||
})
|
.when('/profile/select-org', {
|
||||||
// ADMIN
|
templateUrl: 'public/app/features/org/partials/select_org.html',
|
||||||
.when('/admin', {
|
controller: 'SelectOrgCtrl',
|
||||||
templateUrl: 'public/app/features/admin/partials/admin_home.html',
|
resolve: loadOrgBundle,
|
||||||
controller : 'AdminHomeCtrl',
|
})
|
||||||
controllerAs: 'ctrl',
|
// ADMIN
|
||||||
resolve: loadAdminBundle,
|
.when('/admin', {
|
||||||
})
|
templateUrl: 'public/app/features/admin/partials/admin_home.html',
|
||||||
.when('/admin/settings', {
|
controller: 'AdminHomeCtrl',
|
||||||
templateUrl: 'public/app/features/admin/partials/settings.html',
|
controllerAs: 'ctrl',
|
||||||
controller : 'AdminSettingsCtrl',
|
resolve: loadAdminBundle,
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
resolve: loadAdminBundle,
|
.when('/admin/settings', {
|
||||||
})
|
templateUrl: 'public/app/features/admin/partials/settings.html',
|
||||||
.when('/admin/users', {
|
controller: 'AdminSettingsCtrl',
|
||||||
templateUrl: 'public/app/features/admin/partials/users.html',
|
controllerAs: 'ctrl',
|
||||||
controller : 'AdminListUsersCtrl',
|
resolve: loadAdminBundle,
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
resolve: loadAdminBundle,
|
.when('/admin/users', {
|
||||||
})
|
templateUrl: 'public/app/features/admin/partials/users.html',
|
||||||
.when('/admin/users/create', {
|
controller: 'AdminListUsersCtrl',
|
||||||
templateUrl: 'public/app/features/admin/partials/new_user.html',
|
controllerAs: 'ctrl',
|
||||||
controller : 'AdminEditUserCtrl',
|
resolve: loadAdminBundle,
|
||||||
resolve: loadAdminBundle,
|
})
|
||||||
})
|
.when('/admin/users/create', {
|
||||||
.when('/admin/users/edit/:id', {
|
templateUrl: 'public/app/features/admin/partials/new_user.html',
|
||||||
templateUrl: 'public/app/features/admin/partials/edit_user.html',
|
controller: 'AdminEditUserCtrl',
|
||||||
controller : 'AdminEditUserCtrl',
|
resolve: loadAdminBundle,
|
||||||
resolve: loadAdminBundle,
|
})
|
||||||
})
|
.when('/admin/users/edit/:id', {
|
||||||
.when('/admin/orgs', {
|
templateUrl: 'public/app/features/admin/partials/edit_user.html',
|
||||||
templateUrl: 'public/app/features/admin/partials/orgs.html',
|
controller: 'AdminEditUserCtrl',
|
||||||
controller : 'AdminListOrgsCtrl',
|
resolve: loadAdminBundle,
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
resolve: loadAdminBundle,
|
.when('/admin/orgs', {
|
||||||
})
|
templateUrl: 'public/app/features/admin/partials/orgs.html',
|
||||||
.when('/admin/orgs/edit/:id', {
|
controller: 'AdminListOrgsCtrl',
|
||||||
templateUrl: 'public/app/features/admin/partials/edit_org.html',
|
controllerAs: 'ctrl',
|
||||||
controller : 'AdminEditOrgCtrl',
|
resolve: loadAdminBundle,
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
resolve: loadAdminBundle,
|
.when('/admin/orgs/edit/:id', {
|
||||||
})
|
templateUrl: 'public/app/features/admin/partials/edit_org.html',
|
||||||
.when('/admin/stats', {
|
controller: 'AdminEditOrgCtrl',
|
||||||
templateUrl: 'public/app/features/admin/partials/stats.html',
|
controllerAs: 'ctrl',
|
||||||
controller : 'AdminStatsCtrl',
|
resolve: loadAdminBundle,
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
resolve: loadAdminBundle,
|
.when('/admin/stats', {
|
||||||
})
|
templateUrl: 'public/app/features/admin/partials/stats.html',
|
||||||
// LOGIN / SIGNUP
|
controller: 'AdminStatsCtrl',
|
||||||
.when('/login', {
|
controllerAs: 'ctrl',
|
||||||
templateUrl: 'public/app/partials/login.html',
|
resolve: loadAdminBundle,
|
||||||
controller : 'LoginCtrl',
|
})
|
||||||
pageClass: 'login-page sidemenu-hidden',
|
// LOGIN / SIGNUP
|
||||||
})
|
.when('/login', {
|
||||||
.when('/invite/:code', {
|
templateUrl: 'public/app/partials/login.html',
|
||||||
templateUrl: 'public/app/partials/signup_invited.html',
|
controller: 'LoginCtrl',
|
||||||
controller : 'InvitedCtrl',
|
pageClass: 'login-page sidemenu-hidden',
|
||||||
pageClass: 'sidemenu-hidden',
|
})
|
||||||
})
|
.when('/invite/:code', {
|
||||||
.when('/signup', {
|
templateUrl: 'public/app/partials/signup_invited.html',
|
||||||
templateUrl: 'public/app/partials/signup_step2.html',
|
controller: 'InvitedCtrl',
|
||||||
controller : 'SignUpCtrl',
|
pageClass: 'sidemenu-hidden',
|
||||||
pageClass: 'sidemenu-hidden',
|
})
|
||||||
})
|
.when('/signup', {
|
||||||
.when('/user/password/send-reset-email', {
|
templateUrl: 'public/app/partials/signup_step2.html',
|
||||||
templateUrl: 'public/app/partials/reset_password.html',
|
controller: 'SignUpCtrl',
|
||||||
controller : 'ResetPasswordCtrl',
|
pageClass: 'sidemenu-hidden',
|
||||||
pageClass: 'sidemenu-hidden',
|
})
|
||||||
})
|
.when('/user/password/send-reset-email', {
|
||||||
.when('/user/password/reset', {
|
templateUrl: 'public/app/partials/reset_password.html',
|
||||||
templateUrl: 'public/app/partials/reset_password.html',
|
controller: 'ResetPasswordCtrl',
|
||||||
controller : 'ResetPasswordCtrl',
|
pageClass: 'sidemenu-hidden',
|
||||||
pageClass: 'sidemenu-hidden',
|
})
|
||||||
})
|
.when('/user/password/reset', {
|
||||||
.when('/dashboard/snapshots', {
|
templateUrl: 'public/app/partials/reset_password.html',
|
||||||
templateUrl: 'public/app/features/snapshot/partials/snapshots.html',
|
controller: 'ResetPasswordCtrl',
|
||||||
controller : 'SnapshotsCtrl',
|
pageClass: 'sidemenu-hidden',
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
})
|
.when('/dashboard/snapshots', {
|
||||||
.when('/plugins', {
|
templateUrl: 'public/app/features/snapshot/partials/snapshots.html',
|
||||||
templateUrl: 'public/app/features/plugins/partials/plugin_list.html',
|
controller: 'SnapshotsCtrl',
|
||||||
controller: 'PluginListCtrl',
|
controllerAs: 'ctrl',
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
})
|
.when('/plugins', {
|
||||||
.when('/plugins/:pluginId/edit', {
|
templateUrl: 'public/app/features/plugins/partials/plugin_list.html',
|
||||||
templateUrl: 'public/app/features/plugins/partials/plugin_edit.html',
|
controller: 'PluginListCtrl',
|
||||||
controller: 'PluginEditCtrl',
|
controllerAs: 'ctrl',
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
})
|
.when('/plugins/:pluginId/edit', {
|
||||||
.when('/plugins/:pluginId/page/:slug', {
|
templateUrl: 'public/app/features/plugins/partials/plugin_edit.html',
|
||||||
templateUrl: 'public/app/features/plugins/partials/plugin_page.html',
|
controller: 'PluginEditCtrl',
|
||||||
controller: 'AppPageCtrl',
|
controllerAs: 'ctrl',
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
})
|
.when('/plugins/:pluginId/page/:slug', {
|
||||||
.when('/styleguide/:page?', {
|
templateUrl: 'public/app/features/plugins/partials/plugin_page.html',
|
||||||
controller: 'StyleGuideCtrl',
|
controller: 'AppPageCtrl',
|
||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
templateUrl: 'public/app/features/styleguide/styleguide.html',
|
})
|
||||||
})
|
.when('/styleguide/:page?', {
|
||||||
.when('/alerting', {
|
controller: 'StyleGuideCtrl',
|
||||||
redirectTo: '/alerting/list'
|
controllerAs: 'ctrl',
|
||||||
})
|
templateUrl: 'public/app/features/styleguide/styleguide.html',
|
||||||
.when('/alerting/list', {
|
})
|
||||||
templateUrl: 'public/app/features/alerting/partials/alert_list.html',
|
.when('/alerting', {
|
||||||
controller: 'AlertListCtrl',
|
redirectTo: '/alerting/list',
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
resolve: loadAlertingBundle,
|
.when('/alerting/list', {
|
||||||
})
|
templateUrl: 'public/app/features/alerting/partials/alert_list.html',
|
||||||
.when('/alerting/notifications', {
|
controller: 'AlertListCtrl',
|
||||||
templateUrl: 'public/app/features/alerting/partials/notifications_list.html',
|
controllerAs: 'ctrl',
|
||||||
controller: 'AlertNotificationsListCtrl',
|
resolve: loadAlertingBundle,
|
||||||
controllerAs: 'ctrl',
|
})
|
||||||
resolve: loadAlertingBundle,
|
.when('/alerting/notifications', {
|
||||||
})
|
templateUrl:
|
||||||
.when('/alerting/notification/new', {
|
'public/app/features/alerting/partials/notifications_list.html',
|
||||||
templateUrl: 'public/app/features/alerting/partials/notification_edit.html',
|
controller: 'AlertNotificationsListCtrl',
|
||||||
controller: 'AlertNotificationEditCtrl',
|
controllerAs: 'ctrl',
|
||||||
controllerAs: 'ctrl',
|
resolve: loadAlertingBundle,
|
||||||
resolve: loadAlertingBundle,
|
})
|
||||||
})
|
.when('/alerting/notification/new', {
|
||||||
.when('/alerting/notification/:id/edit', {
|
templateUrl:
|
||||||
templateUrl: 'public/app/features/alerting/partials/notification_edit.html',
|
'public/app/features/alerting/partials/notification_edit.html',
|
||||||
controller: 'AlertNotificationEditCtrl',
|
controller: 'AlertNotificationEditCtrl',
|
||||||
controllerAs: 'ctrl',
|
controllerAs: 'ctrl',
|
||||||
resolve: loadAlertingBundle,
|
resolve: loadAlertingBundle,
|
||||||
})
|
})
|
||||||
.otherwise({
|
.when('/alerting/notification/:id/edit', {
|
||||||
templateUrl: 'public/app/partials/error.html',
|
templateUrl:
|
||||||
controller: 'ErrorCtrl'
|
'public/app/features/alerting/partials/notification_edit.html',
|
||||||
});
|
controller: 'AlertNotificationEditCtrl',
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
resolve: loadAlertingBundle,
|
||||||
|
})
|
||||||
|
.otherwise({
|
||||||
|
templateUrl: 'public/app/partials/error.html',
|
||||||
|
controller: 'ErrorCtrl',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
coreModule.config(setupAngularRoutes);
|
coreModule.config(setupAngularRoutes);
|
||||||
|
|||||||
@@ -14,29 +14,50 @@ export class AlertSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.$rootScope.onAppEvent('alert-error', (e, alert) => {
|
this.$rootScope.onAppEvent(
|
||||||
this.set(alert[0], alert[1], 'error', 12000);
|
'alert-error',
|
||||||
}, this.$rootScope);
|
(e, alert) => {
|
||||||
|
this.set(alert[0], alert[1], 'error', 12000);
|
||||||
|
},
|
||||||
|
this.$rootScope
|
||||||
|
);
|
||||||
|
|
||||||
this.$rootScope.onAppEvent('alert-warning', (e, alert) => {
|
this.$rootScope.onAppEvent(
|
||||||
this.set(alert[0], alert[1], 'warning', 5000);
|
'alert-warning',
|
||||||
}, this.$rootScope);
|
(e, alert) => {
|
||||||
|
this.set(alert[0], alert[1], 'warning', 5000);
|
||||||
|
},
|
||||||
|
this.$rootScope
|
||||||
|
);
|
||||||
|
|
||||||
this.$rootScope.onAppEvent('alert-success', (e, alert) => {
|
this.$rootScope.onAppEvent(
|
||||||
this.set(alert[0], alert[1], 'success', 3000);
|
'alert-success',
|
||||||
}, this.$rootScope);
|
(e, alert) => {
|
||||||
|
this.set(alert[0], alert[1], 'success', 3000);
|
||||||
|
},
|
||||||
|
this.$rootScope
|
||||||
|
);
|
||||||
|
|
||||||
appEvents.on('alert-warning', options => this.set(options[0], options[1], 'warning', 5000));
|
appEvents.on('alert-warning', options =>
|
||||||
appEvents.on('alert-success', options => this.set(options[0], options[1], 'success', 3000));
|
this.set(options[0], options[1], 'warning', 5000)
|
||||||
appEvents.on('alert-error', options => this.set(options[0], options[1], 'error', 7000));
|
);
|
||||||
|
appEvents.on('alert-success', options =>
|
||||||
|
this.set(options[0], options[1], 'success', 3000)
|
||||||
|
);
|
||||||
|
appEvents.on('alert-error', options =>
|
||||||
|
this.set(options[0], options[1], 'error', 7000)
|
||||||
|
);
|
||||||
appEvents.on('confirm-modal', this.showConfirmModal.bind(this));
|
appEvents.on('confirm-modal', this.showConfirmModal.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
getIconForSeverity(severity) {
|
getIconForSeverity(severity) {
|
||||||
switch (severity) {
|
switch (severity) {
|
||||||
case 'success': return 'fa fa-check';
|
case 'success':
|
||||||
case 'error': return 'fa fa-exclamation-triangle';
|
return 'fa fa-check';
|
||||||
default: return 'fa fa-exclamation';
|
case 'error':
|
||||||
|
return 'fa fa-exclamation-triangle';
|
||||||
|
default:
|
||||||
|
return 'fa fa-exclamation';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +73,7 @@ export class AlertSrv {
|
|||||||
title: title || '',
|
title: title || '',
|
||||||
text: text || '',
|
text: text || '',
|
||||||
severity: severity || 'info',
|
severity: severity || 'info',
|
||||||
icon: this.getIconForSeverity(severity)
|
icon: this.getIconForSeverity(severity),
|
||||||
};
|
};
|
||||||
|
|
||||||
var newAlertJson = angular.toJson(newAlert);
|
var newAlertJson = angular.toJson(newAlert);
|
||||||
@@ -73,7 +94,7 @@ export class AlertSrv {
|
|||||||
this.$rootScope.$digest();
|
this.$rootScope.$digest();
|
||||||
}
|
}
|
||||||
|
|
||||||
return(newAlert);
|
return newAlert;
|
||||||
}
|
}
|
||||||
|
|
||||||
clear(alert) {
|
clear(alert) {
|
||||||
@@ -93,7 +114,8 @@ export class AlertSrv {
|
|||||||
};
|
};
|
||||||
|
|
||||||
scope.updateConfirmText = function(value) {
|
scope.updateConfirmText = function(value) {
|
||||||
scope.confirmTextValid = payload.confirmText.toLowerCase() === value.toLowerCase();
|
scope.confirmTextValid =
|
||||||
|
payload.confirmText.toLowerCase() === value.toLowerCase();
|
||||||
};
|
};
|
||||||
|
|
||||||
scope.title = payload.title;
|
scope.title = payload.title;
|
||||||
@@ -104,9 +126,9 @@ export class AlertSrv {
|
|||||||
scope.onConfirm = payload.onConfirm;
|
scope.onConfirm = payload.onConfirm;
|
||||||
scope.onAltAction = payload.onAltAction;
|
scope.onAltAction = payload.onAltAction;
|
||||||
scope.altActionText = payload.altActionText;
|
scope.altActionText = payload.altActionText;
|
||||||
scope.icon = payload.icon || "fa-check";
|
scope.icon = payload.icon || 'fa-check';
|
||||||
scope.yesText = payload.yesText || "Yes";
|
scope.yesText = payload.yesText || 'Yes';
|
||||||
scope.noText = payload.noText || "Cancel";
|
scope.noText = payload.noText || 'Cancel';
|
||||||
scope.confirmTextValid = scope.confirmText ? false : true;
|
scope.confirmTextValid = scope.confirmText ? false : true;
|
||||||
|
|
||||||
var confirmModal = this.$modal({
|
var confirmModal = this.$modal({
|
||||||
@@ -115,7 +137,7 @@ export class AlertSrv {
|
|||||||
modalClass: 'confirm-modal',
|
modalClass: 'confirm-modal',
|
||||||
show: false,
|
show: false,
|
||||||
scope: scope,
|
scope: scope,
|
||||||
keyboard: false
|
keyboard: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
confirmModal.then(function(modalEl) {
|
confirmModal.then(function(modalEl) {
|
||||||
|
|||||||
@@ -3,14 +3,17 @@ import coreModule from 'app/core/core_module';
|
|||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
|
|
||||||
export class Analytics {
|
export class Analytics {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $rootScope, private $location) {
|
constructor(private $rootScope, private $location) {}
|
||||||
}
|
|
||||||
|
|
||||||
gaInit() {
|
gaInit() {
|
||||||
$.getScript('https://www.google-analytics.com/analytics.js'); // jQuery shortcut
|
$.getScript('https://www.google-analytics.com/analytics.js'); // jQuery shortcut
|
||||||
var ga = (<any>window).ga = (<any>window).ga || function () { (ga.q = ga.q || []).push(arguments); }; ga.l = +new Date;
|
var ga = ((<any>window).ga =
|
||||||
|
(<any>window).ga ||
|
||||||
|
function() {
|
||||||
|
(ga.q = ga.q || []).push(arguments);
|
||||||
|
});
|
||||||
|
ga.l = +new Date();
|
||||||
ga('create', (<any>config).googleAnalyticsId, 'auto');
|
ga('create', (<any>config).googleAnalyticsId, 'auto');
|
||||||
return ga;
|
return ga;
|
||||||
}
|
}
|
||||||
@@ -33,4 +36,3 @@ function startAnalytics(googleAnalyticsSrv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
coreModule.service('googleAnalyticsSrv', Analytics).run(startAnalytics);
|
coreModule.service('googleAnalyticsSrv', Analytics).run(startAnalytics);
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,13 @@ export class BackendSrv {
|
|||||||
private noBackendCache: boolean;
|
private noBackendCache: boolean;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $http, private alertSrv, private $q, private $timeout, private contextSrv) {
|
constructor(
|
||||||
}
|
private $http,
|
||||||
|
private alertSrv,
|
||||||
|
private $q,
|
||||||
|
private $timeout,
|
||||||
|
private contextSrv
|
||||||
|
) {}
|
||||||
|
|
||||||
get(url, params?) {
|
get(url, params?) {
|
||||||
return this.request({ method: 'GET', url: url, params: params });
|
return this.request({ method: 'GET', url: url, params: params });
|
||||||
@@ -52,22 +57,22 @@ export class BackendSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (err.status === 422) {
|
if (err.status === 422) {
|
||||||
this.alertSrv.set("Validation failed", data.message, "warning", 4000);
|
this.alertSrv.set('Validation failed', data.message, 'warning', 4000);
|
||||||
throw data;
|
throw data;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.severity = 'error';
|
data.severity = 'error';
|
||||||
|
|
||||||
if (err.status < 500) {
|
if (err.status < 500) {
|
||||||
data.severity = "warning";
|
data.severity = 'warning';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.message) {
|
if (data.message) {
|
||||||
let description = "";
|
let description = '';
|
||||||
let message = data.message;
|
let message = data.message;
|
||||||
if (message.length > 80) {
|
if (message.length > 80) {
|
||||||
description = message;
|
description = message;
|
||||||
message = "Error";
|
message = 'Error';
|
||||||
}
|
}
|
||||||
this.alertSrv.set(message, description, data.severity, 10000);
|
this.alertSrv.set(message, description, data.severity, 10000);
|
||||||
}
|
}
|
||||||
@@ -86,32 +91,39 @@ export class BackendSrv {
|
|||||||
options.headers['X-Grafana-Org-Id'] = this.contextSrv.user.orgId;
|
options.headers['X-Grafana-Org-Id'] = this.contextSrv.user.orgId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.url.indexOf("/") === 0) {
|
if (options.url.indexOf('/') === 0) {
|
||||||
options.url = options.url.substring(1);
|
options.url = options.url.substring(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.$http(options).then(results => {
|
return this.$http(options).then(
|
||||||
if (options.method !== 'GET') {
|
results => {
|
||||||
if (results && results.data.message) {
|
if (options.method !== 'GET') {
|
||||||
if (options.showSuccessAlert !== false) {
|
if (results && results.data.message) {
|
||||||
this.alertSrv.set(results.data.message, '', 'success', 3000);
|
if (options.showSuccessAlert !== false) {
|
||||||
|
this.alertSrv.set(results.data.message, '', 'success', 3000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return results.data;
|
||||||
return results.data;
|
},
|
||||||
}, err => {
|
err => {
|
||||||
// handle unauthorized
|
// handle unauthorized
|
||||||
if (err.status === 401 && this.contextSrv.user.isSignedIn && firstAttempt) {
|
if (
|
||||||
return this.loginPing().then(() => {
|
err.status === 401 &&
|
||||||
options.retry = 1;
|
this.contextSrv.user.isSignedIn &&
|
||||||
return this.request(options);
|
firstAttempt
|
||||||
});
|
) {
|
||||||
}
|
return this.loginPing().then(() => {
|
||||||
|
options.retry = 1;
|
||||||
|
return this.request(options);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.$timeout(this.requestErrorHandler.bind(this, err), 50);
|
this.$timeout(this.requestErrorHandler.bind(this, err), 50);
|
||||||
throw err;
|
throw err;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
addCanceler(requestId, canceler) {
|
addCanceler(requestId, canceler) {
|
||||||
@@ -154,7 +166,7 @@ export class BackendSrv {
|
|||||||
options.headers['X-Grafana-Org-Id'] = this.contextSrv.user.orgId;
|
options.headers['X-Grafana-Org-Id'] = this.contextSrv.user.orgId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.url.indexOf("/") === 0) {
|
if (options.url.indexOf('/') === 0) {
|
||||||
options.url = options.url.substring(1);
|
options.url = options.url.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,51 +180,53 @@ export class BackendSrv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.$http(options).then(response => {
|
return this.$http(options)
|
||||||
appEvents.emit('ds-request-response', response);
|
.then(response => {
|
||||||
return response;
|
appEvents.emit('ds-request-response', response);
|
||||||
}).catch(err => {
|
return response;
|
||||||
if (err.status === this.HTTP_REQUEST_CANCELLED) {
|
})
|
||||||
throw {err, cancelled: true};
|
.catch(err => {
|
||||||
}
|
if (err.status === this.HTTP_REQUEST_CANCELLED) {
|
||||||
|
throw { err, cancelled: true };
|
||||||
|
}
|
||||||
|
|
||||||
// handle unauthorized for backend requests
|
// handle unauthorized for backend requests
|
||||||
if (requestIsLocal && firstAttempt && err.status === 401) {
|
if (requestIsLocal && firstAttempt && err.status === 401) {
|
||||||
return this.loginPing().then(() => {
|
return this.loginPing().then(() => {
|
||||||
options.retry = 1;
|
options.retry = 1;
|
||||||
if (canceler) {
|
if (canceler) {
|
||||||
canceler.resolve();
|
canceler.resolve();
|
||||||
}
|
}
|
||||||
return this.datasourceRequest(options);
|
return this.datasourceRequest(options);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// populate error obj on Internal Error
|
// populate error obj on Internal Error
|
||||||
if (_.isString(err.data) && err.status === 500) {
|
if (_.isString(err.data) && err.status === 500) {
|
||||||
err.data = {
|
err.data = {
|
||||||
error: err.statusText,
|
error: err.statusText,
|
||||||
response: err.data,
|
response: err.data,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// for Prometheus
|
// for Prometheus
|
||||||
if (err.data && !err.data.message && _.isString(err.data.error)) {
|
if (err.data && !err.data.message && _.isString(err.data.error)) {
|
||||||
err.data.message = err.data.error;
|
err.data.message = err.data.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
appEvents.emit('ds-request-error', err);
|
appEvents.emit('ds-request-error', err);
|
||||||
throw err;
|
throw err;
|
||||||
|
})
|
||||||
}).finally(() => {
|
.finally(() => {
|
||||||
// clean up
|
// clean up
|
||||||
if (options.requestId) {
|
if (options.requestId) {
|
||||||
this.inFlightRequests[options.requestId].shift();
|
this.inFlightRequests[options.requestId].shift();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loginPing() {
|
loginPing() {
|
||||||
return this.request({url: '/api/login/ping', method: 'GET', retry: 1 });
|
return this.request({ url: '/api/login/ping', method: 'GET', retry: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
search(query) {
|
search(query) {
|
||||||
@@ -224,7 +238,7 @@ export class BackendSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveDashboard(dash, options) {
|
saveDashboard(dash, options) {
|
||||||
options = (options || {});
|
options = options || {};
|
||||||
|
|
||||||
return this.post('/api/dashboards/db/', {
|
return this.post('/api/dashboards/db/', {
|
||||||
dashboard: dash,
|
dashboard: dash,
|
||||||
@@ -237,13 +251,16 @@ export class BackendSrv {
|
|||||||
createDashboardFolder(name) {
|
createDashboardFolder(name) {
|
||||||
const dash = {
|
const dash = {
|
||||||
schemaVersion: 16,
|
schemaVersion: 16,
|
||||||
title: name,
|
title: name.trim(),
|
||||||
editable: true,
|
editable: true,
|
||||||
panels: []
|
panels: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.post('/api/dashboards/db/', {dashboard: dash, isFolder: true, overwrite: false})
|
return this.post('/api/dashboards/db/', {
|
||||||
.then(res => {
|
dashboard: dash,
|
||||||
|
isFolder: true,
|
||||||
|
overwrite: false,
|
||||||
|
}).then(res => {
|
||||||
return this.getDashboard('db', res.slug);
|
return this.getDashboard('db', res.slug);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -251,15 +268,15 @@ export class BackendSrv {
|
|||||||
deleteDashboard(slug) {
|
deleteDashboard(slug) {
|
||||||
let deferred = this.$q.defer();
|
let deferred = this.$q.defer();
|
||||||
|
|
||||||
this.getDashboard('db', slug)
|
this.getDashboard('db', slug).then(fullDash => {
|
||||||
.then(fullDash => {
|
this.delete(`/api/dashboards/db/${slug}`)
|
||||||
this.delete(`/api/dashboards/db/${slug}`)
|
.then(() => {
|
||||||
.then(() => {
|
deferred.resolve(fullDash);
|
||||||
deferred.resolve(fullDash);
|
})
|
||||||
}).catch(err => {
|
.catch(err => {
|
||||||
deferred.reject(err);
|
deferred.reject(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
@@ -278,17 +295,19 @@ export class BackendSrv {
|
|||||||
const tasks = [];
|
const tasks = [];
|
||||||
|
|
||||||
for (let slug of dashboardSlugs) {
|
for (let slug of dashboardSlugs) {
|
||||||
tasks.push(this.createTask(this.moveDashboard.bind(this), true, slug, toFolder));
|
tasks.push(
|
||||||
|
this.createTask(this.moveDashboard.bind(this), true, slug, toFolder)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.executeInOrder(tasks, [])
|
return this.executeInOrder(tasks, []).then(result => {
|
||||||
.then(result => {
|
return {
|
||||||
return {
|
totalCount: result.length,
|
||||||
totalCount: result.length,
|
successCount: _.filter(result, { succeeded: true }).length,
|
||||||
successCount: _.filter(result, { succeeded: true }).length,
|
alreadyInFolderCount: _.filter(result, { alreadyInFolder: true })
|
||||||
alreadyInFolderCount: _.filter(result, { alreadyInFolder: true }).length
|
.length,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private moveDashboard(slug, toFolder) {
|
private moveDashboard(slug, toFolder) {
|
||||||
@@ -297,9 +316,11 @@ export class BackendSrv {
|
|||||||
this.getDashboard('db', slug).then(fullDash => {
|
this.getDashboard('db', slug).then(fullDash => {
|
||||||
const model = new DashboardModel(fullDash.dashboard, fullDash.meta);
|
const model = new DashboardModel(fullDash.dashboard, fullDash.meta);
|
||||||
|
|
||||||
if ((!fullDash.meta.folderId && toFolder.id === 0) ||
|
if (
|
||||||
fullDash.meta.folderId === toFolder.id) {
|
(!fullDash.meta.folderId && toFolder.id === 0) ||
|
||||||
deferred.resolve({alreadyInFolder: true});
|
fullDash.meta.folderId === toFolder.id
|
||||||
|
) {
|
||||||
|
deferred.resolve({ alreadyInFolder: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,19 +331,21 @@ export class BackendSrv {
|
|||||||
|
|
||||||
this.saveDashboard(clone, {})
|
this.saveDashboard(clone, {})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
deferred.resolve({succeeded: true});
|
deferred.resolve({ succeeded: true });
|
||||||
}).catch(err => {
|
})
|
||||||
if (err.data && err.data.status === "plugin-dashboard") {
|
.catch(err => {
|
||||||
|
if (err.data && err.data.status === 'plugin-dashboard') {
|
||||||
err.isHandled = true;
|
err.isHandled = true;
|
||||||
|
|
||||||
this.saveDashboard(clone, {overwrite: true})
|
this.saveDashboard(clone, { overwrite: true })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
deferred.resolve({succeeded: true});
|
deferred.resolve({ succeeded: true });
|
||||||
}).catch(err => {
|
})
|
||||||
deferred.resolve({succeeded: false});
|
.catch(err => {
|
||||||
|
deferred.resolve({ succeeded: false });
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
deferred.resolve({succeeded: false});
|
deferred.resolve({ succeeded: false });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -331,11 +354,13 @@ export class BackendSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createTask(fn, ignoreRejections, ...args: any[]) {
|
private createTask(fn, ignoreRejections, ...args: any[]) {
|
||||||
return (result) => {
|
return result => {
|
||||||
return fn.apply(null, args)
|
return fn
|
||||||
|
.apply(null, args)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
return Array.prototype.concat(result, [res]);
|
return Array.prototype.concat(result, [res]);
|
||||||
}).catch(err => {
|
})
|
||||||
|
.catch(err => {
|
||||||
if (ignoreRejections) {
|
if (ignoreRejections) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -350,5 +375,4 @@ export class BackendSrv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
coreModule.service('backendSrv', BackendSrv);
|
coreModule.service('backendSrv', BackendSrv);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class ContextSrv {
|
|||||||
config.buildInfo = {};
|
config.buildInfo = {};
|
||||||
}
|
}
|
||||||
if (!config.bootData) {
|
if (!config.bootData) {
|
||||||
config.bootData = {user: {}, settings: {}};
|
config.bootData = { user: {}, settings: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
this.version = config.buildInfo.version;
|
this.version = config.buildInfo.version;
|
||||||
@@ -51,7 +51,10 @@ export class ContextSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isGrafanaVisible() {
|
isGrafanaVisible() {
|
||||||
return !!(document.visibilityState === undefined || document.visibilityState === 'visible');
|
return !!(
|
||||||
|
document.visibilityState === undefined ||
|
||||||
|
document.visibilityState === 'visible'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleSideMenu() {
|
toggleSideMenu() {
|
||||||
@@ -61,7 +64,7 @@ export class ContextSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var contextSrv = new ContextSrv();
|
var contextSrv = new ContextSrv();
|
||||||
export {contextSrv};
|
export { contextSrv };
|
||||||
|
|
||||||
coreModule.factory('contextSrv', function() {
|
coreModule.factory('contextSrv', function() {
|
||||||
return contextSrv;
|
return contextSrv;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import angular from 'angular';
|
|||||||
import coreModule from '../core_module';
|
import coreModule from '../core_module';
|
||||||
|
|
||||||
class DynamicDirectiveSrv {
|
class DynamicDirectiveSrv {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $compile, private $rootScope) {}
|
constructor(private $compile, private $rootScope) {}
|
||||||
|
|
||||||
@@ -17,22 +16,31 @@ class DynamicDirectiveSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
link(scope, elem, attrs, options) {
|
link(scope, elem, attrs, options) {
|
||||||
options.directive(scope).then(directiveInfo => {
|
options
|
||||||
if (!directiveInfo || !directiveInfo.fn) {
|
.directive(scope)
|
||||||
elem.empty();
|
.then(directiveInfo => {
|
||||||
return;
|
if (!directiveInfo || !directiveInfo.fn) {
|
||||||
}
|
elem.empty();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!directiveInfo.fn.registered) {
|
if (!directiveInfo.fn.registered) {
|
||||||
coreModule.directive(attrs.$normalize(directiveInfo.name), directiveInfo.fn);
|
coreModule.directive(
|
||||||
directiveInfo.fn.registered = true;
|
attrs.$normalize(directiveInfo.name),
|
||||||
}
|
directiveInfo.fn
|
||||||
|
);
|
||||||
|
directiveInfo.fn.registered = true;
|
||||||
|
}
|
||||||
|
|
||||||
this.addDirective(elem, directiveInfo.name, scope);
|
this.addDirective(elem, directiveInfo.name, scope);
|
||||||
}).catch(err => {
|
})
|
||||||
console.log('Plugin load:', err);
|
.catch(err => {
|
||||||
this.$rootScope.appEvent('alert-error', ['Plugin error', err.toString()]);
|
console.log('Plugin load:', err);
|
||||||
});
|
this.$rootScope.appEvent('alert-error', [
|
||||||
|
'Plugin error',
|
||||||
|
err.toString(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
create(options) {
|
create(options) {
|
||||||
@@ -52,7 +60,7 @@ class DynamicDirectiveSrv {
|
|||||||
} else {
|
} else {
|
||||||
this.link(scope, elem, attrs, options);
|
this.link(scope, elem, attrs, options);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return directiveDef;
|
return directiveDef;
|
||||||
@@ -60,5 +68,3 @@ class DynamicDirectiveSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
coreModule.service('dynamicDirectiveSrv', DynamicDirectiveSrv);
|
coreModule.service('dynamicDirectiveSrv', DynamicDirectiveSrv);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,43 @@
|
|||||||
import coreModule from 'app/core/core_module';
|
import coreModule from 'app/core/core_module';
|
||||||
|
import config from 'app/core/config';
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
|
|
||||||
// This service is for registering global events.
|
// This service is for registering global events.
|
||||||
// Good for communication react > angular and vice verse
|
// Good for communication react > angular and vice verse
|
||||||
export class GlobalEventSrv {
|
export class GlobalEventSrv {
|
||||||
|
private appSubUrl;
|
||||||
|
private fullPageReloadRoutes;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $location, private $timeout) {
|
constructor(private $location, private $timeout, private $window) {
|
||||||
|
this.appSubUrl = config.appSubUrl;
|
||||||
|
this.fullPageReloadRoutes = ['/logout'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Angular's $location does not like <base href...> and absolute urls
|
||||||
|
stripBaseFromUrl(url = '') {
|
||||||
|
const appSubUrl = this.appSubUrl;
|
||||||
|
const stripExtraChars = appSubUrl.endsWith('/') ? 1 : 0;
|
||||||
|
const urlWithoutBase =
|
||||||
|
url.length > 0 && url.indexOf(appSubUrl) === 0
|
||||||
|
? url.slice(appSubUrl.length - stripExtraChars)
|
||||||
|
: url;
|
||||||
|
|
||||||
|
return urlWithoutBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
appEvents.on('location-change', payload => {
|
appEvents.on('location-change', payload => {
|
||||||
this.$timeout(() => { // A hack to use timeout when we're changing things (in this case the url) from outside of Angular.
|
const urlWithoutBase = this.stripBaseFromUrl(payload.href);
|
||||||
this.$location.path(payload.href);
|
if (this.fullPageReloadRoutes.indexOf(urlWithoutBase) > -1) {
|
||||||
});
|
this.$window.location.href = payload.href;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$timeout(() => {
|
||||||
|
// A hack to use timeout when we're changing things (in this case the url) from outside of Angular.
|
||||||
|
this.$location.url(urlWithoutBase);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export class ImpressionSrv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impressions = impressions.filter((imp) => {
|
impressions = impressions.filter(imp => {
|
||||||
return dashboardId !== imp;
|
return dashboardId !== imp;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ export class ImpressionSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDashboardOpened() {
|
getDashboardOpened() {
|
||||||
var impressions = store.get(this.impressionKey(config)) || "[]";
|
var impressions = store.get(this.impressionKey(config)) || '[]';
|
||||||
|
|
||||||
impressions = JSON.parse(impressions);
|
impressions = JSON.parse(impressions);
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ export class ImpressionSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impressionKey(config) {
|
impressionKey(config) {
|
||||||
return "dashboard_impressions-" + config.bootData.user.orgId;
|
return 'dashboard_impressions-' + config.bootData.user.orgId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,7 @@ export class KeybindingSrv {
|
|||||||
helpModal: boolean;
|
helpModal: boolean;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(
|
constructor(private $rootScope, private $location) {
|
||||||
private $rootScope,
|
|
||||||
private $location) {
|
|
||||||
|
|
||||||
// clear out all shortcuts on route change
|
// clear out all shortcuts on route change
|
||||||
$rootScope.$on('$routeChangeSuccess', () => {
|
$rootScope.$on('$routeChangeSuccess', () => {
|
||||||
Mousetrap.reset();
|
Mousetrap.reset();
|
||||||
@@ -26,54 +23,58 @@ export class KeybindingSrv {
|
|||||||
|
|
||||||
setupGlobal() {
|
setupGlobal() {
|
||||||
this.bind(['?', 'h'], this.showHelpModal);
|
this.bind(['?', 'h'], this.showHelpModal);
|
||||||
this.bind("g h", this.goToHome);
|
this.bind('g h', this.goToHome);
|
||||||
this.bind("g a", this.openAlerting);
|
this.bind('g a', this.openAlerting);
|
||||||
this.bind("g p", this.goToProfile);
|
this.bind('g p', this.goToProfile);
|
||||||
this.bind("s s", this.openSearchStarred);
|
this.bind('s s', this.openSearchStarred);
|
||||||
this.bind('s o', this.openSearch);
|
this.bind('s o', this.openSearch);
|
||||||
this.bind('s t', this.openSearchTags);
|
this.bind('s t', this.openSearchTags);
|
||||||
this.bind('f', this.openSearch);
|
this.bind('f', this.openSearch);
|
||||||
}
|
}
|
||||||
|
|
||||||
openSearchStarred() {
|
openSearchStarred() {
|
||||||
this.$rootScope.appEvent('show-dash-search', {starred: true});
|
appEvents.emit('show-dash-search', { starred: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
openSearchTags() {
|
openSearchTags() {
|
||||||
this.$rootScope.appEvent('show-dash-search', {tagsMode: true});
|
appEvents.emit('show-dash-search', { tagsMode: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
openSearch() {
|
openSearch() {
|
||||||
this.$rootScope.appEvent('show-dash-search');
|
appEvents.emit('show-dash-search');
|
||||||
}
|
}
|
||||||
|
|
||||||
openAlerting() {
|
openAlerting() {
|
||||||
this.$location.url("/alerting");
|
this.$location.url('/alerting');
|
||||||
}
|
}
|
||||||
|
|
||||||
goToHome() {
|
goToHome() {
|
||||||
this.$location.url("/");
|
this.$location.url('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
goToProfile() {
|
goToProfile() {
|
||||||
this.$location.url("/profile");
|
this.$location.url('/profile');
|
||||||
}
|
}
|
||||||
|
|
||||||
showHelpModal() {
|
showHelpModal() {
|
||||||
appEvents.emit('show-modal', {templateHtml: '<help-modal></help-modal>'});
|
appEvents.emit('show-modal', { templateHtml: '<help-modal></help-modal>' });
|
||||||
}
|
}
|
||||||
|
|
||||||
bind(keyArg, fn) {
|
bind(keyArg, fn) {
|
||||||
Mousetrap.bind(keyArg, evt => {
|
Mousetrap.bind(
|
||||||
evt.preventDefault();
|
keyArg,
|
||||||
evt.stopPropagation();
|
evt => {
|
||||||
evt.returnValue = false;
|
evt.preventDefault();
|
||||||
return this.$rootScope.$apply(fn.bind(this));
|
evt.stopPropagation();
|
||||||
}, 'keydown');
|
evt.returnValue = false;
|
||||||
|
return this.$rootScope.$apply(fn.bind(this));
|
||||||
|
},
|
||||||
|
'keydown'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
showDashEditView() {
|
showDashEditView() {
|
||||||
var search = _.extend(this.$location.search(), {editview: 'settings'});
|
var search = _.extend(this.$location.search(), { editview: 'settings' });
|
||||||
this.$location.search(search);
|
this.$location.search(search);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +112,7 @@ export class KeybindingSrv {
|
|||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
edit: true,
|
edit: true,
|
||||||
panelId: dashboard.meta.focusPanelId,
|
panelId: dashboard.meta.focusPanelId,
|
||||||
toggle: true
|
toggle: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -140,14 +141,14 @@ export class KeybindingSrv {
|
|||||||
// share panel
|
// share panel
|
||||||
this.bind('p s', () => {
|
this.bind('p s', () => {
|
||||||
if (dashboard.meta.focusPanelId) {
|
if (dashboard.meta.focusPanelId) {
|
||||||
var shareScope = scope.$new();
|
var shareScope = scope.$new();
|
||||||
var panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId);
|
var panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId);
|
||||||
shareScope.panel = panelInfo.panel;
|
shareScope.panel = panelInfo.panel;
|
||||||
shareScope.dashboard = dashboard;
|
shareScope.dashboard = dashboard;
|
||||||
|
|
||||||
appEvents.emit('show-modal', {
|
appEvents.emit('show-modal', {
|
||||||
src: 'public/app/features/dashboard/partials/shareModal.html',
|
src: 'public/app/features/dashboard/partials/shareModal.html',
|
||||||
scope: shareScope
|
scope: shareScope,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -185,7 +186,7 @@ export class KeybindingSrv {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.bind('d n', e => {
|
this.bind('d n', e => {
|
||||||
this.$location.url("/dashboard/new");
|
this.$location.url('/dashboard/new');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.bind('d r', () => {
|
this.bind('d r', () => {
|
||||||
@@ -211,7 +212,7 @@ export class KeybindingSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scope.appEvent('hide-modal');
|
scope.appEvent('hide-modal');
|
||||||
scope.appEvent('panel-change-view', {fullscreen: false, edit: false});
|
scope.appEvent('panel-change-view', { fullscreen: false, edit: false });
|
||||||
|
|
||||||
// close settings view
|
// close settings view
|
||||||
var search = this.$location.search();
|
var search = this.$location.search();
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// This is using ng-react with this PR applied https://github.com/ngReact/ngReact/pull/199
|
// This is using ng-react with this PR applied https://github.com/ngReact/ngReact/pull/199
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
// # ngReact
|
// # ngReact
|
||||||
// ### Use React Components inside of your Angular applications
|
// ### Use React Components inside of your Angular applications
|
||||||
//
|
//
|
||||||
@@ -85,24 +84,27 @@ function applyFunctions(obj, scope, propsConfig?) {
|
|||||||
var value = obj[key];
|
var value = obj[key];
|
||||||
var config = (propsConfig || {})[key] || {};
|
var config = (propsConfig || {})[key] || {};
|
||||||
/**
|
/**
|
||||||
* wrap functions in a function that ensures they are scope.$applied
|
* wrap functions in a function that ensures they are scope.$applied
|
||||||
* ensures that when function is called from a React component
|
* ensures that when function is called from a React component
|
||||||
* the Angular digest cycle is run
|
* the Angular digest cycle is run
|
||||||
*/
|
*/
|
||||||
prev[key] = angular.isFunction(value) && config.wrapApply !== false ? applied(value, scope) : value;
|
prev[key] =
|
||||||
|
angular.isFunction(value) && config.wrapApply !== false
|
||||||
|
? applied(value, scope)
|
||||||
|
: value;
|
||||||
|
|
||||||
return prev;
|
return prev;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param watchDepth (value of HTML watch-depth attribute)
|
* @param watchDepth (value of HTML watch-depth attribute)
|
||||||
* @param scope (angular scope)
|
* @param scope (angular scope)
|
||||||
*
|
*
|
||||||
* Uses the watchDepth attribute to determine how to watch props on scope.
|
* Uses the watchDepth attribute to determine how to watch props on scope.
|
||||||
* If watchDepth attribute is NOT reference or collection, watchDepth defaults to deep watching by value
|
* If watchDepth attribute is NOT reference or collection, watchDepth defaults to deep watching by value
|
||||||
*/
|
*/
|
||||||
function watchProps(watchDepth, scope, watchExpressions, listener) {
|
function watchProps(watchDepth, scope, watchExpressions, listener) {
|
||||||
var supportsWatchCollection = angular.isFunction(scope.$watchCollection);
|
var supportsWatchCollection = angular.isFunction(scope.$watchCollection);
|
||||||
var supportsWatchGroup = angular.isFunction(scope.$watchGroup);
|
var supportsWatchGroup = angular.isFunction(scope.$watchGroup);
|
||||||
@@ -165,7 +167,8 @@ function findAttribute(attrs, propName) {
|
|||||||
|
|
||||||
// get watch depth of prop (string or array)
|
// get watch depth of prop (string or array)
|
||||||
function getPropWatchDepth(defaultWatch, prop) {
|
function getPropWatchDepth(defaultWatch, prop) {
|
||||||
var customWatchDepth = Array.isArray(prop) && angular.isObject(prop[1]) && prop[1].watchDepth;
|
var customWatchDepth =
|
||||||
|
Array.isArray(prop) && angular.isObject(prop[1]) && prop[1].watchDepth;
|
||||||
return customWatchDepth || defaultWatch;
|
return customWatchDepth || defaultWatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +205,9 @@ var reactComponent = function($injector) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// If there are props, re-render when they change
|
// If there are props, re-render when they change
|
||||||
attrs.props ? watchProps(attrs.watchDepth, scope, [attrs.props], renderMyComponent) : renderMyComponent();
|
attrs.props
|
||||||
|
? watchProps(attrs.watchDepth, scope, [attrs.props], renderMyComponent)
|
||||||
|
: renderMyComponent();
|
||||||
|
|
||||||
// cleanup when scope is destroyed
|
// cleanup when scope is destroyed
|
||||||
scope.$on('$destroy', function() {
|
scope.$on('$destroy', function() {
|
||||||
@@ -210,7 +215,10 @@ var reactComponent = function($injector) {
|
|||||||
ReactDOM.unmountComponentAtNode(elem[0]);
|
ReactDOM.unmountComponentAtNode(elem[0]);
|
||||||
} else {
|
} else {
|
||||||
scope.$eval(attrs.onScopeDestroy, {
|
scope.$eval(attrs.onScopeDestroy, {
|
||||||
unmountComponent: ReactDOM.unmountComponentAtNode.bind(this, elem[0]),
|
unmountComponent: ReactDOM.unmountComponentAtNode.bind(
|
||||||
|
this,
|
||||||
|
elem[0]
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -274,11 +282,20 @@ var reactDirective = function($injector) {
|
|||||||
// watch each property name and trigger an update whenever something changes,
|
// watch each property name and trigger an update whenever something changes,
|
||||||
// to update scope.props with new values
|
// to update scope.props with new values
|
||||||
var propExpressions = props.map(function(prop) {
|
var propExpressions = props.map(function(prop) {
|
||||||
return Array.isArray(prop) ? [attrs[getPropName(prop)], getPropConfig(prop)] : attrs[prop];
|
return Array.isArray(prop)
|
||||||
|
? [attrs[getPropName(prop)], getPropConfig(prop)]
|
||||||
|
: attrs[prop];
|
||||||
});
|
});
|
||||||
|
|
||||||
// If we don't have any props, then our watch statement won't fire.
|
// If we don't have any props, then our watch statement won't fire.
|
||||||
props.length ? watchProps(attrs.watchDepth, scope, propExpressions, renderMyComponent) : renderMyComponent();
|
props.length
|
||||||
|
? watchProps(
|
||||||
|
attrs.watchDepth,
|
||||||
|
scope,
|
||||||
|
propExpressions,
|
||||||
|
renderMyComponent
|
||||||
|
)
|
||||||
|
: renderMyComponent();
|
||||||
|
|
||||||
// cleanup when scope is destroyed
|
// cleanup when scope is destroyed
|
||||||
scope.$on('$destroy', function() {
|
scope.$on('$destroy', function() {
|
||||||
@@ -286,7 +303,10 @@ var reactDirective = function($injector) {
|
|||||||
ReactDOM.unmountComponentAtNode(elem[0]);
|
ReactDOM.unmountComponentAtNode(elem[0]);
|
||||||
} else {
|
} else {
|
||||||
scope.$eval(attrs.onScopeDestroy, {
|
scope.$eval(attrs.onScopeDestroy, {
|
||||||
unmountComponent: ReactDOM.unmountComponentAtNode.bind(this, elem[0]),
|
unmountComponent: ReactDOM.unmountComponentAtNode.bind(
|
||||||
|
this,
|
||||||
|
elem[0]
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ function popoverSrv($compile, $rootScope, $timeout) {
|
|||||||
openOn: options.openOn,
|
openOn: options.openOn,
|
||||||
hoverCloseDelay: 200,
|
hoverCloseDelay: 200,
|
||||||
tetherOptions: {
|
tetherOptions: {
|
||||||
constraints: [{to: 'scrollParent', attachment: "none both"}]
|
constraints: [{ to: 'scrollParent', attachment: 'together' }],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
drop.on('close', () => {
|
drop.on('close', () => {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export class SearchSrv {
|
|||||||
return this.queryForRecentDashboards().then(result => {
|
return this.queryForRecentDashboards().then(result => {
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
sections['recent'] = {
|
sections['recent'] = {
|
||||||
title: 'Recent Boards',
|
title: 'Recent',
|
||||||
icon: 'fa fa-clock-o',
|
icon: 'fa fa-clock-o',
|
||||||
score: -1,
|
score: -1,
|
||||||
removable: true,
|
removable: true,
|
||||||
@@ -37,9 +37,11 @@ export class SearchSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this.backendSrv.search({ dashboardIds: dashIds }).then(result => {
|
return this.backendSrv.search({ dashboardIds: dashIds }).then(result => {
|
||||||
return dashIds.map(orderId => {
|
return dashIds
|
||||||
return _.find(result, { id: orderId });
|
.map(orderId => {
|
||||||
}).filter(hit => hit && !hit.isStarred)
|
return _.find(result, { id: orderId });
|
||||||
|
})
|
||||||
|
.filter(hit => hit && !hit.isStarred)
|
||||||
.map(hit => {
|
.map(hit => {
|
||||||
return this.transformToViewModel(hit);
|
return this.transformToViewModel(hit);
|
||||||
});
|
});
|
||||||
@@ -71,10 +73,10 @@ export class SearchSrv {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.backendSrv.search({starred: true, limit: 5}).then(result => {
|
return this.backendSrv.search({ starred: true, limit: 5 }).then(result => {
|
||||||
if (result.length > 0) {
|
if (result.length > 0) {
|
||||||
sections['starred'] = {
|
sections['starred'] = {
|
||||||
title: 'Starred Boards',
|
title: 'Starred',
|
||||||
icon: 'fa fa-star-o',
|
icon: 'fa fa-star-o',
|
||||||
score: -2,
|
score: -2,
|
||||||
expanded: this.starredIsOpen,
|
expanded: this.starredIsOpen,
|
||||||
@@ -94,8 +96,10 @@ export class SearchSrv {
|
|||||||
let sections: any = {};
|
let sections: any = {};
|
||||||
let promises = [];
|
let promises = [];
|
||||||
let query = _.clone(options);
|
let query = _.clone(options);
|
||||||
let hasFilters = options.query ||
|
let hasFilters =
|
||||||
(options.tag && options.tag.length > 0) || options.starred ||
|
options.query ||
|
||||||
|
(options.tag && options.tag.length > 0) ||
|
||||||
|
options.starred ||
|
||||||
(options.folderIds && options.folderIds.length > 0);
|
(options.folderIds && options.folderIds.length > 0);
|
||||||
|
|
||||||
if (!options.skipRecent && !hasFilters) {
|
if (!options.skipRecent && !hasFilters) {
|
||||||
@@ -111,9 +115,11 @@ export class SearchSrv {
|
|||||||
query.folderIds = [0];
|
query.folderIds = [0];
|
||||||
}
|
}
|
||||||
|
|
||||||
promises.push(this.backendSrv.search(query).then(results => {
|
promises.push(
|
||||||
return this.handleSearchResult(sections, results);
|
this.backendSrv.search(query).then(results => {
|
||||||
}));
|
return this.handleSearchResult(sections, results);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
return this.$q.all(promises).then(() => {
|
return this.$q.all(promises).then(() => {
|
||||||
return _.sortBy(_.values(sections), 'score');
|
return _.sortBy(_.values(sections), 'score');
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ export class Timer {
|
|||||||
timers = [];
|
timers = [];
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $timeout) {
|
constructor(private $timeout) {}
|
||||||
}
|
|
||||||
|
|
||||||
register(promise) {
|
register(promise) {
|
||||||
this.timers.push(promise);
|
this.timers.push(promise);
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ export class UtilSrv {
|
|||||||
modalScope: any;
|
modalScope: any;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $rootScope, private $modal) {
|
constructor(private $rootScope, private $modal) {}
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
appEvents.on('show-modal', this.showModal.bind(this), this.$rootScope);
|
appEvents.on('show-modal', this.showModal.bind(this), this.$rootScope);
|
||||||
@@ -43,7 +42,7 @@ export class UtilSrv {
|
|||||||
show: false,
|
show: false,
|
||||||
scope: this.modalScope,
|
scope: this.modalScope,
|
||||||
keyboard: false,
|
keyboard: false,
|
||||||
backdrop: options.backdrop
|
backdrop: options.backdrop,
|
||||||
});
|
});
|
||||||
|
|
||||||
Promise.resolve(modal).then(function(modalEl) {
|
Promise.resolve(modal).then(function(modalEl) {
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import {describe, beforeEach, it, expect, angularMocks} from 'test/lib/common';
|
import {
|
||||||
|
describe,
|
||||||
|
beforeEach,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
angularMocks,
|
||||||
|
} from 'test/lib/common';
|
||||||
import 'app/core/services/backend_srv';
|
import 'app/core/services/backend_srv';
|
||||||
|
|
||||||
describe('backend_srv', function() {
|
describe('backend_srv', function() {
|
||||||
@@ -7,20 +13,24 @@ describe('backend_srv', function() {
|
|||||||
|
|
||||||
beforeEach(angularMocks.module('grafana.core'));
|
beforeEach(angularMocks.module('grafana.core'));
|
||||||
beforeEach(angularMocks.module('grafana.services'));
|
beforeEach(angularMocks.module('grafana.services'));
|
||||||
beforeEach(angularMocks.inject(function ($httpBackend, $http, backendSrv) {
|
beforeEach(
|
||||||
_httpBackend = $httpBackend;
|
angularMocks.inject(function($httpBackend, $http, backendSrv) {
|
||||||
_backendSrv = backendSrv;
|
_httpBackend = $httpBackend;
|
||||||
}));
|
_backendSrv = backendSrv;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
describe('when handling errors', function() {
|
describe('when handling errors', function() {
|
||||||
it('should return the http status code', function(done) {
|
it('should return the http status code', function(done) {
|
||||||
_httpBackend.whenGET('gateway-error').respond(502);
|
_httpBackend.whenGET('gateway-error').respond(502);
|
||||||
_backendSrv.datasourceRequest({
|
_backendSrv
|
||||||
url: 'gateway-error'
|
.datasourceRequest({
|
||||||
}).catch(function(err) {
|
url: 'gateway-error',
|
||||||
expect(err.status).to.be(502);
|
})
|
||||||
done();
|
.catch(function(err) {
|
||||||
});
|
expect(err.status).to.be(502);
|
||||||
|
done();
|
||||||
|
});
|
||||||
_httpBackend.flush();
|
_httpBackend.flush();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import * as dateMath from 'app/core/utils/datemath';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
describe("DateMath", () => {
|
describe('DateMath', () => {
|
||||||
var spans = ['s', 'm', 'h', 'd', 'w', 'M', 'y'];
|
var spans = ['s', 'm', 'h', 'd', 'w', 'M', 'y'];
|
||||||
var anchor = '2014-01-01T06:06:06.666Z';
|
var anchor = '2014-01-01T06:06:06.666Z';
|
||||||
var unix = moment(anchor).valueOf();
|
var unix = moment(anchor).valueOf();
|
||||||
var format = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
|
var format = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
|
||||||
var clock;
|
var clock;
|
||||||
@@ -20,9 +20,12 @@ describe("DateMath", () => {
|
|||||||
expect(dateMath.parse('now&1d')).toBe(undefined);
|
expect(dateMath.parse('now&1d')).toBe(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return undefined if I pass a unit besides' + spans.toString(), () => {
|
it(
|
||||||
expect(dateMath.parse('now+5f')).toBe(undefined);
|
'should return undefined if I pass a unit besides' + spans.toString(),
|
||||||
});
|
() => {
|
||||||
|
expect(dateMath.parse('now+5f')).toBe(undefined);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
it('should return undefined if rounding unit is not 1', () => {
|
it('should return undefined if rounding unit is not 1', () => {
|
||||||
expect(dateMath.parse('now/2y')).toBe(undefined);
|
expect(dateMath.parse('now/2y')).toBe(undefined);
|
||||||
@@ -35,7 +38,7 @@ describe("DateMath", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("now/d should set to start of current day", () => {
|
it('now/d should set to start of current day', () => {
|
||||||
var expected = new Date();
|
var expected = new Date();
|
||||||
expected.setHours(0);
|
expected.setHours(0);
|
||||||
expected.setMinutes(0);
|
expected.setMinutes(0);
|
||||||
@@ -46,9 +49,19 @@ describe("DateMath", () => {
|
|||||||
expect(startOfDay).toBe(expected.getTime());
|
expect(startOfDay).toBe(expected.getTime());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("now/d on a utc dashboard should be start of the current day in UTC time", () => {
|
it('now/d on a utc dashboard should be start of the current day in UTC time', () => {
|
||||||
var today = new Date();
|
var today = new Date();
|
||||||
var expected = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate(), 0, 0, 0, 0));
|
var expected = new Date(
|
||||||
|
Date.UTC(
|
||||||
|
today.getUTCFullYear(),
|
||||||
|
today.getUTCMonth(),
|
||||||
|
today.getUTCDate(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
var startOfDay = dateMath.parse('now/d', false, 'utc').valueOf();
|
var startOfDay = dateMath.parse('now/d', false, 'utc').valueOf();
|
||||||
expect(startOfDay).toBe(expected.getTime());
|
expect(startOfDay).toBe(expected.getTime());
|
||||||
@@ -64,16 +77,20 @@ describe("DateMath", () => {
|
|||||||
anchored = moment(anchor);
|
anchored = moment(anchor);
|
||||||
});
|
});
|
||||||
|
|
||||||
_.each(spans, (span) => {
|
_.each(spans, span => {
|
||||||
var nowEx = 'now-5' + span;
|
var nowEx = 'now-5' + span;
|
||||||
var thenEx = anchor + '||-5' + span;
|
var thenEx = anchor + '||-5' + span;
|
||||||
|
|
||||||
it('should return 5' + span + ' ago', () => {
|
it('should return 5' + span + ' ago', () => {
|
||||||
expect(dateMath.parse(nowEx).format(format)).toEqual(now.subtract(5, span).format(format));
|
expect(dateMath.parse(nowEx).format(format)).toEqual(
|
||||||
|
now.subtract(5, span).format(format)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return 5' + span + ' before ' + anchor, () => {
|
it('should return 5' + span + ' before ' + anchor, () => {
|
||||||
expect(dateMath.parse(thenEx).format(format)).toEqual(anchored.subtract(5, span).format(format));
|
expect(dateMath.parse(thenEx).format(format)).toEqual(
|
||||||
|
anchored.subtract(5, span).format(format)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -90,13 +107,17 @@ describe("DateMath", () => {
|
|||||||
now = moment();
|
now = moment();
|
||||||
});
|
});
|
||||||
|
|
||||||
_.each(spans, (span) => {
|
_.each(spans, span => {
|
||||||
it('should round now to the beginning of the ' + span, function () {
|
it('should round now to the beginning of the ' + span, function() {
|
||||||
expect(dateMath.parse('now/' + span).format(format)).toEqual(now.startOf(span).format(format));
|
expect(dateMath.parse('now/' + span).format(format)).toEqual(
|
||||||
|
now.startOf(span).format(format)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should round now to the end of the ' + span, function () {
|
it('should round now to the end of the ' + span, function() {
|
||||||
expect(dateMath.parse('now/' + span, true).format(format)).toEqual(now.endOf(span).format(format));
|
expect(dateMath.parse('now/' + span, true).format(format)).toEqual(
|
||||||
|
now.endOf(span).format(format)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -130,7 +151,4 @@ describe("DateMath", () => {
|
|||||||
expect(date).toEqual(undefined);
|
expect(date).toEqual(undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import {Emitter} from '../utils/emitter';
|
import { Emitter } from '../utils/emitter';
|
||||||
|
|
||||||
describe("Emitter", () => {
|
|
||||||
|
|
||||||
|
describe('Emitter', () => {
|
||||||
describe('given 2 subscribers', () => {
|
describe('given 2 subscribers', () => {
|
||||||
|
|
||||||
it('should notfiy subscribers', () => {
|
it('should notfiy subscribers', () => {
|
||||||
var events = new Emitter();
|
var events = new Emitter();
|
||||||
var sub1Called = false;
|
var sub1Called = false;
|
||||||
@@ -45,20 +43,22 @@ describe("Emitter", () => {
|
|||||||
|
|
||||||
events.on('test', () => {
|
events.on('test', () => {
|
||||||
sub1Called++;
|
sub1Called++;
|
||||||
throw {message: "hello"};
|
throw { message: 'hello' };
|
||||||
});
|
});
|
||||||
|
|
||||||
events.on('test', () => {
|
events.on('test', () => {
|
||||||
sub2Called++;
|
sub2Called++;
|
||||||
});
|
});
|
||||||
|
|
||||||
try { events.emit('test', null); } catch (_) { }
|
try {
|
||||||
try { events.emit('test', null); } catch (_) {}
|
events.emit('test', null);
|
||||||
|
} catch (_) {}
|
||||||
|
try {
|
||||||
|
events.emit('test', null);
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
expect(sub1Called).toBe(2);
|
expect(sub1Called).toBe(2);
|
||||||
expect(sub2Called).toBe(0);
|
expect(sub2Called).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import flatten from 'app/core/utils/flatten';
|
import flatten from 'app/core/utils/flatten';
|
||||||
|
|
||||||
describe("flatten", () => {
|
describe('flatten', () => {
|
||||||
|
|
||||||
it('should return flatten object', () => {
|
it('should return flatten object', () => {
|
||||||
var flattened = flatten({
|
var flattened = flatten(
|
||||||
level1: 'level1-value',
|
{
|
||||||
deeper: {
|
level1: 'level1-value',
|
||||||
level2: 'level2-value',
|
|
||||||
deeper: {
|
deeper: {
|
||||||
level3: 'level3-value'
|
level2: 'level2-value',
|
||||||
}
|
deeper: {
|
||||||
}
|
level3: 'level3-value',
|
||||||
}, null);
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
expect(flattened['level1']).toBe('level1-value');
|
expect(flattened['level1']).toBe('level1-value');
|
||||||
expect(flattened['deeper.level2']).toBe('level2-value');
|
expect(flattened['deeper.level2']).toBe('level2-value');
|
||||||
expect(flattened['deeper.deeper.level3']).toBe('level3-value');
|
expect(flattened['deeper.deeper.level3']).toBe('level3-value');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { GlobalEventSrv } from 'app/core/services/global_event_srv';
|
||||||
|
import { beforeEach } from 'test/lib/common';
|
||||||
|
|
||||||
|
jest.mock('app/core/config', () => {
|
||||||
|
return {
|
||||||
|
appSubUrl: '/subUrl',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GlobalEventSrv', () => {
|
||||||
|
let searchSrv;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
searchSrv = new GlobalEventSrv(null, null, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('With /subUrl as appSubUrl', () => {
|
||||||
|
it('/subUrl should be stripped', () => {
|
||||||
|
const urlWithoutMaster = searchSrv.stripBaseFromUrl('/subUrl/grafana/');
|
||||||
|
expect(urlWithoutMaster).toBe('/grafana/');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -5,9 +5,7 @@ import moment from 'moment';
|
|||||||
describe('unit format menu', function() {
|
describe('unit format menu', function() {
|
||||||
var menu = kbn.getUnitFormats();
|
var menu = kbn.getUnitFormats();
|
||||||
menu.map(function(submenu) {
|
menu.map(function(submenu) {
|
||||||
|
|
||||||
describe('submenu ' + submenu.text, function() {
|
describe('submenu ' + submenu.text, function() {
|
||||||
|
|
||||||
it('should have a title', function() {
|
it('should have a title', function() {
|
||||||
expect(typeof submenu.text).toBe('string');
|
expect(typeof submenu.text).toBe('string');
|
||||||
});
|
});
|
||||||
@@ -18,8 +16,12 @@ describe('unit format menu', function() {
|
|||||||
|
|
||||||
submenu.submenu.map(function(entry) {
|
submenu.submenu.map(function(entry) {
|
||||||
describe('entry ' + entry.text, function() {
|
describe('entry ' + entry.text, function() {
|
||||||
it('should have a title', function() { expect(typeof entry.text).toBe('string'); });
|
it('should have a title', function() {
|
||||||
it('should have a format', function() { expect(typeof entry.value).toBe('string'); });
|
expect(typeof entry.text).toBe('string');
|
||||||
|
});
|
||||||
|
it('should have a format', function() {
|
||||||
|
expect(typeof entry.value).toBe('string');
|
||||||
|
});
|
||||||
it('should have a valid format', function() {
|
it('should have a valid format', function() {
|
||||||
expect(typeof kbn.valueFormats[entry.value]).toBe('function');
|
expect(typeof kbn.valueFormats[entry.value]).toBe('function');
|
||||||
});
|
});
|
||||||
@@ -30,15 +32,14 @@ describe('unit format menu', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function describeValueFormat(desc, value, tickSize, tickDecimals, result) {
|
function describeValueFormat(desc, value, tickSize, tickDecimals, result) {
|
||||||
|
|
||||||
describe('value format: ' + desc, function() {
|
describe('value format: ' + desc, function() {
|
||||||
it('should translate ' + value + ' as ' + result, function() {
|
it('should translate ' + value + ' as ' + result, function() {
|
||||||
var scaledDecimals = tickDecimals - Math.floor(Math.log(tickSize) / Math.LN10);
|
var scaledDecimals =
|
||||||
|
tickDecimals - Math.floor(Math.log(tickSize) / Math.LN10);
|
||||||
var str = kbn.valueFormats[desc](value, tickDecimals, scaledDecimals);
|
var str = kbn.valueFormats[desc](value, tickDecimals, scaledDecimals);
|
||||||
expect(str).toBe(result);
|
expect(str).toBe(result);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describeValueFormat('ms', 0.0024, 0.0005, 4, '0.0024 ms');
|
describeValueFormat('ms', 0.0024, 0.0005, 4, '0.0024 ms');
|
||||||
@@ -53,7 +54,7 @@ describeValueFormat('none', 2.75e-10, 0, 10, '3e-10');
|
|||||||
describeValueFormat('none', 0, 0, 2, '0');
|
describeValueFormat('none', 0, 0, 2, '0');
|
||||||
describeValueFormat('dB', 10, 1000, 2, '10.00 dB');
|
describeValueFormat('dB', 10, 1000, 2, '10.00 dB');
|
||||||
|
|
||||||
describeValueFormat('percent', 0, 0, 0, '0%');
|
describeValueFormat('percent', 0, 0, 0, '0%');
|
||||||
describeValueFormat('percent', 53, 0, 1, '53.0%');
|
describeValueFormat('percent', 53, 0, 1, '53.0%');
|
||||||
describeValueFormat('percentunit', 0.0, 0, 0, '0%');
|
describeValueFormat('percentunit', 0.0, 0, 0, '0%');
|
||||||
describeValueFormat('percentunit', 0.278, 0, 1, '27.8%');
|
describeValueFormat('percentunit', 0.278, 0, 1, '27.8%');
|
||||||
@@ -63,7 +64,7 @@ describeValueFormat('currencyUSD', 7.42, 10000, 2, '$7.42');
|
|||||||
describeValueFormat('currencyUSD', 1532.82, 1000, 1, '$1.53K');
|
describeValueFormat('currencyUSD', 1532.82, 1000, 1, '$1.53K');
|
||||||
describeValueFormat('currencyUSD', 18520408.7, 10000000, 0, '$19M');
|
describeValueFormat('currencyUSD', 18520408.7, 10000000, 0, '$19M');
|
||||||
|
|
||||||
describeValueFormat('bytes', -1.57e+308, -1.57e+308, 2, 'NA');
|
describeValueFormat('bytes', -1.57e308, -1.57e308, 2, 'NA');
|
||||||
|
|
||||||
describeValueFormat('ns', 25, 1, 0, '25 ns');
|
describeValueFormat('ns', 25, 1, 0, '25 ns');
|
||||||
describeValueFormat('ns', 2558, 50, 0, '2.56 µs');
|
describeValueFormat('ns', 2558, 50, 0, '2.56 µs');
|
||||||
@@ -109,7 +110,7 @@ describe('date time formats', function() {
|
|||||||
it('should format as iso date and skip date when today', function() {
|
it('should format as iso date and skip date when today', function() {
|
||||||
var now = moment();
|
var now = moment();
|
||||||
var str = kbn.valueFormats.dateTimeAsIso(now.valueOf(), 1);
|
var str = kbn.valueFormats.dateTimeAsIso(now.valueOf(), 1);
|
||||||
expect(str).toBe(now.format("HH:mm:ss"));
|
expect(str).toBe(now.format('HH:mm:ss'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format as US date', function() {
|
it('should format as US date', function() {
|
||||||
@@ -120,7 +121,7 @@ describe('date time formats', function() {
|
|||||||
it('should format as US date and skip date when today', function() {
|
it('should format as US date and skip date when today', function() {
|
||||||
var now = moment();
|
var now = moment();
|
||||||
var str = kbn.valueFormats.dateTimeAsUS(now.valueOf(), 1);
|
var str = kbn.valueFormats.dateTimeAsUS(now.valueOf(), 1);
|
||||||
expect(str).toBe(now.format("h:mm:ss a"));
|
expect(str).toBe(now.format('h:mm:ss a'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format as from now with days', function() {
|
it('should format as from now with days', function() {
|
||||||
@@ -190,7 +191,7 @@ describe('calculateInterval', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('fixed user min interval', function() {
|
it('fixed user min interval', function() {
|
||||||
var range = {from: dateMath.parse('now-10m'), to: dateMath.parse('now')};
|
var range = { from: dateMath.parse('now-10m'), to: dateMath.parse('now') };
|
||||||
var res = kbn.calculateInterval(range, 1600, '10s');
|
var res = kbn.calculateInterval(range, 1600, '10s');
|
||||||
expect(res.interval).toBe('10s');
|
expect(res.interval).toBe('10s');
|
||||||
expect(res.intervalMs).toBe(10000);
|
expect(res.intervalMs).toBe(10000);
|
||||||
@@ -203,7 +204,7 @@ describe('calculateInterval', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('large time range and user low limit', function() {
|
it('large time range and user low limit', function() {
|
||||||
var range = {from: dateMath.parse('now-14d'), to: dateMath.parse('now')};
|
var range = { from: dateMath.parse('now-14d'), to: dateMath.parse('now') };
|
||||||
var res = kbn.calculateInterval(range, 1000, '>10s');
|
var res = kbn.calculateInterval(range, 1000, '>10s');
|
||||||
expect(res.interval).toBe('20m');
|
expect(res.interval).toBe('20m');
|
||||||
});
|
});
|
||||||
@@ -222,7 +223,10 @@ describe('calculateInterval', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('86399s 1 resolution', function() {
|
it('86399s 1 resolution', function() {
|
||||||
var range = { from: dateMath.parse('now-86390s'), to: dateMath.parse('now') };
|
var range = {
|
||||||
|
from: dateMath.parse('now-86390s'),
|
||||||
|
to: dateMath.parse('now'),
|
||||||
|
};
|
||||||
var res = kbn.calculateInterval(range, 1, null);
|
var res = kbn.calculateInterval(range, 1, null);
|
||||||
expect(res.interval).toBe('12h');
|
expect(res.interval).toBe('12h');
|
||||||
expect(res.intervalMs).toBe(43200000);
|
expect(res.intervalMs).toBe(43200000);
|
||||||
@@ -254,11 +258,11 @@ describe('hex', function() {
|
|||||||
|
|
||||||
describe('hex 0x', function() {
|
describe('hex 0x', function() {
|
||||||
it('positive integeter', function() {
|
it('positive integeter', function() {
|
||||||
var str = kbn.valueFormats.hex0x(7999,0);
|
var str = kbn.valueFormats.hex0x(7999, 0);
|
||||||
expect(str).toBe('0x1F3F');
|
expect(str).toBe('0x1F3F');
|
||||||
});
|
});
|
||||||
it('negative integer', function() {
|
it('negative integer', function() {
|
||||||
var str = kbn.valueFormats.hex0x(-584,0);
|
var str = kbn.valueFormats.hex0x(-584, 0);
|
||||||
expect(str).toBe('-0x248');
|
expect(str).toBe('-0x248');
|
||||||
});
|
});
|
||||||
it('null', function() {
|
it('null', function() {
|
||||||
@@ -277,71 +281,75 @@ describe('hex 0x', function() {
|
|||||||
|
|
||||||
describe('duration', function() {
|
describe('duration', function() {
|
||||||
it('null', function() {
|
it('null', function() {
|
||||||
var str = kbn.toDuration(null, 0, "millisecond");
|
var str = kbn.toDuration(null, 0, 'millisecond');
|
||||||
expect(str).toBe('');
|
expect(str).toBe('');
|
||||||
});
|
});
|
||||||
it('0 milliseconds', function() {
|
it('0 milliseconds', function() {
|
||||||
var str = kbn.toDuration(0, 0, "millisecond");
|
var str = kbn.toDuration(0, 0, 'millisecond');
|
||||||
expect(str).toBe('0 milliseconds');
|
expect(str).toBe('0 milliseconds');
|
||||||
});
|
});
|
||||||
it('1 millisecond', function() {
|
it('1 millisecond', function() {
|
||||||
var str = kbn.toDuration(1, 0, "millisecond");
|
var str = kbn.toDuration(1, 0, 'millisecond');
|
||||||
expect(str).toBe('1 millisecond');
|
expect(str).toBe('1 millisecond');
|
||||||
});
|
});
|
||||||
it('-1 millisecond', function() {
|
it('-1 millisecond', function() {
|
||||||
var str = kbn.toDuration(-1, 0, "millisecond");
|
var str = kbn.toDuration(-1, 0, 'millisecond');
|
||||||
expect(str).toBe('1 millisecond ago');
|
expect(str).toBe('1 millisecond ago');
|
||||||
});
|
});
|
||||||
it('seconds', function() {
|
it('seconds', function() {
|
||||||
var str = kbn.toDuration(1, 0, "second");
|
var str = kbn.toDuration(1, 0, 'second');
|
||||||
expect(str).toBe('1 second');
|
expect(str).toBe('1 second');
|
||||||
});
|
});
|
||||||
it('minutes', function() {
|
it('minutes', function() {
|
||||||
var str = kbn.toDuration(1, 0, "minute");
|
var str = kbn.toDuration(1, 0, 'minute');
|
||||||
expect(str).toBe('1 minute');
|
expect(str).toBe('1 minute');
|
||||||
});
|
});
|
||||||
it('hours', function() {
|
it('hours', function() {
|
||||||
var str = kbn.toDuration(1, 0, "hour");
|
var str = kbn.toDuration(1, 0, 'hour');
|
||||||
expect(str).toBe('1 hour');
|
expect(str).toBe('1 hour');
|
||||||
});
|
});
|
||||||
it('days', function() {
|
it('days', function() {
|
||||||
var str = kbn.toDuration(1, 0, "day");
|
var str = kbn.toDuration(1, 0, 'day');
|
||||||
expect(str).toBe('1 day');
|
expect(str).toBe('1 day');
|
||||||
});
|
});
|
||||||
it('weeks', function() {
|
it('weeks', function() {
|
||||||
var str = kbn.toDuration(1, 0, "week");
|
var str = kbn.toDuration(1, 0, 'week');
|
||||||
expect(str).toBe('1 week');
|
expect(str).toBe('1 week');
|
||||||
});
|
});
|
||||||
it('months', function() {
|
it('months', function() {
|
||||||
var str = kbn.toDuration(1, 0, "month");
|
var str = kbn.toDuration(1, 0, 'month');
|
||||||
expect(str).toBe('1 month');
|
expect(str).toBe('1 month');
|
||||||
});
|
});
|
||||||
it('years', function() {
|
it('years', function() {
|
||||||
var str = kbn.toDuration(1, 0, "year");
|
var str = kbn.toDuration(1, 0, 'year');
|
||||||
expect(str).toBe('1 year');
|
expect(str).toBe('1 year');
|
||||||
});
|
});
|
||||||
it('decimal days', function() {
|
it('decimal days', function() {
|
||||||
var str = kbn.toDuration(1.5, 2, "day");
|
var str = kbn.toDuration(1.5, 2, 'day');
|
||||||
expect(str).toBe('1 day, 12 hours, 0 minutes');
|
expect(str).toBe('1 day, 12 hours, 0 minutes');
|
||||||
});
|
});
|
||||||
it('decimal months', function() {
|
it('decimal months', function() {
|
||||||
var str = kbn.toDuration(1.5, 3, "month");
|
var str = kbn.toDuration(1.5, 3, 'month');
|
||||||
expect(str).toBe('1 month, 2 weeks, 1 day, 0 hours');
|
expect(str).toBe('1 month, 2 weeks, 1 day, 0 hours');
|
||||||
});
|
});
|
||||||
it('no decimals', function() {
|
it('no decimals', function() {
|
||||||
var str = kbn.toDuration(38898367008, 0, "millisecond");
|
var str = kbn.toDuration(38898367008, 0, 'millisecond');
|
||||||
expect(str).toBe('1 year');
|
expect(str).toBe('1 year');
|
||||||
});
|
});
|
||||||
it('1 decimal', function() {
|
it('1 decimal', function() {
|
||||||
var str = kbn.toDuration(38898367008, 1, "millisecond");
|
var str = kbn.toDuration(38898367008, 1, 'millisecond');
|
||||||
expect(str).toBe('1 year, 2 months');
|
expect(str).toBe('1 year, 2 months');
|
||||||
});
|
});
|
||||||
it('too many decimals', function() {
|
it('too many decimals', function() {
|
||||||
var str = kbn.toDuration(38898367008, 20, "millisecond");
|
var str = kbn.toDuration(38898367008, 20, 'millisecond');
|
||||||
expect(str).toBe('1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, 7 seconds, 8 milliseconds');
|
expect(str).toBe(
|
||||||
|
'1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, 7 seconds, 8 milliseconds'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('floating point error', function() {
|
it('floating point error', function() {
|
||||||
var str = kbn.toDuration(36993906007, 8, "millisecond");
|
var str = kbn.toDuration(36993906007, 8, 'millisecond');
|
||||||
expect(str).toBe('1 year, 2 months, 0 weeks, 3 days, 4 hours, 5 minutes, 6 seconds, 7 milliseconds');
|
expect(str).toBe(
|
||||||
|
'1 year, 2 months, 0 weeks, 3 days, 4 hours, 5 minutes, 6 seconds, 7 milliseconds'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,43 +10,43 @@ describe('ManageDashboards', () => {
|
|||||||
const response = [
|
const response = [
|
||||||
{
|
{
|
||||||
id: 410,
|
id: 410,
|
||||||
title: "afolder",
|
title: 'afolder',
|
||||||
type: "dash-folder",
|
type: 'dash-folder',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: 399,
|
id: 399,
|
||||||
title: "Dashboard Test",
|
title: 'Dashboard Test',
|
||||||
url: "dashboard/db/dashboard-test",
|
url: 'dashboard/db/dashboard-test',
|
||||||
icon: 'fa fa-folder',
|
icon: 'fa fa-folder',
|
||||||
tags: [],
|
tags: [],
|
||||||
isStarred: false,
|
isStarred: false,
|
||||||
folderId: 410,
|
folderId: 410,
|
||||||
folderTitle: "afolder",
|
folderTitle: 'afolder',
|
||||||
folderSlug: "afolder"
|
folderSlug: 'afolder',
|
||||||
}
|
},
|
||||||
],
|
|
||||||
tags: [],
|
|
||||||
isStarred: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
title: "Root",
|
|
||||||
icon: 'fa fa-folder-open',
|
|
||||||
uri: "db/something-else",
|
|
||||||
type: "dash-db",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: 500,
|
|
||||||
title: "Dashboard Test",
|
|
||||||
url: "dashboard/db/dashboard-test",
|
|
||||||
icon: 'fa fa-folder',
|
|
||||||
tags: [],
|
|
||||||
isStarred: false
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
tags: [],
|
tags: [],
|
||||||
isStarred: false,
|
isStarred: false,
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
title: 'Root',
|
||||||
|
icon: 'fa fa-folder-open',
|
||||||
|
uri: 'db/something-else',
|
||||||
|
type: 'dash-db',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: 500,
|
||||||
|
title: 'Dashboard Test',
|
||||||
|
url: 'dashboard/db/dashboard-test',
|
||||||
|
icon: 'fa fa-folder',
|
||||||
|
tags: [],
|
||||||
|
isStarred: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tags: [],
|
||||||
|
isStarred: false,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
ctrl = createCtrlWithStubs(response);
|
ctrl = createCtrlWithStubs(response);
|
||||||
return ctrl.getDashboards();
|
return ctrl.getDashboards();
|
||||||
@@ -67,24 +67,24 @@ describe('ManageDashboards', () => {
|
|||||||
const response = [
|
const response = [
|
||||||
{
|
{
|
||||||
id: 410,
|
id: 410,
|
||||||
title: "afolder",
|
title: 'afolder',
|
||||||
type: "dash-folder",
|
type: 'dash-folder',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: 399,
|
id: 399,
|
||||||
title: "Dashboard Test",
|
title: 'Dashboard Test',
|
||||||
url: "dashboard/db/dashboard-test",
|
url: 'dashboard/db/dashboard-test',
|
||||||
icon: 'fa fa-folder',
|
icon: 'fa fa-folder',
|
||||||
tags: [],
|
tags: [],
|
||||||
isStarred: false,
|
isStarred: false,
|
||||||
folderId: 410,
|
folderId: 410,
|
||||||
folderTitle: "afolder",
|
folderTitle: 'afolder',
|
||||||
folderSlug: "afolder"
|
folderSlug: 'afolder',
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
tags: [],
|
tags: [],
|
||||||
isStarred: false
|
isStarred: false,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
ctrl = createCtrlWithStubs(response);
|
ctrl = createCtrlWithStubs(response);
|
||||||
ctrl.folderId = 410;
|
ctrl.folderId = 410;
|
||||||
@@ -106,26 +106,26 @@ describe('ManageDashboards', () => {
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: 399,
|
id: 399,
|
||||||
title: "Dashboard Test",
|
title: 'Dashboard Test',
|
||||||
url: "dashboard/db/dashboard-test",
|
url: 'dashboard/db/dashboard-test',
|
||||||
icon: 'fa fa-folder',
|
icon: 'fa fa-folder',
|
||||||
tags: [],
|
tags: [],
|
||||||
isStarred: false,
|
isStarred: false,
|
||||||
folderId: 410,
|
folderId: 410,
|
||||||
folderTitle: "afolder",
|
folderTitle: 'afolder',
|
||||||
folderSlug: "afolder"
|
folderSlug: 'afolder',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 500,
|
id: 500,
|
||||||
title: "Dashboard Test",
|
title: 'Dashboard Test',
|
||||||
url: "dashboard/db/dashboard-test",
|
url: 'dashboard/db/dashboard-test',
|
||||||
icon: 'fa fa-folder',
|
icon: 'fa fa-folder',
|
||||||
tags: [],
|
tags: [],
|
||||||
folderId: 499,
|
folderId: 499,
|
||||||
isStarred: false
|
isStarred: false,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
ctrl = createCtrlWithStubs(response);
|
ctrl = createCtrlWithStubs(response);
|
||||||
@@ -261,18 +261,14 @@ describe('ManageDashboards', () => {
|
|||||||
ctrl.sections = [
|
ctrl.sections = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
items: [
|
items: [{ id: 2, checked: false }],
|
||||||
{ id: 2, checked: false }
|
checked: false,
|
||||||
],
|
|
||||||
checked: false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
items: [
|
items: [{ id: 3, checked: false }],
|
||||||
{ id: 3, checked: false }
|
checked: false,
|
||||||
],
|
},
|
||||||
checked: false
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
ctrl.selectionChanged();
|
ctrl.selectionChanged();
|
||||||
});
|
});
|
||||||
@@ -313,18 +309,14 @@ describe('ManageDashboards', () => {
|
|||||||
ctrl.sections = [
|
ctrl.sections = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
items: [
|
items: [{ id: 2, checked: true }],
|
||||||
{ id: 2, checked: true }
|
checked: true,
|
||||||
],
|
|
||||||
checked: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
items: [
|
items: [{ id: 3, checked: true }],
|
||||||
{ id: 3, checked: true }
|
checked: true,
|
||||||
],
|
},
|
||||||
checked: true
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
ctrl.selectionChanged();
|
ctrl.selectionChanged();
|
||||||
});
|
});
|
||||||
@@ -366,19 +358,15 @@ describe('ManageDashboards', () => {
|
|||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: 'folder',
|
title: 'folder',
|
||||||
items: [
|
items: [{ id: 2, checked: false }],
|
||||||
{ id: 2, checked: false }
|
checked: false,
|
||||||
],
|
|
||||||
checked: false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
title: 'Root',
|
title: 'Root',
|
||||||
items: [
|
items: [{ id: 3, checked: true }],
|
||||||
{ id: 3, checked: true }
|
checked: false,
|
||||||
],
|
},
|
||||||
checked: false
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
ctrl.selectionChanged();
|
ctrl.selectionChanged();
|
||||||
});
|
});
|
||||||
@@ -398,19 +386,15 @@ describe('ManageDashboards', () => {
|
|||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: 'folder',
|
title: 'folder',
|
||||||
items: [
|
items: [{ id: 2, checked: true }],
|
||||||
{ id: 2, checked: true }
|
checked: false,
|
||||||
],
|
|
||||||
checked: false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
title: 'Root',
|
title: 'Root',
|
||||||
items: [
|
items: [{ id: 3, checked: false }],
|
||||||
{ id: 3, checked: false }
|
checked: false,
|
||||||
],
|
},
|
||||||
checked: false
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
ctrl.selectionChanged();
|
ctrl.selectionChanged();
|
||||||
@@ -431,19 +415,15 @@ describe('ManageDashboards', () => {
|
|||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: 'folder',
|
title: 'folder',
|
||||||
items: [
|
items: [{ id: 2, checked: true }],
|
||||||
{ id: 2, checked: true }
|
checked: false,
|
||||||
],
|
|
||||||
checked: false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
title: 'Root',
|
title: 'Root',
|
||||||
items: [
|
items: [{ id: 3, checked: true }],
|
||||||
{ id: 3, checked: true }
|
checked: false,
|
||||||
],
|
},
|
||||||
checked: false
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
ctrl.selectionChanged();
|
ctrl.selectionChanged();
|
||||||
@@ -464,27 +444,21 @@ describe('ManageDashboards', () => {
|
|||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: 'folder',
|
title: 'folder',
|
||||||
items: [
|
items: [{ id: 2, checked: false }],
|
||||||
{ id: 2, checked: false }
|
checked: true,
|
||||||
],
|
|
||||||
checked: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
title: 'folder',
|
title: 'folder',
|
||||||
items: [
|
items: [{ id: 4, checked: true }],
|
||||||
{ id: 4, checked: true }
|
checked: false,
|
||||||
],
|
|
||||||
checked: false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
title: 'Root',
|
title: 'Root',
|
||||||
items: [
|
items: [{ id: 3, checked: false }],
|
||||||
{ id: 3, checked: false }
|
checked: false,
|
||||||
],
|
},
|
||||||
checked: false
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
ctrl.selectionChanged();
|
ctrl.selectionChanged();
|
||||||
@@ -510,29 +484,23 @@ describe('ManageDashboards', () => {
|
|||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: 'folder',
|
title: 'folder',
|
||||||
items: [
|
items: [{ id: 2, checked: true, slug: 'folder-dash' }],
|
||||||
{ id: 2, checked: true, slug: 'folder-dash' }
|
|
||||||
],
|
|
||||||
checked: true,
|
checked: true,
|
||||||
slug: 'folder'
|
slug: 'folder',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
title: 'folder-2',
|
title: 'folder-2',
|
||||||
items: [
|
items: [{ id: 3, checked: true, slug: 'folder-2-dash' }],
|
||||||
{ id: 3, checked: true, slug: 'folder-2-dash' }
|
|
||||||
],
|
|
||||||
checked: false,
|
checked: false,
|
||||||
slug: 'folder-2'
|
slug: 'folder-2',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
title: 'Root',
|
title: 'Root',
|
||||||
items: [
|
items: [{ id: 3, checked: true, slug: 'root-dash' }],
|
||||||
{ id: 3, checked: true, slug: 'root-dash' }
|
checked: true,
|
||||||
],
|
},
|
||||||
checked: true
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
toBeDeleted = ctrl.getFoldersAndDashboardsToDelete();
|
toBeDeleted = ctrl.getFoldersAndDashboardsToDelete();
|
||||||
@@ -567,20 +535,16 @@ describe('ManageDashboards', () => {
|
|||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: 'folder',
|
title: 'folder',
|
||||||
items: [
|
items: [{ id: 2, checked: true, slug: 'dash' }],
|
||||||
{ id: 2, checked: true, slug: 'dash' }
|
|
||||||
],
|
|
||||||
checked: false,
|
checked: false,
|
||||||
slug: 'folder'
|
slug: 'folder',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
title: 'Root',
|
title: 'Root',
|
||||||
items: [
|
items: [{ id: 3, checked: true, slug: 'dash-2' }],
|
||||||
{ id: 3, checked: true, slug: 'dash-2' }
|
checked: false,
|
||||||
],
|
},
|
||||||
checked: false
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -600,8 +564,12 @@ function createCtrlWithStubs(searchResponse: any, tags?: any) {
|
|||||||
},
|
},
|
||||||
getDashboardTags: () => {
|
getDashboardTags: () => {
|
||||||
return q.resolve(tags || []);
|
return q.resolve(tags || []);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return new ManageDashboardsCtrl({}, { getNav: () => { } }, <SearchSrv>searchSrvStub);
|
return new ManageDashboardsCtrl(
|
||||||
|
{},
|
||||||
|
{ getNav: () => {} },
|
||||||
|
<SearchSrv>searchSrvStub
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import {OrgSwitchCtrl} from '../components/org_switcher';
|
import { OrgSwitchCtrl } from '../components/org_switcher';
|
||||||
import q from 'q';
|
import q from 'q';
|
||||||
|
|
||||||
jest.mock('app/core/services/context_srv', () => ({
|
jest.mock('app/core/services/context_srv', () => ({
|
||||||
contextSrv: {
|
contextSrv: {
|
||||||
user: {orgId: 1}
|
user: { orgId: 1 },
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('OrgSwitcher', () => {
|
describe('OrgSwitcher', () => {
|
||||||
@@ -13,18 +13,23 @@ describe('OrgSwitcher', () => {
|
|||||||
let expectedUsingUrl;
|
let expectedUsingUrl;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
||||||
const backendSrvStub: any = {
|
const backendSrvStub: any = {
|
||||||
get: (url) => { return q.resolve([]); },
|
get: url => {
|
||||||
post: (url) => { expectedUsingUrl = url; return q.resolve({}); }
|
return q.resolve([]);
|
||||||
|
},
|
||||||
|
post: url => {
|
||||||
|
expectedUsingUrl = url;
|
||||||
|
return q.resolve({});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const orgSwitcherCtrl = new OrgSwitchCtrl(backendSrvStub);
|
const orgSwitcherCtrl = new OrgSwitchCtrl(backendSrvStub);
|
||||||
|
|
||||||
orgSwitcherCtrl.getWindowLocationHref = () => 'http://localhost:3000?orgId=1&from=now-3h&to=now';
|
orgSwitcherCtrl.getWindowLocationHref = () =>
|
||||||
orgSwitcherCtrl.setWindowLocationHref = (href) => expectedHref = href;
|
'http://localhost:3000?orgId=1&from=now-3h&to=now';
|
||||||
|
orgSwitcherCtrl.setWindowLocationHref = href => (expectedHref = href);
|
||||||
|
|
||||||
return orgSwitcherCtrl.setUsingOrg({orgId: 2});
|
return orgSwitcherCtrl.setUsingOrg({ orgId: 2 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should switch orgId in call to backend', () => {
|
it('should switch orgId in call to backend', () => {
|
||||||
@@ -32,7 +37,9 @@ describe('OrgSwitcher', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should switch orgId in url', () => {
|
it('should switch orgId in url', () => {
|
||||||
expect(expectedHref).toBe('http://localhost:3000?orgId=2&from=now-3h&to=now');
|
expect(expectedHref).toBe(
|
||||||
|
'http://localhost:3000?orgId=2&from=now-3h&to=now'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,17 +2,19 @@ import * as rangeUtil from 'app/core/utils/rangeutil';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
describe("rangeUtil", () => {
|
describe('rangeUtil', () => {
|
||||||
|
describe('Can get range grouped list of ranges', () => {
|
||||||
describe("Can get range grouped list of ranges", () => {
|
|
||||||
it('when custom settings should return default range list', () => {
|
it('when custom settings should return default range list', () => {
|
||||||
var groups = rangeUtil.getRelativeTimesList({time_options: []}, 'Last 5 minutes');
|
var groups = rangeUtil.getRelativeTimesList(
|
||||||
|
{ time_options: [] },
|
||||||
|
'Last 5 minutes'
|
||||||
|
);
|
||||||
expect(_.keys(groups).length).toBe(4);
|
expect(_.keys(groups).length).toBe(4);
|
||||||
expect(groups[3][0].active).toBe(true);
|
expect(groups[3][0].active).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Can get range text described", () => {
|
describe('Can get range text described', () => {
|
||||||
it('should handle simple old expression with only amount and unit', () => {
|
it('should handle simple old expression with only amount and unit', () => {
|
||||||
var info = rangeUtil.describeTextRange('5m');
|
var info = rangeUtil.describeTextRange('5m');
|
||||||
expect(info.display).toBe('Last 5 minutes');
|
expect(info.display).toBe('Last 5 minutes');
|
||||||
@@ -57,52 +59,62 @@ describe("rangeUtil", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Can get date range described", () => {
|
describe('Can get date range described', () => {
|
||||||
it('Date range with simple ranges', () => {
|
it('Date range with simple ranges', () => {
|
||||||
var text = rangeUtil.describeTimeRange({from: 'now-1h', to: 'now'});
|
var text = rangeUtil.describeTimeRange({ from: 'now-1h', to: 'now' });
|
||||||
expect(text).toBe('Last 1 hour');
|
expect(text).toBe('Last 1 hour');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Date range with rounding ranges', () => {
|
it('Date range with rounding ranges', () => {
|
||||||
var text = rangeUtil.describeTimeRange({from: 'now/d+6h', to: 'now'});
|
var text = rangeUtil.describeTimeRange({ from: 'now/d+6h', to: 'now' });
|
||||||
expect(text).toBe('now/d+6h to now');
|
expect(text).toBe('now/d+6h to now');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Date range with absolute to now', () => {
|
it('Date range with absolute to now', () => {
|
||||||
var text = rangeUtil.describeTimeRange({from: moment([2014,10,10,2,3,4]), to: 'now'});
|
var text = rangeUtil.describeTimeRange({
|
||||||
|
from: moment([2014, 10, 10, 2, 3, 4]),
|
||||||
|
to: 'now',
|
||||||
|
});
|
||||||
expect(text).toBe('Nov 10, 2014 02:03:04 to a few seconds ago');
|
expect(text).toBe('Nov 10, 2014 02:03:04 to a few seconds ago');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Date range with absolute to relative', () => {
|
it('Date range with absolute to relative', () => {
|
||||||
var text = rangeUtil.describeTimeRange({from: moment([2014,10,10,2,3,4]), to: 'now-1d'});
|
var text = rangeUtil.describeTimeRange({
|
||||||
|
from: moment([2014, 10, 10, 2, 3, 4]),
|
||||||
|
to: 'now-1d',
|
||||||
|
});
|
||||||
expect(text).toBe('Nov 10, 2014 02:03:04 to a day ago');
|
expect(text).toBe('Nov 10, 2014 02:03:04 to a day ago');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Date range with relative to absolute', () => {
|
it('Date range with relative to absolute', () => {
|
||||||
var text = rangeUtil.describeTimeRange({from: 'now-7d', to: moment([2014,10,10,2,3,4])});
|
var text = rangeUtil.describeTimeRange({
|
||||||
|
from: 'now-7d',
|
||||||
|
to: moment([2014, 10, 10, 2, 3, 4]),
|
||||||
|
});
|
||||||
expect(text).toBe('7 days ago to Nov 10, 2014 02:03:04');
|
expect(text).toBe('7 days ago to Nov 10, 2014 02:03:04');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Date range with non matching default ranges', () => {
|
it('Date range with non matching default ranges', () => {
|
||||||
var text = rangeUtil.describeTimeRange({from: 'now-13h', to: 'now'});
|
var text = rangeUtil.describeTimeRange({ from: 'now-13h', to: 'now' });
|
||||||
expect(text).toBe('Last 13 hours');
|
expect(text).toBe('Last 13 hours');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Date range with from and to both are in now-* format', () => {
|
it('Date range with from and to both are in now-* format', () => {
|
||||||
var text = rangeUtil.describeTimeRange({from: 'now-6h', to: 'now-3h'});
|
var text = rangeUtil.describeTimeRange({ from: 'now-6h', to: 'now-3h' });
|
||||||
expect(text).toBe('now-6h to now-3h');
|
expect(text).toBe('now-6h to now-3h');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Date range with from and to both are either in now-* or now/* format', () => {
|
it('Date range with from and to both are either in now-* or now/* format', () => {
|
||||||
var text = rangeUtil.describeTimeRange({from: 'now/d+6h', to: 'now-3h'});
|
var text = rangeUtil.describeTimeRange({
|
||||||
|
from: 'now/d+6h',
|
||||||
|
to: 'now-3h',
|
||||||
|
});
|
||||||
expect(text).toBe('now/d+6h to now-3h');
|
expect(text).toBe('now/d+6h to now-3h');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Date range with from and to both are either in now-* or now+* format', () => {
|
it('Date range with from and to both are either in now-* or now+* format', () => {
|
||||||
var text = rangeUtil.describeTimeRange({from: 'now-6h', to: 'now+1h'});
|
var text = rangeUtil.describeTimeRange({ from: 'now-6h', to: 'now+1h' });
|
||||||
expect(text).toBe('now-6h to now+1h');
|
expect(text).toBe('now-6h to now+1h');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,9 +4,14 @@ import { SearchSrv } from '../services/search_srv';
|
|||||||
describe('SearchCtrl', () => {
|
describe('SearchCtrl', () => {
|
||||||
const searchSrvStub = {
|
const searchSrvStub = {
|
||||||
search: (options: any) => {},
|
search: (options: any) => {},
|
||||||
getDashboardTags: () => {}
|
getDashboardTags: () => {},
|
||||||
};
|
};
|
||||||
let ctrl = new SearchCtrl({}, {}, {}, <SearchSrv>searchSrvStub, { onAppEvent: () => { } });
|
let ctrl = new SearchCtrl(
|
||||||
|
{ $on: () => {} },
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
<SearchSrv>searchSrvStub
|
||||||
|
);
|
||||||
|
|
||||||
describe('Given an empty result', () => {
|
describe('Given an empty result', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -45,19 +50,16 @@ describe('SearchCtrl', () => {
|
|||||||
items: [],
|
items: [],
|
||||||
selected: true,
|
selected: true,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
toggle: (i) => i.expanded = !i.expanded
|
toggle: i => (i.expanded = !i.expanded),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
title: 'Root',
|
title: 'Root',
|
||||||
items: [
|
items: [{ id: 3, selected: false }, { id: 5, selected: false }],
|
||||||
{ id: 3, selected: false },
|
|
||||||
{ id: 5, selected: false }
|
|
||||||
],
|
|
||||||
selected: false,
|
selected: false,
|
||||||
expanded: true,
|
expanded: true,
|
||||||
toggle: (i) => i.expanded = !i.expanded
|
toggle: i => (i.expanded = !i.expanded),
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -142,25 +144,19 @@ describe('SearchCtrl', () => {
|
|||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: 'folder',
|
title: 'folder',
|
||||||
items: [
|
items: [{ id: 2, selected: false }, { id: 4, selected: false }],
|
||||||
{ id: 2, selected: false },
|
|
||||||
{ id: 4, selected: false }
|
|
||||||
],
|
|
||||||
selected: true,
|
selected: true,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
toggle: (i) => i.expanded = !i.expanded
|
toggle: i => (i.expanded = !i.expanded),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
title: 'Root',
|
title: 'Root',
|
||||||
items: [
|
items: [{ id: 3, selected: false }, { id: 5, selected: false }],
|
||||||
{ id: 3, selected: false },
|
|
||||||
{ id: 5, selected: false }
|
|
||||||
],
|
|
||||||
selected: false,
|
selected: false,
|
||||||
expanded: true,
|
expanded: true,
|
||||||
toggle: (i) => i.expanded = !i.expanded
|
toggle: i => (i.expanded = !i.expanded),
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -252,14 +248,11 @@ describe('SearchCtrl', () => {
|
|||||||
ctrl.results = [
|
ctrl.results = [
|
||||||
{
|
{
|
||||||
hideHeader: true,
|
hideHeader: true,
|
||||||
items: [
|
items: [{ id: 3, selected: true }, { id: 5, selected: false }],
|
||||||
{ id: 3, selected: true },
|
|
||||||
{ id: 5, selected: false }
|
|
||||||
],
|
|
||||||
selected: false,
|
selected: false,
|
||||||
expanded: true,
|
expanded: true,
|
||||||
toggle: (i) => i.expanded = !i.expanded
|
toggle: i => (i.expanded = !i.expanded),
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
import { SearchResultsCtrl } from '../components/search/search_results';
|
import { SearchResultsCtrl } from '../components/search/search_results';
|
||||||
|
import { beforeEach, afterEach } from 'test/lib/common';
|
||||||
|
import appEvents from 'app/core/app_events';
|
||||||
|
|
||||||
|
jest.mock('app/core/app_events', () => {
|
||||||
|
return {
|
||||||
|
emit: jest.fn<any>(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
describe('SearchResultsCtrl', () => {
|
describe('SearchResultsCtrl', () => {
|
||||||
let ctrl;
|
let ctrl;
|
||||||
|
|
||||||
describe('when checking an item that is not checked', () => {
|
describe('when checking an item that is not checked', () => {
|
||||||
let item = {checked: false};
|
let item = { checked: false };
|
||||||
let selectionChanged = false;
|
let selectionChanged = false;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
ctrl = new SearchResultsCtrl({});
|
ctrl = new SearchResultsCtrl({});
|
||||||
ctrl.onSelectionChanged = () => selectionChanged = true;
|
ctrl.onSelectionChanged = () => (selectionChanged = true);
|
||||||
ctrl.toggleSelection(item);
|
ctrl.toggleSelection(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -23,12 +31,12 @@ describe('SearchResultsCtrl', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when checking an item that is checked', () => {
|
describe('when checking an item that is checked', () => {
|
||||||
let item = {checked: true};
|
let item = { checked: true };
|
||||||
let selectionChanged = false;
|
let selectionChanged = false;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
ctrl = new SearchResultsCtrl({});
|
ctrl = new SearchResultsCtrl({});
|
||||||
ctrl.onSelectionChanged = () => selectionChanged = true;
|
ctrl.onSelectionChanged = () => (selectionChanged = true);
|
||||||
ctrl.toggleSelection(item);
|
ctrl.toggleSelection(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -46,12 +54,12 @@ describe('SearchResultsCtrl', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
ctrl = new SearchResultsCtrl({});
|
ctrl = new SearchResultsCtrl({});
|
||||||
ctrl.onTagSelected = (tag) => selectedTag = tag;
|
ctrl.onTagSelected = tag => (selectedTag = tag);
|
||||||
ctrl.selectTag('tag-test');
|
ctrl.selectTag('tag-test');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should trigger tag selected callback', () => {
|
it('should trigger tag selected callback', () => {
|
||||||
expect(selectedTag["$tag"]).toBe('tag-test');
|
expect(selectedTag['$tag']).toBe('tag-test');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -60,11 +68,13 @@ describe('SearchResultsCtrl', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
ctrl = new SearchResultsCtrl({});
|
ctrl = new SearchResultsCtrl({});
|
||||||
ctrl.onFolderExpanding = () => { folderExpanded = true; };
|
ctrl.onFolderExpanding = () => {
|
||||||
|
folderExpanded = true;
|
||||||
|
};
|
||||||
|
|
||||||
let folder = {
|
let folder = {
|
||||||
expanded: false,
|
expanded: false,
|
||||||
toggle: () => Promise.resolve(folder)
|
toggle: () => Promise.resolve(folder),
|
||||||
};
|
};
|
||||||
|
|
||||||
ctrl.toggleFolderExpand(folder);
|
ctrl.toggleFolderExpand(folder);
|
||||||
@@ -80,11 +90,13 @@ describe('SearchResultsCtrl', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
ctrl = new SearchResultsCtrl({});
|
ctrl = new SearchResultsCtrl({});
|
||||||
ctrl.onFolderExpanding = () => { folderExpanded = true; };
|
ctrl.onFolderExpanding = () => {
|
||||||
|
folderExpanded = true;
|
||||||
|
};
|
||||||
|
|
||||||
let folder = {
|
let folder = {
|
||||||
expanded: true,
|
expanded: true,
|
||||||
toggle: () => Promise.resolve(folder)
|
toggle: () => Promise.resolve(folder),
|
||||||
};
|
};
|
||||||
|
|
||||||
ctrl.toggleFolderExpand(folder);
|
ctrl.toggleFolderExpand(folder);
|
||||||
@@ -94,4 +106,39 @@ describe('SearchResultsCtrl', () => {
|
|||||||
expect(folderExpanded).toBeFalsy();
|
expect(folderExpanded).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when clicking on a link in search result', () => {
|
||||||
|
const dashPath = 'dashboard/path';
|
||||||
|
const $location = { path: () => dashPath };
|
||||||
|
const appEventsMock = appEvents as any;
|
||||||
|
|
||||||
|
describe('with the same url as current path', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
ctrl = new SearchResultsCtrl($location);
|
||||||
|
const item = { url: dashPath };
|
||||||
|
ctrl.onItemClick(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close the search', () => {
|
||||||
|
expect(appEventsMock.emit.mock.calls.length).toBe(1);
|
||||||
|
expect(appEventsMock.emit.mock.calls[0][0]).toBe('hide-dash-search');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a different url than current path', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
ctrl = new SearchResultsCtrl($location);
|
||||||
|
const item = { url: 'another/path' };
|
||||||
|
ctrl.onItemClick(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should do nothing', () => {
|
||||||
|
expect(appEventsMock.emit.mock.calls.length).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
appEventsMock.emit.mockClear();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -35,7 +35,10 @@ describe('SearchSrv', () => {
|
|||||||
backendSrvMock.search = jest
|
backendSrvMock.search = jest
|
||||||
.fn()
|
.fn()
|
||||||
.mockReturnValueOnce(
|
.mockReturnValueOnce(
|
||||||
Promise.resolve([{ id: 2, title: 'second but first' }, { id: 1, title: 'first but second' }]),
|
Promise.resolve([
|
||||||
|
{ id: 2, title: 'second but first' },
|
||||||
|
{ id: 1, title: 'first but second' },
|
||||||
|
])
|
||||||
)
|
)
|
||||||
.mockReturnValue(Promise.resolve([]));
|
.mockReturnValue(Promise.resolve([]));
|
||||||
|
|
||||||
@@ -47,7 +50,7 @@ describe('SearchSrv', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should include recent dashboards section', () => {
|
it('should include recent dashboards section', () => {
|
||||||
expect(results[0].title).toBe('Recent Boards');
|
expect(results[0].title).toBe('Recent');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return order decided by impressions store not api', () => {
|
it('should return order decided by impressions store not api', () => {
|
||||||
@@ -62,11 +65,13 @@ describe('SearchSrv', () => {
|
|||||||
backendSrvMock.search = jest
|
backendSrvMock.search = jest
|
||||||
.fn()
|
.fn()
|
||||||
.mockReturnValueOnce(
|
.mockReturnValueOnce(
|
||||||
Promise.resolve([{ id: 2, title: 'two' }, { id: 1, title: 'one' }]),
|
Promise.resolve([{ id: 2, title: 'two' }, { id: 1, title: 'one' }])
|
||||||
)
|
)
|
||||||
.mockReturnValue(Promise.resolve([]));
|
.mockReturnValue(Promise.resolve([]));
|
||||||
|
|
||||||
impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([4, 5, 1, 2, 3]);
|
impressionSrv.getDashboardOpened = jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValue([4, 5, 1, 2, 3]);
|
||||||
|
|
||||||
return searchSrv.search({ query: '' }).then(res => {
|
return searchSrv.search({ query: '' }).then(res => {
|
||||||
results = res;
|
results = res;
|
||||||
@@ -87,9 +92,7 @@ describe('SearchSrv', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
backendSrvMock.search = jest
|
backendSrvMock.search = jest
|
||||||
.fn()
|
.fn()
|
||||||
.mockReturnValue(Promise.resolve([
|
.mockReturnValue(Promise.resolve([{ id: 1, title: 'starred' }]));
|
||||||
{id: 1, title: 'starred'}
|
|
||||||
]));
|
|
||||||
|
|
||||||
return searchSrv.search({ query: '' }).then(res => {
|
return searchSrv.search({ query: '' }).then(res => {
|
||||||
results = res;
|
results = res;
|
||||||
@@ -97,7 +100,7 @@ describe('SearchSrv', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should include starred dashboards section', () => {
|
it('should include starred dashboards section', () => {
|
||||||
expect(results[0].title).toBe('Starred Boards');
|
expect(results[0].title).toBe('Starred');
|
||||||
expect(results[0].items.length).toBe(1);
|
expect(results[0].items.length).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -108,30 +111,31 @@ describe('SearchSrv', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
backendSrvMock.search = jest
|
backendSrvMock.search = jest
|
||||||
.fn()
|
.fn()
|
||||||
.mockReturnValueOnce(Promise.resolve([
|
.mockReturnValueOnce(
|
||||||
{id: 1, title: 'starred and recent', isStarred: true},
|
Promise.resolve([
|
||||||
{id: 2, title: 'recent'}
|
{ id: 1, title: 'starred and recent', isStarred: true },
|
||||||
]))
|
{ id: 2, title: 'recent' },
|
||||||
.mockReturnValue(Promise.resolve([
|
])
|
||||||
{id: 1, title: 'starred and recent'}
|
)
|
||||||
]));
|
.mockReturnValue(
|
||||||
|
Promise.resolve([{ id: 1, title: 'starred and recent' }])
|
||||||
|
);
|
||||||
|
|
||||||
impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([1,2]);
|
impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([1, 2]);
|
||||||
return searchSrv.search({ query: '' }).then(res => {
|
return searchSrv.search({ query: '' }).then(res => {
|
||||||
results = res;
|
results = res;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not show starred in recent', () => {
|
it('should not show starred in recent', () => {
|
||||||
expect(results[1].title).toBe('Recent Boards');
|
expect(results[1].title).toBe('Recent');
|
||||||
expect(results[1].items[0].title).toBe('recent');
|
expect(results[1].items[0].title).toBe('recent');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show starred', () => {
|
it('should show starred', () => {
|
||||||
expect(results[0].title).toBe('Starred Boards');
|
expect(results[0].title).toBe('Starred');
|
||||||
expect(results[0].items[0].title).toBe('starred and recent');
|
expect(results[0].items[0].title).toBe('starred and recent');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with no query string and dashboards with folders returned', () => {
|
describe('with no query string and dashboards with folders returned', () => {
|
||||||
@@ -165,7 +169,7 @@ describe('SearchSrv', () => {
|
|||||||
id: 4,
|
id: 4,
|
||||||
folderId: 1,
|
folderId: 1,
|
||||||
},
|
},
|
||||||
]),
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
return searchSrv.search({ query: '' }).then(res => {
|
return searchSrv.search({ query: '' }).then(res => {
|
||||||
@@ -202,7 +206,7 @@ describe('SearchSrv', () => {
|
|||||||
folderId: 1,
|
folderId: 1,
|
||||||
folderTitle: 'folder1',
|
folderTitle: 'folder1',
|
||||||
},
|
},
|
||||||
]),
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
return searchSrv.search({ query: 'search' }).then(res => {
|
return searchSrv.search({ query: 'search' }).then(res => {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user