Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(data): Implement GET /event/device/name/{name} V2 API #2847

Merged
merged 1 commit into from
Oct 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions internal/core/data/v2/application/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,20 @@ func AllEvents(offset int, limit int, dic *di.Container) (events []dtos.Event, e
}
return events, nil
}

// EventsByDeviceName query events with offset, limit and name
func EventsByDeviceName(offset int, limit int, name string, dic *di.Container) (events []dtos.Event, err errors.EdgeX) {
if name == "" {
return events, errors.NewCommonEdgeX(errors.KindContractInvalid, "name is empty", nil)
}
dbClient := v2DataContainer.DBClientFrom(dic.Get)
eventModels, err := dbClient.EventsByDeviceName(offset, limit, name)
if err != nil {
return events, errors.NewCommonEdgeXWrapper(err)
}
events = make([]dtos.Event, len(eventModels))
for i, e := range eventModels {
events[i] = dtos.FromEventModelToDTO(e)
}
return events, nil
}
46 changes: 42 additions & 4 deletions internal/core/data/v2/controller/http/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

"github.com/edgexfoundry/go-mod-core-contracts/clients"
"github.com/edgexfoundry/go-mod-core-contracts/errors"
contractsV2 "github.com/edgexfoundry/go-mod-core-contracts/v2"
"github.com/edgexfoundry/go-mod-core-contracts/v2"
commonDTO "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common"
requestDTO "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/requests"
responseDTO "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/responses"
Expand Down Expand Up @@ -103,7 +103,7 @@ func (ec *EventController) EventById(w http.ResponseWriter, r *http.Request) {

// URL parameters
vars := mux.Vars(r)
id := vars[contractsV2.Id]
id := vars[v2.Id]

var eventResponse interface{}
var statusCode int
Expand Down Expand Up @@ -137,7 +137,7 @@ func (ec *EventController) DeleteEventById(w http.ResponseWriter, r *http.Reques

// URL parameters
vars := mux.Vars(r)
id := vars[contractsV2.Id]
id := vars[v2.Id]

var response interface{}
var statusCode int
Expand Down Expand Up @@ -194,7 +194,7 @@ func (ec *EventController) EventCountByDevice(w http.ResponseWriter, r *http.Req

// URL parameters
vars := mux.Vars(r)
deviceName := vars[contractsV2.DeviceName]
deviceName := vars[v2.DeviceName]

var eventResponse interface{}
var statusCode int
Expand Down Expand Up @@ -289,3 +289,41 @@ func (ec *EventController) AllEvents(w http.ResponseWriter, r *http.Request) {
utils.WriteHttpHeader(w, ctx, statusCode)
pkg.Encode(response, w, lc)
}

func (ec *EventController) EventsByDeviceName(w http.ResponseWriter, r *http.Request) {
lc := container.LoggingClientFrom(ec.dic.Get)
ctx := r.Context()
correlationId := correlation.FromContext(ctx)
config := dataContainer.ConfigurationFrom(ec.dic.Get)

vars := mux.Vars(r)
name := vars[v2.Name]

var response interface{}
var statusCode int

// parse URL query string for offset, limit
offset, limit, _, err := utils.ParseGetAllObjectsRequestQueryString(r, 0, math.MaxUint32, -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 {
events, err := application.EventsByDeviceName(offset, limit, name, ec.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.NewMultiEventsResponse("", "", http.StatusOK, events)
statusCode = http.StatusOK
}
}

utils.WriteHttpHeader(w, ctx, statusCode)
pkg.Encode(response, w, lc)
}
77 changes: 77 additions & 0 deletions internal/core/data/v2/controller/http/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -549,3 +549,80 @@ func TestAllEvents(t *testing.T) {
})
}
}

func TestAllEventsByDeviceName(t *testing.T) {
testDeviceA := "testDeviceA"
testDeviceB := "testDeviceB"
event1WithDeviceA := persistedEvent
event1WithDeviceA.DeviceName = testDeviceA
event2WithDeviceA := persistedEvent
event2WithDeviceA.DeviceName = testDeviceA
event3WithDeviceB := persistedEvent
event3WithDeviceB.DeviceName = testDeviceB

events := []models.Event{event1WithDeviceA, event2WithDeviceA, event3WithDeviceB}

dic := mocks.NewMockDIC()
dbClientMock := &dbMock.DBClient{}
dbClientMock.On("EventsByDeviceName", 0, 5, testDeviceA).Return([]models.Event{events[0], events[1]}, nil)
dbClientMock.On("EventsByDeviceName", 1, 1, testDeviceA).Return([]models.Event{events[1]}, nil)
dbClientMock.On("EventsByDeviceName", 4, 1, testDeviceB).Return([]models.Event{}, errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, "query objects bounds out of range.", nil))
dic.Update(di.ServiceConstructorMap{
v2DataContainer.DBClientInterfaceName: func(get di.Get) interface{} {
return dbClientMock
},
})
ec := NewEventController(dic)
assert.NotNil(t, ec)

tests := []struct {
name string
offset string
limit string
deviceName string
errorExpected bool
expectedCount int
expectedStatusCode int
}{
{"Valid - get events with deviceName", "0", "5", testDeviceA, false, 2, http.StatusOK},
{"Valid - get events with offset and no labels", "1", "1", testDeviceA, false, 1, http.StatusOK},
{"Invalid - offset out of range", "4", "1", testDeviceB, true, 0, http.StatusNotFound},
{"Invalid - get events without deviceName", "0", "10", "", true, 0, http.StatusBadRequest},
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, v2.ApiEventByDeviceNameRoute, http.NoBody)
query := req.URL.Query()
query.Add(v2.Offset, testCase.offset)
query.Add(v2.Limit, testCase.limit)
req.URL.RawQuery = query.Encode()
req = mux.SetURLVars(req, map[string]string{v2.Name: testCase.deviceName})
require.NoError(t, err)

// Act
recorder := httptest.NewRecorder()
handler := http.HandlerFunc(ec.EventsByDeviceName)
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.MultiEventsResponse
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.Equal(t, testCase.expectedCount, len(res.Events), "Device count not as expected")
assert.Empty(t, res.Message, "Message should be empty when it is successful")
}
})
}
}
1 change: 1 addition & 0 deletions internal/core/data/v2/infrastructure/interfaces/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ type DBClient interface {
EventCountByDevice(deviceName string) (uint32, errors.EdgeX)
UpdateEventPushedById(id string) errors.EdgeX
AllEvents(offset int, limit int) ([]model.Event, errors.EdgeX)
EventsByDeviceName(offset int, limit int, name string) ([]model.Event, errors.EdgeX)
}
25 changes: 25 additions & 0 deletions internal/core/data/v2/infrastructure/interfaces/mocks/DBClient.go

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

