VOL-1175: Added proxy CRUD for new data model

Change-Id: Ie218a2567746d87a951f23aa6b774b2f01541cf9
diff --git a/db/model/node.go b/db/model/node.go
index eacbec7..90ab666 100644
--- a/db/model/node.go
+++ b/db/model/node.go
@@ -18,6 +18,7 @@
 import (
 	"fmt"
 	"github.com/golang/protobuf/proto"
+	"github.com/opencord/voltha-go/common/log"
 	"reflect"
 	"strings"
 )
@@ -30,55 +31,55 @@
 	root      *Root
 	Type      interface{}
 	Branches  map[string]*Branch
-	Tags      map[string]*Revision
+	Tags      map[string]Revision
 	Proxy     *Proxy
 	EventBus  *EventBus
 	AutoPrune bool
 }
 
 func NewNode(root *Root, initialData interface{}, autoPrune bool, txid string) *Node {
-	cn := &Node{}
+	n := &Node{}
 
-	cn.root = root
-	cn.Branches = make(map[string]*Branch)
-	cn.Tags = make(map[string]*Revision)
-	cn.Proxy = nil
-	cn.EventBus = nil
-	cn.AutoPrune = autoPrune
+	n.root = root
+	n.Branches = make(map[string]*Branch)
+	n.Tags = make(map[string]Revision)
+	n.Proxy = nil
+	n.EventBus = nil
+	n.AutoPrune = autoPrune
 
 	if IsProtoMessage(initialData) {
-		cn.Type = reflect.ValueOf(initialData).Interface()
+		n.Type = reflect.ValueOf(initialData).Interface()
 		dataCopy := proto.Clone(initialData.(proto.Message))
-		cn.initialize(dataCopy, txid)
+		n.initialize(dataCopy, txid)
 	} else if reflect.ValueOf(initialData).IsValid() {
-		cn.Type = reflect.ValueOf(initialData).Interface()
+		n.Type = reflect.ValueOf(initialData).Interface()
 	} else {
 		// not implemented error
 		fmt.Errorf("cannot process initial data - %+v", initialData)
 	}
 
-	return cn
+	return n
 }
 
