Skip to content

Commit

Permalink
feat(data): Update to use single AddEventRequest DTO for AddEvent
Browse files Browse the repository at this point in the history
Per discussion over #3002 (comment), AddEvent V2 API shall be updated to deal with single AddEventRequest DTO instead of AddEventRequest DTO array. This commit makes such changes and also updates the swagger API doc.

Signed-off-by: Jude Hung <jude@iotechsys.com>
  • Loading branch information
judehung committed Jan 13, 2021
1 parent aec698c commit f2181a9
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 140 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 v0.0.70
github.com/edgexfoundry/go-mod-configuration v0.0.8
github.com/edgexfoundry/go-mod-core-contracts v0.1.144
github.com/edgexfoundry/go-mod-core-contracts v0.1.145
github.com/edgexfoundry/go-mod-messaging v0.1.30
github.com/edgexfoundry/go-mod-registry v0.1.27
github.com/edgexfoundry/go-mod-secrets v0.0.32
Expand Down
8 changes: 4 additions & 4 deletions internal/core/data/v2/application/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ func AddEvent(e models.Event, profileName string, deviceName string, ctx context
return nil
}

// PublishEvents publish incoming array of AddEventRequest through MessageClient
func PublishEvents(addEvents []dto.AddEventRequest, profileName string, deviceName string, ctx context.Context, dic *di.Container) {
// PublishEvent publish incoming array of AddEventRequest through MessageClient
func PublishEvent(addEventReq dto.AddEventRequest, profileName string, deviceName string, ctx context.Context, dic *di.Container) {
lc := container.LoggingClientFrom(dic.Get)
msgClient := dataContainer.MessagingClientFrom(dic.Get)
configuration := dataContainer.ConfigurationFrom(dic.Get)
Expand All @@ -85,9 +85,9 @@ func PublishEvents(addEvents []dto.AddEventRequest, profileName string, deviceNa
ctx = context.WithValue(ctx, clients.ContentType, clients.ContentTypeJSON)
}

data, err = json.Marshal(addEvents)
data, err = json.Marshal(addEventReq)
if err != nil {
lc.Error(fmt.Sprintf("error marshaling V2 AddEventRequest DTO: %+v", addEvents), clients.CorrelationHeader, correlationId)
lc.Error(fmt.Sprintf("error marshaling V2 AddEventRequest DTO: %+v", addEventReq), clients.CorrelationHeader, correlationId)
return
}

Expand Down
64 changes: 26 additions & 38 deletions internal/core/data/v2/controller/http/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,56 +54,44 @@ func (ec *EventController) AddEvent(w http.ResponseWriter, r *http.Request) {
profileName := vars[v2.ProfileName]
deviceName := vars[v2.DeviceName]

addEventReqDTOs, err := ec.reader.ReadAddEventRequest(r.Body)
addEventReqDTO, err := ec.reader.ReadAddEventRequest(r.Body)
if err != nil {
lc.Error(err.Error(), clients.CorrelationHeader, correlationId)
lc.Debug(err.DebugMessages(), clients.CorrelationHeader, correlationId)
errResponses := commonDTO.NewBaseResponse(
"",
err.Message(),
err.Code())
errResponses := commonDTO.NewBaseResponse("", err.Message(), err.Code())
utils.WriteHttpHeader(w, ctx, err.Code())
// encode and send out the response
pkg.Encode(errResponses, w, lc)
return
}
events := requestDTO.AddEventReqToEventModels(addEventReqDTOs)

// map Event models to AddEventResponse DTOs
var addResponses []interface{}
var successAddEventReqDTOs []requestDTO.AddEventRequest
for i, e := range events {
err := application.ValidateEvent(e, profileName, deviceName, ctx, ec.dic)
if err == nil {
err = application.AddEvent(e, profileName, deviceName, ctx, ec.dic)
}
var addEventResponse interface{}
// get the requestID from AddEventRequestDTO
reqId := addEventReqDTOs[i].RequestId

if err != nil {
lc.Error(err.Error(), clients.CorrelationHeader, correlationId)
lc.Debug(err.DebugMessages(), clients.CorrelationHeader, correlationId)
addEventResponse = commonDTO.NewBaseResponse(
reqId,
err.Message(),
err.Code())
} else {
addEventResponse = commonDTO.NewBaseWithIdResponse(
reqId,
"",
http.StatusCreated,
e.Id)
// add newly-created Event into successAddEventReqDTOs for later publish
successAddEventReqDTOs = append(successAddEventReqDTOs, addEventReqDTOs[i])
}
addResponses = append(addResponses, addEventResponse)
var addEventResponse interface{}
var statusCode int

event := requestDTO.AddEventReqToEventModel(addEventReqDTO)
err = application.ValidateEvent(event, profileName, deviceName, ctx, ec.dic)
if err == nil {
err = application.AddEvent(event, profileName, deviceName, ctx, ec.dic)
}
application.PublishEvents(successAddEventReqDTOs, profileName, deviceName, ctx, ec.dic)

utils.WriteHttpHeader(w, ctx, http.StatusMultiStatus)
if err != nil {
lc.Error(err.Error(), clients.CorrelationHeader, correlationId)
lc.Debug(err.DebugMessages(), clients.CorrelationHeader, correlationId)
addEventResponse = commonDTO.NewBaseResponse(addEventReqDTO.RequestId, err.Message(), err.Code())
statusCode = err.Code()
} else {
addEventResponse = commonDTO.NewBaseWithIdResponse(
addEventReqDTO.RequestId,
"",
http.StatusCreated,
event.Id)
statusCode = http.StatusCreated
application.PublishEvent(addEventReqDTO, profileName, deviceName, ctx, ec.dic)
}

utils.WriteHttpHeader(w, ctx, statusCode)
// encode and send out the response
pkg.Encode(addResponses, w, lc)
pkg.Encode(addEventResponse, w, lc)
}

func (ec *EventController) EventById(w http.ResponseWriter, r *http.Request) {
Expand Down
55 changes: 27 additions & 28 deletions internal/core/data/v2/controller/http/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ var persistedEvent = models.Event{
}

func TestAddEvent(t *testing.T) {
expectedResponseCode := http.StatusMultiStatus
expectedRequestId := "82eb2e26-0f24-48aa-ae4c-de9dac3fb9bc"

dbClientMock := &dbMock.DBClient{}
Expand Down Expand Up @@ -172,31 +171,31 @@ func TestAddEvent(t *testing.T) {

tests := []struct {
Name string
Request []requests.AddEventRequest
Request requests.AddEventRequest
ProfileName string
DeviceName string
ErrorExpected bool
ExpectedStatusCode int
}{
{"Valid - AddEventRequest", []requests.AddEventRequest{validRequest}, validRequest.Event.ProfileName, validRequest.Event.DeviceName, false, http.StatusCreated},
{"Valid - No RequestId", []requests.AddEventRequest{noRequestId}, noRequestId.Event.ProfileName, noRequestId.Event.DeviceName, false, http.StatusCreated},
{"Invalid - Bad RequestId", []requests.AddEventRequest{badRequestId}, badRequestId.Event.ProfileName, badRequestId.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Event", []requests.AddEventRequest{noEvent}, "", "", true, http.StatusBadRequest},
{"Invalid - No Event Id", []requests.AddEventRequest{noEventID}, noEventID.Event.ProfileName, noEventID.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - Bad Event Id", []requests.AddEventRequest{badEventID}, badEventID.Event.ProfileName, badEventID.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Event DeviceName", []requests.AddEventRequest{noEventDevice}, noEventDevice.Event.ProfileName, noEventDevice.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Event ProfileName", []requests.AddEventRequest{noEventProfile}, noEventProfile.Event.ProfileName, noEventProfile.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Event Origin", []requests.AddEventRequest{noEventOrigin}, noEventOrigin.Event.ProfileName, noEventOrigin.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Reading", []requests.AddEventRequest{noReading}, noReading.Event.ProfileName, noReading.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Reading DeviceName", []requests.AddEventRequest{noReadingDevice}, noReadingDevice.Event.ProfileName, noReadingDevice.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Reading ResourceName", []requests.AddEventRequest{noReadingResourceName}, noReadingResourceName.Event.ProfileName, noReadingResourceName.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Reading ProfileName", []requests.AddEventRequest{noReadingProfileName}, noReadingProfileName.Event.ProfileName, noReadingProfileName.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Reading Origin", []requests.AddEventRequest{noReadingOrigin}, noReadingOrigin.Event.ProfileName, noReadingOrigin.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Reading ValueType", []requests.AddEventRequest{noReadingValueType}, noReadingValueType.Event.ProfileName, noReadingValueType.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - Invalid Reading ValueType", []requests.AddEventRequest{invalidReadingInvalidValueType}, invalidReadingInvalidValueType.Event.ProfileName, invalidReadingInvalidValueType.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No SimpleReading Value", []requests.AddEventRequest{noSimpleValue}, noSimpleValue.Event.ProfileName, noSimpleValue.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No BinaryReading BinaryValue", []requests.AddEventRequest{noBinaryValue}, noBinaryValue.Event.ProfileName, noBinaryValue.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No BinaryReading MediaType", []requests.AddEventRequest{noBinaryMediaType}, noBinaryMediaType.Event.ProfileName, noBinaryMediaType.Event.DeviceName, true, http.StatusBadRequest},
{"Valid - AddEventRequest", validRequest, validRequest.Event.ProfileName, validRequest.Event.DeviceName, false, http.StatusCreated},
{"Valid - No RequestId", noRequestId, noRequestId.Event.ProfileName, noRequestId.Event.DeviceName, false, http.StatusCreated},
{"Invalid - Bad RequestId", badRequestId, badRequestId.Event.ProfileName, badRequestId.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Event", noEvent, "", "", true, http.StatusBadRequest},
{"Invalid - No Event Id", noEventID, noEventID.Event.ProfileName, noEventID.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - Bad Event Id", badEventID, badEventID.Event.ProfileName, badEventID.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Event DeviceName", noEventDevice, noEventDevice.Event.ProfileName, noEventDevice.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Event ProfileName", noEventProfile, noEventProfile.Event.ProfileName, noEventProfile.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Event Origin", noEventOrigin, noEventOrigin.Event.ProfileName, noEventOrigin.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Reading", noReading, noReading.Event.ProfileName, noReading.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Reading DeviceName", noReadingDevice, noReadingDevice.Event.ProfileName, noReadingDevice.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Reading ResourceName", noReadingResourceName, noReadingResourceName.Event.ProfileName, noReadingResourceName.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Reading ProfileName", noReadingProfileName, noReadingProfileName.Event.ProfileName, noReadingProfileName.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Reading Origin", noReadingOrigin, noReadingOrigin.Event.ProfileName, noReadingOrigin.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No Reading ValueType", noReadingValueType, noReadingValueType.Event.ProfileName, noReadingValueType.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - Invalid Reading ValueType", invalidReadingInvalidValueType, invalidReadingInvalidValueType.Event.ProfileName, invalidReadingInvalidValueType.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No SimpleReading Value", noSimpleValue, noSimpleValue.Event.ProfileName, noSimpleValue.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No BinaryReading BinaryValue", noBinaryValue, noBinaryValue.Event.ProfileName, noBinaryValue.Event.DeviceName, true, http.StatusBadRequest},
{"Invalid - No BinaryReading MediaType", noBinaryMediaType, noBinaryMediaType.Event.ProfileName, noBinaryMediaType.Event.DeviceName, true, http.StatusBadRequest},
}

for _, testCase := range tests {
Expand All @@ -214,7 +213,7 @@ func TestAddEvent(t *testing.T) {
handler := http.HandlerFunc(ec.AddEvent)
handler.ServeHTTP(recorder, req)

var actualResponse []common.BaseWithIdResponse
var actualResponse common.BaseWithIdResponse
err = json.Unmarshal(recorder.Body.Bytes(), &actualResponse)

if testCase.ErrorExpected {
Expand All @@ -223,13 +222,13 @@ func TestAddEvent(t *testing.T) {
}

require.NoError(t, err)
assert.Equal(t, expectedResponseCode, recorder.Result().StatusCode, "HTTP status code not as expected")
assert.Equal(t, v2.ApiVersion, actualResponse[0].ApiVersion, "API Version not as expected")
assert.Equal(t, testCase.ExpectedStatusCode, int(actualResponse[0].StatusCode), "BaseResponse status code not as expected")
if actualResponse[0].RequestId != "" {
assert.Equal(t, expectedRequestId, actualResponse[0].RequestId, "RequestID not as expected")
assert.Equal(t, testCase.ExpectedStatusCode, recorder.Result().StatusCode, "HTTP status code not as expected")
assert.Equal(t, v2.ApiVersion, actualResponse.ApiVersion, "API Version not as expected")
assert.Equal(t, testCase.ExpectedStatusCode, int(actualResponse.StatusCode), "BaseResponse status code not as expected")
if actualResponse.RequestId != "" {
assert.Equal(t, expectedRequestId, actualResponse.RequestId, "RequestID not as expected")
}
assert.Empty(t, actualResponse[0].Message, "Message should be empty when it is successful")
assert.Empty(t, actualResponse.Message, "Message should be empty when it is successful")
})
}
}
Expand Down
12 changes: 6 additions & 6 deletions internal/core/data/v2/io/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

// EventReader unmarshals a request body into an Event type
type EventReader interface {
ReadAddEventRequest(reader io.Reader) ([]dto.AddEventRequest, errors.EdgeX)
ReadAddEventRequest(reader io.Reader) (dto.AddEventRequest, errors.EdgeX)
}

// NewRequestReader returns a BodyReader capable of processing the request body
Expand All @@ -32,11 +32,11 @@ func NewJsonReader() jsonEventReader {
}

// Read reads and converts the request's JSON event data into an Event struct
func (jsonEventReader) ReadAddEventRequest(reader io.Reader) ([]dto.AddEventRequest, errors.EdgeX) {
var addEvents []dto.AddEventRequest
err := json.NewDecoder(reader).Decode(&addEvents)
func (jsonEventReader) ReadAddEventRequest(reader io.Reader) (dto.AddEventRequest, errors.EdgeX) {
var addEvent dto.AddEventRequest
err := json.NewDecoder(reader).Decode(&addEvent)
if err != nil {
return nil, errors.NewCommonEdgeX(errors.KindContractInvalid, "event json decoding failed", err)
return addEvent, errors.NewCommonEdgeX(errors.KindContractInvalid, "event json decoding failed", err)
}
return addEvents, nil
return addEvent, nil
}
86 changes: 23 additions & 63 deletions openapi/v2/core-data.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -475,83 +475,43 @@ paths:
type: string
description: "Uniquely identifies a given device"
post:
summary: "Allows for the ingestion of event/reading data, and the deviceName and profileName of Events must match to the given deviceName and profileName specified in the path"
summary: "Allows for the ingestion of event/reading data, and the deviceName and profileName of Event must match to the given deviceName and profileName as specified in the path"
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/AddEventRequest'
$ref: '#/components/schemas/AddEventRequest'
example:
- event:
deviceName: device-002
id: d5471d59-2810-419a-8744-18eb8fa03465
origin: 1602168089665565300
readings:
- deviceName: device-002
resourceName: resource-002
profileName: profile-002
id: 7003cacc-0e00-4676-977c-4e58b9612abd
origin: 1602168089665565300
valueType: Float32
value: '12.2'
- event:
deviceName: device-002
id: d5471d59-2810-419a-8744-18eb8fa03465
origin: 1602168089665565300
readings:
- deviceName: device-002
resourceName: resource-002
profileName: profile-002
id: 7003cacc-0e00-4676-977c-4e58b9612abd
origin: 1602168089665565300
valueType: Float32
value: '12.2'
- event:
deviceName: device-002
id: 73fc4f9c-2d64-4920-addb-b1f33a8f8514
origin: 1602168089665565300
readings:
- apiVersion: v2
created: 1594879337014
deviceName: device-002
resourceName: resource-002
profileName: profile-002
id: 71c601d9-cb56-453a-8c75-54461e444713
origin: 1602168089665565300
valueType: Binary
binaryValue: '83010203'
mediaType: image
event:
deviceName: device-002
profileName: profile-002
id: d5471d59-2810-419a-8744-18eb8fa03465
origin: 1602168089665565300
readings:
- deviceName: device-002
resourceName: resource-002
profileName: profile-002
id: 7003cacc-0e00-4676-977c-4e58b9612abd
origin: 1602168089665565300
valueType: Float32
value: '12.2'
responses:
'207':
description: "Indicates a multi-part response supportive of accepting multiple requests at once. The 'statusCode' property of each response in the returned array will indicate success or failure."
'201':
description: "Indicates the event has been successfully created."
headers:
X-Correlation-ID:
$ref: '#/components/headers/correlatedResponseHeader'
content:
application/json:
schema:
type: array
items:
anyOf:
- $ref: '#/components/schemas/ErrorResponse'
- $ref: '#/components/schemas/BaseWithIdResponse'
$ref: '#/components/schemas/BaseWithIdResponse'
example:
- requestId: ""
apiVersion: "v2"
statusCode: 201
message: ""
id: "040bd523-ec33-440d-9d72-e5813a465f37"
- requestId: ""
apiVersion: "v2"
statusCode: 409
message: "Event Id exists"
- requestId: ""
apiVersion: "v2"
statusCode: 500
message: "Interval Server Error"
requestId: ""
apiVersion: "v2"
statusCode: 201
message: ""
id: "040bd523-ec33-440d-9d72-e5813a465f37"
'400':
description: "Request is in an invalid state"
headers:
Expand Down

0 comments on commit f2181a9

Please sign in to comment.