Skip to content

Commit

Permalink
Merge pull request #3066 from weichou1229/issue-3043-device-service
Browse files Browse the repository at this point in the history
refactor(meta): Refactor device service update operation to DBClient
  • Loading branch information
cloudxxx8 authored Jan 22, 2021
2 parents 001da8f + 6b9e3f1 commit 5d6d036
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 40 deletions.
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
}

0 comments on commit 5d6d036

Please sign in to comment.