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

refactor(meta): Refactor device service update operation to DBClient #3066

Merged
merged 1 commit into from
Jan 22, 2021
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
9 changes: 2 additions & 7 deletions internal/core/metadata/v2/application/deviceservice.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (C) 2020 IOTech Ltd
// Copyright (C) 2020-2021 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0

Expand Down Expand Up @@ -79,12 +79,7 @@ func PatchDeviceService(dto dtos.UpdateDeviceService, ctx context.Context, dic *

requests.ReplaceDeviceServiceModelFieldsWithDTO(&deviceService, dto)

edgeXerr = dbClient.DeleteDeviceServiceById(deviceService.Id)
if edgeXerr != nil {
return errors.NewCommonEdgeXWrapper(edgeXerr)
}

_, edgeXerr = dbClient.AddDeviceService(deviceService)
edgeXerr = dbClient.UpdateDeviceService(deviceService)
if edgeXerr != nil {
return errors.NewCommonEdgeXWrapper(edgeXerr)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,7 @@ func TestPatchDeviceService(t *testing.T) {

valid := testReq
dbClientMock.On("DeviceServiceById", *valid.Service.Id).Return(dsModels, nil)
dbClientMock.On("DeleteDeviceServiceById", *valid.Service.Id).Return(nil)
dbClientMock.On("AddDeviceService", mock.Anything).Return(dsModels, nil)
dbClientMock.On("UpdateDeviceService", mock.Anything).Return(nil)
validWithNoReqID := testReq
validWithNoReqID.RequestId = ""
validWithNoId := testReq
Expand Down
3 changes: 2 additions & 1 deletion internal/core/metadata/v2/infrastructure/interfaces/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ type DBClient interface {
DeviceProfilesByManufacturer(offset int, limit int, manufacturer string) ([]model.DeviceProfile, errors.EdgeX)
DeviceProfilesByManufacturerAndModel(offset int, limit int, manufacturer string, model string) ([]model.DeviceProfile, errors.EdgeX)

AddDeviceService(e model.DeviceService) (model.DeviceService, errors.EdgeX)
AddDeviceService(ds model.DeviceService) (model.DeviceService, errors.EdgeX)
DeviceServiceById(id string) (model.DeviceService, errors.EdgeX)
DeviceServiceByName(name string) (model.DeviceService, errors.EdgeX)
DeleteDeviceServiceById(id string) errors.EdgeX
DeleteDeviceServiceByName(name string) errors.EdgeX
DeviceServiceNameExists(name string) (bool, errors.EdgeX)
AllDeviceServices(offset int, limit int, labels []string) ([]model.DeviceService, errors.EdgeX)
UpdateDeviceService(ds model.DeviceService) errors.EdgeX

AddDevice(d model.Device) (model.Device, errors.EdgeX)
DeleteDeviceById(id string) errors.EdgeX
Expand Down

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

7 changes: 7 additions & 0 deletions internal/pkg/v2/infrastructure/redis/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,13 @@ func (c *Client) DeviceServiceNameExists(name string) (bool, errors.EdgeX) {
return deviceServiceNameExist(conn, name)
}

// UpdateDeviceService updates a device service
func (c *Client) UpdateDeviceService(ds model.DeviceService) errors.EdgeX {
conn := c.Pool.Get()
defer conn.Close()
return updateDeviceService(conn, ds)
}

// DeviceProfileByName gets a device profile by name
func (c *Client) DeviceProfileByName(name string) (deviceProfile model.DeviceProfile, edgeXerr errors.EdgeX) {
conn := c.Pool.Get()
Expand Down
81 changes: 56 additions & 25 deletions internal/pkg/v2/infrastructure/redis/device_service.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (C) 2020 IOTech Ltd
// Copyright (C) 2020-2021 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0

Expand All @@ -8,6 +8,7 @@ package redis
import (
"encoding/json"
"fmt"

"github.com/edgexfoundry/edgex-go/internal/pkg/common"

"github.com/edgexfoundry/go-mod-core-contracts/v2/errors"
Expand Down Expand Up @@ -37,6 +38,24 @@ func deviceServiceNameExist(conn redis.Conn, name string) (bool, errors.EdgeX) {
return exists, nil
}

// sendAddDeviceServiceCmd send redis command for adding device service
func sendAddDeviceServiceCmd(conn redis.Conn, storedKey string, ds models.DeviceService) errors.EdgeX {
m, err := json.Marshal(ds)
if err != nil {
return errors.NewCommonEdgeX(errors.KindContractInvalid, "unable to JSON marshal device service for Redis persistence", err)
}
// Set the storedKey to associate with object byte array for later retrieval
_ = conn.Send(SET, storedKey, m)
// Store the storedKey into a Sorted Set with Modified as the score for order
_ = conn.Send(ZADD, DeviceServiceCollection, ds.Modified, storedKey)
// Store the ds.Name into a Hash for later Name existence check
_ = conn.Send(HSET, DeviceServiceCollectionName, ds.Name, storedKey)
for _, label := range ds.Labels { // Store the redisKey into Sorted Set of labels with Modified as the score for order
_ = conn.Send(ZADD, CreateKey(DeviceServiceCollectionLabel, label), ds.Modified, storedKey)
}
return nil
}

// addDeviceService adds a new device service into DB
func addDeviceService(conn redis.Conn, ds models.DeviceService) (addedDeviceService models.DeviceService, edgeXerr errors.EdgeX) {
// retrieve Device Service by Id first to ensure there is no Id conflict; when Id exists, return duplicate error
Expand All @@ -55,35 +74,21 @@ func addDeviceService(conn redis.Conn, ds models.DeviceService) (addedDeviceServ
return addedDeviceService, errors.NewCommonEdgeX(errors.KindDuplicateName, fmt.Sprintf("device service name %s already exists", ds.Name), edgeXerr)
}

ts := common.MakeTimestamp()
// For Redis DB, the PUT or PATCH operation will removes the old object and add the modified one,
// so the Created is not zero value and we shouldn't set the timestamp again.
if ds.Created == 0 {
ds.Created = ts
ds.Created = common.MakeTimestamp()
}
// query API will sort the result based on Modified, so even newly created device service shall specify Modified as Created
ds.Modified = ds.Created

dsJSONBytes, err := json.Marshal(ds)
if err != nil {
return addedDeviceService, errors.NewCommonEdgeX(errors.KindContractInvalid, "unable to JSON marshal device service for Redis persistence", err)
}

// redisKey represents the key stored in the redis, use the format of #{DeviceServiceCollection}:#{ds.Id}
// storedKey represents the key stored in the redis, use the format of #{DeviceServiceCollection}:#{ds.Id}
// as the redisKey to avoid data being accidentally deleted when other objects, e.g. device profiles, also
// coincidentally have the same Id.
redisKey := deviceServiceStoredKey(ds.Id)
storedKey := deviceServiceStoredKey(ds.Id)
_ = conn.Send(MULTI)
// Set the redisKey to associate with object byte array for later retrieval
_ = conn.Send(SET, redisKey, dsJSONBytes)
// Store the redisKey into a Sorted Set with Modified as the score for order
_ = conn.Send(ZADD, DeviceServiceCollection, ds.Modified, redisKey)
// Store the ds.Name into a Hash for later Name existence check
_ = conn.Send(HSET, DeviceServiceCollectionName, ds.Name, redisKey)
for _, label := range ds.Labels { // Store the redisKey into Sorted Set of labels with Modified as the score for order
_ = conn.Send(ZADD, CreateKey(DeviceServiceCollectionLabel, label), ds.Modified, redisKey)
}
_, err = conn.Do(EXEC)
edgeXerr = sendAddDeviceServiceCmd(conn, storedKey, ds)
_, err := conn.Do(EXEC)
if err != nil {
edgeXerr = errors.NewCommonEdgeX(errors.KindDatabaseError, "device service creation failed", err)
}
Expand All @@ -109,16 +114,20 @@ func deviceServiceByName(conn redis.Conn, name string) (deviceService models.Dev
return
}

func deleteDeviceService(conn redis.Conn, deviceService models.DeviceService) errors.EdgeX {
storedKey := deviceServiceStoredKey(deviceService.Id)
_ = conn.Send(MULTI)
// sendDeleteDeviceServiceCmd send redis command for deleting device service
func sendDeleteDeviceServiceCmd(conn redis.Conn, storedKey string, ds models.DeviceService) {
_ = conn.Send(DEL, storedKey)
_ = conn.Send(ZREM, DeviceServiceCollection, storedKey)
_ = conn.Send(HDEL, DeviceServiceCollectionName, deviceService.Name)
for _, label := range deviceService.Labels {
_ = conn.Send(HDEL, DeviceServiceCollectionName, ds.Name)
for _, label := range ds.Labels {
_ = conn.Send(ZREM, CreateKey(DeviceServiceCollectionLabel, label), storedKey)
}
}

func deleteDeviceService(conn redis.Conn, ds models.DeviceService) errors.EdgeX {
storedKey := deviceServiceStoredKey(ds.Id)
_ = conn.Send(MULTI)
sendDeleteDeviceServiceCmd(conn, storedKey, ds)
_, err := conn.Do(EXEC)
if err != nil {
return errors.NewCommonEdgeX(errors.KindDatabaseError, "device service deletion failed", err)
Expand Down Expand Up @@ -174,3 +183,25 @@ func deviceServicesByLabels(conn redis.Conn, offset int, limit int, labels []str
}
return deviceServices, nil
}

func updateDeviceService(conn redis.Conn, ds models.DeviceService) errors.EdgeX {
oldDeviceService, edgeXerr := deviceServiceByName(conn, ds.Name)
if edgeXerr != nil {
return errors.NewCommonEdgeXWrapper(edgeXerr)
}

ds.Modified = common.MakeTimestamp()
storedKey := deviceServiceStoredKey(ds.Id)
_ = conn.Send(MULTI)
sendDeleteDeviceServiceCmd(conn, storedKey, oldDeviceService)
edgeXerr = sendAddDeviceServiceCmd(conn, storedKey, ds)
if edgeXerr != nil {
return errors.NewCommonEdgeXWrapper(edgeXerr)
}
_, err := conn.Do(EXEC)
if err != nil {
return errors.NewCommonEdgeX(errors.KindDatabaseError, "device service update failed", err)
}

return nil
}