Skip to content

Commit

Permalink
feat(scheduler): Implement GET /interval/all V2 API (#3185)
Browse files Browse the repository at this point in the history
Implement the V2 API according to the swagger doc https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/support-scheduler/2.x#/default/get_interval_all

Close #3184

Signed-off-by: weichou <weichou1229@gmail.com>
  • Loading branch information
weichou1229 authored Feb 22, 2021
1 parent 61638ca commit a8b212a
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 2 deletions.
12 changes: 12 additions & 0 deletions internal/pkg/v2/infrastructure/redis/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,18 @@ func (c *Client) IntervalByName(name string) (interval model.Interval, edgeXerr
return
}

// AllIntervals query intervals with offset and limit
func (c *Client) AllIntervals(offset int, limit int) (intervals []model.Interval, edgeXerr errors.EdgeX) {
conn := c.Pool.Get()
defer conn.Close()

intervals, edgeXerr = allIntervals(conn, offset, limit)
if edgeXerr != nil {
return intervals, errors.NewCommonEdgeXWrapper(edgeXerr)
}
return intervals, nil
}

// AddSubscription adds a new subscription
func (c *Client) AddSubscription(subscription model.Subscription) (model.Subscription, errors.EdgeX) {
conn := c.Pool.Get()
Expand Down
2 changes: 2 additions & 0 deletions internal/pkg/v2/infrastructure/redis/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package redis

import dataInterfaces "github.com/edgexfoundry/edgex-go/internal/core/data/v2/infrastructure/interfaces"
import metadataInterfaces "github.com/edgexfoundry/edgex-go/internal/core/metadata/v2/infrastructure/interfaces"
import schedulerInterfaces "github.com/edgexfoundry/edgex-go/internal/support/scheduler/v2/infrastructure/interfaces"

// Check the implementation of Redis satisfies the DB client
var _ dataInterfaces.DBClient = &Client{}
var _ metadataInterfaces.DBClient = &Client{}
var _ schedulerInterfaces.DBClient = &Client{}
25 changes: 24 additions & 1 deletion internal/pkg/v2/infrastructure/redis/interval.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func addInterval(conn redis.Conn, interval models.Interval) (models.Interval, er
storedKey := intervalStoredKey(interval.Id)
_ = conn.Send(MULTI)
_ = conn.Send(SET, storedKey, dsJSONBytes)
_ = conn.Send(ZADD, IntervalCollection, 0, storedKey)
_ = conn.Send(ZADD, IntervalCollection, interval.Modified, storedKey)
_ = conn.Send(HSET, IntervalCollectionName, interval.Name, storedKey)
_, err = conn.Do(EXEC)
if err != nil {
Expand All @@ -76,3 +76,26 @@ func intervalByName(conn redis.Conn, name string) (interval models.Interval, edg
}
return
}

// allIntervals queries intervals by offset and limit
func allIntervals(conn redis.Conn, offset, limit int) (intervals []models.Interval, edgeXerr errors.EdgeX) {
end := offset + limit - 1
if limit == -1 { //-1 limit means that clients want to retrieve all remaining records after offset from DB, so specifying -1 for end
end = limit
}
objects, edgeXerr := getObjectsByRevRange(conn, IntervalCollection, offset, end)
if edgeXerr != nil {
return intervals, errors.NewCommonEdgeXWrapper(edgeXerr)
}

intervals = make([]models.Interval, len(objects))
for i, o := range objects {
s := models.Interval{}
err := json.Unmarshal(o, &s)
if err != nil {
return []models.Interval{}, errors.NewCommonEdgeX(errors.KindDatabaseError, "interval format parsing failed from the database", err)
}
intervals[i] = s
}
return intervals, nil
}
15 changes: 15 additions & 0 deletions internal/support/scheduler/v2/application/interval.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,18 @@ func IntervalByName(name string, ctx context.Context, dic *di.Container) (dto dt
dto = dtos.FromIntervalModelToDTO(interval)
return dto, nil
}

// AllIntervals query the intervals with offset and limit
func AllIntervals(offset int, limit int, dic *di.Container) (intervalDTOs []dtos.Interval, err errors.EdgeX) {
dbClient := v2SchedulerContainer.DBClientFrom(dic.Get)
intervals, err := dbClient.AllIntervals(offset, limit)
if err != nil {
return intervalDTOs, errors.NewCommonEdgeXWrapper(err)
}
intervalDTOs = make([]dtos.Interval, len(intervals))
for i, interval := range intervals {
dto := dtos.FromIntervalModelToDTO(interval)
intervalDTOs[i] = dto
}
return intervalDTOs, nil
}
37 changes: 37 additions & 0 deletions internal/support/scheduler/v2/controller/http/interval.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
package http

import (
"math"
"net/http"

"github.com/edgexfoundry/edgex-go/internal/pkg"
"github.com/edgexfoundry/edgex-go/internal/pkg/correlation"
"github.com/edgexfoundry/edgex-go/internal/pkg/v2/utils"
schedulerContainer "github.com/edgexfoundry/edgex-go/internal/support/scheduler/container"
"github.com/edgexfoundry/edgex-go/internal/support/scheduler/v2/application"
"github.com/edgexfoundry/edgex-go/internal/support/scheduler/v2/io"

Expand Down Expand Up @@ -124,3 +126,38 @@ func (dc *IntervalController) IntervalByName(w http.ResponseWriter, r *http.Requ
utils.WriteHttpHeader(w, ctx, statusCode)
pkg.Encode(response, w, lc)
}

func (dc *IntervalController) AllIntervals(w http.ResponseWriter, r *http.Request) {
lc := container.LoggingClientFrom(dc.dic.Get)
ctx := r.Context()
correlationId := correlation.FromContext(ctx)
config := schedulerContainer.ConfigurationFrom(dc.dic.Get)

var response interface{}
var statusCode int

// parse URL query string for offset, limit, and labels
offset, limit, _, err := utils.ParseGetAllObjectsRequestQueryString(r, 0, math.MaxInt32, -1, config.Service.MaxResultCount)
if err != nil {
lc.Error(err.Error(), clients.CorrelationHeader, correlationId)
lc.Debug(err.DebugMessages(), clients.CorrelationHeader, correlationId)
response = commonDTO.NewBaseResponse("", err.Message(), err.Code())
statusCode = err.Code()
} else {
intervals, err := application.AllIntervals(offset, limit, dc.dic)
if err != nil {
if errors.Kind(err) != errors.KindEntityDoesNotExist {
lc.Error(err.Error(), clients.CorrelationHeader, correlationId)
}
lc.Debug(err.DebugMessages(), clients.CorrelationHeader, correlationId)
response = commonDTO.NewBaseResponse("", err.Message(), err.Code())
statusCode = err.Code()
} else {
response = responseDTO.NewMultiIntervalsResponse("", "", http.StatusOK, intervals)
statusCode = http.StatusOK
}
}

utils.WriteHttpHeader(w, ctx, statusCode)
pkg.Encode(response, w, lc)
}
67 changes: 66 additions & 1 deletion internal/support/scheduler/v2/controller/http/interval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func mockDic() *di.Container {
LogLevel: "DEBUG",
},
Service: bootstrapConfig.ServiceInfo{
MaxResultCount: 30,
MaxResultCount: 20,
},
}
},
Expand Down Expand Up @@ -211,3 +211,68 @@ func TestIntervalByName(t *testing.T) {
})
}
}

