Skip to content

Commit

Permalink
feat(scheduler): Implement GET /intervalaction/all V2 API (#3290)
Browse files Browse the repository at this point in the history
* feat(scheduler): Implement GET /intervalaction/all V2 API

Implement the API according to the doc https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/support-scheduler/2.x#/default/get_intervalaction_all

Close #3289

Signed-off-by: weichou <weichou1229@gmail.com>
  • Loading branch information
weichou1229 authored Mar 23, 2021
1 parent 65ce927 commit c524ea4
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 66 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/edgexfoundry/go-mod-bootstrap/v2 v2.0.0-dev.20
github.com/edgexfoundry/go-mod-configuration/v2 v2.0.0-dev.4
github.com/edgexfoundry/go-mod-core-contracts/v2 v2.0.0-dev.54
github.com/edgexfoundry/go-mod-core-contracts/v2 v2.0.0-dev.55
github.com/edgexfoundry/go-mod-messaging/v2 v2.0.0-dev.6
github.com/edgexfoundry/go-mod-registry/v2 v2.0.0-dev.3
github.com/edgexfoundry/go-mod-secrets/v2 v2.0.0-dev.10
Expand Down
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 @@ -753,6 +753,18 @@ func (c *Client) AddIntervalAction(action model.IntervalAction) (model.IntervalA
return addIntervalAction(conn, action)
}

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

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

// AddSubscription adds a new subscription
func (c *Client) AddSubscription(subscription model.Subscription) (model.Subscription, errors.EdgeX) {
conn := c.Pool.Get()
Expand Down
28 changes: 25 additions & 3 deletions internal/pkg/v2/infrastructure/redis/intervalaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ import (
)

const (
IntervalActionCollection = "ss|ia"
IntervalActionCollectionName = IntervalActionCollection + DBKeySeparator + v2.Name
IntervalActionCollectionTarget = IntervalActionCollection + DBKeySeparator + v2.Target
IntervalActionCollection = "ss|ia"
IntervalActionCollectionName = IntervalActionCollection + DBKeySeparator + v2.Name
)

// intervalActionStoredKey return the intervalAction's stored key which combines the collection name and object id
Expand Down Expand Up @@ -68,3 +67,26 @@ func addIntervalAction(conn redis.Conn, action models.IntervalAction) (models.In

return action, edgeXerr
}

// allIntervalActions queries intervalActions by offset and limit
func allIntervalActions(conn redis.Conn, offset, limit int) (intervalActions []models.IntervalAction, 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, IntervalActionCollection, offset, end)
if edgeXerr != nil {
return intervalActions, errors.NewCommonEdgeXWrapper(edgeXerr)
}

intervalActions = make([]models.IntervalAction, len(objects))
for i, o := range objects {
action := models.IntervalAction{}
err := json.Unmarshal(o, &action)
if err != nil {
return []models.IntervalAction{}, errors.NewCommonEdgeX(errors.KindDatabaseError, "intervalAction format parsing failed from the database", err)
}
intervalActions[i] = action
}
return intervalActions, nil
}
16 changes: 16 additions & 0 deletions internal/support/scheduler/v2/application/intervalaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/container"
"github.com/edgexfoundry/go-mod-bootstrap/v2/di"
"github.com/edgexfoundry/go-mod-core-contracts/v2/errors"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/models"
)

Expand All @@ -40,3 +41,18 @@ func AddIntervalAction(action models.IntervalAction, ctx context.Context, dic *d

return addedAction.Id, nil
}

// AllIntervalActions query the intervalActions with offset and limit
func AllIntervalActions(offset int, limit int, dic *di.Container) (intervalActionDTOs []dtos.IntervalAction, err errors.EdgeX) {
dbClient := v2SchedulerContainer.DBClientFrom(dic.Get)
intervalActions, err := dbClient.AllIntervalActions(offset, limit)
if err != nil {
return intervalActionDTOs, errors.NewCommonEdgeXWrapper(err)
}
intervalActionDTOs = make([]dtos.IntervalAction, len(intervalActions))
for i, action := range intervalActions {
dto := dtos.FromIntervalActionModelToDTO(action)
intervalActionDTOs[i] = dto
}
return intervalActionDTOs, nil
}
47 changes: 43 additions & 4 deletions internal/support/scheduler/v2/controller/http/intervalaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,23 @@
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"

"github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/container"
"github.com/edgexfoundry/go-mod-bootstrap/v2/di"
"github.com/edgexfoundry/go-mod-core-contracts/v2/clients"
"github.com/edgexfoundry/go-mod-core-contracts/v2/errors"
commonDTO "github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos/common"
requestDTO "github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos/requests"
responseDTO "github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos/responses"
)

type IntervalActionController struct {
Expand All @@ -34,17 +38,17 @@ func NewIntervalActionController(dic *di.Container) *IntervalActionController {
}
}

