Commit ed661767 by bergquist

follow go idiom and return error as second param

parent 79b873e4
...@@ -43,7 +43,7 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response { ...@@ -43,7 +43,7 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
}) })
} }
resp, err := tsdb.HandleRequest(context.Background(), request) resp, err := tsdb.HandleRequest(context.Background(), dsQuery.Result, request)
if err != nil { if err != nil {
return ApiError(500, "Metric request error", err) return ApiError(500, "Metric request error", err)
} }
...@@ -100,16 +100,17 @@ func GetTestDataRandomWalk(c *middleware.Context) Response { ...@@ -100,16 +100,17 @@ func GetTestDataRandomWalk(c *middleware.Context) Response {
timeRange := tsdb.NewTimeRange(from, to) timeRange := tsdb.NewTimeRange(from, to)
request := &tsdb.TsdbQuery{TimeRange: timeRange} request := &tsdb.TsdbQuery{TimeRange: timeRange}
dsInfo := &models.DataSource{Type: "grafana-testdata-datasource"}
request.Queries = append(request.Queries, &tsdb.Query{ request.Queries = append(request.Queries, &tsdb.Query{
RefId: "A", RefId: "A",
IntervalMs: intervalMs, IntervalMs: intervalMs,
Model: simplejson.NewFromAny(&util.DynMap{ Model: simplejson.NewFromAny(&util.DynMap{
"scenario": "random_walk", "scenario": "random_walk",
}), }),
DataSource: &models.DataSource{Type: "grafana-testdata-datasource"}, DataSource: dsInfo,
}) })
resp, err := tsdb.HandleRequest(context.Background(), request) resp, err := tsdb.HandleRequest(context.Background(), dsInfo, request)
if err != nil { if err != nil {
return ApiError(500, "Metric request error", err) return ApiError(500, "Metric request error", err)
} }
......
...@@ -112,7 +112,7 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange * ...@@ -112,7 +112,7 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *
req := c.getRequestForAlertRule(getDsInfo.Result, timeRange) req := c.getRequestForAlertRule(getDsInfo.Result, timeRange)
result := make(tsdb.TimeSeriesSlice, 0) result := make(tsdb.TimeSeriesSlice, 0)
resp, err := c.HandleRequest(context.Ctx, req) resp, err := c.HandleRequest(context.Ctx, getDsInfo.Result, req)
if err != nil { if err != nil {
if err == gocontext.DeadlineExceeded { if err == gocontext.DeadlineExceeded {
return nil, fmt.Errorf("Alert execution exceeded the timeout") return nil, fmt.Errorf("Alert execution exceeded the timeout")
......
...@@ -168,7 +168,7 @@ func (ctx *queryConditionTestContext) exec() (*alerting.ConditionResult, error) ...@@ -168,7 +168,7 @@ func (ctx *queryConditionTestContext) exec() (*alerting.ConditionResult, error)
ctx.condition = condition ctx.condition = condition
condition.HandleRequest = func(context context.Context, req *tsdb.TsdbQuery) (*tsdb.Response, error) { condition.HandleRequest = func(context context.Context, dsInfo *m.DataSource, req *tsdb.TsdbQuery) (*tsdb.Response, error) {
return &tsdb.Response{ return &tsdb.Response{
Results: map[string]*tsdb.QueryResult{ Results: map[string]*tsdb.QueryResult{
"A": {Series: ctx.series}, "A": {Series: ctx.series},
......
...@@ -20,18 +20,18 @@ func NewFakeExecutor(dsInfo *models.DataSource) (*FakeExecutor, error) { ...@@ -20,18 +20,18 @@ func NewFakeExecutor(dsInfo *models.DataSource) (*FakeExecutor, error) {
}, nil }, nil
} }
func (e *FakeExecutor) Query(ctx context.Context, dsInfo *models.DataSource, context *TsdbQuery) *BatchResult { func (e *FakeExecutor) Query(ctx context.Context, dsInfo *models.DataSource, context *TsdbQuery) (*Response, error) {
result := &BatchResult{QueryResults: make(map[string]*QueryResult)} result := &Response{Results: make(map[string]*QueryResult)}
for _, query := range context.Queries { for _, query := range context.Queries {
if results, has := e.results[query.RefId]; has { if results, has := e.results[query.RefId]; has {
result.QueryResults[query.RefId] = results result.Results[query.RefId] = results
} }
if testFunc, has := e.resultsFn[query.RefId]; has { if testFunc, has := e.resultsFn[query.RefId]; has {
result.QueryResults[query.RefId] = testFunc(context) result.Results[query.RefId] = testFunc(context)
} }
} }
return result return result, nil
} }
func (e *FakeExecutor) Return(refId string, series TimeSeriesSlice) { func (e *FakeExecutor) Return(refId string, series TimeSeriesSlice) {
......
...@@ -37,8 +37,8 @@ func init() { ...@@ -37,8 +37,8 @@ func init() {
tsdb.RegisterTsdbQueryEndpoint("graphite", NewGraphiteExecutor) tsdb.RegisterTsdbQueryEndpoint("graphite", NewGraphiteExecutor)
} }
func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult { func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
result := &tsdb.BatchResult{} result := &tsdb.Response{}
from := "-" + formatTimeRange(tsdbQuery.TimeRange.From) from := "-" + formatTimeRange(tsdbQuery.TimeRange.From)
until := formatTimeRange(tsdbQuery.TimeRange.To) until := formatTimeRange(tsdbQuery.TimeRange.To)
...@@ -67,14 +67,12 @@ func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource, ...@@ -67,14 +67,12 @@ func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource,
req, err := e.createRequest(dsInfo, formData) req, err := e.createRequest(dsInfo, formData)
if err != nil { if err != nil {
result.Error = err return nil, err
return result
} }
httpClient, err := dsInfo.GetHttpClient() httpClient, err := dsInfo.GetHttpClient()
if err != nil { if err != nil {
result.Error = err return nil, err
return result
} }
span, ctx := opentracing.StartSpanFromContext(ctx, "graphite query") span, ctx := opentracing.StartSpanFromContext(ctx, "graphite query")
...@@ -90,17 +88,15 @@ func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource, ...@@ -90,17 +88,15 @@ func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource,
res, err := ctxhttp.Do(ctx, httpClient, req) res, err := ctxhttp.Do(ctx, httpClient, req)
if err != nil { if err != nil {
result.Error = err return nil, err
return result
} }
data, err := e.parseResponse(res) data, err := e.parseResponse(res)
if err != nil { if err != nil {
result.Error = err return nil, err
return result
} }
result.QueryResults = make(map[string]*tsdb.QueryResult) result.Results = make(map[string]*tsdb.QueryResult)
queryRes := tsdb.NewQueryResult() queryRes := tsdb.NewQueryResult()
for _, series := range data { for _, series := range data {
...@@ -114,8 +110,8 @@ func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource, ...@@ -114,8 +110,8 @@ func (e *GraphiteExecutor) Query(ctx context.Context, dsInfo *models.DataSource,
} }
} }
result.QueryResults["A"] = queryRes result.Results["A"] = queryRes
return result return result, nil
} }
func (e *GraphiteExecutor) parseResponse(res *http.Response) ([]TargetResponseDTO, error) { func (e *GraphiteExecutor) parseResponse(res *http.Response) ([]TargetResponseDTO, error) {
......
...@@ -39,17 +39,17 @@ func init() { ...@@ -39,17 +39,17 @@ func init() {
tsdb.RegisterTsdbQueryEndpoint("influxdb", NewInfluxDBExecutor) tsdb.RegisterTsdbQueryEndpoint("influxdb", NewInfluxDBExecutor)
} }
func (e *InfluxDBExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult { func (e *InfluxDBExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
result := &tsdb.BatchResult{} result := &tsdb.Response{}
query, err := e.getQuery(dsInfo, tsdbQuery.Queries, tsdbQuery) query, err := e.getQuery(dsInfo, tsdbQuery.Queries, tsdbQuery)
if err != nil { if err != nil {
return result.WithError(err) return nil, err
} }
rawQuery, err := query.Build(tsdbQuery) rawQuery, err := query.Build(tsdbQuery)
if err != nil { if err != nil {
return result.WithError(err) return nil, err
} }
if setting.Env == setting.DEV { if setting.Env == setting.DEV {
...@@ -58,21 +58,21 @@ func (e *InfluxDBExecutor) Query(ctx context.Context, dsInfo *models.DataSource, ...@@ -58,21 +58,21 @@ func (e *InfluxDBExecutor) Query(ctx context.Context, dsInfo *models.DataSource,
req, err := e.createRequest(dsInfo, rawQuery) req, err := e.createRequest(dsInfo, rawQuery)
if err != nil { if err != nil {
return result.WithError(err) return nil, err
} }
httpClient, err := dsInfo.GetHttpClient() httpClient, err := dsInfo.GetHttpClient()
if err != nil { if err != nil {
return result.WithError(err) return nil, err
} }
resp, err := ctxhttp.Do(ctx, httpClient, req) resp, err := ctxhttp.Do(ctx, httpClient, req)
if err != nil { if err != nil {
return result.WithError(err) return nil, err
} }
if resp.StatusCode/100 != 2 { if resp.StatusCode/100 != 2 {
return result.WithError(fmt.Errorf("Influxdb returned statuscode invalid status code: %v", resp.Status)) return nil, fmt.Errorf("Influxdb returned statuscode invalid status code: %v", resp.Status)
} }
var response Response var response Response
...@@ -82,17 +82,17 @@ func (e *InfluxDBExecutor) Query(ctx context.Context, dsInfo *models.DataSource, ...@@ -82,17 +82,17 @@ func (e *InfluxDBExecutor) Query(ctx context.Context, dsInfo *models.DataSource,
err = dec.Decode(&response) err = dec.Decode(&response)
if err != nil { if err != nil {
return result.WithError(err) return nil, err
} }
if response.Err != nil { if response.Err != nil {
return result.WithError(response.Err) return nil, response.Err
} }
result.QueryResults = make(map[string]*tsdb.QueryResult) result.Results = make(map[string]*tsdb.QueryResult)
result.QueryResults["A"] = e.ResponseParser.Parse(&response, query) result.Results["A"] = e.ResponseParser.Parse(&response, query)
return result return result, nil
} }
func (e *InfluxDBExecutor) getQuery(dsInfo *models.DataSource, queries []*tsdb.Query, context *tsdb.TsdbQuery) (*Query, error) { func (e *InfluxDBExecutor) getQuery(dsInfo *models.DataSource, queries []*tsdb.Query, context *tsdb.TsdbQuery) (*Query, error) {
......
...@@ -22,24 +22,8 @@ type Query struct { ...@@ -22,24 +22,8 @@ type Query struct {
} }
type Response struct { type Response struct {
BatchTimings []*BatchTiming `json:"timings"` Results map[string]*QueryResult `json:"results"`
Results map[string]*QueryResult `json:"results"` Message string `json:"message,omitempty"`
Message string `json:"message,omitempty"`
}
type BatchTiming struct {
TimeElapsed int64
}
type BatchResult struct {
Error error
QueryResults map[string]*QueryResult
Timings *BatchTiming
}
func (br *BatchResult) WithError(err error) *BatchResult {
br.Error = err
return br
} }
type QueryResult struct { type QueryResult struct {
......
...@@ -85,9 +85,9 @@ func (e *MysqlExecutor) initEngine(dsInfo *models.DataSource) error { ...@@ -85,9 +85,9 @@ func (e *MysqlExecutor) initEngine(dsInfo *models.DataSource) error {
return nil return nil
} }
func (e *MysqlExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult { func (e *MysqlExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
result := &tsdb.BatchResult{ result := &tsdb.Response{
QueryResults: make(map[string]*tsdb.QueryResult), Results: make(map[string]*tsdb.QueryResult),
} }
macroEngine := NewMysqlMacroEngine(tsdbQuery.TimeRange) macroEngine := NewMysqlMacroEngine(tsdbQuery.TimeRange)
...@@ -102,7 +102,7 @@ func (e *MysqlExecutor) Query(ctx context.Context, dsInfo *models.DataSource, ts ...@@ -102,7 +102,7 @@ func (e *MysqlExecutor) Query(ctx context.Context, dsInfo *models.DataSource, ts
} }
queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: query.RefId} queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: query.RefId}
result.QueryResults[query.RefId] = queryResult result.Results[query.RefId] = queryResult
rawSql, err := macroEngine.Interpolate(rawSql) rawSql, err := macroEngine.Interpolate(rawSql)
if err != nil { if err != nil {
...@@ -138,7 +138,7 @@ func (e *MysqlExecutor) Query(ctx context.Context, dsInfo *models.DataSource, ts ...@@ -138,7 +138,7 @@ func (e *MysqlExecutor) Query(ctx context.Context, dsInfo *models.DataSource, ts
} }
} }
return result return result, nil
} }
func (e MysqlExecutor) TransformToTable(query *tsdb.Query, rows *core.Rows, result *tsdb.QueryResult) error { func (e MysqlExecutor) TransformToTable(query *tsdb.Query, rows *core.Rows, result *tsdb.QueryResult) error {
......
...@@ -50,8 +50,8 @@ func init() { ...@@ -50,8 +50,8 @@ func init() {
tsdb.RegisterTsdbQueryEndpoint("opentsdb", NewOpenTsdbExecutor) tsdb.RegisterTsdbQueryEndpoint("opentsdb", NewOpenTsdbExecutor)
} }
func (e *OpenTsdbExecutor) Query(ctx context.Context, dsInfo *models.DataSource, queryContext *tsdb.TsdbQuery) *tsdb.BatchResult { func (e *OpenTsdbExecutor) Query(ctx context.Context, dsInfo *models.DataSource, queryContext *tsdb.TsdbQuery) (*tsdb.Response, error) {
result := &tsdb.BatchResult{} result := &tsdb.Response{}
var tsdbQuery OpenTsdbQuery var tsdbQuery OpenTsdbQuery
...@@ -69,29 +69,26 @@ func (e *OpenTsdbExecutor) Query(ctx context.Context, dsInfo *models.DataSource, ...@@ -69,29 +69,26 @@ func (e *OpenTsdbExecutor) Query(ctx context.Context, dsInfo *models.DataSource,
req, err := e.createRequest(dsInfo, tsdbQuery) req, err := e.createRequest(dsInfo, tsdbQuery)
if err != nil { if err != nil {
result.Error = err return nil, err
return result
} }
httpClient, err := dsInfo.GetHttpClient() httpClient, err := dsInfo.GetHttpClient()
if err != nil { if err != nil {
result.Error = err return nil, err
return result
} }
res, err := ctxhttp.Do(ctx, httpClient, req) res, err := ctxhttp.Do(ctx, httpClient, req)
if err != nil { if err != nil {
result.Error = err return nil, err
return result
} }
queryResult, err := e.parseResponse(tsdbQuery, res) queryResult, err := e.parseResponse(tsdbQuery, res)
if err != nil { if err != nil {
return result.WithError(err) return nil, err
} }
result.QueryResults = queryResult result.Results = queryResult
return result return result, nil
} }
func (e *OpenTsdbExecutor) createRequest(dsInfo *models.DataSource, data OpenTsdbQuery) (*http.Request, error) { func (e *OpenTsdbExecutor) createRequest(dsInfo *models.DataSource, data OpenTsdbQuery) (*http.Request, error) {
......
...@@ -80,17 +80,17 @@ func (e *PrometheusExecutor) getClient(dsInfo *models.DataSource) (apiv1.API, er ...@@ -80,17 +80,17 @@ func (e *PrometheusExecutor) getClient(dsInfo *models.DataSource) (apiv1.API, er
return apiv1.NewAPI(client), nil return apiv1.NewAPI(client), nil
} }
func (e *PrometheusExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult { func (e *PrometheusExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
result := &tsdb.BatchResult{} result := &tsdb.Response{}
client, err := e.getClient(dsInfo) client, err := e.getClient(dsInfo)
if err != nil { if err != nil {
return result.WithError(err) return nil, err
} }
query, err := parseQuery(tsdbQuery.Queries, tsdbQuery) query, err := parseQuery(tsdbQuery.Queries, tsdbQuery)
if err != nil { if err != nil {
return result.WithError(err) return nil, err
} }
timeRange := apiv1.Range{ timeRange := apiv1.Range{
...@@ -108,15 +108,15 @@ func (e *PrometheusExecutor) Query(ctx context.Context, dsInfo *models.DataSourc ...@@ -108,15 +108,15 @@ func (e *PrometheusExecutor) Query(ctx context.Context, dsInfo *models.DataSourc
value, err := client.QueryRange(ctx, query.Expr, timeRange) value, err := client.QueryRange(ctx, query.Expr, timeRange)
if err != nil { if err != nil {
return result.WithError(err) return nil, err
} }
queryResult, err := parseResponse(value, query) queryResult, err := parseResponse(value, query)
if err != nil { if err != nil {
return result.WithError(err) return nil, err
} }
result.QueryResults = queryResult result.Results = queryResult
return result return result, nil
} }
func formatLegend(metric model.Metric, query *PrometheusQuery) string { func formatLegend(metric model.Metric, query *PrometheusQuery) string {
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
) )
type TsdbQueryEndpoint interface { type TsdbQueryEndpoint interface {
Query(ctx context.Context, ds *models.DataSource, query *TsdbQuery) *BatchResult Query(ctx context.Context, ds *models.DataSource, query *TsdbQuery) (*Response, error)
} }
var registry map[string]GetTsdbQueryEndpointFn var registry map[string]GetTsdbQueryEndpointFn
......
...@@ -2,25 +2,17 @@ package tsdb ...@@ -2,25 +2,17 @@ package tsdb
import ( import (
"context" "context"
"github.com/grafana/grafana/pkg/models"
) )
type HandleRequestFunc func(ctx context.Context, req *TsdbQuery) (*Response, error) type HandleRequestFunc func(ctx context.Context, dsInfo *models.DataSource, req *TsdbQuery) (*Response, error)
func HandleRequest(ctx context.Context, req *TsdbQuery) (*Response, error) { func HandleRequest(ctx context.Context, dsInfo *models.DataSource, req *TsdbQuery) (*Response, error) {
//TODO niceify endpoint, err := getTsdbQueryEndpointFor(dsInfo)
ds := req.Queries[0].DataSource
endpoint, err := getTsdbQueryEndpointFor(ds)
if err != nil { if err != nil {
return nil, err return nil, err
} }
res := endpoint.Query(ctx, ds, req) return endpoint.Query(ctx, dsInfo, req)
if res.Error != nil {
return nil, res.Error
}
return &Response{
Results: res.QueryResults,
BatchTimings: []*BatchTiming{res.Timings},
}, nil
} }
...@@ -24,19 +24,19 @@ func init() { ...@@ -24,19 +24,19 @@ func init() {
tsdb.RegisterTsdbQueryEndpoint("grafana-testdata-datasource", NewTestDataExecutor) tsdb.RegisterTsdbQueryEndpoint("grafana-testdata-datasource", NewTestDataExecutor)
} }
func (e *TestDataExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) *tsdb.BatchResult { func (e *TestDataExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
result := &tsdb.BatchResult{} result := &tsdb.Response{}
result.QueryResults = make(map[string]*tsdb.QueryResult) result.Results = make(map[string]*tsdb.QueryResult)
for _, query := range tsdbQuery.Queries { for _, query := range tsdbQuery.Queries {
scenarioId := query.Model.Get("scenarioId").MustString("random_walk") scenarioId := query.Model.Get("scenarioId").MustString("random_walk")
if scenario, exist := ScenarioRegistry[scenarioId]; exist { if scenario, exist := ScenarioRegistry[scenarioId]; exist {
result.QueryResults[query.RefId] = scenario.Handler(query, tsdbQuery) result.Results[query.RefId] = scenario.Handler(query, tsdbQuery)
result.QueryResults[query.RefId].RefId = query.RefId result.Results[query.RefId].RefId = query.RefId
} else { } else {
e.log.Error("Scenario not found", "scenarioId", scenarioId) e.log.Error("Scenario not found", "scenarioId", scenarioId)
} }
} }
return result return result, nil
} }
...@@ -19,7 +19,7 @@ func TestMetricQuery(t *testing.T) { ...@@ -19,7 +19,7 @@ func TestMetricQuery(t *testing.T) {
fakeExecutor := registerFakeExecutor() fakeExecutor := registerFakeExecutor()
fakeExecutor.Return("A", TimeSeriesSlice{&TimeSeries{Name: "argh"}}) fakeExecutor.Return("A", TimeSeriesSlice{&TimeSeries{Name: "argh"}})
res, err := HandleRequest(context.TODO(), req) res, err := HandleRequest(context.TODO(), &models.DataSource{Id: 1, Type: "test"}, req)
So(err, ShouldBeNil) So(err, ShouldBeNil)
Convey("Should return query results", func() { Convey("Should return query results", func() {
...@@ -40,18 +40,13 @@ func TestMetricQuery(t *testing.T) { ...@@ -40,18 +40,13 @@ func TestMetricQuery(t *testing.T) {
fakeExecutor.Return("A", TimeSeriesSlice{&TimeSeries{Name: "argh"}}) fakeExecutor.Return("A", TimeSeriesSlice{&TimeSeries{Name: "argh"}})
fakeExecutor.Return("B", TimeSeriesSlice{&TimeSeries{Name: "barg"}}) fakeExecutor.Return("B", TimeSeriesSlice{&TimeSeries{Name: "barg"}})
res, err := HandleRequest(context.TODO(), req) res, err := HandleRequest(context.TODO(), &models.DataSource{Id: 1, Type: "test"}, req)
So(err, ShouldBeNil) So(err, ShouldBeNil)
Convey("Should return query results", func() { Convey("Should return query results", func() {
So(len(res.Results), ShouldEqual, 2) So(len(res.Results), ShouldEqual, 2)
So(res.Results["B"].Series[0].Name, ShouldEqual, "barg") So(res.Results["B"].Series[0].Name, ShouldEqual, "barg")
}) })
Convey("Should have been batched in one request", func() {
So(len(res.BatchTimings), ShouldEqual, 1)
})
}) })
Convey("When query uses data source of unknown type", t, func() { Convey("When query uses data source of unknown type", t, func() {
...@@ -61,7 +56,7 @@ func TestMetricQuery(t *testing.T) { ...@@ -61,7 +56,7 @@ func TestMetricQuery(t *testing.T) {
}, },
} }
_, err := HandleRequest(context.TODO(), req) _, err := HandleRequest(context.TODO(), &models.DataSource{Id: 12, Type: "testjughjgjg"}, req)
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
}) })
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment