VOL-2999 - Reworked how Proxies are created & used.

Added DB Paths to separate location specification logic from entry access logic.
Also merged Update() and AddWithID() and renamed to Set().

Change-Id: I9ed5eafd63c180dddc5845a166554f89bda12325
diff --git a/db/model/proxy.go b/db/model/proxy.go
index 997ebe4..cd35507 100644
--- a/db/model/proxy.go
+++ b/db/model/proxy.go
@@ -20,10 +20,12 @@
 	"context"
 	"errors"
 	"fmt"
+	"reflect"
+	"strings"
+
 	"github.com/gogo/protobuf/proto"
 	"github.com/opencord/voltha-lib-go/v3/pkg/db"
 	"github.com/opencord/voltha-lib-go/v3/pkg/log"
-	"reflect"
 )
 
 // RequestTimestamp attribute used to store a timestamp in the context object
@@ -31,30 +33,43 @@
 
 type contextKey string
 
-// Proxy holds the information for a specific location with the data model
-type Proxy struct {
+// Path holds the information for a specific location within the data model
+type Path struct {
 	kvStore *db.Backend
 	path    string
 }
 
-// NewProxy instantiates a new proxy to a specific location
-func NewProxy(kvStore *db.Backend, path string) *Proxy {
-	if path == "/" {
-		path = ""
-	}
-	return &Proxy{
-		kvStore: kvStore,
-		path:    path,
+// NewDBPath returns a path to the default db location
+func NewDBPath(kvStore *db.Backend) *Path {
+	return &Path{kvStore: kvStore}
+}
+
+// SubPath returns a path which points to a more specific db location
+func (p *Path) SubPath(path string) *Path {
+	path = strings.TrimRight(strings.TrimLeft(path, "/"), "/")
+	return &Path{
+		kvStore: p.kvStore,
+		path:    p.path + path + "/",
 	}
 }
 
-// List will retrieve information from the data model at the specified path location, and write it to the target slice
-// target must be a type of the form *[]<proto.Message Type>  For example: *[]*voltha.Device
-func (p *Proxy) List(ctx context.Context, path string, target interface{}) error {
-	completePath := p.path + path
+// Proxy contains all the information needed to reference a specific resource within the kv
+type Proxy Path
 
+// Proxy returns a new proxy which references the specified resource
+func (p *Path) Proxy(resource string) *Proxy {
+	resource = strings.TrimRight(strings.TrimLeft(resource, "/"), "/")
+	return &Proxy{
+		kvStore: p.kvStore,
+		path:    p.path + resource + "/",
+	}
+}
+
+// List will retrieve information from the data model at the proxy's path location, and write it to the target slice
+// target must be a type of the form *[]<proto.Message Type>  For example: *[]*voltha.Device
+func (p *Proxy) List(ctx context.Context, target interface{}) error {
 	logger.Debugw("proxy-list", log.Fields{
-		"path": completePath,
+		"path": p.path,
 	})
 
 	// verify type of target is *[]*<type>
@@ -72,13 +87,13 @@
 	}
 	dataType := elemType.Elem() // type
 
-	blobs, err := p.kvStore.List(ctx, completePath)
+	blobs, err := p.kvStore.List(ctx, p.path)
 	if err != nil {
-		return fmt.Errorf("failed to retrieve %s from kvstore: %s", path, err)
+		return fmt.Errorf("failed to retrieve %s from kvstore: %s", p.path, err)
 	}
 
 	logger.Debugw("parsing-data-blobs", log.Fields{
-		"path": path,
+		"path": p.path,
 		"size": len(blobs),
 	})
 
@@ -96,9 +111,9 @@
 	return nil
 }
 
-// Get will retrieve information from the data model at the specified path location, and write it to target
-func (p *Proxy) Get(ctx context.Context, path string, target proto.Message) (bool, error) {
-	completePath := p.path + path
+// Get will retrieve information from the data model at the proxy's path location, and write it to target
+func (p *Proxy) Get(ctx context.Context, id string, target proto.Message) (bool, error) {
+	completePath := p.path + id
 
 	logger.Debugw("proxy-get", log.Fields{
 		"path": completePath,
@@ -106,13 +121,13 @@
 
 	blob, err := p.kvStore.Get(ctx, completePath)
 	if err != nil {
-		return false, fmt.Errorf("failed to retrieve %s from kvstore: %s", path, err)
+		return false, fmt.Errorf("failed to retrieve %s from kvstore: %s", completePath, err)
 	} else if blob == nil {
 		return false, nil // this blob does not exist
 	}
 
 	logger.Debugw("parsing-data-blobs", log.Fields{
-		"path": path,
+		"path": completePath,
 	})
 
 	if err := proto.Unmarshal(blob.Value.([]byte), target); err != nil {
@@ -121,20 +136,9 @@
 	return true, nil
 }
 
-// Update will modify information in the data model at the specified location with the provided data
-func (p *Proxy) Update(ctx context.Context, path string, data proto.Message) error {
-	return p.add(ctx, path, data)
-}
-
-// AddWithID will insert new data at specified location.
-// This method also allows the user to specify the ID.
-func (p *Proxy) AddWithID(ctx context.Context, path string, id string, data proto.Message) error {
-	return p.add(ctx, path+"/"+id, data)
-}
-
-// add will insert new data at specified location.
-func (p *Proxy) add(ctx context.Context, path string, data proto.Message) error {
-	completePath := p.path + path
+// Set will add new or update existing entry at the proxy's path location
+func (p *Proxy) Set(ctx context.Context, id string, data proto.Message) error {
+	completePath := p.path + id
 
 	logger.Debugw("proxy-add", log.Fields{
 		"path": completePath,
@@ -151,9 +155,9 @@
 	return nil
 }
 
-// Remove will delete an entry at the specified location
-func (p *Proxy) Remove(ctx context.Context, path string) error {
-	completePath := p.path + path
+// Remove will delete an entry at the proxy's path location
+func (p *Proxy) Remove(ctx context.Context, id string) error {
+	completePath := p.path + id
 
 	logger.Debugw("proxy-remove", log.Fields{
 		"path": completePath,
diff --git a/db/model/proxy_test.go b/db/model/proxy_test.go
index 683e0a4..a73a071 100644
--- a/db/model/proxy_test.go
+++ b/db/model/proxy_test.go
@@ -19,6 +19,11 @@
 	"context"
 	"encoding/hex"
 	"encoding/json"
+	"strconv"
+	"strings"
+	"sync"
+	"testing"
+
 	"github.com/google/uuid"
 	"github.com/opencord/voltha-lib-go/v3/pkg/db"
 	"github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
@@ -27,10 +32,6 @@
 	"github.com/opencord/voltha-protos/v3/go/openflow_13"
 	"github.com/opencord/voltha-protos/v3/go/voltha"
 	"github.com/stretchr/testify/assert"
-	"strconv"
-	"strings"
-	"sync"
-	"testing"
 )
 
 var (
@@ -66,9 +67,10 @@
 	}
 	log.SetPackageLogLevel("github.com/opencord/voltha-go/db/model", log.DebugLevel)
 
-	TestProxyRootLogicalDevice = NewProxy(mockBackend, "/")
-	TestProxyRootDevice = NewProxy(mockBackend, "/")
-	TestProxyRootAdapter = NewProxy(mockBackend, "/")
+	proxy := NewDBPath(mockBackend)
+	TestProxyRootLogicalDevice = proxy.Proxy("logical_devices")
+	TestProxyRootDevice = proxy.Proxy("device")
+	TestProxyRootAdapter = proxy.Proxy("adapters")
 
 	TestProxyLogicalPorts = []*voltha.LogicalPort{
 		{
@@ -169,7 +171,7 @@
 	TestProxyDeviceID = "0001" + hex.EncodeToString(devIDBin)[:12]
 	TestProxyDevice.Id = TestProxyDeviceID
 
-	if err := TestProxyRootDevice.AddWithID(context.Background(), "devices", TestProxyDeviceID, TestProxyDevice); err != nil {
+	if err := TestProxyRootDevice.Set(context.Background(), TestProxyDeviceID, TestProxyDevice); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to add test proxy device due to error: %v", err)
 		t.Errorf("failed to add device: %s", err)
 	}
@@ -177,7 +179,7 @@
 
 	// Verify that the added device can now be retrieved
 	d := &voltha.Device{}
-	if have, err := TestProxyRootDevice.Get(context.Background(), "devices/"+TestProxyDeviceID, d); err != nil {
+	if have, err := TestProxyRootDevice.Get(context.Background(), TestProxyDeviceID, d); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed get device info from test proxy due to error: %v", err)
 		assert.NotNil(t, err)
 	} else if !have {
@@ -191,13 +193,13 @@
 func TestProxy_1_1_2_Add_ExistingDevice(t *testing.T) {
 	TestProxyDevice.Id = TestProxyDeviceID
 
-	if err := TestProxyRootDevice.add(context.Background(), "devices", TestProxyDevice); err != nil {
+	if err := TestProxyRootDevice.Set(context.Background(), "devices", TestProxyDevice); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to add device to test proxy due to error: %v", err)
 		assert.NotNil(t, err)
 	}
 
 	d := &voltha.Device{}
-	if have, err := TestProxyRootDevice.Get(context.Background(), "devices/"+TestProxyDeviceID, d); err != nil {
+	if have, err := TestProxyRootDevice.Get(context.Background(), TestProxyDeviceID, d); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed get device info from test proxy due to error: %v", err)
 		assert.NotNil(t, err)
 	} else if !have {
@@ -217,7 +219,7 @@
 	TestProxyAdapter.Id = TestProxyAdapterID
 
 	// Add the adapter
-	if err := TestProxyRootAdapter.AddWithID(context.Background(), "adapters", TestProxyAdapterID, TestProxyAdapter); err != nil {
+	if err := TestProxyRootAdapter.Set(context.Background(), TestProxyAdapterID, TestProxyAdapter); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to add adapter to test proxy due to error: %v", err)
 		assert.NotNil(t, err)
 	} else {
@@ -226,7 +228,7 @@
 
 	// Verify that the added device can now be retrieved
 	d := &voltha.Device{}
-	if have, err := TestProxyRootAdapter.Get(context.Background(), "adapters/"+TestProxyAdapterID, d); err != nil {
+	if have, err := TestProxyRootAdapter.Get(context.Background(), TestProxyAdapterID, d); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to retrieve device info from test proxy due to error: %v", err)
 		assert.NotNil(t, err)
 	} else if !have {
@@ -240,7 +242,7 @@
 
 func TestProxy_1_2_1_Get_AllDevices(t *testing.T) {
 	var devices []*voltha.Device
-	if err := TestProxyRootDevice.List(context.Background(), "devices", &devices); err != nil {
+	if err := TestProxyRootDevice.List(context.Background(), &devices); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to get all devices info from test proxy due to error: %v", err)
 		assert.NotNil(t, err)
 	}
@@ -255,7 +257,7 @@
 
 func TestProxy_1_2_2_Get_SingleDevice(t *testing.T) {
 	d := &voltha.Device{}
-	if have, err := TestProxyRootDevice.Get(context.Background(), "devices/"+TestProxyTargetDeviceID, d); err != nil {
+	if have, err := TestProxyRootDevice.Get(context.Background(), TestProxyTargetDeviceID, d); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to get single device info from test proxy due to error: %v", err)
 		assert.NotNil(t, err)
 	} else if !have {
@@ -270,7 +272,7 @@
 	var fwVersion int
 
 	retrieved := &voltha.Device{}
-	if have, err := TestProxyRootDevice.Get(context.Background(), "devices/"+TestProxyTargetDeviceID, retrieved); err != nil {
+	if have, err := TestProxyRootDevice.Get(context.Background(), TestProxyTargetDeviceID, retrieved); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to get device info from test proxy due to error: %v", err)
 		assert.NotNil(t, err)
 	} else if !have {
@@ -287,12 +289,12 @@
 
 		retrieved.FirmwareVersion = strconv.Itoa(fwVersion)
 
-		if err := TestProxyRootDevice.Update(context.Background(), "devices/"+TestProxyTargetDeviceID, retrieved); err != nil {
+		if err := TestProxyRootDevice.Set(context.Background(), TestProxyTargetDeviceID, retrieved); err != nil {
 			BenchmarkProxyLogger.Errorf("Failed to update device info test proxy due to error: %v", err)
 			assert.NotNil(t, err)
 		}
 		afterUpdate := &voltha.Device{}
-		if have, err := TestProxyRootDevice.Get(context.Background(), "devices/"+TestProxyTargetDeviceID, afterUpdate); err != nil {
+		if have, err := TestProxyRootDevice.Get(context.Background(), TestProxyTargetDeviceID, afterUpdate); err != nil {
 			BenchmarkProxyLogger.Errorf("Failed to get device info from test proxy due to error: %v", err)
 		} else if !have {
 			t.Error("Failed to update device")
@@ -301,7 +303,7 @@
 		}
 
 		d := &voltha.Device{}
-		if have, err := TestProxyRootDevice.Get(context.Background(), "devices/"+TestProxyTargetDeviceID, d); err != nil {
+		if have, err := TestProxyRootDevice.Get(context.Background(), TestProxyTargetDeviceID, d); err != nil {
 			BenchmarkProxyLogger.Errorf("Failed to get device info from test proxy due to error: %v", err)
 			assert.NotNil(t, err)
 		} else if !have {
@@ -315,10 +317,10 @@
 
 func TestProxy_1_3_3_Update_Adapter(t *testing.T) {
 
-	adaptersProxy := NewProxy(mockBackend, "/adapters")
+	adaptersProxy := NewDBPath(mockBackend).Proxy("adapters")
 
 	retrieved := &voltha.Adapter{}
-	if have, err := TestProxyRootAdapter.Get(context.Background(), "adapters/"+TestProxyAdapterID, retrieved); err != nil {
+	if have, err := TestProxyRootAdapter.Get(context.Background(), TestProxyAdapterID, retrieved); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to retrieve adapter info from adapters proxy due to error: %v", err)
 		assert.NotNil(t, err)
 	} else if !have {
@@ -328,7 +330,7 @@
 
 		retrieved.Version = "test-adapter-version-2"
 
-		if err := adaptersProxy.Update(context.Background(), TestProxyAdapterID, retrieved); err != nil {
+		if err := adaptersProxy.Set(context.Background(), TestProxyAdapterID, retrieved); err != nil {
 			BenchmarkProxyLogger.Errorf("Failed to update adapter info in adapters proxy due to error: %v", err)
 			assert.NotNil(t, err)
 		} else {
@@ -336,7 +338,7 @@
 		}
 
 		d := &voltha.Adapter{}
-		if have, err := TestProxyRootAdapter.Get(context.Background(), "adapters/"+TestProxyAdapterID, d); err != nil {
+		if have, err := TestProxyRootAdapter.Get(context.Background(), TestProxyAdapterID, d); err != nil {
 			BenchmarkProxyLogger.Errorf("Failed to get updated adapter info from adapters proxy due to error: %v", err)
 			assert.NotNil(t, err)
 		} else if !have {
@@ -349,7 +351,7 @@
 }
 
 func TestProxy_1_4_1_Remove_Device(t *testing.T) {
-	if err := TestProxyRootDevice.Remove(context.Background(), "devices/"+TestProxyDeviceID); err != nil {
+	if err := TestProxyRootDevice.Remove(context.Background(), TestProxyDeviceID); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to remove device from devices proxy due to error: %v", err)
 		t.Errorf("failed to remove device: %s", err)
 	} else {
@@ -357,7 +359,7 @@
 	}
 
 	d := &voltha.Device{}
-	have, err := TestProxyRootDevice.Get(context.Background(), "devices/"+TestProxyDeviceID, d)
+	have, err := TestProxyRootDevice.Get(context.Background(), TestProxyDeviceID, d)
 	if err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to get device info from devices proxy due to error: %v", err)
 		assert.NotNil(t, err)
@@ -375,7 +377,7 @@
 	TestProxyLogicalDeviceID = "0001" + hex.EncodeToString(ldIDBin)[:12]
 	TestProxyLogicalDevice.Id = TestProxyLogicalDeviceID
 
-	if err := TestProxyRootLogicalDevice.AddWithID(context.Background(), "logical_devices", TestProxyLogicalDeviceID, TestProxyLogicalDevice); err != nil {
+	if err := TestProxyRootLogicalDevice.Set(context.Background(), TestProxyLogicalDeviceID, TestProxyLogicalDevice); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to add new logical device into proxy due to error: %v", err)
 		assert.NotNil(t, err)
 	} else {
@@ -383,7 +385,7 @@
 	}
 
 	ld := &voltha.LogicalDevice{}
-	if have, err := TestProxyRootLogicalDevice.Get(context.Background(), "logical_devices/"+TestProxyLogicalDeviceID, ld); err != nil {
+	if have, err := TestProxyRootLogicalDevice.Get(context.Background(), TestProxyLogicalDeviceID, ld); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to get logical device info from logical device proxy due to error: %v", err)
 		assert.NotNil(t, err)
 	} else if !have {
@@ -397,7 +399,7 @@
 func TestProxy_2_1_2_Add_ExistingLogicalDevice(t *testing.T) {
 	TestProxyLogicalDevice.Id = TestProxyLogicalDeviceID
 
-	if err := TestProxyRootLogicalDevice.add(context.Background(), "logical_devices", TestProxyLogicalDevice); err != nil {
+	if err := TestProxyRootLogicalDevice.Set(context.Background(), "logical_devices", TestProxyLogicalDevice); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to add logical device due to error: %v", err)
 		assert.NotNil(t, err)
 	}
@@ -417,7 +419,7 @@
 
 func TestProxy_2_2_1_Get_AllLogicalDevices(t *testing.T) {
 	var logicalDevices []*voltha.LogicalDevice
-	if err := TestProxyRootLogicalDevice.List(context.Background(), "logical_devices", &logicalDevices); err != nil {
+	if err := TestProxyRootLogicalDevice.List(context.Background(), &logicalDevices); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to get all logical devices from proxy due to error: %v", err)
 		assert.NotNil(t, err)
 	}
@@ -432,7 +434,7 @@
 
 func TestProxy_2_2_2_Get_SingleLogicalDevice(t *testing.T) {
 	ld := &voltha.LogicalDevice{}
-	if have, err := TestProxyRootLogicalDevice.Get(context.Background(), "logical_devices/"+TestProxyTargetLogicalDeviceID, ld); err != nil {
+	if have, err := TestProxyRootLogicalDevice.Get(context.Background(), TestProxyTargetLogicalDeviceID, ld); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to get single logical device from proxy due to error: %v", err)
 		assert.NotNil(t, err)
 	} else if !have {
@@ -448,7 +450,7 @@
 	var fwVersion int
 
 	retrieved := &voltha.LogicalDevice{}
-	if have, err := TestProxyRootLogicalDevice.Get(context.Background(), "logical_devices/"+TestProxyTargetLogicalDeviceID, retrieved); err != nil {
+	if have, err := TestProxyRootLogicalDevice.Get(context.Background(), TestProxyTargetLogicalDeviceID, retrieved); err != nil {
 		BenchmarkProxyLogger.Errorf("Failed to get logical devices due to error: %v", err)
 		assert.NotNil(t, err)
 	} else if !have {
@@ -465,7 +467,7 @@
 
 		retrieved.RootDeviceId = strconv.Itoa(fwVersion)
 
-		if err := TestProxyRootLogicalDevice.Update(context.Background(), "logical_devices/"+TestProxyTargetLogicalDeviceID, retrieved); err != nil {
+		if err := TestProxyRootLogicalDevice.Set(context.Background(), TestProxyTargetLogicalDeviceID, retrieved); err != nil {
 			BenchmarkProxyLogger.Errorf("Faield to update logical device info due to error: %v", err)
 			assert.NotNil(t, err)
 		} else {
@@ -473,7 +475,7 @@
 		}
 
 		d := &voltha.LogicalDevice{}
-		if have, err := TestProxyRootLogicalDevice.Get(context.Background(), "logical_devices/"+TestProxyTargetLogicalDeviceID, d); err != nil {
+		if have, err := TestProxyRootLogicalDevice.Get(context.Background(), TestProxyTargetLogicalDeviceID, d); err != nil {
 			BenchmarkProxyLogger.Errorf("Failed to get logical device info due to error: %v", err)
 			assert.NotNil(t, err)
 		} else if !have {