blob: 7bfdca06753f85905bcb1815fd2b407c5a5ae08d [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
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400120 // Keep a reference to the current revision
121 var previous string
122 if branch.GetLatest() != nil {
123 previous = branch.GetLatest().GetHash()
124 }
125
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400126 branch.AddRevision(revision)
127
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400128 // If anything is new, then set the revision as the latest
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400129 if branch.GetLatest() == nil || revision.GetHash() != branch.GetLatest().GetHash() {
130 branch.SetLatest(revision)
131 }
132
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400133 // Delete the previous revision if anything has changed
134 if previous != "" && previous != branch.GetLatest().GetHash() {
135 branch.DeleteRevision(previous)
136 }
137
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400138 if changeAnnouncement != nil && branch.Txid == "" {
139 if n.Proxy != nil {
140 for _, change := range changeAnnouncement {
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400141 log.Debugw("adding-callback",
142 log.Fields{
143 "callbacks": n.Proxy.getCallbacks(change.Type),
144 "type": change.Type,
145 "previousData": change.PreviousData,
146 "latestData": change.LatestData,
147 })
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400148 n.Root.AddCallback(
149 n.Proxy.InvokeCallbacks,
150 change.Type,
151 true,
152 change.PreviousData,
153 change.LatestData)
154 }
155 }
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400156 }
157}
158
159// Latest returns the latest revision of node with or without the transaction id
160func (n *node) Latest(txid ...string) Revision {
161 var branch *Branch
162
163 if len(txid) > 0 && txid[0] != "" {
164 if branch = n.GetBranch(txid[0]); branch != nil {
165 return branch.GetLatest()
166 }
167 } else if branch = n.GetBranch(NONE); branch != nil {
168 return branch.GetLatest()
169 }
170 return nil
171}
172
173// initialize prepares the content of a node along with its possible ramifications
174func (n *node) initialize(data interface{}, txid string) {
175 n.Lock()
176 children := make(map[string][]Revision)
177 for fieldName, field := range ChildrenFields(n.Type) {
178 _, fieldValue := GetAttributeValue(data, fieldName, 0)
179
180 if fieldValue.IsValid() {
181 if field.IsContainer {
182 if field.Key != "" {
183 for i := 0; i < fieldValue.Len(); i++ {
184 v := fieldValue.Index(i)
185
186 if rev := n.MakeNode(v.Interface(), txid).Latest(txid); rev != nil {
187 children[fieldName] = append(children[fieldName], rev)
188 }
189
190 // TODO: The following logic was ported from v1.0. Need to verify if it is required
191 //var keysSeen []string
192 //_, key := GetAttributeValue(v.Interface(), field.Key, 0)
193 //for _, k := range keysSeen {
194 // if k == key.String() {
195 // //log.Errorf("duplicate key - %s", k)
196 // }
197 //}
198 //keysSeen = append(keysSeen, key.String())
199 }
200
201 } else {
202 for i := 0; i < fieldValue.Len(); i++ {
203 v := fieldValue.Index(i)
204 if newNodeRev := n.MakeNode(v.Interface(), txid).Latest(); newNodeRev != nil {
205 children[fieldName] = append(children[fieldName], newNodeRev)
206 }
207 }
208 }
209 } else {
210 if newNodeRev := n.MakeNode(fieldValue.Interface(), txid).Latest(); newNodeRev != nil {
211 children[fieldName] = append(children[fieldName], newNodeRev)
212 }
213 }
214 } else {
215 log.Errorf("field is invalid - %+v", fieldValue)
216 }
217 }
218 n.Unlock()
219
220 branch := NewBranch(n, "", nil, n.AutoPrune)
221 rev := n.MakeRevision(branch, data, children)
222 n.makeLatest(branch, rev, nil)
223
224 if txid == "" {
225 n.SetBranch(NONE, branch)
226 } else {
227 n.SetBranch(txid, branch)
228 }
229}
230
231// findRevByKey retrieves a specific revision from a node tree
232func (n *node) findRevByKey(revs []Revision, keyName string, value interface{}) (int, Revision) {
233 n.Lock()
234 defer n.Unlock()
235
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
242 a := fmt.Sprintf("%s", fieldValue.Interface())
243 b := fmt.Sprintf("%s", value)
244 if a == b {
245 return i, revs[i]
246 }
247 }
248
249 return -1, nil
250}
251
252// Get retrieves the data from a node tree that resides at the specified path
253func (n *node) List(path string, hash string, depth int, deep bool, txid string) interface{} {
254 log.Debugw("node-list-request", log.Fields{"path": path, "hash": hash, "depth": depth, "deep": deep, "txid": txid})
255 if deep {
256 depth = -1
257 }
258
259 for strings.HasPrefix(path, "/") {
260 path = path[1:]
261 }
262
263 var branch *Branch
264 var rev Revision
265
266 if branch = n.GetBranch(txid); txid == "" || branch == nil {
267 branch = n.GetBranch(NONE)
268 }
269
270 if hash != "" {
271 rev = branch.GetRevision(hash)
272 } else {
273 rev = branch.GetLatest()
274 }
275
276 var result interface{}
277 var prList []interface{}
278 if pr := rev.LoadFromPersistence(path, txid); pr != nil {
279 for _, revEntry := range pr {
280 prList = append(prList, revEntry.GetData())
281 }
282 result = prList
283 }
284
285 return result
286}
287
288// Get retrieves the data from a node tree that resides at the specified path
289func (n *node) Get(path string, hash string, depth int, reconcile bool, txid string) interface{} {
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400290 log.Debugw("node-get-request", log.Fields{"path": path, "hash": hash, "depth": depth, "reconcile": reconcile, "txid": txid})
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400291 for strings.HasPrefix(path, "/") {
292 path = path[1:]
293 }
294
295 var branch *Branch
296 var rev Revision
297
298 if branch = n.GetBranch(txid); txid == "" || branch == nil {
299 branch = n.GetBranch(NONE)
300 }
301
302 if hash != "" {
303 rev = branch.GetRevision(hash)
304 } else {
305 rev = branch.GetLatest()
306 }
307
308 var result interface{}
309
310 // If there is not request to reconcile, try to get it from memory
311 if !reconcile {
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400312 if result = n.getPath(rev.GetBranch().GetLatest(), path, depth); result != nil && reflect.ValueOf(result).IsValid() && !reflect.ValueOf(result).IsNil() {
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400313 return result
314 }
315 }
316
317 // If we got to this point, we are either trying to reconcile with the db or
318 // or we simply failed at getting information from memory
319 if n.Root.KvStore != nil {
320 var prList []interface{}
321 if pr := rev.LoadFromPersistence(path, txid); pr != nil && len(pr) > 0 {
322 // Did we receive a single or multiple revisions?
323 if len(pr) > 1 {
324 for _, revEntry := range pr {
325 prList = append(prList, revEntry.GetData())
326 }
327 result = prList
328 } else {
329 result = pr[0].GetData()
330 }
331 }
332 }
333
334 return result
335}
336
337// getPath traverses the specified path and retrieves the data associated to it
338func (n *node) getPath(rev Revision, path string, depth int) interface{} {
339 if path == "" {
340 return n.getData(rev, depth)
341 }
342
343 partition := strings.SplitN(path, "/", 2)
344 name := partition[0]
345
346 if len(partition) < 2 {
347 path = ""
348 } else {
349 path = partition[1]
350 }
351
352 names := ChildrenFields(n.Type)
353 field := names[name]
354
355 if field != nil && field.IsContainer {
356 children := make([]Revision, len(rev.GetChildren(name)))
357 copy(children, rev.GetChildren(name))
358
359 if field.Key != "" {
360 if path != "" {
361 partition = strings.SplitN(path, "/", 2)
362 key := partition[0]
363 path = ""
364 keyValue := field.KeyFromStr(key)
365 if _, childRev := n.findRevByKey(children, field.Key, keyValue); childRev == nil {
366 return nil
367 } else {
368 childNode := childRev.GetNode()
369 return childNode.getPath(childRev, path, depth)
370 }
371 } else {
372 var response []interface{}
373 for _, childRev := range children {
374 childNode := childRev.GetNode()
375 value := childNode.getData(childRev, depth)
376 response = append(response, value)
377 }
378 return response
379 }
380 } else {
381 var response []interface{}
382 if path != "" {
383 // TODO: raise error
384 return response
385 }
386 for _, childRev := range children {
387 childNode := childRev.GetNode()
388 value := childNode.getData(childRev, depth)
389 response = append(response, value)
390 }
391 return response
392 }
393 }
394
395 childRev := rev.GetChildren(name)[0]
396 childNode := childRev.GetNode()
397 return childNode.getPath(childRev, path, depth)
398}
399
400// getData retrieves the data from a node revision
401func (n *node) getData(rev Revision, depth int) interface{} {
402 msg := rev.GetBranch().GetLatest().Get(depth)
403 var modifiedMsg interface{}
404
405 if n.GetProxy() != nil {
406 log.Debugw("invoking-get-callbacks", log.Fields{"data": msg})
407 if modifiedMsg = n.GetProxy().InvokeCallbacks(GET, false, msg); modifiedMsg != nil {
408 msg = modifiedMsg
409 }
410
411 }
412
413 return msg
414}
415
416// Update changes the content of a node at the specified path with the provided data
417func (n *node) Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
418 log.Debugw("node-update-request", log.Fields{"path": path, "strict": strict, "txid": txid, "makeBranch": makeBranch})
419
420 for strings.HasPrefix(path, "/") {
421 path = path[1:]
422 }
423
424 var branch *Branch
425 if txid == "" {
426 branch = n.GetBranch(NONE)
427 } else if branch = n.GetBranch(txid); branch == nil {
428 branch = makeBranch(n)
429 }
430
431 if branch.GetLatest() != nil {
432 log.Debugf("Branch data : %+v, Passed data: %+v", branch.GetLatest().GetData(), data)
433 }
434 if path == "" {
435 return n.doUpdate(branch, data, strict)
436 }
437
438 rev := branch.GetLatest()
439
440 partition := strings.SplitN(path, "/", 2)
441 name := partition[0]
442
443 if len(partition) < 2 {
444 path = ""
445 } else {
446 path = partition[1]
447 }
448
449 field := ChildrenFields(n.Type)[name]
450 var children []Revision
451
452 if field == nil {
453 return n.doUpdate(branch, data, strict)
454 }
455
456 if field.IsContainer {
457 if path == "" {
458 log.Errorf("cannot update a list")
459 } else if field.Key != "" {
460 partition := strings.SplitN(path, "/", 2)
461 key := partition[0]
462 if len(partition) < 2 {
463 path = ""
464 } else {
465 path = partition[1]
466 }
467 keyValue := field.KeyFromStr(key)
468
469 children = make([]Revision, len(rev.GetChildren(name)))
470 copy(children, rev.GetChildren(name))
471
472 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400473
474 if childRev == nil {
475 return branch.GetLatest()
476 }
477
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400478 childNode := childRev.GetNode()
479
480 // Save proxy in child node to ensure callbacks are called later on
481 // only assign in cases of non sub-folder proxies, i.e. "/"
482 if childNode.Proxy == nil && n.Proxy != nil && n.Proxy.getFullPath() == "" {
483 childNode.Proxy = n.Proxy
484 }
485
486 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
487
488 if newChildRev.GetHash() == childRev.GetHash() {
489 if newChildRev != childRev {
490 log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
491 newChildRev.ClearHash()
492 }
493 return branch.GetLatest()
494 }
495
496 _, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0)
497
498 _newKeyType := fmt.Sprintf("%s", newKey)
499 _keyValueType := fmt.Sprintf("%s", keyValue)
500
501 if _newKeyType != _keyValueType {
502 log.Errorf("cannot change key field")
503 }
504
505 // Prefix the hash value with the data type (e.g. devices, logical_devices, adapters)
506 newChildRev.SetName(name + "/" + _keyValueType)
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400507
508 if idx >= 0 {
509 children[idx] = newChildRev
510 } else {
511 children = append(children, newChildRev)
512 }
513
514 branch.LatestLock.Lock()
515 defer branch.LatestLock.Unlock()
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400516
517 updatedRev := rev.UpdateChildren(name, children, branch)
518
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400519 n.makeLatest(branch, updatedRev, nil)
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400520 updatedRev.ChildDrop(name, childRev.GetHash())
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400521
522 return newChildRev
523
524 } else {
525 log.Errorf("cannot index into container with no keys")
526 }
527 } else {
528 childRev := rev.GetChildren(name)[0]
529 childNode := childRev.GetNode()
530 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400531
532 branch.LatestLock.Lock()
533 defer branch.LatestLock.Unlock()
534
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400535 updatedRev := rev.UpdateChildren(name, []Revision{newChildRev}, branch)
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400536 n.makeLatest(branch, updatedRev, nil)
537
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400538 updatedRev.ChildDrop(name, childRev.GetHash())
539
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400540 return newChildRev
541 }
542
543 return nil
544}
545
546func (n *node) doUpdate(branch *Branch, data interface{}, strict bool) Revision {
547 log.Debugf("Comparing types - expected: %+v, actual: %+v &&&&&& %s", reflect.ValueOf(n.Type).Type(),
548 reflect.TypeOf(data),
549 string(debug.Stack()))
550
551 if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
552 // TODO raise error
553 log.Errorf("data does not match type: %+v", n.Type)
554 return nil
555 }
556
557 // TODO: validate that this actually works
558 //if n.hasChildren(data) {
559 // return nil
560 //}
561
562 if n.GetProxy() != nil {
563 log.Debug("invoking proxy PRE_UPDATE Callbacks")
564 n.GetProxy().InvokeCallbacks(PRE_UPDATE, false, branch.GetLatest(), data)
565 }
566
567 if branch.GetLatest().GetData().(proto.Message).String() != data.(proto.Message).String() {
568 if strict {
569 // TODO: checkAccessViolations(data, Branch.GetLatest.data)
570 log.Debugf("checking access violations")
571 }
572
573 rev := branch.GetLatest().UpdateData(data, branch)
574 changes := []ChangeTuple{{POST_UPDATE, branch.GetLatest().GetData(), rev.GetData()}}
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400575 n.makeLatest(branch, rev, changes)
576
577 return rev
578 }
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400579 return branch.GetLatest()
580}
581
582// Add inserts a new node at the specified path with the provided data
583func (n *node) Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
584 log.Debugw("node-add-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
585
586 for strings.HasPrefix(path, "/") {
587 path = path[1:]
588 }
589 if path == "" {
590 // TODO raise error
591 log.Errorf("cannot add for non-container mode")
592 return nil
593 }
594
595 var branch *Branch
596 if txid == "" {
597 branch = n.GetBranch(NONE)
598 } else if branch = n.GetBranch(txid); branch == nil {
599 branch = makeBranch(n)
600 }
601
602 rev := branch.GetLatest()
603
604 partition := strings.SplitN(path, "/", 2)
605 name := partition[0]
606
607 if len(partition) < 2 {
608 path = ""
609 } else {
610 path = partition[1]
611 }
612
613 field := ChildrenFields(n.Type)[name]
614
615 var children []Revision
616
617 if field.IsContainer {
618 if path == "" {
619 if field.Key != "" {
620 if n.GetProxy() != nil {
621 log.Debug("invoking proxy PRE_ADD Callbacks")
622 n.GetProxy().InvokeCallbacks(PRE_ADD, false, data)
623 }
624
625 children = make([]Revision, len(rev.GetChildren(name)))
626 copy(children, rev.GetChildren(name))
627
628 _, key := GetAttributeValue(data, field.Key, 0)
629
630 if _, exists := n.findRevByKey(children, field.Key, key.String()); exists != nil {
631 // TODO raise error
632 log.Warnw("duplicate-key-found", log.Fields{"key": key.String()})
633 return exists
634 }
635 childRev := n.MakeNode(data, "").Latest()
636
637 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
638 childRev.SetName(name + "/" + key.String())
639
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400640 branch.LatestLock.Lock()
641 defer branch.LatestLock.Unlock()
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400642
643 children = append(children, childRev)
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400644
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400645 updatedRev := rev.UpdateChildren(name, children, branch)
646 changes := []ChangeTuple{{POST_ADD, nil, childRev.GetData()}}
647 childRev.SetupWatch(childRev.GetName())
648
649 n.makeLatest(branch, updatedRev, changes)
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400650
651 return childRev
652 }
653 log.Errorf("cannot add to non-keyed container")
654
655 } else if field.Key != "" {
656 partition := strings.SplitN(path, "/", 2)
657 key := partition[0]
658 if len(partition) < 2 {
659 path = ""
660 } else {
661 path = partition[1]
662 }
663 keyValue := field.KeyFromStr(key)
664
665 children = make([]Revision, len(rev.GetChildren(name)))
666 copy(children, rev.GetChildren(name))
667
668 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
669
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400670 if childRev == nil {
671 return branch.GetLatest()
672 }
673
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400674 childNode := childRev.GetNode()
675 newChildRev := childNode.Add(path, data, txid, makeBranch)
676
677 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
678 childRev.SetName(name + "/" + keyValue.(string))
679
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400680 if idx >= 0 {
681 children[idx] = newChildRev
682 } else {
683 children = append(children, newChildRev)
684 }
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400685
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400686 branch.LatestLock.Lock()
687 defer branch.LatestLock.Unlock()
688
689 updatedRev := rev.UpdateChildren(name, children, branch)
690 n.makeLatest(branch, updatedRev, nil)
691
692 updatedRev.ChildDrop(name, childRev.GetHash())
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400693
694 return newChildRev
695 } else {
696 log.Errorf("cannot add to non-keyed container")
697 }
698 } else {
699 log.Errorf("cannot add to non-container field")
700 }
701
702 return nil
703}
704
705// Remove eliminates a node at the specified path
706func (n *node) Remove(path string, txid string, makeBranch MakeBranchFunction) Revision {
707 log.Debugw("node-remove-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
708
709 for strings.HasPrefix(path, "/") {
710 path = path[1:]
711 }
712 if path == "" {
713 // TODO raise error
714 log.Errorf("cannot remove for non-container mode")
715 }
716 var branch *Branch
717 if txid == "" {
718 branch = n.GetBranch(NONE)
719 } else if branch = n.GetBranch(txid); branch == nil {
720 branch = makeBranch(n)
721 }
722
723 rev := branch.GetLatest()
724
725 partition := strings.SplitN(path, "/", 2)
726 name := partition[0]
727 if len(partition) < 2 {
728 path = ""
729 } else {
730 path = partition[1]
731 }
732
733 field := ChildrenFields(n.Type)[name]
734 var children []Revision
735 postAnnouncement := []ChangeTuple{}
736
737 if field.IsContainer {
738 if path == "" {
739 log.Errorw("cannot-remove-without-key", log.Fields{"name": name, "key": path})
740 } else if field.Key != "" {
741 partition := strings.SplitN(path, "/", 2)
742 key := partition[0]
743 if len(partition) < 2 {
744 path = ""
745 } else {
746 path = partition[1]
747 }
748
749 keyValue := field.KeyFromStr(key)
750 children = make([]Revision, len(rev.GetChildren(name)))
751 copy(children, rev.GetChildren(name))
752
753 if path != "" {
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400754 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
755 childNode := childRev.GetNode()
756 if childNode.Proxy == nil {
757 childNode.Proxy = n.Proxy
758 }
759 newChildRev := childNode.Remove(path, txid, makeBranch)
760
761 if idx >= 0 {
762 children[idx] = newChildRev
763 } else {
764 children = append(children, newChildRev)
765 }
766
767 branch.LatestLock.Lock()
768 defer branch.LatestLock.Unlock()
769
770 rev.SetChildren(name, children)
771 branch.GetLatest().Drop(txid, false)
772 n.makeLatest(branch, rev, nil)
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400773 }
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400774 return branch.GetLatest()
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400775 }
776
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400777 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil && idx >= 0 {
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400778 if n.GetProxy() != nil {
779 data := childRev.GetData()
780 n.GetProxy().InvokeCallbacks(PRE_REMOVE, false, data)
781 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, data, nil})
782 } else {
783 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, childRev.GetData(), nil})
784 }
785
786 childRev.StorageDrop(txid, true)
Matt Jeanneret384d8c92019-05-06 14:27:31 -0400787
788 branch.LatestLock.Lock()
789 defer branch.LatestLock.Unlock()
790
Matt Jeanneretcab955f2019-04-10 15:45:57 -0400791 children = append(children[:idx], children[idx+1:]...)
792 rev.SetChildren(name, children)
793
794 branch.GetLatest().Drop(txid, false)
795 n.makeLatest(branch, rev, postAnnouncement)
796
797 return rev
798 } else {
799 log.Errorw("failed-to-find-revision", log.Fields{"name": name, "key": keyValue.(string)})
800 }
801 }
802 log.Errorw("cannot-add-to-non-keyed-container", log.Fields{"name": name, "path": path, "fieldKey": field.Key})
803
804 } else {
805 log.Errorw("cannot-add-to-non-container-field", log.Fields{"name": name, "path": path})
806 }
807
808 return nil
809}
810
811// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
812
813// MakeBranchFunction is a type for function references intented to create a branch
814type MakeBranchFunction func(*node) *Branch
815
816// MakeBranch creates a new branch for the provided transaction id
817func (n *node) MakeBranch(txid string) *Branch {
818 branchPoint := n.GetBranch(NONE).GetLatest()
819 branch := NewBranch(n, txid, branchPoint, true)
820 n.SetBranch(txid, branch)
821 return branch
822}
823
824// DeleteBranch removes a branch with the specified id
825func (n *node) DeleteBranch(txid string) {
826 n.Lock()
827 defer n.Unlock()
828 delete(n.Branches, txid)
829}
830
831func (n *node) mergeChild(txid string, dryRun bool) func(Revision) Revision {
832 f := func(rev Revision) Revision {
833 childBranch := rev.GetBranch()
834
835 if childBranch.Txid == txid {
836 rev, _ = childBranch.Node.MergeBranch(txid, dryRun)
837 }
838
839 return rev
840 }
841 return f
842}
843
844// MergeBranch will integrate the contents of a transaction branch within the latest branch of a given node
845func (n *node) MergeBranch(txid string, dryRun bool) (Revision, error) {
846 srcBranch := n.GetBranch(txid)
847 dstBranch := n.GetBranch(NONE)
848
849 forkRev := srcBranch.Origin
850 srcRev := srcBranch.GetLatest()
851 dstRev := dstBranch.GetLatest()
852
853 rev, changes := Merge3Way(forkRev, srcRev, dstRev, n.mergeChild(txid, dryRun), dryRun)
854
855 if !dryRun {
856 if rev != nil {
857 rev.SetName(dstRev.GetName())
858 n.makeLatest(dstBranch, rev, changes)
859 }
860 n.DeleteBranch(txid)
861 }
862
863 // TODO: return proper error when one occurs
864 return rev, nil
865}
866
867// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diff utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
868
869//func (n *node) diff(hash1, hash2, txid string) {
870// branch := n.Branches[txid]
871// rev1 := branch.GetHash(hash1)
872// rev2 := branch.GetHash(hash2)
873//
874// if rev1.GetHash() == rev2.GetHash() {
875// // empty patch
876// } else {
877// // translate data to json and generate patch
878// patch, err := jsonpatch.MakePatch(rev1.GetData(), rev2.GetData())
879// patch.
880// }
881//}
882
883// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tag utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
884
885// TODO: is tag mgmt used in the python implementation? Need to validate
886
887// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
888
889func (n *node) hasChildren(data interface{}) bool {
890 for fieldName, field := range ChildrenFields(n.Type) {
891 _, fieldValue := GetAttributeValue(data, fieldName, 0)
892
893 if (field.IsContainer && fieldValue.Len() > 0) || !fieldValue.IsNil() {
894 log.Error("cannot update external children")
895 return true
896 }
897 }
898
899 return false
900}
901
902// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ node Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
903
904// CreateProxy returns a reference to a sub-tree of the data model
905func (n *node) CreateProxy(path string, exclusive bool) *Proxy {
906 return n.createProxy(path, path, n, exclusive)
907}
908
909func (n *node) createProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
910 for strings.HasPrefix(path, "/") {
911 path = path[1:]
912 }
913 if path == "" {
914 return n.makeProxy(path, fullPath, parentNode, exclusive)
915 }
916
917 rev := n.GetBranch(NONE).GetLatest()
918 partition := strings.SplitN(path, "/", 2)
919 name := partition[0]
920 if len(partition) < 2 {
921 path = ""
922 } else {
923 path = partition[1]
924 }
925
926 field := ChildrenFields(n.Type)[name]
927 if field.IsContainer {
928 if path == "" {
929 //log.Error("cannot proxy a container field")
930 newNode := n.MakeNode(reflect.New(field.ClassType.Elem()).Interface(), "")
931 return newNode.makeProxy(path, fullPath, parentNode, exclusive)
932 } else if field.Key != "" {
933 partition := strings.SplitN(path, "/", 2)
934 key := partition[0]
935 if len(partition) < 2 {
936 path = ""
937 } else {
938 path = partition[1]
939 }
940 keyValue := field.KeyFromStr(key)
941 var children []Revision
942 children = make([]Revision, len(rev.GetChildren(name)))
943 copy(children, rev.GetChildren(name))
944 if _, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
945 childNode := childRev.GetNode()
946 return childNode.createProxy(path, fullPath, n, exclusive)
947 }
948 } else {
949 log.Error("cannot index into container with no keys")
950 }
951 } else {
952 childRev := rev.GetChildren(name)[0]
953 childNode := childRev.GetNode()
954 return childNode.createProxy(path, fullPath, n, exclusive)
955 }
956
957 log.Warnf("Cannot create proxy - latest rev:%s, all revs:%+v", rev.GetHash(), n.GetBranch(NONE).Revisions)
958 return nil
959}
960
961func (n *node) makeProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
962 n.Lock()
963 defer n.Unlock()
964 r := &root{
965 node: n,
966 Callbacks: n.Root.GetCallbacks(),
967 NotificationCallbacks: n.Root.GetNotificationCallbacks(),
968 DirtyNodes: n.Root.DirtyNodes,
969 KvStore: n.Root.KvStore,
970 Loading: n.Root.Loading,
971 RevisionClass: n.Root.RevisionClass,
972 }
973
974 if n.Proxy == nil {
975 n.Proxy = NewProxy(r, n, parentNode, path, fullPath, exclusive)
976 } else {
977 if n.Proxy.Exclusive {
978 log.Error("node is already owned exclusively")
979 }
980 }
981
982 return n.Proxy
983}
984
985func (n *node) makeEventBus() *EventBus {
986 n.Lock()
987 defer n.Unlock()
988 if n.EventBus == nil {
989 n.EventBus = NewEventBus()
990 }
991 return n.EventBus
992}
993
994func (n *node) SetProxy(proxy *Proxy) {
995 n.Lock()
996 defer n.Unlock()
997 n.Proxy = proxy
998}
999
1000func (n *node) GetProxy() *Proxy {
1001 n.Lock()
1002 defer n.Unlock()
1003 return n.Proxy
1004}
1005
1006func (n *node) GetBranch(key string) *Branch {
1007 n.Lock()
1008 defer n.Unlock()
1009
1010 if n.Branches != nil {
1011 if branch, exists := n.Branches[key]; exists {
1012 return branch
1013 }
1014 }
1015 return nil
1016}
1017
1018func (n *node) SetBranch(key string, branch *Branch) {
1019 n.Lock()
1020 defer n.Unlock()
1021 n.Branches[key] = branch
1022}
1023
1024func (n *node) GetRoot() *root {
1025 n.Lock()
1026 defer n.Unlock()
1027 return n.Root
1028}