blob: fcd3b5f7f39189acc8986a16262de323ee92d57b [file] [log] [blame]
khenaidoobf6e7bb2018-08-14 22:27:29 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Stephane Barbariedc5022d2018-11-19 15:21:44 -050016
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040017package model
18
Stephane Barbariee16186c2018-09-11 10:46:34 -040019// TODO: proper error handling
20// TODO: proper logging
21
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040022import (
Stephane Barbarieef6650d2019-07-18 12:15:09 -040023 "context"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040024 "fmt"
25 "github.com/golang/protobuf/proto"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040026 "github.com/opencord/voltha-go/common/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040027 "reflect"
28 "strings"
Stephane Barbariedc5022d2018-11-19 15:21:44 -050029 "sync"
Stephane Barbarie802aca42019-05-21 12:19:28 -040030 "time"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040031)
32
Stephane Barbariedc5022d2018-11-19 15:21:44 -050033// When a branch has no transaction id, everything gets stored in NONE
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040034const (
35 NONE string = "none"
36)
37
Stephane Barbariedc5022d2018-11-19 15:21:44 -050038// Node interface is an abstraction of the node data structure
Stephane Barbarie06c4a742018-10-01 11:09:32 -040039type Node interface {
40 MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple)
41
42 // CRUD functions
Stephane Barbarieef6650d2019-07-18 12:15:09 -040043 Add(ctx context.Context, path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision
44 Get(ctx context.Context, path string, hash string, depth int, deep bool, txid string) interface{}
45 List(ctx context.Context, path string, hash string, depth int, deep bool, txid string) interface{}
46 Update(ctx context.Context, path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision
47 Remove(ctx context.Context, path string, txid string, makeBranch MakeBranchFunction) Revision
48 CreateProxy(ctx context.Context, path string, exclusive bool) *Proxy
49
50 GetProxy() *Proxy
Stephane Barbarie06c4a742018-10-01 11:09:32 -040051
52 MakeBranch(txid string) *Branch
53 DeleteBranch(txid string)
54 MergeBranch(txid string, dryRun bool) (Revision, error)
55
56 MakeTxBranch() string
57 DeleteTxBranch(txid string)
58 FoldTxBranch(txid string)
Stephane Barbarie06c4a742018-10-01 11:09:32 -040059}
60
61type node struct {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040062 mutex sync.RWMutex
63 Root *root
64 Type interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040065 Branches map[string]*Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -040066 Tags map[string]Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040067 Proxy *Proxy
68 EventBus *EventBus
69 AutoPrune bool
70}
71
Stephane Barbariedc5022d2018-11-19 15:21:44 -050072// ChangeTuple holds details of modifications made to a revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -040073type ChangeTuple struct {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -040074 Type CallbackType
75 PreviousData interface{}
76 LatestData interface{}
Stephane Barbarie694e2b92018-09-07 12:17:36 -040077}
78
Stephane Barbariedc5022d2018-11-19 15:21:44 -050079// NewNode creates a new instance of the node data structure
Stephane Barbarie06c4a742018-10-01 11:09:32 -040080func NewNode(root *root, initialData interface{}, autoPrune bool, txid string) *node {
81 n := &node{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040082
Stephane Barbarie126101e2018-10-11 16:18:48 -040083 n.Root = root
Stephane Barbarieec0919b2018-09-05 14:14:29 -040084 n.Branches = make(map[string]*Branch)
85 n.Tags = make(map[string]Revision)
86 n.Proxy = nil
87 n.EventBus = nil
88 n.AutoPrune = autoPrune
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040089
90 if IsProtoMessage(initialData) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040091 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040092 dataCopy := proto.Clone(initialData.(proto.Message))
Stephane Barbarieec0919b2018-09-05 14:14:29 -040093 n.initialize(dataCopy, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040094 } else if reflect.ValueOf(initialData).IsValid() {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050095 // FIXME: this block does not reflect the original implementation
96 // it should be checking if the provided initial_data is already a type!??!
97 // it should be checked before IsProtoMessage
Stephane Barbarieec0919b2018-09-05 14:14:29 -040098 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040099 } else {
100 // not implemented error
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400101 log.Errorf("cannot process initial data - %+v", initialData)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400102 }
103
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400104 return n
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400105}
106
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500107// MakeNode creates a new node in the tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400108func (n *node) MakeNode(data interface{}, txid string) *node {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400109 return NewNode(n.Root, data, true, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400110}
111
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500112// MakeRevision create a new revision of the node in the tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400113func (n *node) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500114 return n.GetRoot().MakeRevision(branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400115}
116
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500117// makeLatest will mark the revision of a node as being the latest
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400118func (n *node) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400119 // Keep a reference to the current revision
120 var previous string
121 if branch.GetLatest() != nil {
122 previous = branch.GetLatest().GetHash()
123 }
124
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500125 branch.AddRevision(revision)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400126
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400127 // If anything is new, then set the revision as the latest
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500128 if branch.GetLatest() == nil || revision.GetHash() != branch.GetLatest().GetHash() {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400129 if revision.GetName() != "" {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400130 log.Debugw("saving-latest-data", log.Fields{"hash": revision.GetHash(), "data": revision.GetData()})
131 // Tag a timestamp to that revision
132 revision.SetLastUpdate()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400133 GetRevCache().Set(revision.GetName(), revision)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400134 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500135 branch.SetLatest(revision)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400136 }
137
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400138 // Delete the previous revision if anything has changed
139 if previous != "" && previous != branch.GetLatest().GetHash() {
140 branch.DeleteRevision(previous)
141 }
142
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400143 if changeAnnouncement != nil && branch.Txid == "" {
Stephane Barbarie260a5632019-02-26 16:12:49 -0500144 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400145 for _, change := range changeAnnouncement {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400146 log.Debugw("adding-callback",
147 log.Fields{
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400148 "callbacks": n.GetProxy().getCallbacks(change.Type),
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400149 "type": change.Type,
150 "previousData": change.PreviousData,
151 "latestData": change.LatestData,
152 })
Stephane Barbarie260a5632019-02-26 16:12:49 -0500153 n.Root.AddCallback(
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400154 n.GetProxy().InvokeCallbacks,
Stephane Barbarie126101e2018-10-11 16:18:48 -0400155 change.Type,
156 true,
157 change.PreviousData,
158 change.LatestData)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400159 }
160 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400161 }
162}
163
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500164// Latest returns the latest revision of node with or without the transaction id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400165func (n *node) Latest(txid ...string) Revision {
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400166 var branch *Branch
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400167
168 if len(txid) > 0 && txid[0] != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500169 if branch = n.GetBranch(txid[0]); branch != nil {
170 return branch.GetLatest()
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400171 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500172 } else if branch = n.GetBranch(NONE); branch != nil {
173 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400174 }
175 return nil
176}
177
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500178// initialize prepares the content of a node along with its possible ramifications
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400179func (n *node) initialize(data interface{}, txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500180 children := make(map[string][]Revision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400181 for fieldName, field := range ChildrenFields(n.Type) {
182 _, fieldValue := GetAttributeValue(data, fieldName, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400183
184 if fieldValue.IsValid() {
185 if field.IsContainer {
186 if field.Key != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400187 for i := 0; i < fieldValue.Len(); i++ {
188 v := fieldValue.Index(i)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400189
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500190 if rev := n.MakeNode(v.Interface(), txid).Latest(txid); rev != nil {
191 children[fieldName] = append(children[fieldName], rev)
192 }
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400193
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500194 // TODO: The following logic was ported from v1.0. Need to verify if it is required
195 //var keysSeen []string
196 //_, key := GetAttributeValue(v.Interface(), field.Key, 0)
197 //for _, k := range keysSeen {
198 // if k == key.String() {
199 // //log.Errorf("duplicate key - %s", k)
200 // }
201 //}
202 //keysSeen = append(keysSeen, key.String())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400203 }
204
205 } else {
206 for i := 0; i < fieldValue.Len(); i++ {
207 v := fieldValue.Index(i)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500208 if newNodeRev := n.MakeNode(v.Interface(), txid).Latest(); newNodeRev != nil {
209 children[fieldName] = append(children[fieldName], newNodeRev)
210 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400211 }
212 }
213 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500214 if newNodeRev := n.MakeNode(fieldValue.Interface(), txid).Latest(); newNodeRev != nil {
215 children[fieldName] = append(children[fieldName], newNodeRev)
216 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400217 }
218 } else {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400219 log.Errorf("field is invalid - %+v", fieldValue)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400220 }
221 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500222
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400223 branch := NewBranch(n, "", nil, n.AutoPrune)
224 rev := n.MakeRevision(branch, data, children)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400225 n.makeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400226
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400227 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500228 n.SetBranch(NONE, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400229 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500230 n.SetBranch(txid, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400231 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400232}
233
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500234// findRevByKey retrieves a specific revision from a node tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400235func (n *node) findRevByKey(revs []Revision, keyName string, value interface{}) (int, Revision) {
236 for i, rev := range revs {
237 dataValue := reflect.ValueOf(rev.GetData())
238 dataStruct := GetAttributeStructure(rev.GetData(), keyName, 0)
239
240 fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
241
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400242 a := fmt.Sprintf("%s", fieldValue.Interface())
243 b := fmt.Sprintf("%s", value)
244 if a == b {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500245 return i, revs[i]
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400246 }
247 }
248
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400249 return -1, nil
250}
251
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500252// Get retrieves the data from a node tree that resides at the specified path
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400253func (n *node) List(ctx context.Context, path string, hash string, depth int, deep bool, txid string) interface{} {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400254 n.mutex.Lock()
255 defer n.mutex.Unlock()
256
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500257 log.Debugw("node-list-request", log.Fields{"path": path, "hash": hash, "depth": depth, "deep": deep, "txid": txid})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500258 if deep {
259 depth = -1
260 }
261
262 for strings.HasPrefix(path, "/") {
263 path = path[1:]
264 }
265
266 var branch *Branch
267 var rev Revision
268
269 if branch = n.GetBranch(txid); txid == "" || branch == nil {
270 branch = n.GetBranch(NONE)
271 }
272
273 if hash != "" {
274 rev = branch.GetRevision(hash)
275 } else {
276 rev = branch.GetLatest()
277 }
278
279 var result interface{}
280 var prList []interface{}
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400281 if pr := rev.LoadFromPersistence(ctx, path, txid, nil); pr != nil {
Stephane Barbarieaa467942019-02-06 14:09:44 -0500282 for _, revEntry := range pr {
283 prList = append(prList, revEntry.GetData())
284 }
285 result = prList
286 }
287
288 return result
289}
290
291// Get retrieves the data from a node tree that resides at the specified path
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400292func (n *node) Get(ctx context.Context, path string, hash string, depth int, reconcile bool, txid string) interface{} {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400293 n.mutex.Lock()
294 defer n.mutex.Unlock()
295
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400296 log.Debugw("node-get-request", log.Fields{"path": path, "hash": hash, "depth": depth, "reconcile": reconcile, "txid": txid})
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400297
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400298 for strings.HasPrefix(path, "/") {
299 path = path[1:]
300 }
301
302 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400303 var rev Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400304
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500305 if branch = n.GetBranch(txid); txid == "" || branch == nil {
306 branch = n.GetBranch(NONE)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400307 }
308
309 if hash != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500310 rev = branch.GetRevision(hash)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400311 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500312 rev = branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400313 }
314
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500315 var result interface{}
Stephane Barbarie260a5632019-02-26 16:12:49 -0500316
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400317 // If there is no request to reconcile, try to get it from memory
Stephane Barbarie260a5632019-02-26 16:12:49 -0500318 if !reconcile {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400319 // Try to find an entry matching the path value from one of these sources
320 // 1. Start with the cache which stores revisions by watch names
321 // 2. Then look in the revision tree, especially if it's a sub-path such as /devices/1234/flows
Stephane Barbarie802aca42019-05-21 12:19:28 -0400322 // 3. Move on to the KV store if that path cannot be found or if the entry has expired
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400323 if entry, exists := GetRevCache().Get(path); exists && entry.(Revision) != nil {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400324 entryAge := time.Now().Sub(entry.(Revision).GetLastUpdate()).Nanoseconds() / int64(time.Millisecond)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400325 if entryAge < DataRefreshPeriod {
Stephane Barbariec92d1072019-06-07 16:21:49 -0400326 log.Debugw("using-cache-entry", log.Fields{
327 "path": path,
328 "hash": hash,
329 "age": entryAge,
330 })
Stephane Barbarie802aca42019-05-21 12:19:28 -0400331 return proto.Clone(entry.(Revision).GetData().(proto.Message))
332 } else {
333 log.Debugw("cache-entry-expired", log.Fields{"path": path, "hash": hash, "age": entryAge})
334 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400335 } else if result = n.getPath(ctx, rev.GetBranch().GetLatest(), path, depth); result != nil && reflect.ValueOf(result).IsValid() && !reflect.ValueOf(result).IsNil() {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400336 log.Debugw("using-rev-tree-entry", log.Fields{"path": path, "hash": hash, "depth": depth, "reconcile": reconcile, "txid": txid})
Stephane Barbarie260a5632019-02-26 16:12:49 -0500337 return result
Stephane Barbarie802aca42019-05-21 12:19:28 -0400338 } else {
339 log.Debugw("not-using-cache-entry", log.Fields{
340 "path": path,
341 "hash": hash, "depth": depth,
342 "reconcile": reconcile,
343 "txid": txid,
344 })
Stephane Barbarie260a5632019-02-26 16:12:49 -0500345 }
Stephane Barbarie802aca42019-05-21 12:19:28 -0400346 } else {
347 log.Debugw("reconcile-requested", log.Fields{
348 "path": path,
349 "hash": hash,
350 "reconcile": reconcile,
351 })
Stephane Barbarie260a5632019-02-26 16:12:49 -0500352 }
353
Stephane Barbarie802aca42019-05-21 12:19:28 -0400354 // If we got to this point, we are either trying to reconcile with the db
Stephane Barbarie260a5632019-02-26 16:12:49 -0500355 // or we simply failed at getting information from memory
356 if n.Root.KvStore != nil {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400357 if pr := rev.LoadFromPersistence(ctx, path, txid, nil); pr != nil && len(pr) > 0 {
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500358 // Did we receive a single or multiple revisions?
359 if len(pr) > 1 {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400360 var revs []interface{}
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500361 for _, revEntry := range pr {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400362 revs = append(revs, revEntry.GetData())
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500363 }
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400364 result = revs
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500365 } else {
366 result = pr[0].GetData()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500367 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500368 }
369 }
370
371 return result
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400372}
373
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400374//getPath traverses the specified path and retrieves the data associated to it
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400375func (n *node) getPath(ctx context.Context, rev Revision, path string, depth int) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400376 if path == "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400377 return n.getData(rev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400378 }
379
380 partition := strings.SplitN(path, "/", 2)
381 name := partition[0]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400382
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400383 if len(partition) < 2 {
384 path = ""
385 } else {
386 path = partition[1]
387 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400388
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400389 names := ChildrenFields(n.Type)
390 field := names[name]
391
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500392 if field != nil && field.IsContainer {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500393 children := make([]Revision, len(rev.GetChildren(name)))
394 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500395
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400396 if field.Key != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400397 if path != "" {
398 partition = strings.SplitN(path, "/", 2)
399 key := partition[0]
400 path = ""
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400401 keyValue := field.KeyFromStr(key)
402 if _, childRev := n.findRevByKey(children, field.Key, keyValue); childRev == nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400403 return nil
404 } else {
405 childNode := childRev.GetNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400406 return childNode.getPath(ctx, childRev, path, depth)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400407 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400408 } else {
409 var response []interface{}
410 for _, childRev := range children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400411 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400412 value := childNode.getData(childRev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400413 response = append(response, value)
414 }
415 return response
416 }
417 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400418 var response []interface{}
419 if path != "" {
420 // TODO: raise error
421 return response
422 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500423 for _, childRev := range children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400424 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400425 value := childNode.getData(childRev, depth)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400426 response = append(response, value)
427 }
428 return response
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400429 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400430 } else if children := rev.GetChildren(name); children != nil && len(children) > 0 {
431 childRev := children[0]
432 childNode := childRev.GetNode()
433 return childNode.getPath(ctx, childRev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400434 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500435
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400436 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400437}
438
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500439// getData retrieves the data from a node revision
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400440func (n *node) getData(rev Revision, depth int) interface{} {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500441 msg := rev.GetBranch().GetLatest().Get(depth)
Stephane Barbariea188d942018-10-16 16:43:04 -0400442 var modifiedMsg interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400443
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500444 if n.GetProxy() != nil {
Stephane Barbarieaa467942019-02-06 14:09:44 -0500445 log.Debugw("invoking-get-callbacks", log.Fields{"data": msg})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500446 if modifiedMsg = n.GetProxy().InvokeCallbacks(GET, false, msg); modifiedMsg != nil {
Stephane Barbariea188d942018-10-16 16:43:04 -0400447 msg = modifiedMsg
448 }
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400449
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400450 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500451
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400452 return msg
453}
454
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500455// Update changes the content of a node at the specified path with the provided data
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400456func (n *node) Update(ctx context.Context, path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400457 n.mutex.Lock()
458 defer n.mutex.Unlock()
459
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500460 log.Debugw("node-update-request", log.Fields{"path": path, "strict": strict, "txid": txid, "makeBranch": makeBranch})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500461
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400462 for strings.HasPrefix(path, "/") {
463 path = path[1:]
464 }
465
466 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400467 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500468 branch = n.GetBranch(NONE)
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500469 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400470 branch = makeBranch(n)
471 }
472
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500473 if branch.GetLatest() != nil {
474 log.Debugf("Branch data : %+v, Passed data: %+v", branch.GetLatest().GetData(), data)
475 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400476 if path == "" {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400477 return n.doUpdate(ctx, branch, data, strict)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400478 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400479
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500480 rev := branch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400481
482 partition := strings.SplitN(path, "/", 2)
483 name := partition[0]
484
485 if len(partition) < 2 {
486 path = ""
487 } else {
488 path = partition[1]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400489 }
490
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400491 field := ChildrenFields(n.Type)[name]
492 var children []Revision
493
Stephane Barbarieaa467942019-02-06 14:09:44 -0500494 if field == nil {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400495 return n.doUpdate(ctx, branch, data, strict)
Stephane Barbarieaa467942019-02-06 14:09:44 -0500496 }
497
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400498 if field.IsContainer {
499 if path == "" {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400500 log.Errorf("cannot update a list")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400501 } else if field.Key != "" {
502 partition := strings.SplitN(path, "/", 2)
503 key := partition[0]
504 if len(partition) < 2 {
505 path = ""
506 } else {
507 path = partition[1]
508 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400509 keyValue := field.KeyFromStr(key)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500510
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500511 children = make([]Revision, len(rev.GetChildren(name)))
512 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500513
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400514 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400515
516 if childRev == nil {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400517 log.Debugw("child-revision-is-nil", log.Fields{"key": keyValue})
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400518 return branch.GetLatest()
519 }
520
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400521 childNode := childRev.GetNode()
Stephane Barbariea188d942018-10-16 16:43:04 -0400522
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500523 // Save proxy in child node to ensure callbacks are called later on
Stephane Barbaried62ac4e2019-02-05 14:08:38 -0500524 // only assign in cases of non sub-folder proxies, i.e. "/"
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400525 if childNode.Proxy == nil && n.Proxy != nil && n.GetProxy().getFullPath() == "" {
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500526 childNode.Proxy = n.Proxy
527 }
528
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400529 newChildRev := childNode.Update(ctx, path, data, strict, txid, makeBranch)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400530
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400531 if newChildRev.GetHash() == childRev.GetHash() {
532 if newChildRev != childRev {
533 log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
534 newChildRev.ClearHash()
535 }
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400536 log.Debugw("child-revisions-have-matching-hash", log.Fields{"hash": childRev.GetHash(), "key": keyValue})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500537 return branch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400538 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400539
540 _, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500541
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400542 _newKeyType := fmt.Sprintf("%s", newKey)
543 _keyValueType := fmt.Sprintf("%s", keyValue)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500544
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400545 if _newKeyType != _keyValueType {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400546 log.Errorf("cannot change key field")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400547 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500548
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500549 // Prefix the hash value with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400550 newChildRev.SetName(name + "/" + _keyValueType)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400551
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400552 branch.LatestLock.Lock()
553 defer branch.LatestLock.Unlock()
554
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400555 if idx >= 0 {
556 children[idx] = newChildRev
557 } else {
558 children = append(children, newChildRev)
559 }
560
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400561 updatedRev := rev.UpdateChildren(ctx, name, children, branch)
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500562
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500563 n.makeLatest(branch, updatedRev, nil)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400564 updatedRev.ChildDrop(name, childRev.GetHash())
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500565
Stephane Barbariea188d942018-10-16 16:43:04 -0400566 return newChildRev
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500567
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400568 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400569 log.Errorf("cannot index into container with no keys")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400570 }
571 } else {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500572 childRev := rev.GetChildren(name)[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400573 childNode := childRev.GetNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400574 newChildRev := childNode.Update(ctx, path, data, strict, txid, makeBranch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400575
576 branch.LatestLock.Lock()
577 defer branch.LatestLock.Unlock()
578
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400579 updatedRev := rev.UpdateChildren(ctx, name, []Revision{newChildRev}, branch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500580 n.makeLatest(branch, updatedRev, nil)
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500581
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400582 updatedRev.ChildDrop(name, childRev.GetHash())
583
Stephane Barbariea188d942018-10-16 16:43:04 -0400584 return newChildRev
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400585 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500586
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400587 return nil
588}
589
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400590func (n *node) doUpdate(ctx context.Context, branch *Branch, data interface{}, strict bool) Revision {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400591 log.Debugw("comparing-types", log.Fields{"expected": reflect.ValueOf(n.Type).Type(), "actual": reflect.TypeOf(data)})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400592
593 if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
594 // TODO raise error
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400595 log.Errorw("types-do-not-match: %+v", log.Fields{"actual": reflect.TypeOf(data), "expected": n.Type})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400596 return nil
597 }
598
599 // TODO: validate that this actually works
600 //if n.hasChildren(data) {
601 // return nil
602 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400603
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500604 if n.GetProxy() != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400605 log.Debug("invoking proxy PRE_UPDATE Callbacks")
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500606 n.GetProxy().InvokeCallbacks(PRE_UPDATE, false, branch.GetLatest(), data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400607 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500608
609 if branch.GetLatest().GetData().(proto.Message).String() != data.(proto.Message).String() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400610 if strict {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400611 // TODO: checkAccessViolations(data, Branch.GetLatest.data)
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400612 log.Debugf("checking access violations")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400613 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500614
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400615 rev := branch.GetLatest().UpdateData(ctx, data, branch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500616 changes := []ChangeTuple{{POST_UPDATE, branch.GetLatest().GetData(), rev.GetData()}}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500617 n.makeLatest(branch, rev, changes)
618
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400619 return rev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400620 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500621 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400622}
623
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500624// Add inserts a new node at the specified path with the provided data
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400625func (n *node) Add(ctx context.Context, path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400626 n.mutex.Lock()
627 defer n.mutex.Unlock()
628
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500629 log.Debugw("node-add-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500630
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400631 for strings.HasPrefix(path, "/") {
632 path = path[1:]
633 }
634 if path == "" {
635 // TODO raise error
Stephane Barbarie126101e2018-10-11 16:18:48 -0400636 log.Errorf("cannot add for non-container mode")
Stephane Barbarieaa467942019-02-06 14:09:44 -0500637 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400638 }
639
640 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400641 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500642 branch = n.GetBranch(NONE)
643 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400644 branch = makeBranch(n)
645 }
646
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500647 rev := branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400648
649 partition := strings.SplitN(path, "/", 2)
650 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400651
652 if len(partition) < 2 {
653 path = ""
654 } else {
655 path = partition[1]
656 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400657
658 field := ChildrenFields(n.Type)[name]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500659
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400660 var children []Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400661
662 if field.IsContainer {
663 if path == "" {
664 if field.Key != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500665 if n.GetProxy() != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400666 log.Debug("invoking proxy PRE_ADD Callbacks")
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500667 n.GetProxy().InvokeCallbacks(PRE_ADD, false, data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400668 }
669
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500670 children = make([]Revision, len(rev.GetChildren(name)))
671 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500672
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400673 _, key := GetAttributeValue(data, field.Key, 0)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500674
Stephane Barbarie126101e2018-10-11 16:18:48 -0400675 if _, exists := n.findRevByKey(children, field.Key, key.String()); exists != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400676 // TODO raise error
Stephane Barbarie260a5632019-02-26 16:12:49 -0500677 log.Warnw("duplicate-key-found", log.Fields{"key": key.String()})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500678 return exists
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400679 }
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500680 childRev := n.MakeNode(data, "").Latest()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500681
682 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400683 childRev.SetName(name + "/" + key.String())
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500684
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400685 branch.LatestLock.Lock()
686 defer branch.LatestLock.Unlock()
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500687
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500688 children = append(children, childRev)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400689
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400690 updatedRev := rev.UpdateChildren(ctx, name, children, branch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400691 changes := []ChangeTuple{{POST_ADD, nil, childRev.GetData()}}
692 childRev.SetupWatch(childRev.GetName())
693
694 n.makeLatest(branch, updatedRev, changes)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500695
696 return childRev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400697 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500698 log.Errorf("cannot add to non-keyed container")
699
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400700 } else if field.Key != "" {
701 partition := strings.SplitN(path, "/", 2)
702 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400703 if len(partition) < 2 {
704 path = ""
705 } else {
706 path = partition[1]
707 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400708 keyValue := field.KeyFromStr(key)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500709
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500710 children = make([]Revision, len(rev.GetChildren(name)))
711 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500712
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400713 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500714
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400715 if childRev == nil {
716 return branch.GetLatest()
717 }
718
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400719 childNode := childRev.GetNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400720 newChildRev := childNode.Add(ctx, path, data, txid, makeBranch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500721
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500722 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400723 newChildRev.SetName(name + "/" + keyValue.(string))
724
725 branch.LatestLock.Lock()
726 defer branch.LatestLock.Unlock()
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500727
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400728 if idx >= 0 {
729 children[idx] = newChildRev
730 } else {
731 children = append(children, newChildRev)
732 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500733
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400734 updatedRev := rev.UpdateChildren(ctx, name, children, branch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400735 n.makeLatest(branch, updatedRev, nil)
736
737 updatedRev.ChildDrop(name, childRev.GetHash())
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500738
739 return newChildRev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400740 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400741 log.Errorf("cannot add to non-keyed container")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400742 }
743 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400744 log.Errorf("cannot add to non-container field")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400745 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500746
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400747 return nil
748}
749
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500750// Remove eliminates a node at the specified path
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400751func (n *node) Remove(ctx context.Context, path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400752 n.mutex.Lock()
753 defer n.mutex.Unlock()
754
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500755 log.Debugw("node-remove-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500756
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400757 for strings.HasPrefix(path, "/") {
758 path = path[1:]
759 }
760 if path == "" {
761 // TODO raise error
Stephane Barbarie126101e2018-10-11 16:18:48 -0400762 log.Errorf("cannot remove for non-container mode")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400763 }
764 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400765 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500766 branch = n.GetBranch(NONE)
767 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400768 branch = makeBranch(n)
769 }
770
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500771 rev := branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400772
773 partition := strings.SplitN(path, "/", 2)
774 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400775 if len(partition) < 2 {
776 path = ""
777 } else {
778 path = partition[1]
779 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400780
781 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400782 var children []Revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400783 postAnnouncement := []ChangeTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400784
785 if field.IsContainer {
786 if path == "" {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500787 log.Errorw("cannot-remove-without-key", log.Fields{"name": name, "key": path})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400788 } else if field.Key != "" {
789 partition := strings.SplitN(path, "/", 2)
790 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400791 if len(partition) < 2 {
792 path = ""
793 } else {
794 path = partition[1]
795 }
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500796
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400797 keyValue := field.KeyFromStr(key)
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500798 children = make([]Revision, len(rev.GetChildren(name)))
799 copy(children, rev.GetChildren(name))
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500800
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400801 if path != "" {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400802 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
803 childNode := childRev.GetNode()
804 if childNode.Proxy == nil {
805 childNode.Proxy = n.Proxy
806 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400807 newChildRev := childNode.Remove(ctx, path, txid, makeBranch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400808
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400809 branch.LatestLock.Lock()
810 defer branch.LatestLock.Unlock()
811
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400812 if idx >= 0 {
813 children[idx] = newChildRev
814 } else {
815 children = append(children, newChildRev)
816 }
817
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400818 rev.SetChildren(name, children)
819 branch.GetLatest().Drop(txid, false)
820 n.makeLatest(branch, rev, nil)
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500821 }
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400822 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400823 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500824
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400825 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil && idx >= 0 {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500826 if n.GetProxy() != nil {
827 data := childRev.GetData()
828 n.GetProxy().InvokeCallbacks(PRE_REMOVE, false, data)
829 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, data, nil})
830 } else {
831 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, childRev.GetData(), nil})
832 }
833
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400834 childRev.StorageDrop(txid, true)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400835 GetRevCache().Delete(childRev.GetName())
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400836
837 branch.LatestLock.Lock()
838 defer branch.LatestLock.Unlock()
839
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500840 children = append(children[:idx], children[idx+1:]...)
841 rev.SetChildren(name, children)
842
843 branch.GetLatest().Drop(txid, false)
844 n.makeLatest(branch, rev, postAnnouncement)
845
846 return rev
847 } else {
848 log.Errorw("failed-to-find-revision", log.Fields{"name": name, "key": keyValue.(string)})
849 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400850 }
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500851 log.Errorw("cannot-add-to-non-keyed-container", log.Fields{"name": name, "path": path, "fieldKey": field.Key})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500852
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400853 } else {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500854 log.Errorw("cannot-add-to-non-container-field", log.Fields{"name": name, "path": path})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400855 }
856
857 return nil
858}
859
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400860// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
861
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500862// MakeBranchFunction is a type for function references intented to create a branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400863type MakeBranchFunction func(*node) *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400864
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500865// MakeBranch creates a new branch for the provided transaction id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400866func (n *node) MakeBranch(txid string) *Branch {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500867 branchPoint := n.GetBranch(NONE).GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400868 branch := NewBranch(n, txid, branchPoint, true)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500869 n.SetBranch(txid, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400870 return branch
871}
872
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500873// DeleteBranch removes a branch with the specified id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400874func (n *node) DeleteBranch(txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400875 delete(n.Branches, txid)
876}
877
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400878func (n *node) mergeChild(txid string, dryRun bool) func(Revision) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400879 f := func(rev Revision) Revision {
880 childBranch := rev.GetBranch()
881
882 if childBranch.Txid == txid {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400883 rev, _ = childBranch.Node.MergeBranch(txid, dryRun)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400884 }
885
886 return rev
887 }
888 return f
889}
890
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500891// MergeBranch will integrate the contents of a transaction branch within the latest branch of a given node
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400892func (n *node) MergeBranch(txid string, dryRun bool) (Revision, error) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500893 srcBranch := n.GetBranch(txid)
894 dstBranch := n.GetBranch(NONE)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400895
896 forkRev := srcBranch.Origin
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500897 srcRev := srcBranch.GetLatest()
898 dstRev := dstBranch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400899
900 rev, changes := Merge3Way(forkRev, srcRev, dstRev, n.mergeChild(txid, dryRun), dryRun)
901
902 if !dryRun {
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500903 if rev != nil {
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400904 rev.SetName(dstRev.GetName())
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500905 n.makeLatest(dstBranch, rev, changes)
906 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500907 n.DeleteBranch(txid)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400908 }
909
Stephane Barbariee16186c2018-09-11 10:46:34 -0400910 // TODO: return proper error when one occurs
911 return rev, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400912}
913
914// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diff utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
915
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400916//func (n *node) diff(hash1, hash2, txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400917// branch := n.Branches[txid]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500918// rev1 := branch.GetHash(hash1)
919// rev2 := branch.GetHash(hash2)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400920//
921// if rev1.GetHash() == rev2.GetHash() {
922// // empty patch
923// } else {
924// // translate data to json and generate patch
925// patch, err := jsonpatch.MakePatch(rev1.GetData(), rev2.GetData())
926// patch.
927// }
928//}
929
930// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tag utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
931
932// TODO: is tag mgmt used in the python implementation? Need to validate
933
934// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
935
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400936func (n *node) hasChildren(data interface{}) bool {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400937 for fieldName, field := range ChildrenFields(n.Type) {
938 _, fieldValue := GetAttributeValue(data, fieldName, 0)
939
940 if (field.IsContainer && fieldValue.Len() > 0) || !fieldValue.IsNil() {
941 log.Error("cannot update external children")
942 return true
943 }
944 }
945
946 return false
947}
948
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400949// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ node Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400950
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500951// CreateProxy returns a reference to a sub-tree of the data model
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400952func (n *node) CreateProxy(ctx context.Context, path string, exclusive bool) *Proxy {
953 return n.createProxy(ctx, path, path, n, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400954}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500955
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400956func (n *node) createProxy(ctx context.Context, path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
Stephane Barbariec92d1072019-06-07 16:21:49 -0400957 log.Debugw("node-create-proxy", log.Fields{
958 "node-type": reflect.ValueOf(n.Type).Type(),
959 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
960 "path": path,
961 "fullPath": fullPath,
962 })
963
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400964 for strings.HasPrefix(path, "/") {
965 path = path[1:]
966 }
967 if path == "" {
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500968 return n.makeProxy(path, fullPath, parentNode, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400969 }
970
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500971 rev := n.GetBranch(NONE).GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400972 partition := strings.SplitN(path, "/", 2)
973 name := partition[0]
Stephane Barbariec92d1072019-06-07 16:21:49 -0400974 var nodeType interface{}
Stephane Barbarie126101e2018-10-11 16:18:48 -0400975 if len(partition) < 2 {
976 path = ""
Stephane Barbariec92d1072019-06-07 16:21:49 -0400977 nodeType = n.Type
Stephane Barbarie126101e2018-10-11 16:18:48 -0400978 } else {
979 path = partition[1]
Stephane Barbariec92d1072019-06-07 16:21:49 -0400980 nodeType = parentNode.Type
Stephane Barbarie126101e2018-10-11 16:18:48 -0400981 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400982
Stephane Barbariec92d1072019-06-07 16:21:49 -0400983 field := ChildrenFields(nodeType)[name]
984
985 if field != nil {
986 if field.IsContainer {
987 log.Debugw("container-field", log.Fields{
988 "node-type": reflect.ValueOf(n.Type).Type(),
989 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
990 "path": path,
991 "name": name,
992 })
993 if path == "" {
994 log.Debugw("folder-proxy", log.Fields{
995 "node-type": reflect.ValueOf(n.Type).Type(),
996 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
997 "fullPath": fullPath,
998 "name": name,
999 })
1000 newNode := n.MakeNode(reflect.New(field.ClassType.Elem()).Interface(), "")
1001 return newNode.makeProxy(path, fullPath, parentNode, exclusive)
1002 } else if field.Key != "" {
1003 log.Debugw("key-proxy", log.Fields{
1004 "node-type": reflect.ValueOf(n.Type).Type(),
1005 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1006 "fullPath": fullPath,
1007 "name": name,
1008 })
1009 partition := strings.SplitN(path, "/", 2)
1010 key := partition[0]
1011 if len(partition) < 2 {
1012 path = ""
1013 } else {
1014 path = partition[1]
1015 }
1016 keyValue := field.KeyFromStr(key)
1017 var children []Revision
1018 children = make([]Revision, len(rev.GetChildren(name)))
1019 copy(children, rev.GetChildren(name))
1020
Stephane Barbariec92d1072019-06-07 16:21:49 -04001021 var childRev Revision
1022 if _, childRev = n.findRevByKey(children, field.Key, keyValue); childRev != nil {
1023 log.Debugw("found-revision-matching-key-in-memory", log.Fields{
1024 "node-type": reflect.ValueOf(n.Type).Type(),
1025 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1026 "fullPath": fullPath,
1027 "name": name,
1028 })
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001029 } else if revs := n.GetBranch(NONE).GetLatest().LoadFromPersistence(ctx, fullPath, "", nil); revs != nil && len(revs) > 0 {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001030 log.Debugw("found-revision-matching-key-in-db", log.Fields{
1031 "node-type": reflect.ValueOf(n.Type).Type(),
1032 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1033 "fullPath": fullPath,
1034 "name": name,
1035 })
1036 childRev = revs[0]
1037 } else {
1038 log.Debugw("no-revision-matching-key", log.Fields{
1039 "node-type": reflect.ValueOf(n.Type).Type(),
1040 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1041 "fullPath": fullPath,
1042 "name": name,
1043 })
1044 }
1045 if childRev != nil {
1046 childNode := childRev.GetNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001047 return childNode.createProxy(ctx, path, fullPath, n, exclusive)
Stephane Barbariec92d1072019-06-07 16:21:49 -04001048 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -04001049 } else {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001050 log.Errorw("cannot-access-index-of-empty-container", log.Fields{
1051 "node-type": reflect.ValueOf(n.Type).Type(),
1052 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1053 "path": path,
1054 "name": name,
1055 })
Stephane Barbarie933b09b2019-01-09 11:12:09 -05001056 }
Stephane Barbarie126101e2018-10-11 16:18:48 -04001057 } else {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001058 log.Debugw("non-container-field", log.Fields{
1059 "node-type": reflect.ValueOf(n.Type).Type(),
1060 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1061 "path": path,
1062 "name": name,
1063 })
1064 childRev := rev.GetChildren(name)[0]
1065 childNode := childRev.GetNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001066 return childNode.createProxy(ctx, path, fullPath, n, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001067 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001068 } else {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001069 log.Debugw("field-object-is-nil", log.Fields{
1070 "node-type": reflect.ValueOf(n.Type).Type(),
1071 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1072 "fullPath": fullPath,
1073 "name": name,
1074 })
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001075 }
1076
Stephane Barbariec92d1072019-06-07 16:21:49 -04001077 log.Warnw("cannot-create-proxy", log.Fields{
1078 "node-type": reflect.ValueOf(n.Type).Type(),
1079 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1080 "path": path,
1081 "fullPath": fullPath,
1082 "latest-rev": rev.GetHash(),
1083 })
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001084 return nil
1085}
1086
Stephane Barbarie1ab43272018-12-08 21:42:13 -05001087func (n *node) makeProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001088 log.Debugw("node-make-proxy", log.Fields{
1089 "node-type": reflect.ValueOf(n.Type).Type(),
1090 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1091 "path": path,
1092 "fullPath": fullPath,
1093 })
1094
Stephane Barbarie126101e2018-10-11 16:18:48 -04001095 r := &root{
1096 node: n,
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001097 Callbacks: n.Root.GetCallbacks(),
1098 NotificationCallbacks: n.Root.GetNotificationCallbacks(),
Stephane Barbarie126101e2018-10-11 16:18:48 -04001099 DirtyNodes: n.Root.DirtyNodes,
1100 KvStore: n.Root.KvStore,
1101 Loading: n.Root.Loading,
1102 RevisionClass: n.Root.RevisionClass,
1103 }
1104
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001105 if n.Proxy == nil {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001106 log.Debugw("constructing-new-proxy", log.Fields{
1107 "node-type": reflect.ValueOf(n.Type).Type(),
1108 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1109 "path": path,
1110 "fullPath": fullPath,
1111 })
Stephane Barbarie1ab43272018-12-08 21:42:13 -05001112 n.Proxy = NewProxy(r, n, parentNode, path, fullPath, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001113 } else {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001114 log.Debugw("node-has-existing-proxy", log.Fields{
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001115 "node-type": reflect.ValueOf(n.GetProxy().Node.Type).Type(),
1116 "parent-node-type": reflect.ValueOf(n.GetProxy().ParentNode.Type).Type(),
1117 "path": n.GetProxy().Path,
1118 "fullPath": n.GetProxy().FullPath,
Stephane Barbariec92d1072019-06-07 16:21:49 -04001119 })
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001120 if n.GetProxy().Exclusive {
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001121 log.Error("node is already owned exclusively")
1122 }
1123 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001124
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001125 return n.Proxy
1126}
1127
Stephane Barbarie06c4a742018-10-01 11:09:32 -04001128func (n *node) makeEventBus() *EventBus {
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001129 if n.EventBus == nil {
1130 n.EventBus = NewEventBus()
1131 }
1132 return n.EventBus
1133}
1134
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001135func (n *node) SetProxy(proxy *Proxy) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001136 n.Proxy = proxy
1137}
1138
1139func (n *node) GetProxy() *Proxy {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001140 return n.Proxy
1141}
1142
1143func (n *node) GetBranch(key string) *Branch {
Stephane Barbarie1ab43272018-12-08 21:42:13 -05001144 if n.Branches != nil {
1145 if branch, exists := n.Branches[key]; exists {
1146 return branch
1147 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001148 }
1149 return nil
1150}
1151
1152func (n *node) SetBranch(key string, branch *Branch) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001153 n.Branches[key] = branch
1154}
1155
1156func (n *node) GetRoot() *root {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001157 return n.Root
Stephane Barbarie06c4a742018-10-01 11:09:32 -04001158}
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001159func (n *node) SetRoot(root *root) {
1160 n.Root = root
1161}