Merge branch 'master' into develop
This commit is contained in:
@@ -76,6 +76,7 @@ func AdminUpdateUserPassword(c *m.ReqContext, form dtos.AdminUpdateUserPasswordF
|
||||
c.JsonOK("User password updated")
|
||||
}
|
||||
|
||||
// PUT /api/admin/users/:id/permissions
|
||||
func AdminUpdateUserPermissions(c *m.ReqContext, form dtos.AdminUpdateUserPermissionsForm) {
|
||||
userID := c.ParamsInt64(":id")
|
||||
|
||||
@@ -85,6 +86,11 @@ func AdminUpdateUserPermissions(c *m.ReqContext, form dtos.AdminUpdateUserPermis
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
if err == m.ErrLastGrafanaAdmin {
|
||||
c.JsonApiErr(400, m.ErrLastGrafanaAdmin.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonApiErr(500, "Failed to update user permissions", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestAdminApiEndpoint(t *testing.T) {
|
||||
role := m.ROLE_ADMIN
|
||||
Convey("Given a server admin attempts to remove themself as an admin", t, func() {
|
||||
|
||||
updateCmd := dtos.AdminUpdateUserPermissionsForm{
|
||||
IsGrafanaAdmin: false,
|
||||
}
|
||||
|
||||
bus.AddHandler("test", func(cmd *m.UpdateUserPermissionsCommand) error {
|
||||
return m.ErrLastGrafanaAdmin
|
||||
})
|
||||
|
||||
putAdminScenario("When calling PUT on", "/api/admin/users/1/permissions", "/api/admin/users/:id/permissions", role, updateCmd, func(sc *scenarioContext) {
|
||||
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
|
||||
So(sc.resp.Code, ShouldEqual, 400)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func putAdminScenario(desc string, url string, routePattern string, role m.RoleType, cmd dtos.AdminUpdateUserPermissionsForm, fn scenarioFunc) {
|
||||
Convey(desc+" "+url, func() {
|
||||
defer bus.ClearBusHandlers()
|
||||
|
||||
sc := setupScenarioContext(url)
|
||||
sc.defaultHandler = Wrap(func(c *m.ReqContext) {
|
||||
sc.context = c
|
||||
sc.context.UserId = TestUserID
|
||||
sc.context.OrgId = TestOrgID
|
||||
sc.context.OrgRole = role
|
||||
|
||||
AdminUpdateUserPermissions(c, cmd)
|
||||
})
|
||||
|
||||
sc.m.Put(routePattern, sc.defaultHandler)
|
||||
|
||||
fn(sc)
|
||||
})
|
||||
}
|
||||
@@ -277,10 +277,6 @@ func PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) Response {
|
||||
return Error(500, "Failed to save dashboard", err)
|
||||
}
|
||||
|
||||
if err == m.ErrDashboardFailedToUpdateAlertData {
|
||||
return Error(500, "Invalid alert data. Cannot save dashboard", err)
|
||||
}
|
||||
|
||||
c.TimeRequest(metrics.M_Api_Dashboard_Save)
|
||||
return JSON(200, util.DynMap{
|
||||
"status": "success",
|
||||
|
||||
@@ -727,7 +727,6 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
||||
{SaveError: m.ErrDashboardTitleEmpty, ExpectedStatusCode: 400},
|
||||
{SaveError: m.ErrDashboardFolderCannotHaveParent, ExpectedStatusCode: 400},
|
||||
{SaveError: alerting.ValidationError{Reason: "Mu"}, ExpectedStatusCode: 422},
|
||||
{SaveError: m.ErrDashboardFailedToUpdateAlertData, ExpectedStatusCode: 500},
|
||||
{SaveError: m.ErrDashboardFailedGenerateUniqueUid, ExpectedStatusCode: 500},
|
||||
{SaveError: m.ErrDashboardTypeMismatch, ExpectedStatusCode: 400},
|
||||
{SaveError: m.ErrDashboardFolderWithSameNameAsDashboard, ExpectedStatusCode: 400},
|
||||
|
||||
@@ -4,10 +4,18 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func SendResetPasswordEmail(c *m.ReqContext, form dtos.SendResetPasswordEmailForm) Response {
|
||||
if setting.LdapEnabled || setting.AuthProxyEnabled {
|
||||
return Error(401, "Not allowed to reset password when LDAP or Auth Proxy is enabled", nil)
|
||||
}
|
||||
if setting.DisableLoginForm {
|
||||
return Error(401, "Not allowed to reset password when login form is disabled", nil)
|
||||
}
|
||||
|
||||
userQuery := m.GetUserByLoginQuery{LoginOrEmail: form.UserOrEmail}
|
||||
|
||||
if err := bus.Dispatch(&userQuery); err != nil {
|
||||
|
||||
@@ -51,7 +51,7 @@ func ApplyRoute(ctx context.Context, req *http.Request, proxyPath string, route
|
||||
if token, err := tokenProvider.getAccessToken(data); err != nil {
|
||||
logger.Error("Failed to get access token", "error", err)
|
||||
} else {
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func ApplyRoute(ctx context.Context, req *http.Request, proxyPath string, route
|
||||
if token, err := tokenProvider.getJwtAccessToken(ctx, data); err != nil {
|
||||
logger.Error("Failed to get access token", "error", err)
|
||||
} else {
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func ApplyRoute(ctx context.Context, req *http.Request, proxyPath string, route
|
||||
if err != nil {
|
||||
logger.Error("Failed to get default access token from meta data server", "error", err)
|
||||
} else {
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token.AccessToken))
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token.AccessToken))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ func NewApiPluginProxy(ctx *m.ReqContext, proxyPath string, route *plugins.AppPl
|
||||
}
|
||||
|
||||
for key, value := range headers {
|
||||
log.Trace("setting key %v value %v", key, value[0])
|
||||
log.Trace("setting key %v value <redacted>", key)
|
||||
req.Header.Set(key, value[0])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ var (
|
||||
ErrDashboardVersionMismatch = errors.New("The dashboard has been changed by someone else")
|
||||
ErrDashboardTitleEmpty = errors.New("Dashboard title cannot be empty")
|
||||
ErrDashboardFolderCannotHaveParent = errors.New("A Dashboard Folder cannot be added to another folder")
|
||||
ErrDashboardFailedToUpdateAlertData = errors.New("Failed to save alert data")
|
||||
ErrDashboardsWithSameSlugExists = errors.New("Multiple dashboards with the same slug exists")
|
||||
ErrDashboardFailedGenerateUniqueUid = errors.New("Failed to generate unique dashboard id")
|
||||
ErrDashboardTypeMismatch = errors.New("Dashboard cannot be changed to a folder")
|
||||
|
||||
+2
-1
@@ -7,7 +7,8 @@ import (
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrUserNotFound = errors.New("User not found")
|
||||
ErrUserNotFound = errors.New("User not found")
|
||||
ErrLastGrafanaAdmin = errors.New("Cannot remove last grafana admin")
|
||||
)
|
||||
|
||||
type Password string
|
||||
|
||||
@@ -165,7 +165,7 @@ func (dr *dashboardServiceImpl) updateAlerting(cmd *models.SaveDashboardCommand,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&alertCmd); err != nil {
|
||||
return models.ErrDashboardFailedToUpdateAlertData
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -504,8 +504,18 @@ func UpdateUserPermissions(cmd *m.UpdateUserPermissionsCommand) error {
|
||||
|
||||
user.IsAdmin = cmd.IsGrafanaAdmin
|
||||
sess.UseBool("is_admin")
|
||||
|
||||
_, err := sess.ID(user.Id).Update(&user)
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// validate that after update there is at least one server admin
|
||||
if err := validateOneAdminLeft(sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -522,3 +532,17 @@ func SetUserHelpFlag(cmd *m.SetUserHelpFlagCommand) error {
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func validateOneAdminLeft(sess *DBSession) error {
|
||||
// validate that there is an admin user left
|
||||
count, err := sess.Where("is_admin=?", true).Count(&m.User{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return m.ErrLastGrafanaAdmin
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -155,6 +155,32 @@ func TestUserDataAccess(t *testing.T) {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given one grafana admin user", func() {
|
||||
var err error
|
||||
createUserCmd := &m.CreateUserCommand{
|
||||
Email: fmt.Sprint("admin", "@test.com"),
|
||||
Name: fmt.Sprint("admin"),
|
||||
Login: fmt.Sprint("admin"),
|
||||
IsAdmin: true,
|
||||
}
|
||||
err = CreateUser(context.Background(), createUserCmd)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("Cannot make themselves a non-admin", func() {
|
||||
updateUserPermsCmd := m.UpdateUserPermissionsCommand{IsGrafanaAdmin: false, UserId: 1}
|
||||
updatePermsError := UpdateUserPermissions(&updateUserPermsCmd)
|
||||
|
||||
So(updatePermsError, ShouldEqual, m.ErrLastGrafanaAdmin)
|
||||
|
||||
query := m.GetUserByIdQuery{Id: createUserCmd.Result.Id}
|
||||
getUserError := GetUserById(&query)
|
||||
|
||||
So(getUserError, ShouldBeNil)
|
||||
|
||||
So(query.Result.IsAdmin, ShouldEqual, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ func init() {
|
||||
"AWS/CloudFront": {"Requests", "BytesDownloaded", "BytesUploaded", "TotalErrorRate", "4xxErrorRate", "5xxErrorRate"},
|
||||
"AWS/CloudSearch": {"SuccessfulRequests", "SearchableDocuments", "IndexUtilization", "Partitions"},
|
||||
"AWS/CloudHSM": {"HsmUnhealthy", "HsmTemperature", "HsmKeysSessionOccupied", "HsmKeysTokenOccupied", "HsmSslCtxsOccupied", "HsmSessionCount", "HsmUsersAvailable", "HsmUsersMax", "InterfaceEth2OctetsInput", "InterfaceEth2OctetsOutput"},
|
||||
"AWS/CodeBuild": {"BuildDuration", "Builds", "DownloadSourceDuration", "Duration", "FailedBuilds", "FinalizingDuration", "InstallDuration", "PostBuildDuration", "PreBuildDuration", "ProvisioningDuration", "QueuedDuration", "SubmittedDuration", "SucceededBuilds", "UploadArtifactsDuration"},
|
||||
"AWS/Connect": {"CallsBreachingConcurrencyQuota", "CallBackNotDialableNumber", "CallRecordingUploadError", "CallsPerInterval", "ConcurrentCalls", "ConcurrentCallsPercentage", "ContactFlowErrors", "ContactFlowFatalErrors", "LongestQueueWaitTime", "MissedCalls", "MisconfiguredPhoneNumbers", "PublicSigningKeyUsage", "QueueCapacityExceededError", "QueueSize", "ThrottledCalls", "ToInstancePacketLossRate"},
|
||||
"AWS/DMS": {"FreeableMemory", "WriteIOPS", "ReadIOPS", "WriteThroughput", "ReadThroughput", "WriteLatency", "ReadLatency", "SwapUsage", "NetworkTransmitThroughput", "NetworkReceiveThroughput", "FullLoadThroughputBandwidthSource", "FullLoadThroughputBandwidthTarget", "FullLoadThroughputRowsSource", "FullLoadThroughputRowsTarget", "CDCIncomingChanges", "CDCChangesMemorySource", "CDCChangesMemoryTarget", "CDCChangesDiskSource", "CDCChangesDiskTarget", "CDCThroughputBandwidthTarget", "CDCThroughputRowsSource", "CDCThroughputRowsTarget", "CDCLatencySource", "CDCLatencyTarget"},
|
||||
"AWS/DX": {"ConnectionState", "ConnectionBpsEgress", "ConnectionBpsIngress", "ConnectionPpsEgress", "ConnectionPpsIngress", "ConnectionCRCErrorCount", "ConnectionLightLevelTx", "ConnectionLightLevelRx"},
|
||||
@@ -123,6 +124,7 @@ func init() {
|
||||
"AWS/CloudFront": {"DistributionId", "Region"},
|
||||
"AWS/CloudSearch": {},
|
||||
"AWS/CloudHSM": {"Region", "ClusterId", "HsmId"},
|
||||
"AWS/CodeBuild": {"ProjectName"},
|
||||
"AWS/Connect": {"InstanceId", "MetricGroup", "Participant", "QueueName", "Stream Type", "Type of Connection"},
|
||||
"AWS/DMS": {"ReplicationInstanceIdentifier", "ReplicationTaskIdentifier"},
|
||||
"AWS/DX": {"ConnectionId"},
|
||||
|
||||
@@ -65,7 +65,7 @@ var NewClient = func(ctx context.Context, ds *models.DataSource, timeRange *tsdb
|
||||
clientLog.Debug("Creating new client", "version", version, "timeField", timeField, "indices", strings.Join(indices, ", "))
|
||||
|
||||
switch version {
|
||||
case 2, 5, 56:
|
||||
case 2, 5, 56, 60:
|
||||
return &baseClientImpl{
|
||||
ctx: ctx,
|
||||
ds: ds,
|
||||
|
||||
@@ -90,6 +90,19 @@ func TestClient(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(c.GetVersion(), ShouldEqual, 56)
|
||||
})
|
||||
|
||||
Convey("When version 60 should return v6.0 client", func() {
|
||||
ds := &models.DataSource{
|
||||
JsonData: simplejson.NewFromAny(map[string]interface{}{
|
||||
"esVersion": 60,
|
||||
"timeField": "@timestamp",
|
||||
}),
|
||||
}
|
||||
|
||||
c, err := NewClient(context.Background(), ds, nil)
|
||||
So(err, ShouldBeNil)
|
||||
So(c.GetVersion(), ShouldEqual, 60)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given a fake http client", func() {
|
||||
@@ -153,8 +166,6 @@ func TestClient(t *testing.T) {
|
||||
jBody, err := simplejson.NewJson(bodyBytes)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
fmt.Println("body", string(headerBytes))
|
||||
|
||||
So(jHeader.Get("index").MustString(), ShouldEqual, "metrics-2018.05.15")
|
||||
So(jHeader.Get("ignore_unavailable").MustBool(false), ShouldEqual, true)
|
||||
So(jHeader.Get("search_type").MustString(), ShouldEqual, "count")
|
||||
@@ -209,8 +220,6 @@ func TestClient(t *testing.T) {
|
||||
jBody, err := simplejson.NewJson(bodyBytes)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
fmt.Println("body", string(headerBytes))
|
||||
|
||||
So(jHeader.Get("index").MustString(), ShouldEqual, "metrics-2018.05.15")
|
||||
So(jHeader.Get("ignore_unavailable").MustBool(false), ShouldEqual, true)
|
||||
So(jHeader.Get("search_type").MustString(), ShouldEqual, "query_then_fetch")
|
||||
@@ -265,8 +274,6 @@ func TestClient(t *testing.T) {
|
||||
jBody, err := simplejson.NewJson(bodyBytes)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
fmt.Println("body", string(headerBytes))
|
||||
|
||||
So(jHeader.Get("index").MustString(), ShouldEqual, "metrics-2018.05.15")
|
||||
So(jHeader.Get("ignore_unavailable").MustBool(false), ShouldEqual, true)
|
||||
So(jHeader.Get("search_type").MustString(), ShouldEqual, "query_then_fetch")
|
||||
|
||||
@@ -240,6 +240,7 @@ type DateHistogramAgg struct {
|
||||
Missing *string `json:"missing,omitempty"`
|
||||
ExtendedBounds *ExtendedBounds `json:"extended_bounds"`
|
||||
Format string `json:"format"`
|
||||
Offset string `json:"offset,omitempty"`
|
||||
}
|
||||
|
||||
// FiltersAggregation represents a filters aggregation
|
||||
|
||||
@@ -134,6 +134,10 @@ func addDateHistogramAgg(aggBuilder es.AggBuilder, bucketAgg *BucketAgg, timeFro
|
||||
a.Interval = "$__interval"
|
||||
}
|
||||
|
||||
if offset, err := bucketAgg.Settings.Get("offset").String(); err == nil {
|
||||
a.Offset = offset
|
||||
}
|
||||
|
||||
if missing, err := bucketAgg.Settings.Get("missing").String(); err == nil {
|
||||
a.Missing = &missing
|
||||
}
|
||||
|
||||
@@ -66,6 +66,10 @@ func (m *msSqlMacroEngine) evaluateMacro(name string, args []string) (string, er
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s BETWEEN '%s' AND '%s'", args[0], m.timeRange.GetFromAsTimeUTC().Format(time.RFC3339), m.timeRange.GetToAsTimeUTC().Format(time.RFC3339)), nil
|
||||
case "__timeFrom":
|
||||
return fmt.Sprintf("'%s'", m.timeRange.GetFromAsTimeUTC().Format(time.RFC3339)), nil
|
||||
case "__timeTo":
|
||||
return fmt.Sprintf("'%s'", m.timeRange.GetToAsTimeUTC().Format(time.RFC3339)), nil
|
||||
case "__timeGroup":
|
||||
if len(args) < 2 {
|
||||
return "", fmt.Errorf("macro %v needs time column and interval", name)
|
||||
|
||||
@@ -52,6 +52,20 @@ func TestMacroEngine(t *testing.T) {
|
||||
So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339), to.Format(time.RFC3339)))
|
||||
})
|
||||
|
||||
Convey("interpolate __timeFrom function", func() {
|
||||
sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom()")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(sql, ShouldEqual, "select '2018-04-12T18:00:00Z'")
|
||||
})
|
||||
|
||||
Convey("interpolate __timeTo function", func() {
|
||||
sql, err := engine.Interpolate(query, timeRange, "select $__timeTo()")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(sql, ShouldEqual, "select '2018-04-12T18:05:00Z'")
|
||||
})
|
||||
|
||||
Convey("interpolate __timeGroup function", func() {
|
||||
sql, err := engine.Interpolate(query, timeRange, "GROUP BY $__timeGroup(time_column,'5m')")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
@@ -61,6 +61,10 @@ func (m *mySqlMacroEngine) evaluateMacro(name string, args []string) (string, er
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s BETWEEN FROM_UNIXTIME(%d) AND FROM_UNIXTIME(%d)", args[0], m.timeRange.GetFromAsSecondsEpoch(), m.timeRange.GetToAsSecondsEpoch()), nil
|
||||
case "__timeFrom":
|
||||
return fmt.Sprintf("FROM_UNIXTIME(%d)", m.timeRange.GetFromAsSecondsEpoch()), nil
|
||||
case "__timeTo":
|
||||
return fmt.Sprintf("FROM_UNIXTIME(%d)", m.timeRange.GetToAsSecondsEpoch()), nil
|
||||
case "__timeGroup":
|
||||
if len(args) < 2 {
|
||||
return "", fmt.Errorf("macro %v needs time column and interval", name)
|
||||
|
||||
@@ -63,6 +63,20 @@ func TestMacroEngine(t *testing.T) {
|
||||
So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN FROM_UNIXTIME(%d) AND FROM_UNIXTIME(%d)", from.Unix(), to.Unix()))
|
||||
})
|
||||
|
||||
Convey("interpolate __timeFrom function", func() {
|
||||
sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom()")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", from.Unix()))
|
||||
})
|
||||
|
||||
Convey("interpolate __timeTo function", func() {
|
||||
sql, err := engine.Interpolate(query, timeRange, "select $__timeTo()")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", to.Unix()))
|
||||
})
|
||||
|
||||
Convey("interpolate __unixEpochFilter function", func() {
|
||||
sql, err := engine.Interpolate(query, timeRange, "select $__unixEpochFilter(time)")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
@@ -761,7 +761,7 @@ func TestMySQL(t *testing.T) {
|
||||
{
|
||||
DataSource: &models.DataSource{JsonData: simplejson.New()},
|
||||
Model: simplejson.NewFromAny(map[string]interface{}{
|
||||
"rawSql": `SELECT time FROM metric_values WHERE time > $__timeFrom() OR time < $__timeFrom() OR 1 < $__unixEpochFrom() OR $__unixEpochTo() > 1 ORDER BY 1`,
|
||||
"rawSql": `SELECT time FROM metric_values WHERE time > $__timeFrom() OR time < $__timeTo() OR 1 < $__unixEpochFrom() OR $__unixEpochTo() > 1 ORDER BY 1`,
|
||||
"format": "time_series",
|
||||
}),
|
||||
RefId: "A",
|
||||
@@ -773,7 +773,7 @@ func TestMySQL(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
queryResult := resp.Results["A"]
|
||||
So(queryResult.Error, ShouldBeNil)
|
||||
So(queryResult.Meta.Get("sql").MustString(), ShouldEqual, "SELECT time FROM metric_values WHERE time > '2018-03-15T12:55:00Z' OR time < '2018-03-15T12:55:00Z' OR 1 < 1521118500 OR 1521118800 > 1 ORDER BY 1")
|
||||
So(queryResult.Meta.Get("sql").MustString(), ShouldEqual, "SELECT time FROM metric_values WHERE time > FROM_UNIXTIME(1521118500) OR time < FROM_UNIXTIME(1521118800) OR 1 < 1521118500 OR 1521118800 > 1 ORDER BY 1")
|
||||
|
||||
})
|
||||
|
||||
|
||||
@@ -87,6 +87,10 @@ func (m *postgresMacroEngine) evaluateMacro(name string, args []string) (string,
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s BETWEEN '%s' AND '%s'", args[0], m.timeRange.GetFromAsTimeUTC().Format(time.RFC3339), m.timeRange.GetToAsTimeUTC().Format(time.RFC3339)), nil
|
||||
case "__timeFrom":
|
||||
return fmt.Sprintf("'%s'", m.timeRange.GetFromAsTimeUTC().Format(time.RFC3339)), nil
|
||||
case "__timeTo":
|
||||
return fmt.Sprintf("'%s'", m.timeRange.GetToAsTimeUTC().Format(time.RFC3339)), nil
|
||||
case "__timeGroup":
|
||||
if len(args) < 2 {
|
||||
return "", fmt.Errorf("macro %v needs time column and interval and optional fill value", name)
|
||||
|
||||
@@ -44,6 +44,20 @@ func TestMacroEngine(t *testing.T) {
|
||||
So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339), to.Format(time.RFC3339)))
|
||||
})
|
||||
|
||||
Convey("interpolate __timeFrom function", func() {
|
||||
sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom()")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(sql, ShouldEqual, "select '2018-04-12T18:00:00Z'")
|
||||
})
|
||||
|
||||
Convey("interpolate __timeTo function", func() {
|
||||
sql, err := engine.Interpolate(query, timeRange, "select $__timeTo()")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(sql, ShouldEqual, "select '2018-04-12T18:05:00Z'")
|
||||
})
|
||||
|
||||
Convey("interpolate __timeGroup function pre 5.3 compatibility", func() {
|
||||
|
||||
sql, err := engine.Interpolate(query, timeRange, "SELECT $__timeGroup(time_column,'5m'), value")
|
||||
|
||||
@@ -196,8 +196,6 @@ var Interpolate = func(query *Query, timeRange *TimeRange, sql string) (string,
|
||||
|
||||
sql = strings.Replace(sql, "$__interval_ms", strconv.FormatInt(interval.Milliseconds(), 10), -1)
|
||||
sql = strings.Replace(sql, "$__interval", interval.Text, -1)
|
||||
sql = strings.Replace(sql, "$__timeFrom()", fmt.Sprintf("'%s'", timeRange.GetFromAsTimeUTC().Format(time.RFC3339)), -1)
|
||||
sql = strings.Replace(sql, "$__timeTo()", fmt.Sprintf("'%s'", timeRange.GetToAsTimeUTC().Format(time.RFC3339)), -1)
|
||||
sql = strings.Replace(sql, "$__unixEpochFrom()", fmt.Sprintf("%d", timeRange.GetFromAsSecondsEpoch()), -1)
|
||||
sql = strings.Replace(sql, "$__unixEpochTo()", fmt.Sprintf("%d", timeRange.GetToAsSecondsEpoch()), -1)
|
||||
|
||||
|
||||
@@ -44,20 +44,6 @@ func TestSqlEngine(t *testing.T) {
|
||||
So(sql, ShouldEqual, "select 60000 ")
|
||||
})
|
||||
|
||||
Convey("interpolate __timeFrom function", func() {
|
||||
sql, err := Interpolate(query, timeRange, "select $__timeFrom()")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(sql, ShouldEqual, fmt.Sprintf("select '%s'", from.Format(time.RFC3339)))
|
||||
})
|
||||
|
||||
Convey("interpolate __timeTo function", func() {
|
||||
sql, err := Interpolate(query, timeRange, "select $__timeTo()")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(sql, ShouldEqual, fmt.Sprintf("select '%s'", to.Format(time.RFC3339)))
|
||||
})
|
||||
|
||||
Convey("interpolate __unixEpochFrom function", func() {
|
||||
sql, err := Interpolate(query, timeRange, "select $__unixEpochFrom()")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Reference in New Issue
Block a user