Merge remote-tracking branch 'grafana/master' into influx-db-query2

This commit is contained in:
ryan
2017-04-24 09:50:23 -07:00
72 changed files with 1299 additions and 562 deletions
+1
View File
@@ -25,6 +25,7 @@ public/css/*.min.css
*.swp *.swp
.idea/ .idea/
*.iml *.iml
*.tmp
.vscode/ .vscode/
/data/* /data/*
+1
View File
@@ -19,6 +19,7 @@
* **Units**: New number format: Scientific notation [#7781](https://github.com/grafana/grafana/issues/7781) thx [@cadnce](https://github.com/cadnce) * **Units**: New number format: Scientific notation [#7781](https://github.com/grafana/grafana/issues/7781) thx [@cadnce](https://github.com/cadnce)
* **Oauth**: Add common type for oauth authorization errors [#6428](https://github.com/grafana/grafana/issues/6428) thx [@amenzhinsky](https://github.com/amenzhinsky) * **Oauth**: Add common type for oauth authorization errors [#6428](https://github.com/grafana/grafana/issues/6428) thx [@amenzhinsky](https://github.com/amenzhinsky)
* **Templating**: Data source variable now supports multi value and panel repeats [#7030](https://github.com/grafana/grafana/issues/7030) thx [@mtanda](https://github.com/mtanda) * **Templating**: Data source variable now supports multi value and panel repeats [#7030](https://github.com/grafana/grafana/issues/7030) thx [@mtanda](https://github.com/mtanda)
* **Telegram**: Telegram alert is not sending metric and legend. [#8110](https://github.com/grafana/grafana/issues/8110), thx [@bashgeek](https://github.com/bashgeek)
## Fixes ## Fixes
* **Table Panel**: Fixed annotation display in table panel, [#8023](https://github.com/grafana/grafana/issues/8023) * **Table Panel**: Fixed annotation display in table panel, [#8023](https://github.com/grafana/grafana/issues/8023)
+31
View File
@@ -0,0 +1,31 @@
# Roadmap (2017-04-23)
This roadmap is a tentative plan for the core development team. Things change constantly as PRs come in and priorities change.
But it will give you an idea of our current vision and plan.
### Short term (1-4 months)
- New Heatmap Panel (Implemented and available in master)
- Support for MySQL & Postgres as data sources (Work started and a alpha version for MySQL is available in master)
- User Groups & Dashboard folders with ACLs (work started, not yet completed, https://github.com/grafana/grafana/issues/1611#issuecomment-287742633)
- Improve new user UX
- Improve docs
- Support for alerting for Elasticsearch (can be tested in [branch](https://github.com/grafana/grafana/tree/alerting-elasticsearch) but needs more work)
- Graph annotations (create from grafana, region annotations, better annotation viz)
- Improve alerting (clustering, silence rules)
### Long term
- Improved dashboard panel layout engine (to make it easier and enable more flexible layouts)
- Backend plugins to support more Auth options, Alerting data sources & notifications
- Universial time series transformations for any data source (meta queries)
- Reporting
- Web socket & live data streams
- Migrate to Angular2
### Outside contributions
We know this is being worked on right now by contributors (and we hope to merge it when it's ready).
- Dashboard revisions (be able to revert dashboard changes)
- Clustering for alert engine (load distribution)
+2
View File
@@ -10,3 +10,5 @@ mysql:
volumes: volumes:
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro - /etc/timezone:/etc/timezone:ro
command: [mysqld, --character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci, --innodb_monitor_enable=all]
+20
View File
@@ -0,0 +1,20 @@
## MySQL with Open Data Set from NYC Open Data (https://data.cityofnewyork.us)
FROM mysql:latest
ENV MYSQL_DATABASE="testdata" \
MYSQL_ROOT_PASSWORD="rootpass" \
MYSQL_USER="grafana" \
MYSQL_PASSWORD="password"
# Install requirement (wget)
RUN apt-get update && apt-get install -y wget && apt-get install unzip
# Fetch NYC Data Set
RUN wget https://data.cityofnewyork.us/download/57g5-etyj/application%2Fzip -O /tmp/data.zip && \
unzip -j /tmp/data.zip 311_Service_Requests_from_2015.csv -d /var/lib/mysql-files && \
rm /tmp/data.zip
ADD import_csv.sql /docker-entrypoint-initdb.d/
EXPOSE 3306
+9
View File
@@ -0,0 +1,9 @@
mysql_opendata:
build: blocks/mysql_opendata
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: testdata
MYSQL_USER: grafana
MYSQL_PASSWORD: password
ports:
- "3307:3306"
@@ -0,0 +1,80 @@
use testdata;
DROP TABLE IF EXISTS `nyc_open_data`;
CREATE TABLE IF NOT EXISTS `nyc_open_data` (
UniqueKey bigint(255),
`CreatedDate` varchar(255),
`ClosedDate` varchar(255),
Agency varchar(255),
AgencyName varchar(255),
ComplaintType varchar(255),
Descriptor varchar(255),
LocationType varchar(255),
IncidentZip varchar(255),
IncidentAddress varchar(255),
StreetName varchar(255),
CrossStreet1 varchar(255),
CrossStreet2 varchar(255),
IntersectionStreet1 varchar(255),
IntersectionStreet2 varchar(255),
AddressType varchar(255),
City varchar(255),
Landmark varchar(255),
FacilityType varchar(255),
Status varchar(255),
`DueDate` varchar(255),
ResolutionDescription varchar(2048),
`ResolutionActionUpdatedDate` varchar(255),
CommunityBoard varchar(255),
Borough varchar(255),
XCoordinateStatePlane varchar(255),
YCoordinateStatePlane varchar(255),
ParkFacilityName varchar(255),
ParkBorough varchar(255),
SchoolName varchar(255),
SchoolNumber varchar(255),
SchoolRegion varchar(255),
SchoolCode varchar(255),
SchoolPhoneNumber varchar(255),
SchoolAddress varchar(255),
SchoolCity varchar(255),
SchoolState varchar(255),
SchoolZip varchar(255),
SchoolNotFound varchar(255),
SchoolOrCitywideComplaint varchar(255),
VehicleType varchar(255),
TaxiCompanyBorough varchar(255),
TaxiPickUpLocation varchar(255),
BridgeHighwayName varchar(255),
BridgeHighwayDirection varchar(255),
RoadRamp varchar(255),
BridgeHighwaySegment varchar(255),
GarageLotName varchar(255),
FerryDirection varchar(255),
FerryTerminalName varchar(255),
Latitude varchar(255),
Longitude varchar(255),
Location varchar(255)
);
LOAD DATA INFILE '/var/lib/mysql-files/311_Service_Requests_from_2015.csv' INTO TABLE nyc_open_data FIELDS OPTIONALLY ENCLOSED BY '"' TERMINATED BY ',' IGNORE 1 LINES;
UPDATE nyc_open_data SET CreatedDate = STR_TO_DATE(CreatedDate, '%m/%d/%Y %r') WHERE CreatedDate <> '';
UPDATE nyc_open_data SET ClosedDate = STR_TO_DATE(ClosedDate, '%m/%d/%Y %r') WHERE ClosedDate <> '';
UPDATE nyc_open_data SET DueDate = STR_TO_DATE(DueDate, '%m/%d/%Y %r') WHERE DueDate <> '';
UPDATE nyc_open_data SET ResolutionActionUpdatedDate = STR_TO_DATE(ResolutionActionUpdatedDate, '%m/%d/%Y %r') WHERE ResolutionActionUpdatedDate <> '';
UPDATE nyc_open_data SET CreatedDate=null WHERE CreatedDate = '';
UPDATE nyc_open_data SET ClosedDate=null WHERE ClosedDate = '';
UPDATE nyc_open_data SET DueDate=null WHERE DueDate = '';
UPDATE nyc_open_data SET ResolutionActionUpdatedDate=null WHERE ResolutionActionUpdatedDate = '';
ALTER TABLE nyc_open_data modify CreatedDate datetime NULL;
ALTER TABLE nyc_open_data modify ClosedDate datetime NULL;
ALTER TABLE nyc_open_data modify DueDate datetime NULL;
ALTER TABLE nyc_open_data modify ResolutionActionUpdatedDate datetime NULL;
ALTER TABLE `nyc_open_data` ADD INDEX `IX_ComplaintType` (`ComplaintType`);
ALTER TABLE `nyc_open_data` ADD INDEX `IX_CreatedDate` (`CreatedDate`);
ALTER TABLE `nyc_open_data` ADD INDEX `IX_LocationType` (`LocationType`);
ALTER TABLE `nyc_open_data` ADD INDEX `IX_AgencyName` (`AgencyName`);
ALTER TABLE `nyc_open_data` ADD INDEX `IX_City` (`City`);
SYSTEM rm /var/lib/mysql-files/311_Service_Requests_from_2015.csv
+19 -1
View File
@@ -27,6 +27,24 @@ To show all admin commands:
### Reset admin password ### Reset admin password
You can reset the password for the admin user using the CLI. You can reset the password for the admin user using the CLI. The use case for this command is when you have lost the admin password.
`grafana-cli admin reset-admin-password ...` `grafana-cli admin reset-admin-password ...`
If running the command returns this error:
> Could not find config defaults, make sure homepath command line parameter is set or working directory is homepath
then there are two flags that can be used to set homepath and the config file path.
`grafana-cli admin reset-admin-password --homepath "/usr/share/grafana" newpass`
If you have not lost the admin password then it is better to set in the Grafana UI. If you need to set the password in a script then the [Grafana API](http://docs.grafana.org/http_api/user/#change-password) can be used. Here is an example with curl using basic auth:
```
curl -X PUT -H "Content-Type: application/json" -d '{
"oldPassword": "admin",
"newPassword": "newpass",
"confirmNew": "newpass"
}' http://admin:admin@<your_grafana_host>:3000/api/user/password
```
+1 -1
View File
@@ -202,7 +202,7 @@ This API can also be used to create, update and delete alert notifications.
**Example Request**: **Example Request**:
DELETE /api/alerts-notifications/1 HTTP/1.1 DELETE /api/alert-notifications/1 HTTP/1.1
Accept: application/json Accept: application/json
Content-Type: application/json Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
+77
View File
@@ -41,3 +41,80 @@ You use the token in all requests in the `Authorization` header, like this:
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
The `Authorization` header value should be `Bearer <your api key>`. The `Authorization` header value should be `Bearer <your api key>`.
# Auth HTTP resources / actions
## Api Keys
`GET /api/auth/keys`
**Example Request**:
GET /api/auth/keys HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
**Example Response**:
HTTP/1.1 200
Content-Type: application/json
[
{
"id": 3,
"name": "API",
"role": "Admin"
},
{
"id": 1,
"name": "TestAdmin",
"role": "Admin"
}
]
## Create API Key
`POST /api/auth/keys`
**Example Request**:
POST /api/auth/keys HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
{
"name": "mykey",
"role": "Admin"
}
JSON Body schema:
- **name** – The key name
- **role** – Sets the access level/Grafana Role for the key. Can be one of the following values: `Viewer`, `Editor`, `Read Only Editor` or `Admin`.
**Example Response**:
HTTP/1.1 200
Content-Type: application/json
{"name":"mykey","key":"eyJrIjoiWHZiSWd3NzdCYUZnNUtibE9obUpESmE3bzJYNDRIc0UiLCJuIjoibXlrZXkiLCJpZCI6MX1="}
## Delete API Key
`DELETE /api/auth/keys/:id`
**Example Request**:
DELETE /api/auth/keys/3 HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
**Example Response**:
HTTP/1.1 200
Content-Type: application/json
{"message":"API key deleted"}
+2 -2
View File
@@ -76,8 +76,8 @@
"systemjs-builder": "^0.15.34", "systemjs-builder": "^0.15.34",
"tether": "^1.4.0", "tether": "^1.4.0",
"tether-drop": "https://github.com/torkelo/drop", "tether-drop": "https://github.com/torkelo/drop",
"tslint": "^4.5.1", "tslint": "^5.1.0",
"typescript": "^2.1.4", "typescript": "^2.2.2",
"virtual-scroll": "^1.1.1" "virtual-scroll": "^1.1.1"
} }
} }
+4 -1
View File
@@ -50,13 +50,16 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
return ApiError(500, "Metric request error", err) return ApiError(500, "Metric request error", err)
} }
statusCode := 200
for _, res := range resp.Results { for _, res := range resp.Results {
if res.Error != nil { if res.Error != nil {
res.ErrorString = res.Error.Error() res.ErrorString = res.Error.Error()
resp.Message = res.ErrorString
statusCode = 500
} }
} }
return Json(200, &resp) return Json(statusCode, &resp)
} }
// GET /api/tsdb/testdata/scenarios // GET /api/tsdb/testdata/scenarios
+13 -7
View File
@@ -11,22 +11,18 @@ import (
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
var configFile = flag.String("config", "", "path to config file")
var homePath = flag.String("homepath", "", "path to grafana install/home path, defaults to working directory")
func runDbCommand(command func(commandLine CommandLine) error) func(context *cli.Context) { func runDbCommand(command func(commandLine CommandLine) error) func(context *cli.Context) {
return func(context *cli.Context) { return func(context *cli.Context) {
cmd := &contextCommandLine{context}
flag.Parse()
setting.NewConfigContext(&setting.CommandLineArgs{ setting.NewConfigContext(&setting.CommandLineArgs{
Config: *configFile, Config: cmd.String("config"),
HomePath: *homePath, HomePath: cmd.String("homepath"),
Args: flag.Args(), Args: flag.Args(),
}) })
sqlstore.NewEngine() sqlstore.NewEngine()
cmd := &contextCommandLine{context}
if err := command(cmd); err != nil { if err := command(cmd); err != nil {
logger.Errorf("\n%s: ", color.RedString("Error")) logger.Errorf("\n%s: ", color.RedString("Error"))
logger.Errorf("%s\n\n", err) logger.Errorf("%s\n\n", err)
@@ -95,6 +91,16 @@ var adminCommands = []cli.Command{
Name: "reset-admin-password", Name: "reset-admin-password",
Usage: "reset-admin-password <new password>", Usage: "reset-admin-password <new password>",
Action: runDbCommand(resetPasswordCommand), Action: runDbCommand(resetPasswordCommand),
Flags: []cli.Flag{
cli.StringFlag{
Name: "homepath",
Usage: "path to grafana install/home path, defaults to working directory",
},
cli.StringFlag{
Name: "config",
Usage: "path to config file",
},
},
}, },
} }
+14 -1
View File
@@ -87,7 +87,7 @@ func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
bodyJSON.Set("chat_id", this.ChatID) bodyJSON.Set("chat_id", this.ChatID)
bodyJSON.Set("parse_mode", "html") bodyJSON.Set("parse_mode", "html")
message := fmt.Sprintf("%s\nState: %s\nMessage: %s\n", evalContext.GetNotificationTitle(), evalContext.Rule.Name, evalContext.Rule.Message) message := fmt.Sprintf("<b>%s</b>\nState: %s\nMessage: %s\n", evalContext.GetNotificationTitle(), evalContext.Rule.Name, evalContext.Rule.Message)
ruleUrl, err := evalContext.GetRuleUrl() ruleUrl, err := evalContext.GetRuleUrl()
if err == nil { if err == nil {
@@ -96,6 +96,19 @@ func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
if evalContext.ImagePublicUrl != "" { if evalContext.ImagePublicUrl != "" {
message = message + fmt.Sprintf("Image: %s\n", evalContext.ImagePublicUrl) message = message + fmt.Sprintf("Image: %s\n", evalContext.ImagePublicUrl)
} }
metrics := ""
fieldLimitCount := 4
for index, evt := range evalContext.EvalMatches {
metrics += fmt.Sprintf("\n%s: %s", evt.Metric, evt.Value)
if index > fieldLimitCount {
break
}
}
if metrics != "" {
message = message + fmt.Sprintf("\n<i>Metrics:</i>%s", metrics)
}
bodyJSON.Set("text", message) bodyJSON.Set("text", message)
url := fmt.Sprintf(telegeramApiUrl, this.BotToken, "sendMessage") url := fmt.Sprintf(telegeramApiUrl, this.BotToken, "sendMessage")
@@ -24,7 +24,6 @@ func AddMigrations(mg *Migrator) {
addPreferencesMigrations(mg) addPreferencesMigrations(mg)
addAlertMigrations(mg) addAlertMigrations(mg)
addAnnotationMig(mg) addAnnotationMig(mg)
addStatsMigrations(mg)
addTestDataMigrations(mg) addTestDataMigrations(mg)
} }
+2 -7
View File
@@ -14,7 +14,7 @@ func init() {
func sqlRandomWalk(m1 string, m2 string, intWalker int64, floatWalker float64, sess *session) error { func sqlRandomWalk(m1 string, m2 string, intWalker int64, floatWalker float64, sess *session) error {
timeWalker := time.Now().UTC().Add(time.Hour * -1) timeWalker := time.Now().UTC().Add(time.Hour * -200)
now := time.Now().UTC() now := time.Now().UTC()
step := time.Minute step := time.Minute
@@ -29,7 +29,7 @@ func sqlRandomWalk(m1 string, m2 string, intWalker int64, floatWalker float64, s
timeWalker = timeWalker.Add(step) timeWalker = timeWalker.Add(step)
row.Id = 0 row.Id = 0
row.ValueBigInt += rand.Int63n(100) - 100 row.ValueBigInt += rand.Int63n(200) - 100
row.ValueDouble += rand.Float64() - 0.5 row.ValueDouble += rand.Float64() - 0.5
row.ValueFloat += rand.Float32() - 0.5 row.ValueFloat += rand.Float32() - 0.5
row.TimeEpoch = timeWalker.Unix() row.TimeEpoch = timeWalker.Unix()
@@ -61,11 +61,6 @@ func InsertSqlTestData(cmd *m.InsertSqlTestDataCommand) error {
sqlRandomWalk("server2", "frontend", 100, 1.123, sess) sqlRandomWalk("server2", "frontend", 100, 1.123, sess)
sqlRandomWalk("server3", "frontend", 100, 1.123, sess) sqlRandomWalk("server3", "frontend", 100, 1.123, sess)
sqlRandomWalk("server1", "backend", 100, 1.123, sess)
sqlRandomWalk("server2", "backend", 100, 1.123, sess)
sqlRandomWalk("server3", "backend", 100, 1.123, sess)
sqlRandomWalk("db-server1", "backend", 100, 1.123, sess)
return err return err
}) })
} }
+18 -5
View File
@@ -27,6 +27,7 @@ type Request struct {
type Response struct { type Response struct {
BatchTimings []*BatchTiming `json:"timings"` BatchTimings []*BatchTiming `json:"timings"`
Results map[string]*QueryResult `json:"results"` Results map[string]*QueryResult `json:"results"`
Message string `json:"message,omitempty"`
} }
type BatchTiming struct { type BatchTiming struct {
@@ -45,18 +46,30 @@ func (br *BatchResult) WithError(err error) *BatchResult {
} }
type QueryResult struct { type QueryResult struct {
Error error `json:"-"` Error error `json:"-"`
ErrorString string `json:"error"` ErrorString string `json:"error,omitempty"`
RefId string `json:"refId"` RefId string `json:"refId"`
Series TimeSeriesSlice `json:"series"` Meta *simplejson.Json `json:"meta,omitempty"`
Series TimeSeriesSlice `json:"series"`
Tables []*Table `json:"tables"`
} }
type TimeSeries struct { type TimeSeries struct {
Name string `json:"name"` Name string `json:"name"`
Points TimeSeriesPoints `json:"points"` Points TimeSeriesPoints `json:"points"`
Tags map[string]string `json:"tags"` Tags map[string]string `json:"tags,omitempty"`
} }
type Table struct {
Columns []TableColumn `json:"columns"`
Rows []RowValues `json:"rows"`
}
type TableColumn struct {
Text string `json:"text"`
}
type RowValues []interface{}
type TimePoint [2]null.Float type TimePoint [2]null.Float
type TimeSeriesPoints []TimePoint type TimeSeriesPoints []TimePoint
type TimeSeriesSlice []*TimeSeries type TimeSeriesSlice []*TimeSeries
+80
View File
@@ -0,0 +1,80 @@
package mysql
import (
"fmt"
"regexp"
"github.com/grafana/grafana/pkg/tsdb"
)
//const rsString = `(?:"([^"]*)")`;
const rsIdentifier = `([_a-zA-Z0-9]+)`
const sExpr = `\$` + rsIdentifier + `\(([^\)]*)\)`
type SqlMacroEngine interface {
Interpolate(sql string) (string, error)
}
type MySqlMacroEngine struct {
TimeRange *tsdb.TimeRange
}
func NewMysqlMacroEngine(timeRange *tsdb.TimeRange) SqlMacroEngine {
return &MySqlMacroEngine{
TimeRange: timeRange,
}
}
func (m *MySqlMacroEngine) Interpolate(sql string) (string, error) {
rExp, _ := regexp.Compile(sExpr)
var macroError error
sql = ReplaceAllStringSubmatchFunc(rExp, sql, func(groups []string) string {
res, err := m.EvaluateMacro(groups[1], groups[2:])
if err != nil && macroError == nil {
macroError = err
return "macro_error()"
}
return res
})
if macroError != nil {
return "", macroError
}
return sql, nil
}
func ReplaceAllStringSubmatchFunc(re *regexp.Regexp, str string, repl func([]string) string) string {
result := ""
lastIndex := 0
for _, v := range re.FindAllSubmatchIndex([]byte(str), -1) {
groups := []string{}
for i := 0; i < len(v); i += 2 {
groups = append(groups, str[v[i]:v[i+1]])
}
result += str[lastIndex:v[0]] + repl(groups)
lastIndex = v[1]
}
return result + str[lastIndex:]
}
func (m *MySqlMacroEngine) EvaluateMacro(name string, args []string) (string, error) {
switch name {
case "__time":
if len(args) == 0 {
return "", fmt.Errorf("missing time column argument for macro %v", name)
}
return fmt.Sprintf("UNIX_TIMESTAMP(%s) as time_sec", args[0]), nil
case "__timeFilter":
if len(args) == 0 {
return "", fmt.Errorf("missing time column argument for macro %v", name)
}
return fmt.Sprintf("%s > FROM_UNIXTIME(%d) AND %s < FROM_UNIXTIME(%d)", args[0], uint64(m.TimeRange.GetFromAsMsEpoch()/1000), args[0], uint64(m.TimeRange.GetToAsMsEpoch()/1000)), nil
default:
return "", fmt.Errorf("Unknown macro %v", name)
}
}
+43
View File
@@ -0,0 +1,43 @@
package mysql
import (
"testing"
"github.com/grafana/grafana/pkg/tsdb"
. "github.com/smartystreets/goconvey/convey"
)
func TestMacroEngine(t *testing.T) {
Convey("MacroEngine", t, func() {
Convey("interpolate __time function", func() {
engine := &MySqlMacroEngine{}
sql, err := engine.Interpolate("select $__time(time_column)")
So(err, ShouldBeNil)
So(sql, ShouldEqual, "select UNIX_TIMESTAMP(time_column) as time_sec")
})
Convey("interpolate __time function wrapped in aggregation", func() {
engine := &MySqlMacroEngine{}
sql, err := engine.Interpolate("select min($__time(time_column))")
So(err, ShouldBeNil)
So(sql, ShouldEqual, "select min(UNIX_TIMESTAMP(time_column) as time_sec)")
})
Convey("interpolate __timeFilter function", func() {
engine := &MySqlMacroEngine{
TimeRange: &tsdb.TimeRange{From: "5m", To: "now"},
}
sql, err := engine.Interpolate("WHERE $__timeFilter(time_column)")
So(err, ShouldBeNil)
So(sql, ShouldEqual, "WHERE time_column > FROM_UNIXTIME(18446744066914186738) AND time_column < FROM_UNIXTIME(18446744066914187038)")
})
})
}
+117 -14
View File
@@ -7,9 +7,13 @@ import (
"strconv" "strconv"
"sync" "sync"
"time"
"github.com/go-sql-driver/mysql"
"github.com/go-xorm/core" "github.com/go-xorm/core"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
"github.com/grafana/grafana/pkg/components/null" "github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb" "github.com/grafana/grafana/pkg/tsdb"
@@ -81,6 +85,7 @@ func (e *MysqlExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, co
QueryResults: make(map[string]*tsdb.QueryResult), QueryResults: make(map[string]*tsdb.QueryResult),
} }
macroEngine := NewMysqlMacroEngine(context.TimeRange)
session := e.engine.NewSession() session := e.engine.NewSession()
defer session.Close() defer session.Close()
db := session.DB() db := session.DB()
@@ -91,48 +96,145 @@ func (e *MysqlExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, co
continue continue
} }
queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: query.RefId}
result.QueryResults[query.RefId] = queryResult
rawSql, err := macroEngine.Interpolate(rawSql)
if err != nil {
queryResult.Error = err
continue
}
queryResult.Meta.Set("sql", rawSql)
rows, err := db.Query(rawSql) rows, err := db.Query(rawSql)
if err != nil { if err != nil {
result.QueryResults[query.RefId] = &tsdb.QueryResult{Error: err} queryResult.Error = err
continue continue
} }
defer rows.Close() defer rows.Close()
result.QueryResults[query.RefId] = e.TransformToTimeSeries(query, rows) format := query.Model.Get("format").MustString("time_series")
switch format {
case "time_series":
err := e.TransformToTimeSeries(query, rows, queryResult)
if err != nil {
queryResult.Error = err
continue
}
case "table":
err := e.TransformToTable(query, rows, queryResult)
if err != nil {
queryResult.Error = err
continue
}
}
} }
return result return result
} }
func (e MysqlExecutor) TransformToTimeSeries(query *tsdb.Query, rows *core.Rows) *tsdb.QueryResult { func (e MysqlExecutor) TransformToTable(query *tsdb.Query, rows *core.Rows, result *tsdb.QueryResult) error {
result := &tsdb.QueryResult{RefId: query.RefId} columnNames, err := rows.Columns()
columnCount := len(columnNames)
if err != nil {
return err
}
table := &tsdb.Table{
Columns: make([]tsdb.TableColumn, columnCount),
Rows: make([]tsdb.RowValues, 0),
}
for i, name := range columnNames {
table.Columns[i].Text = name
}
columnTypes, err := rows.ColumnTypes()
if err != nil {
return err
}
rowLimit := 1000000
rowCount := 0
for ; rows.Next(); rowCount += 1 {
if rowCount > rowLimit {
return fmt.Errorf("MySQL query row limit exceeded, limit %d", rowLimit)
}
values, err := e.getTypedRowData(columnTypes, rows)
if err != nil {
return err
}
table.Rows = append(table.Rows, values)
}
result.Tables = append(result.Tables, table)
result.Meta.Set("rowCount", rowCount)
return nil
}
func (e MysqlExecutor) getTypedRowData(types []*sql.ColumnType, rows *core.Rows) (tsdb.RowValues, error) {
values := make([]interface{}, len(types))
for i, stype := range types {
switch stype.DatabaseTypeName() {
case mysql.FieldTypeNameVarString:
values[i] = new(string)
case mysql.FieldTypeNameLongLong:
values[i] = new(int64)
case mysql.FieldTypeNameDouble:
values[i] = new(float64)
case mysql.FieldTypeNameDateTime:
values[i] = new(time.Time)
default:
return nil, fmt.Errorf("Database type %s not supported", stype.DatabaseTypeName())
}
}
if err := rows.Scan(values...); err != nil {
return nil, err
}
return values, nil
}
func (e MysqlExecutor) TransformToTimeSeries(query *tsdb.Query, rows *core.Rows, result *tsdb.QueryResult) error {
pointsBySeries := make(map[string]*tsdb.TimeSeries) pointsBySeries := make(map[string]*tsdb.TimeSeries)
columnNames, err := rows.Columns() columnNames, err := rows.Columns()
if err != nil { if err != nil {
result.Error = err return err
return result
} }
rowData := NewStringStringScan(columnNames) rowData := NewStringStringScan(columnNames)
for rows.Next() { rowLimit := 1000000
rowCount := 0
for ; rows.Next(); rowCount += 1 {
if rowCount > rowLimit {
return fmt.Errorf("MySQL query row limit exceeded, limit %d", rowLimit)
}
err := rowData.Update(rows.Rows) err := rowData.Update(rows.Rows)
if err != nil { if err != nil {
e.log.Error("Mysql response parsing", "error", err) e.log.Error("MySQL response parsing", "error", err)
result.Error = err return fmt.Errorf("MySQL response parsing error %v", err)
return result
} }
if rowData.metric == "" { if rowData.metric == "" {
rowData.metric = "Unknown" rowData.metric = "Unknown"
} }
e.log.Info("Rows", "metric", rowData.metric, "time", rowData.time, "value", rowData.value) //e.log.Debug("Rows", "metric", rowData.metric, "time", rowData.time, "value", rowData.value)
if !rowData.time.Valid { if !rowData.time.Valid {
result.Error = fmt.Errorf("Found row with no time value") return fmt.Errorf("Found row with no time value")
return result
} }
if series, exist := pointsBySeries[rowData.metric]; exist { if series, exist := pointsBySeries[rowData.metric]; exist {
@@ -148,7 +250,8 @@ func (e MysqlExecutor) TransformToTimeSeries(query *tsdb.Query, rows *core.Rows)
result.Series = append(result.Series, value) result.Series = append(result.Series, value)
} }
return result result.Meta.Set("rowCount", rowCount)
return nil
} }
type stringStringScan struct { type stringStringScan struct {
+6 -6
View File
@@ -117,7 +117,7 @@ export class SearchCtrl {
queryHasNoFilters() { queryHasNoFilters() {
var query = this.query; var query = this.query;
return query.query === '' && query.starred === false && query.tag.length === 0; return query.query === '' && query.starred === false && query.tag.length === 0;
}; }
filterByTag(tag, evt) { filterByTag(tag, evt) {
this.query.tag.push(tag); this.query.tag.push(tag);
@@ -127,7 +127,7 @@ export class SearchCtrl {
evt.stopPropagation(); evt.stopPropagation();
evt.preventDefault(); evt.preventDefault();
} }
}; }
removeTag(tag, evt) { removeTag(tag, evt) {
this.query.tag = _.without(this.query.tag, tag); this.query.tag = _.without(this.query.tag, tag);
@@ -135,7 +135,7 @@ export class SearchCtrl {
this.giveSearchFocus = this.giveSearchFocus + 1; this.giveSearchFocus = this.giveSearchFocus + 1;
evt.stopPropagation(); evt.stopPropagation();
evt.preventDefault(); evt.preventDefault();
}; }
getTags() { getTags() {
return this.backendSrv.get('/api/dashboards/tags').then((results) => { return this.backendSrv.get('/api/dashboards/tags').then((results) => {
@@ -146,19 +146,19 @@ export class SearchCtrl {
this.search(); this.search();
} }
}); });
}; }
showStarred() { showStarred() {
this.query.starred = !this.query.starred; this.query.starred = !this.query.starred;
this.giveSearchFocus = this.giveSearchFocus + 1; this.giveSearchFocus = this.giveSearchFocus + 1;
this.search(); this.search();
}; }
search() { search() {
this.showImport = false; this.showImport = false;
this.selectedIndex = 0; this.selectedIndex = 0;
this.searchDashboards(); this.searchDashboards();
}; }
} }
@@ -84,7 +84,11 @@ export class SideMenuCtrl {
return; return;
} }
if (this.orgItems.length < this.maxShownOrgs && (this.orgFilter === '' || org.name.indexOf(this.orgFilter) !== -1)){ if (this.orgItems.length === this.maxShownOrgs) {
return;
}
if (this.orgFilter === '' || (org.name.toLowerCase().indexOf(this.orgFilter.toLowerCase()) !== -1)) {
this.orgItems.push({ this.orgItems.push({
text: "Switch to " + org.name, text: "Switch to " + org.name,
icon: "fa fa-fw fa-random", icon: "fa fa-fw fa-random",
+1 -1
View File
@@ -44,7 +44,7 @@ export class SignUpCtrl {
window.location.href = config.appSubUrl + '/'; window.location.href = config.appSubUrl + '/';
} }
}); });
}; }
} }
coreModule.controller('SignUpCtrl', SignUpCtrl); coreModule.controller('SignUpCtrl', SignUpCtrl);
@@ -75,7 +75,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
if (!PanelCtrl || PanelCtrl.registered) { if (!PanelCtrl || PanelCtrl.registered) {
return componentInfo; return componentInfo;
}; }
if (PanelCtrl.templatePromise) { if (PanelCtrl.templatePromise) {
return PanelCtrl.templatePromise.then(res => { return PanelCtrl.templatePromise.then(res => {
+3 -3
View File
@@ -23,7 +23,7 @@ export class BackendSrv {
post(url, data) { post(url, data) {
return this.request({ method: 'POST', url: url, data: data }); return this.request({ method: 'POST', url: url, data: data });
}; }
patch(url, data) { patch(url, data) {
return this.request({ method: 'PATCH', url: url, data: data }); return this.request({ method: 'PATCH', url: url, data: data });
@@ -98,7 +98,7 @@ export class BackendSrv {
this.$timeout(this.requestErrorHandler.bind(this, err), 50); this.$timeout(this.requestErrorHandler.bind(this, err), 50);
throw err; throw err;
}); });
}; }
addCanceler(requestId, canceler) { addCanceler(requestId, canceler) {
if (requestId in this.inFlightRequests) { if (requestId in this.inFlightRequests) {
@@ -186,7 +186,7 @@ export class BackendSrv {
this.inFlightRequests[options.requestId].shift(); this.inFlightRequests[options.requestId].shift();
} }
}); });
}; }
loginPing() { loginPing() {
return this.request({url: '/api/login/ping', method: 'GET', retry: 1 }); return this.request({url: '/api/login/ping', method: 'GET', retry: 1 });
+1 -1
View File
@@ -92,7 +92,7 @@ export default class TimeSeries {
this.yaxis = override.yaxis; this.yaxis = override.yaxis;
} }
} }
}; }
getFlotPairs(fillStyle) { getFlotPairs(fillStyle) {
var result = []; var result = [];
+4 -4
View File
@@ -12,7 +12,7 @@ export function exportSeriesListToCsv(seriesList) {
}); });
}); });
saveSaveBlob(text, 'grafana_data_export.csv'); saveSaveBlob(text, 'grafana_data_export.csv');
}; }
export function exportSeriesListToCsvColumns(seriesList) { export function exportSeriesListToCsvColumns(seriesList) {
var text = 'sep=;\nTime;'; var text = 'sep=;\nTime;';
@@ -47,7 +47,7 @@ export function exportSeriesListToCsvColumns(seriesList) {
text += '\n'; text += '\n';
} }
saveSaveBlob(text, 'grafana_data_export.csv'); saveSaveBlob(text, 'grafana_data_export.csv');
}; }
export function exportTableDataToCsv(table) { export function exportTableDataToCsv(table) {
var text = 'sep=;\n'; var text = 'sep=;\n';
@@ -64,9 +64,9 @@ export function exportTableDataToCsv(table) {
text += '\n'; text += '\n';
}); });
saveSaveBlob(text, 'grafana_data_export.csv'); saveSaveBlob(text, 'grafana_data_export.csv');
}; }
export function saveSaveBlob(payload, fname) { export function saveSaveBlob(payload, fname) {
var blob = new Blob([payload], { type: "text/csv;charset=utf-8" }); var blob = new Blob([payload], { type: "text/csv;charset=utf-8" });
window.saveAs(blob, fname); window.saveAs(blob, fname);
}; }
@@ -69,7 +69,7 @@ export class AnnotationsEditorCtrl {
this.reset(); this.reset();
this.mode = 'list'; this.mode = 'list';
this.$scope.broadcastRefresh(); this.$scope.broadcastRefresh();
}; }
add() { add() {
this.annotations.push(this.currentAnnotation); this.annotations.push(this.currentAnnotation);
@@ -77,7 +77,7 @@ export class AnnotationsEditorCtrl {
this.mode = 'list'; this.mode = 'list';
this.$scope.broadcastRefresh(); this.$scope.broadcastRefresh();
this.$scope.dashboard.updateSubmenuVisibility(); this.$scope.dashboard.updateSubmenuVisibility();
}; }
removeAnnotation(annotation) { removeAnnotation(annotation) {
var index = _.indexOf(this.annotations, annotation); var index = _.indexOf(this.annotations, annotation);
@@ -216,7 +216,6 @@ coreModule.directive('panelDropZone', function($timeout) {
} }
if (indrag === true) { if (indrag === true) {
var dropZoneSpan = 12 - row.span;
if (dropZoneSpan > 1) { if (dropZoneSpan > 1) {
return showPanel(dropZoneSpan, 'Drop Here'); return showPanel(dropZoneSpan, 'Drop Here');
} }
+5 -5
View File
@@ -60,7 +60,7 @@ class TimeSrv {
if (_.isString(this.time.to) && this.time.to.indexOf('Z') >= 0) { if (_.isString(this.time.to) && this.time.to.indexOf('Z') >= 0) {
this.time.to = moment(this.time.to).utc(); this.time.to = moment(this.time.to).utc();
} }
}; }
private parseUrlParam(value) { private parseUrlParam(value) {
if (value.indexOf('now') !== -1) { if (value.indexOf('now') !== -1) {
@@ -92,7 +92,7 @@ class TimeSrv {
if (params.refresh) { if (params.refresh) {
this.refresh = params.refresh || this.refresh; this.refresh = params.refresh || this.refresh;
} }
}; }
private routeUpdated() { private routeUpdated() {
var params = this.$location.search(); var params = this.$location.search();
@@ -154,7 +154,7 @@ class TimeSrv {
private cancelNextRefresh() { private cancelNextRefresh() {
this.timer.cancel(this.refreshTimer); this.timer.cancel(this.refreshTimer);
}; }
setTime(time, fromRouteUpdate?) { setTime(time, fromRouteUpdate?) {
_.extend(this.time, time); _.extend(this.time, time);
@@ -184,8 +184,8 @@ class TimeSrv {
timeRangeForUrl() { timeRangeForUrl() {
var range = this.timeRange().raw; var range = this.timeRange().raw;
if (moment.isMoment(range.from)) { range.from = range.from.valueOf(); } if (moment.isMoment(range.from)) { range.from = range.from.valueOf().toString(); }
if (moment.isMoment(range.to)) { range.to = range.to.valueOf(); } if (moment.isMoment(range.to)) { range.to = range.to.valueOf().toString(); }
return range; return range;
} }
@@ -31,6 +31,7 @@ class MetricsPanelCtrl extends PanelCtrl {
skipDataOnInit: boolean; skipDataOnInit: boolean;
dataStream: any; dataStream: any;
dataSubscription: any; dataSubscription: any;
dataList: any;
constructor($scope, $injector) { constructor($scope, $injector) {
super($scope, $injector); super($scope, $injector);
@@ -106,6 +107,16 @@ class MetricsPanelCtrl extends PanelCtrl {
this.loading = false; this.loading = false;
this.error = err.message || "Request Error"; this.error = err.message || "Request Error";
this.inspector = {error: err}; this.inspector = {error: err};
if (err.data) {
if (err.data.message) {
this.error = err.data.message;
}
if (err.data.error) {
this.error = err.data.error;
}
}
this.events.emit('data-error', err); this.events.emit('data-error', err);
console.log('Panel data error:', err); console.log('Panel data error:', err);
}); });
@@ -136,7 +147,7 @@ class MetricsPanelCtrl extends PanelCtrl {
this.calculateInterval(); this.calculateInterval();
return this.datasource; return this.datasource;
}; }
calculateInterval() { calculateInterval() {
var intervalOverride = this.panel.interval; var intervalOverride = this.panel.interval;
@@ -194,7 +205,7 @@ class MetricsPanelCtrl extends PanelCtrl {
if (this.panel.hideTimeOverride) { if (this.panel.hideTimeOverride) {
this.timeInfo = ''; this.timeInfo = '';
} }
}; }
issueQueries(datasource) { issueQueries(datasource) {
this.datasource = datasource; this.datasource = datasource;
@@ -1,7 +1,7 @@
<div class="gf-form-query"> <div class="gf-form-query">
<div class="gf-form"> <div class="gf-form gf-form-query-letter-cell">
<label class="gf-form-label gf-form-query-letter-cell"> <label class="gf-form-label">
<a class="pointer" tabindex="1" ng-click="ctrl.toggleCollapse()"> <a class="pointer" tabindex="1" ng-click="ctrl.toggleCollapse()">
<span ng-class="{muted: !ctrl.canCollapse}" class="gf-form-query-letter-cell-carret"> <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-down" ng-hide="ctrl.collapsed"></i>
@@ -74,7 +74,7 @@ export class PlaylistEditCtrl {
return playlistItem === listedPlaylistItem; return playlistItem === listedPlaylistItem;
}); });
this.filterFoundPlaylistItems(); this.filterFoundPlaylistItems();
}; }
savePlaylist(playlist, playlistItems) { savePlaylist(playlist, playlistItems) {
var savePromise; var savePromise;
+1 -1
View File
@@ -154,7 +154,7 @@ export class DataSourceEditCtrl {
this.$location.path('datasources/edit/' + result.id); this.$location.path('datasources/edit/' + result.id);
}); });
} }
}; }
confirmDelete() { confirmDelete() {
this.backendSrv.delete('/api/datasources/' + this.current.id).then(() => { this.backendSrv.delete('/api/datasources/' + this.current.id).then(() => {
+1 -1
View File
@@ -17,4 +17,4 @@ export {
CustomVariable, CustomVariable,
ConstantVariable, ConstantVariable,
AdhocVariable, AdhocVariable,
} };
@@ -92,7 +92,7 @@ export class VariableSrv {
addVariable(model) { addVariable(model) {
var variable = this.createVariableFromModel(model); var variable = this.createVariableFromModel(model);
this.variables.push(this.createVariableFromModel(variable)); this.variables.push(variable);
return variable; return variable;
} }
@@ -104,7 +104,7 @@ function (angular, _) {
query = $scope.datasource.getDimensionKeys($scope.target.namespace, $scope.target.region); query = $scope.datasource.getDimensionKeys($scope.target.namespace, $scope.target.region);
} else if (segment.type === 'value') { } else if (segment.type === 'value') {
var dimensionKey = $scope.dimSegments[$index-2].value; var dimensionKey = $scope.dimSegments[$index-2].value;
query = $scope.datasource.getDimensionValues(target.region, target.namespace, target.metricName, dimensionKey, {}); query = $scope.datasource.getDimensionValues(target.region, target.namespace, target.metricName, dimensionKey, target.dimensions);
} }
return query.then($scope.transformToSegments(true)).then(function(results) { return query.then($scope.transformToSegments(true)).then(function(results) {
@@ -13,7 +13,7 @@ class GrafanaDatasource {
metricFindQuery() { metricFindQuery() {
return this.$q.when([]); return this.$q.when([]);
}; }
annotationQuery(options) { annotationQuery(options) {
return this.backendSrv.get('/api/annotations', { return this.backendSrv.get('/api/annotations', {
@@ -120,7 +120,7 @@ export default class InfluxDatasource {
return {data: seriesList}; return {data: seriesList};
}); });
}; }
annotationQuery(options) { annotationQuery(options) {
if (!options.annotation.query) { if (!options.annotation.query) {
@@ -137,7 +137,7 @@ export default class InfluxDatasource {
} }
return new InfluxSeries({series: data.results[0].series, annotation: options.annotation}).getAnnotations(); return new InfluxSeries({series: data.results[0].series, annotation: options.annotation}).getAnnotations();
}); });
}; }
targetContainsTemplate(target) { targetContainsTemplate(target) {
for (let group of target.groupBy) { for (let group of target.groupBy) {
@@ -155,7 +155,7 @@ export default class InfluxDatasource {
} }
return false; return false;
}; }
metricFindQuery(query: string, options?: any) { metricFindQuery(query: string, options?: any) {
var interpolated = this.templateSrv.replace(query, null, 'regex'); var interpolated = this.templateSrv.replace(query, null, 'regex');
@@ -256,7 +256,7 @@ export default class InfluxDatasource {
} }
} }
}); });
}; }
getTimeFilter(options) { getTimeFilter(options) {
var from = this.getInfluxTime(options.rangeRaw.from, false); var from = this.getInfluxTime(options.rangeRaw.from, false);
@@ -195,7 +195,7 @@ export default class InfluxQuery {
var escapedValues = _.map(value, kbn.regexEscape); var escapedValues = _.map(value, kbn.regexEscape);
return escapedValues.join('|'); return escapedValues.join('|');
}; }
render(interpolate?) { render(interpolate?) {
var target = this.target; var target = this.target;
@@ -91,7 +91,13 @@ function (_) {
query += ' WHERE ' + whereConditions.join(' '); query += ' WHERE ' + whereConditions.join(' ');
} }
} }
if (type === 'MEASUREMENTS')
{
query += ' LIMIT 100';
//Solve issue #2524 by limiting the number of measurements returned
//LIMIT must be after WITH MEASUREMENT and WHERE clauses
//This also could be used for TAG KEYS and TAG VALUES, if desired
}
return query; return query;
}; };
@@ -28,7 +28,7 @@ function createPart(part): any {
} }
return new QueryPart(part, def); return new QueryPart(part, def);
}; }
function register(options: any) { function register(options: any) {
index[options.type] = new QueryPartDef(options); index[options.type] = new QueryPartDef(options);
@@ -34,31 +34,31 @@ describe('InfluxQueryBuilder', function() {
it('should have no conditions in measurement query for query with no tags', function() { it('should have no conditions in measurement query for query with no tags', function() {
var builder = new InfluxQueryBuilder({ measurement: '', tags: [] }); var builder = new InfluxQueryBuilder({ measurement: '', tags: [] });
var query = builder.buildExploreQuery('MEASUREMENTS'); var query = builder.buildExploreQuery('MEASUREMENTS');
expect(query).to.be('SHOW MEASUREMENTS'); expect(query).to.be('SHOW MEASUREMENTS LIMIT 100');
}); });
it('should have no conditions in measurement query for query with no tags and empty query', function() { it('should have no conditions in measurement query for query with no tags and empty query', function() {
var builder = new InfluxQueryBuilder({ measurement: '', tags: [] }); var builder = new InfluxQueryBuilder({ measurement: '', tags: [] });
var query = builder.buildExploreQuery('MEASUREMENTS', undefined, ''); var query = builder.buildExploreQuery('MEASUREMENTS', undefined, '');
expect(query).to.be('SHOW MEASUREMENTS'); expect(query).to.be('SHOW MEASUREMENTS LIMIT 100');
}); });
it('should have WITH MEASUREMENT in measurement query for non-empty query with no tags', function() { it('should have WITH MEASUREMENT in measurement query for non-empty query with no tags', function() {
var builder = new InfluxQueryBuilder({ measurement: '', tags: [] }); var builder = new InfluxQueryBuilder({ measurement: '', tags: [] });
var query = builder.buildExploreQuery('MEASUREMENTS', undefined, 'something'); var query = builder.buildExploreQuery('MEASUREMENTS', undefined, 'something');
expect(query).to.be('SHOW MEASUREMENTS WITH MEASUREMENT =~ /something/'); expect(query).to.be('SHOW MEASUREMENTS WITH MEASUREMENT =~ /something/ LIMIT 100');
}); });
it('should have WITH MEASUREMENT WHERE in measurement query for non-empty query with tags', function() { it('should have WITH MEASUREMENT WHERE in measurement query for non-empty query with tags', function() {
var builder = new InfluxQueryBuilder({ measurement: '', tags: [{key: 'app', value: 'email'}] }); var builder = new InfluxQueryBuilder({ measurement: '', tags: [{key: 'app', value: 'email'}] });
var query = builder.buildExploreQuery('MEASUREMENTS', undefined, 'something'); var query = builder.buildExploreQuery('MEASUREMENTS', undefined, 'something');
expect(query).to.be("SHOW MEASUREMENTS WITH MEASUREMENT =~ /something/ WHERE \"app\" = 'email'"); expect(query).to.be("SHOW MEASUREMENTS WITH MEASUREMENT =~ /something/ WHERE \"app\" = 'email' LIMIT 100");
}); });
it('should have where condition in measurement query for query with tags', function() { it('should have where condition in measurement query for query with tags', function() {
var builder = new InfluxQueryBuilder({measurement: '', tags: [{key: 'app', value: 'email'}]}); var builder = new InfluxQueryBuilder({measurement: '', tags: [{key: 'app', value: 'email'}]});
var query = builder.buildExploreQuery('MEASUREMENTS'); var query = builder.buildExploreQuery('MEASUREMENTS');
expect(query).to.be("SHOW MEASUREMENTS WHERE \"app\" = 'email'"); expect(query).to.be("SHOW MEASUREMENTS WHERE \"app\" = 'email' LIMIT 100");
}); });
it('should have where tag name IN filter in tag values query for query with one tag', function() { it('should have where tag name IN filter in tag values query for query with one tag', function() {
@@ -30,4 +30,4 @@ class MixedDatasource {
} }
} }
export {MixedDatasource, MixedDatasource as Datasource} export {MixedDatasource, MixedDatasource as Datasource};
@@ -6,12 +6,23 @@ export class MysqlDatasource {
id: any; id: any;
name: any; name: any;
/** @ngInject */ /** @ngInject **/
constructor(instanceSettings, private backendSrv, private $q) { constructor(instanceSettings, private backendSrv, private $q, private templateSrv) {
this.name = instanceSettings.name; this.name = instanceSettings.name;
this.id = instanceSettings.id; this.id = instanceSettings.id;
} }
interpolateVariable(value) {
if (typeof value === 'string') {
return '\"' + value + '\"';
}
var quotedValues = _.map(value, function(val) {
return '\"' + val + '\"';
});
return quotedValues.join(',');
}
query(options) { query(options) {
var queries = _.filter(options.targets, item => { var queries = _.filter(options.targets, item => {
return item.hide !== true; return item.hide !== true;
@@ -21,7 +32,8 @@ export class MysqlDatasource {
intervalMs: options.intervalMs, intervalMs: options.intervalMs,
maxDataPoints: options.maxDataPoints, maxDataPoints: options.maxDataPoints,
datasourceId: this.id, datasourceId: this.id,
rawSql: item.rawSql, rawSql: this.templateSrv.replace(item.rawSql, options.scopedVars, this.interpolateVariable),
format: item.format,
}; };
}); });
@@ -29,32 +41,49 @@ export class MysqlDatasource {
return this.$q.when({data: []}); return this.$q.when({data: []});
} }
return this.backendSrv.post('/api/tsdb/query', { return this.backendSrv.datasourceRequest({
from: options.range.from.valueOf().toString(), url: '/api/tsdb/query',
to: options.range.to.valueOf().toString(), method: 'POST',
queries: queries, data: {
}).then(res => { from: options.range.from.valueOf().toString(),
console.log('mysql response', res); to: options.range.to.valueOf().toString(),
queries: queries,
}
}).then(this.processQueryResult.bind(this));
}
var data = []; processQueryResult(res) {
if (res.results) { var data = [];
_.forEach(res.results, queryRes => {
if (queryRes.error) { if (!res.data.results) {
throw {error: queryRes.error, message: queryRes.error}; return {data: data};
} }
for (let series of queryRes.series) { for (let key in res.data.results) {
data.push({ let queryRes = res.data.results[key];
target: series.name,
datapoints: series.points if (queryRes.series) {
}); for (let series of queryRes.series) {
} data.push({
}); target: series.name,
datapoints: series.points,
refId: queryRes.refId,
meta: queryRes.meta,
});
}
} }
return {data: data}; if (queryRes.tables) {
}); for (let table of queryRes.tables) {
table.type = 'table';
table.refId = queryRes.refId;
table.meta = queryRes.meta;
data.push(table);
}
}
}
return {data: data};
} }
} }

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