-func (cn *Node) makeNode(data interface{}, txid string) *Node {
-	return NewNode(cn.root, data, true, txid)
+func (n *Node) MakeNode(data interface{}, txid string) *Node {
+	return NewNode(n.root, data, true, txid)
 }
 
-func (cn *Node) makeRevision(branch *Branch, data interface{}, children map[string][]*Revision) *Revision {
-	return cn.root.makeRevision(branch, data, children)
+func (n *Node) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
+	return n.root.MakeRevision(branch, data, children)
 }
 
-func (cn *Node) makeLatest(branch *Branch, revision *Revision, changeAnnouncement map[string]interface{}) {
-	if _, ok := branch.revisions[revision.Hash]; !ok {
-		branch.revisions[revision.Hash] = revision
+func (n *Node) MakeLatest(branch *Branch, revision Revision, changeAnnouncement map[CallbackType][]interface{}) {
+	if _, ok := branch.Revisions[revision.GetHash()]; !ok {
+		branch.Revisions[revision.GetHash()] = revision
 	}
 
-	if branch.Latest == nil || revision.Hash != branch.Latest.Hash {
+	if branch.Latest == nil || revision.GetHash() != branch.Latest.GetHash() {
 		branch.Latest = revision
 	}
 
 	if changeAnnouncement != nil && branch.Txid == "" {
-		if cn.Proxy != nil {
+		if n.Proxy != nil {
 			for changeType, data := range changeAnnouncement {
 				// TODO: Invoke callback
 				fmt.Printf("invoking callback - changeType: %+v, data:%+v\n", changeType, data)
@@ -92,22 +93,22 @@
 	}
 }
 
-func (cn *Node) Latest() *Revision {
-	if branch, exists := cn.Branches[NONE]; exists {
+func (n *Node) Latest() Revision {
+	if branch, exists := n.Branches[NONE]; exists {
 		return branch.Latest
 	}
 	return nil
 }
 
-func (cn *Node) GetHash(hash string) *Revision {
-	return cn.Branches[NONE].revisions[hash]
+func (n *Node) GetHash(hash string) Revision {
+	return n.Branches[NONE].Revisions[hash]
 }
 
-func (cn *Node) initialize(data interface{}, txid string) {
-	var children map[string][]*Revision
-	children = make(map[string][]*Revision)
-	for fieldName, field := range ChildrenFields(cn.Type) {
-		fieldValue := GetAttributeValue(data, fieldName, 0)
+func (n *Node) initialize(data interface{}, txid string) {
+	var children map[string][]Revision
+	children = make(map[string][]Revision)
+	for fieldName, field := range ChildrenFields(n.Type) {
+		_, fieldValue := GetAttributeValue(data, fieldName, 0)
 
 		if fieldValue.IsValid() {
 			if field.IsContainer {
@@ -116,8 +117,8 @@
 
 					for i := 0; i < fieldValue.Len(); i++ {
 						v := fieldValue.Index(i)
-						rev := cn.makeNode(v.Interface(), txid).Latest()
-						key := GetAttributeValue(v.Interface(), field.Key, 0)
+						rev := n.MakeNode(v.Interface(), txid).Latest()
+						_, key := GetAttributeValue(v.Interface(), field.Key, 0)
 						for _, k := range keysSeen {
 							if k == key.String() {
 								fmt.Errorf("duplicate key - %s", k)
@@ -130,11 +131,11 @@
 				} else {
 					for i := 0; i < fieldValue.Len(); i++ {
 						v := fieldValue.Index(i)
-						children[fieldName] = append(children[fieldName], cn.makeNode(v.Interface(), txid).Latest())
+						children[fieldName] = append(children[fieldName], n.MakeNode(v.Interface(), txid).Latest())
 					}
 				}
 			} else {
-				children[fieldName] = append(children[fieldName], cn.makeNode(fieldValue.Interface(), txid).Latest())
+				children[fieldName] = append(children[fieldName], n.MakeNode(fieldValue.Interface(), txid).Latest())
 			}
 		} else {
 			fmt.Errorf("field is invalid - %+v", fieldValue)
@@ -142,29 +143,21 @@
 	}
 	// FIXME: ClearField???  No such method in go protos.  Reset?
 	//data.ClearField(field_name)
-	branch := NewBranch(cn, "", nil, cn.AutoPrune)
-	rev := cn.makeRevision(branch, data, children)
-	cn.makeLatest(branch, rev, nil)
-	cn.Branches[txid] = branch
-}
+	branch := NewBranch(n, "", nil, n.AutoPrune)
+	rev := n.MakeRevision(branch, data, children)
+	n.MakeLatest(branch, rev, nil)
 
-func (cn *Node) makeTxBranch(txid string) *Branch {
-	branchPoint := cn.Branches[NONE].Latest
-	branch := NewBranch(cn, txid, branchPoint, true)
-	cn.Branches[txid] = branch
-	return branch
+	if txid == "" {
+		n.Branches[NONE] = branch
+	} else {
+		n.Branches[txid] = branch
+	}
 }
 
-func (cn *Node) deleteTxBranch(txid string) {
-	delete(cn.Branches, txid)
-}
-
-type t_makeBranch func(*Node) *Branch
-
 //
 // Get operation
 //
-func (cn *Node) Get(path string, hash string, depth int, deep bool, txid string) interface{} {
+func (n *Node) Get(path string, hash string, depth int, deep bool, txid string) interface{} {
 	if deep {
 		depth = -1
 	}
@@ -174,26 +167,26 @@
 	}
 
 	var branch *Branch
-	var rev *Revision
+	var rev Revision
 
 	// FIXME: should empty txid be cleaned up?
-	if branch = cn.Branches[txid]; txid == "" || branch == nil {
-		branch = cn.Branches[NONE]
+	if branch = n.Branches[txid]; txid == "" || branch == nil {
+		branch = n.Branches[NONE]
 	}
 
 	if hash != "" {
-		rev = branch.revisions[hash]
+		rev = branch.Revisions[hash]
 	} else {
 		rev = branch.Latest
 	}
 
-	return cn.get(rev, path, depth)
+	return n.get(rev, path, depth)
 }
 
-func (cn *Node) findRevByKey(revs []*Revision, keyName string, value string) (int, *Revision) {
+func (n *Node) findRevByKey(revs []Revision, keyName string, value string) (int, Revision) {
 	for i, rev := range revs {
-		dataValue := reflect.ValueOf(rev.Config.Data)
-		dataStruct := GetAttributeStructure(rev.Config.Data, keyName, 0)
+		dataValue := reflect.ValueOf(rev.GetData())
+		dataStruct := GetAttributeStructure(rev.GetData(), keyName, 0)
 
 		fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
 
@@ -207,50 +200,72 @@
 	return -1, nil
 }
 
-func (cn *Node) get(rev *Revision, path string, depth int) interface{} {
+func (n *Node) get(rev Revision, path string, depth int) interface{} {
 	if path == "" {
-		return cn.doGet(rev, depth)
+		return n.doGet(rev, depth)
 	}
 
 	partition := strings.SplitN(path, "/", 2)
 	name := partition[0]
-	path = partition[1]
 
-	field := ChildrenFields(cn.Type)[name]
+	if len(partition) < 2 {
+		path = ""
+	} else {
+		path = partition[1]
+	}
 
-	if field.IsContainer {
+	names := ChildrenFields(n.Type)
+	field := names[name]
+
+	if field != nil && field.IsContainer {
 		if field.Key != "" {
-			children := rev.Children[name]
+			children := rev.GetChildren()[name]
 			if path != "" {
 				partition = strings.SplitN(path, "/", 2)
 				key := partition[0]
 				path = ""
 				key = field.KeyFromStr(key).(string)
-				_, childRev := cn.findRevByKey(children, field.Key, key)
-				childNode := childRev.getNode()
-				return childNode.get(childRev, path, depth)
+				if _, childRev := n.findRevByKey(children, field.Key, key); childRev == nil {
+					return nil
+				} else {
+					childNode := childRev.GetNode()
+					return childNode.get(childRev, path, depth)
+				}
 			} else {
 				var response []interface{}
 				for _, childRev := range children {
-					childNode := childRev.getNode()
+					childNode := childRev.GetNode()
 					value := childNode.doGet(childRev, depth)
 					response = append(response, value)
 				}
 				return response
 			}
 		} else {
-			childRev := rev.Children[name][0]
-			childNode := childRev.getNode()
-			return childNode.get(childRev, path, depth)
+			var response []interface{}
+			if path != "" {
+				// TODO: raise error
+				return response
+			}
+			for _, childRev := range rev.GetChildren()[name] {
+				childNode := childRev.GetNode()
+				value := childNode.doGet(childRev, depth)
+				response = append(response, value)
+			}
+			return response
 		}
+	} else {
+		c1 := rev.GetChildren()[name]
+		childRev := c1[0]
+		childNode := childRev.GetNode()
+		return childNode.get(childRev, path, depth)
 	}
 	return nil
 }
 
-func (cn *Node) doGet(rev *Revision, depth int) interface{} {
+func (n *Node) doGet(rev Revision, depth int) interface{} {
 	msg := rev.Get(depth)
 
-	if cn.Proxy != nil {
+	if n.Proxy != nil {
 		// TODO: invoke GET callback
 		fmt.Println("invoking proxy GET Callbacks")
 	}
@@ -260,7 +275,7 @@
 //
 // Update operation
 //
-func (n *Node) Update(path string, data interface{}, strict bool, txid string, makeBranch t_makeBranch) *Revision {
+func (n *Node) Update(path string, data interface{}, strict bool, txid string, makeBranch t_makeBranch) Revision {
 	// FIXME: is this required ... a bit overkill to take out a "/"
 	for strings.HasPrefix(path, "/") {
 		path = path[1:]
@@ -268,63 +283,117 @@
 
 	var branch *Branch
 	var ok bool
-	if branch, ok = n.Branches[txid]; !ok {
+	if txid == "" {
+		branch = n.Branches[NONE]
+	} else if branch, ok = n.Branches[txid]; !ok {
 		branch = makeBranch(n)
 	}
 
+	log.Debugf("Branch data : %+v, Passed data: %+v", branch.Latest.GetData(), data)
+
 	if path == "" {
 		return n.doUpdate(branch, data, strict)
 	}
-	return &Revision{}
-}
 
-func (n *Node) doUpdate(branch *Branch, data interface{}, strict bool) *Revision {
-	if reflect.TypeOf(data) != n.Type {
-		// TODO raise error
-		fmt.Errorf("data does not match type: %+v", n.Type)
+	// TODO missing some code here...
+	rev := branch.Latest
+
+	partition := strings.SplitN(path, "/", 2)
+	name := partition[0]
+
+	if len(partition) < 2 {
+		path = ""
+	} else {
+		path = partition[1]
 	}
 
-	// TODO: noChildren?
+	field := ChildrenFields(n.Type)[name]
+	var children []Revision
+
+	if field.IsContainer {
+		if path == "" {
+			fmt.Errorf("cannot update a list\n")
+		} else if field.Key != "" {
+			partition := strings.SplitN(path, "/", 2)
+			key := partition[0]
+			if len(partition) < 2 {
+				path = ""
+			} else {
+				path = partition[1]
+			}
+			key = field.KeyFromStr(key).(string)
+			// TODO. Est-ce que le copy ne fonctionne pas? dois-je plutôt faire un clone de chaque item?
+			for _, v := range rev.GetChildren()[name] {
+				revCopy := reflect.ValueOf(v).Interface().(Revision)
+				children = append(children, revCopy)
+			}
+			idx, childRev := n.findRevByKey(children, field.Key, key)
+			childNode := childRev.GetNode()
+			newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
+			if newChildRev.GetHash() == childRev.GetHash() {
+				if newChildRev != childRev {
+					log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
+					newChildRev.ClearHash()
+				}
+				return branch.Latest
+			}
+			if _, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0); newKey.Interface().(string) != key {
+				fmt.Errorf("cannot change key field\n")
+			}
+			children[idx] = newChildRev
+			rev = rev.UpdateChildren(name, children, branch)
+			n.root.MakeLatest(branch, rev, nil)
+			return rev
+		} else {
+			fmt.Errorf("cannot index into container with no keys\n")
+		}
+	} else {
+		childRev := rev.GetChildren()[name][0]
+		childNode := childRev.GetNode()
+		newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
+		rev = rev.UpdateChildren(name, []Revision{newChildRev}, branch)
+		n.root.MakeLatest(branch, rev, nil)
+		return rev
+	}
+	return nil
+}
+
+func (n *Node) doUpdate(branch *Branch, data interface{}, strict bool) Revision {
+	log.Debugf("Comparing types - expected: %+v, actual: %+v", reflect.ValueOf(n.Type).Type(), reflect.TypeOf(data))
+
+	if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
+		// TODO raise error
+		fmt.Errorf("data does not match type: %+v", n.Type)
+		return nil
+	}
+
+	// TODO: validate that this actually works
+	//if n.hasChildren(data) {
+	//	return nil
+	//}
 
 	if n.Proxy != nil {
 		// TODO: n.proxy.InvokeCallbacks(CallbackType.PRE_UPDATE, data)
 		fmt.Println("invoking proxy PRE_UPDATE Callbacks")
 	}
-	if branch.Latest.getData() != data {
+	if !reflect.DeepEqual(branch.Latest.GetData(), data) {
 		if strict {
-			// TODO: checkAccessViolations(data, branch.GetLatest.data)
+			// TODO: checkAccessViolations(data, Branch.GetLatest.data)
 			fmt.Println("checking access violations")
 		}
 		rev := branch.Latest.UpdateData(data, branch)
-		n.makeLatest(branch, rev, nil) // TODO -> changeAnnouncement needs to be a tuple (CallbackType.POST_UPDATE, rev.data)
+		n.root.MakeLatest(branch, rev, nil) // TODO -> changeAnnouncement needs to be a tuple (CallbackType.
+		// POST_UPDATE, rev.data)
 		return rev
 	} else {
 		return branch.Latest
 	}
 }
 
-// TODO: the python implementation has a method to check if the data has no children
-//func (n *SomeNode) noChildren(data interface{}) bool {
-//	for fieldName, field := range ChildrenFields(n.Type) {
-//		fieldValue := GetAttributeValue(data, fieldName)
-//
-//		if fieldValue.IsValid() {
-//			if field.IsContainer {
-//				if len(fieldValue) > 0 {
-//
-//				}
-//			} else {
-//
-//			}
-//
-//		}
-//	}
-//}
-
 //
 // Add operation
 //
-func (n *Node) Add(path string, data interface{}, txid string, makeBranch t_makeBranch) *Revision {
+func (n *Node) Add(path string, data interface{}, txid string, makeBranch t_makeBranch) Revision {
 	for strings.HasPrefix(path, "/") {
 		path = path[1:]
 	}
@@ -335,7 +404,9 @@
 
 	var branch *Branch
 	var ok bool
-	if branch, ok = n.Branches[txid]; !ok {
+	if txid == "" {
+		branch = n.Branches[NONE]
+	} else if branch, ok = n.Branches[txid]; !ok {
 		branch = makeBranch(n)
 	}
 
@@ -343,10 +414,15 @@
 
 	partition := strings.SplitN(path, "/", 2)
 	name := partition[0]
-	path = partition[1]
+
+	if len(partition) < 2 {
+		path = ""
+	} else {
+		path = partition[1]
+	}
 
 	field := ChildrenFields(n.Type)[name]
-	var children []*Revision
+	var children []Revision
 
 	if field.IsContainer {
 		if path == "" {
@@ -356,17 +432,21 @@
 					fmt.Println("invoking proxy PRE_ADD Callbacks")
 				}
 
-				copy(children, rev.Children[name])
-				key := GetAttributeValue(data, field.Key, 0)
+				for _, v := range rev.GetChildren()[name] {
+					revCopy := reflect.ValueOf(v).Interface().(Revision)
+					children = append(children, revCopy)
+				}
+				_, key := GetAttributeValue(data, field.Key, 0)
 				if _, rev := n.findRevByKey(children, field.Key, key.String()); rev != nil {
 					// TODO raise error
 					fmt.Errorf("duplicate key found: %s", key.String())
 				}
 
-				childRev := n.makeNode(data, "").Latest()
+				childRev := n.MakeNode(data, "").Latest()
 				children = append(children, childRev)
 				rev := rev.UpdateChildren(name, children, branch)
-				n.makeLatest(branch, rev, nil) // TODO -> changeAnnouncement needs to be a tuple (CallbackType.POST_ADD, rev.data)
+				n.root.MakeLatest(branch, rev, nil) // TODO -> changeAnnouncement needs to be a tuple (CallbackType.
+				// POST_ADD, rev.data)
 				return rev
 			} else {
 				fmt.Errorf("cannot add to non-keyed container\n")
@@ -374,15 +454,19 @@
 		} else if field.Key != "" {
 			partition := strings.SplitN(path, "/", 2)
 			key := partition[0]
-			path = partition[1]
+			if len(partition) < 2 {
+				path = ""
+			} else {
+				path = partition[1]
+			}
 			key = field.KeyFromStr(key).(string)
-			copy(children, rev.Children[name])
+			copy(children, rev.GetChildren()[name])
 			idx, childRev := n.findRevByKey(children, field.Key, key)
-			childNode := childRev.getNode()
+			childNode := childRev.GetNode()
 			newChildRev := childNode.Add(path, data, txid, makeBranch)
 			children[idx] = newChildRev
 			rev := rev.UpdateChildren(name, children, branch)
-			n.makeLatest(branch, rev, nil)
+			n.root.MakeLatest(branch, rev, nil)
 			return rev
 		} else {
 			fmt.Errorf("cannot add to non-keyed container\n")
@@ -396,7 +480,7 @@
 //
 // Remove operation
 //
-func (n *Node) Remove(path string, txid string, makeBranch t_makeBranch) *Revision {
+func (n *Node) Remove(path string, txid string, makeBranch t_makeBranch) Revision {
 	for strings.HasPrefix(path, "/") {
 		path = path[1:]
 	}
@@ -406,7 +490,9 @@
 	}
 	var branch *Branch
 	var ok bool
-	if branch, ok = n.Branches[txid]; !ok {
+	if txid == "" {
+		branch = n.Branches[NONE]
+	} else if branch, ok = n.Branches[txid]; !ok {
 		branch = makeBranch(n)
 	}
 
@@ -414,11 +500,15 @@
 
 	partition := strings.SplitN(path, "/", 2)
 	name := partition[0]
-	path = partition[1]
+	if len(partition) < 2 {
+		path = ""
+	} else {
+		path = partition[1]
+	}
 
 	field := ChildrenFields(n.Type)[name]
-	var children []*Revision
-	post_anno := make(map[string]interface{})
+	var children []Revision
+	post_anno := make(map[CallbackType][]interface{})
 
 	if field.IsContainer {
 		if path == "" {
@@ -426,22 +516,32 @@
 		} else if field.Key != "" {
 			partition := strings.SplitN(path, "/", 2)
 			key := partition[0]
-			path = partition[1]
+			if len(partition) < 2 {
+				path = ""
+			} else {
+				path = partition[1]
+			}
 			key = field.KeyFromStr(key).(string)
 			if path != "" {
-				copy(children, rev.Children[name])
+				for _, v := range rev.GetChildren()[name] {
+					newV := reflect.ValueOf(v).Interface().(Revision)
+					children = append(children, newV)
+				}
 				idx, childRev := n.findRevByKey(children, field.Key, key)
-				childNode := childRev.getNode()
+				childNode := childRev.GetNode()
 				newChildRev := childNode.Remove(path, txid, makeBranch)
 				children[idx] = newChildRev
 				rev := rev.UpdateChildren(name, children, branch)
-				n.makeLatest(branch, rev, nil)
+				n.root.MakeLatest(branch, rev, nil)
 				return rev
 			} else {
-				copy(children, rev.Children[name])
+				for _, v := range rev.GetChildren()[name] {
+					newV := reflect.ValueOf(v).Interface().(Revision)
+					children = append(children, newV)
+				}
 				idx, childRev := n.findRevByKey(children, field.Key, key)
 				if n.Proxy != nil {
-					data := childRev.getData()
+					data := childRev.GetData()
 					fmt.Println("invoking proxy PRE_REMOVE Callbacks")
 					fmt.Printf("setting POST_REMOVE Callbacks : %+v\n", data)
 				} else {
@@ -449,7 +549,7 @@
 				}
 				children = append(children[:idx], children[idx+1:]...)
 				rev := rev.UpdateChildren(name, children, branch)
-				n.makeLatest(branch, rev, post_anno)
+				n.root.MakeLatest(branch, rev, post_anno)
 				return rev
 			}
 		} else {
@@ -462,10 +562,154 @@
 	return nil
 }
 
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+type t_makeBranch func(*Node) *Branch
+
+func (n *Node) makeTxBranch(txid string) *Branch {
+	branchPoint := n.Branches[NONE].Latest
+	branch := NewBranch(n, txid, branchPoint, true)
+	n.Branches[txid] = branch
+	return branch
+}
+
+func (n *Node) deleteTxBranch(txid string) {
+	delete(n.Branches, txid)
+}
+
+func (n *Node) mergeChild(txid string, dryRun bool) func(Revision) Revision {
+	f := func(rev Revision) Revision {
+		childBranch := rev.GetBranch()
+
+		if childBranch.Txid == txid {
+			rev = childBranch.Node.mergeTxBranch(txid, dryRun)
+		}
+
+		return rev
+	}
+	return f
+}
+
+func (n *Node) mergeTxBranch(txid string, dryRun bool) Revision {
+	srcBranch := n.Branches[txid]
+	dstBranch := n.Branches[NONE]
+
+	forkRev := srcBranch.Origin
+	srcRev := srcBranch.Latest
+	dstRev := dstBranch.Latest
+
+	rev, changes := Merge3Way(forkRev, srcRev, dstRev, n.mergeChild(txid, dryRun), dryRun)
+
+	if !dryRun {
+		n.root.MakeLatest(dstBranch, rev, changes)
+		delete(n.Branches, txid)
+	}
+
+	return rev
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diff utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+//func (n *Node) diff(hash1, hash2, txid string) {
+//	branch := n.Branches[txid]
+//	rev1 := branch.get(hash1)
+//	rev2 := branch.get(hash2)
+//
+//	if rev1.GetHash() == rev2.GetHash() {
+//		// empty patch
+//	} else {
+//		// translate data to json and generate patch
+//		patch, err := jsonpatch.MakePatch(rev1.GetData(), rev2.GetData())
+//		patch.
+//	}
+//}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tag utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+// TODO: is tag mgmt used in the python implementation? Need to validate
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+func (n *Node) hasChildren(data interface{}) bool {
+	for fieldName, field := range ChildrenFields(n.Type) {
+		_, fieldValue := GetAttributeValue(data, fieldName, 0)
+
+		if (field.IsContainer && fieldValue.Len() > 0) || !fieldValue.IsNil() {
+			log.Error("cannot update external children")
+			return true
+		}
+	}
+
+	return false
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Node Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+func (n *Node) GetProxy(path string, exclusive bool) *Proxy {
+	return n.getProxy(path, n.root, path, exclusive)
+}
+func (n *Node) getProxy(path string, root *Root, fullPath string, exclusive bool) *Proxy {
+	for strings.HasPrefix(path, "/") {
+		path = path[1:]
+	}
+	if path == "" {
+		return n.makeProxy(n.root, path, exclusive)
+	}
+
+	rev := n.Branches[NONE].Latest
+	partition := strings.SplitN(path, "/", 2)
+	name := partition[0]
+	path = partition[1]
+
+	field := ChildrenFields(n.Type)[name]
+	if field.IsContainer {
+		if path == "" {
+			log.Error("cannot proxy a container field")
+		}
+		if field.Key != "" {
+			partition := strings.SplitN(path, "/", 2)
+			key := partition[0]
+			path = partition[1]
+			key = field.KeyFromStr(key).(string)
+			children := rev.GetChildren()[name]
+			_, childRev := n.findRevByKey(children, field.Key, key)
+			childNode := childRev.GetNode()
+			return childNode.getProxy(path, root, fullPath, exclusive)
+		}
+		log.Error("cannot index into container with no keys")
+	} else {
+		childRev := rev.GetChildren()[name][0]
+		childNode := childRev.GetNode()
+		return childNode.getProxy(path, root, fullPath, exclusive)
+	}
+
+	return nil
+}
+
+func (n *Node) makeProxy(root *Root, fullPath string, exclusive bool) *Proxy {
+	if n.Proxy == nil {
+		n.Proxy = NewProxy(root, n, fullPath, exclusive)
+	} else {
+		if n.Proxy.Exclusive {
+			log.Error("node is already owned exclusively")
+		}
+	}
+	return n.Proxy
+}
+
+func (n *Node) makeEventBus() *EventBus {
+	if n.EventBus == nil {
+		n.EventBus = NewEventBus()
+	}
+	return n.EventBus
+}
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Persistence Loading ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
 func (n *Node) LoadLatest(kvStore *Backend, hash string) {
 	branch := NewBranch(n, "", nil, n.AutoPrune)
 	pr := &PersistedRevision{}
-	rev := pr.load(branch, kvStore, n.Type, hash)
-	n.makeLatest(branch, rev.Revision, nil)
+	rev := pr.Load(branch, kvStore, n.Type, hash)
+	n.MakeLatest(branch, rev, nil)
 	n.Branches[NONE] = branch
 }