1 change: 1 addition & 0 deletions internal/core/data/v2/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func LoadRestRoutes(r *mux.Router, dic *di.Container) {
r.HandleFunc(v2Constant.ApiEventCountByDeviceRoute, ec.EventCountByDevice).Methods(http.MethodGet)
r.HandleFunc(v2Constant.ApiEventPushRoute, ec.UpdateEventPushedById).Methods(http.MethodPut)
r.HandleFunc(v2Constant.ApiAllEventRoute, ec.AllEvents).Methods(http.MethodGet)
r.HandleFunc(v2Constant.ApiEventByDeviceNameRoute, ec.EventsByDeviceName).Methods(http.MethodGet)

r.Use(correlation.ManageHeader)
r.Use(correlation.OnResponseComplete)
Expand Down
6 changes: 3 additions & 3 deletions internal/core/metadata/v2/application/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ func DeleteDeviceByName(name string, dic *di.Container) errors.EdgeX {
return nil
}

// AllDeviceByServiceName query devices with offset, limit and name
func AllDeviceByServiceName(offset int, limit int, name string, ctx context.Context, dic *di.Container) (devices []dtos.Device, err errors.EdgeX) {
// DevicesByServiceName query devices with offset, limit and name
func DevicesByServiceName(offset int, limit int, name string, ctx context.Context, dic *di.Container) (devices []dtos.Device, err errors.EdgeX) {
if name == "" {
return devices, errors.NewCommonEdgeX(errors.KindContractInvalid, "name is empty", nil)
}
dbClient := v2MetadataContainer.DBClientFrom(dic.Get)
deviceModels, err := dbClient.AllDeviceByServiceName(offset, limit, name)
deviceModels, err := dbClient.DevicesByServiceName(offset, limit, name)
if err != nil {
return devices, errors.NewCommonEdgeXWrapper(err)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/core/metadata/v2/controller/http/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func (dc *DeviceController) DeleteDeviceByName(w http.ResponseWriter, r *http.Re
pkg.Encode(response, w, lc)
}

func (dc *DeviceController) AllDeviceByServiceName(w http.ResponseWriter, r *http.Request) {
func (dc *DeviceController) DevicesByServiceName(w http.ResponseWriter, r *http.Request) {
lc := container.LoggingClientFrom(dc.dic.Get)
ctx := r.Context()
correlationId := correlation.FromContext(ctx)
Expand All @@ -174,7 +174,7 @@ func (dc *DeviceController) AllDeviceByServiceName(w http.ResponseWriter, r *htt
response = commonDTO.NewBaseResponse("", err.Message(), err.Code())
statusCode = err.Code()
} else {
devices, err := application.AllDeviceByServiceName(offset, limit, name, ctx, dc.dic)
devices, err := application.DevicesByServiceName(offset, limit, name, ctx, dc.dic)
if err != nil {
if errors.Kind(err) != errors.KindEntityDoesNotExist {
lc.Error(err.Error(), clients.CorrelationHeader, correlationId)
Expand Down
8 changes: 4 additions & 4 deletions internal/core/metadata/v2/controller/http/device_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,9 +342,9 @@ func TestAllDeviceByServiceName(t *testing.T) {

dic := mockDic()
dbClientMock := &dbMock.DBClient{}
dbClientMock.On("AllDeviceByServiceName", 0, 5, testServiceA).Return([]models.Device{devices[0], devices[1]}, nil)
dbClientMock.On("AllDeviceByServiceName", 1, 1, testServiceA).Return([]models.Device{devices[1]}, nil)
dbClientMock.On("AllDeviceByServiceName", 4, 1, testServiceB).Return([]models.Device{}, errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, "query objects bounds out of range.", nil))
dbClientMock.On("DevicesByServiceName", 0, 5, testServiceA).Return([]models.Device{devices[0], devices[1]}, nil)
dbClientMock.On("DevicesByServiceName", 1, 1, testServiceA).Return([]models.Device{devices[1]}, nil)
dbClientMock.On("DevicesByServiceName", 4, 1, testServiceB).Return([]models.Device{}, errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, "query objects bounds out of range.", nil))
dic.Update(di.ServiceConstructorMap{
v2MetadataContainer.DBClientInterfaceName: func(get di.Get) interface{} {
return dbClientMock
Expand Down Expand Up @@ -379,7 +379,7 @@ func TestAllDeviceByServiceName(t *testing.T) {

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

// Assert
Expand Down
2 changes: 1 addition & 1 deletion internal/core/metadata/v2/infrastructure/interfaces/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type DBClient interface {
AddDevice(d model.Device) (model.Device, errors.EdgeX)
DeleteDeviceById(id string) errors.EdgeX
DeleteDeviceByName(name string) errors.EdgeX
AllDeviceByServiceName(offset int, limit int, name string) ([]model.Device, errors.EdgeX)
DevicesByServiceName(offset int, limit int, name string) ([]model.Device, errors.EdgeX)
DeviceIdExists(id string) (bool, errors.EdgeX)
DeviceNameExists(id string) (bool, errors.EdgeX)
DeviceById(id string) (model.Device, errors.EdgeX)
Expand Down

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

2 changes: 1 addition & 1 deletion internal/core/metadata/v2/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func LoadRestRoutes(r *mux.Router, dic *di.Container) {
r.HandleFunc(v2Constant.ApiDeviceRoute, d.AddDevice).Methods(http.MethodPost)
r.HandleFunc(v2Constant.ApiDeviceByIdRoute, d.DeleteDeviceById).Methods(http.MethodDelete)
r.HandleFunc(v2Constant.ApiDeviceByNameRoute, d.DeleteDeviceByName).Methods(http.MethodDelete)
r.HandleFunc(v2Constant.ApiDeviceByServiceNameRoute, d.AllDeviceByServiceName).Methods(http.MethodGet)
r.HandleFunc(v2Constant.ApiDeviceByServiceNameRoute, d.DevicesByServiceName).Methods(http.MethodGet)
r.HandleFunc(v2Constant.ApiDeviceIdExistsRoute, d.DeviceIdExists).Methods(http.MethodGet)
r.HandleFunc(v2Constant.ApiDeviceNameExistsRoute, d.DeviceNameExists).Methods(http.MethodGet)
r.HandleFunc(v2Constant.ApiDeviceRoute, d.PatchDevice).Methods(http.MethodPatch)
Expand Down
19 changes: 16 additions & 3 deletions internal/pkg/v2/infrastructure/redis/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,12 +328,12 @@ func (c *Client) DeleteDeviceByName(name string) errors.EdgeX {
return nil
}

// AllDeviceByServiceName query devices by offset, limit and name
func (c *Client) AllDeviceByServiceName(offset int, limit int, name string) (devices []model.Device, edgeXerr errors.EdgeX) {
// DevicesByServiceName query devices by offset, limit and name
func (c *Client) DevicesByServiceName(offset int, limit int, name string) (devices []model.Device, edgeXerr errors.EdgeX) {
conn := c.Pool.Get()
defer conn.Close()

devices, edgeXerr = allDeviceByServiceName(conn, offset, limit, name)
devices, edgeXerr = devicesByServiceName(conn, offset, limit, name)
if edgeXerr != nil {
return devices, errors.NewCommonEdgeX(errors.Kind(edgeXerr),
fmt.Sprintf("fail to query devices by offset %d, limit %d and name %s", offset, limit, name), edgeXerr)
Expand Down Expand Up @@ -413,3 +413,16 @@ func (c *Client) AllDevices(offset int, limit int, labels []string) ([]model.Dev
}
return devices, nil
}

// EventsByDeviceName query events by offset, limit and device name
func (c *Client) EventsByDeviceName(offset int, limit int, name string) (events []model.Event, edgeXerr errors.EdgeX) {
conn := c.Pool.Get()
defer conn.Close()

events, edgeXerr = eventsByDeviceName(conn, offset, limit, name)
if edgeXerr != nil {
return events, errors.NewCommonEdgeX(errors.Kind(edgeXerr),
fmt.Sprintf("fail to query events by offset %d, limit %d and name %s", offset, limit, name), edgeXerr)
}
return events, nil
}
Loading