From a8f770479522fe3c7b88a2b5d2d54eca0cad3755 Mon Sep 17 00:00:00 2001 From: weichou Date: Thu, 21 Jan 2021 13:58:54 +0800 Subject: [PATCH] refactor(meta): Refactor device update operation to DBClient Refactor update operation to DBClient because deleting and adding to update a record only used for RedisDB. Signed-off-by: weichou --- .../core/metadata/v2/application/device.go | 9 +-- .../v2/controller/http/device_test.go | 5 +- .../v2/infrastructure/interfaces/db.go | 1 + .../interfaces/mocks/DBClient.go | 18 ++++- .../pkg/v2/infrastructure/redis/client.go | 8 ++ .../pkg/v2/infrastructure/redis/device.go | 73 ++++++++++++++----- 6 files changed, 85 insertions(+), 29 deletions(-) diff --git a/internal/core/metadata/v2/application/device.go b/internal/core/metadata/v2/application/device.go index 6f9808095b..5a1842931e 100644 --- a/internal/core/metadata/v2/application/device.go +++ b/internal/core/metadata/v2/application/device.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2020 IOTech Ltd +// Copyright (C) 2020-2021 IOTech Ltd // // SPDX-License-Identifier: Apache-2.0 @@ -139,12 +139,7 @@ func PatchDevice(dto dtos.UpdateDevice, ctx context.Context, dic *di.Container) requests.ReplaceDeviceModelFieldsWithDTO(&device, dto) - err = dbClient.DeleteDeviceById(device.Id) - if err != nil { - return errors.NewCommonEdgeXWrapper(err) - } - - _, err = dbClient.AddDevice(device) + err = dbClient.UpdateDevice(device) if err != nil { return errors.NewCommonEdgeXWrapper(err) } diff --git a/internal/core/metadata/v2/controller/http/device_test.go b/internal/core/metadata/v2/controller/http/device_test.go index 8124b72a3d..c0d42811a4 100644 --- a/internal/core/metadata/v2/controller/http/device_test.go +++ b/internal/core/metadata/v2/controller/http/device_test.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2020 IOTech Ltd +// Copyright (C) 2020-2021 IOTech Ltd // // SPDX-License-Identifier: Apache-2.0 @@ -433,8 +433,7 @@ func TestPatchDevice(t *testing.T) { dbClientMock.On("DeviceServiceNameExists", *valid.Device.ServiceName).Return(true, nil) dbClientMock.On("DeviceProfileNameExists", *valid.Device.ProfileName).Return(true, nil) dbClientMock.On("DeviceById", *valid.Device.Id).Return(dsModels, nil) - dbClientMock.On("DeleteDeviceById", *valid.Device.Id).Return(nil) - dbClientMock.On("AddDevice", mock.Anything).Return(dsModels, nil) + dbClientMock.On("UpdateDevice", mock.Anything).Return(nil) dbClientMock.On("DeviceServiceByName", *valid.Device.ServiceName).Return(models.DeviceService{BaseAddress: testBaseAddress}, nil) validWithNoReqID := testReq diff --git a/internal/core/metadata/v2/infrastructure/interfaces/db.go b/internal/core/metadata/v2/infrastructure/interfaces/db.go index 1517503d01..a8994e9c7c 100644 --- a/internal/core/metadata/v2/infrastructure/interfaces/db.go +++ b/internal/core/metadata/v2/infrastructure/interfaces/db.go @@ -42,6 +42,7 @@ type DBClient interface { DeviceByName(name string) (model.Device, errors.EdgeX) AllDevices(offset int, limit int, labels []string) ([]model.Device, errors.EdgeX) DevicesByProfileName(offset int, limit int, profileName string) ([]model.Device, errors.EdgeX) + UpdateDevice(d model.Device) errors.EdgeX AddProvisionWatcher(pw model.ProvisionWatcher) (model.ProvisionWatcher, errors.EdgeX) ProvisionWatcherById(id string) (model.ProvisionWatcher, errors.EdgeX) diff --git a/internal/core/metadata/v2/infrastructure/interfaces/mocks/DBClient.go b/internal/core/metadata/v2/infrastructure/interfaces/mocks/DBClient.go index 4e91bbe112..eb3bc03616 100644 --- a/internal/core/metadata/v2/infrastructure/interfaces/mocks/DBClient.go +++ b/internal/core/metadata/v2/infrastructure/interfaces/mocks/DBClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v0.0.0-dev. DO NOT EDIT. +// Code generated by mockery v2.2.1. DO NOT EDIT. package mocks @@ -752,6 +752,22 @@ func (_m *DBClient) ProvisionWatchersByServiceName(offset int, limit int, name s return r0, r1 } +// UpdateDevice provides a mock function with given fields: d +func (_m *DBClient) UpdateDevice(d models.Device) errors.EdgeX { + ret := _m.Called(d) + + var r0 errors.EdgeX + if rf, ok := ret.Get(0).(func(models.Device) errors.EdgeX); ok { + r0 = rf(d) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(errors.EdgeX) + } + } + + return r0 +} + // UpdateDeviceProfile provides a mock function with given fields: e func (_m *DBClient) UpdateDeviceProfile(e models.DeviceProfile) errors.EdgeX { ret := _m.Called(e) diff --git a/internal/pkg/v2/infrastructure/redis/client.go b/internal/pkg/v2/infrastructure/redis/client.go index f12165f352..39cf554f2e 100644 --- a/internal/pkg/v2/infrastructure/redis/client.go +++ b/internal/pkg/v2/infrastructure/redis/client.go @@ -430,6 +430,14 @@ func (c *Client) DevicesByProfileName(offset int, limit int, profileName string) return devices, nil } +// Update a device +func (c *Client) UpdateDevice(d model.Device) errors.EdgeX { + conn := c.Pool.Get() + defer conn.Close() + + return updateDevice(conn, d) +} + // AllEvents query events by offset and limit func (c *Client) AllEvents(offset int, limit int) ([]model.Event, errors.EdgeX) { conn := c.Pool.Get() diff --git a/internal/pkg/v2/infrastructure/redis/device.go b/internal/pkg/v2/infrastructure/redis/device.go index 69de58c01e..c4ee7c8979 100644 --- a/internal/pkg/v2/infrastructure/redis/device.go +++ b/internal/pkg/v2/infrastructure/redis/device.go @@ -1,5 +1,5 @@ // -// Copyright (C) 2020 IOTech Ltd +// Copyright (C) 2020-2021 IOTech Ltd // // SPDX-License-Identifier: Apache-2.0 @@ -49,6 +49,23 @@ func deviceIdExists(conn redis.Conn, id string) (bool, errors.EdgeX) { return exists, nil } +// sendAddDeviceCmd send redis command for adding device +func sendAddDeviceCmd(conn redis.Conn, storedKey string, d models.Device) errors.EdgeX { + dsJSONBytes, err := json.Marshal(d) + if err != nil { + return errors.NewCommonEdgeX(errors.KindContractInvalid, "unable to JSON marshal device for Redis persistence", err) + } + _ = conn.Send(SET, storedKey, dsJSONBytes) + _ = conn.Send(ZADD, DeviceCollection, 0, storedKey) + _ = conn.Send(HSET, DeviceCollectionName, d.Name, storedKey) + _ = conn.Send(ZADD, CreateKey(DeviceCollectionServiceName, d.ServiceName), d.Modified, storedKey) + _ = conn.Send(ZADD, CreateKey(DeviceCollectionProfileName, d.ProfileName), d.Modified, storedKey) + for _, label := range d.Labels { + _ = conn.Send(ZADD, CreateKey(DeviceCollectionLabel, label), d.Modified, storedKey) + } + return nil +} + // addDevice adds a new device into DB func addDevice(conn redis.Conn, d models.Device) (models.Device, errors.EdgeX) { exists, edgeXerr := deviceIdExists(conn, d.Id) @@ -71,22 +88,13 @@ func addDevice(conn redis.Conn, d models.Device) (models.Device, errors.EdgeX) { } d.Modified = ts - dsJSONBytes, err := json.Marshal(d) - if err != nil { - return d, errors.NewCommonEdgeX(errors.KindContractInvalid, "unable to JSON marshal device for Redis persistence", err) - } - storedKey := deviceStoredKey(d.Id) _ = conn.Send(MULTI) - _ = conn.Send(SET, storedKey, dsJSONBytes) - _ = conn.Send(ZADD, DeviceCollection, 0, storedKey) - _ = conn.Send(HSET, DeviceCollectionName, d.Name, storedKey) - _ = conn.Send(ZADD, CreateKey(DeviceCollectionServiceName, d.ServiceName), d.Modified, storedKey) - _ = conn.Send(ZADD, CreateKey(DeviceCollectionProfileName, d.ProfileName), d.Modified, storedKey) - for _, label := range d.Labels { - _ = conn.Send(ZADD, CreateKey(DeviceCollectionLabel, label), d.Modified, storedKey) + edgeXerr = sendAddDeviceCmd(conn, storedKey, d) + if edgeXerr != nil { + return d, errors.NewCommonEdgeXWrapper(edgeXerr) } - _, err = conn.Do(EXEC) + _, err := conn.Do(EXEC) if err != nil { edgeXerr = errors.NewCommonEdgeX(errors.KindDatabaseError, "device creation failed", err) } @@ -138,10 +146,8 @@ func deleteDeviceByName(conn redis.Conn, name string) errors.EdgeX { return nil } -// deleteDevice deletes a device -func deleteDevice(conn redis.Conn, device models.Device) errors.EdgeX { - storedKey := deviceStoredKey(device.Id) - _ = conn.Send(MULTI) +// sendDeleteDeviceCmd send redis command for deleting device +func sendDeleteDeviceCmd(conn redis.Conn, storedKey string, device models.Device) { _ = conn.Send(DEL, storedKey) _ = conn.Send(ZREM, DeviceCollection, storedKey) _ = conn.Send(HDEL, DeviceCollectionName, device.Name) @@ -150,6 +156,13 @@ func deleteDevice(conn redis.Conn, device models.Device) errors.EdgeX { for _, label := range device.Labels { _ = conn.Send(ZREM, CreateKey(DeviceCollectionLabel, label), storedKey) } +} + +// deleteDevice deletes a device +func deleteDevice(conn redis.Conn, device models.Device) errors.EdgeX { + storedKey := deviceStoredKey(device.Id) + _ = conn.Send(MULTI) + sendDeleteDeviceCmd(conn, storedKey, device) _, err := conn.Do(EXEC) if err != nil { return errors.NewCommonEdgeX(errors.KindDatabaseError, "device deletion failed", err) @@ -225,3 +238,27 @@ func devicesByProfileName(conn redis.Conn, offset int, limit int, profileName st } return devices, nil } + +func updateDevice(conn redis.Conn, d models.Device) errors.EdgeX { + oldDevice, edgexErr := deviceByName(conn, d.Name) + if edgexErr != nil { + return errors.NewCommonEdgeXWrapper(edgexErr) + } + + ts := common.MakeTimestamp() + d.Modified = ts + + storedKey := deviceStoredKey(d.Id) + _ = conn.Send(MULTI) + sendDeleteDeviceCmd(conn, storedKey, oldDevice) + edgexErr = sendAddDeviceCmd(conn, storedKey, d) + if edgexErr != nil { + return errors.NewCommonEdgeXWrapper(edgexErr) + } + _, err := conn.Do(EXEC) + if err != nil { + return errors.NewCommonEdgeX(errors.KindDatabaseError, "device update failed", err) + } + + return nil +}