mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(data source variable): progress on data source as variable
This commit is contained in:
commit
7349427189
CHANGELOG.mdMakefileREADME.mdbuild.gocircle.ymlsystem.conf.js
docs/sources
package.jsonpkg
api
cmd/grafana-cli
models
plugins
services/sqlstore
public
app
core
features
dashboard
panel
all.jsmetrics_ds_selector.tsmetrics_panel_ctrl.tspanel_ctrl.ts
partials
query_ctrl.tsquery_editor_row.tsplugins
templating
partials
plugins
datasource
cloudwatch/partials
elasticsearch
grafana/partials
graphite
influxdb
opentsdb/partials
prometheus/partials
panel
dashlist
graph
pluginlist
singlestat
table
text
sass
_grafana.scss_variables.dark.scss_variables.light.scss
base
components
_drop.scss_dropdown.scss_gf-form.scss_panel_graph.scss_panel_singlestat.scss_panel_text.scss_query_editor.scss_query_part.scss
mixins
utils
test/specs
25
CHANGELOG.md
25
CHANGELOG.md
@ -1,3 +1,28 @@
|
||||
# 3.0.0-beta6 (unreleased)
|
||||
|
||||
### Enhancements
|
||||
* **Singlestat**: Support for gauges in singlestat panel. closes [#3688](https://github.com/grafana/grafana/pull/3688)
|
||||
|
||||
### Bug fixes
|
||||
* **InfluxDB 0.12**: Fixed issue templating and `show tag values` query only returning tags for first measurement, fixes [#4726](https://github.com/grafana/grafana/issues/4726)
|
||||
* **Templating**: Fixed issue with regex formating when matching multiple values, fixes [#4755](https://github.com/grafana/grafana/issues/4755)
|
||||
* **Templating**: Fixed issue with custom all value and escaping, fixes [#4736](https://github.com/grafana/grafana/issues/4736)
|
||||
* **Dashlist**: Fixed issue dashboard list panel and caching tags, fixes [#4768](https://github.com/grafana/grafana/issues/4768)
|
||||
* **Graph**: Fixed issue with unneeded scrollbar in legend for Firefox, fixes [#4760](https://github.com/grafana/grafana/issues/4760)
|
||||
* **Table panel**: Fixed issue table panel formating string array properties, fixes [#4791](https://github.com/grafana/grafana/issues/4791)
|
||||
* **grafana-cli**: Improve error message when failing to install plugins due to corrupt response, fixes [#4651](https://github.com/grafana/grafana/issues/4651)
|
||||
* **Singlestat**: Fixes prefix an postfix for gauges, fixes [#4812](https://github.com/grafana/grafana/issues/4812)
|
||||
* **Singlestat**: Fixes auto-refresh on change for some options, fixes [#4809](https://github.com/grafana/grafana/issues/4809)
|
||||
|
||||
### Breaking changes
|
||||
**Data Source Query Editors**: Issue [#3900](https://github.com/grafana/grafana/issues/3900)
|
||||
|
||||
Query editors have been updated to use the new form styles. External data source plugins needs to be
|
||||
updated to work. Sorry to introduce breaking change this late in beta phase. We wanted to get this change
|
||||
in before 3.0 stable is released so we don't have to break data sources in next release (3.1). If you are
|
||||
a data source plugin author and want help for how the new form styles work please ask for help in
|
||||
slack channel (link to slack channel in readme).
|
||||
|
||||
# 3.0.0-beta5 (2016-04-15)
|
||||
|
||||
### Bug fixes
|
||||
|
17
Makefile
Normal file
17
Makefile
Normal file
@ -0,0 +1,17 @@
|
||||
all: deps build
|
||||
|
||||
deps:
|
||||
go run build.go setup
|
||||
godep restore
|
||||
npm install
|
||||
|
||||
build:
|
||||
go run build.go build
|
||||
npm run build
|
||||
|
||||
test:
|
||||
godep go test -v ./pkg/...
|
||||
npm test
|
||||
|
||||
run:
|
||||
./bin/grafana-server
|
@ -103,8 +103,7 @@ npm (v2.5.0) and grunt (v0.4.5). Run the following:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm install -g grunt-cli
|
||||
grunt
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Recompile backend on source change
|
||||
@ -145,4 +144,3 @@ please [sign the CLA](http://docs.grafana.org/project/cla/)
|
||||
|
||||
Grafana is distributed under Apache 2.0 License.
|
||||
Work in progress Grafana 2.0 (with included Grafana backend)
|
||||
|
||||
|
2
build.go
2
build.go
@ -306,7 +306,7 @@ func ChangeWorkingDir(dir string) {
|
||||
}
|
||||
|
||||
func grunt(params ...string) {
|
||||
runPrint("./node_modules/grunt-cli/bin/grunt", params...)
|
||||
runPrint("./node_modules/.bin/grunt", params...)
|
||||
}
|
||||
|
||||
func setup() {
|
||||
|
@ -25,12 +25,12 @@ test:
|
||||
# Go test
|
||||
- godep go test -v ./pkg/...
|
||||
# js tests
|
||||
- ./node_modules/grunt-cli/bin/grunt test
|
||||
- npm test
|
||||
- npm run coveralls
|
||||
|
||||
deployment:
|
||||
master:
|
||||
branch: master
|
||||
owner: grafana
|
||||
commands:
|
||||
commands:
|
||||
- ./trigger_grafana_packer.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN}
|
||||
|
@ -191,7 +191,7 @@ Will return the home dashboard.
|
||||
|
||||
`GET /api/dashboards/tags`
|
||||
|
||||
Get all tabs of dashboards
|
||||
Get all tags of dashboards
|
||||
|
||||
**Example Request**:
|
||||
|
||||
|
@ -15,7 +15,7 @@ Grafana already have a strong community of contributors and plugin developers.
|
||||
By making it easier to develop and install plugins we hope that the community
|
||||
can grow even stronger and develop new plugins that we would never think about.
|
||||
|
||||
You can discover available plugins on [Grafana.net](http://grafana.net)
|
||||
You can discover available plugins on [Grafana.net](https://grafana.net)
|
||||
|
||||
|
||||
|
||||
|
@ -54,7 +54,7 @@
|
||||
"phantomjs-prebuilt": "^2.1.3",
|
||||
"reflect-metadata": "0.1.2",
|
||||
"rxjs": "5.0.0-beta.4",
|
||||
"sass-lint": "^1.5.0",
|
||||
"sass-lint": "^1.6.0",
|
||||
"systemjs": "0.19.24"
|
||||
},
|
||||
"engines": {
|
||||
@ -62,6 +62,7 @@
|
||||
"npm": "2.14.x"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "grunt",
|
||||
"test": "grunt test",
|
||||
"coveralls": "grunt karma:coveralls && rm -rf ./coverage"
|
||||
},
|
||||
|
@ -56,7 +56,7 @@ func init() {
|
||||
"HbaseBackupFailed", "MostRecentBackupDuration", "TimeSinceLastSuccessfulBackup"},
|
||||
"AWS/ES": {"ClusterStatus.green", "ClusterStatus.yellow", "ClusterStatus.red", "Nodes", "SearchableDocuments", "DeletedDocuments", "CPUUtilization", "FreeStorageSpace", "JVMMemoryPressure", "AutomatedSnapshotFailure", "MasterCPUUtilization", "MasterFreeStorageSpace", "MasterJVMMemoryPressure", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "DiskQueueLength", "ReadIOPS", "WriteIOPS"},
|
||||
"AWS/Events": {"Invocations", "FailedInvocations", "TriggeredRules", "MatchedEvents", "ThrottledRules"},
|
||||
"AWS/Kinesis": {"PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Success", "PutRecords.Bytes", "PutRecords.Latency", "PutRecords.Records", "PutRecords.Success", "IncomingBytes", "IncomingRecords", "GetRecords.Bytes", "GetRecords.IteratorAgeMilliseconds", "GetRecords.Latency", "GetRecords.Success"},
|
||||
"AWS/Kinesis": {"GetRecords.Bytes", "GetRecords.IteratorAge", "GetRecords.IteratorAgeMilliseconds", "GetRecords.Latency", "GetRecords.Records", "GetRecords.Success", "IncomingBytes", "IncomingRecords", "PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Success", "PutRecords.Bytes", "PutRecords.Latency", "PutRecords.Records", "PutRecords.Success", "ReadProvisionedThroughputExceeded", "WriteProvisionedThroughputExceeded", "IteratorAgeMilliseconds", "OutgoingBytes", "OutgoingRecords"},
|
||||
"AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles"},
|
||||
"AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"},
|
||||
"AWS/ML": {"PredictCount", "PredictFailureCount"},
|
||||
@ -88,7 +88,7 @@ func init() {
|
||||
"AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"},
|
||||
"AWS/ES": {},
|
||||
"AWS/Events": {"RuleName"},
|
||||
"AWS/Kinesis": {"StreamName"},
|
||||
"AWS/Kinesis": {"StreamName", "ShardID"},
|
||||
"AWS/Lambda": {"FunctionName"},
|
||||
"AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"},
|
||||
"AWS/ML": {"MLModelId", "RequestMode"},
|
||||
|
@ -21,6 +21,10 @@ func GetSharingOptions(c *middleware.Context) {
|
||||
}
|
||||
|
||||
func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapshotCommand) {
|
||||
if cmd.Name == "" {
|
||||
cmd.Name = "Unnamed snapshot"
|
||||
}
|
||||
|
||||
if cmd.External {
|
||||
// external snapshot ref requires key and delete key
|
||||
if cmd.Key == "" || cmd.DeleteKey == "" {
|
||||
|
@ -41,7 +41,6 @@ func NewReverseProxy(ds *m.DataSource, proxyPath string, targetUrl *url.URL) *ht
|
||||
req.URL.RawQuery = reqQueryVals.Encode()
|
||||
} else if ds.Type == m.DS_INFLUXDB {
|
||||
req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
|
||||
reqQueryVals.Add("db", ds.Database)
|
||||
req.URL.RawQuery = reqQueryVals.Encode()
|
||||
if !ds.BasicAuth {
|
||||
req.Header.Del("Authorization")
|
||||
|
@ -48,6 +48,6 @@ func (slice PluginList) Swap(i, j int) {
|
||||
type ImportDashboardCommand struct {
|
||||
PluginId string `json:"pluginId"`
|
||||
Path string `json:"path"`
|
||||
Reinstall bool `json:"reinstall"`
|
||||
Overwrite bool `json:"overwrite"`
|
||||
Inputs []plugins.ImportDashboardInput `json:"inputs"`
|
||||
}
|
||||
|
@ -103,6 +103,10 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
||||
}
|
||||
|
||||
for _, include := range plugin.Includes {
|
||||
if !c.HasUserRole(include.Role) {
|
||||
continue
|
||||
}
|
||||
|
||||
if include.Type == "page" && include.AddToNav {
|
||||
link := &dtos.NavLink{
|
||||
Url: setting.AppSubUrl + "/plugins/" + plugin.Id + "/page/" + include.Slug,
|
||||
@ -110,6 +114,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
||||
}
|
||||
appLink.Children = append(appLink.Children, link)
|
||||
}
|
||||
|
||||
if include.Type == "dashboard" && include.AddToNav {
|
||||
link := &dtos.NavLink{
|
||||
Url: setting.AppSubUrl + "/dashboard/db/" + include.Slug,
|
||||
@ -124,7 +129,9 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
||||
appLink.Children = append(appLink.Children, &dtos.NavLink{Text: "Plugin Config", Icon: "fa fa-cog", Url: setting.AppSubUrl + "/plugins/" + plugin.Id + "/edit"})
|
||||
}
|
||||
|
||||
data.MainNavLinks = append(data.MainNavLinks, appLink)
|
||||
if len(appLink.Children) > 0 {
|
||||
data.MainNavLinks = append(data.MainNavLinks, appLink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,11 +156,12 @@ func GetPluginReadme(c *middleware.Context) Response {
|
||||
func ImportDashboard(c *middleware.Context, apiCmd dtos.ImportDashboardCommand) Response {
|
||||
|
||||
cmd := plugins.ImportDashboardCommand{
|
||||
OrgId: c.OrgId,
|
||||
UserId: c.UserId,
|
||||
PluginId: apiCmd.PluginId,
|
||||
Path: apiCmd.Path,
|
||||
Inputs: apiCmd.Inputs,
|
||||
OrgId: c.OrgId,
|
||||
UserId: c.UserId,
|
||||
PluginId: apiCmd.PluginId,
|
||||
Path: apiCmd.Path,
|
||||
Inputs: apiCmd.Inputs,
|
||||
Overwrite: apiCmd.Overwrite,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/fatih/color"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
|
||||
)
|
||||
|
||||
@ -12,7 +13,7 @@ func runCommand(command func(commandLine CommandLine) error) func(context *cli.C
|
||||
|
||||
cmd := &contextCommandLine{context}
|
||||
if err := command(cmd); err != nil {
|
||||
log.Error("\nError: ")
|
||||
log.Errorf("\n%s: ", color.RedString("Error"))
|
||||
log.Errorf("%s\n\n", err)
|
||||
|
||||
cmd.ShowHelp()
|
||||
|
@ -126,11 +126,16 @@ func downloadFile(pluginName, filePath, url string) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
retryCount++
|
||||
if retryCount == 1 {
|
||||
log.Debug("\nFailed downloading. Will retry once.\n")
|
||||
downloadFile(pluginName, filePath, url)
|
||||
if retryCount < 3 {
|
||||
fmt.Println("Failed downloading. Will retry once.")
|
||||
err = downloadFile(pluginName, filePath, url)
|
||||
} else {
|
||||
panic(r)
|
||||
failure := fmt.Sprintf("%v", r)
|
||||
if failure == "runtime error: makeslice: len out of range" {
|
||||
err = fmt.Errorf("Corrupt http response from source. Please try again.\n")
|
||||
} else {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
@ -164,14 +169,14 @@ func downloadFile(pluginName, filePath, url string) (err error) {
|
||||
return fmt.Errorf(permissionsDeniedMessage, newFile)
|
||||
}
|
||||
|
||||
defer dst.Close()
|
||||
src, err := zf.Open()
|
||||
if err != nil {
|
||||
log.Errorf("%v", err)
|
||||
log.Errorf("Failed to extract file: %v", err)
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
io.Copy(dst, src)
|
||||
dst.Close()
|
||||
src.Close()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ package commands
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
|
||||
"fmt"
|
||||
m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
|
||||
services "github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
|
||||
)
|
||||
@ -15,22 +15,17 @@ func removeCommand(c CommandLine) error {
|
||||
pluginPath := c.GlobalString("pluginsDir")
|
||||
localPlugins := getPluginss(pluginPath)
|
||||
|
||||
log.Info("remove!\n")
|
||||
|
||||
plugin := c.Args().First()
|
||||
log.Info("plugin: " + plugin + "\n")
|
||||
if plugin == "" {
|
||||
return errors.New("Missing plugin parameter")
|
||||
}
|
||||
|
||||
log.Infof("plugins : \n%v\n", localPlugins)
|
||||
|
||||
for _, p := range localPlugins {
|
||||
if p.Id == c.Args().First() {
|
||||
log.Infof("removing plugin %s", p.Id)
|
||||
removePlugin(pluginPath, p.Id)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return fmt.Errorf("Could not find plugin named %s", c.Args().First())
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var version = "master"
|
||||
@ -18,7 +17,7 @@ func getGrafanaPluginDir() string {
|
||||
defaultNix := "/var/lib/grafana/plugins"
|
||||
|
||||
if currentOS == "windows" {
|
||||
return "C:\\opt\\grafana\\plugins"
|
||||
return "../data/plugins"
|
||||
}
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
@ -29,16 +28,17 @@ func getGrafanaPluginDir() string {
|
||||
}
|
||||
|
||||
if isDevenvironment(pwd) {
|
||||
return "../../../data/plugins"
|
||||
return "../data/plugins"
|
||||
}
|
||||
|
||||
return defaultNix
|
||||
}
|
||||
|
||||
func isDevenvironment(pwd string) bool {
|
||||
// if grafana-cli is executed from the cmd folder we can assume
|
||||
// if ../conf/defaults.ini exists, grafana is not installed as package
|
||||
// that its in development environment.
|
||||
return strings.HasSuffix(pwd, "/pkg/cmd/grafana-cli")
|
||||
_, err := os.Stat("../conf/defaults.ini")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -45,7 +45,7 @@ type DashboardSnapshotDTO struct {
|
||||
|
||||
type CreateDashboardSnapshotCommand struct {
|
||||
Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
|
||||
Name string `json:"name" binding:"Required"`
|
||||
Name string `json:"name"`
|
||||
Expires int64 `json:"expires"`
|
||||
|
||||
// these are passed when storing an external snapshot ref
|
||||
|
@ -1,7 +1,9 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -37,6 +39,26 @@ func (r RoleType) Includes(other RoleType) bool {
|
||||
return r == other
|
||||
}
|
||||
|
||||
func (r *RoleType) UnmarshalJSON(data []byte) error {
|
||||
var str string
|
||||
err := json.Unmarshal(data, &str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*r = RoleType(str)
|
||||
|
||||
if (*r).IsValid() == false {
|
||||
if (*r) != "" {
|
||||
return errors.New(fmt.Sprintf("JSON validation error: invalid role value: %s", *r))
|
||||
}
|
||||
|
||||
*r = ROLE_VIEWER
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type OrgUser struct {
|
||||
Id int64
|
||||
OrgId int64
|
||||
|
@ -11,12 +11,13 @@ import (
|
||||
)
|
||||
|
||||
type ImportDashboardCommand struct {
|
||||
Path string `json:"string"`
|
||||
Inputs []ImportDashboardInput `json:"inputs"`
|
||||
Path string
|
||||
Inputs []ImportDashboardInput
|
||||
Overwrite bool
|
||||
|
||||
OrgId int64 `json:"-"`
|
||||
UserId int64 `json:"-"`
|
||||
PluginId string `json:"-"`
|
||||
OrgId int64
|
||||
UserId int64
|
||||
PluginId string
|
||||
Result *PluginDashboardInfoDTO
|
||||
}
|
||||
|
||||
@ -67,6 +68,7 @@ func ImportDashboard(cmd *ImportDashboardCommand) error {
|
||||
Dashboard: generatedDash,
|
||||
OrgId: cmd.OrgId,
|
||||
UserId: cmd.UserId,
|
||||
Overwrite: cmd.Overwrite,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&saveCmd); err != nil {
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@ -69,6 +69,12 @@ func (pb *PluginBase) registerPlugin(pluginDir string) error {
|
||||
pb.Dependencies.GrafanaVersion = "*"
|
||||
}
|
||||
|
||||
for _, include := range pb.Includes {
|
||||
if include.Role == "" {
|
||||
include.Role = m.RoleType(m.ROLE_VIEWER)
|
||||
}
|
||||
}
|
||||
|
||||
pb.PluginDir = pluginDir
|
||||
Plugins[pb.Id] = pb
|
||||
return nil
|
||||
@ -80,14 +86,14 @@ type PluginDependencies struct {
|
||||
}
|
||||
|
||||
type PluginInclude struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Type string `json:"type"`
|
||||
Component string `json:"component"`
|
||||
Role models.RoleType `json:"role"`
|
||||
AddToNav bool `json:"addToNav"`
|
||||
DefaultNav bool `json:"defaultNav"`
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Type string `json:"type"`
|
||||
Component string `json:"component"`
|
||||
Role m.RoleType `json:"role"`
|
||||
AddToNav bool `json:"addToNav"`
|
||||
DefaultNav bool `json:"defaultNav"`
|
||||
Slug string `json:"slug"`
|
||||
|
||||
Id string `json:"-"`
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ func GetDashboardTags(query *m.GetDashboardTagsQuery) error {
|
||||
func DeleteDashboard(cmd *m.DeleteDashboardCommand) error {
|
||||
return inTransaction2(func(sess *session) error {
|
||||
dashboard := m.Dashboard{Slug: cmd.Slug, OrgId: cmd.OrgId}
|
||||
has, err := x.Get(&dashboard)
|
||||
has, err := sess.Get(&dashboard)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has == false {
|
||||
|
@ -61,7 +61,6 @@ func UpdatePluginSetting(cmd *m.UpdatePluginSettingCmd) error {
|
||||
for key, data := range cmd.SecureJsonData {
|
||||
pluginSetting.SecureJsonData[key] = util.Encrypt([]byte(data), setting.SecretKey)
|
||||
}
|
||||
pluginSetting.SecureJsonData = cmd.GetEncryptedJsonData()
|
||||
pluginSetting.Updated = time.Now()
|
||||
pluginSetting.Enabled = cmd.Enabled
|
||||
pluginSetting.JsonData = cmd.JsonData
|
||||
|
@ -9,10 +9,10 @@ function (_, $, coreModule) {
|
||||
coreModule.default.directive('dropdownTypeahead', function($compile) {
|
||||
|
||||
var inputTemplate = '<input type="text"'+
|
||||
' class="tight-form-input input-medium tight-form-input"' +
|
||||
' class="gf-form-input input-medium tight-form-input"' +
|
||||
' spellcheck="false" style="display:none"></input>';
|
||||
|
||||
var buttonTemplate = '<a class="tight-form-item tight-form-func dropdown-toggle"' +
|
||||
var buttonTemplate = '<a class="gf-form-label tight-form-func dropdown-toggle"' +
|
||||
' tabindex="1" gf-dropdown="menuItems" data-toggle="dropdown"' +
|
||||
' data-placement="top"><i class="fa fa-plus"></i></a>';
|
||||
|
||||
|
@ -8,10 +8,13 @@ function (_, $, coreModule) {
|
||||
|
||||
coreModule.default.directive('metricSegment', function($compile, $sce) {
|
||||
var inputTemplate = '<input type="text" data-provide="typeahead" ' +
|
||||
' class="tight-form-clear-input input-medium"' +
|
||||
' class="gf-form-input input-medium"' +
|
||||
' spellcheck="false" style="display:none"></input>';
|
||||
|
||||
var buttonTemplate = '<a class="tight-form-item" ng-class="segment.cssClass" ' +
|
||||
var linkTemplate = '<a class="gf-form-label" ng-class="segment.cssClass" ' +
|
||||
'tabindex="1" give-focus="segment.focus" ng-bind-html="segment.html"></a>';
|
||||
|
||||
var selectTemplate = '<a class="gf-form-input gf-form-input--dropdown" ng-class="segment.cssClass" ' +
|
||||
'tabindex="1" give-focus="segment.focus" ng-bind-html="segment.html"></a>';
|
||||
|
||||
return {
|
||||
@ -20,9 +23,9 @@ function (_, $, coreModule) {
|
||||
getOptions: "&",
|
||||
onChange: "&",
|
||||
},
|
||||
link: function($scope, elem) {
|
||||
link: function($scope, elem, attrs) {
|
||||
var $input = $(inputTemplate);
|
||||
var $button = $(buttonTemplate);
|
||||
var $button = $(attrs.styleMode === 'select' ? selectTemplate : linkTemplate);
|
||||
var segment = $scope.segment;
|
||||
var options = null;
|
||||
var cancelBlur = null;
|
||||
|
@ -206,9 +206,15 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
||||
});
|
||||
|
||||
$compile(child)(scope);
|
||||
|
||||
elem.empty();
|
||||
elem.append(child);
|
||||
|
||||
// let a binding digest cycle complete before adding to dom
|
||||
setTimeout(function() {
|
||||
elem.append(child);
|
||||
scope.$apply(function() {
|
||||
scope.$broadcast('refresh');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function registerPluginComponent(scope, elem, attrs, componentInfo) {
|
||||
|
@ -25,7 +25,6 @@ export class ContextSrv {
|
||||
isGrafanaAdmin: any;
|
||||
isEditor: any;
|
||||
sidemenu: any;
|
||||
lightTheme: any;
|
||||
|
||||
constructor() {
|
||||
this.pinned = store.getBool('grafana.sidemenu.pinned', false);
|
||||
@ -41,7 +40,6 @@ export class ContextSrv {
|
||||
}
|
||||
|
||||
this.version = config.buildInfo.version;
|
||||
this.lightTheme = false;
|
||||
this.user = new User();
|
||||
this.isSignedIn = this.user.isSignedIn;
|
||||
this.isGrafanaAdmin = this.user.isGrafanaAdmin;
|
||||
|
@ -32,6 +32,8 @@ export default class TableModel {
|
||||
if (options.desc) {
|
||||
this.rows.reverse();
|
||||
this.columns[options.col].desc = true;
|
||||
} else {
|
||||
this.columns[options.col].desc = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ export class Emitter {
|
||||
this.emitter.on(name, handler);
|
||||
|
||||
if (scope) {
|
||||
scope.$on('$destroy', function() {
|
||||
scope.$on('$destroy', () => {
|
||||
this.emitter.off(name, handler);
|
||||
});
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ function (angular, _, $) {
|
||||
self.update(payload);
|
||||
});
|
||||
|
||||
$scope.onAppEvent('panel-instantiated', function(evt, payload) {
|
||||
$scope.onAppEvent('panel-initialized', function(evt, payload) {
|
||||
self.registerPanel(payload.scope);
|
||||
});
|
||||
|
||||
|
@ -5,4 +5,5 @@ define([
|
||||
'./query_ctrl',
|
||||
'./panel_editor_tab',
|
||||
'./query_editor_row',
|
||||
'./metrics_ds_selector',
|
||||
], function () {});
|
||||
|
110
public/app/features/panel/metrics_ds_selector.ts
Normal file
110
public/app/features/panel/metrics_ds_selector.ts
Normal file
@ -0,0 +1,110 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
|
||||
var module = angular.module('grafana.directives');
|
||||
|
||||
var template = `
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label">
|
||||
<i class="icon-gf icon-gf-datasource"></i>
|
||||
</label>
|
||||
<label class="gf-form-label">
|
||||
Panel data source
|
||||
</label>
|
||||
|
||||
<metric-segment segment="ctrl.dsSegment" style-mode="select"
|
||||
get-options="ctrl.getOptions()"
|
||||
on-change="ctrl.datasourceChanged()"></metric-segment>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--offset-1">
|
||||
<button class="btn btn-inverse gf-form-btn" ng-click="ctrl.addDataQuery()" ng-hide="ctrl.current.meta.mixed">
|
||||
<i class="fa fa-plus"></i>
|
||||
Add query
|
||||
</button>
|
||||
|
||||
<div class="dropdown" ng-if="ctrl.current.meta.mixed">
|
||||
<button class="btn btn-inverse dropdown-toggle gf-form-btn" data-toggle="dropdown">
|
||||
Add Query <span class="fa fa-caret-down"></span>
|
||||
</button>
|
||||
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li ng-repeat="datasource in ctrl.datasources" role="menuitem" ng-hide="datasource.meta.builtIn">
|
||||
<a ng-click="ctrl.addDataQuery(datasource);">{{datasource.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
export class MetricsDsSelectorCtrl {
|
||||
dsSegment: any;
|
||||
dsName: string;
|
||||
panelCtrl: any;
|
||||
datasources: any[];
|
||||
current: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private uiSegmentSrv, datasourceSrv) {
|
||||
this.datasources = datasourceSrv.getMetricSources();
|
||||
|
||||
var dsValue = this.panelCtrl.panel.datasource || null;
|
||||
|
||||
for (let ds of this.datasources) {
|
||||
if (ds.value === dsValue) {
|
||||
this.current = ds;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.current) {
|
||||
this.current = {name: dsValue + ' not found', value: null};
|
||||
}
|
||||
|
||||
this.dsSegment = uiSegmentSrv.newSegment(this.current.name);
|
||||
}
|
||||
|
||||
getOptions() {
|
||||
return Promise.resolve(this.datasources.map(value => {
|
||||
return this.uiSegmentSrv.newSegment(value.name);
|
||||
}));
|
||||
}
|
||||
|
||||
datasourceChanged() {
|
||||
var ds = _.findWhere(this.datasources, {name: this.dsSegment.value});
|
||||
if (ds) {
|
||||
this.current = ds;
|
||||
this.panelCtrl.setDatasource(ds);
|
||||
}
|
||||
}
|
||||
|
||||
addDataQuery(datasource) {
|
||||
var target: any = {isNew: true};
|
||||
|
||||
if (datasource) {
|
||||
target.datasource = datasource.name;
|
||||
}
|
||||
|
||||
this.panelCtrl.panel.targets.push(target);
|
||||
}
|
||||
}
|
||||
|
||||
module.directive('metricsDsSelector', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
controller: MetricsDsSelectorCtrl,
|
||||
bindToController: true,
|
||||
controllerAs: 'ctrl',
|
||||
transclude: true,
|
||||
scope: {
|
||||
panelCtrl: "="
|
||||
}
|
||||
};
|
||||
});
|
@ -28,7 +28,6 @@ class MetricsPanelCtrl extends PanelCtrl {
|
||||
resolution: any;
|
||||
timeInfo: any;
|
||||
skipDataOnInit: boolean;
|
||||
datasources: any[];
|
||||
dataStream: any;
|
||||
dataSubscription: any;
|
||||
|
||||
@ -53,13 +52,6 @@ class MetricsPanelCtrl extends PanelCtrl {
|
||||
private onInitMetricsPanelEditMode() {
|
||||
this.addEditorTab('Metrics', 'public/app/partials/metrics.html');
|
||||
this.addEditorTab('Time range', 'public/app/features/panel/partials/panelTime.html');
|
||||
this.datasources = this.datasourceSrv.getMetricSources();
|
||||
|
||||
// find current
|
||||
var current = _.findWhere(this.datasources, {value: this.panel.datasource});
|
||||
if (current) {
|
||||
this.datasourceName = current.name;
|
||||
}
|
||||
}
|
||||
|
||||
private onMetricsPanelRefresh() {
|
||||
@ -257,16 +249,6 @@ class MetricsPanelCtrl extends PanelCtrl {
|
||||
this.datasource = null;
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
addDataQuery(datasource) {
|
||||
var target: any = {};
|
||||
|
||||
if (datasource) {
|
||||
target.datasource = datasource.name;
|
||||
}
|
||||
|
||||
this.panel.targets.push(target);
|
||||
}
|
||||
}
|
||||
|
||||
export {MetricsPanelCtrl};
|
||||
|
@ -44,15 +44,15 @@ export class PanelCtrl {
|
||||
this.pluginName = plugin.name;
|
||||
}
|
||||
|
||||
$scope.$on("refresh", () => this.refresh());
|
||||
$scope.$on("render", () => this.render());
|
||||
$scope.$on("refresh", this.refresh.bind(this));
|
||||
$scope.$on("render", this.render.bind(this));
|
||||
$scope.$on("$destroy", () => this.events.emit('panel-teardown'));
|
||||
}
|
||||
|
||||
init() {
|
||||
this.publishAppEvent('panel-instantiated', {scope: this.$scope});
|
||||
this.calculatePanelHeight();
|
||||
this.refresh();
|
||||
this.publishAppEvent('panel-initialized', {scope: this.$scope});
|
||||
this.events.emit('panel-initialized');
|
||||
}
|
||||
|
||||
renderingCompleted() {
|
||||
|
@ -1,4 +1,63 @@
|
||||
<div class="tight-form">
|
||||
|
||||
<div class="gf-form-query">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label gf-form-query-letter-cell">
|
||||
<a class="pointer" tabindex="1" ng-click="ctrl.toggleCollapse()">
|
||||
<span ng-class="{muted: !ctrl.canCollapse}" class="gf-form-query-letter-cell-carret">
|
||||
<i class="fa fa-caret-down" ng-hide="ctrl.collapsed"></i>
|
||||
<i class="fa fa-caret-right" ng-show="ctrl.collapsed"></i>
|
||||
</span>
|
||||
<span class="gf-form-query-letter-cell-letter">{{ctrl.target.refId}}</span>
|
||||
<em class="gf-form-query-letter-cell-ds" ng-show="ctrl.target.datasource">({{ctrl.target.datasource}})</em>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-query-content gf-form-query-content--collapsed" ng-if="ctrl.collapsed">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label pointer gf-form-label--grow" ng-click="ctrl.toggleCollapse()">
|
||||
{{ctrl.collapsedText}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-transclude class="gf-form-query-content" ng-if="!ctrl.collapsed">
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label dropdown">
|
||||
<a class="pointer dropdown-toggle" data-toggle="dropdown" tabindex="1">
|
||||
<i class="fa fa-bars"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right" role="menu">
|
||||
<li role="menuitem" ng-if="ctrl.hasTextEditMode">
|
||||
<a tabindex="1" ng-click="ctrl.toggleEditorMode()">Toggle Edit Mode</a>
|
||||
</li>
|
||||
<li role="menuitem">
|
||||
<a tabindex="1" ng-click="ctrl.duplicateQuery()">Duplicate</a>
|
||||
</li>
|
||||
<li role="menuitem">
|
||||
<a tabindex="1" ng-click="ctrl.moveQuery(-1)">Move up</a>
|
||||
</li>
|
||||
<li role="menuitem">
|
||||
<a tabindex="1" ng-click="ctrl.moveQuery(1)">Move down</a>
|
||||
</li>
|
||||
</ul>
|
||||
</label>
|
||||
<label class="gf-form-label">
|
||||
<a ng-click="ctrl.toggleHideQuery()" role="menuitem">
|
||||
<i class="fa fa-eye"></i>
|
||||
</a>
|
||||
</label>
|
||||
<label class="gf-form-label">
|
||||
<a class="pointer" tabindex="1" ng-click="ctrl.removeQuery(ctrl.target)">
|
||||
<i class="fa fa-trash"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form" ng-if="false">
|
||||
<ul class="tight-form-list pull-right">
|
||||
<li ng-show="ctrl.error" class="tight-form-item">
|
||||
<a bs-tooltip="ctrl.error" style="color: rgb(229, 189, 28)" role="menuitem">
|
||||
|
@ -13,45 +13,11 @@ export class QueryCtrl {
|
||||
|
||||
constructor(public $scope, private $injector) {
|
||||
this.panel = this.panelCtrl.panel;
|
||||
|
||||
if (!this.target.refId) {
|
||||
this.target.refId = this.getNextQueryLetter();
|
||||
}
|
||||
}
|
||||
|
||||
getNextQueryLetter() {
|
||||
var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
|
||||
return _.find(letters, refId => {
|
||||
return _.every(this.panel.targets, function(other) {
|
||||
return other.refId !== refId;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
removeQuery() {
|
||||
this.panel.targets = _.without(this.panel.targets, this.target);
|
||||
this.panelCtrl.refresh();
|
||||
};
|
||||
|
||||
duplicateQuery() {
|
||||
var clone = angular.copy(this.target);
|
||||
clone.refId = this.getNextQueryLetter();
|
||||
this.panel.targets.push(clone);
|
||||
}
|
||||
|
||||
moveQuery(direction) {
|
||||
var index = _.indexOf(this.panel.targets, this.target);
|
||||
_.move(this.panel.targets, index, index + direction);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.panelCtrl.refresh();
|
||||
}
|
||||
|
||||
toggleHideQuery() {
|
||||
this.target.hide = !this.target.hide;
|
||||
this.panelCtrl.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,115 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import angular from 'angular';
|
||||
import $ from 'jquery';
|
||||
import _ from 'lodash';
|
||||
|
||||
var module = angular.module('grafana.directives');
|
||||
|
||||
export class QueryRowCtrl {
|
||||
collapsedText: string;
|
||||
canCollapse: boolean;
|
||||
getCollapsedText: any;
|
||||
target: any;
|
||||
queryCtrl: any;
|
||||
panelCtrl: any;
|
||||
panel: any;
|
||||
collapsed: any;
|
||||
|
||||
constructor() {
|
||||
this.panelCtrl = this.queryCtrl.panelCtrl;
|
||||
this.target = this.queryCtrl.target;
|
||||
this.panel = this.panelCtrl.panel;
|
||||
|
||||
if (!this.target.refId) {
|
||||
this.target.refId = this.getNextQueryLetter();
|
||||
}
|
||||
|
||||
this.toggleCollapse(true);
|
||||
if (this.target.isNew) {
|
||||
delete this.target.isNew;
|
||||
this.toggleCollapse(false);
|
||||
}
|
||||
}
|
||||
|
||||
toggleHideQuery() {
|
||||
this.target.hide = !this.target.hide;
|
||||
this.panelCtrl.refresh();
|
||||
}
|
||||
|
||||
getNextQueryLetter() {
|
||||
var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
|
||||
return _.find(letters, refId => {
|
||||
return _.every(this.panel.targets, function(other) {
|
||||
return other.refId !== refId;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
toggleCollapse(init) {
|
||||
if (!this.canCollapse) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.panelCtrl.__collapsedQueryCache) {
|
||||
this.panelCtrl.__collapsedQueryCache = {};
|
||||
}
|
||||
|
||||
if (init) {
|
||||
this.collapsed = this.panelCtrl.__collapsedQueryCache[this.target.refId] !== false;
|
||||
} else {
|
||||
this.collapsed = !this.collapsed;
|
||||
this.panelCtrl.__collapsedQueryCache[this.target.refId] = this.collapsed;
|
||||
}
|
||||
|
||||
try {
|
||||
this.collapsedText = this.queryCtrl.getCollapsedText();
|
||||
} catch (e) {
|
||||
var err = e.message || e.toString();
|
||||
this.collapsedText = 'Error: ' + err;
|
||||
}
|
||||
}
|
||||
|
||||
toggleEditorMode() {
|
||||
if (this.canCollapse && this.collapsed) {
|
||||
this.collapsed = false;
|
||||
}
|
||||
|
||||
this.queryCtrl.toggleEditorMode();
|
||||
}
|
||||
|
||||
removeQuery() {
|
||||
delete this.panelCtrl.__collapsedQueryCache[this.target.refId];
|
||||
this.panel.targets = _.without(this.panel.targets, this.target);
|
||||
this.panelCtrl.refresh();
|
||||
}
|
||||
|
||||
duplicateQuery() {
|
||||
var clone = angular.copy(this.target);
|
||||
clone.refId = this.getNextQueryLetter();
|
||||
this.panel.targets.push(clone);
|
||||
}
|
||||
|
||||
moveQuery(direction) {
|
||||
var index = _.indexOf(this.panel.targets, this.target);
|
||||
_.move(this.panel.targets, index, index + direction);
|
||||
}
|
||||
}
|
||||
|
||||
/** @ngInject **/
|
||||
function queryEditorRowDirective() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
controller: QueryRowCtrl,
|
||||
bindToController: true,
|
||||
controllerAs: "ctrl",
|
||||
templateUrl: 'public/app/features/panel/partials/query_editor_row.html',
|
||||
transclude: true,
|
||||
scope: {ctrl: "="},
|
||||
scope: {
|
||||
queryCtrl: "=",
|
||||
canCollapse: "=",
|
||||
hasTextEditMode: "=",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -98,9 +98,7 @@ export class DataSourceEditCtrl {
|
||||
|
||||
this.datasourceSrv.get(this.current.name).then(datasource => {
|
||||
if (!datasource.testDatasource) {
|
||||
this.testing.message = 'Data source does not support test connection feature.';
|
||||
this.testing.status = 'warning';
|
||||
this.testing.title = 'Unknown';
|
||||
delete this.testing;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -118,7 +116,9 @@ export class DataSourceEditCtrl {
|
||||
}
|
||||
});
|
||||
}).finally(() => {
|
||||
this.testing.done = true;
|
||||
if (this.testing) {
|
||||
this.testing.done = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -15,16 +15,16 @@
|
||||
</td>
|
||||
<td>
|
||||
v{{dash.revision}}
|
||||
</td>
|
||||
<td ng-if="dash.installed">
|
||||
Imported v{{dash.installedRevision}}
|
||||
<span ng-if="dash.installed">
|
||||
(Imported v{{dash.installedRevision}})
|
||||
<span>
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
<button class="btn btn-secondary" ng-click="ctrl.import(dash, false)" ng-show="!dash.installed">
|
||||
Import
|
||||
</button>
|
||||
<button class="btn btn-secondary" ng-click="ctrl.import(dash, true)" ng-show="dash.installed">
|
||||
Re-Import
|
||||
Update
|
||||
</button>
|
||||
<button class="btn btn-danger" ng-click="ctrl.remove(dash)" ng-show="dash.installed">
|
||||
Delete
|
||||
|
@ -43,11 +43,11 @@ export class DashImportListCtrl {
|
||||
});
|
||||
}
|
||||
|
||||
import(dash, reinstall) {
|
||||
import(dash, overwrite) {
|
||||
var installCmd = {
|
||||
pluginId: this.plugin.id,
|
||||
path: dash.path,
|
||||
reinstall: reinstall,
|
||||
overwrite: overwrite,
|
||||
inputs: []
|
||||
};
|
||||
|
||||
|
@ -53,7 +53,7 @@
|
||||
</plugin-component>
|
||||
</rebuild-on-change>
|
||||
|
||||
<div ng-if="ctrl.testing" style="margin-top: 25px">
|
||||
<div ng-if="ctrl.testing" class="gf-form-group">
|
||||
<h5 ng-show="!ctrl.testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>
|
||||
<div class="alert-{{ctrl.testing.status}} alert">
|
||||
<div class="alert-title">{{ctrl.testing.title}}</div>
|
||||
|
@ -14,8 +14,8 @@
|
||||
<div class="gf-form-group">
|
||||
<p>Type the following on the command line to update {{plugin.name}}.</p>
|
||||
<pre><code>grafana-cli plugins update {{plugin.id}}</code></pre>
|
||||
<span class="small">Check out {{plugin.name}} on <a href="http://grafana/net/plugins/{{plugin.id}}">Grafana.net</a> for README and changelog. If you do not have access to the command line, ask your Grafana administator.</span>
|
||||
<span class="small">Check out {{plugin.name}} on <a href="https://grafana.net/plugins/{{plugin.id}}">Grafana.net</a> for README and changelog. If you do not have access to the command line, ask your Grafana administator.</span>
|
||||
</div>
|
||||
<p class="pluginlist-none-installed code--line"><img class="pluginlist-inline-logo" src="public/img/grafana_icon.svg"><strong>Pro tip</strong>: To update all plugins at once, type <code class="code--small">grafana-cli plugins update-all</code> on the command line.</div>
|
||||
<p class="pluginlist-none-installed"><img class="pluginlist-inline-logo" src="public/img/grafana_icon.svg"><strong>Pro tip</strong>: To update all plugins at once, type <code class="code--small">grafana-cli plugins update-all</code> on the command line.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -57,7 +57,7 @@ function (angular, _) {
|
||||
}
|
||||
|
||||
var escapedValues = _.map(value, regexEscape);
|
||||
return escapedValues.join('|');
|
||||
return '(' + escapedValues.join('|') + ')';
|
||||
}
|
||||
case "lucene": {
|
||||
if (typeof value === 'string') {
|
||||
@ -152,6 +152,10 @@ function (angular, _) {
|
||||
value = variable.current.value;
|
||||
if (self.isAllValue(value)) {
|
||||
value = self.getAllValue(variable);
|
||||
// skip formating of custom all values
|
||||
if (variable.allValue) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
var res = self.formatValue(value, format, variable);
|
||||
|
@ -323,17 +323,10 @@ function (angular, _, kbn) {
|
||||
options[value] = {text: text, value: value};
|
||||
}
|
||||
|
||||
return _.map(_.keys(options).sort(), function(key) {
|
||||
return options[key];
|
||||
});
|
||||
return _.sortBy(options, 'text');
|
||||
};
|
||||
|
||||
this.addAllOption = function(variable) {
|
||||
if (variable.allValue) {
|
||||
variable.options.unshift({text: 'All', value: variable.allValue});
|
||||
return;
|
||||
}
|
||||
|
||||
variable.options.unshift({text: 'All', value: "$__all"});
|
||||
};
|
||||
|
||||
|
@ -1,56 +1,19 @@
|
||||
<div class="editor-row">
|
||||
|
||||
<div class="tight-form-container">
|
||||
<div ng-repeat="target in ctrl.panel.targets" ng-class="{'tight-form-disabled': target.hide}">
|
||||
<rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
|
||||
<plugin-component type="query-ctrl">
|
||||
</plugin-component>
|
||||
</rebuild-on-change>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin: 20px 0 0 0">
|
||||
<button class="btn btn-inverse" ng-click="ctrl.addDataQuery()" ng-hide="ctrl.datasource.meta.mixed">
|
||||
<i class="fa fa-plus"></i>
|
||||
Query
|
||||
</button>
|
||||
|
||||
<div class="dropdown" ng-if="ctrl.datasource.meta.mixed">
|
||||
<button class="btn btn-inverse dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-plus"></i>
|
||||
Query <span class="caret"></span>
|
||||
</button>
|
||||
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li ng-repeat="datasource in ctrl.datasources" role="menuitem" ng-hide="datasource.meta.builtIn">
|
||||
<a ng-click="ctrl.addDataQuery(datasource);">{{datasource.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<rebuild-on-change property="ctrl.panel.datasource" show-null="true">
|
||||
<plugin-component type="query-options-ctrl">
|
||||
</plugin-component>
|
||||
</rebuild-on-change>
|
||||
|
||||
<div class="query-editor-rows gf-form-group">
|
||||
<div ng-repeat="target in ctrl.panel.targets" ng-class="{'gf-form-disabled': target.hide}">
|
||||
<rebuild-on-change property="ctrl.panel.datasource || target.datasource" show-null="true">
|
||||
<plugin-component type="query-ctrl">
|
||||
</plugin-component>
|
||||
</rebuild-on-change>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row">
|
||||
<metrics-ds-selector panel-ctrl="ctrl"></metrics-ds-selector>
|
||||
|
||||
<div class="pull-right dropdown" style="margin-right: 10px;">
|
||||
<button class="btn btn-inverse dropdown-toggle" data-toggle="dropdown" bs-tooltip="'Datasource'">
|
||||
<i class="fa fa-database"></i>
|
||||
{{ctrl.datasourceName}} <span class="fa fa-caret-down"></span>
|
||||
</button>
|
||||
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li ng-repeat="datasource in ctrl.datasources" role="menuitem">
|
||||
<a ng-click="ctrl.setDatasource(datasource);">{{datasource.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<div class="gf-form-group">
|
||||
<rebuild-on-change property="ctrl.panel.datasource" show-null="true">
|
||||
<plugin-component type="query-options-ctrl">
|
||||
</plugin-component>
|
||||
</rebuild-on-change>
|
||||
</div>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
<query-editor-row ctrl="ctrl">
|
||||
<query-editor-row query-ctrl="ctrl" can-collapse="false">
|
||||
<cloudwatch-query-parameter target="ctrl.target" datasource="ctrl.datasource" on-change="ctrl.refresh()"></cloudwatch-query-parameter>
|
||||
</query-editor-row>
|
||||
|
||||
<cloudwatch-query-parameter target="ctrl.target" datasource="ctrl.datasource" on-change="ctrl.refresh()"></cloudwatch-query-parameter>
|
||||
|
@ -1,58 +1,59 @@
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list" role="menu">
|
||||
<li class="tight-form-item query-keyword tight-form-align" style="width: 100px">
|
||||
Metric
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment segment="regionSegment" get-options="getRegions()" on-change="regionChanged()"></metric-segment>
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment segment="namespaceSegment" get-options="getNamespaces()" on-change="namespaceChanged()"></metric-segment>
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment segment="metricSegment" get-options="getMetrics()" on-change="metricChanged()"></metric-segment>
|
||||
</li>
|
||||
<li class="tight-form-item query-keyword">
|
||||
Stats
|
||||
</li>
|
||||
<li ng-repeat="segment in statSegments">
|
||||
<metric-segment segment="segment" get-options="getStatSegments(segment, $index)" on-change="statSegmentChanged(segment, $index)"></metric-segment>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">Metric</label>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<metric-segment segment="regionSegment" get-options="getRegions()" on-change="regionChanged()"></metric-segment>
|
||||
<metric-segment segment="namespaceSegment" get-options="getNamespaces()" on-change="namespaceChanged()"></metric-segment>
|
||||
<metric-segment segment="metricSegment" get-options="getMetrics()" on-change="metricChanged()"></metric-segment>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword">Stats</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-repeat="segment in statSegments">
|
||||
<metric-segment segment="segment" get-options="getStatSegments(segment, $index)" on-change="statSegmentChanged(segment, $index)"></metric-segment>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list" role="menu">
|
||||
<li class="tight-form-item query-keyword tight-form-align" style="width: 100px">
|
||||
Dimensions
|
||||
</li>
|
||||
<li ng-repeat="segment in dimSegments">
|
||||
<metric-segment segment="segment" get-options="getDimSegments(segment, $index)" on-change="dimSegmentChanged(segment, $index)"></metric-segment>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">Dimensions</label>
|
||||
<metric-segment ng-repeat="segment in dimSegments" segment="segment" get-options="getDimSegments(segment, $index)" on-change="dimSegmentChanged(segment, $index)"></metric-segment>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list" role="menu">
|
||||
<li class="tight-form-item query-keyword tight-form-align" style="width: 100px">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">
|
||||
Period
|
||||
<tip>Interval between points in seconds</tip>
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-mini tight-form-input" ng-model="target.period" spellcheck='false' placeholder="auto" ng-model-onblur ng-change="onChange()" />
|
||||
</li>
|
||||
<li class="tight-form-item query-keyword">
|
||||
Alias
|
||||
<tip>{{metric}} {{stat}} {{namespace}} {{region}} {{DIMENSION_NAME}}</tip>
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-xlarge tight-form-input" ng-model="target.alias" spellcheck='false' ng-model-onblur ng-change="onChange()">
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
<info-popover mode="right-normal">Interval between points in seconds</info-popover>
|
||||
</label>
|
||||
<input type="text" class="gf-form-input" ng-model="target.period" spellcheck='false' placeholder="auto" ng-model-onblur ng-change="onChange()" />
|
||||
</div>
|
||||
<div class="gf-form max-width-30">
|
||||
<label class="gf-form-label query-keyword width-7">Alias</label>
|
||||
<input type="text" class="gf-form-input" ng-model="target.alias" spellcheck='false' ng-model-onblur ng-change="onChange()">
|
||||
<info-popover mode="right-absolute">
|
||||
Alias replacement variables:
|
||||
<ul ng-non-bindable>
|
||||
<li>{{metric}}</li>
|
||||
<li>{{stat}}</li>
|
||||
<li>{{namespace}}</li>
|
||||
<li>{{region}}</li>
|
||||
<li>{{DIMENSION_NAME}}</li>
|
||||
</ul>
|
||||
</info-popover>
|
||||
</div>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -60,6 +60,10 @@ function (angular, _, queryDef) {
|
||||
$scope.agg.query = '*';
|
||||
break;
|
||||
}
|
||||
case 'geohash_grid': {
|
||||
$scope.agg.settings.precision = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.validateModel();
|
||||
@ -121,6 +125,13 @@ function (angular, _, queryDef) {
|
||||
if (settings.trimEdges && settings.trimEdges > 0) {
|
||||
settingsLinkText += ', Trim edges: ' + settings.trimEdges;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'geohash_grid': {
|
||||
// limit precision to 7
|
||||
settings.precision = Math.max(Math.min(settings.precision, 7), 1);
|
||||
settingsLinkText = 'Precision: ' + settings.precision;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
Binary file not shown.
Before ![]() (image error) Size: 40 KiB After ![]() (image error) Size: 38 KiB ![]() ![]() |
@ -1,131 +1,96 @@
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">
|
||||
<span ng-show="isFirst">Group by</span>
|
||||
<span ng-hide="isFirst">Then by</span>
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment-model property="agg.type" options="bucketAggTypes" on-change="onTypeChanged()" custom="false" css-class="tight-form-item-large"></metric-segment-model>
|
||||
</li>
|
||||
<li ng-if="agg.field">
|
||||
<metric-segment-model property="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="tight-form-item-xxlarge"></metric-segment>
|
||||
</li>
|
||||
<li ng-if="!agg.field">
|
||||
<span class="tight-form-item tight-form-item-xxlarge"> </span>
|
||||
</li>
|
||||
<li class="tight-form-item last" ng-if="settingsLinkText">
|
||||
</label>
|
||||
|
||||
<metric-segment-model property="agg.type" options="bucketAggTypes" on-change="onTypeChanged()" custom="false" css-class="width-10"></metric-segment-model>
|
||||
<metric-segment-model ng-if="agg.field" property="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="width-12"></metric-segment>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<label class="gf-form-label gf-form-label--grow">
|
||||
<a ng-click="toggleOptions()">
|
||||
<i class="fa fa-caret-down" ng-show="showOptions"></i>
|
||||
<i class="fa fa-caret-right" ng-hide="showOptions"></i>
|
||||
{{settingsLinkText}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<ul class="tight-form-list pull-right">
|
||||
<li class="tight-form-item last" ng-if="isFirst">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label" ng-if="isFirst">
|
||||
<a class="pointer" ng-click="addBucketAgg()"><i class="fa fa-plus"></i></a>
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
</label>
|
||||
<label class="gf-form-label">
|
||||
<a class="pointer" ng-click="removeBucketAgg()"><i class="fa fa-minus"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form" ng-if="showOptions">
|
||||
<div class="tight-form-inner-box" ng-if="agg.type === 'date_histogram'">
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 170px">
|
||||
Interval
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment-model property="agg.settings.interval" get-options="getIntervalOptions()" on-change="onChangeInternal()" css-class="last" custom="true"></metric-segment-model>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
<div class="gf-form-group" ng-if="showOptions">
|
||||
<div ng-if="agg.type === 'date_histogram'">
|
||||
<div class="gf-form offset-width-7">
|
||||
<label class="gf-form-label width-10">Interval</label>
|
||||
<metric-segment-model property="agg.settings.interval" get-options="getIntervalOptions()" on-change="onChangeInternal()" css-class="width-12" custom="true"></metric-segment-model>
|
||||
</div>
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 170px">
|
||||
Min Doc Count
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="tight-form-input" ng-model="agg.settings.min_doc_count" ng-blur="onChangeInternal()">
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class="gf-form offset-width-7">
|
||||
<label class="gf-form-label width-10">Min Doc Count</label>
|
||||
<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.min_doc_count" ng-blur="onChangeInternal()">
|
||||
</div>
|
||||
<div class="tight-form last">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 170px">
|
||||
Trim edges points
|
||||
</li>
|
||||
<li>
|
||||
<input class="tight-form-input" type="number" ng-model="agg.settings.trimEdges" ng-change="onChangeInternal()">
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
<i class="fa fa-question-circle" bs-tooltip="'Trim the edges on the timeseries x datapoints'" data-placement="right"></i>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class="gf-form offset-width-7">
|
||||
<label class="gf-form-label width-10">
|
||||
Trim edges
|
||||
<info-popover mode="right-normal">
|
||||
Trim the edges on the timeseries datapoints
|
||||
</info-popover>
|
||||
</label>
|
||||
<input class="gf-form-input max-width-12" type="number" ng-model="agg.settings.trimEdges" ng-change="onChangeInternal()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="tight-form-inner-box" ng-if="agg.type === 'terms'">
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 60px">
|
||||
Order
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment-model property="agg.settings.order" options="orderOptions" on-change="onChangeInternal()" css-class="last"></metric-segment-model>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div ng-if="agg.type === 'terms'">
|
||||
<div class="gf-form offset-width-7">
|
||||
<label class="gf-form-label">Order</label>
|
||||
<metric-segment-model property="agg.settings.order" options="orderOptions" on-change="onChangeInternal()" css-class="width-12"></metric-segment-model>
|
||||
</div>
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 60px">
|
||||
Size
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment-model property="agg.settings.size" options="sizeOptions" on-change="onChangeInternal()" css-class="last"></metric-segment-model>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class="gf-form offset-width-7">
|
||||
<label class="gf-form-label width-10">Size</label>
|
||||
<metric-segment-model property="agg.settings.size" options="sizeOptions" on-change="onChangeInternal()" css-class="width-12"></metric-segment-model>
|
||||
</div>
|
||||
<div class="tight-form last">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 60px">
|
||||
Order By
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment-model property="agg.settings.orderBy" options="orderByOptions" on-change="onChangeInternal()" css-class="last"></metric-segment-model>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class="gf-form offset-width-7">
|
||||
<label class="gf-form-label width-10">Order By</label>
|
||||
<metric-segment-model property="agg.settings.orderBy" options="orderByOptions" on-change="onChangeInternal()" css-class="width-12"></metric-segment-model>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tight-form-inner-box" ng-if="agg.type === 'filters'">
|
||||
<div class="tight-form" ng-repeat="filter in agg.settings.filters" ng-class="{last: $last}">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 100px">
|
||||
Query {{$index + 1}}
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="tight-form-input input-large" ng-model="filter.query" spellcheck='false' placeholder="Lucene query" ng-blur="onChangeInternal()">
|
||||
</li>
|
||||
<li class="tight-form-item last" ng-if="$first">
|
||||
|
||||
<div ng-if="agg.type === 'filters'">
|
||||
<div class="gf-form-inline" ng-repeat="filter in agg.settings.filters" ng-class="{last: $last}">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-item width-10">Query {{$index + 1}}</label>
|
||||
<input type="text" class="gf-form-input max-width-12" ng-model="filter.query" spellcheck='false' placeholder="Lucene query" ng-blur="onChangeInternal()">
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label" ng-if="$first">
|
||||
<a class="pointer" ng-click="addFiltersQuery()"><i class="fa fa-plus"></i></a>
|
||||
</li>
|
||||
<li class="tight-form-item last" ng-if="!$first">
|
||||
</label>
|
||||
<label class="gf-form-label" ng-if="!$first">
|
||||
<a class="pointer" ng-click="removeFiltersQuery(filter)"><i class="fa fa-minus"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="agg.type === 'geohash_grid'">
|
||||
<div class="gf-form offset-width-7">
|
||||
<label class="gf-form-label">Precision</label>
|
||||
<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.precision" spellcheck='false' placeholder="3" ng-blur="onChangeInternal()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -1,138 +1,82 @@
|
||||
<div class="tight-form" ng-class="{'tight-form-disabled': agg.hide}">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
|
||||
<div class="gf-form-inline" ng-class="{'gf-form-disabled': agg.hide}">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">
|
||||
Metric
|
||||
|
||||
<a ng-click="toggleShowMetric()" bs-tooltip="'Click to toggle show / hide metric'">
|
||||
<i class="fa fa-eye" ng-hide="agg.hide"></i>
|
||||
<i class="fa fa-eye-slash" ng-show="agg.hide"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment-model property="agg.type" options="metricAggTypes" on-change="onTypeChange()" custom="false" css-class="tight-form-item-large"></metric-segment-model>
|
||||
</li>
|
||||
<li ng-if="aggDef.requiresField">
|
||||
<metric-segment-model property="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="tight-form-item-xxlarge"></metric-segment-model>
|
||||
</li>
|
||||
<li ng-if="aggDef.isPipelineAgg">
|
||||
<metric-segment-model property="agg.pipelineAgg" options="pipelineAggOptions" on-change="onChangeInternal()" custom="false" css-class="tight-form-item-xxlarge"></metric-segment-model>
|
||||
</li>
|
||||
<li class="tight-form-item last" ng-if="settingsLinkText">
|
||||
<a ng-click="toggleOptions()">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<metric-segment-model property="agg.type" options="metricAggTypes" on-change="onTypeChange()" custom="false" css-class="width-10"></metric-segment-model>
|
||||
<metric-segment-model ng-if="aggDef.requiresField" property="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="width-12"></metric-segment-model>
|
||||
<metric-segment-model ng-if="aggDef.isPipelineAgg" property="agg.pipelineAgg" options="pipelineAggOptions" on-change="onChangeInternal()" custom="false" css-class="width-12"></metric-segment-model>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<label class="gf-form-label gf-form-label--grow">
|
||||
<a ng-click="toggleOptions()" ng-if="settingsLinkText">
|
||||
<i class="fa fa-caret-down" ng-show="showOptions"></i>
|
||||
<i class="fa fa-caret-right" ng-hide="showOptions"></i>
|
||||
{{settingsLinkText}}
|
||||
{{settingsLinkText}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<ul class="tight-form-list pull-right">
|
||||
<li class="tight-form-item last" ng-if="isFirst">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label" ng-if="isFirst">
|
||||
<a class="pointer" ng-click="addMetricAgg()"><i class="fa fa-plus"></i></a>
|
||||
</li>
|
||||
<li class="tight-form-item last" ng-if="!isSingle">
|
||||
</label>
|
||||
<label class="gf-form-label" ng-if="!isSingle">
|
||||
<a class="pointer" ng-click="removeMetricAgg()"><i class="fa fa-minus"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form" ng-if="showOptions">
|
||||
<div class="tight-form-inner-box tight-form-container">
|
||||
<div class="tight-form" ng-if="agg.type === 'derivative'">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 75px;">
|
||||
Unit
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-medium tight-form-input last" ng-model="agg.settings.unit" ng-blur="onChangeInternal()" spellcheck='false'>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form" ng-if="agg.type === 'moving_avg'">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 75px;">
|
||||
Window
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-medium tight-form-input last" ng-model="agg.settings.window" ng-blur="onChangeInternal()" spellcheck='false'>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="tight-form" ng-if="agg.type === 'moving_avg'">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 75px;">
|
||||
Model
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-medium tight-form-input last" ng-change="onChangeInternal()" ng-model="agg.settings.model" blur="onChange()" spellcheck='false'>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="tight-form last" ng-if="agg.type === 'percentiles'">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item">
|
||||
Percentiles
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-xlarge tight-form-input last" ng-model="agg.settings.percents" array-join ng-blur="onChange()"></input>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div ng-if="agg.type === 'extended_stats'">
|
||||
<div class="tight-form" ng-repeat="stat in extendedStats">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 100px">
|
||||
{{stat.text}}
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
<editor-checkbox text="" model="agg.meta.{{stat.value}}" change="onChange()"></editor-checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tight-form" ng-if="agg.type === 'extended_stats'">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 100px">
|
||||
Sigma
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-mini tight-form-input last" placeholder="3" ng-model="agg.settings.sigma" ng-blur="onChange()"></input>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form" ng-if="aggDef.supportsInlineScript">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 100px;">
|
||||
Script
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-medium tight-form-input last" empty-to-null ng-model="agg.inlineScript" ng-blur="onChangeInternal()" spellcheck='false' placeholder="_value * 1">
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form" ng-if="aggDef.supportsMissing">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 100px;">
|
||||
Missing
|
||||
<tip>The missing parameter defines how documents that are missing a value should be treated. By default they will be ignored but it is also possible to treat them as if they had a value</tip>
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-medium tight-form-input last" empty-to-null ng-model="agg.settings.missing" ng-blur="onChangeInternal()" spellcheck='false'>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-group" ng-if="showOptions">
|
||||
|
||||
<div class="gf-form offset-width-7" ng-if="agg.type === 'derivative'">
|
||||
<label class="gf-form-label width-10">Unit</label>
|
||||
<input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.unit" ng-blur="onChangeInternal()" spellcheck='false'>
|
||||
</div>
|
||||
|
||||
<div class="gf-form offset-width-7" ng-if="agg.type === 'moving_avg'">
|
||||
<label class="gf-form-label width-10">Window</label>
|
||||
<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.window" ng-blur="onChangeInternal()" spellcheck='false'>
|
||||
</div>
|
||||
|
||||
<div class="gf-form offset-width-7" ng-if="agg.type === 'moving_avg'">
|
||||
<label class="gf-form-label width-10">Model</label>
|
||||
<input type="text" class="gf-form-input max-width-12" ng-change="onChangeInternal()" ng-model="agg.settings.model" blur="onChange()" spellcheck='false'>
|
||||
</div>
|
||||
|
||||
<div class="gf-form offset-width-7" ng-if="agg.type === 'percentiles'">
|
||||
<label class="gf-form-label width-10">Percentiles</label>
|
||||
<input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.percents" array-join ng-blur="onChange()"></input>
|
||||
</div>
|
||||
|
||||
<div ng-if="agg.type === 'extended_stats'">
|
||||
<gf-form-switch ng-repeat="stat in extendedStats" class="gf-form offset-width-7" label="{{stat.text}}" label-class="width-10" checked="agg.meta[stat.value]" on-change="onChangeInternal()"></gf-form-switch>
|
||||
|
||||
<div class="gf-form offset-width-7">
|
||||
<label class="gf-form-label width-10">Sigma</label>
|
||||
<input type="number" class="gf-form-input max-width-12" placeholder="3" ng-model="agg.settings.sigma" ng-blur="onChange()"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form offset-width-7" ng-if="aggDef.supportsInlineScript">
|
||||
<label class="gf-form-label width-10">Script</label>
|
||||
<input type="text" class="gf-form-input max-width-12" empty-to-null ng-model="agg.inlineScript" ng-blur="onChangeInternal()" spellcheck='false' placeholder="_value * 1">
|
||||
</div>
|
||||
|
||||
<div class="gf-form offset-width-7" ng-if="aggDef.supportsMissing">
|
||||
<label class="gf-form-label width-10">
|
||||
Missing
|
||||
<tip>The missing parameter defines how documents that are missing a value should be treated. By default they will be ignored but it is also possible to treat them as if they had a value</tip>
|
||||
</label>
|
||||
<input type="number" class="gf-form-input max-width-12" empty-to-null ng-model="agg.settings.missing" ng-blur="onChangeInternal()" spellcheck='false'>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,32 +1,31 @@
|
||||
<query-editor-row ctrl="ctrl">
|
||||
<li class="tight-form-item query-keyword" style="width: 75px">
|
||||
Query
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="tight-form-input" style="width: 345px;" ng-model="ctrl.target.query" spellcheck='false' placeholder="Lucene query" ng-blur="ctrl.refresh()">
|
||||
</li>
|
||||
<li class="tight-form-item query-keyword">
|
||||
Alias
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="tight-form-input" style="width: 200px;" ng-model="ctrl.target.alias" spellcheck='false' placeholder="alias patterns (empty = auto)" ng-blur="ctrl.refresh()">
|
||||
</li>
|
||||
<query-editor-row query-ctrl="ctrl" can-collapse="true">
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form gf-form--grow">
|
||||
<label class="gf-form-label query-keyword width-7">Query</label>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.target.query" spellcheck='false' placeholder="Lucene query" ng-blur="ctrl.refresh()">
|
||||
</div>
|
||||
<div class="gf-form max-width-15">
|
||||
<label class="gf-form-label query-keyword">Alias</label>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.target.alias" spellcheck='false' placeholder="alias patterns" ng-blur="ctrl.refresh()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="agg in ctrl.target.metrics">
|
||||
<elastic-metric-agg
|
||||
target="ctrl.target" index="$index"
|
||||
get-fields="ctrl.getFields($fieldType)"
|
||||
on-change="ctrl.queryUpdated()"
|
||||
es-version="ctrl.esVersion">
|
||||
</elastic-metric-agg>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="agg in ctrl.target.bucketAggs">
|
||||
<elastic-bucket-agg
|
||||
target="ctrl.target" index="$index"
|
||||
get-fields="ctrl.getFields($fieldType)"
|
||||
on-change="ctrl.queryUpdated()">
|
||||
</elastic-bucket-agg>
|
||||
</div>
|
||||
|
||||
</query-editor-row>
|
||||
|
||||
<div ng-repeat="agg in ctrl.target.metrics">
|
||||
<elastic-metric-agg
|
||||
target="ctrl.target" index="$index"
|
||||
get-fields="ctrl.getFields($fieldType)"
|
||||
on-change="ctrl.queryUpdated()"
|
||||
es-version="ctrl.esVersion">
|
||||
</elastic-metric-agg>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="agg in ctrl.target.bucketAggs">
|
||||
<elastic-bucket-agg
|
||||
target="ctrl.target" index="$index"
|
||||
get-fields="ctrl.getFields($fieldType)"
|
||||
on-change="ctrl.queryUpdated()">
|
||||
</elastic-bucket-agg>
|
||||
</div>
|
||||
|
||||
|
@ -153,6 +153,10 @@ function (queryDef) {
|
||||
this.buildTermsAgg(aggDef, esAgg, target);
|
||||
break;
|
||||
}
|
||||
case 'geohash_grid': {
|
||||
esAgg['geohash_grid'] = {field: aggDef.field, precision: aggDef.settings.precision};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nestedAggs.aggs = nestedAggs.aggs || {};
|
||||
|
@ -5,6 +5,7 @@ import './metric_agg';
|
||||
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import queryDef from './query_def';
|
||||
import {QueryCtrl} from 'app/plugins/sdk';
|
||||
|
||||
export class ElasticQueryCtrl extends QueryCtrl {
|
||||
@ -38,6 +39,48 @@ export class ElasticQueryCtrl extends QueryCtrl {
|
||||
this.$rootScope.appEvent('elastic-query-updated');
|
||||
}
|
||||
|
||||
getCollapsedText() {
|
||||
var metricAggs = this.target.metrics;
|
||||
var bucketAggs = this.target.bucketAggs;
|
||||
var metricAggTypes = queryDef.getMetricAggTypes(this.esVersion);
|
||||
var bucketAggTypes = queryDef.bucketAggTypes;
|
||||
var text = '';
|
||||
|
||||
if (this.target.query) {
|
||||
text += 'Query: ' + this.target.query + ', ';
|
||||
}
|
||||
|
||||
text += 'Metrics: ';
|
||||
|
||||
_.each(metricAggs, (metric, index) => {
|
||||
var aggDef = _.findWhere(metricAggTypes, {value: metric.type});
|
||||
text += aggDef.text + '(';
|
||||
if (aggDef.requiresField) {
|
||||
text += metric.field;
|
||||
}
|
||||
text += '), ';
|
||||
});
|
||||
|
||||
_.each(bucketAggs, (bucketAgg, index) => {
|
||||
if (index === 0) {
|
||||
text += ' Group by: ';
|
||||
}
|
||||
|
||||
var aggDef = _.findWhere(bucketAggTypes, {value: bucketAgg.type});
|
||||
text += aggDef.text + '(';
|
||||
if (aggDef.requiresField) {
|
||||
text += bucketAgg.field;
|
||||
}
|
||||
text += '), ';
|
||||
});
|
||||
|
||||
if (this.target.alias) {
|
||||
text += 'Alias: ' + this.target.alias;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
handleQueryError(err) {
|
||||
this.error = err.message || 'Failed to issue metric query';
|
||||
return [];
|
||||
|
@ -20,9 +20,10 @@ function (_) {
|
||||
],
|
||||
|
||||
bucketAggTypes: [
|
||||
{text: "Terms", value: 'terms' },
|
||||
{text: "Terms", value: 'terms', requiresField: true},
|
||||
{text: "Filters", value: 'filters' },
|
||||
{text: "Date Histogram", value: 'date_histogram' },
|
||||
{text: "Geo Hash Grid", value: 'geohash_grid', requiresField: true},
|
||||
{text: "Date Histogram", value: 'date_histogram', requiresField: true},
|
||||
],
|
||||
|
||||
orderByOptions: [
|
||||
|
@ -1,5 +1,7 @@
|
||||
<query-editor-row ctrl="ctrl">
|
||||
<li class="tight-form-item">
|
||||
Test metric (fake data source)
|
||||
</li>
|
||||
<query-editor-row query-ctrl="ctrl" can-collapse="false">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label">Test metric (fake data source)</label>
|
||||
</div>
|
||||
</div>
|
||||
</query-editor-row>
|
||||
|
@ -11,10 +11,10 @@ function (angular, _, $, gfunc) {
|
||||
.module('grafana.directives')
|
||||
.directive('graphiteAddFunc', function($compile) {
|
||||
var inputTemplate = '<input type="text"'+
|
||||
' class="tight-form-input input-medium tight-form-input"' +
|
||||
' class="gf-form-input"' +
|
||||
' spellcheck="false" style="display:none"></input>';
|
||||
|
||||
var buttonTemplate = '<a class="tight-form-item tight-form-func dropdown-toggle"' +
|
||||
var buttonTemplate = '<a class="gf-form-label query-part dropdown-toggle"' +
|
||||
' tabindex="1" gf-dropdown="functionMenu" data-toggle="dropdown">' +
|
||||
'<i class="fa fa-plus"></i></a>';
|
||||
|
||||
|
@ -80,6 +80,13 @@ function (_, $) {
|
||||
category: categories.Calculate,
|
||||
});
|
||||
|
||||
addFuncDef({
|
||||
name: 'stddevSeries',
|
||||
params: optionalSeriesRefArgs,
|
||||
defaultParams: [''],
|
||||
category: categories.Calculate,
|
||||
});
|
||||
|
||||
addFuncDef({
|
||||
name: 'divideSeries',
|
||||
params: optionalSeriesRefArgs,
|
||||
|
@ -1,21 +1,27 @@
|
||||
<query-editor-row ctrl="ctrl">
|
||||
<query-editor-row query-ctrl="ctrl" has-text-edit-mode="true">
|
||||
|
||||
<li class="tight-form-flex-wrapper" ng-show="ctrl.target.textEditor">
|
||||
<input type="text" class="tight-form-clear-input" style="width: 100%;" ng-model="ctrl.target.target" give-focus="ctrl.target.textEditor" spellcheck='false' ng-model-onblur ng-change="ctrl.targetTextChanged()"></input>
|
||||
</li>
|
||||
<div class="gf-form" ng-show="ctrl.target.textEditor">
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.target.target" spellcheck="false" ng-blur="ctrl.refresh()"></input>
|
||||
</div>
|
||||
|
||||
<li ng-hide-start="ctrl.target.textEditor"></li>
|
||||
<div ng-hide="ctrl.target.textEditor">
|
||||
<div class="gf-form-inline">
|
||||
<div ng-repeat="segment in ctrl.segments" role="menuitem" class="gf-form">
|
||||
<metric-segment segment="segment" get-options="ctrl.getAltSegments($index)" on-change="ctrl.segmentValueChanged(segment, $index)"></metric-segment>
|
||||
</div>
|
||||
|
||||
<li ng-repeat="segment in ctrl.segments" role="menuitem">
|
||||
<metric-segment segment="segment" get-options="ctrl.getAltSegments($index)" on-change="ctrl.segmentValueChanged(segment, $index)"></metric-segment>
|
||||
</li>
|
||||
<li ng-repeat="func in ctrl.functions">
|
||||
<span graphite-func-editor class="tight-form-item tight-form-func">
|
||||
</span>
|
||||
</li>
|
||||
<li class="dropdown" graphite-add-func>
|
||||
</li>
|
||||
<div ng-repeat="func in ctrl.functions" class="gf-form">
|
||||
<span graphite-func-editor class="gf-form-label query-part"></span>
|
||||
</div>
|
||||
|
||||
<li ng-hide-end></li>
|
||||
<div class="gf-form dropdown">
|
||||
<span graphite-add-func></span>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</query-editor-row>
|
||||
|
@ -6,8 +6,8 @@ There are currently two separate datasources for InfluxDB in Grafana: InfluxDB 0
|
||||
|
||||
This is the plugin for InfluxDB 0.9. It is rapidly evolving and we continue to track its API.
|
||||
|
||||
InfluxDB 0.8 is no longer maintained by InfluxDB Inc, but we provide support as a convenience to existing users. You can find it [here](https://www.grafana.net/plugins/grafana-influxdb-08-datasource).
|
||||
InfluxDB 0.8 is no longer maintained by InfluxDB Inc, but we provide support as a convenience to existing users. You can find it [here](https://grafana.net/plugins/grafana-influxdb-08-datasource).
|
||||
|
||||
Read more about InfluxDB here:
|
||||
|
||||
[http://docs.grafana.org/datasources/influxdb/](http://docs.grafana.org/datasources/influxdb/)
|
||||
[http://docs.grafana.org/datasources/influxdb/](http://docs.grafana.org/datasources/influxdb/)
|
||||
|
@ -152,7 +152,9 @@ export default class InfluxQuery {
|
||||
if (interpolate) {
|
||||
value = this.templateSrv.replace(value, this.scopedVars);
|
||||
}
|
||||
value = "'" + value.replace('\\', '\\\\') + "'";
|
||||
if (isNaN(+value)) {
|
||||
value = "'" + value.replace('\\', '\\\\') + "'";
|
||||
}
|
||||
} else if (interpolate){
|
||||
value = this.templateSrv.replace(value, this.scopedVars, 'regex');
|
||||
}
|
||||
@ -160,12 +162,14 @@ export default class InfluxQuery {
|
||||
return str + '"' + tag.key + '" ' + operator + ' ' + value;
|
||||
}
|
||||
|
||||
getMeasurementAndPolicy() {
|
||||
getMeasurementAndPolicy(interpolate) {
|
||||
var policy = this.target.policy;
|
||||
var measurement = this.target.measurement;
|
||||
var measurement = this.target.measurement || 'measurement';
|
||||
|
||||
if (!measurement.match('^/.*/')) {
|
||||
measurement = '"' + measurement+ '"';
|
||||
} else if (interpolate) {
|
||||
measurement = this.templateSrv.replace(measurement, this.scopedVars, 'regex');
|
||||
}
|
||||
|
||||
if (policy !== 'default') {
|
||||
@ -188,10 +192,6 @@ export default class InfluxQuery {
|
||||
}
|
||||
}
|
||||
|
||||
if (!target.measurement) {
|
||||
throw {message: "Metric measurement is missing"};
|
||||
}
|
||||
|
||||
var query = 'SELECT ';
|
||||
var i, y;
|
||||
for (i = 0; i < this.selectModels.length; i++) {
|
||||
@ -208,7 +208,7 @@ export default class InfluxQuery {
|
||||
query += selectText;
|
||||
}
|
||||
|
||||
query += ' FROM ' + this.getMeasurementAndPolicy() + ' WHERE ';
|
||||
query += ' FROM ' + this.getMeasurementAndPolicy(interpolate) + ' WHERE ';
|
||||
var conditions = _.map(target.tags, (tag, index) => {
|
||||
return this.renderTagCondition(tag, index, interpolate);
|
||||
});
|
||||
|
@ -1,73 +1,96 @@
|
||||
<query-editor-row ctrl="ctrl">
|
||||
<ul class="tight-form-list" ng-hide="ctrl.target.rawQuery">
|
||||
<li class="tight-form-item query-keyword" style="width: 75px">
|
||||
FROM
|
||||
</li>
|
||||
<li>
|
||||
<query-editor-row query-ctrl="ctrl" can-collapse="true" has-text-edit-mode="true">
|
||||
|
||||
<div class="gf-form" ng-if="ctrl.target.rawQuery">
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.target.query" spellcheck="false" ng-blur="ctrl.refresh()"></input>
|
||||
</div>
|
||||
|
||||
<div ng-if="!ctrl.target.rawQuery">
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">FROM</label>
|
||||
|
||||
<metric-segment segment="ctrl.policySegment" get-options="ctrl.getPolicySegments()" on-change="ctrl.policyChanged()"></metric-segment>
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment segment="ctrl.measurementSegment" get-options="ctrl.getMeasurements()" on-change="ctrl.measurementChanged()"></metric-segment>
|
||||
</li>
|
||||
<li class="tight-form-item query-keyword" style="padding-left: 15px; padding-right: 15px;">
|
||||
WHERE
|
||||
</li>
|
||||
<li ng-repeat="segment in ctrl.tagSegments">
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword">WHERE</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-repeat="segment in ctrl.tagSegments">
|
||||
<metric-segment segment="segment" get-options="ctrl.getTagsOrValues(segment, $index)" on-change="ctrl.tagSegmentUpdated(segment, $index)"></metric-segment>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="tight-form-flex-wrapper" ng-show="ctrl.target.rawQuery">
|
||||
<input type="text" class="tight-form-clear-input" ng-model="ctrl.target.query" spellcheck="false" style="width: 100%;" ng-blur="ctrl.refresh()"></input>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline" ng-repeat="selectParts in ctrl.queryModel.selectModels">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">
|
||||
<span ng-show="$index === 0">SELECT</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-repeat="part in selectParts">
|
||||
<influx-query-part-editor
|
||||
class="gf-form-label query-part"
|
||||
part="part"
|
||||
remove-action="ctrl.removeSelectPart(selectParts, part)"
|
||||
part-updated="ctrl.selectPartUpdated(selectParts, part)"
|
||||
get-options="ctrl.getPartOptions(part)">
|
||||
</influx-query-part-editor>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<label class="dropdown"
|
||||
dropdown-typeahead="ctrl.selectMenu"
|
||||
dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-7">
|
||||
<span>GROUP BY</span>
|
||||
</label>
|
||||
|
||||
<influx-query-part-editor
|
||||
ng-repeat="part in ctrl.queryModel.groupByParts"
|
||||
part="part"
|
||||
class="gf-form-label query-part"
|
||||
remove-action="ctrl.removeGroupByPart(part, $index)" part-updated="ctrl.refresh();" get-options="ctrl.getPartOptions(part)">
|
||||
</influx-query-part-editor>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-30">
|
||||
<label class="gf-form-label query-keyword width-7">ALIAS BY</label>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="ctrl.refresh()">
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label">Format as</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select class="gf-form-input gf-size-auto" ng-model="ctrl.target.resultFormat" ng-options="f.value as f.text for f in ctrl.resultFormats" ng-change="ctrl.refresh()"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</query-editor-row>
|
||||
|
||||
<div ng-hide="ctrl.target.rawQuery">
|
||||
<div class="tight-form" ng-repeat="selectParts in ctrl.queryModel.selectModels">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
|
||||
<span ng-show="$index === 0">SELECT</span>
|
||||
</li>
|
||||
<li ng-repeat="part in selectParts">
|
||||
<influx-query-part-editor part="part" class="tight-form-item tight-form-func" remove-action="ctrl.removeSelectPart(selectParts, part)" part-updated="ctrl.selectPartUpdated(selectParts, part)" get-options="ctrl.getPartOptions(part)"></influx-query-part-editor>
|
||||
</li>
|
||||
<li class="dropdown" dropdown-typeahead="ctrl.selectMenu" dropdown-typeahead-on-select="ctrl.addSelectPart(selectParts, $item, $subItem)">
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
|
||||
<span>GROUP BY</span>
|
||||
</li>
|
||||
<li ng-repeat="part in ctrl.queryModel.groupByParts">
|
||||
<influx-query-part-editor part="part" class="tight-form-item tight-form-func" remove-action="ctrl.removeGroupByPart(part, $index)" part-updated="ctrl.refresh();" get-options="ctrl.getPartOptions(part)"></influx-query-part-editor>
|
||||
</li>
|
||||
<li>
|
||||
<metric-segment segment="ctrl.groupBySegment" get-options="ctrl.getGroupByOptions()" on-change="ctrl.groupByAction(part, $index)"></metric-segment>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
|
||||
ALIAS BY
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="tight-form-clear-input input-xlarge" ng-model="ctrl.target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="ctrl.refresh()">
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Format as
|
||||
</li>
|
||||
<li>
|
||||
<select class="input-small tight-form-input" style="width: 104px" ng-model="ctrl.target.resultFormat" ng-options="f.value as f.text for f in ctrl.resultFormats" ng-change="ctrl.refresh()"></select>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
|
@ -38,7 +38,7 @@
|
||||
</section>
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="pull-left" style="margin-top: 30px;">
|
||||
<div class="pull-left">
|
||||
|
||||
<div class="grafana-info-box span6" ng-if="ctrl.panelCtrl.editorHelpIndex === 1">
|
||||
<h5>Alias patterns</h5>
|
||||
|
@ -2,4 +2,4 @@
|
||||
<span class="pointer fa fa-remove" ng-click="removeActionInternal()" ></span>
|
||||
</div>
|
||||
|
||||
<a ng-click="toggleControls()">{{part.def.type}}</a><span>(</span><span class="query-part-parameters"></span><span>)</span>
|
||||
<a ng-click="toggleControls()" class="query-part-name">{{part.def.type}}</a><span>(</span><span class="query-part-parameters"></span><span>)</span>
|
||||
|
@ -25,8 +25,8 @@ function (_) {
|
||||
}
|
||||
}
|
||||
|
||||
// quote value unless regex
|
||||
if (operator !== '=~' && operator !== '!~') {
|
||||
// quote value unless regex or number
|
||||
if (operator !== '=~' && operator !== '!~' && isNaN(+value)) {
|
||||
value = "'" + value + "'";
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ export class InfluxQueryCtrl extends QueryCtrl {
|
||||
measurementSegment: any;
|
||||
removeTagFilterSegment: any;
|
||||
|
||||
|
||||
/** @ngInject **/
|
||||
constructor($scope, $injector, private templateSrv, private $q, private uiSegmentSrv) {
|
||||
super($scope, $injector);
|
||||
@ -154,7 +155,11 @@ export class InfluxQueryCtrl extends QueryCtrl {
|
||||
}
|
||||
|
||||
toggleEditorMode() {
|
||||
this.target.query = this.queryModel.render(false);
|
||||
try {
|
||||
this.target.query = this.queryModel.render(false);
|
||||
} catch (err) {
|
||||
console.log('query render error');
|
||||
}
|
||||
this.target.rawQuery = !this.target.rawQuery;
|
||||
}
|
||||
|
||||
@ -316,5 +321,9 @@ export class InfluxQueryCtrl extends QueryCtrl {
|
||||
return '=';
|
||||
}
|
||||
}
|
||||
|
||||
getCollapsedText() {
|
||||
return this.queryModel.render(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,17 +12,29 @@ export default class ResponseParser {
|
||||
return [];
|
||||
}
|
||||
|
||||
var series = influxResults.series[0];
|
||||
return _.map(series.values, (value) => {
|
||||
if (_.isArray(value)) {
|
||||
if (query.toLowerCase().indexOf('show tag values') >= 0) {
|
||||
return { text: (value[1] || value[0]) };
|
||||
var influxdb11format = query.toLowerCase().indexOf('show tag values') >= 0;
|
||||
|
||||
var res = {};
|
||||
_.each(influxResults.series, serie => {
|
||||
_.each(serie.values, value => {
|
||||
if (_.isArray(value)) {
|
||||
if (influxdb11format) {
|
||||
addUnique(res, value[1] || value[0]);
|
||||
} else {
|
||||
addUnique(res, value[0]);
|
||||
}
|
||||
} else {
|
||||
return { text: value[0] };
|
||||
addUnique(res, value);
|
||||
}
|
||||
} else {
|
||||
return { text: value };
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return _.map(res, value => {
|
||||
return { text: value};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addUnique(arr, value) {
|
||||
arr[value] = value;
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ describe("influxdb response parser", () => {
|
||||
{
|
||||
"name": "hostnameTagValues",
|
||||
"columns": ["hostname"],
|
||||
"values": [ ["server1"], ["server2"] ]
|
||||
"values": [ ["server1"], ["server2"], ["server2"] ]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -54,7 +54,7 @@ describe("influxdb response parser", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("response from 0.11.0", () => {
|
||||
describe("response from 0.12.0", () => {
|
||||
var response = {
|
||||
"results": [
|
||||
{
|
||||
@ -62,8 +62,19 @@ describe("influxdb response parser", () => {
|
||||
{
|
||||
"name": "cpu",
|
||||
"columns": [ "key", "value"],
|
||||
"values": [ [ "source", "site" ], [ "source", "api" ] ]
|
||||
}
|
||||
"values": [
|
||||
[ "source", "site" ],
|
||||
[ "source", "api" ]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "logins",
|
||||
"columns": [ "key", "value"],
|
||||
"values": [
|
||||
[ "source", "site" ],
|
||||
[ "source", "webapi"]
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -72,15 +83,12 @@ describe("influxdb response parser", () => {
|
||||
var result = this.parser.parse(query, response);
|
||||
|
||||
it("should get two responses", () => {
|
||||
expect(_.size(result)).to.be(2);
|
||||
expect(_.size(result)).to.be(3);
|
||||
expect(result[0].text).to.be('site');
|
||||
expect(result[1].text).to.be('api');
|
||||
expect(result[2].text).to.be('webapi');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe("SHOW FIELD response", () => {
|
||||
|
@ -1,223 +1,253 @@
|
||||
<query-editor-row ctrl="ctrl">
|
||||
<li class="tight-form-item query-keyword" style="width: 100px">
|
||||
Metric
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-large tight-form-input" ng-model="ctrl.target.metric"
|
||||
spellcheck='false' bs-typeahead="ctrl.suggestMetrics" placeholder="metric name" data-min-length=0 data-items=100
|
||||
ng-blur="ctrl.targetBlur()">
|
||||
</input>
|
||||
<a bs-tooltip="ctrl.errors.metric" style="color: rgb(229, 189, 28)" ng-show="ctrl.errors.metric">
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="tight-form-item query-keyword">
|
||||
Aggregator
|
||||
</li>
|
||||
<li>
|
||||
<select ng-model="ctrl.target.aggregator" class="tight-form-input input-small"
|
||||
ng-options="agg for agg in ctrl.aggregators"
|
||||
ng-change="ctrl.targetBlur()">
|
||||
</select>
|
||||
<a bs-tooltip="ctrl.errors.aggregator" style="color: rgb(229, 189, 28)" ng-show="ctrl.errors.aggregator">
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
</li>
|
||||
<query-editor-row query-ctrl="ctrl" can-collapse="false">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-25">
|
||||
<label class="gf-form-label query-keyword width-8">
|
||||
Metric
|
||||
<label class="gf-form-label" bs-tooltip="ctrl.errors.metric" style="color: rgb(229, 189, 28)" ng-show="ctrl.errors.metric">
|
||||
<i class="fa fa-warning"></i>
|
||||
</label>
|
||||
</label>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.target.metric"
|
||||
spellcheck='false' bs-typeahead="ctrl.suggestMetrics" placeholder="metric name" data-min-length=0 data-items=100
|
||||
ng-blur="ctrl.targetBlur()">
|
||||
</input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword">
|
||||
Aggregator
|
||||
<a bs-tooltip="ctrl.errors.aggregator" style="color: rgb(229, 189, 28)" ng-show="ctrl.errors.aggregator">
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
</label>
|
||||
<div class="gf-form-select-wrapper max-width-15">
|
||||
<select ng-model="ctrl.target.aggregator" class="gf-form-input"
|
||||
ng-options="agg for agg in ctrl.aggregators"
|
||||
ng-change="ctrl.targetBlur()">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form max-width-20">
|
||||
<label class="gf-form-label query-keyword width-6">
|
||||
Alias:
|
||||
<info-popover mode="right-normal">
|
||||
Use patterns like $tag_tagname to replace part of the alias for a tag value
|
||||
</info-popover>
|
||||
</label>
|
||||
<input type="text" class="gf-form-input"
|
||||
ng-model="ctrl.target.alias"
|
||||
spellcheck='false'
|
||||
placeholder="series alias"
|
||||
data-min-length=0 data-items=100
|
||||
ng-blur="ctrl.targetBlur()"></input>
|
||||
</div>
|
||||
|
||||
<li class="tight-form-item query-keyword">
|
||||
Alias:
|
||||
<tip>Use patterns like $tag_tagname to replace part of the alias for a tag value</tip>
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="tight-form-input input-large"
|
||||
ng-model="ctrl.target.alias"
|
||||
spellcheck='false'
|
||||
placeholder="series alias"
|
||||
data-min-length=0 data-items=100
|
||||
ng-blur="ctrl.targetBlur()"></input>
|
||||
</li>
|
||||
</query-editor-row>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list" role="menu">
|
||||
<li class="tight-form-item tight-form-align query-keyword" style="width: 100px">
|
||||
Down sample
|
||||
</li>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-25">
|
||||
<label class="gf-form-label query-keyword width-8">Down sample</label>
|
||||
<input type="text" class="gf-form-input"
|
||||
ng-model="ctrl.target.downsampleInterval"
|
||||
ng-model-onblur
|
||||
ng-change="ctrl.targetBlur()"
|
||||
placeholder="interval"></input>
|
||||
<info-popover mode="right-absolute">
|
||||
blank for auto, or for example <code>1m</code>
|
||||
</info-popover>
|
||||
</div>
|
||||
|
||||
<li>
|
||||
<input type="text" class="input-large tight-form-input"
|
||||
ng-model="ctrl.target.downsampleInterval"
|
||||
ng-model-onblur
|
||||
ng-change="ctrl.targetBlur()"
|
||||
placeholder="interval (empty = auto)"></input>
|
||||
</li>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword">Aggregator</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select ng-model="ctrl.target.downsampleAggregator" class="gf-form-input"
|
||||
ng-options="agg for agg in ctrl.aggregators"
|
||||
ng-change="ctrl.targetBlur()">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<li class="tight-form-item query-keyword">
|
||||
Aggregator
|
||||
</li>
|
||||
<div class="gf-form" ng-if="ctrl.tsdbVersion == 2">
|
||||
<label class="gf-form-label query-keyword width-6">Fill</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select ng-model="ctrl.target.downsampleFillPolicy" class="gf-form-input"
|
||||
ng-options="agg for agg in ctrl.fillPolicies"
|
||||
ng-change="ctrl.targetBlur()">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<li>
|
||||
<select ng-model="ctrl.target.downsampleAggregator" class="tight-form-input input-small"
|
||||
ng-options="agg for agg in ctrl.aggregators"
|
||||
ng-change="ctrl.targetBlur()">
|
||||
</select>
|
||||
</li>
|
||||
<gf-form-switch class="gf-form"
|
||||
label="Disable downsampling"
|
||||
checked="ctrl.target.disableDownsampling"
|
||||
on-change="ctrl.targetBlur()">
|
||||
</gf-form-switch>
|
||||
|
||||
<li class="tight-form-item query-keyword" style="width: 59px" ng-if="ctrl.tsdbVersion == 2">
|
||||
Fill
|
||||
</li>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<li ng-if="ctrl.tsdbVersion == 2">
|
||||
<select ng-model="ctrl.target.downsampleFillPolicy" class="tight-form-input input-small"
|
||||
ng-options="agg for agg in ctrl.fillPolicies"
|
||||
ng-change="ctrl.targetBlur()">
|
||||
</select>
|
||||
</li>
|
||||
<div class="gf-form-inline" ng-if="ctrl.tsdbVersion == 2">
|
||||
<div class="gf-form">
|
||||
|
||||
<li class="tight-form-item query-keyword">
|
||||
Disable downsampling <editor-checkbox text="" model="ctrl.target.disableDownsampling" change="ctrl.targetBlur()"></editor-checkbox>
|
||||
</li>
|
||||
<label class="gf-form-label query-keyword width-8">
|
||||
Filters
|
||||
<info-popover mode="right-normal">
|
||||
Filters does not work with tags, either of the two will work but not both.
|
||||
</info-popover>
|
||||
</label>
|
||||
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div ng-repeat="fil in ctrl.target.filters track by $index" class="gf-form-label">
|
||||
{{fil.tagk}} = {{fil.type}}({{fil.filter}}) , groupBy = {{fil.groupBy}}
|
||||
<a ng-click="ctrl.editFilter(fil, $index)">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a ng-click="ctrl.removeFilter($index)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</div>
|
||||
<label class="gf-form-label query-keyword" ng-hide="ctrl.addFilterMode">
|
||||
<a ng-click="ctrl.addFilter()">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="tight-form" ng-if="ctrl.tsdbVersion == 2">
|
||||
<ul class="tight-form-list" role="menu">
|
||||
<li class="tight-form-item tight-form-align query-keyword" style="width: 100px">
|
||||
Filters
|
||||
<tip ng-if="ctrl.tsdbVersion == 2">Filters does not work with tags, either of the two will work but not both.</tip>
|
||||
</li>
|
||||
<li ng-repeat="fil in ctrl.target.filters track by $index" class="tight-form-item">
|
||||
{{fil.tagk}} = {{fil.type}}({{fil.filter}}) , groupBy = {{fil.groupBy}}
|
||||
<a ng-click="ctrl.editFilter(fil, $index)">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a ng-click="ctrl.removeFilter($index)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="tight-form-item query-keyword" ng-hide="ctrl.addFilterMode">
|
||||
<a ng-click="ctrl.addFilter()">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
</li>
|
||||
<div class="gf-form-inline" ng-show="ctrl.addFilterMode">
|
||||
<div class="gf-form">
|
||||
<input type="text" class="gf-form-input" spellcheck='false'
|
||||
bs-typeahead="ctrl.suggestTagKeys" data-min-length=0 data-items=100
|
||||
ng-model="ctrl.target.currentFilterKey" placeholder="key">
|
||||
</input>
|
||||
</div>
|
||||
|
||||
<li class="query-keyword" ng-show="ctrl.addFilterMode">
|
||||
<input type="text" class="input-small tight-form-input" spellcheck='false'
|
||||
bs-typeahead="ctrl.suggestTagKeys" data-min-length=0 data-items=100
|
||||
ng-model="ctrl.target.currentFilterKey" placeholder="key"></input>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label">Type</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select ng-model="ctrl.target.currentFilterType" class="gf-form-input" ng-options="filType for filType in ctrl.filterTypes">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Type <select ng-model="ctrl.target.currentFilterType"
|
||||
class="tight-form-input input-small"
|
||||
ng-options="filType for filType in ctrl.filterTypes">
|
||||
</select>
|
||||
|
||||
<input type="text" class="input-small tight-form-input"
|
||||
spellcheck='false' bs-typeahead="ctrl.suggestTagValues"
|
||||
data-min-length=0 data-items=100 ng-model="ctrl.target.currentFilterValue" placeholder="filter">
|
||||
</input>
|
||||
<div class="gf-form">
|
||||
<input type="text" class="gf-form-input" spellcheck='false' bs-typeahead="ctrl.suggestTagValues" data-min-length=0 data-items=100 ng-model="ctrl.target.currentFilterValue" placeholder="filter">
|
||||
</input>
|
||||
</div>
|
||||
|
||||
groupBy <editor-checkbox text="" model="ctrl.target.currentFilterGroupBy"></editor-checkbox>
|
||||
<gf-form-switch class="gf-form" label="Group by" checked="ctrl.target.currentFilterGroupBy" on-change="ctrl.targetBlur()">
|
||||
</gf-form-switch>
|
||||
|
||||
<a bs-tooltip="ctrl.errors.filters"
|
||||
style="color: rgb(229, 189, 28)"
|
||||
ng-show="ctrl.errors.filters">
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
|
||||
<a ng-click="ctrl.addFilter()" ng-hide="ctrl.errors.filters">
|
||||
add filter
|
||||
</a>
|
||||
<a ng-click="ctrl.closeAddFilterMode()">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
<div class="gf-form" ng-show="ctrl.addFilterMode">
|
||||
<label class="gf-form-label" ng-show="ctrl.errors.filters">
|
||||
<a bs-tooltip="ctrl.errors.filters" style="color: rgb(229, 189, 28)" >
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
</label>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<label class="gf-form-label">
|
||||
<a ng-click="ctrl.addFilter()" ng-hide="ctrl.errors.filters">add filter</a>
|
||||
<a ng-click="ctrl.closeAddFilterMode()">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list" role="menu">
|
||||
<li class="tight-form-item tight-form-align query-keyword" style="width: 100px">
|
||||
Tags
|
||||
<tip ng-if="ctrl.tsdbVersion == 2">Please use filters, tags are deprecated in opentsdb 2.2</tip>
|
||||
</li>
|
||||
<li ng-repeat="(key, value) in ctrl.target.tags track by $index" class="tight-form-item">
|
||||
{{key}} = {{value}}
|
||||
<a ng-click="ctrl.editTag(key, value)">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a ng-click="ctrl.removeTag(key)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
|
||||
<li class="tight-form-item query-keyword" ng-hide="ctrl.addTagMode">
|
||||
<a ng-click="ctrl.addTag()">
|
||||
<i class="fa fa-plus"></i>
|
||||
</a>
|
||||
</li>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<li ng-show="ctrl.addTagMode">
|
||||
<input type="text" class="input-small tight-form-input" spellcheck='false'
|
||||
bs-typeahead="ctrl.suggestTagKeys" data-min-length=0 data-items=100
|
||||
ng-model="ctrl.target.currentTagKey" placeholder="key"></input>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-8">
|
||||
Tags
|
||||
<info-popover mode="right-normal" ng-if="ctrl.tsdbVersion == 2">
|
||||
Please use filters, tags are deprecated in opentsdb 2.2
|
||||
</info-popover>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input type="text" class="input-small tight-form-input"
|
||||
spellcheck='false' bs-typeahead="ctrl.suggestTagValues"
|
||||
data-min-length=0 data-items=100 ng-model="ctrl.target.currentTagValue" placeholder="value">
|
||||
<div class="gf-form" ng-repeat="(key, value) in ctrl.target.tags track by $index" class="gf-form">
|
||||
<label class="gf-form-label">
|
||||
{{key}} = {{value}}
|
||||
<a ng-click="ctrl.editTag(key, value)">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a ng-click="ctrl.removeTag(key)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-hide="ctrl.addTagMode">
|
||||
<label class="gf-form-label query-keyword">
|
||||
<a ng-click="ctrl.addTag()"><i class="fa fa-plus"></i></a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-show="ctrl.addTagMode">
|
||||
<input type="text"
|
||||
class="gf-form-input" spellcheck='false'
|
||||
bs-typeahead="ctrl.suggestTagKeys" data-min-length=0 data-items=100
|
||||
ng-model="ctrl.target.currentTagKey" placeholder="key">
|
||||
</input>
|
||||
|
||||
<a bs-tooltip="ctrl.errors.tags"
|
||||
style="color: rgb(229, 189, 28)"
|
||||
ng-show="ctrl.errors.tags">
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
<input type="text" class="gf-form-input"
|
||||
spellcheck='false' bs-typeahead="ctrl.suggestTagValues"
|
||||
data-min-length=0 data-items=100 ng-model="ctrl.target.currentTagValue" placeholder="value">
|
||||
</input>
|
||||
|
||||
<a ng-click="ctrl.addTag()" ng-hide="ctrl.errors.tags">
|
||||
add tag
|
||||
</a>
|
||||
<a ng-click="ctrl.closeAddTagMode()">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<label class="gf-form-label" ng-show="ctrl.errors.tags">
|
||||
<a bs-tooltip="ctrl.errors.tags" style="color: rgb(229, 189, 28)" >
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
</label>
|
||||
<label class="gf-form-label" >
|
||||
<a ng-click="ctrl.addTag()" ng-hide="ctrl.errors.tags">add tag</a>
|
||||
<a ng-click="ctrl.closeAddTagMode()"><i class="fa fa-remove"></i></a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list" role="menu">
|
||||
<li class="tight-form-item tight-form-align query-keyword" style="width: 100px">
|
||||
Rate <editor-checkbox text="" model="ctrl.target.shouldComputeRate" change="ctrl.targetBlur()"></editor-checkbox>
|
||||
</li>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<li class="tight-form-item query-keyword" ng-hide="!ctrl.target.shouldComputeRate">
|
||||
Counter <editor-checkbox text="" model="ctrl.target.isCounter" change="ctrl.targetBlur()"></editor-checkbox>
|
||||
</li>
|
||||
<div class="gf-form-inline">
|
||||
<gf-form-switch class="gf-form" label="Rate" label-class="width-8 query-keyword" checked="ctrl.target.shouldComputeRate" on-change="ctrl.targetBlur()">
|
||||
</gf-form-switch>
|
||||
|
||||
<li class="tight-form-item query-keyword" ng-hide="!ctrl.target.isCounter || !ctrl.target.shouldComputeRate">
|
||||
Counter Max:
|
||||
</li>
|
||||
<gf-form-switch ng-hide="!ctrl.target.shouldComputeRate"
|
||||
class="gf-form" label="Counter" checked="ctrl.target.isCounter" on-change="ctrl.targetBlur()">
|
||||
</gf-form-switch>
|
||||
|
||||
<li ng-hide="!ctrl.target.isCounter || !ctrl.target.shouldComputeRate">
|
||||
<input type="text" class="tight-form-input input-small" ng-disabled="!ctrl.target.shouldComputeRate"
|
||||
ng-model="ctrl.target.counterMax" spellcheck='false'
|
||||
placeholder="max value" ng-model-onblur
|
||||
ng-blur="ctrl.targetBlur()"></input>
|
||||
</li>
|
||||
<li class="tight-form-item query-keyword" ng-hide="!ctrl.target.isCounter || !ctrl.target.shouldComputeRate">
|
||||
Reset Value:
|
||||
</li>
|
||||
<li ng-hide="!ctrl.target.isCounter || !ctrl.target.shouldComputeRate">
|
||||
<input type="text" class="tight-form-input input-small" ng-disabled="!ctrl.target.shouldComputeRate"
|
||||
ng-model="ctrl.target.counterResetValue" spellcheck='false'
|
||||
placeholder="reset value" ng-model-onblur
|
||||
ng-blur="ctrl.targetBlur()"></input>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="gf-form" ng-hide="!ctrl.target.isCounter || !ctrl.target.shouldComputeRate">
|
||||
<label class="gf-form-label">Counter Max</label>
|
||||
<input type="text" class="gf-form-input"
|
||||
ng-disabled="!ctrl.target.shouldComputeRate"
|
||||
ng-model="ctrl.target.counterMax" spellcheck='false'
|
||||
placeholder="max value" ng-model-onblur
|
||||
ng-blur="ctrl.targetBlur()">
|
||||
</input>
|
||||
|
||||
<label class="gf-form-label">Reset Value</label>
|
||||
<input type="text" class="tight-form-input input-small"
|
||||
ng-disabled="!ctrl.target.shouldComputeRate"
|
||||
ng-model="ctrl.target.counterResetValue" spellcheck='false'
|
||||
placeholder="reset value" ng-model-onblur
|
||||
ng-blur="ctrl.targetBlur()">
|
||||
</input>
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
</query-editor-row>
|
||||
|
||||
|
@ -1,80 +1,54 @@
|
||||
<query-editor-row ctrl="ctrl">
|
||||
<li class="tight-form-item" style="width: 94px">
|
||||
Query
|
||||
</li>
|
||||
<li>
|
||||
<input type="text"
|
||||
class="input-xxlarge tight-form-input"
|
||||
ng-model="ctrl.target.expr"
|
||||
spellcheck='false'
|
||||
placeholder="query expression"
|
||||
data-min-length=0 data-items=100
|
||||
ng-model-onblur
|
||||
ng-change="ctrl.refreshMetricData()">
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Metric
|
||||
</li>
|
||||
<li>
|
||||
<input type="text"
|
||||
class="input-medium tight-form-input"
|
||||
ng-model="ctrl.target.metric"
|
||||
spellcheck='false'
|
||||
bs-typeahead="ctrl.suggestMetrics"
|
||||
placeholder="metric name"
|
||||
data-min-length=0 data-items=100>
|
||||
</li>
|
||||
</query-editor-row>
|
||||
<query-editor-row query-ctrl="ctrl" can-collapse="false">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form gf-form--grow">
|
||||
<label class="gf-form-label width-8">Query</label>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.target.expr" spellcheck='false' placeholder="query expression" data-min-length=0 data-items=100 ng-model-onblur ng-change="ctrl.refreshMetricData()">
|
||||
</div>
|
||||
<div class="gf-form max-width-22">
|
||||
<label class="gf-form-label">Metric lookup</label>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.target.metric" spellcheck='false' bs-typeahead="ctrl.suggestMetrics" placeholder="metric name" data-min-length=0 data-items=100>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list" role="menu">
|
||||
<li class="tight-form-item tight-form-align" style="width: 94px">
|
||||
Legend format
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="tight-form-input input-xxlarge" ng-model="ctrl.target.legendFormat"
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-26">
|
||||
<label class="gf-form-label width-8">Legend format</label>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.target.legendFormat"
|
||||
spellcheck='false' placeholder="legend format" data-min-length=0 data-items=1000
|
||||
ng-model-onblur ng-change="ctrl.refreshMetricData()">
|
||||
</input>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label width-5">Step</label>
|
||||
<input type="text" class="gf-form-input max-width-5" ng-model="ctrl.target.interval"
|
||||
data-placement="right"
|
||||
spellcheck='false'
|
||||
placeholder="{{ctrl.panelCtrl.interval}}"
|
||||
data-min-length=0 data-items=100
|
||||
ng-model-onblur
|
||||
ng-change="ctrl.refreshMetricData()"/>
|
||||
<info-popover mode="right-absolute">
|
||||
Leave blank for auto handling based on time range and panel width
|
||||
</info-popover>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label">Resolution</label>
|
||||
<div class="gf-form-select-wrapper max-width-15">
|
||||
<select ng-model="ctrl.target.intervalFactor" class="gf-form-input"
|
||||
ng-options="r.factor as r.label for r in ctrl.resolutions"
|
||||
ng-change="ctrl.refreshMetricData()">
|
||||
</select>
|
||||
</div>
|
||||
<label class="gf-form-label">
|
||||
<a href="{{ctrl.linkToPrometheus}}" target="_blank" bs-tooltip="'Link to Graph in Prometheus'">
|
||||
<i class="fa fa-share-square-o"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list" role="menu">
|
||||
<li class="tight-form-item tight-form-align" style="width: 94px">
|
||||
Step
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="input-mini tight-form-input" ng-model="ctrl.target.interval"
|
||||
bs-tooltip="'Leave blank for auto handling based on time range and panel width'"
|
||||
data-placement="right"
|
||||
spellcheck='false'
|
||||
placeholder="{{ctrl.panelCtrl.interval}}"
|
||||
data-min-length=0 data-items=100
|
||||
ng-model-onblur
|
||||
ng-change="ctrl.refreshMetricData()"
|
||||
/>
|
||||
</input>
|
||||
</li>
|
||||
|
||||
<li class="tight-form-item">
|
||||
Resolution
|
||||
</li>
|
||||
<li>
|
||||
<select ng-model="ctrl.target.intervalFactor" class="tight-form-input input-mini"
|
||||
ng-options="r.factor as r.label for r in ctrl.resolutions"
|
||||
ng-change="ctrl.refreshMetricData()">
|
||||
</select>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<a href="{{ctrl.linkToPrometheus}}" target="_blank" bs-tooltip="'Link to Graph in Prometheus'">
|
||||
<i class="fa fa-share-square-o"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</query-editor-row>
|
||||
|
@ -5,27 +5,26 @@ import config from 'app/core/config';
|
||||
import {PanelCtrl} from 'app/plugins/sdk';
|
||||
import {impressions} from 'app/features/dashboard/impression_store';
|
||||
|
||||
// Set and populate defaults
|
||||
var panelDefaults = {
|
||||
query: '',
|
||||
limit: 10,
|
||||
tags: [],
|
||||
recent: false,
|
||||
search: false,
|
||||
starred: true,
|
||||
headings: true,
|
||||
};
|
||||
|
||||
class DashListCtrl extends PanelCtrl {
|
||||
static templateUrl = 'module.html';
|
||||
|
||||
groups: any[];
|
||||
modes: any[];
|
||||
|
||||
panelDefaults = {
|
||||
query: '',
|
||||
limit: 10,
|
||||
tags: [],
|
||||
recent: false,
|
||||
search: false,
|
||||
starred: true,
|
||||
headings: true,
|
||||
};
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $injector, private backendSrv) {
|
||||
super($scope, $injector);
|
||||
_.defaults(this.panel, panelDefaults);
|
||||
_.defaults(this.panel, this.panelDefaults);
|
||||
|
||||
if (this.panel.tag) {
|
||||
this.panel.tags = [this.panel.tag];
|
||||
|
@ -73,7 +73,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
var legendSeries = _.filter(data, function(series) {
|
||||
return series.hideFromLegend(panel.legend) === false;
|
||||
});
|
||||
var total = 23 + (22 * legendSeries.length);
|
||||
var total = 23 + (21 * legendSeries.length);
|
||||
return Math.min(total, Math.floor(panelHeight/2));
|
||||
} else {
|
||||
return 26;
|
||||
|
@ -13,85 +13,6 @@ import TimeSeries from 'app/core/time_series2';
|
||||
import * as fileExport from 'app/core/utils/file_export';
|
||||
import {MetricsPanelCtrl} from 'app/plugins/sdk';
|
||||
|
||||
var panelDefaults = {
|
||||
// datasource name, null = default datasource
|
||||
datasource: null,
|
||||
// sets client side (flot) or native graphite png renderer (png)
|
||||
renderer: 'flot',
|
||||
yaxes: [
|
||||
{
|
||||
label: null,
|
||||
show: true,
|
||||
logBase: 1,
|
||||
min: null,
|
||||
max: null,
|
||||
format: 'short'
|
||||
},
|
||||
{
|
||||
label: null,
|
||||
show: true,
|
||||
logBase: 1,
|
||||
min: null,
|
||||
max: null,
|
||||
format: 'short'
|
||||
}
|
||||
],
|
||||
xaxis: {
|
||||
show: true
|
||||
},
|
||||
grid : {
|
||||
threshold1: null,
|
||||
threshold2: null,
|
||||
threshold1Color: 'rgba(216, 200, 27, 0.27)',
|
||||
threshold2Color: 'rgba(234, 112, 112, 0.22)'
|
||||
},
|
||||
// show/hide lines
|
||||
lines : true,
|
||||
// fill factor
|
||||
fill : 1,
|
||||
// line width in pixels
|
||||
linewidth : 2,
|
||||
// show hide points
|
||||
points : false,
|
||||
// point radius in pixels
|
||||
pointradius : 5,
|
||||
// show hide bars
|
||||
bars : false,
|
||||
// enable/disable stacking
|
||||
stack : false,
|
||||
// stack percentage mode
|
||||
percentage : false,
|
||||
// legend options
|
||||
legend: {
|
||||
show: true, // disable/enable legend
|
||||
values: false, // disable/enable legend values
|
||||
min: false,
|
||||
max: false,
|
||||
current: false,
|
||||
total: false,
|
||||
avg: false
|
||||
},
|
||||
// how null points should be handled
|
||||
nullPointMode : 'connected',
|
||||
// staircase line mode
|
||||
steppedLine: false,
|
||||
// tooltip options
|
||||
tooltip : {
|
||||
value_type: 'cumulative',
|
||||
shared: true,
|
||||
msResolution: false,
|
||||
},
|
||||
// time overrides
|
||||
timeFrom: null,
|
||||
timeShift: null,
|
||||
// metric queries
|
||||
targets: [{}],
|
||||
// series color overrides
|
||||
aliasColors: {},
|
||||
// other style overrides
|
||||
seriesOverrides: [],
|
||||
};
|
||||
|
||||
class GraphCtrl extends MetricsPanelCtrl {
|
||||
static template = template;
|
||||
|
||||
@ -105,14 +26,93 @@ class GraphCtrl extends MetricsPanelCtrl {
|
||||
datapointsWarning: boolean;
|
||||
colors: any = [];
|
||||
|
||||
panelDefaults = {
|
||||
// datasource name, null = default datasource
|
||||
datasource: null,
|
||||
// sets client side (flot) or native graphite png renderer (png)
|
||||
renderer: 'flot',
|
||||
yaxes: [
|
||||
{
|
||||
label: null,
|
||||
show: true,
|
||||
logBase: 1,
|
||||
min: null,
|
||||
max: null,
|
||||
format: 'short'
|
||||
},
|
||||
{
|
||||
label: null,
|
||||
show: true,
|
||||
logBase: 1,
|
||||
min: null,
|
||||
max: null,
|
||||
format: 'short'
|
||||
}
|
||||
],
|
||||
xaxis: {
|
||||
show: true
|
||||
},
|
||||
grid : {
|
||||
threshold1: null,
|
||||
threshold2: null,
|
||||
threshold1Color: 'rgba(216, 200, 27, 0.27)',
|
||||
threshold2Color: 'rgba(234, 112, 112, 0.22)'
|
||||
},
|
||||
// show/hide lines
|
||||
lines : true,
|
||||
// fill factor
|
||||
fill : 1,
|
||||
// line width in pixels
|
||||
linewidth : 2,
|
||||
// show hide points
|
||||
points : false,
|
||||
// point radius in pixels
|
||||
pointradius : 5,
|
||||
// show hide bars
|
||||
bars : false,
|
||||
// enable/disable stacking
|
||||
stack : false,
|
||||
// stack percentage mode
|
||||
percentage : false,
|
||||
// legend options
|
||||
legend: {
|
||||
show: true, // disable/enable legend
|
||||
values: false, // disable/enable legend values
|
||||
min: false,
|
||||
max: false,
|
||||
current: false,
|
||||
total: false,
|
||||
avg: false
|
||||
},
|
||||
// how null points should be handled
|
||||
nullPointMode : 'connected',
|
||||
// staircase line mode
|
||||
steppedLine: false,
|
||||
// tooltip options
|
||||
tooltip : {
|
||||
value_type: 'cumulative',
|
||||
shared: true,
|
||||
msResolution: false,
|
||||
},
|
||||
// time overrides
|
||||
timeFrom: null,
|
||||
timeShift: null,
|
||||
// metric queries
|
||||
targets: [{}],
|
||||
// series color overrides
|
||||
aliasColors: {},
|
||||
// other style overrides
|
||||
seriesOverrides: [],
|
||||
};
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $injector, private annotationsSrv) {
|
||||
super($scope, $injector);
|
||||
|
||||
_.defaults(this.panel, angular.copy(panelDefaults));
|
||||
_.defaults(this.panel.tooltip, panelDefaults.tooltip);
|
||||
_.defaults(this.panel.grid, panelDefaults.grid);
|
||||
_.defaults(this.panel.legend, panelDefaults.legend);
|
||||
_.defaults(this.panel, this.panelDefaults);
|
||||
_.defaults(this.panel.tooltip, this.panelDefaults.tooltip);
|
||||
_.defaults(this.panel.grid, this.panelDefaults.grid);
|
||||
_.defaults(this.panel.legend, this.panelDefaults.legend);
|
||||
|
||||
this.colors = $scope.$root.colors;
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
</a>
|
||||
</div>
|
||||
<div class="pluginlist-item" ng-show="category.list.length === 0">
|
||||
<a class="pluginlist-link pluginlist-link-{{plugin.state}}" href="http://grafana.net/plugins/">
|
||||
<a class="pluginlist-link pluginlist-link-{{plugin.state}}" href="https://grafana.net/plugins">
|
||||
<span class="pluginlist-none-installed">No additional panels installed. <span class="pluginlist-emphasis">Browse Grafana.net</span></span>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -4,20 +4,20 @@ import _ from 'lodash';
|
||||
import config from 'app/core/config';
|
||||
import {PanelCtrl} from '../../../features/panel/panel_ctrl';
|
||||
|
||||
// Set and populate defaults
|
||||
var panelDefaults = {
|
||||
};
|
||||
|
||||
class PluginListCtrl extends PanelCtrl {
|
||||
static templateUrl = 'module.html';
|
||||
|
||||
pluginList: any[];
|
||||
viewModel: any;
|
||||
|
||||
// Set and populate defaults
|
||||
panelDefaults = {
|
||||
};
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $injector, private backendSrv, private $location) {
|
||||
super($scope, $injector);
|
||||
_.defaults(this.panel, panelDefaults);
|
||||
_.defaults(this.panel, this.panelDefaults);
|
||||
|
||||
this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
|
||||
this.pluginList = [];
|
||||
|
@ -156,6 +156,55 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="section" style="margin-bottom: 20px">
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 80px">
|
||||
<strong>Gauge</strong>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Show
|
||||
<input class="cr1" id="panel.gauge.show" type="checkbox"
|
||||
ng-model="ctrl.panel.gauge.show" ng-checked="ctrl.panel.gauge.show" ng-change="ctrl.render()">
|
||||
<label for="panel.gauge.show" class="cr1"></label>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Min
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-small tight-form-input" ng-model="ctrl.panel.gauge.minValue" ng-blur="ctrl.render()" placeholder="0"></input>
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
Max
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-small tight-form-input last" ng-model="ctrl.panel.gauge.maxValue" ng-blur="ctrl.render()" placeholder="100"></input>
|
||||
<span class="alert-state-critical" ng-show="ctrl.invalidGaugeRange">
|
||||
|
||||
<i class="fa fa-warning"></i>
|
||||
Min value is bigger than max.
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="tight-form last">
|
||||
<li class="tight-form-item">
|
||||
Threshold labels
|
||||
<input class="cr1" id="panel.gauge.thresholdLabels" type="checkbox" ng-model="ctrl.panel.gauge.thresholdLabels" ng-checked="ctrl.panel.gauge.thresholdLabels" ng-change="ctrl.render()">
|
||||
<label for="panel.gauge.thresholdLabels" class="cr1"></label>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
Threshold markers
|
||||
<input class="cr1" id="panel.gauge.thresholdMarkers" type="checkbox" ng-model="ctrl.panel.gauge.thresholdMarkers" ng-checked="ctrl.panel.gauge.thresholdMarkers" ng-change="ctrl.render()">
|
||||
<label for="panel.gauge.thresholdMarkers" class="cr1"></label>
|
||||
</li>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-row">
|
||||
<div class="section" style="margin-bottom: 20px">
|
||||
<div class="tight-form last">
|
||||
|
@ -4,43 +4,13 @@ import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import 'jquery.flot';
|
||||
import 'jquery.flot.gauge';
|
||||
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import config from 'app/core/config';
|
||||
import TimeSeries from 'app/core/time_series2';
|
||||
import {MetricsPanelCtrl} from 'app/plugins/sdk';
|
||||
|
||||
// Set and populate defaults
|
||||
var panelDefaults = {
|
||||
links: [],
|
||||
datasource: null,
|
||||
maxDataPoints: 100,
|
||||
interval: null,
|
||||
targets: [{}],
|
||||
cacheTimeout: null,
|
||||
format: 'none',
|
||||
prefix: '',
|
||||
postfix: '',
|
||||
nullText: null,
|
||||
valueMaps: [
|
||||
{ value: 'null', op: '=', text: 'N/A' }
|
||||
],
|
||||
nullPointMode: 'connected',
|
||||
valueName: 'avg',
|
||||
prefixFontSize: '50%',
|
||||
valueFontSize: '80%',
|
||||
postfixFontSize: '50%',
|
||||
thresholds: '',
|
||||
colorBackground: false,
|
||||
colorValue: false,
|
||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||
sparkline: {
|
||||
show: false,
|
||||
full: false,
|
||||
lineColor: 'rgb(31, 120, 193)',
|
||||
fillColor: 'rgba(31, 118, 189, 0.18)',
|
||||
}
|
||||
};
|
||||
|
||||
class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
static templateUrl = 'module.html';
|
||||
|
||||
@ -48,11 +18,51 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
data: any;
|
||||
fontSizes: any[];
|
||||
unitFormats: any[];
|
||||
invalidGaugeRange: boolean;
|
||||
|
||||
// Set and populate defaults
|
||||
panelDefaults = {
|
||||
links: [],
|
||||
datasource: null,
|
||||
maxDataPoints: 100,
|
||||
interval: null,
|
||||
targets: [{}],
|
||||
cacheTimeout: null,
|
||||
format: 'none',
|
||||
prefix: '',
|
||||
postfix: '',
|
||||
nullText: null,
|
||||
valueMaps: [
|
||||
{ value: 'null', op: '=', text: 'N/A' }
|
||||
],
|
||||
nullPointMode: 'connected',
|
||||
valueName: 'avg',
|
||||
prefixFontSize: '50%',
|
||||
valueFontSize: '80%',
|
||||
postfixFontSize: '50%',
|
||||
thresholds: '',
|
||||
colorBackground: false,
|
||||
colorValue: false,
|
||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||
sparkline: {
|
||||
show: false,
|
||||
full: false,
|
||||
lineColor: 'rgb(31, 120, 193)',
|
||||
fillColor: 'rgba(31, 118, 189, 0.18)',
|
||||
},
|
||||
gauge: {
|
||||
show: false,
|
||||
minValue: 0,
|
||||
maxValue: 100,
|
||||
thresholdMarkers: true,
|
||||
thresholdLabels: false
|
||||
}
|
||||
};
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $injector, private $location, private linkSrv) {
|
||||
super($scope, $injector);
|
||||
_.defaults(this.panel, panelDefaults);
|
||||
_.defaults(this.panel, this.panelDefaults);
|
||||
|
||||
this.events.on('data-received', this.onDataReceived.bind(this));
|
||||
this.events.on('data-error', this.onDataError.bind(this));
|
||||
@ -270,6 +280,109 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
return body;
|
||||
}
|
||||
|
||||
function getValueText() {
|
||||
var result = panel.prefix ? panel.prefix : '';
|
||||
result += data.valueFormated;
|
||||
result += panel.postfix ? panel.postfix : '';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function addGauge() {
|
||||
ctrl.invalidGaugeRange = false;
|
||||
if (panel.gauge.minValue > panel.gauge.maxValue) {
|
||||
ctrl.invalidGaugeRange = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var plotCanvas = $('<div></div>');
|
||||
var width = elem.width();
|
||||
var height = elem.height();
|
||||
var plotCss = {
|
||||
top: '10px',
|
||||
margin: 'auto',
|
||||
position: 'relative',
|
||||
height: (height * 0.9) + 'px',
|
||||
width: width + 'px'
|
||||
};
|
||||
|
||||
plotCanvas.css(plotCss);
|
||||
|
||||
var thresholds = [];
|
||||
for (var i = 0; i < data.thresholds.length; i++) {
|
||||
thresholds.push({
|
||||
value: data.thresholds[i],
|
||||
color: data.colorMap[i]
|
||||
});
|
||||
}
|
||||
thresholds.push({
|
||||
value: panel.gauge.maxValue,
|
||||
color: data.colorMap[data.colorMap.length - 1]
|
||||
});
|
||||
|
||||
var bgColor = config.bootData.user.lightTheme
|
||||
? 'rgb(230,230,230)'
|
||||
: 'rgb(38,38,38)';
|
||||
|
||||
|
||||
var dimension = Math.min(width, height);
|
||||
var fontSize = Math.min(dimension/4, 100);
|
||||
var gaugeWidth = Math.min(dimension/6, 60);
|
||||
var thresholdMarkersWidth = gaugeWidth/5;
|
||||
|
||||
var options = {
|
||||
series: {
|
||||
gauges: {
|
||||
gauge: {
|
||||
min: panel.gauge.minValue,
|
||||
max: panel.gauge.maxValue,
|
||||
background: { color: bgColor },
|
||||
border: { color: null },
|
||||
shadow: { show: false },
|
||||
width: gaugeWidth,
|
||||
},
|
||||
frame: { show: false },
|
||||
label: { show: false },
|
||||
layout: { margin: 0, thresholdWidth: 0 },
|
||||
cell: { border: { width: 0 } },
|
||||
threshold: {
|
||||
values: thresholds,
|
||||
label: {
|
||||
show: panel.gauge.thresholdLabels,
|
||||
margin: 8,
|
||||
font: { size: 18 }
|
||||
},
|
||||
show: panel.gauge.thresholdMarkers,
|
||||
width: thresholdMarkersWidth,
|
||||
},
|
||||
value: {
|
||||
color: panel.colorValue ? getColorForValue(data, data.valueRounded) : null,
|
||||
formatter: function() { return getValueText(); },
|
||||
font: { size: fontSize, family: 'Helvetica Neue", Helvetica, Arial, sans-serif' }
|
||||
},
|
||||
show: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
elem.append(plotCanvas);
|
||||
|
||||
var plotSeries = {
|
||||
data: [[0, data.valueRounded]]
|
||||
};
|
||||
|
||||
$.plot(plotCanvas, [plotSeries], options);
|
||||
}
|
||||
|
||||
function getGaugeFontSize() {
|
||||
if (panel.valueFontSize) {
|
||||
var num = parseInt(panel.valueFontSize.substring(0, panel.valueFontSize.length - 1));
|
||||
return (30 * (num / 100)) + 15;
|
||||
} else {
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
|
||||
function addSparkline() {
|
||||
var width = elem.width() + 20;
|
||||
if (width < 30) {
|
||||
@ -331,11 +444,11 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
|
||||
function render() {
|
||||
if (!ctrl.data) { return; }
|
||||
|
||||
ctrl.setValues(ctrl.data);
|
||||
data = ctrl.data;
|
||||
setElementHeight();
|
||||
|
||||
var body = getBigValueHtml();
|
||||
var body = panel.gauge.show ? '' : getBigValueHtml();
|
||||
|
||||
if (panel.colorBackground && !isNaN(data.valueRounded)) {
|
||||
var color = getColorForValue(data, data.valueRounded);
|
||||
@ -358,6 +471,10 @@ class SingleStatCtrl extends MetricsPanelCtrl {
|
||||
addSparkline();
|
||||
}
|
||||
|
||||
if (panel.gauge.show) {
|
||||
addGauge();
|
||||
}
|
||||
|
||||
elem.toggleClass('pointer', panel.links.length > 0);
|
||||
|
||||
if (panel.links.length > 0) {
|
||||
|
@ -10,33 +10,6 @@ import {transformDataToTable} from './transformers';
|
||||
import {tablePanelEditor} from './editor';
|
||||
import {TableRenderer} from './renderer';
|
||||
|
||||
var panelDefaults = {
|
||||
targets: [{}],
|
||||
transform: 'timeseries_to_columns',
|
||||
pageSize: null,
|
||||
showHeader: true,
|
||||
styles: [
|
||||
{
|
||||
type: 'date',
|
||||
pattern: 'Time',
|
||||
dateFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
{
|
||||
unit: 'short',
|
||||
type: 'number',
|
||||
decimals: 2,
|
||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||
colorMode: null,
|
||||
pattern: '/.*/',
|
||||
thresholds: [],
|
||||
}
|
||||
],
|
||||
columns: [],
|
||||
scroll: true,
|
||||
fontSize: '100%',
|
||||
sort: {col: 0, desc: true},
|
||||
};
|
||||
|
||||
class TablePanelCtrl extends MetricsPanelCtrl {
|
||||
static templateUrl = 'module.html';
|
||||
|
||||
@ -44,6 +17,33 @@ class TablePanelCtrl extends MetricsPanelCtrl {
|
||||
dataRaw: any;
|
||||
table: any;
|
||||
|
||||
panelDefaults = {
|
||||
targets: [{}],
|
||||
transform: 'timeseries_to_columns',
|
||||
pageSize: null,
|
||||
showHeader: true,
|
||||
styles: [
|
||||
{
|
||||
type: 'date',
|
||||
pattern: 'Time',
|
||||
dateFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
{
|
||||
unit: 'short',
|
||||
type: 'number',
|
||||
decimals: 2,
|
||||
colors: ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||
colorMode: null,
|
||||
pattern: '/.*/',
|
||||
thresholds: [],
|
||||
}
|
||||
],
|
||||
columns: [],
|
||||
scroll: true,
|
||||
fontSize: '100%',
|
||||
sort: {col: 0, desc: true},
|
||||
};
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $injector, private annotationsSrv) {
|
||||
super($scope, $injector);
|
||||
@ -56,7 +56,7 @@ class TablePanelCtrl extends MetricsPanelCtrl {
|
||||
delete this.panel.fields;
|
||||
}
|
||||
|
||||
_.defaults(this.panel, panelDefaults);
|
||||
_.defaults(this.panel, this.panelDefaults);
|
||||
|
||||
this.events.on('data-received', this.onDataReceived.bind(this));
|
||||
this.events.on('data-error', this.onDataError.bind(this));
|
||||
@ -120,6 +120,11 @@ class TablePanelCtrl extends MetricsPanelCtrl {
|
||||
}
|
||||
|
||||
toggleColumnSort(col, colIndex) {
|
||||
// remove sort flag from current column
|
||||
if (this.table.columns[this.panel.sort.col]) {
|
||||
this.table.columns[this.panel.sort.col].sort = false;
|
||||
}
|
||||
|
||||
if (this.panel.sort.col === colIndex) {
|
||||
if (this.panel.sort.desc) {
|
||||
this.panel.sort.desc = false;
|
||||
|
@ -30,7 +30,7 @@ export class TableRenderer {
|
||||
}
|
||||
|
||||
if (_.isArray(v)) {
|
||||
v = v.join(', ');
|
||||
v = v.join(', ');
|
||||
}
|
||||
|
||||
return v;
|
||||
|
@ -3,23 +3,21 @@
|
||||
import _ from 'lodash';
|
||||
import {PanelCtrl} from 'app/plugins/sdk';
|
||||
|
||||
// Set and populate defaults
|
||||
var panelDefaults = {
|
||||
mode : "markdown", // 'html', 'markdown', 'text'
|
||||
content : "# title",
|
||||
};
|
||||
|
||||
export class TextPanelCtrl extends PanelCtrl {
|
||||
static templateUrl = `public/app/plugins/panel/text/module.html`;
|
||||
|
||||
remarkable: any;
|
||||
content: string;
|
||||
|
||||
// Set and populate defaults
|
||||
panelDefaults = {
|
||||
mode : "markdown", // 'html', 'markdown', 'text'
|
||||
content : "# title",
|
||||
};
|
||||
/** @ngInject */
|
||||
constructor($scope, $injector, private templateSrv, private $sce) {
|
||||
super($scope, $injector);
|
||||
|
||||
_.defaults(this.panel, panelDefaults);
|
||||
_.defaults(this.panel, this.panelDefaults);
|
||||
|
||||
this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
|
||||
this.events.on('refresh', this.onRender.bind(this));
|
||||
|
@ -27,7 +27,8 @@ System.config({
|
||||
"jquery.flot.stackpercent": "vendor/flot/jquery.flot.stackpercent",
|
||||
"jquery.flot.time": "vendor/flot/jquery.flot.time",
|
||||
"jquery.flot.crosshair": "vendor/flot/jquery.flot.crosshair",
|
||||
"jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow"
|
||||
"jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow",
|
||||
"jquery.flot.gauge": "vendor/flot/jquery.flot.gauge"
|
||||
},
|
||||
|
||||
packages: {
|
||||
|
@ -70,6 +70,7 @@
|
||||
@import "components/drop";
|
||||
@import "components/query_editor";
|
||||
@import "components/tabbed_view";
|
||||
@import "components/query_part";
|
||||
|
||||
// PAGES
|
||||
@import "pages/login";
|
||||
|
@ -67,8 +67,8 @@ $page-gradient: linear-gradient(60deg, transparent 70%, darken($page-bg, 4%) 98%
|
||||
|
||||
// Links
|
||||
// -------------------------
|
||||
$link-color: darken($white,11%);
|
||||
$link-color-disabled: darken($link-color,30%);
|
||||
$link-color: darken($white, 11%);
|
||||
$link-color-disabled: darken($link-color, 30%);
|
||||
$link-hover-color: $white;
|
||||
$external-link-color: $blue;
|
||||
|
||||
@ -76,7 +76,7 @@ $external-link-color: $blue;
|
||||
// -------------------------
|
||||
$headings-color: darken($white,11%);
|
||||
$abbr-border-color: $gray-3 !default;
|
||||
$text-muted: darken($link-color,30%);
|
||||
$text-muted: $text-color-weak;
|
||||
|
||||
$blockquote-small-color: $gray-3 !default;
|
||||
$blockquote-border-color: $gray-4 !default;
|
||||
|
@ -82,7 +82,7 @@ $external-link-color: $blue;
|
||||
// -------------------------
|
||||
$headings-color: $text-color;
|
||||
$abbr-border-color: $gray-2 !default;
|
||||
$text-muted: darken($link-color,30%);
|
||||
$text-muted: $text-color-weak;
|
||||
|
||||
$blockquote-small-color: $gray-2 !default;
|
||||
$blockquote-border-color: $gray-3 !default;
|
||||
|
@ -18,6 +18,8 @@ pre {
|
||||
code {
|
||||
color: $text-color;
|
||||
white-space: nowrap;
|
||||
padding: 2px 5px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
code.code--small {
|
||||
@ -26,10 +28,6 @@ code.code--small {
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
p.code--line {
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
// Blocks of code
|
||||
pre {
|
||||
display: block;
|
||||
|
@ -27,9 +27,9 @@ em { font-style: italic; color: $headings-color; }
|
||||
cite { font-style: normal; }
|
||||
|
||||
// Utility classes
|
||||
.muted { color: $gray-2; }
|
||||
.muted { color: $text-muted; }
|
||||
a.muted:hover,
|
||||
a.muted:focus { color: darken($gray-2, 10%); }
|
||||
a.muted:focus { color: darken($text-muted, 10%); }
|
||||
|
||||
.text-warning { color: $state-warning-text; }
|
||||
a.text-warning:hover,
|
||||
@ -118,6 +118,10 @@ small,
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.small-xs {
|
||||
font-size: $font-size-xs;
|
||||
}
|
||||
|
||||
mark,
|
||||
.mark {
|
||||
padding: .2em;
|
||||
|
@ -1,4 +1,4 @@
|
||||
$popover-arrow-size: 1rem;
|
||||
$popover-arrow-size: 0.7rem;
|
||||
$color: inherit;
|
||||
$backgroundColor: $btn-secondary-bg;
|
||||
$color: $text-color;
|
||||
|
@ -67,18 +67,20 @@
|
||||
}
|
||||
|
||||
// Links within the dropdown menu
|
||||
> li > a {
|
||||
display: block;
|
||||
padding: 3px 20px 3px 15px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: $line-height-base;
|
||||
color: $dropdownLinkColor;
|
||||
white-space: nowrap;
|
||||
> li {
|
||||
> a {
|
||||
display: block;
|
||||
padding: 3px 20px 3px 15px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: $line-height-base;
|
||||
color: $dropdownLinkColor;
|
||||
white-space: nowrap;
|
||||
|
||||
i {
|
||||
padding-right: 5px;
|
||||
color: $link-color-disabled;
|
||||
i {
|
||||
padding-right: 5px;
|
||||
color: $link-color-disabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,22 @@ $gf-form-margin: 0.25rem;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
|
||||
.cr1 {
|
||||
margin-left: 8px;
|
||||
&--offset-1 {
|
||||
margin-left: $spacer;
|
||||
}
|
||||
|
||||
&--grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.gf-form-disabled {
|
||||
color: $text-color-weak;
|
||||
|
||||
.query-keyword,
|
||||
a,
|
||||
.gf-form-input {
|
||||
color: $text-color-weak;
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,10 +36,6 @@ $gf-form-margin: 0.25rem;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
|
||||
.gf-form-flex {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.gf-form-button-row {
|
||||
@ -48,6 +58,12 @@ $gf-form-margin: 0.25rem;
|
||||
|
||||
border: $input-btn-border-width solid transparent;
|
||||
@include border-radius($label-border-radius-sm);
|
||||
|
||||
|
||||
&--grow {
|
||||
flex-grow: 1;
|
||||
min-height: 2.70rem;
|
||||
}
|
||||
}
|
||||
|
||||
.gf-form-checkbox {
|
||||
@ -107,6 +123,21 @@ $gf-form-margin: 0.25rem;
|
||||
}
|
||||
|
||||
&.gf-size-auto { width: auto; }
|
||||
|
||||
&--dropdown {
|
||||
padding-right: $input-padding-x*2;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 35%;
|
||||
right: $input-padding-x/2;
|
||||
background-color: transparent;
|
||||
color: $input-color;
|
||||
font: normal normal normal $font-size-sm/1 FontAwesome;
|
||||
content: '\f0d7';
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gf-form-select-wrapper {
|
||||
@ -152,9 +183,11 @@ $gf-form-margin: 0.25rem;
|
||||
}
|
||||
|
||||
.gf-form-btn {
|
||||
margin-right: $gf-form-margin;
|
||||
padding: $input-padding-y $input-padding-x;
|
||||
margin-right: $gf-form-margin;
|
||||
line-height: $input-line-height;
|
||||
font-size: $font-size-sm;
|
||||
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
}
|
||||
@ -209,4 +242,3 @@ $gf-form-margin: 0.25rem;
|
||||
select.gf-form-input ~ .gf-form-help-icon {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,8 @@
|
||||
}
|
||||
|
||||
.graph-legend-table {
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
.graph-legend-series {
|
||||
display: table-row;
|
||||
|
@ -48,4 +48,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
#flotGagueValue0 {
|
||||
font-weight: bold; //please dont hurt me for this!
|
||||
}
|
||||
|
||||
|
||||
|
@ -3,4 +3,6 @@
|
||||
ul {
|
||||
margin: 0 0 $spacer $spacer * 1.5;
|
||||
}
|
||||
li {line-height: 2;}
|
||||
a { color: $external-link-color; }
|
||||
}
|
||||
|
@ -4,13 +4,66 @@
|
||||
}
|
||||
|
||||
.query-segment-key {
|
||||
border-right: none;
|
||||
padding-right: 1px;
|
||||
//border-right: none;
|
||||
//padding-right: 1px;
|
||||
}
|
||||
|
||||
.query-segment-operator {
|
||||
padding-right: 1px;
|
||||
border-right: none;
|
||||
//padding-right: 1px;
|
||||
//border-right: none;
|
||||
color: $orange;
|
||||
}
|
||||
|
||||
.gf-form-query {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-content: flex-start;
|
||||
align-items: flex-start;
|
||||
|
||||
.gf-form,
|
||||
.gf-form-filler {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.gf-form-switch,
|
||||
.gf-form-switch label,
|
||||
.gf-form-input,
|
||||
.gf-form-select-wrapper,
|
||||
.gf-form-filler,
|
||||
.gf-form-label {
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.gf-form-query-content {
|
||||
flex-grow: 2;
|
||||
|
||||
&--collapsed {
|
||||
overflow: hidden;
|
||||
|
||||
.gf-form-label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gf-form-query-letter-cell {
|
||||
.gf-form-query-letter-cell-carret {
|
||||
display: inline-block;
|
||||
width: 0.7rem;
|
||||
position: relative;
|
||||
left: -2px;
|
||||
}
|
||||
.gf-form-query-letter-cell-letter {
|
||||
font-weight: bold;
|
||||
color: $blue;
|
||||
}
|
||||
.gf-form-query-letter-cell-ds {
|
||||
color: $text-color-weak;
|
||||
}
|
||||
}
|
||||
|
||||
|
11
public/sass/components/_query_part.scss
Normal file
11
public/sass/components/_query_part.scss
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
.query-part {
|
||||
background-color: $input-bg !important;
|
||||
|
||||
&.show-function-controls {
|
||||
padding-top: 5px;
|
||||
min-width: 100px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
font-family: inherit;
|
||||
background: $theme-bg;
|
||||
color: $theme-color;
|
||||
padding: $spacer;
|
||||
padding: 0.65rem;
|
||||
font-size: $font-size-sm;
|
||||
max-width: 20rem;
|
||||
|
||||
|
@ -61,13 +61,14 @@ button.close {
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
// Visibility
|
||||
.invisible {
|
||||
visibility: hidden;
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
// For Affix plugin
|
||||
|
@ -17,3 +17,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
@for $i from 1 through 30 {
|
||||
.offset-width-#{$i} {
|
||||
margin-left: ($spacer * $i) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,6 +99,11 @@ define([
|
||||
var target = _templateSrv.replace('this.$test.filters', {}, 'glob');
|
||||
expect(target).to.be('this.*.filters');
|
||||
});
|
||||
|
||||
it('should not escape custom all value', function() {
|
||||
var target = _templateSrv.replace('this.$test', {}, 'regex');
|
||||
expect(target).to.be('this.*');
|
||||
});
|
||||
});
|
||||
|
||||
describe('lucene format', function() {
|
||||
@ -127,7 +132,7 @@ define([
|
||||
|
||||
it('multi value and regex format should render regex string', function() {
|
||||
var result = _templateSrv.formatValue(['test.','test2'], 'regex');
|
||||
expect(result).to.be('test\\.|test2');
|
||||
expect(result).to.be('(test\\.|test2)');
|
||||
});
|
||||
|
||||
it('multi value and pipe should render pipe string', function() {
|
||||
|
@ -280,7 +280,7 @@ define([
|
||||
});
|
||||
|
||||
it('should add All option with custom value', function() {
|
||||
expect(scenario.variable.options[0].value).to.be('*');
|
||||
expect(scenario.variable.options[0].value).to.be('$__all');
|
||||
});
|
||||
});
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user