func TestAllIntervals(t *testing.T) {
dic := mockDic()
dbClientMock := &dbMock.DBClient{}
dbClientMock.On("AllIntervals", 0, 20).Return([]models.Interval{}, nil)
dbClientMock.On("AllIntervals", 0, 1).Return([]models.Interval{}, nil)
dic.Update(di.ServiceConstructorMap{
v2SchedulerContainer.DBClientInterfaceName: func(get di.Get) interface{} {
return dbClientMock
},
})
controller := NewIntervalController(dic)
require.NotNil(t, controller)

tests := []struct {
name string
offset string
limit string
errorExpected bool
expectedStatusCode int
}{
{"Valid - get intervals without offset and limit", "", "", false, http.StatusOK},
{"Valid - get intervals with offset and limit", "0", "1", false, http.StatusOK},
{"Invalid - invalid offset format", "aaa", "1", true, http.StatusBadRequest},
{"Invalid - invalid limit format", "1", "aaa", true, http.StatusBadRequest},
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, v2.ApiAllIntervalRoute, http.NoBody)
query := req.URL.Query()
if testCase.offset != "" {
query.Add(v2.Offset, testCase.offset)
}
if testCase.limit != "" {
query.Add(v2.Limit, testCase.limit)
}
req.URL.RawQuery = query.Encode()
require.NoError(t, err)

// Act
recorder := httptest.NewRecorder()
handler := http.HandlerFunc(controller.AllIntervals)
handler.ServeHTTP(recorder, req)

// Assert
if testCase.errorExpected {
var res common.BaseResponse
err = json.Unmarshal(recorder.Body.Bytes(), &res)
require.NoError(t, err)
assert.Equal(t, v2.ApiVersion, res.ApiVersion, "API Version not as expected")
assert.Equal(t, testCase.expectedStatusCode, recorder.Result().StatusCode, "HTTP status code not as expected")
assert.Equal(t, testCase.expectedStatusCode, int(res.StatusCode), "Response status code not as expected")
assert.NotEmpty(t, res.Message, "Response message doesn't contain the error message")
} else {
var res responseDTO.MultiIntervalsResponse
err = json.Unmarshal(recorder.Body.Bytes(), &res)
require.NoError(t, err)
assert.Equal(t, v2.ApiVersion, res.ApiVersion, "API Version not as expected")
assert.Equal(t, testCase.expectedStatusCode, recorder.Result().StatusCode, "HTTP status code not as expected")
assert.Equal(t, testCase.expectedStatusCode, int(res.StatusCode), "Response status code not as expected")
assert.Empty(t, res.Message, "Message should be empty when it is successful")
}
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ type DBClient interface {

AddInterval(e model.Interval) (model.Interval, errors.EdgeX)
IntervalByName(name string) (model.Interval, errors.EdgeX)
AllIntervals(offset int, limit int) ([]model.Interval, errors.EdgeX)
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions internal/support/scheduler/v2/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ func LoadRestRoutes(r *mux.Router, dic *di.Container) {
interval := schedulerController.NewIntervalController(dic)
r.HandleFunc(v2Constant.ApiIntervalRoute, interval.AddInterval).Methods(http.MethodPost)
r.HandleFunc(v2Constant.ApiIntervalByNameRoute, interval.IntervalByName).Methods(http.MethodGet)
r.HandleFunc(v2Constant.ApiAllIntervalRoute, interval.AllIntervals).Methods(http.MethodGet)
}

0 comments on commit a8b212a

Please sign in to comment.