blob: fd58cf8febb165a3a774076124e878c358e4f1e1 [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
Stephane Barbariee16186c2018-09-11 10:46:34 -040018// TODO: proper error handling
19// TODO: proper logging
20
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040021import (
22 "fmt"
23 "github.com/golang/protobuf/proto"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040024 "github.com/opencord/voltha-go/common/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040025 "reflect"
26 "strings"
27)
28
29const (
30 NONE string = "none"
31)
32
33type Node struct {
34 root *Root
35 Type interface{}
36 Branches map[string]*Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -040037 Tags map[string]Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040038 Proxy *Proxy
39 EventBus *EventBus
40 AutoPrune bool
41}
42
Stephane Barbarie694e2b92018-09-07 12:17:36 -040043type ChangeTuple struct {
44 Type CallbackType
45 Data interface{}
46}
47
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040048func NewNode(root *Root, initialData interface{}, autoPrune bool, txid string) *Node {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040049 n := &Node{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040050
Stephane Barbarieec0919b2018-09-05 14:14:29 -040051 n.root = root
52 n.Branches = make(map[string]*Branch)
53 n.Tags = make(map[string]Revision)
54 n.Proxy = nil
55 n.EventBus = nil
56 n.AutoPrune = autoPrune
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040057
58 if IsProtoMessage(initialData) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040059 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040060 dataCopy := proto.Clone(initialData.(proto.Message))
Stephane Barbarieec0919b2018-09-05 14:14:29 -040061 n.initialize(dataCopy, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040062 } else if reflect.ValueOf(initialData).IsValid() {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040063 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040064 } else {
65 // not implemented error
66 fmt.Errorf("cannot process initial data - %+v", initialData)
67 }
68
Stephane Barbarieec0919b2018-09-05 14:14:29 -040069 return n
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040070}
71
Stephane Barbarieec0919b2018-09-05 14:14:29 -040072func (n *Node) MakeNode(data interface{}, txid string) *Node {
73 return NewNode(n.root, data, true, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040074}
75
Stephane Barbarieec0919b2018-09-05 14:14:29 -040076func (n *Node) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
77 return n.root.MakeRevision(branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040078}
79
Stephane Barbarie694e2b92018-09-07 12:17:36 -040080func (n *Node) MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040081 if _, ok := branch.Revisions[revision.GetHash()]; !ok {
82 branch.Revisions[revision.GetHash()] = revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040083 }
84
Stephane Barbarieec0919b2018-09-05 14:14:29 -040085 if branch.Latest == nil || revision.GetHash() != branch.Latest.GetHash() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040086 branch.Latest = revision
87 }
88
89 if changeAnnouncement != nil && branch.Txid == "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040090 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -040091 for _, change := range changeAnnouncement {
Stephane Barbarie694e2b92018-09-07 12:17:36 -040092 fmt.Printf("invoking callback - changeType: %+v, data:%+v\n", change.Type, change.Data)
93 n.root.addCallback(n.Proxy.InvokeCallbacks, change.Type, change.Data, true)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040094 }
95 }
96
Stephane Barbarie694e2b92018-09-07 12:17:36 -040097 for _, change := range changeAnnouncement {
Stephane Barbarie694e2b92018-09-07 12:17:36 -040098 fmt.Printf("sending notification - changeType: %+v, data:%+v\n", change.Type, change.Data)
99 n.root.addNotificationCallback(n.makeEventBus().Advertise, change.Type, change.Data, revision.GetHash())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400100 }
101 }
102}
103
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400104func (n *Node) Latest(txid ...string) Revision {
105 var branch *Branch
106 var exists bool
107
108 if len(txid) > 0 && txid[0] != "" {
109 if branch, exists = n.Branches[txid[0]]; exists {
110 return branch.Latest
111 }
112 } else if branch, exists = n.Branches[NONE]; exists {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400113 return branch.Latest
114 }
115 return nil
116}
117
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400118func (n *Node) GetHash(hash string) Revision {
119 return n.Branches[NONE].Revisions[hash]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400120}
121
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400122func (n *Node) initialize(data interface{}, txid string) {
123 var children map[string][]Revision
124 children = make(map[string][]Revision)
125 for fieldName, field := range ChildrenFields(n.Type) {
126 _, fieldValue := GetAttributeValue(data, fieldName, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400127
128 if fieldValue.IsValid() {
129 if field.IsContainer {
130 if field.Key != "" {
131 var keysSeen []string
132
133 for i := 0; i < fieldValue.Len(); i++ {
134 v := fieldValue.Index(i)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400135
136 rev := n.MakeNode(v.Interface(), txid).Latest(txid)
137
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400138 _, key := GetAttributeValue(v.Interface(), field.Key, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400139 for _, k := range keysSeen {
140 if k == key.String() {
141 fmt.Errorf("duplicate key - %s", k)
142 }
143 }
144 children[fieldName] = append(children[fieldName], rev)
145 keysSeen = append(keysSeen, key.String())
146 }
147
148 } else {
149 for i := 0; i < fieldValue.Len(); i++ {
150 v := fieldValue.Index(i)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400151 children[fieldName] = append(children[fieldName], n.MakeNode(v.Interface(), txid).Latest())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400152 }
153 }
154 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400155 children[fieldName] = append(children[fieldName], n.MakeNode(fieldValue.Interface(), txid).Latest())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400156 }
157 } else {
158 fmt.Errorf("field is invalid - %+v", fieldValue)
159 }
160 }
161 // FIXME: ClearField??? No such method in go protos. Reset?
162 //data.ClearField(field_name)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400163 branch := NewBranch(n, "", nil, n.AutoPrune)
164 rev := n.MakeRevision(branch, data, children)
165 n.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400166
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400167 if txid == "" {
168 n.Branches[NONE] = branch
169 } else {
170 n.Branches[txid] = branch
171 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400172}
173
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400174//
175// Get operation
176//
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400177func (n *Node) Get(path string, hash string, depth int, deep bool, txid string) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400178 if deep {
179 depth = -1
180 }
181
182 for strings.HasPrefix(path, "/") {
183 path = path[1:]
184 }
185
186 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400187 var rev Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400188
189 // FIXME: should empty txid be cleaned up?
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400190 if branch = n.Branches[txid]; txid == "" || branch == nil {
191 branch = n.Branches[NONE]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400192 }
193
194 if hash != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400195 rev = branch.Revisions[hash]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400196 } else {
197 rev = branch.Latest
198 }
199
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400200 return n.get(rev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400201}
202
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400203func (n *Node) findRevByKey(revs []Revision, keyName string, value string) (int, Revision) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400204 for i, rev := range revs {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400205 dataValue := reflect.ValueOf(rev.GetData())
206 dataStruct := GetAttributeStructure(rev.GetData(), keyName, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400207
208 fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
209
210 if fieldValue.Interface().(string) == value {
211 return i, rev
212 }
213 }
214
215 fmt.Errorf("key %s=%s not found", keyName, value)
216
217 return -1, nil
218}
219
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400220func (n *Node) get(rev Revision, path string, depth int) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400221 if path == "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400222 return n.doGet(rev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400223 }
224
225 partition := strings.SplitN(path, "/", 2)
226 name := partition[0]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400227
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400228 if len(partition) < 2 {
229 path = ""
230 } else {
231 path = partition[1]
232 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400233
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400234 names := ChildrenFields(n.Type)
235 field := names[name]
236
237 if field != nil && field.IsContainer {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400238 if field.Key != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400239 children := rev.GetChildren()[name]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400240 if path != "" {
241 partition = strings.SplitN(path, "/", 2)
242 key := partition[0]
243 path = ""
244 key = field.KeyFromStr(key).(string)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400245 if _, childRev := n.findRevByKey(children, field.Key, key); childRev == nil {
246 return nil
247 } else {
248 childNode := childRev.GetNode()
249 return childNode.get(childRev, path, depth)
250 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400251 } else {
252 var response []interface{}
253 for _, childRev := range children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400254 childNode := childRev.GetNode()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400255 value := childNode.doGet(childRev, depth)
256 response = append(response, value)
257 }
258 return response
259 }
260 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400261 var response []interface{}
262 if path != "" {
263 // TODO: raise error
264 return response
265 }
266 for _, childRev := range rev.GetChildren()[name] {
267 childNode := childRev.GetNode()
268 value := childNode.doGet(childRev, depth)
269 response = append(response, value)
270 }
271 return response
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400272 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400273 } else {
274 c1 := rev.GetChildren()[name]
275 childRev := c1[0]
276 childNode := childRev.GetNode()
277 return childNode.get(childRev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400278 }
279 return nil
280}
281
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400282func (n *Node) doGet(rev Revision, depth int) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400283 msg := rev.Get(depth)
284
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400285 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400286 log.Debug("invoking proxy GET Callbacks")
287 msg = n.Proxy.InvokeCallbacks(GET, msg, false)
288
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400289 }
290 return msg
291}
292
293//
294// Update operation
295//
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400296func (n *Node) Update(path string, data interface{}, strict bool, txid string, makeBranch t_makeBranch) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400297 // FIXME: is this required ... a bit overkill to take out a "/"
298 for strings.HasPrefix(path, "/") {
299 path = path[1:]
300 }
301
302 var branch *Branch
303 var ok bool
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400304 if txid == "" {
305 branch = n.Branches[NONE]
306 } else if branch, ok = n.Branches[txid]; !ok {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400307 branch = makeBranch(n)
308 }
309
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400310 log.Debugf("Branch data : %+v, Passed data: %+v", branch.Latest.GetData(), data)
311
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400312 if path == "" {
313 return n.doUpdate(branch, data, strict)
314 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400315
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400316 // TODO missing some code here...
317 rev := branch.Latest
318
319 partition := strings.SplitN(path, "/", 2)
320 name := partition[0]
321
322 if len(partition) < 2 {
323 path = ""
324 } else {
325 path = partition[1]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400326 }
327
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400328 field := ChildrenFields(n.Type)[name]
329 var children []Revision
330
331 if field.IsContainer {
332 if path == "" {
333 fmt.Errorf("cannot update a list\n")
334 } else if field.Key != "" {
335 partition := strings.SplitN(path, "/", 2)
336 key := partition[0]
337 if len(partition) < 2 {
338 path = ""
339 } else {
340 path = partition[1]
341 }
342 key = field.KeyFromStr(key).(string)
343 // TODO. Est-ce que le copy ne fonctionne pas? dois-je plutôt faire un clone de chaque item?
344 for _, v := range rev.GetChildren()[name] {
345 revCopy := reflect.ValueOf(v).Interface().(Revision)
346 children = append(children, revCopy)
347 }
348 idx, childRev := n.findRevByKey(children, field.Key, key)
349 childNode := childRev.GetNode()
350 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
351 if newChildRev.GetHash() == childRev.GetHash() {
352 if newChildRev != childRev {
353 log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
354 newChildRev.ClearHash()
355 }
356 return branch.Latest
357 }
358 if _, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0); newKey.Interface().(string) != key {
359 fmt.Errorf("cannot change key field\n")
360 }
361 children[idx] = newChildRev
362 rev = rev.UpdateChildren(name, children, branch)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400363 branch.Latest.Drop(txid, false)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400364 n.root.MakeLatest(branch, rev, nil)
365 return rev
366 } else {
367 fmt.Errorf("cannot index into container with no keys\n")
368 }
369 } else {
370 childRev := rev.GetChildren()[name][0]
371 childNode := childRev.GetNode()
372 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
373 rev = rev.UpdateChildren(name, []Revision{newChildRev}, branch)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400374 branch.Latest.Drop(txid, false)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400375 n.root.MakeLatest(branch, rev, nil)
376 return rev
377 }
378 return nil
379}
380
381func (n *Node) doUpdate(branch *Branch, data interface{}, strict bool) Revision {
382 log.Debugf("Comparing types - expected: %+v, actual: %+v", reflect.ValueOf(n.Type).Type(), reflect.TypeOf(data))
383
384 if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
385 // TODO raise error
386 fmt.Errorf("data does not match type: %+v", n.Type)
387 return nil
388 }
389
390 // TODO: validate that this actually works
391 //if n.hasChildren(data) {
392 // return nil
393 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400394
395 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400396 log.Debug("invoking proxy PRE_UPDATE Callbacks")
397 n.Proxy.InvokeCallbacks(PRE_UPDATE, data, false)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400398 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400399 if !reflect.DeepEqual(branch.Latest.GetData(), data) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400400 if strict {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400401 // TODO: checkAccessViolations(data, Branch.GetLatest.data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400402 fmt.Println("checking access violations")
403 }
404 rev := branch.Latest.UpdateData(data, branch)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400405 branch.Latest.Drop(branch.Txid, true)
406 n.root.MakeLatest(branch, rev, []ChangeTuple{{POST_UPDATE, rev.GetData()}})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400407 return rev
408 } else {
409 return branch.Latest
410 }
411}
412
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400413//
414// Add operation
415//
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400416func (n *Node) Add(path string, data interface{}, txid string, makeBranch t_makeBranch) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400417 for strings.HasPrefix(path, "/") {
418 path = path[1:]
419 }
420 if path == "" {
421 // TODO raise error
422 fmt.Errorf("cannot add for non-container mode\n")
423 }
424
425 var branch *Branch
426 var ok bool
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400427 if txid == "" {
428 branch = n.Branches[NONE]
429 } else if branch, ok = n.Branches[txid]; !ok {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400430 branch = makeBranch(n)
431 }
432
433 rev := branch.Latest
434
435 partition := strings.SplitN(path, "/", 2)
436 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400437
438 if len(partition) < 2 {
439 path = ""
440 } else {
441 path = partition[1]
442 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400443
444 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400445 var children []Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400446
447 if field.IsContainer {
448 if path == "" {
449 if field.Key != "" {
450 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400451 log.Debug("invoking proxy PRE_ADD Callbacks")
452 n.Proxy.InvokeCallbacks(PRE_ADD, data, false)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400453 }
454
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400455 for _, v := range rev.GetChildren()[name] {
456 revCopy := reflect.ValueOf(v).Interface().(Revision)
457 children = append(children, revCopy)
458 }
459 _, key := GetAttributeValue(data, field.Key, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400460 if _, rev := n.findRevByKey(children, field.Key, key.String()); rev != nil {
461 // TODO raise error
462 fmt.Errorf("duplicate key found: %s", key.String())
463 }
464
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400465 childRev := n.MakeNode(data, txid).Latest(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400466 children = append(children, childRev)
467 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400468 branch.Latest.Drop(txid, false)
469 n.root.MakeLatest(branch, rev, []ChangeTuple{{POST_ADD, rev.GetData()}})
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 if field.Key != "" {
475 partition := strings.SplitN(path, "/", 2)
476 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400477 if len(partition) < 2 {
478 path = ""
479 } else {
480 path = partition[1]
481 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400482 key = field.KeyFromStr(key).(string)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400483 copy(children, rev.GetChildren()[name])
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400484 idx, childRev := n.findRevByKey(children, field.Key, key)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400485 childNode := childRev.GetNode()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400486 newChildRev := childNode.Add(path, data, txid, makeBranch)
487 children[idx] = newChildRev
488 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400489 branch.Latest.Drop(txid, false)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400490 n.root.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400491 return rev
492 } else {
493 fmt.Errorf("cannot add to non-keyed container\n")
494 }
495 } else {
496 fmt.Errorf("cannot add to non-container field\n")
497 }
498 return nil
499}
500
501//
502// Remove operation
503//
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400504func (n *Node) Remove(path string, txid string, makeBranch t_makeBranch) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400505 for strings.HasPrefix(path, "/") {
506 path = path[1:]
507 }
508 if path == "" {
509 // TODO raise error
510 fmt.Errorf("cannot remove for non-container mode\n")
511 }
512 var branch *Branch
513 var ok bool
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400514 if txid == "" {
515 branch = n.Branches[NONE]
516 } else if branch, ok = n.Branches[txid]; !ok {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400517 branch = makeBranch(n)
518 }
519
520 rev := branch.Latest
521
522 partition := strings.SplitN(path, "/", 2)
523 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400524 if len(partition) < 2 {
525 path = ""
526 } else {
527 path = partition[1]
528 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400529
530 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400531 var children []Revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400532 postAnnouncement := []ChangeTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400533
534 if field.IsContainer {
535 if path == "" {
536 fmt.Errorf("cannot remove without a key\n")
537 } else if field.Key != "" {
538 partition := strings.SplitN(path, "/", 2)
539 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400540 if len(partition) < 2 {
541 path = ""
542 } else {
543 path = partition[1]
544 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400545 key = field.KeyFromStr(key).(string)
546 if path != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400547 for _, v := range rev.GetChildren()[name] {
548 newV := reflect.ValueOf(v).Interface().(Revision)
549 children = append(children, newV)
550 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400551 idx, childRev := n.findRevByKey(children, field.Key, key)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400552 childNode := childRev.GetNode()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400553 newChildRev := childNode.Remove(path, txid, makeBranch)
554 children[idx] = newChildRev
555 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400556 branch.Latest.Drop(txid, false)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400557 n.root.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400558 return rev
559 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400560 for _, v := range rev.GetChildren()[name] {
561 newV := reflect.ValueOf(v).Interface().(Revision)
562 children = append(children, newV)
563 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400564 idx, childRev := n.findRevByKey(children, field.Key, key)
565 if n.Proxy != nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400566 data := childRev.GetData()
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400567 n.Proxy.InvokeCallbacks(PRE_REMOVE, data, false)
568 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, data})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400569 } else {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400570 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, childRev.GetData()})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400571 }
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400572 childRev.Drop(txid, true)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400573 children = append(children[:idx], children[idx+1:]...)
574 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400575 branch.Latest.Drop(txid, false)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400576 n.root.MakeLatest(branch, rev, postAnnouncement)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400577 return rev
578 }
579 } else {
580 fmt.Errorf("cannot add to non-keyed container\n")
581 }
582 } else {
583 fmt.Errorf("cannot add to non-container field\n")
584 }
585
586 return nil
587}
588
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400589// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
590
591type t_makeBranch func(*Node) *Branch
592
593func (n *Node) makeTxBranch(txid string) *Branch {
594 branchPoint := n.Branches[NONE].Latest
595 branch := NewBranch(n, txid, branchPoint, true)
596 n.Branches[txid] = branch
597 return branch
598}
599
600func (n *Node) deleteTxBranch(txid string) {
601 delete(n.Branches, txid)
602}
603
604func (n *Node) mergeChild(txid string, dryRun bool) func(Revision) Revision {
605 f := func(rev Revision) Revision {
606 childBranch := rev.GetBranch()
607
608 if childBranch.Txid == txid {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400609 rev, _ = childBranch.Node.mergeTxBranch(txid, dryRun)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400610 }
611
612 return rev
613 }
614 return f
615}
616
Stephane Barbariee16186c2018-09-11 10:46:34 -0400617func (n *Node) mergeTxBranch(txid string, dryRun bool) (Revision, error) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400618 srcBranch := n.Branches[txid]
619 dstBranch := n.Branches[NONE]
620
621 forkRev := srcBranch.Origin
622 srcRev := srcBranch.Latest
623 dstRev := dstBranch.Latest
624
625 rev, changes := Merge3Way(forkRev, srcRev, dstRev, n.mergeChild(txid, dryRun), dryRun)
626
627 if !dryRun {
628 n.root.MakeLatest(dstBranch, rev, changes)
629 delete(n.Branches, txid)
630 }
631
Stephane Barbariee16186c2018-09-11 10:46:34 -0400632 // TODO: return proper error when one occurs
633 return rev, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400634}
635
636// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diff utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
637
638//func (n *Node) diff(hash1, hash2, txid string) {
639// branch := n.Branches[txid]
640// rev1 := branch.get(hash1)
641// rev2 := branch.get(hash2)
642//
643// if rev1.GetHash() == rev2.GetHash() {
644// // empty patch
645// } else {
646// // translate data to json and generate patch
647// patch, err := jsonpatch.MakePatch(rev1.GetData(), rev2.GetData())
648// patch.
649// }
650//}
651
652// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tag utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
653
654// TODO: is tag mgmt used in the python implementation? Need to validate
655
656// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
657
658func (n *Node) hasChildren(data interface{}) bool {
659 for fieldName, field := range ChildrenFields(n.Type) {
660 _, fieldValue := GetAttributeValue(data, fieldName, 0)
661
662 if (field.IsContainer && fieldValue.Len() > 0) || !fieldValue.IsNil() {
663 log.Error("cannot update external children")
664 return true
665 }
666 }
667
668 return false
669}
670
671// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Node Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
672
673func (n *Node) GetProxy(path string, exclusive bool) *Proxy {
674 return n.getProxy(path, n.root, path, exclusive)
675}
676func (n *Node) getProxy(path string, root *Root, fullPath string, exclusive bool) *Proxy {
677 for strings.HasPrefix(path, "/") {
678 path = path[1:]
679 }
680 if path == "" {
681 return n.makeProxy(n.root, path, exclusive)
682 }
683
684 rev := n.Branches[NONE].Latest
685 partition := strings.SplitN(path, "/", 2)
686 name := partition[0]
687 path = partition[1]
688
689 field := ChildrenFields(n.Type)[name]
690 if field.IsContainer {
691 if path == "" {
692 log.Error("cannot proxy a container field")
693 }
694 if field.Key != "" {
695 partition := strings.SplitN(path, "/", 2)
696 key := partition[0]
697 path = partition[1]
698 key = field.KeyFromStr(key).(string)
699 children := rev.GetChildren()[name]
700 _, childRev := n.findRevByKey(children, field.Key, key)
701 childNode := childRev.GetNode()
702 return childNode.getProxy(path, root, fullPath, exclusive)
703 }
704 log.Error("cannot index into container with no keys")
705 } else {
706 childRev := rev.GetChildren()[name][0]
707 childNode := childRev.GetNode()
708 return childNode.getProxy(path, root, fullPath, exclusive)
709 }
710
711 return nil
712}
713
714func (n *Node) makeProxy(root *Root, fullPath string, exclusive bool) *Proxy {
715 if n.Proxy == nil {
716 n.Proxy = NewProxy(root, n, fullPath, exclusive)
717 } else {
718 if n.Proxy.Exclusive {
719 log.Error("node is already owned exclusively")
720 }
721 }
722 return n.Proxy
723}
724
725func (n *Node) makeEventBus() *EventBus {
726 if n.EventBus == nil {
727 n.EventBus = NewEventBus()
728 }
729 return n.EventBus
730}
731
732// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Persistence Loading ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
733
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400734func (n *Node) LoadLatest(kvStore *Backend, hash string) {
735 branch := NewBranch(n, "", nil, n.AutoPrune)
736 pr := &PersistedRevision{}
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400737 rev := pr.Load(branch, kvStore, n.Type, hash)
738 n.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400739 n.Branches[NONE] = branch
740}