func (dc *IntervalActionController) AddIntervalAction(w http.ResponseWriter, r *http.Request) {
func (ic *IntervalActionController) AddIntervalAction(w http.ResponseWriter, r *http.Request) {
if r.Body != nil {
defer func() { _ = r.Body.Close() }()
}

lc := container.LoggingClientFrom(dc.dic.Get)
lc := container.LoggingClientFrom(ic.dic.Get)

ctx := r.Context()
correlationId := correlation.FromContext(ctx)

actionDTOs, err := dc.reader.ReadAddIntervalActionRequest(r.Body)
actionDTOs, err := ic.reader.ReadAddIntervalActionRequest(r.Body)
if err != nil {
lc.Error(err.Error(), clients.CorrelationHeader, correlationId)
lc.Debug(err.DebugMessages(), clients.CorrelationHeader, correlationId)
Expand All @@ -62,7 +66,7 @@ func (dc *IntervalActionController) AddIntervalAction(w http.ResponseWriter, r *
for i, action := range actions {
var response interface{}
reqId := actionDTOs[i].RequestId
newId, err := application.AddIntervalAction(action, ctx, dc.dic)
newId, err := application.AddIntervalAction(action, ctx, ic.dic)
if err != nil {
lc.Error(err.Error(), clients.CorrelationHeader, correlationId)
lc.Debug(err.DebugMessages(), clients.CorrelationHeader, correlationId)
Expand All @@ -86,3 +90,38 @@ func (dc *IntervalActionController) AddIntervalAction(w http.ResponseWriter, r *
utils.WriteHttpHeader(w, ctx, http.StatusMultiStatus)
pkg.Encode(addResponses, w, lc)
}

func (ic *IntervalActionController) AllIntervalActions(w http.ResponseWriter, r *http.Request) {
lc := container.LoggingClientFrom(ic.dic.Get)
ctx := r.Context()
correlationId := correlation.FromContext(ctx)
config := schedulerContainer.ConfigurationFrom(ic.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 {
intervalActions, err := application.AllIntervalActions(offset, limit, ic.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.NewMultiIntervalActionsResponse("", "", http.StatusOK, intervalActions)
statusCode = http.StatusOK
}
}

utils.WriteHttpHeader(w, ctx, statusCode)
pkg.Encode(response, w, lc)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos/common"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos/requests"
responseDTO "github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos/responses"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/models"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -106,3 +107,68 @@ func TestAddIntervalAction(t *testing.T) {
})
}
}

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

tests := []struct {
name string
offset string
limit string
errorExpected bool
expectedStatusCode int
}{
{"Valid - get intervalActions without offset and limit", "", "", false, http.StatusOK},
{"Valid - get intervalActions 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.ApiAllIntervalActionRoute, 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.AllIntervalActions)
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 @@ -21,4 +21,5 @@ type DBClient interface {
UpdateInterval(interval model.Interval) errors.EdgeX

AddIntervalAction(e model.IntervalAction) (model.IntervalAction, errors.EdgeX)
AllIntervalActions(offset int, limit int) ([]model.IntervalAction, 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 @@ -37,6 +37,7 @@ func LoadRestRoutes(r *mux.Router, dic *di.Container) {
// IntervalAction
action := schedulerController.NewIntervalActionController(dic)
r.HandleFunc(v2Constant.ApiIntervalActionRoute, action.AddIntervalAction).Methods(http.MethodPost)
r.HandleFunc(v2Constant.ApiAllIntervalActionRoute, action.AllIntervalActions).Methods(http.MethodGet)

r.Use(correlation.ManageHeader)
r.Use(correlation.OnResponseComplete)
Expand Down
58 changes: 0 additions & 58 deletions openapi/v2/support-scheduler.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -877,64 +877,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/intervalaction/target/{target}:
parameters:
- $ref: '#/components/parameters/correlatedRequestHeader'
- name: target
in: path
required: true
schema:
type: string
description: "The target of an interval action"
get:
summary: "Returns a paginated list of all interval actions associated with the specified target."
parameters:
- $ref: '#/components/parameters/offsetParam'
- $ref: '#/components/parameters/limitParam'
responses:
'200':
description: "OK"
headers:
X-Correlation-ID:
$ref: '#/components/headers/correlatedResponseHeader'
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/IntervalActionResponse'
'500':
description: "An unexpected error occurred on the server"
headers:
X-Correlation-ID:
$ref: '#/components/headers/correlatedResponseHeader'
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ErrorResponse'
delete:
summary: "Deletes all interval actions associated with the specified target."
responses:
'200':
description: "OK"
headers:
X-Correlation-ID:
$ref: '#/components/headers/correlatedResponseHeader'
content:
application/json:
schema:
$ref: '#/components/schemas/IntervalActionResponse'
'500':
description: "An unexpected error occurred on the server"
headers:
X-Correlation-ID:
$ref: '#/components/headers/correlatedResponseHeader'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/config:
get:
summary: "Returns the current configuration of the service."
Expand Down

0 comments on commit c524ea4

Please sign in to comment.