blob: 90ab6662a51a464210eb3641a34c9b646eb9d657 [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 Barbarie4a2564d2018-07-26 11:02:58 -040016package model
17
18import (
19 "fmt"
20 "github.com/golang/protobuf/proto"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040021 "github.com/opencord/voltha-go/common/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040022 "reflect"
23 "strings"
24)
25
26const (
27 NONE string = "none"
28)
29
30type Node struct {
31 root *Root
32 Type interface{}
33 Branches map[string]*Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -040034 Tags map[string]Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040035 Proxy *Proxy
36 EventBus *EventBus
37 AutoPrune bool
38}
39
40func NewNode(root *Root, initialData interface{}, autoPrune bool, txid string) *Node {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040041 n := &Node{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040042
Stephane Barbarieec0919b2018-09-05 14:14:29 -040043 n.root = root
44 n.Branches = make(map[string]*Branch)
45 n.Tags = make(map[string]Revision)
46 n.Proxy = nil
47 n.EventBus = nil
48 n.AutoPrune = autoPrune
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040049
50 if IsProtoMessage(initialData) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040051 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040052 dataCopy := proto.Clone(initialData.(proto.Message))
Stephane Barbarieec0919b2018-09-05 14:14:29 -040053 n.initialize(dataCopy, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040054 } else if reflect.ValueOf(initialData).IsValid() {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040055 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040056 } else {
57 // not implemented error
58 fmt.Errorf("cannot process initial data - %+v", initialData)
59 }
60
Stephane Barbarieec0919b2018-09-05 14:14:29 -040061 return n
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040062}
63
Stephane Barbarieec0919b2018-09-05 14:14:29 -040064func (n *Node) MakeNode(data interface{}, txid string) *Node {
65 return NewNode(n.root, data, true, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040066}
67
Stephane Barbarieec0919b2018-09-05 14:14:29 -040068func (n *Node) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
69 return n.root.MakeRevision(branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040070}
71
Stephane Barbarieec0919b2018-09-05 14:14:29 -040072func (n *Node) MakeLatest(branch *Branch, revision Revision, changeAnnouncement map[CallbackType][]interface{}) {
73 if _, ok := branch.Revisions[revision.GetHash()]; !ok {
74 branch.Revisions[revision.GetHash()] = revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040075 }
76
Stephane Barbarieec0919b2018-09-05 14:14:29 -040077 if branch.Latest == nil || revision.GetHash() != branch.Latest.GetHash() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040078 branch.Latest = revision
79 }
80
81 if changeAnnouncement != nil && branch.Txid == "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040082 if n.Proxy != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040083 for changeType, data := range changeAnnouncement {
84 // TODO: Invoke callback
85 fmt.Printf("invoking callback - changeType: %+v, data:%+v\n", changeType, data)
86 }
87 }
88
89 for changeType, data := range changeAnnouncement {
90 // TODO: send notifications
91 fmt.Printf("sending notification - changeType: %+v, data:%+v\n", changeType, data)
92 }
93 }
94}
95
Stephane Barbarieec0919b2018-09-05 14:14:29 -040096func (n *Node) Latest() Revision {
97 if branch, exists := n.Branches[NONE]; exists {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040098 return branch.Latest
99 }
100 return nil
101}
102
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400103func (n *Node) GetHash(hash string) Revision {
104 return n.Branches[NONE].Revisions[hash]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400105}
106
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400107func (n *Node) initialize(data interface{}, txid string) {
108 var children map[string][]Revision
109 children = make(map[string][]Revision)
110 for fieldName, field := range ChildrenFields(n.Type) {
111 _, fieldValue := GetAttributeValue(data, fieldName, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400112
113 if fieldValue.IsValid() {
114 if field.IsContainer {
115 if field.Key != "" {
116 var keysSeen []string
117
118 for i := 0; i < fieldValue.Len(); i++ {
119 v := fieldValue.Index(i)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400120 rev := n.MakeNode(v.Interface(), txid).Latest()
121 _, key := GetAttributeValue(v.Interface(), field.Key, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400122 for _, k := range keysSeen {
123 if k == key.String() {
124 fmt.Errorf("duplicate key - %s", k)
125 }
126 }
127 children[fieldName] = append(children[fieldName], rev)
128 keysSeen = append(keysSeen, key.String())
129 }
130
131 } else {
132 for i := 0; i < fieldValue.Len(); i++ {
133 v := fieldValue.Index(i)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400134 children[fieldName] = append(children[fieldName], n.MakeNode(v.Interface(), txid).Latest())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400135 }
136 }
137 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400138 children[fieldName] = append(children[fieldName], n.MakeNode(fieldValue.Interface(), txid).Latest())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400139 }
140 } else {
141 fmt.Errorf("field is invalid - %+v", fieldValue)
142 }
143 }
144 // FIXME: ClearField??? No such method in go protos. Reset?
145 //data.ClearField(field_name)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400146 branch := NewBranch(n, "", nil, n.AutoPrune)
147 rev := n.MakeRevision(branch, data, children)
148 n.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400149
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400150 if txid == "" {
151 n.Branches[NONE] = branch
152 } else {
153 n.Branches[txid] = branch
154 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400155}
156
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400157//
158// Get operation
159//
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400160func (n *Node) Get(path string, hash string, depth int, deep bool, txid string) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400161 if deep {
162 depth = -1
163 }
164
165 for strings.HasPrefix(path, "/") {
166 path = path[1:]
167 }
168
169 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400170 var rev Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400171
172 // FIXME: should empty txid be cleaned up?
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400173 if branch = n.Branches[txid]; txid == "" || branch == nil {
174 branch = n.Branches[NONE]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400175 }
176
177 if hash != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400178 rev = branch.Revisions[hash]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400179 } else {
180 rev = branch.Latest
181 }
182
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400183 return n.get(rev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400184}
185
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400186func (n *Node) findRevByKey(revs []Revision, keyName string, value string) (int, Revision) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400187 for i, rev := range revs {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400188 dataValue := reflect.ValueOf(rev.GetData())
189 dataStruct := GetAttributeStructure(rev.GetData(), keyName, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400190
191 fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
192
193 if fieldValue.Interface().(string) == value {
194 return i, rev
195 }
196 }
197
198 fmt.Errorf("key %s=%s not found", keyName, value)
199
200 return -1, nil
201}
202
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400203func (n *Node) get(rev Revision, path string, depth int) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400204 if path == "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400205 return n.doGet(rev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400206 }
207
208 partition := strings.SplitN(path, "/", 2)
209 name := partition[0]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400210
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400211 if len(partition) < 2 {
212 path = ""
213 } else {
214 path = partition[1]
215 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400216
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400217 names := ChildrenFields(n.Type)
218 field := names[name]
219
220 if field != nil && field.IsContainer {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400221 if field.Key != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400222 children := rev.GetChildren()[name]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400223 if path != "" {
224 partition = strings.SplitN(path, "/", 2)
225 key := partition[0]
226 path = ""
227 key = field.KeyFromStr(key).(string)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400228 if _, childRev := n.findRevByKey(children, field.Key, key); childRev == nil {
229 return nil
230 } else {
231 childNode := childRev.GetNode()
232 return childNode.get(childRev, path, depth)
233 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400234 } else {
235 var response []interface{}
236 for _, childRev := range children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400237 childNode := childRev.GetNode()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400238 value := childNode.doGet(childRev, depth)
239 response = append(response, value)
240 }
241 return response
242 }
243 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400244 var response []interface{}
245 if path != "" {
246 // TODO: raise error
247 return response
248 }
249 for _, childRev := range rev.GetChildren()[name] {
250 childNode := childRev.GetNode()
251 value := childNode.doGet(childRev, depth)
252 response = append(response, value)
253 }
254 return response
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400255 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400256 } else {
257 c1 := rev.GetChildren()[name]
258 childRev := c1[0]
259 childNode := childRev.GetNode()
260 return childNode.get(childRev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400261 }
262 return nil
263}
264
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400265func (n *Node) doGet(rev Revision, depth int) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400266 msg := rev.Get(depth)
267
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400268 if n.Proxy != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400269 // TODO: invoke GET callback
270 fmt.Println("invoking proxy GET Callbacks")
271 }
272 return msg
273}
274
275//
276// Update operation
277//
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400278func (n *Node) Update(path string, data interface{}, strict bool, txid string, makeBranch t_makeBranch) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400279 // FIXME: is this required ... a bit overkill to take out a "/"
280 for strings.HasPrefix(path, "/") {
281 path = path[1:]
282 }
283
284 var branch *Branch
285 var ok bool
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400286 if txid == "" {
287 branch = n.Branches[NONE]
288 } else if branch, ok = n.Branches[txid]; !ok {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400289 branch = makeBranch(n)
290 }
291
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400292 log.Debugf("Branch data : %+v, Passed data: %+v", branch.Latest.GetData(), data)
293
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400294 if path == "" {
295 return n.doUpdate(branch, data, strict)
296 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400297
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400298 // TODO missing some code here...
299 rev := branch.Latest
300
301 partition := strings.SplitN(path, "/", 2)
302 name := partition[0]
303
304 if len(partition) < 2 {
305 path = ""
306 } else {
307 path = partition[1]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400308 }
309
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400310 field := ChildrenFields(n.Type)[name]
311 var children []Revision
312
313 if field.IsContainer {
314 if path == "" {
315 fmt.Errorf("cannot update a list\n")
316 } else if field.Key != "" {
317 partition := strings.SplitN(path, "/", 2)
318 key := partition[0]
319 if len(partition) < 2 {
320 path = ""
321 } else {
322 path = partition[1]
323 }
324 key = field.KeyFromStr(key).(string)
325 // TODO. Est-ce que le copy ne fonctionne pas? dois-je plutôt faire un clone de chaque item?
326 for _, v := range rev.GetChildren()[name] {
327 revCopy := reflect.ValueOf(v).Interface().(Revision)
328 children = append(children, revCopy)
329 }
330 idx, childRev := n.findRevByKey(children, field.Key, key)
331 childNode := childRev.GetNode()
332 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
333 if newChildRev.GetHash() == childRev.GetHash() {
334 if newChildRev != childRev {
335 log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
336 newChildRev.ClearHash()
337 }
338 return branch.Latest
339 }
340 if _, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0); newKey.Interface().(string) != key {
341 fmt.Errorf("cannot change key field\n")
342 }
343 children[idx] = newChildRev
344 rev = rev.UpdateChildren(name, children, branch)
345 n.root.MakeLatest(branch, rev, nil)
346 return rev
347 } else {
348 fmt.Errorf("cannot index into container with no keys\n")
349 }
350 } else {
351 childRev := rev.GetChildren()[name][0]
352 childNode := childRev.GetNode()
353 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
354 rev = rev.UpdateChildren(name, []Revision{newChildRev}, branch)
355 n.root.MakeLatest(branch, rev, nil)
356 return rev
357 }
358 return nil
359}
360
361func (n *Node) doUpdate(branch *Branch, data interface{}, strict bool) Revision {
362 log.Debugf("Comparing types - expected: %+v, actual: %+v", reflect.ValueOf(n.Type).Type(), reflect.TypeOf(data))
363
364 if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
365 // TODO raise error
366 fmt.Errorf("data does not match type: %+v", n.Type)
367 return nil
368 }
369
370 // TODO: validate that this actually works
371 //if n.hasChildren(data) {
372 // return nil
373 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400374
375 if n.Proxy != nil {
376 // TODO: n.proxy.InvokeCallbacks(CallbackType.PRE_UPDATE, data)
377 fmt.Println("invoking proxy PRE_UPDATE Callbacks")
378 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400379 if !reflect.DeepEqual(branch.Latest.GetData(), data) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400380 if strict {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400381 // TODO: checkAccessViolations(data, Branch.GetLatest.data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400382 fmt.Println("checking access violations")
383 }
384 rev := branch.Latest.UpdateData(data, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400385 n.root.MakeLatest(branch, rev, nil) // TODO -> changeAnnouncement needs to be a tuple (CallbackType.
386 // POST_UPDATE, rev.data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400387 return rev
388 } else {
389 return branch.Latest
390 }
391}
392
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400393//
394// Add operation
395//
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400396func (n *Node) Add(path string, data interface{}, txid string, makeBranch t_makeBranch) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400397 for strings.HasPrefix(path, "/") {
398 path = path[1:]
399 }
400 if path == "" {
401 // TODO raise error
402 fmt.Errorf("cannot add for non-container mode\n")
403 }
404
405 var branch *Branch
406 var ok bool
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400407 if txid == "" {
408 branch = n.Branches[NONE]
409 } else if branch, ok = n.Branches[txid]; !ok {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400410 branch = makeBranch(n)
411 }
412
413 rev := branch.Latest
414
415 partition := strings.SplitN(path, "/", 2)
416 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400417
418 if len(partition) < 2 {
419 path = ""
420 } else {
421 path = partition[1]
422 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400423
424 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400425 var children []Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400426
427 if field.IsContainer {
428 if path == "" {
429 if field.Key != "" {
430 if n.Proxy != nil {
431 // TODO -> n.proxy.InvokeCallbacks(PRE_ADD, data)
432 fmt.Println("invoking proxy PRE_ADD Callbacks")
433 }
434
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400435 for _, v := range rev.GetChildren()[name] {
436 revCopy := reflect.ValueOf(v).Interface().(Revision)
437 children = append(children, revCopy)
438 }
439 _, key := GetAttributeValue(data, field.Key, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400440 if _, rev := n.findRevByKey(children, field.Key, key.String()); rev != nil {
441 // TODO raise error
442 fmt.Errorf("duplicate key found: %s", key.String())
443 }
444
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400445 childRev := n.MakeNode(data, "").Latest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400446 children = append(children, childRev)
447 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400448 n.root.MakeLatest(branch, rev, nil) // TODO -> changeAnnouncement needs to be a tuple (CallbackType.
449 // POST_ADD, rev.data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400450 return rev
451 } else {
452 fmt.Errorf("cannot add to non-keyed container\n")
453 }
454 } else if field.Key != "" {
455 partition := strings.SplitN(path, "/", 2)
456 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400457 if len(partition) < 2 {
458 path = ""
459 } else {
460 path = partition[1]
461 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400462 key = field.KeyFromStr(key).(string)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400463 copy(children, rev.GetChildren()[name])
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400464 idx, childRev := n.findRevByKey(children, field.Key, key)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400465 childNode := childRev.GetNode()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400466 newChildRev := childNode.Add(path, data, txid, makeBranch)
467 children[idx] = newChildRev
468 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400469 n.root.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400470 return rev
471 } else {
472 fmt.Errorf("cannot add to non-keyed container\n")
473 }
474 } else {
475 fmt.Errorf("cannot add to non-container field\n")
476 }
477 return nil
478}
479
480//
481// Remove operation
482//
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400483func (n *Node) Remove(path string, txid string, makeBranch t_makeBranch) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400484 for strings.HasPrefix(path, "/") {
485 path = path[1:]
486 }
487 if path == "" {
488 // TODO raise error
489 fmt.Errorf("cannot remove for non-container mode\n")
490 }
491 var branch *Branch
492 var ok bool
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400493 if txid == "" {
494 branch = n.Branches[NONE]
495 } else if branch, ok = n.Branches[txid]; !ok {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400496 branch = makeBranch(n)
497 }
498
499 rev := branch.Latest
500
501 partition := strings.SplitN(path, "/", 2)
502 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400503 if len(partition) < 2 {
504 path = ""
505 } else {
506 path = partition[1]
507 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400508
509 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400510 var children []Revision
511 post_anno := make(map[CallbackType][]interface{})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400512
513 if field.IsContainer {
514 if path == "" {
515 fmt.Errorf("cannot remove without a key\n")
516 } else if field.Key != "" {
517 partition := strings.SplitN(path, "/", 2)
518 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400519 if len(partition) < 2 {
520 path = ""
521 } else {
522 path = partition[1]
523 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400524 key = field.KeyFromStr(key).(string)
525 if path != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400526 for _, v := range rev.GetChildren()[name] {
527 newV := reflect.ValueOf(v).Interface().(Revision)
528 children = append(children, newV)
529 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400530 idx, childRev := n.findRevByKey(children, field.Key, key)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400531 childNode := childRev.GetNode()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400532 newChildRev := childNode.Remove(path, txid, makeBranch)
533 children[idx] = newChildRev
534 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400535 n.root.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400536 return rev
537 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400538 for _, v := range rev.GetChildren()[name] {
539 newV := reflect.ValueOf(v).Interface().(Revision)
540 children = append(children, newV)
541 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400542 idx, childRev := n.findRevByKey(children, field.Key, key)
543 if n.Proxy != nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400544 data := childRev.GetData()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400545 fmt.Println("invoking proxy PRE_REMOVE Callbacks")
546 fmt.Printf("setting POST_REMOVE Callbacks : %+v\n", data)
547 } else {
548 fmt.Println("setting POST_REMOVE Callbacks")
549 }
550 children = append(children[:idx], children[idx+1:]...)
551 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400552 n.root.MakeLatest(branch, rev, post_anno)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400553 return rev
554 }
555 } else {
556 fmt.Errorf("cannot add to non-keyed container\n")
557 }
558 } else {
559 fmt.Errorf("cannot add to non-container field\n")
560 }
561
562 return nil
563}
564
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400565// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
566
567type t_makeBranch func(*Node) *Branch
568
569func (n *Node) makeTxBranch(txid string) *Branch {
570 branchPoint := n.Branches[NONE].Latest
571 branch := NewBranch(n, txid, branchPoint, true)
572 n.Branches[txid] = branch
573 return branch
574}
575
576func (n *Node) deleteTxBranch(txid string) {
577 delete(n.Branches, txid)
578}
579
580func (n *Node) mergeChild(txid string, dryRun bool) func(Revision) Revision {
581 f := func(rev Revision) Revision {
582 childBranch := rev.GetBranch()
583
584 if childBranch.Txid == txid {
585 rev = childBranch.Node.mergeTxBranch(txid, dryRun)
586 }
587
588 return rev
589 }
590 return f
591}
592
593func (n *Node) mergeTxBranch(txid string, dryRun bool) Revision {
594 srcBranch := n.Branches[txid]
595 dstBranch := n.Branches[NONE]
596
597 forkRev := srcBranch.Origin
598 srcRev := srcBranch.Latest
599 dstRev := dstBranch.Latest
600
601 rev, changes := Merge3Way(forkRev, srcRev, dstRev, n.mergeChild(txid, dryRun), dryRun)
602
603 if !dryRun {
604 n.root.MakeLatest(dstBranch, rev, changes)
605 delete(n.Branches, txid)
606 }
607
608 return rev
609}
610
611// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diff utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
612
613//func (n *Node) diff(hash1, hash2, txid string) {
614// branch := n.Branches[txid]
615// rev1 := branch.get(hash1)
616// rev2 := branch.get(hash2)
617//
618// if rev1.GetHash() == rev2.GetHash() {
619// // empty patch
620// } else {
621// // translate data to json and generate patch
622// patch, err := jsonpatch.MakePatch(rev1.GetData(), rev2.GetData())
623// patch.
624// }
625//}
626
627// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tag utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
628
629// TODO: is tag mgmt used in the python implementation? Need to validate
630
631// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
632
633func (n *Node) hasChildren(data interface{}) bool {
634 for fieldName, field := range ChildrenFields(n.Type) {
635 _, fieldValue := GetAttributeValue(data, fieldName, 0)
636
637 if (field.IsContainer && fieldValue.Len() > 0) || !fieldValue.IsNil() {
638 log.Error("cannot update external children")
639 return true
640 }
641 }
642
643 return false
644}
645
646// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Node Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
647
648func (n *Node) GetProxy(path string, exclusive bool) *Proxy {
649 return n.getProxy(path, n.root, path, exclusive)
650}
651func (n *Node) getProxy(path string, root *Root, fullPath string, exclusive bool) *Proxy {
652 for strings.HasPrefix(path, "/") {
653 path = path[1:]
654 }
655 if path == "" {
656 return n.makeProxy(n.root, path, exclusive)
657 }
658
659 rev := n.Branches[NONE].Latest
660 partition := strings.SplitN(path, "/", 2)
661 name := partition[0]
662 path = partition[1]
663
664 field := ChildrenFields(n.Type)[name]
665 if field.IsContainer {
666 if path == "" {
667 log.Error("cannot proxy a container field")
668 }
669 if field.Key != "" {
670 partition := strings.SplitN(path, "/", 2)
671 key := partition[0]
672 path = partition[1]
673 key = field.KeyFromStr(key).(string)
674 children := rev.GetChildren()[name]
675 _, childRev := n.findRevByKey(children, field.Key, key)
676 childNode := childRev.GetNode()
677 return childNode.getProxy(path, root, fullPath, exclusive)
678 }
679 log.Error("cannot index into container with no keys")
680 } else {
681 childRev := rev.GetChildren()[name][0]
682 childNode := childRev.GetNode()
683 return childNode.getProxy(path, root, fullPath, exclusive)
684 }
685
686 return nil
687}
688
689func (n *Node) makeProxy(root *Root, fullPath string, exclusive bool) *Proxy {
690 if n.Proxy == nil {
691 n.Proxy = NewProxy(root, n, fullPath, exclusive)
692 } else {
693 if n.Proxy.Exclusive {
694 log.Error("node is already owned exclusively")
695 }
696 }
697 return n.Proxy
698}
699
700func (n *Node) makeEventBus() *EventBus {
701 if n.EventBus == nil {
702 n.EventBus = NewEventBus()
703 }
704 return n.EventBus
705}
706
707// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Persistence Loading ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
708
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400709func (n *Node) LoadLatest(kvStore *Backend, hash string) {
710 branch := NewBranch(n, "", nil, n.AutoPrune)
711 pr := &PersistedRevision{}
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400712 rev := pr.Load(branch, kvStore, n.Type, hash)
713 n.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400714 n.Branches[NONE] = branch
715}