+2 -20
View File
@@ -1,27 +1,9 @@
///<reference path="../../../headers/common.d.ts" /> ///<reference path="../../../headers/common.d.ts" />
import angular from 'angular'; import angular from 'angular';
import _ from 'lodash';
import {MysqlDatasource} from './datasource'; import {MysqlDatasource} from './datasource';
import {QueryCtrl} from 'app/plugins/sdk'; import {MysqlQueryCtrl} from './query_ctrl';
class MysqlQueryCtrl extends QueryCtrl {
static templateUrl = 'partials/query.editor.html';
resultFormats: any;
target: any;
constructor($scope, $injector) {
super($scope, $injector);
this.target.resultFormat = 'time_series';
this.target.alias = "{{table}}{{col_3}}";
this.resultFormats = [
{text: 'Time series', value: 'time_series'},
{text: 'Table', value: 'table'},
];
}
}
class MysqlConfigCtrl { class MysqlConfigCtrl {
static templateUrl = 'partials/config.html'; static templateUrl = 'partials/config.html';
@@ -1,7 +1,7 @@
<query-editor-row query-ctrl="ctrl" can-collapse="false"> <query-editor-row query-ctrl="ctrl" can-collapse="false">
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
<textarea rows="6" class="gf-form-input" ng-model="ctrl.target.rawSql" spellcheck="false" placeholder="query expression" data-min-length=0 data-items=100 ng-model-onblur ng-change="ctrl.panelCtrl.refresh()"></textarea> <textarea rows="10" class="gf-form-input" ng-model="ctrl.target.rawSql" spellcheck="false" placeholder="query expression" data-min-length=0 data-items=100 ng-model-onblur ng-change="ctrl.panelCtrl.refresh()"></textarea>
</div> </div>
</div> </div>
@@ -9,17 +9,51 @@
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword">Format as</label> <label class="gf-form-label query-keyword">Format as</label>
<div class="gf-form-select-wrapper"> <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> <select class="gf-form-input gf-size-auto" ng-model="ctrl.target.format" ng-options="f.value as f.text for f in ctrl.formats" ng-change="ctrl.refresh()"></select>
</div> </div>
</div> </div>
<div class="gf-form max-width-30"> <div class="gf-form">
<label class="gf-form-label query-keyword">Name by</label> <label class="gf-form-label query-keyword" ng-click="ctrl.showHelp = !ctrl.showHelp">
<input type="text" class="gf-form-input" ng-model="ctrl.target.alias" spellcheck='false' placeholder="pattern" ng-blur="ctrl.refresh()"> Show Help
<i class="fa fa-caret-down" ng-show="ctrl.showHelp"></i>
<i class="fa fa-caret-right" ng-hide="ctrl.showHelp"></i>
</label>
</div>
<div class="gf-form" ng-show="ctrl.lastQueryMeta">
<label class="gf-form-label query-keyword" ng-click="ctrl.showLastQuerySQL = !ctrl.showLastQuerySQL">
Generated SQL
<i class="fa fa-caret-down" ng-show="ctrl.showLastQuerySQL"></i>
<i class="fa fa-caret-right" ng-hide="ctrl.showLastQuerySQL"></i>
</label>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div> <div class="gf-form-label gf-form-label--grow"></div>
</div> </div>
</div> </div>
<div class="gf-form" ng-show="ctrl.showLastQuerySQL">
<pre class="gf-form-pre">{{ctrl.lastQueryMeta.sql}}</pre>
</div>
<div class="gf-form" ng-show="ctrl.showHelp">
<pre class="gf-form-pre alert alert-info">Time series:
- return column named time_sec (UTC in seconds), use UNIX_TIMESTAMP(column)
- return column named value for the time point value
- return column named metric to represent the series name
Table:
- return any set of columns
Macros:
- $__time(column) -&gt; UNIX_TIMESTAMP(column) as time_sec
- $__timeFilter(column) -&gt; UNIX_TIMESTAMP(time_date_time) &gt; from AND UNIX_TIMESTAMP(time_date_time) &lt; 1492750877
</pre>
</div>
</div>
<div class="gf-form" ng-show="ctrl.lastQueryError">
<pre class="gf-form-pre alert alert-error">{{ctrl.lastQueryError}}</pre>
</div>
</query-editor-row> </query-editor-row>
@@ -0,0 +1,79 @@
///<reference path="../../../headers/common.d.ts" />
import angular from 'angular';
import _ from 'lodash';
import {MysqlDatasource} from './datasource';
import {QueryCtrl} from 'app/plugins/sdk';
export interface MysqlQuery {
refId: string;
format: string;
alias: string;
rawSql: string;
}
export interface QueryMeta {
sql: string;
}
var defaulQuery = `SELECT
UNIX_TIMESTAMP(<time_column>) as time_sec,
<value column> as value,
<series name column> as metric
FROM <table name>
WHERE $__timeFilter(time_column)
ORDER BY <time_column> ASC
`;
export class MysqlQueryCtrl extends QueryCtrl {
static templateUrl = 'partials/query.editor.html';
showLastQuerySQL: boolean;
formats: any[];
target: MysqlQuery;
lastQueryMeta: QueryMeta;
lastQueryError: string;
showHelp: boolean;
/** @ngInject **/
constructor($scope, $injector) {
super($scope, $injector);
this.target.format = this.target.format || 'time_series';
this.target.alias = "";
this.formats = [
{text: 'Time series', value: 'time_series'},
{text: 'Table', value: 'table'},
];
if (!this.target.rawSql) {
this.target.rawSql = defaulQuery;
}
this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope);
this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope);
}
onDataReceived(dataList) {
this.lastQueryMeta = null;
this.lastQueryError = null;
let anySeriesFromQuery = _.find(dataList, {refId: this.target.refId});
if (anySeriesFromQuery) {
this.lastQueryMeta = anySeriesFromQuery.meta;
}
}
onDataError(err) {
if (err.data && err.data.results) {
let queryRes = err.data.results[this.target.refId];
if (queryRes) {
this.lastQueryMeta = queryRes.meta;
this.lastQueryError = queryRes.error;
}
}
}
}
+1 -1
View File
@@ -139,4 +139,4 @@ class AlertListPanel extends PanelCtrl {
export { export {
AlertListPanel, AlertListPanel,
AlertListPanel as PanelCtrl AlertListPanel as PanelCtrl
} };
+1 -1
View File
@@ -125,4 +125,4 @@ class DashListCtrl extends PanelCtrl {
} }
} }
export {DashListCtrl, DashListCtrl as PanelCtrl} export {DashListCtrl, DashListCtrl as PanelCtrl};
@@ -116,4 +116,4 @@ class GettingStartedPanelCtrl extends PanelCtrl {
} }
} }
export {GettingStartedPanelCtrl, GettingStartedPanelCtrl as PanelCtrl} export {GettingStartedPanelCtrl, GettingStartedPanelCtrl as PanelCtrl};
+2 -2
View File
@@ -271,7 +271,7 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
}; };
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
var series = data[i]; let series = data[i];
series.data = series.getFlotPairs(series.nullPointMode || panel.nullPointMode); series.data = series.getFlotPairs(series.nullPointMode || panel.nullPointMode);
// if hidden remove points and disable stack // if hidden remove points and disable stack
@@ -287,7 +287,7 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
options.series.bars.align = 'center'; options.series.bars.align = 'center';
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
var series = data[i]; let series = data[i];
series.data = [[i + 1, series.stats[panel.xaxis.values[0]]]]; series.data = [[i + 1, series.stats[panel.xaxis.values[0]]]];
} }
+2 -2
View File
@@ -284,7 +284,7 @@ class GraphCtrl extends MetricsPanelCtrl {
} }
info.yaxis = override.yaxis = info.yaxis === 2 ? 1 : 2; info.yaxis = override.yaxis = info.yaxis === 2 ? 1 : 2;
this.render(); this.render();
}; }
addSeriesOverride(override) { addSeriesOverride(override) {
this.panel.seriesOverrides.push(override || {}); this.panel.seriesOverrides.push(override || {});
@@ -316,4 +316,4 @@ class GraphCtrl extends MetricsPanelCtrl {
} }
export {GraphCtrl, GraphCtrl as PanelCtrl} export {GraphCtrl, GraphCtrl as PanelCtrl};
@@ -26,8 +26,8 @@ export class AxesEditorCtrl {
}; };
this.dataFormats = { this.dataFormats = {
'Timeseries': 'timeseries', 'TS': 'timeseries',
'ES histogram': 'es_histogram' 'TS Pre-bucketed': 'tsbuckets'
}; };
} }
@@ -135,7 +135,7 @@ export class HeatmapCtrl extends MetricsPanelCtrl {
let xBucketSize, yBucketSize, heatmapStats, bucketsData; let xBucketSize, yBucketSize, heatmapStats, bucketsData;
let logBase = this.panel.yAxis.logBase; let logBase = this.panel.yAxis.logBase;
if (this.panel.dataFormat === 'es_histogram') { if (this.panel.dataFormat === 'tsbuckets') {
heatmapStats = this.parseHistogramSeries(this.series); heatmapStats = this.parseHistogramSeries(this.series);
bucketsData = elasticHistogramToHeatmap(this.series); bucketsData = elasticHistogramToHeatmap(this.series);
@@ -70,4 +70,4 @@ class PluginListCtrl extends PanelCtrl {
} }
} }
export {PluginListCtrl, PluginListCtrl as PanelCtrl} export {PluginListCtrl, PluginListCtrl as PanelCtrl};
@@ -3,11 +3,19 @@
<h5 class="section-heading">Value</h5> <h5 class="section-heading">Value</h5>
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form" ng-show="ctrl.dataType === 'timeseries'">
<label class="gf-form-label width-6">Stat</label> <label class="gf-form-label width-6">Stat</label>
<div class="gf-form-select-wrapper width-7"> <div class="gf-form-select-wrapper width-7">
<select class="gf-form-input" ng-model="ctrl.panel.valueName" ng-options="f for f in ctrl.valueNameOptions" ng-change="ctrl.render()"></select> <select class="gf-form-input" ng-model="ctrl.panel.valueName" ng-options="f for f in ctrl.valueNameOptions" ng-change="ctrl.render()"></select>
</div> </div>
</div>
<div class="gf-form" ng-show="ctrl.dataType === 'table'">
<label class="gf-form-label width-6">Column</label>
<div class="gf-form-select-wrapper width-7">
<select class="gf-form-input" ng-model="ctrl.panel.tableColumn" ng-options="f for f in ctrl.tableColumnOptions" ng-change="ctrl.refresh()"></select>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label width-6">Font size</label> <label class="gf-form-label width-6">Font size</label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input" ng-model="ctrl.panel.valueFontSize" ng-options="f for f in ctrl.fontSizes" ng-change="ctrl.render()"></select> <select class="gf-form-input" ng-model="ctrl.panel.valueFontSize" ng-options="f for f in ctrl.fontSizes" ng-change="ctrl.render()"></select>
+97 -23
View File
@@ -14,6 +14,7 @@ import {MetricsPanelCtrl} from 'app/plugins/sdk';
class SingleStatCtrl extends MetricsPanelCtrl { class SingleStatCtrl extends MetricsPanelCtrl {
static templateUrl = 'module.html'; static templateUrl = 'module.html';
dataType = 'timeseries';
series: any[]; series: any[];
data: any; data: any;
fontSizes: any[]; fontSizes: any[];
@@ -22,6 +23,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
panel: any; panel: any;
events: any; events: any;
valueNameOptions: any[] = ['min','max','avg', 'current', 'total', 'name', 'first', 'delta', 'diff', 'range']; valueNameOptions: any[] = ['min','max','avg', 'current', 'total', 'name', 'first', 'delta', 'diff', 'range'];
tableColumnOptions: any;
// Set and populate defaults // Set and populate defaults
panelDefaults = { panelDefaults = {
@@ -67,7 +69,8 @@ class SingleStatCtrl extends MetricsPanelCtrl {
maxValue: 100, maxValue: 100,
thresholdMarkers: true, thresholdMarkers: true,
thresholdLabels: false thresholdLabels: false
} },
tableColumn: ''
}; };
/** @ngInject */ /** @ngInject */
@@ -98,11 +101,16 @@ class SingleStatCtrl extends MetricsPanelCtrl {
} }
onDataReceived(dataList) { onDataReceived(dataList) {
this.series = dataList.map(this.seriesHandler.bind(this)); const data: any = {};
if (dataList.length > 0 && dataList[0].type === 'table'){
var data: any = {}; this.dataType = 'table';
this.setValues(data); const tableData = dataList.map(this.tableHandler.bind(this));
this.setTableValues(tableData, data);
} else {
this.dataType = 'timeseries';
this.series = dataList.map(this.seriesHandler.bind(this));
this.setValues(data);
}
this.data = data; this.data = data;
this.render(); this.render();
} }
@@ -117,6 +125,69 @@ class SingleStatCtrl extends MetricsPanelCtrl {
return series; return series;
} }
tableHandler(tableData) {
const datapoints = [];
const columnNames = {};
tableData.columns.forEach((column, columnIndex) => {
columnNames[columnIndex] = column.text;
});
this.tableColumnOptions = columnNames;
if (!_.find(tableData.columns, ['text', this.panel.tableColumn])) {
this.setTableColumnToSensibleDefault(tableData);
}
tableData.rows.forEach((row) => {
const datapoint = {};
row.forEach((value, columnIndex) => {
const key = columnNames[columnIndex];
datapoint[key] = value;
});
datapoints.push(datapoint);
});
return datapoints;
}
setTableColumnToSensibleDefault(tableData) {
if (this.tableColumnOptions.length === 1) {
this.panel.tableColumn = this.tableColumnOptions[0];
} else {
this.panel.tableColumn = _.find(tableData.columns, (col) => { return col.type !== 'time'; }).text;
}
}
setTableValues(tableData, data) {
if (!tableData || tableData.length === 0) {
return;
}
if (tableData[0].length === 0 || !tableData[0][0][this.panel.tableColumn]) {
return;
}
let highestValue = 0;
let lowestValue = Number.MAX_VALUE;
const datapoint = tableData[0][0];
data.value = datapoint[this.panel.tableColumn];
if (_.isString(data.value)) {
data.valueFormatted = _.escape(data.value);
data.value = 0;
data.valueRounded = 0;
} else {
const decimalInfo = this.getDecimalsForValue(data.value);
const formatFunc = kbn.valueFormats[this.panel.format];
data.valueFormatted = formatFunc(datapoint[this.panel.tableColumn], decimalInfo.decimals, decimalInfo.scaledDecimals);
data.valueRounded = kbn.roundValue(data.value, this.panel.decimals || 0);
}
this.setValueMapping(data);
}
setColoring(options) { setColoring(options) {
if (options.background) { if (options.background) {
this.panel.colorValue = false; this.panel.colorValue = false;
@@ -192,10 +263,10 @@ class SingleStatCtrl extends MetricsPanelCtrl {
if (this.panel.valueName === 'name') { if (this.panel.valueName === 'name') {
data.value = 0; data.value = 0;
data.valueRounded = 0; data.valueRounded = 0;
data.valueFormated = this.series[0].alias; data.valueFormatted = this.series[0].alias;
} else if (_.isString(lastValue)) { } else if (_.isString(lastValue)) {
data.value = 0; data.value = 0;
data.valueFormated = _.escape(lastValue); data.valueFormatted = _.escape(lastValue);
data.valueRounded = 0; data.valueRounded = 0;
} else { } else {
data.value = this.series[0].stats[this.panel.valueName]; data.value = this.series[0].stats[this.panel.valueName];
@@ -203,7 +274,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
var decimalInfo = this.getDecimalsForValue(data.value); var decimalInfo = this.getDecimalsForValue(data.value);
var formatFunc = kbn.valueFormats[this.panel.format]; var formatFunc = kbn.valueFormats[this.panel.format];
data.valueFormated = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals); data.valueFormatted = formatFunc(data.value, decimalInfo.decimals, decimalInfo.scaledDecimals);
data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals); data.valueRounded = kbn.roundValue(data.value, decimalInfo.decimals);
} }
@@ -211,15 +282,18 @@ class SingleStatCtrl extends MetricsPanelCtrl {
data.scopedVars = _.extend({}, this.panel.scopedVars); data.scopedVars = _.extend({}, this.panel.scopedVars);
data.scopedVars["__name"] = {value: this.series[0].label}; data.scopedVars["__name"] = {value: this.series[0].label};
} }
this.setValueMapping(data);
}
setValueMapping(data) {
// check value to text mappings if its enabled // check value to text mappings if its enabled
if (this.panel.mappingType === 1) { if (this.panel.mappingType === 1) {
for (var i = 0; i < this.panel.valueMaps.length; i++) { for (let i = 0; i < this.panel.valueMaps.length; i++) {
var map = this.panel.valueMaps[i]; let map = this.panel.valueMaps[i];
// special null case // special null case
if (map.value === 'null') { if (map.value === 'null') {
if (data.value === null || data.value === void 0) { if (data.value === null || data.value === void 0) {
data.valueFormated = map.text; data.valueFormatted = map.text;
return; return;
} }
continue; continue;
@@ -228,17 +302,17 @@ class SingleStatCtrl extends MetricsPanelCtrl {
// value/number to text mapping // value/number to text mapping
var value = parseFloat(map.value); var value = parseFloat(map.value);
if (value === data.valueRounded) { if (value === data.valueRounded) {
data.valueFormated = map.text; data.valueFormatted = map.text;
return; return;
} }
} }
} else if (this.panel.mappingType === 2) { } else if (this.panel.mappingType === 2) {
for (var i = 0; i < this.panel.rangeMaps.length; i++) { for (let i = 0; i < this.panel.rangeMaps.length; i++) {
var map = this.panel.rangeMaps[i]; let map = this.panel.rangeMaps[i];
// special null case // special null case
if (map.from === 'null' && map.to === 'null') { if (map.from === 'null' && map.to === 'null') {
if (data.value === null || data.value === void 0) { if (data.value === null || data.value === void 0) {
data.valueFormated = map.text; data.valueFormatted = map.text;
return; return;
} }
continue; continue;
@@ -248,22 +322,22 @@ class SingleStatCtrl extends MetricsPanelCtrl {
var from = parseFloat(map.from); var from = parseFloat(map.from);
var to = parseFloat(map.to); var to = parseFloat(map.to);
if (to >= data.valueRounded && from <= data.valueRounded) { if (to >= data.valueRounded && from <= data.valueRounded) {
data.valueFormated = map.text; data.valueFormatted = map.text;
return; return;
} }
} }
} }
if (data.value === null || data.value === void 0) { if (data.value === null || data.value === void 0) {
data.valueFormated = "no value"; data.valueFormatted = "no value";
} }
}; }
removeValueMap(map) { removeValueMap(map) {
var index = _.indexOf(this.panel.valueMaps, map); var index = _.indexOf(this.panel.valueMaps, map);
this.panel.valueMaps.splice(index, 1); this.panel.valueMaps.splice(index, 1);
this.render(); this.render();
}; }
addValueMap() { addValueMap() {
this.panel.valueMaps.push({value: '', op: '=', text: '' }); this.panel.valueMaps.push({value: '', op: '=', text: '' });
@@ -273,7 +347,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
var index = _.indexOf(this.panel.rangeMaps, rangeMap); var index = _.indexOf(this.panel.rangeMaps, rangeMap);
this.panel.rangeMaps.splice(index, 1); this.panel.rangeMaps.splice(index, 1);
this.render(); this.render();
}; }
addRangeMap() { addRangeMap() {
this.panel.rangeMaps.push({from: '', to: '', text: ''}); this.panel.rangeMaps.push({from: '', to: '', text: ''});
@@ -317,7 +391,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
if (panel.prefix) { body += getSpan('singlestat-panel-prefix', panel.prefixFontSize, panel.prefix); } if (panel.prefix) { body += getSpan('singlestat-panel-prefix', panel.prefixFontSize, panel.prefix); }
var value = applyColoringThresholds(data.value, data.valueFormated); var value = applyColoringThresholds(data.value, data.valueFormatted);
body += getSpan('singlestat-panel-value', panel.valueFontSize, value); body += getSpan('singlestat-panel-value', panel.valueFontSize, value);
if (panel.postfix) { body += getSpan('singlestat-panel-postfix', panel.postfixFontSize, panel.postfix); } if (panel.postfix) { body += getSpan('singlestat-panel-postfix', panel.postfixFontSize, panel.postfix); }
@@ -329,7 +403,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
function getValueText() { function getValueText() {
var result = panel.prefix ? panel.prefix : ''; var result = panel.prefix ? panel.prefix : '';
result += data.valueFormated; result += data.valueFormatted;
result += panel.postfix ? panel.postfix : ''; result += panel.postfix ? panel.postfix : '';
return result; return result;
@@ -1,131 +0,0 @@
///<reference path="../../../../headers/common.d.ts" />
import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
import angular from 'angular';
import helpers from '../../../../../test/specs/helpers';
import {SingleStatCtrl} from '../module';
describe('SingleStatCtrl', function() {
var ctx = new helpers.ControllerTestContext();
function singleStatScenario(desc, func) {
describe(desc, function() {
ctx.setup = function (setupFunc) {
beforeEach(angularMocks.module('grafana.services'));
beforeEach(angularMocks.module('grafana.controllers'));
beforeEach(angularMocks.module(function($compileProvider) {
$compileProvider.preAssignBindingsEnabled(true);
}));
beforeEach(ctx.providePhase());
beforeEach(ctx.createPanelController(SingleStatCtrl));
beforeEach(function() {
setupFunc();
var data = [
{target: 'test.cpu1', datapoints: ctx.datapoints}
];
ctx.ctrl.onDataReceived(data);
ctx.data = ctx.ctrl.data;
});
};
func(ctx);
});
}
singleStatScenario('with defaults', function(ctx) {
ctx.setup(function() {
ctx.datapoints = [[10,1], [20,2]];
});
it('Should use series avg as default main value', function() {
expect(ctx.data.value).to.be(15);
expect(ctx.data.valueRounded).to.be(15);
});
it('should set formated falue', function() {
expect(ctx.data.valueFormated).to.be('15');
});
});
singleStatScenario('showing serie name instead of value', function(ctx) {
ctx.setup(function() {
ctx.datapoints = [[10,1], [20,2]];
ctx.ctrl.panel.valueName = 'name';
});
it('Should use series avg as default main value', function() {
expect(ctx.data.value).to.be(0);
expect(ctx.data.valueRounded).to.be(0);
});
it('should set formated falue', function() {
expect(ctx.data.valueFormated).to.be('test.cpu1');
});
});
singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(ctx) {
ctx.setup(function() {
ctx.datapoints = [[99.999,1], [99.99999,2]];
});
it('Should be rounded', function() {
expect(ctx.data.value).to.be(99.999495);
expect(ctx.data.valueRounded).to.be(100);
});
it('should set formated falue', function() {
expect(ctx.data.valueFormated).to.be('100');
});
});
singleStatScenario('When value to text mapping is specified', function(ctx) {
ctx.setup(function() {
ctx.datapoints = [[9.9,1]];
ctx.ctrl.panel.valueMaps = [{value: '10', text: 'OK'}];
});
it('value should remain', function() {
expect(ctx.data.value).to.be(9.9);
});
it('round should be rounded up', function() {
expect(ctx.data.valueRounded).to.be(10);
});
it('Should replace value with text', function() {
expect(ctx.data.valueFormated).to.be('OK');
});
});
singleStatScenario('When range to text mapping is specifiedfor first range', function(ctx) {
ctx.setup(function() {
ctx.datapoints = [[41,50]];
ctx.ctrl.panel.mappingType = 2;
ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
});
it('Should replace value with text OK', function() {
expect(ctx.data.valueFormated).to.be('OK');
});
});
singleStatScenario('When range to text mapping is specified for other ranges', function(ctx) {
ctx.setup(function() {
ctx.datapoints = [[65,75]];
ctx.ctrl.panel.mappingType = 2;
ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
});
it('Should replace value with text NOT OK', function() {
expect(ctx.data.valueFormated).to.be('NOT OK');
});
});
});
@@ -0,0 +1,260 @@
///<reference path="../../../../headers/common.d.ts" />
import {describe, beforeEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
import angular from 'angular';
import helpers from '../../../../../test/specs/helpers';
import {SingleStatCtrl} from '../module';
describe('SingleStatCtrl', function() {
var ctx = new helpers.ControllerTestContext();
function singleStatScenario(desc, func) {
describe(desc, function() {
ctx.setup = function (setupFunc) {
beforeEach(angularMocks.module('grafana.services'));
beforeEach(angularMocks.module('grafana.controllers'));
beforeEach(angularMocks.module(function($compileProvider) {
$compileProvider.preAssignBindingsEnabled(true);
}));
beforeEach(ctx.providePhase());
beforeEach(ctx.createPanelController(SingleStatCtrl));
beforeEach(function() {
setupFunc();
ctx.ctrl.onDataReceived(ctx.data);
ctx.data = ctx.ctrl.data;
});
};
func(ctx);
});
}
singleStatScenario('with defaults', function(ctx) {
ctx.setup(function() {
ctx.data = [
{target: 'test.cpu1', datapoints: [[10,1], [20,2]]}
];
});
it('Should use series avg as default main value', function() {
expect(ctx.data.value).to.be(15);
expect(ctx.data.valueRounded).to.be(15);
});
it('should set formatted falue', function() {
expect(ctx.data.valueFormatted).to.be('15');
});
});
singleStatScenario('showing serie name instead of value', function(ctx) {
ctx.setup(function() {
ctx.data = [
{target: 'test.cpu1', datapoints: [[10,1], [20,2]]}
];
ctx.ctrl.panel.valueName = 'name';
});
it('Should use series avg as default main value', function() {
expect(ctx.data.value).to.be(0);
expect(ctx.data.valueRounded).to.be(0);
});
it('should set formatted falue', function() {
expect(ctx.data.valueFormatted).to.be('test.cpu1');
});
});
singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(ctx) {
ctx.setup(function() {
ctx.data = [
{target: 'test.cpu1', datapoints: [[99.999,1], [99.99999,2]]}
];
});
it('Should be rounded', function() {
expect(ctx.data.value).to.be(99.999495);
expect(ctx.data.valueRounded).to.be(100);
});
it('should set formatted falue', function() {
expect(ctx.data.valueFormatted).to.be('100');
});
});
singleStatScenario('When value to text mapping is specified', function(ctx) {
ctx.setup(function() {
ctx.data = [
{target: 'test.cpu1', datapoints: [[9.9,1]]}
];
ctx.ctrl.panel.valueMaps = [{value: '10', text: 'OK'}];
});
it('value should remain', function() {
expect(ctx.data.value).to.be(9.9);
});
it('round should be rounded up', function() {
expect(ctx.data.valueRounded).to.be(10);
});
it('Should replace value with text', function() {
expect(ctx.data.valueFormatted).to.be('OK');
});
});
singleStatScenario('When range to text mapping is specifiedfor first range', function(ctx) {
ctx.setup(function() {
ctx.data = [
{target: 'test.cpu1', datapoints: [[41,50]]}
];
ctx.ctrl.panel.mappingType = 2;
ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
});
it('Should replace value with text OK', function() {
expect(ctx.data.valueFormatted).to.be('OK');
});
});
singleStatScenario('When range to text mapping is specified for other ranges', function(ctx) {
ctx.setup(function() {
ctx.data = [
{target: 'test.cpu1', datapoints: [[65,75]]}
];
ctx.ctrl.panel.mappingType = 2;
ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
});
it('Should replace value with text NOT OK', function() {
expect(ctx.data.valueFormatted).to.be('NOT OK');
});
});
describe('When table data', function() {
const tableData = [{
"columns": [
{ "text": "Time", "type": "time" },
{ "text": "test1" },
{ "text": "mean" },
{ "text": "test2" }
],
"rows": [
[1492759673649, 'ignore1', 15, 'ignore2']
],
"type": "table"
}];
singleStatScenario('with default values', function(ctx) {
ctx.setup(function() {
ctx.data = tableData;
ctx.ctrl.panel.tableColumn = 'mean';
});
it('Should use first rows value as default main value', function() {
expect(ctx.data.value).to.be(15);
expect(ctx.data.valueRounded).to.be(15);
});
it('should set formatted value', function() {
expect(ctx.data.valueFormatted).to.be('15');
});
});
singleStatScenario('When table data has multiple columns', function(ctx) {
ctx.setup(function() {
ctx.data = tableData;
ctx.ctrl.panel.tableColumn = '';
});
it('Should set column to first column that is not time', function() {
expect(ctx.ctrl.panel.tableColumn).to.be('test1');
});
});
singleStatScenario('MainValue should use same number for decimals as displayed when checking thresholds', function(ctx) {
ctx.setup(function() {
ctx.data = tableData;
ctx.data[0].rows[0] = [1492759673649,'ignore1', 99.99999, 'ignore2'];
ctx.ctrl.panel.tableColumn = 'mean';
});
it('Should be rounded', function() {
expect(ctx.data.value).to.be(99.99999);
expect(ctx.data.valueRounded).to.be(100);
});
it('should set formatted falue', function() {
expect(ctx.data.valueFormatted).to.be('100');
});
});
singleStatScenario('When value to text mapping is specified', function(ctx) {
ctx.setup(function() {
ctx.data = tableData;
ctx.data[0].rows[0] = [1492759673649,'ignore1', 9.9, 'ignore2'];
ctx.ctrl.panel.tableColumn = 'mean';
ctx.ctrl.panel.valueMaps = [{value: '10', text: 'OK'}];
});
it('value should remain', function() {
expect(ctx.data.value).to.be(9.9);
});
it('round should be rounded up', function() {
expect(ctx.data.valueRounded).to.be(10);
});
it('Should replace value with text', function() {
expect(ctx.data.valueFormatted).to.be('OK');
});
});
singleStatScenario('When range to text mapping is specified for first range', function(ctx) {
ctx.setup(function() {
ctx.data = tableData;
ctx.data[0].rows[0] = [1492759673649,'ignore1', 41, 'ignore2'];
ctx.ctrl.panel.tableColumn = 'mean';
ctx.ctrl.panel.mappingType = 2;
ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
});
it('Should replace value with text OK', function() {
expect(ctx.data.valueFormatted).to.be('OK');
});
});
singleStatScenario('When range to text mapping is specified for other ranges', function(ctx) {
ctx.setup(function() {
ctx.data = tableData;
ctx.data[0].rows[0] = [1492759673649,'ignore1', 65, 'ignore2'];
ctx.ctrl.panel.tableColumn = 'mean';
ctx.ctrl.panel.mappingType = 2;
ctx.ctrl.panel.rangeMaps = [{from: '10', to: '50', text: 'OK'},{from: '51', to: '100', text: 'NOT OK'}];
});
it('Should replace value with text NOT OK', function() {
expect(ctx.data.valueFormatted).to.be('NOT OK');
});
});
singleStatScenario('When value is string', function(ctx) {
ctx.setup(function() {
ctx.data = tableData;
ctx.data[0].rows[0] = [1492759673649,'ignore1', 65, 'ignore2'];
ctx.ctrl.panel.tableColumn = 'test1';
});
it('Should replace value with text NOT OK', function() {
expect(ctx.data.valueFormatted).to.be('ignore1');
});
});
});
});
+1 -1
View File
@@ -101,7 +101,7 @@ export class TablePanelEditorCtrl {
setUnitFormat(column, subItem) { setUnitFormat(column, subItem) {
column.unit = subItem.value; column.unit = subItem.value;
this.panelCtrl.render(); this.panelCtrl.render();
}; }
addColumnStyle() { addColumnStyle() {
var columnStyleDefaults = { var columnStyleDefaults = {
@@ -42,7 +42,7 @@ transformers['timeseries_to_columns'] = {
// group by time // group by time
var points = {}; var points = {};
for (var i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
var series = data[i]; var series = data[i];
model.columns.push({text: series.target}); model.columns.push({text: series.target});
@@ -63,7 +63,7 @@ transformers['timeseries_to_columns'] = {
var point = points[time]; var point = points[time];
var values = [point.time]; var values = [point.time];
for (var i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
var value = point[i]; var value = point[i];
values.push(value); values.push(value);
} }
@@ -242,4 +242,4 @@ function transformDataToTable(data, panel) {
return model; return model;
} }
export {transformers, transformDataToTable} export {transformers, transformDataToTable};
+1 -1
View File
@@ -79,4 +79,4 @@ export class TextPanelCtrl extends PanelCtrl {
} }
} }
export {TextPanelCtrl as PanelCtrl} export {TextPanelCtrl as PanelCtrl};
+1 -1
View File
@@ -18,4 +18,4 @@ export {
MetricsPanelCtrl, MetricsPanelCtrl,
QueryCtrl, QueryCtrl,
alertTab, alertTab,
} };
+10
View File
@@ -66,6 +66,16 @@ $gf-form-margin: 0.25rem;
} }
} }
.gf-form-pre {
display: block;
flex-grow: 1;
font-size: $font-size-sm;
margin: 0;
margin-right: $gf-form-margin;
border: $input-btn-border-width solid transparent;
@include border-radius($label-border-radius-sm);
}
.gf-form-error { .gf-form-error {
padding: $input-padding-y $input-padding-x; padding: $input-padding-y $input-padding-x;
margin-right: $gf-form-margin; margin-right: $gf-form-margin;
+1 -1
View File
@@ -21,4 +21,4 @@ export {
sinon, sinon,
expect, expect,
angularMocks, angularMocks,
} };
+1 -1
View File
@@ -8,10 +8,10 @@ module.exports = function(grunt) {
'jshint:source', 'jshint:source',
'jshint:tests', 'jshint:tests',
'jscs', 'jscs',
'exec:tslint',
'clean:release', 'clean:release',
'copy:node_modules', 'copy:node_modules',
'copy:public_to_gen', 'copy:public_to_gen',
'exec:tslint',
'exec:tscompile', 'exec:tscompile',
'karma:test', 'karma:test',
'phantomjs', 'phantomjs',
+2 -2
View File
@@ -14,12 +14,12 @@ module.exports = function(grunt) {
); );
grunt.registerTask('default', [ grunt.registerTask('default', [
'clean:gen',
'jscs', 'jscs',
'jshint', 'jshint',
'exec:tslint',
'clean:gen',
'copy:node_modules', 'copy:node_modules',
'copy:public_to_gen', 'copy:public_to_gen',
'exec:tslint',
'phantomjs', 'phantomjs',
'css', 'css',
'exec:tscompile' 'exec:tscompile'
+2 -2
View File
@@ -1,8 +1,8 @@
module.exports = function(config, grunt) { module.exports = function(config, grunt) {
'use strict' 'use strict'
return { return {
tslint : "node ./node_modules/tslint/lib/tslint-cli.js -c tslint.json --project ./tsconfig.json", tslint : "node ./node_modules/tslint/lib/tslint-cli.js -c tslint.json --project ./tsconfig.json --type-check",
tslintfile : "node ./node_modules/tslint/lib/tslint-cli.js -c tslint.json --project ./tsconfig.json <%= tslint.source.files.src %>", tslintfile : "node ./node_modules/tslint/lib/tslint-cli.js -c tslint.json --project ./tsconfig.json --type-check <%= tslint.source.files.src %>",
tscompile: "node ./node_modules/typescript/lib/tsc.js -p tsconfig.json --diagnostics", tscompile: "node ./node_modules/typescript/lib/tsc.js -p tsconfig.json --diagnostics",
tswatch: "node ./node_modules/typescript/lib/tsc.js -p tsconfig.json --diagnostics --watch", tswatch: "node ./node_modules/typescript/lib/tsc.js -p tsconfig.json --diagnostics --watch",
}; };
+78
View File
@@ -0,0 +1,78 @@
package mysql
const (
// In case we get something unexpected
FieldTypeUnknown = "UNKNOWN"
// Human-readable names for each distinct type byte
FieldTypeNameDecimal = "DECIMAL"
FieldTypeNameTiny = "TINY"
FieldTypeNameShort = "SHORT"
FieldTypeNameLong = "LONG"
FieldTypeNameFloat = "FLOAT"
FieldTypeNameDouble = "DOUBLE"
FieldTypeNameNULL = "NULL"
FieldTypeNameTimestamp = "TIMESTAMP"
FieldTypeNameLongLong = "LONGLONG"
FieldTypeNameInt24 = "INT24"
FieldTypeNameDate = "DATE"
FieldTypeNameTime = "TIME"
FieldTypeNameDateTime = "DATETIME"
FieldTypeNameYear = "YEAR"
FieldTypeNameNewDate = "NEWDATE"
FieldTypeNameVarChar = "VARCHAR"
FieldTypeNameBit = "BIT"
FieldTypeNameJSON = "JSON"
FieldTypeNameNewDecimal = "NEWDECIMAL"
FieldTypeNameEnum = "ENUM"
FieldTypeNameSet = "SET"
FieldTypeNameTinyBLOB = "TINYBLOB"
FieldTypeNameMediumBLOB = "MEDIUMBLOB"
FieldTypeNameLongBLOB = "LONGBLOB"
FieldTypeNameBLOB = "BLOB"
FieldTypeNameVarString = "VARSTRING"
FieldTypeNameString = "STRING"
FieldTypeNameGeometry = "GEOMETRY"
)
// mapping from each type identifier to human readable string
var mysqlTypeMap = map[byte]string{
fieldTypeDecimal: FieldTypeNameDecimal,
fieldTypeTiny: FieldTypeNameTiny,
fieldTypeShort: FieldTypeNameShort,
fieldTypeLong: FieldTypeNameLong,
fieldTypeFloat: FieldTypeNameFloat,
fieldTypeDouble: FieldTypeNameDouble,
fieldTypeNULL: FieldTypeNameNULL,
fieldTypeTimestamp: FieldTypeNameTimestamp,
fieldTypeLongLong: FieldTypeNameLongLong,
fieldTypeInt24: FieldTypeNameInt24,
fieldTypeDate: FieldTypeNameDate,
fieldTypeTime: FieldTypeNameTime,
fieldTypeDateTime: FieldTypeNameDateTime,
fieldTypeYear: FieldTypeNameYear,
fieldTypeNewDate: FieldTypeNameNewDate,
fieldTypeVarChar: FieldTypeNameVarChar,
fieldTypeBit: FieldTypeNameBit,
fieldTypeJSON: FieldTypeNameJSON,
fieldTypeNewDecimal: FieldTypeNameNewDecimal,
fieldTypeEnum: FieldTypeNameEnum,
fieldTypeSet: FieldTypeNameSet,
fieldTypeTinyBLOB: FieldTypeNameTinyBLOB,
fieldTypeMediumBLOB: FieldTypeNameMediumBLOB,
fieldTypeLongBLOB: FieldTypeNameLongBLOB,
fieldTypeBLOB: FieldTypeNameBLOB,
fieldTypeVarString: FieldTypeNameVarString,
fieldTypeString: FieldTypeNameString,
fieldTypeGeometry: FieldTypeNameGeometry,
}
// Make Rows implement the optional RowsColumnTypeDatabaseTypeName interface.
// See https://github.com/golang/go/commit/2a85578b0ecd424e95b29d810b7a414a299fd6a7
// - (go 1.8 required for this to have any effect)
func (rows *mysqlRows) ColumnTypeDatabaseTypeName(index int) string {
if typeName, ok := mysqlTypeMap[rows.rs.columns[index].fieldType]; ok {
return typeName
}
return FieldTypeUnknown
}
+46 -246
View File
@@ -68,12 +68,6 @@ amdefine@>=0.0.4:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
ansi-align@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-1.1.0.tgz#2f0c1658829739add5ebb15e6b0c6e3423f016ba"
dependencies:
string-width "^1.0.1"
ansi-escapes@^1.1.0: ansi-escapes@^1.1.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
@@ -255,7 +249,7 @@ aws4@^1.2.1:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.5.0.tgz#0a29ffb79c31c9e712eeb087e8e7a64b4a56d755" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.5.0.tgz#0a29ffb79c31c9e712eeb087e8e7a64b4a56d755"
babel-code-frame@^6.20.0, babel-code-frame@^6.22.0: babel-code-frame@^6.22.0:
version "6.22.0" version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4"
dependencies: dependencies:
@@ -499,20 +493,6 @@ boom@2.x.x:
dependencies: dependencies:
hoek "2.x.x" hoek "2.x.x"
boxen@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-0.6.0.tgz#8364d4248ac34ff0ef1b2f2bf49a6c60ce0d81b6"
dependencies:
ansi-align "^1.1.0"
camelcase "^2.1.0"
chalk "^1.1.1"
cli-boxes "^1.0.0"
filled-array "^1.0.0"
object-assign "^4.0.1"
repeating "^2.0.0"
string-width "^1.0.1"
widest-line "^1.0.0"
brace-expansion@^1.0.0: brace-expansion@^1.0.0:
version "1.1.6" version "1.1.6"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9"
@@ -603,7 +583,7 @@ camelcase@^1.0.2:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
camelcase@^2.0.0, camelcase@^2.1.0: camelcase@^2.0.0:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
@@ -615,10 +595,6 @@ caniuse-db@^1.0.30000617:
version "1.0.30000618" version "1.0.30000618"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000618.tgz#821258ff484f662864f28ffbcf849a6247acf1fa" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000618.tgz#821258ff484f662864f28ffbcf849a6247acf1fa"
capture-stack-trace@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d"
caseless@~0.11.0: caseless@~0.11.0:
version "0.11.0" version "0.11.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7"
@@ -697,10 +673,6 @@ clean-css@3.4.x, clean-css@~3.4.2:
commander "2.8.x" commander "2.8.x"
source-map "0.4.x" source-map "0.4.x"
cli-boxes@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
cli-cursor@^1.0.1: cli-cursor@^1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
@@ -823,7 +795,7 @@ concat-map@0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
concat-stream@1.5.0: concat-stream@1.5.0, concat-stream@^1.4.1, concat-stream@^1.4.6:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.0.tgz#53f7d43c51c5e43f81c8fdd03321c631be68d611" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.0.tgz#53f7d43c51c5e43f81c8fdd03321c631be68d611"
dependencies: dependencies:
@@ -831,28 +803,6 @@ concat-stream@1.5.0:
readable-stream "~2.0.0" readable-stream "~2.0.0"
typedarray "~0.0.5" typedarray "~0.0.5"
concat-stream@^1.4.1, concat-stream@^1.4.6:
version "1.6.0"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
dependencies:
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
configstore@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/configstore/-/configstore-2.1.0.tgz#737a3a7036e9886102aa6099e47bb33ab1aba1a1"
dependencies:
dot-prop "^3.0.0"
graceful-fs "^4.1.2"
mkdirp "^0.5.0"
object-assign "^4.0.1"
os-tmpdir "^1.0.0"
osenv "^0.1.0"
uuid "^2.0.1"
write-file-atomic "^1.1.2"
xdg-basedir "^2.0.0"
connect@^3.3.5: connect@^3.3.5:
version "3.5.0" version "3.5.0"
resolved "https://registry.yarnpkg.com/connect/-/connect-3.5.0.tgz#b357525a0b4c1f50599cd983e1d9efeea9677198" resolved "https://registry.yarnpkg.com/connect/-/connect-3.5.0.tgz#b357525a0b4c1f50599cd983e1d9efeea9677198"
@@ -910,12 +860,6 @@ crc@^3.4.4:
version "3.4.4" version "3.4.4"
resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.4.tgz#9da1e980e3bd44fc5c93bf5ab3da3378d85e466b" resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.4.tgz#9da1e980e3bd44fc5c93bf5ab3da3378d85e466b"
create-error-class@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
dependencies:
capture-stack-trace "^1.0.0"
cross-spawn@^3.0.0: cross-spawn@^3.0.0:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
@@ -1062,7 +1006,7 @@ diff@^2.0.2:
version "2.2.3" version "2.2.3"
resolved "https://registry.yarnpkg.com/diff/-/diff-2.2.3.tgz#60eafd0d28ee906e4e8ff0a52c1229521033bf99" resolved "https://registry.yarnpkg.com/diff/-/diff-2.2.3.tgz#60eafd0d28ee906e4e8ff0a52c1229521033bf99"
diff@^3.0.1: diff@^3.2.0:
version "3.2.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
@@ -1116,18 +1060,6 @@ dot-case@^2.1.0:
dependencies: dependencies:
no-case "^2.2.0" no-case "^2.2.0"
dot-prop@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177"
dependencies:
is-obj "^1.0.0"
duplexer2@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
dependencies:
readable-stream "^2.0.2"
each-async@^0.1.3: each-async@^0.1.3:
version "0.1.3" version "0.1.3"
resolved "https://registry.yarnpkg.com/each-async/-/each-async-0.1.3.tgz#b436025b08da2f86608025519e3096763dedfca3" resolved "https://registry.yarnpkg.com/each-async/-/each-async-0.1.3.tgz#b436025b08da2f86608025519e3096763dedfca3"
@@ -1534,10 +1466,6 @@ fill-range@^2.1.0:
repeat-element "^1.1.2" repeat-element "^1.1.2"
repeat-string "^1.5.2" repeat-string "^1.5.2"
filled-array@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/filled-array/-/filled-array-1.1.0.tgz#c3c4f6c663b923459a9aa29912d2d031f1507f84"
finalhandler@0.5.0: finalhandler@0.5.0:
version "0.5.0" version "0.5.0"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7"
@@ -1735,9 +1663,9 @@ glob@7.0.5:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@^7.0.0, glob@^7.1.1, glob@~7.1.1: glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.0.0:
version "7.1.1" version "7.0.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a"
dependencies: dependencies:
fs.realpath "^1.0.0" fs.realpath "^1.0.0"
inflight "^1.0.4" inflight "^1.0.4"
@@ -1746,9 +1674,9 @@ glob@^7.0.0, glob@^7.1.1, glob@~7.1.1:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@^7.0.3, glob@^7.0.5, glob@~7.0.0: glob@^7.1.1, glob@~7.1.1:
version "7.0.6" version "7.1.1"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
dependencies: dependencies:
fs.realpath "^1.0.0" fs.realpath "^1.0.0"
inflight "^1.0.4" inflight "^1.0.4"
@@ -1801,27 +1729,7 @@ gonzales-pe@3.4.7:
dependencies: dependencies:
minimist "1.1.x" minimist "1.1.x"
got@^5.0.0: graceful-fs@^4.1.0, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
version "5.7.1"
resolved "https://registry.yarnpkg.com/got/-/got-5.7.1.tgz#5f81635a61e4a6589f180569ea4e381680a51f35"
dependencies:
create-error-class "^3.0.1"
duplexer2 "^0.1.4"
is-redirect "^1.0.0"
is-retry-allowed "^1.0.0"
is-stream "^1.0.0"
lowercase-keys "^1.0.0"
node-status-codes "^1.0.0"
object-assign "^4.0.1"
parse-json "^2.1.0"
pinkie-promise "^2.0.0"
read-all-stream "^3.0.0"
readable-stream "^2.0.5"
timed-out "^3.0.0"
unzip-response "^1.0.2"
url-parse-lax "^1.0.0"
graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
version "4.1.11" version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@@ -2311,7 +2219,7 @@ inherits@1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b"
inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1:
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
@@ -2430,10 +2338,6 @@ is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4:
jsonpointer "^4.0.0" jsonpointer "^4.0.0"
xtend "^4.0.0" xtend "^4.0.0"
is-npm@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4"
is-number@^0.1.1: is-number@^0.1.1:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806" resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806"
@@ -2444,10 +2348,6 @@ is-number@^2.0.2, is-number@^2.1.0:
dependencies: dependencies:
kind-of "^3.0.2" kind-of "^3.0.2"
is-obj@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
is-path-cwd@^1.0.0: is-path-cwd@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
@@ -2476,21 +2376,13 @@ is-property@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
is-redirect@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24"
is-resolvable@^1.0.0: is-resolvable@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62"
dependencies: dependencies:
tryit "^1.0.1" tryit "^1.0.1"
is-retry-allowed@^1.0.0: is-stream@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
is-stream@^1.0.0, is-stream@^1.0.1:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
@@ -2811,20 +2703,10 @@ klaw@^1.0.0:
optionalDependencies: optionalDependencies:
graceful-fs "^4.1.9" graceful-fs "^4.1.9"
latest-version@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-2.0.0.tgz#56f8d6139620847b8017f8f1f4d78e211324168b"
dependencies:
package-json "^2.0.0"
lazy-cache@^1.0.3: lazy-cache@^1.0.3:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
lazy-req@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/lazy-req/-/lazy-req-1.1.0.tgz#bdaebead30f8d824039ce0ce149d4daa07ba1fac"
lazystream@^1.0.0: lazystream@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4"
@@ -3006,10 +2888,6 @@ lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.3.tgz#c92393d976793eee5ba4edb583cf8eae35bd9bfb" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.3.tgz#c92393d976793eee5ba4edb583cf8eae35bd9bfb"
lowercase-keys@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
lru-cache@2: lru-cache@2:
version "2.7.3" version "2.7.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952"
@@ -3294,10 +3172,6 @@ node-sass@^3.7.0:
request "^2.61.0" request "^2.61.0"
sass-graph "^2.1.1" sass-graph "^2.1.1"
node-status-codes@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/node-status-codes/-/node-status-codes-1.0.0.tgz#5ae5541d024645d32a58fcddc9ceecea7ae3ac2f"
"nomnom@>= 1.5.x": "nomnom@>= 1.5.x":
version "1.8.1" version "1.8.1"
resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7" resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7"
@@ -3442,22 +3316,13 @@ os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
osenv@0, osenv@^0.1.0: osenv@0:
version "0.1.4" version "0.1.4"
resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644"
dependencies: dependencies:
os-homedir "^1.0.0" os-homedir "^1.0.0"
os-tmpdir "^1.0.0" os-tmpdir "^1.0.0"
package-json@^2.0.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/package-json/-/package-json-2.4.0.tgz#0d15bd67d1cbbddbb2ca222ff2edb86bcb31a8bb"
dependencies:
got "^5.0.0"
registry-auth-token "^3.0.1"
registry-url "^3.0.3"
semver "^5.1.0"
pako@~0.2.0: pako@~0.2.0:
version "0.2.9" version "0.2.9"
resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
@@ -3477,7 +3342,7 @@ parse-glob@^3.0.4:
is-extglob "^1.0.0" is-extglob "^1.0.0"
is-glob "^2.0.0" is-glob "^2.0.0"
parse-json@^2.1.0, parse-json@^2.2.0: parse-json@^2.2.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
dependencies: dependencies:
@@ -3536,6 +3401,10 @@ path-is-inside@^1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
path-parse@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
path-type@^1.0.0: path-type@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
@@ -3621,10 +3490,6 @@ prelude-ls@~1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
prepend-http@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
preserve@^0.2.0: preserve@^0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
@@ -3723,7 +3588,7 @@ raw-body@~2.2.0:
iconv-lite "0.4.15" iconv-lite "0.4.15"
unpipe "1.0.0" unpipe "1.0.0"
rc@^1.0.1, rc@^1.1.6, rc@~1.1.6: rc@~1.1.6:
version "1.1.6" version "1.1.6"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.6.tgz#43651b76b6ae53b5c802f1151fa3fc3b059969c9" resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.6.tgz#43651b76b6ae53b5c802f1151fa3fc3b059969c9"
dependencies: dependencies:
@@ -3732,13 +3597,6 @@ rc@^1.0.1, rc@^1.1.6, rc@~1.1.6:
minimist "^1.2.0" minimist "^1.2.0"
strip-json-comments "~1.0.4" strip-json-comments "~1.0.4"
read-all-stream@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa"
dependencies:
pinkie-promise "^2.0.0"
readable-stream "^2.0.0"
read-pkg-up@^1.0.1: read-pkg-up@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
@@ -3769,7 +3627,7 @@ readable-stream@1.1:
isarray "0.0.1" isarray "0.0.1"
string_decoder "~0.10.x" string_decoder "~0.10.x"
readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.2.2: readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5:
version "2.2.2" version "2.2.2"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e"
dependencies: dependencies:
@@ -3852,18 +3710,6 @@ regex-cache@^0.4.2:
is-equal-shallow "^0.1.3" is-equal-shallow "^0.1.3"
is-primitive "^2.0.0" is-primitive "^2.0.0"
registry-auth-token@^3.0.1:
version "3.1.0"
resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.1.0.tgz#997c08256e0c7999837b90e944db39d8a790276b"
dependencies:
rc "^1.1.6"
registry-url@^3.0.3:
version "3.1.0"
resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
dependencies:
rc "^1.0.1"
relateurl@0.2.x: relateurl@0.2.x:
version "0.2.7" version "0.2.7"
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
@@ -3961,10 +3807,16 @@ resolve-pkg@^0.1.0:
dependencies: dependencies:
resolve-from "^2.0.0" resolve-from "^2.0.0"
resolve@1.1.x, resolve@^1.1.6, resolve@^1.1.7, resolve@~1.1.0: resolve@1.1.x, resolve@^1.1.6, resolve@~1.1.0:
version "1.1.7" version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
resolve@^1.3.2:
version "1.3.3"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5"
dependencies:
path-parse "^1.0.5"
restore-cursor@^1.0.1: restore-cursor@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
@@ -4048,13 +3900,7 @@ sass-lint@^1.10.2:
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
util "^0.10.3" util "^0.10.3"
semver-diff@^2.0.0: "semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@~5.3.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
dependencies:
semver "^5.0.3"
"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@~5.3.0:
version "5.3.0" version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
@@ -4122,10 +3968,6 @@ slice-ansi@0.0.4:
version "0.0.4" version "0.0.4"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
slide@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707"
snake-case@^2.1.0: snake-case@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f"
@@ -4468,10 +4310,6 @@ through@^2.3.6:
version "2.3.8" version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
timed-out@^3.0.0:
version "3.1.3"
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-3.1.3.tgz#95860bfcc5c76c277f8f8326fd0f5b2e20eba217"
tiny-emitter@^1.0.0: tiny-emitter@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-1.1.0.tgz#ab405a21ffed814a76c19739648093d70654fecb" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-1.1.0.tgz#ab405a21ffed814a76c19739648093d70654fecb"
@@ -4544,18 +4382,23 @@ tryor@~0.1.2:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/tryor/-/tryor-0.1.2.tgz#8145e4ca7caff40acde3ccf946e8b8bb75b4172b" resolved "https://registry.yarnpkg.com/tryor/-/tryor-0.1.2.tgz#8145e4ca7caff40acde3ccf946e8b8bb75b4172b"
tslint@^4.0.2: tslint@^5.1.0:
version "4.4.2" version "5.1.0"
resolved "https://registry.yarnpkg.com/tslint/-/tslint-4.4.2.tgz#b14cb79ae039c72471ab4c2627226b940dda19c6" resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.1.0.tgz#51a47baeeb58956fcd617bd2cf00e2ef0eea2ed9"
dependencies: dependencies:
babel-code-frame "^6.20.0" babel-code-frame "^6.22.0"
colors "^1.1.2" colors "^1.1.2"
diff "^3.0.1" diff "^3.2.0"
findup-sync "~0.3.0" findup-sync "~0.3.0"
glob "^7.1.1" glob "^7.1.1"
optimist "~0.6.0" optimist "~0.6.0"
resolve "^1.1.7" resolve "^1.3.2"
update-notifier "^1.0.2" semver "^5.3.0"
tsutils "^1.4.0"
tsutils@^1.4.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-1.7.0.tgz#2e63ccc2d6912bb095f7e363ff4100721dc86f50"
tunnel-agent@~0.4.1: tunnel-agent@~0.4.1:
version "0.4.3" version "0.4.3"
@@ -4578,13 +4421,13 @@ type-is@~1.6.10, type-is@~1.6.14:
media-typer "0.3.0" media-typer "0.3.0"
mime-types "~2.1.13" mime-types "~2.1.13"
typedarray@^0.0.6, typedarray@~0.0.5: typedarray@~0.0.5:
version "0.0.6" version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
typescript@^2.1.4: typescript@^2.2.2:
version "2.1.5" version "2.2.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.1.5.tgz#6fe9479e00e01855247cea216e7561bafcdbcd4a" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.2.2.tgz#606022508479b55ffa368b58fee963a03dfd7b0c"
uglify-js@2.6.x: uglify-js@2.6.x:
version "2.6.4" version "2.6.4"
@@ -4640,23 +4483,6 @@ unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
unzip-response@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe"
update-notifier@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-1.0.3.tgz#8f92c515482bd6831b7c93013e70f87552c7cf5a"
dependencies:
boxen "^0.6.0"
chalk "^1.0.0"
configstore "^2.0.0"
is-npm "^1.0.0"
latest-version "^2.0.0"
lazy-req "^1.1.0"
semver-diff "^2.0.0"
xdg-basedir "^2.0.0"
upper-case-first@^1.1.0, upper-case-first@^1.1.2: upper-case-first@^1.1.0, upper-case-first@^1.1.2:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115" resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115"
@@ -4671,12 +4497,6 @@ uri-path@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/uri-path/-/uri-path-1.0.0.tgz#9747f018358933c31de0fccfd82d138e67262e32" resolved "https://registry.yarnpkg.com/uri-path/-/uri-path-1.0.0.tgz#9747f018358933c31de0fccfd82d138e67262e32"
url-parse-lax@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73"
dependencies:
prepend-http "^1.0.1"
user-home@^2.0.0: user-home@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f"
@@ -4718,7 +4538,7 @@ utils-merge@1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8"
uuid@^2.0.1, uuid@^2.0.2: uuid@^2.0.2:
version "2.0.3" version "2.0.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
@@ -4809,12 +4629,6 @@ wide-align@^1.1.0:
dependencies: dependencies:
string-width "^1.0.1" string-width "^1.0.1"
widest-line@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-1.0.0.tgz#0c09c85c2a94683d0d7eaf8ee097d564bf0e105c"
dependencies:
string-width "^1.0.1"
window-size@0.1.0: window-size@0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
@@ -4858,14 +4672,6 @@ wrappy@1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
write-file-atomic@^1.1.2:
version "1.3.1"
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.1.tgz#7d45ba32316328dd1ec7d90f60ebc0d845bb759a"
dependencies:
graceful-fs "^4.1.11"
imurmurhash "^0.1.4"
slide "^1.1.5"
write@^0.2.1: write@^0.2.1:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
@@ -4879,12 +4685,6 @@ ws@1.0.1:
options ">=0.0.5" options ">=0.0.5"
ultron "1.0.x" ultron "1.0.x"
xdg-basedir@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2"
dependencies:
os-homedir "^1.0.0"
xml-char-classes@^1.0.0: xml-char-classes@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d" resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d"