blob: 2a9309ca5e157c5da79961a5f6fcbcc951a56865 [file] [log] [blame]
Matt Jeanneretcab955f2019-04-10 15:45:57 -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 */
16
17package model
18
19// TODO: proper error handling
20// TODO: proper logging
21
22import (
23 "fmt"
24 "github.com/golang/protobuf/proto"
25 "github.com/opencord/voltha-go/common/log"
26 "reflect"
27 "runtime/debug"
28 "strings"
29 "sync"
30)
31
32// When a branch has no transaction id, everything gets stored in NONE
33const (
34 NONE string = "none"
35)
36
37// Node interface is an abstraction of the node data structure
38type Node interface {
39 MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple)
40
41 // CRUD functions
42 Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision
43 Get(path string, hash string, depth int, deep bool, txid string) interface{}
44 Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision
45 Remove(path string, txid string, makeBranch MakeBranchFunction) Revision
46
47 MakeBranch(txid string) *Branch
48 DeleteBranch(txid string)
49 MergeBranch(txid string, dryRun bool) (Revision, error)
50
51 MakeTxBranch() string
52 DeleteTxBranch(txid string)
53 FoldTxBranch(txid string)
54
55 CreateProxy(path string, exclusive bool) *Proxy
56 GetProxy() *Proxy
57}
58
59type node struct {
60 sync.RWMutex
61 Root *root
62 Type interface{}
63 Branches map[string]*Branch
64 Tags map[string]Revision
65 Proxy *Proxy
66 EventBus *EventBus
67 AutoPrune bool
68}
69
70// ChangeTuple holds details of modifications made to a revision
71type ChangeTuple struct {
72 Type CallbackType
73 PreviousData interface{}
74 LatestData interface{}
75}
76
77// NewNode creates a new instance of the node data structure
78func NewNode(root *root, initialData interface{}, autoPrune bool, txid string) *node {
79 n := &node{}
80
81 n.Root = root
82 n.Branches = make(map[string]*Branch)
83 n.Tags = make(map[string]Revision)
84 n.Proxy = nil
85 n.EventBus = nil
86 n.AutoPrune = autoPrune
87
88 if IsProtoMessage(initialData) {
89 n.Type = reflect.ValueOf(initialData).Interface()
90 dataCopy := proto.Clone(initialData.(proto.Message))
91 n.initialize(dataCopy, txid)
92 } else if reflect.ValueOf(initialData).IsValid() {
93 // FIXME: this block does not reflect the original implementation
94 // it should be checking if the provided initial_data is already a type!??!
95 // it should be checked before IsProtoMessage
96 n.Type = reflect.ValueOf(initialData).Interface()
97 } else {
98 // not implemented error
99 log.Errorf("cannot process initial data - %+v", initialData)
100 }
101
102 return n
103}
104
105// MakeNode creates a new node in the tree
106func (n *node) MakeNode(data interface{}, txid string) *node {
107 return NewNode(n.Root, data, true, txid)
108}
109
110// MakeRevision create a new revision of the node in the tree
111func (n *node) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
112 return n.GetRoot().MakeRevision(branch, data, children)
113}
114
115// makeLatest will mark the revision of a node as being the latest
116func (n *node) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
117 n.Lock()
118 defer n.Unlock()
119
120 branch.AddRevision(revision)
121
122 if branch.GetLatest() == nil || revision.GetHash() != branch.GetLatest().GetHash() {
123 branch.SetLatest(revision)
124 }
125
126 if changeAnnouncement != nil && branch.Txid == "" {
127 if n.Proxy != nil {
128 for _, change := range changeAnnouncement {
129 //log.Debugw("invoking callback",
130 // log.Fields{
131 // "callbacks": n.Proxy.getCallbacks(change.Type),
132 // "type": change.Type,
133 // "previousData": change.PreviousData,
134 // "latestData": change.LatestData,
135 // })
136 n.Root.AddCallback(
137 n.Proxy.InvokeCallbacks,
138 change.Type,
139 true,
140 change.PreviousData,
141 change.LatestData)
142 }
143 }
144
145 //for _, change := range changeAnnouncement {
146 //log.Debugf("sending notification - changeType: %+v, previous:%+v, latest: %+v",
147 // change.Type,
148 // change.PreviousData,
149 // change.LatestData)
150 //n.Root.AddNotificationCallback(
151 // n.makeEventBus().Advertise,
152 // change.Type,
153 // revision.GetHash(),
154 // change.PreviousData,
155 // change.LatestData)
156 //}
157 }
158}
159
160// Latest returns the latest revision of node with or without the transaction id
161func (n *node) Latest(txid ...string) Revision {
162 var branch *Branch
163
164 if len(txid) > 0 && txid[0] != "" {
165 if branch = n.GetBranch(txid[0]); branch != nil {
166 return branch.GetLatest()
167 }
168 } else if branch = n.GetBranch(NONE); branch != nil {
169 return branch.GetLatest()
170 }
171 return nil
172}
173
174// initialize prepares the content of a node along with its possible ramifications
175func (n *node) initialize(data interface{}, txid string) {
176 n.Lock()
177 children := make(map[string][]Revision)
178 for fieldName, field := range ChildrenFields(n.Type) {
179 _, fieldValue := GetAttributeValue(data, fieldName, 0)
180
181 if fieldValue.IsValid() {
182 if field.IsContainer {
183 if field.Key != "" {
184 for i := 0; i < fieldValue.Len(); i++ {
185 v := fieldValue.Index(i)
186
187 if rev := n.MakeNode(v.Interface(), txid).Latest(txid); rev != nil {
188 children[fieldName] = append(children[fieldName], rev)
189 }
190
191 // TODO: The following logic was ported from v1.0. Need to verify if it is required
192 //var keysSeen []string
193 //_, key := GetAttributeValue(v.Interface(), field.Key, 0)
194 //for _, k := range keysSeen {
195 // if k == key.String() {
196 // //log.Errorf("duplicate key - %s", k)
197 // }
198 //}
199 //keysSeen = append(keysSeen, key.String())
200 }
201
202 } else {
203 for i := 0; i < fieldValue.Len(); i++ {
204 v := fieldValue.Index(i)
205 if newNodeRev := n.MakeNode(v.Interface(), txid).Latest(); newNodeRev != nil {
206 children[fieldName] = append(children[fieldName], newNodeRev)
207 }
208 }
209 }
210 } else {
211 if newNodeRev := n.MakeNode(fieldValue.Interface(), txid).Latest(); newNodeRev != nil {
212 children[fieldName] = append(children[fieldName], newNodeRev)
213 }
214 }
215 } else {
216 log.Errorf("field is invalid - %+v", fieldValue)
217 }
218 }
219 n.Unlock()
220
221 branch := NewBranch(n, "", nil, n.AutoPrune)
222 rev := n.MakeRevision(branch, data, children)
223 n.makeLatest(branch, rev, nil)
224
225 if txid == "" {
226 n.SetBranch(NONE, branch)
227 } else {
228 n.SetBranch(txid, branch)
229 }
230}
231
232// findRevByKey retrieves a specific revision from a node tree
233func (n *node) findRevByKey(revs []Revision, keyName string, value interface{}) (int, Revision) {
234 n.Lock()
235 defer n.Unlock()
236
237 for i, rev := range revs {
238 dataValue := reflect.ValueOf(rev.GetData())
239 dataStruct := GetAttributeStructure(rev.GetData(), keyName, 0)
240
241 fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
242
243 a := fmt.Sprintf("%s", fieldValue.Interface())
244 b := fmt.Sprintf("%s", value)
245 if a == b {
246 return i, revs[i]
247 }
248 }
249
250 return -1, nil
251}
252
253// Get retrieves the data from a node tree that resides at the specified path
254func (n *node) List(path string, hash string, depth int, deep bool, txid string) interface{} {
255 log.Debugw("node-list-request", log.Fields{"path": path, "hash": hash, "depth": depth, "deep": deep, "txid": txid})
256 if deep {
257 depth = -1
258 }
259
260 for strings.HasPrefix(path, "/") {
261 path = path[1:]
262 }
263
264 var branch *Branch
265 var rev Revision
266
267 if branch = n.GetBranch(txid); txid == "" || branch == nil {
268 branch = n.GetBranch(NONE)
269 }
270
271 if hash != "" {
272 rev = branch.GetRevision(hash)
273 } else {
274 rev = branch.GetLatest()
275 }
276
277 var result interface{}
278 var prList []interface{}
279 if pr := rev.LoadFromPersistence(path, txid); pr != nil {
280 for _, revEntry := range pr {
281 prList = append(prList, revEntry.GetData())
282 }
283 result = prList
284 }
285
286 return result
287}
288
289// Get retrieves the data from a node tree that resides at the specified path
290func (n *node) Get(path string, hash string, depth int, reconcile bool, txid string) interface{} {
291 log.Debugw("node-get-request", log.Fields{"path": path, "hash": hash, "depth": depth, "reconcile": reconcile,
292 "txid": txid})
293 for strings.HasPrefix(path, "/") {
294 path = path[1:]
295 }
296
297 var branch *Branch
298 var rev Revision
299
300 if branch = n.GetBranch(txid); txid == "" || branch == nil {
301 branch = n.GetBranch(NONE)
302 }
303
304 if hash != "" {
305 rev = branch.GetRevision(hash)
306 } else {
307 rev = branch.GetLatest()
308 }
309
310 var result interface{}
311
312 // If there is not request to reconcile, try to get it from memory
313 if !reconcile {
314 if result = n.getPath(rev.GetBranch().GetLatest(), path, depth);
315 result != nil && reflect.ValueOf(result).IsValid() && !reflect.ValueOf(result).IsNil() {
316 return result
317 }
318 }
319
320 // If we got to this point, we are either trying to reconcile with the db or
321 // or we simply failed at getting information from memory
322 if n.Root.KvStore != nil {
323 var prList []interface{}
324 if pr := rev.LoadFromPersistence(path, txid); pr != nil && len(pr) > 0 {
325 // Did we receive a single or multiple revisions?
326 if len(pr) > 1 {
327 for _, revEntry := range pr {
328 prList = append(prList, revEntry.GetData())
329 }
330 result = prList
331 } else {
332 result = pr[0].GetData()
333 }
334 }
335 }
336
337 return result
338}
339
340// getPath traverses the specified path and retrieves the data associated to it
341func (n *node) getPath(rev Revision, path string, depth int) interface{} {
342 if path == "" {
343 return n.getData(rev, depth)
344 }
345
346 partition := strings.SplitN(path, "/", 2)
347 name := partition[0]
348
349 if len(partition) < 2 {
350 path = ""
351 } else {
352 path = partition[1]
353 }
354
355 names := ChildrenFields(n.Type)
356 field := names[name]
357
358 if field != nil && field.IsContainer {
359 children := make([]Revision, len(rev.GetChildren(name)))
360 copy(children, rev.GetChildren(name))
361
362 if field.Key != "" {
363 if path != "" {
364 partition = strings.SplitN(path, "/", 2)
365 key := partition[0]
366 path = ""
367 keyValue := field.KeyFromStr(key)
368 if _, childRev := n.findRevByKey(children, field.Key, keyValue); childRev == nil {
369 return nil
370 } else {
371 childNode := childRev.GetNode()
372 return childNode.getPath(childRev, path, depth)
373 }
374 } else {
375 var response []interface{}
376 for _, childRev := range children {
377 childNode := childRev.GetNode()
378 value := childNode.getData(childRev, depth)
379 response = append(response, value)
380 }
381 return response
382 }
383 } else {
384 var response []interface{}
385 if path != "" {
386 // TODO: raise error
387 return response
388 }
389 for _, childRev := range children {
390 childNode := childRev.GetNode()
391 value := childNode.getData(childRev, depth)
392 response = append(response, value)
393 }
394 return response
395 }
396 }
397
398 childRev := rev.GetChildren(name)[0]
399 childNode := childRev.GetNode()
400 return childNode.getPath(childRev, path, depth)
401}
402
403// getData retrieves the data from a node revision
404func (n *node) getData(rev Revision, depth int) interface{} {
405 msg := rev.GetBranch().GetLatest().Get(depth)
406 var modifiedMsg interface{}
407
408 if n.GetProxy() != nil {
409 log.Debugw("invoking-get-callbacks", log.Fields{"data": msg})
410 if modifiedMsg = n.GetProxy().InvokeCallbacks(GET, false, msg); modifiedMsg != nil {
411 msg = modifiedMsg
412 }
413
414 }
415
416 return msg
417}
418
419// Update changes the content of a node at the specified path with the provided data
420func (n *node) Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
421 log.Debugw("node-update-request", log.Fields{"path": path, "strict": strict, "txid": txid, "makeBranch": makeBranch})
422
423 for strings.HasPrefix(path, "/") {
424 path = path[1:]
425 }
426
427 var branch *Branch
428 if txid == "" {
429 branch = n.GetBranch(NONE)
430 } else if branch = n.GetBranch(txid); branch == nil {
431 branch = makeBranch(n)
432 }
433
434 if branch.GetLatest() != nil {
435 log.Debugf("Branch data : %+v, Passed data: %+v", branch.GetLatest().GetData(), data)
436 }
437 if path == "" {
438 return n.doUpdate(branch, data, strict)
439 }
440
441 rev := branch.GetLatest()
442
443 partition := strings.SplitN(path, "/", 2)
444 name := partition[0]
445
446 if len(partition) < 2 {
447 path = ""
448 } else {
449 path = partition[1]
450 }
451
452 field := ChildrenFields(n.Type)[name]
453 var children []Revision
454
455 if field == nil {
456 return n.doUpdate(branch, data, strict)
457 }
458
459 if field.IsContainer {
460 if path == "" {
461 log.Errorf("cannot update a list")
462 } else if field.Key != "" {
463 partition := strings.SplitN(path, "/", 2)
464 key := partition[0]
465 if len(partition) < 2 {
466 path = ""
467 } else {
468 path = partition[1]
469 }
470 keyValue := field.KeyFromStr(key)
471
472 children = make([]Revision, len(rev.GetChildren(name)))
473 copy(children, rev.GetChildren(name))
474
475 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
476 childNode := childRev.GetNode()
477
478 // Save proxy in child node to ensure callbacks are called later on
479 // only assign in cases of non sub-folder proxies, i.e. "/"
480 if childNode.Proxy == nil && n.Proxy != nil && n.Proxy.getFullPath() == "" {
481 childNode.Proxy = n.Proxy
482 }
483
484 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
485
486 if newChildRev.GetHash() == childRev.GetHash() {
487 if newChildRev != childRev {
488 log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
489 newChildRev.ClearHash()
490 }
491 return branch.GetLatest()
492 }
493
494 _, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0)
495
496 _newKeyType := fmt.Sprintf("%s", newKey)
497 _keyValueType := fmt.Sprintf("%s", keyValue)
498
499 if _newKeyType != _keyValueType {
500 log.Errorf("cannot change key field")
501 }
502
503 // Prefix the hash value with the data type (e.g. devices, logical_devices, adapters)
504 newChildRev.SetName(name + "/" + _keyValueType)
505 children[idx] = newChildRev
506
507 updatedRev := rev.UpdateChildren(name, children, branch)
508
509 branch.GetLatest().Drop(txid, false)
510 n.makeLatest(branch, updatedRev, nil)
511
512 return newChildRev
513
514 } else {
515 log.Errorf("cannot index into container with no keys")
516 }
517 } else {
518 childRev := rev.GetChildren(name)[0]
519 childNode := childRev.GetNode()
520 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
521 updatedRev := rev.UpdateChildren(name, []Revision{newChildRev}, branch)
522 rev.Drop(txid, false)
523 n.makeLatest(branch, updatedRev, nil)
524
525 return newChildRev
526 }
527
528 return nil
529}
530
531func (n *node) doUpdate(branch *Branch, data interface{}, strict bool) Revision {
532 log.Debugf("Comparing types - expected: %+v, actual: %+v &&&&&& %s", reflect.ValueOf(n.Type).Type(),
533 reflect.TypeOf(data),
534 string(debug.Stack()))
535
536 if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
537 // TODO raise error
538 log.Errorf("data does not match type: %+v", n.Type)
539 return nil
540 }
541
542 // TODO: validate that this actually works
543 //if n.hasChildren(data) {
544 // return nil
545 //}
546
547 if n.GetProxy() != nil {
548 log.Debug("invoking proxy PRE_UPDATE Callbacks")
549 n.GetProxy().InvokeCallbacks(PRE_UPDATE, false, branch.GetLatest(), data)
550 }
551
552 if branch.GetLatest().GetData().(proto.Message).String() != data.(proto.Message).String() {
553 if strict {
554 // TODO: checkAccessViolations(data, Branch.GetLatest.data)
555 log.Debugf("checking access violations")
556 }
557
558 rev := branch.GetLatest().UpdateData(data, branch)
559 changes := []ChangeTuple{{POST_UPDATE, branch.GetLatest().GetData(), rev.GetData()}}
560
561 // FIXME VOL-1293: the following statement corrupts the kv when using a subproxy (commenting for now)
562 // FIXME VOL-1293 cont'd: need to figure out the required conditions otherwise we are not cleaning up entries
563 //branch.GetLatest().Drop(branch.Txid, false)
564
565 n.makeLatest(branch, rev, changes)
566
567 return rev
568 }
569
570 return branch.GetLatest()
571}
572
573// Add inserts a new node at the specified path with the provided data
574func (n *node) Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
575 log.Debugw("node-add-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
576
577 for strings.HasPrefix(path, "/") {
578 path = path[1:]
579 }
580 if path == "" {
581 // TODO raise error
582 log.Errorf("cannot add for non-container mode")
583 return nil
584 }
585
586 var branch *Branch
587 if txid == "" {
588 branch = n.GetBranch(NONE)
589 } else if branch = n.GetBranch(txid); branch == nil {
590 branch = makeBranch(n)
591 }
592
593 rev := branch.GetLatest()
594
595 partition := strings.SplitN(path, "/", 2)
596 name := partition[0]
597
598 if len(partition) < 2 {
599 path = ""
600 } else {
601 path = partition[1]
602 }
603
604 field := ChildrenFields(n.Type)[name]
605
606 var children []Revision
607
608 if field.IsContainer {
609 if path == "" {
610 if field.Key != "" {
611 if n.GetProxy() != nil {
612 log.Debug("invoking proxy PRE_ADD Callbacks")
613 n.GetProxy().InvokeCallbacks(PRE_ADD, false, data)
614 }
615
616 children = make([]Revision, len(rev.GetChildren(name)))
617 copy(children, rev.GetChildren(name))
618
619 _, key := GetAttributeValue(data, field.Key, 0)
620
621 if _, exists := n.findRevByKey(children, field.Key, key.String()); exists != nil {
622 // TODO raise error
623 log.Warnw("duplicate-key-found", log.Fields{"key": key.String()})
624 return exists
625 }
626 childRev := n.MakeNode(data, "").Latest()
627
628 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
629 childRev.SetName(name + "/" + key.String())
630
631 // Create watch for <component>/<key>
632 childRev.SetupWatch(childRev.GetName())
633
634 children = append(children, childRev)
635 rev = rev.UpdateChildren(name, children, branch)
636 changes := []ChangeTuple{{POST_ADD, nil, childRev.GetData()}}
637
638 rev.Drop(txid, false)
639 n.makeLatest(branch, rev, changes)
640
641 return childRev
642 }
643 log.Errorf("cannot add to non-keyed container")
644
645 } else if field.Key != "" {
646 partition := strings.SplitN(path, "/", 2)
647 key := partition[0]
648 if len(partition) < 2 {
649 path = ""
650 } else {
651 path = partition[1]
652 }
653 keyValue := field.KeyFromStr(key)
654
655 children = make([]Revision, len(rev.GetChildren(name)))
656 copy(children, rev.GetChildren(name))
657
658 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
659
660 childNode := childRev.GetNode()
661 newChildRev := childNode.Add(path, data, txid, makeBranch)
662
663 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
664 childRev.SetName(name + "/" + keyValue.(string))
665
666 children[idx] = newChildRev
667
668 rev = rev.UpdateChildren(name, children, branch)
669 rev.Drop(txid, false)
670 n.makeLatest(branch, rev.GetBranch().GetLatest(), nil)
671
672 return newChildRev
673 } else {
674 log.Errorf("cannot add to non-keyed container")
675 }
676 } else {
677 log.Errorf("cannot add to non-container field")
678 }
679
680 return nil
681}
682
683// Remove eliminates a node at the specified path
684func (n *node) Remove(path string, txid string, makeBranch MakeBranchFunction) Revision {
685 log.Debugw("node-remove-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
686
687 for strings.HasPrefix(path, "/") {
688 path = path[1:]
689 }
690 if path == "" {
691 // TODO raise error
692 log.Errorf("cannot remove for non-container mode")
693 }
694 var branch *Branch
695 if txid == "" {
696 branch = n.GetBranch(NONE)
697 } else if branch = n.GetBranch(txid); branch == nil {
698 branch = makeBranch(n)
699 }
700
701 rev := branch.GetLatest()
702
703 partition := strings.SplitN(path, "/", 2)
704 name := partition[0]
705 if len(partition) < 2 {
706 path = ""
707 } else {
708 path = partition[1]
709 }
710
711 field := ChildrenFields(n.Type)[name]
712 var children []Revision
713 postAnnouncement := []ChangeTuple{}
714
715 if field.IsContainer {
716 if path == "" {
717 log.Errorw("cannot-remove-without-key", log.Fields{"name": name, "key": path})
718 } else if field.Key != "" {
719 partition := strings.SplitN(path, "/", 2)
720 key := partition[0]
721 if len(partition) < 2 {
722 path = ""
723 } else {
724 path = partition[1]
725 }
726
727 keyValue := field.KeyFromStr(key)
728 children = make([]Revision, len(rev.GetChildren(name)))
729 copy(children, rev.GetChildren(name))
730
731 if path != "" {
732 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
733 childNode := childRev.GetNode()
734 if childNode.Proxy == nil {
735 childNode.Proxy = n.Proxy
736 }
737 newChildRev := childNode.Remove(path, txid, makeBranch)
738 children[idx] = newChildRev
739 rev.SetChildren(name, children)
740 branch.GetLatest().Drop(txid, false)
741 n.makeLatest(branch, rev, nil)
742 return nil
743 }
744
745 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
746 if n.GetProxy() != nil {
747 data := childRev.GetData()
748 n.GetProxy().InvokeCallbacks(PRE_REMOVE, false, data)
749 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, data, nil})
750 } else {
751 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, childRev.GetData(), nil})
752 }
753
754 childRev.StorageDrop(txid, true)
755 children = append(children[:idx], children[idx+1:]...)
756 rev.SetChildren(name, children)
757
758 branch.GetLatest().Drop(txid, false)
759 n.makeLatest(branch, rev, postAnnouncement)
760
761 return rev
762 } else {
763 log.Errorw("failed-to-find-revision", log.Fields{"name": name, "key": keyValue.(string)})
764 }
765 }
766 log.Errorw("cannot-add-to-non-keyed-container", log.Fields{"name": name, "path": path, "fieldKey": field.Key})
767
768 } else {
769 log.Errorw("cannot-add-to-non-container-field", log.Fields{"name": name, "path": path})
770 }
771
772 return nil
773}
774
775// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
776
777// MakeBranchFunction is a type for function references intented to create a branch
778type MakeBranchFunction func(*node) *Branch
779
780// MakeBranch creates a new branch for the provided transaction id
781func (n *node) MakeBranch(txid string) *Branch {
782 branchPoint := n.GetBranch(NONE).GetLatest()
783 branch := NewBranch(n, txid, branchPoint, true)
784 n.SetBranch(txid, branch)
785 return branch
786}
787
788// DeleteBranch removes a branch with the specified id
789func (n *node) DeleteBranch(txid string) {
790 n.Lock()
791 defer n.Unlock()
792 delete(n.Branches, txid)
793}
794
795func (n *node) mergeChild(txid string, dryRun bool) func(Revision) Revision {
796 f := func(rev Revision) Revision {
797 childBranch := rev.GetBranch()
798
799 if childBranch.Txid == txid {
800 rev, _ = childBranch.Node.MergeBranch(txid, dryRun)
801 }
802
803 return rev
804 }
805 return f
806}
807
808// MergeBranch will integrate the contents of a transaction branch within the latest branch of a given node
809func (n *node) MergeBranch(txid string, dryRun bool) (Revision, error) {
810 srcBranch := n.GetBranch(txid)
811 dstBranch := n.GetBranch(NONE)
812
813 forkRev := srcBranch.Origin
814 srcRev := srcBranch.GetLatest()
815 dstRev := dstBranch.GetLatest()
816
817 rev, changes := Merge3Way(forkRev, srcRev, dstRev, n.mergeChild(txid, dryRun), dryRun)
818
819 if !dryRun {
820 if rev != nil {
821 rev.SetName(dstRev.GetName())
822 n.makeLatest(dstBranch, rev, changes)
823 }
824 n.DeleteBranch(txid)
825 }
826
827 // TODO: return proper error when one occurs
828 return rev, nil
829}
830
831// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diff utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
832
833//func (n *node) diff(hash1, hash2, txid string) {
834// branch := n.Branches[txid]
835// rev1 := branch.GetHash(hash1)
836// rev2 := branch.GetHash(hash2)
837//
838// if rev1.GetHash() == rev2.GetHash() {
839// // empty patch
840// } else {
841// // translate data to json and generate patch
842// patch, err := jsonpatch.MakePatch(rev1.GetData(), rev2.GetData())
843// patch.
844// }
845//}
846
847// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tag utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
848
849// TODO: is tag mgmt used in the python implementation? Need to validate
850
851// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
852
853func (n *node) hasChildren(data interface{}) bool {
854 for fieldName, field := range ChildrenFields(n.Type) {
855 _, fieldValue := GetAttributeValue(data, fieldName, 0)
856
857 if (field.IsContainer && fieldValue.Len() > 0) || !fieldValue.IsNil() {
858 log.Error("cannot update external children")
859 return true
860 }
861 }
862
863 return false
864}
865
866// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ node Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
867
868// CreateProxy returns a reference to a sub-tree of the data model
869func (n *node) CreateProxy(path string, exclusive bool) *Proxy {
870 return n.createProxy(path, path, n, exclusive)
871}
872
873func (n *node) createProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
874 for strings.HasPrefix(path, "/") {
875 path = path[1:]
876 }
877 if path == "" {
878 return n.makeProxy(path, fullPath, parentNode, exclusive)
879 }
880
881 rev := n.GetBranch(NONE).GetLatest()
882 partition := strings.SplitN(path, "/", 2)
883 name := partition[0]
884 if len(partition) < 2 {
885 path = ""
886 } else {
887 path = partition[1]
888 }
889
890 field := ChildrenFields(n.Type)[name]
891 if field.IsContainer {
892 if path == "" {
893 //log.Error("cannot proxy a container field")
894 newNode := n.MakeNode(reflect.New(field.ClassType.Elem()).Interface(), "")
895 return newNode.makeProxy(path, fullPath, parentNode, exclusive)
896 } else if field.Key != "" {
897 partition := strings.SplitN(path, "/", 2)
898 key := partition[0]
899 if len(partition) < 2 {
900 path = ""
901 } else {
902 path = partition[1]
903 }
904 keyValue := field.KeyFromStr(key)
905 var children []Revision
906 children = make([]Revision, len(rev.GetChildren(name)))
907 copy(children, rev.GetChildren(name))
908 if _, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
909 childNode := childRev.GetNode()
910 return childNode.createProxy(path, fullPath, n, exclusive)
911 }
912 } else {
913 log.Error("cannot index into container with no keys")
914 }
915 } else {
916 childRev := rev.GetChildren(name)[0]
917 childNode := childRev.GetNode()
918 return childNode.createProxy(path, fullPath, n, exclusive)
919 }
920
921 log.Warnf("Cannot create proxy - latest rev:%s, all revs:%+v", rev.GetHash(), n.GetBranch(NONE).Revisions)
922 return nil
923}
924
925func (n *node) makeProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
926 n.Lock()
927 defer n.Unlock()
928 r := &root{
929 node: n,
930 Callbacks: n.Root.GetCallbacks(),
931 NotificationCallbacks: n.Root.GetNotificationCallbacks(),
932 DirtyNodes: n.Root.DirtyNodes,
933 KvStore: n.Root.KvStore,
934 Loading: n.Root.Loading,
935 RevisionClass: n.Root.RevisionClass,
936 }
937
938 if n.Proxy == nil {
939 n.Proxy = NewProxy(r, n, parentNode, path, fullPath, exclusive)
940 } else {
941 if n.Proxy.Exclusive {
942 log.Error("node is already owned exclusively")
943 }
944 }
945
946 return n.Proxy
947}
948
949func (n *node) makeEventBus() *EventBus {
950 n.Lock()
951 defer n.Unlock()
952 if n.EventBus == nil {
953 n.EventBus = NewEventBus()
954 }
955 return n.EventBus
956}
957
958func (n *node) SetProxy(proxy *Proxy) {
959 n.Lock()
960 defer n.Unlock()
961 n.Proxy = proxy
962}
963
964func (n *node) GetProxy() *Proxy {
965 n.Lock()
966 defer n.Unlock()
967 return n.Proxy
968}
969
970func (n *node) GetBranch(key string) *Branch {
971 n.Lock()
972 defer n.Unlock()
973
974 if n.Branches != nil {
975 if branch, exists := n.Branches[key]; exists {
976 return branch
977 }
978 }
979 return nil
980}
981
982func (n *node) SetBranch(key string, branch *Branch) {
983 n.Lock()
984 defer n.Unlock()
985 n.Branches[key] = branch
986}
987
988func (n *node) GetRoot() *root {
989 n.Lock()
990 defer n.Unlock()
991 return n.Root
992}