Skip to content

Commit

Permalink
feat: Add new bypassValidation query param to Add/Patch Device API (#…
Browse files Browse the repository at this point in the history
…4797)

* feat: Add new bypassValidation query param to Add Device API

Fixes #4789. Add new bypassValidation query param to Add Device API.

Signed-off-by: Lindsey Cheng <[email protected]>

* feat: Add bypassValidation to patch device API

Signed-off-by: Lindsey Cheng <[email protected]>

---------

Signed-off-by: Lindsey Cheng <[email protected]>
  • Loading branch information
lindseysimple committed May 7, 2024
1 parent 971ded0 commit 17930b1
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 11 deletions.
25 changes: 17 additions & 8 deletions internal/core/metadata/application/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const minAutoEventInterval = 1 * time.Millisecond

// The AddDevice function accepts the new device model from the controller function
// and then invokes AddDevice function of infrastructure layer to add new device
func AddDevice(d models.Device, ctx context.Context, dic *di.Container) (id string, edgeXerr errors.EdgeX) {
func AddDevice(d models.Device, ctx context.Context, dic *di.Container, bypassValidation bool) (id string, edgeXerr errors.EdgeX) {
dbClient := container.DBClientFrom(dic.Get)
lc := bootstrapContainer.LoggingClientFrom(dic.Get)

Expand All @@ -58,9 +58,13 @@ func AddDevice(d models.Device, ctx context.Context, dic *di.Container) (id stri
return "", errors.NewCommonEdgeXWrapper(err)
}

err = validateDeviceCallback(dtos.FromDeviceModelToDTO(d), dic)
if err != nil {
return "", errors.NewCommonEdgeXWrapper(err)
// Execute the Device Service Validation when bypassValidation is false by default
// Skip the Device Service Validation if bypassValidation is true
if !bypassValidation {
err = validateDeviceCallback(dtos.FromDeviceModelToDTO(d), dic)
if err != nil {
return "", errors.NewCommonEdgeXWrapper(err)
}
}

addedDevice, err := dbClient.AddDevice(d)
Expand Down Expand Up @@ -140,7 +144,7 @@ func DeviceNameExists(name string, dic *di.Container) (exists bool, err errors.E
}

// PatchDevice executes the PATCH operation with the device DTO to replace the old data
func PatchDevice(dto dtos.UpdateDevice, ctx context.Context, dic *di.Container) errors.EdgeX {
func PatchDevice(dto dtos.UpdateDevice, ctx context.Context, dic *di.Container, bypassValidation bool) errors.EdgeX {
dbClient := container.DBClientFrom(dic.Get)
lc := bootstrapContainer.LoggingClientFrom(dic.Get)

Expand Down Expand Up @@ -173,9 +177,14 @@ func PatchDevice(dto dtos.UpdateDevice, ctx context.Context, dic *di.Container)
}

deviceDTO := dtos.FromDeviceModelToDTO(device)
err = validateDeviceCallback(deviceDTO, dic)
if err != nil {
return errors.NewCommonEdgeXWrapper(err)

// Execute the Device Service Validation when bypassValidation is false by default
// Skip the Device Service Validation if bypassValidation is true
if !bypassValidation {
err = validateDeviceCallback(deviceDTO, dic)
if err != nil {
return errors.NewCommonEdgeXWrapper(err)
}
}

err = dbClient.UpdateDevice(device)
Expand Down
22 changes: 19 additions & 3 deletions internal/core/metadata/controller/http/device.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (C) 2020-2023 IOTech Ltd
// Copyright (C) 2020-2024 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0

Expand Down Expand Up @@ -27,6 +27,8 @@ import (
"github.com/labstack/echo/v4"
)

const bypassValidationQueryParam = "bypassValidation" // query param to specify whether to skip the Device Service Validation API call

type DeviceController struct {
reader io.DtoReader
dic *di.Container
Expand All @@ -52,6 +54,13 @@ func (dc *DeviceController) AddDevice(c echo.Context) error {
ctx := r.Context()
correlationId := correlation.FromContext(ctx)

var bypassValidation bool
// parse URL query string for bypassValidation
bypassValidationParamStr := utils.ParseQueryStringToString(r, bypassValidationQueryParam, common.ValueFalse)
if bypassValidationParamStr == common.ValueTrue {
bypassValidation = true
}

var reqDTOs []requests.AddDeviceRequest
err := dc.reader.Read(r.Body, &reqDTOs)
if err != nil {
Expand All @@ -63,7 +72,7 @@ func (dc *DeviceController) AddDevice(c echo.Context) error {
for i, d := range devices {
var response interface{}
reqId := reqDTOs[i].RequestId
newId, err := application.AddDevice(d, ctx, dc.dic)
newId, err := application.AddDevice(d, ctx, dc.dic, bypassValidation)
if err != nil {
lc.Error(err.Error(), common.CorrelationHeader, correlationId)
lc.Debug(err.DebugMessages(), common.CorrelationHeader, correlationId)
Expand Down Expand Up @@ -168,6 +177,13 @@ func (dc *DeviceController) PatchDevice(c echo.Context) error {
ctx := r.Context()
correlationId := correlation.FromContext(ctx)

var bypassValidation bool
// parse URL query string for bypassValidation
bypassValidationParamStr := utils.ParseQueryStringToString(r, bypassValidationQueryParam, common.ValueFalse)
if bypassValidationParamStr == common.ValueTrue {
bypassValidation = true
}

var reqDTOs []requests.UpdateDeviceRequest
err := dc.reader.Read(r.Body, &reqDTOs)
if err != nil {
Expand All @@ -178,7 +194,7 @@ func (dc *DeviceController) PatchDevice(c echo.Context) error {
for _, dto := range reqDTOs {
var response interface{}
reqId := dto.RequestId
err := application.PatchDevice(dto.Device, ctx, dc.dic)
err := application.PatchDevice(dto.Device, ctx, dc.dic, bypassValidation)
if err != nil {
lc.Error(err.Error(), common.CorrelationHeader, correlationId)
lc.Debug(err.DebugMessages(), common.CorrelationHeader, correlationId)
Expand Down
14 changes: 14 additions & 0 deletions internal/core/metadata/controller/http/device_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ func TestAddDevice(t *testing.T) {
expectedSystemEvent bool
}{
{"Valid", []requests.AddDeviceRequest{valid}, http.StatusMultiStatus, http.StatusCreated, true, true},
{"Valid - bypassValidation", []requests.AddDeviceRequest{valid}, http.StatusMultiStatus, http.StatusCreated, false, true},
{"Invalid - not found profile", []requests.AddDeviceRequest{notFoundProfile}, http.StatusMultiStatus, http.StatusNotFound, true, false},
{"Invalid - no name", []requests.AddDeviceRequest{noName}, http.StatusBadRequest, http.StatusBadRequest, false, false},
{"Invalid - no adminState", []requests.AddDeviceRequest{noAdminState}, http.StatusBadRequest, http.StatusBadRequest, false, false},
Expand Down Expand Up @@ -243,6 +244,12 @@ func TestAddDevice(t *testing.T) {
req, err := http.NewRequest(http.MethodPost, common.ApiDeviceRoute, reader)
require.NoError(t, err)

if !testCase.expectedValidation {
query := req.URL.Query()
query.Add(bypassValidationQueryParam, common.ValueTrue)
req.URL.RawQuery = query.Encode()
}

// Act
recorder := httptest.NewRecorder()
c := e.NewContext(req, recorder)
Expand Down Expand Up @@ -591,6 +598,7 @@ func TestPatchDevice(t *testing.T) {
{"Valid - no requestId", []requests.UpdateDeviceRequest{validWithNoReqID}, http.StatusMultiStatus, http.StatusOK, true, true},
{"Valid - no id", []requests.UpdateDeviceRequest{validWithNoId}, http.StatusMultiStatus, http.StatusOK, true, true},
{"Valid - no name", []requests.UpdateDeviceRequest{validWithNoName}, http.StatusMultiStatus, http.StatusOK, true, true},
{"Valid - bypassValidation", []requests.UpdateDeviceRequest{valid}, http.StatusMultiStatus, http.StatusOK, false, true},
{"Invalid - invalid id", []requests.UpdateDeviceRequest{invalidId}, http.StatusBadRequest, http.StatusBadRequest, false, false},
{"Invalid - empty id", []requests.UpdateDeviceRequest{emptyId}, http.StatusBadRequest, http.StatusBadRequest, false, false},
{"Invalid - empty name", []requests.UpdateDeviceRequest{emptyName}, http.StatusBadRequest, http.StatusBadRequest, false, false},
Expand Down Expand Up @@ -647,6 +655,12 @@ func TestPatchDevice(t *testing.T) {
req, err := http.NewRequest(http.MethodPatch, common.ApiDeviceRoute, reader)
require.NoError(t, err)

if !testCase.expectedValidation {
query := req.URL.Query()
query.Add(bypassValidationQueryParam, common.ValueTrue)
req.URL.RawQuery = query.Encode()
}

// Act
recorder := httptest.NewRecorder()
c := e.NewContext(req, recorder)
Expand Down
9 changes: 9 additions & 0 deletions openapi/v3/core-metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,14 @@ components:
schema:
type: string
description: "Allows for querying a given object by associated user-defined label. More than one label may be specified via a comma-delimited list."
bypassValidationParam:
in: query
name: bypassValidation
required: false
schema:
type: boolean
description: "Indicates whether to skip the Device Service Validation API call."
default: false
headers:
correlatedResponseHeader:
description: "A response header that returns the unique correlation ID used to initiate the request."
Expand Down Expand Up @@ -1582,6 +1590,7 @@ paths:
/device:
parameters:
- $ref: '#/components/parameters/correlatedRequestHeader'
- $ref: '#/components/parameters/bypassValidationParam'
post:
summary: "Allows provisioning of a new device"
requestBody:
Expand Down

0 comments on commit 17930b1

Please sign in to comment.