blob: 0a77fff61aa701fd3989c4aafc524ced4d53472d [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 Barbarie4a2564d2018-07-26 11:02:58 -040092 // TODO: Invoke callback
Stephane Barbarie694e2b92018-09-07 12:17:36 -040093 fmt.Printf("invoking callback - changeType: %+v, data:%+v\n", change.Type, change.Data)
94 n.root.addCallback(n.Proxy.InvokeCallbacks, change.Type, change.Data, true)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040095 }
96 }
97
Stephane Barbarie694e2b92018-09-07 12:17:36 -040098 for _, change := range changeAnnouncement {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040099 // TODO: send notifications
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400100 fmt.Printf("sending notification - changeType: %+v, data:%+v\n", change.Type, change.Data)
101 n.root.addNotificationCallback(n.makeEventBus().Advertise, change.Type, change.Data, revision.GetHash())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400102 }
103 }
104}
105
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400106func (n *Node) Latest() Revision {
107 if branch, exists := n.Branches[NONE]; exists {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400108 return branch.Latest
109 }
110 return nil
111}
112
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400113func (n *Node) GetHash(hash string) Revision {
114 return n.Branches[NONE].Revisions[hash]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400115}
116
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400117func (n *Node) initialize(data interface{}, txid string) {
118 var children map[string][]Revision
119 children = make(map[string][]Revision)
120 for fieldName, field := range ChildrenFields(n.Type) {
121 _, fieldValue := GetAttributeValue(data, fieldName, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400122
123 if fieldValue.IsValid() {
124 if field.IsContainer {
125 if field.Key != "" {
126 var keysSeen []string
127
128 for i := 0; i < fieldValue.Len(); i++ {
129 v := fieldValue.Index(i)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400130 rev := n.MakeNode(v.Interface(), txid).Latest()
131 _, key := GetAttributeValue(v.Interface(), field.Key, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400132 for _, k := range keysSeen {
133 if k == key.String() {
134 fmt.Errorf("duplicate key - %s", k)
135 }
136 }
137 children[fieldName] = append(children[fieldName], rev)
138 keysSeen = append(keysSeen, key.String())
139 }
140
141 } else {
142 for i := 0; i < fieldValue.Len(); i++ {
143 v := fieldValue.Index(i)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400144 children[fieldName] = append(children[fieldName], n.MakeNode(v.Interface(), txid).Latest())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400145 }
146 }
147 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400148 children[fieldName] = append(children[fieldName], n.MakeNode(fieldValue.Interface(), txid).Latest())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400149 }
150 } else {
151 fmt.Errorf("field is invalid - %+v", fieldValue)
152 }
153 }
154 // FIXME: ClearField??? No such method in go protos. Reset?
155 //data.ClearField(field_name)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400156 branch := NewBranch(n, "", nil, n.AutoPrune)
157 rev := n.MakeRevision(branch, data, children)
158 n.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400159
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400160 if txid == "" {
161 n.Branches[NONE] = branch
162 } else {
163 n.Branches[txid] = branch
164 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400165}
166
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400167//
168// Get operation
169//
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400170func (n *Node) Get(path string, hash string, depth int, deep bool, txid string) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400171 if deep {
172 depth = -1
173 }
174
175 for strings.HasPrefix(path, "/") {
176 path = path[1:]
177 }
178
179 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400180 var rev Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400181
182 // FIXME: should empty txid be cleaned up?
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400183 if branch = n.Branches[txid]; txid == "" || branch == nil {
184 branch = n.Branches[NONE]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400185 }
186
187 if hash != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400188 rev = branch.Revisions[hash]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400189 } else {
190 rev = branch.Latest
191 }
192
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400193 return n.get(rev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400194}
195
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400196func (n *Node) findRevByKey(revs []Revision, keyName string, value string) (int, Revision) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400197 for i, rev := range revs {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400198 dataValue := reflect.ValueOf(rev.GetData())
199 dataStruct := GetAttributeStructure(rev.GetData(), keyName, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400200
201 fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
202
203 if fieldValue.Interface().(string) == value {
204 return i, rev
205 }
206 }
207
208 fmt.Errorf("key %s=%s not found", keyName, value)
209
210 return -1, nil
211}
212
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400213func (n *Node) get(rev Revision, path string, depth int) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400214 if path == "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400215 return n.doGet(rev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400216 }
217
218 partition := strings.SplitN(path, "/", 2)
219 name := partition[0]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400220
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400221 if len(partition) < 2 {
222 path = ""
223 } else {
224 path = partition[1]
225 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400226
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400227 names := ChildrenFields(n.Type)
228 field := names[name]
229
230 if field != nil && field.IsContainer {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400231 if field.Key != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400232 children := rev.GetChildren()[name]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400233 if path != "" {
234 partition = strings.SplitN(path, "/", 2)
235 key := partition[0]
236 path = ""
237 key = field.KeyFromStr(key).(string)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400238 if _, childRev := n.findRevByKey(children, field.Key, key); childRev == nil {
239 return nil
240 } else {
241 childNode := childRev.GetNode()
242 return childNode.get(childRev, path, depth)
243 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400244 } else {
245 var response []interface{}
246 for _, childRev := range children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400247 childNode := childRev.GetNode()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400248 value := childNode.doGet(childRev, depth)
249 response = append(response, value)
250 }
251 return response
252 }
253 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400254 var response []interface{}
255 if path != "" {
256 // TODO: raise error
257 return response
258 }
259 for _, childRev := range rev.GetChildren()[name] {
260 childNode := childRev.GetNode()
261 value := childNode.doGet(childRev, depth)
262 response = append(response, value)
263 }
264 return response
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400265 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400266 } else {
267 c1 := rev.GetChildren()[name]
268 childRev := c1[0]
269 childNode := childRev.GetNode()
270 return childNode.get(childRev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400271 }
272 return nil
273}
274
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400275func (n *Node) doGet(rev Revision, depth int) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400276 msg := rev.Get(depth)
277
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400278 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400279 log.Debug("invoking proxy GET Callbacks")
280 msg = n.Proxy.InvokeCallbacks(GET, msg, false)
281
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400282 }
283 return msg
284}
285
286//
287// Update operation
288//
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400289func (n *Node) Update(path string, data interface{}, strict bool, txid string, makeBranch t_makeBranch) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400290 // FIXME: is this required ... a bit overkill to take out a "/"
291 for strings.HasPrefix(path, "/") {
292 path = path[1:]
293 }
294
295 var branch *Branch
296 var ok bool
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400297 if txid == "" {
298 branch = n.Branches[NONE]
299 } else if branch, ok = n.Branches[txid]; !ok {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400300 branch = makeBranch(n)
301 }
302
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400303 log.Debugf("Branch data : %+v, Passed data: %+v", branch.Latest.GetData(), data)
304
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400305 if path == "" {
306 return n.doUpdate(branch, data, strict)
307 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400308
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400309 // TODO missing some code here...
310 rev := branch.Latest
311
312 partition := strings.SplitN(path, "/", 2)
313 name := partition[0]
314
315 if len(partition) < 2 {
316 path = ""
317 } else {
318 path = partition[1]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400319 }
320
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400321 field := ChildrenFields(n.Type)[name]
322 var children []Revision
323
324 if field.IsContainer {
325 if path == "" {
326 fmt.Errorf("cannot update a list\n")
327 } else if field.Key != "" {
328 partition := strings.SplitN(path, "/", 2)
329 key := partition[0]
330 if len(partition) < 2 {
331 path = ""
332 } else {
333 path = partition[1]
334 }
335 key = field.KeyFromStr(key).(string)
336 // TODO. Est-ce que le copy ne fonctionne pas? dois-je plutôt faire un clone de chaque item?
337 for _, v := range rev.GetChildren()[name] {
338 revCopy := reflect.ValueOf(v).Interface().(Revision)
339 children = append(children, revCopy)
340 }
341 idx, childRev := n.findRevByKey(children, field.Key, key)
342 childNode := childRev.GetNode()
343 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
344 if newChildRev.GetHash() == childRev.GetHash() {
345 if newChildRev != childRev {
346 log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
347 newChildRev.ClearHash()
348 }
349 return branch.Latest
350 }
351 if _, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0); newKey.Interface().(string) != key {
352 fmt.Errorf("cannot change key field\n")
353 }
354 children[idx] = newChildRev
355 rev = rev.UpdateChildren(name, children, branch)
356 n.root.MakeLatest(branch, rev, nil)
357 return rev
358 } else {
359 fmt.Errorf("cannot index into container with no keys\n")
360 }
361 } else {
362 childRev := rev.GetChildren()[name][0]
363 childNode := childRev.GetNode()
364 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
365 rev = rev.UpdateChildren(name, []Revision{newChildRev}, branch)
366 n.root.MakeLatest(branch, rev, nil)
367 return rev
368 }
369 return nil
370}
371
372func (n *Node) doUpdate(branch *Branch, data interface{}, strict bool) Revision {
373 log.Debugf("Comparing types - expected: %+v, actual: %+v", reflect.ValueOf(n.Type).Type(), reflect.TypeOf(data))
374
375 if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
376 // TODO raise error
377 fmt.Errorf("data does not match type: %+v", n.Type)
378 return nil
379 }
380
381 // TODO: validate that this actually works
382 //if n.hasChildren(data) {
383 // return nil
384 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400385
386 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400387 log.Debug("invoking proxy PRE_UPDATE Callbacks")
388 n.Proxy.InvokeCallbacks(PRE_UPDATE, data, false)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400389 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400390 if !reflect.DeepEqual(branch.Latest.GetData(), data) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400391 if strict {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400392 // TODO: checkAccessViolations(data, Branch.GetLatest.data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400393 fmt.Println("checking access violations")
394 }
395 rev := branch.Latest.UpdateData(data, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400396 n.root.MakeLatest(branch, rev, nil) // TODO -> changeAnnouncement needs to be a tuple (CallbackType.
397 // POST_UPDATE, rev.data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400398 return rev
399 } else {
400 return branch.Latest
401 }
402}
403
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400404//
405// Add operation
406//
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400407func (n *Node) Add(path string, data interface{}, txid string, makeBranch t_makeBranch) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400408 for strings.HasPrefix(path, "/") {
409 path = path[1:]
410 }
411 if path == "" {
412 // TODO raise error
413 fmt.Errorf("cannot add for non-container mode\n")
414 }
415
416 var branch *Branch
417 var ok bool
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400418 if txid == "" {
419 branch = n.Branches[NONE]
420 } else if branch, ok = n.Branches[txid]; !ok {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400421 branch = makeBranch(n)
422 }
423
424 rev := branch.Latest
425
426 partition := strings.SplitN(path, "/", 2)
427 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400428
429 if len(partition) < 2 {
430 path = ""
431 } else {
432 path = partition[1]
433 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400434
435 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400436 var children []Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400437
438 if field.IsContainer {
439 if path == "" {
440 if field.Key != "" {
441 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400442 log.Debug("invoking proxy PRE_ADD Callbacks")
443 n.Proxy.InvokeCallbacks(PRE_ADD, data, false)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400444 }
445
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400446 for _, v := range rev.GetChildren()[name] {
447 revCopy := reflect.ValueOf(v).Interface().(Revision)
448 children = append(children, revCopy)
449 }
450 _, key := GetAttributeValue(data, field.Key, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400451 if _, rev := n.findRevByKey(children, field.Key, key.String()); rev != nil {
452 // TODO raise error
453 fmt.Errorf("duplicate key found: %s", key.String())
454 }
455
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400456 childRev := n.MakeNode(data, "").Latest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400457 children = append(children, childRev)
458 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400459 n.root.MakeLatest(branch, rev, nil) // TODO -> changeAnnouncement needs to be a tuple (CallbackType.
460 // POST_ADD, rev.data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400461 return rev
462 } else {
463 fmt.Errorf("cannot add to non-keyed container\n")
464 }
465 } else if field.Key != "" {
466 partition := strings.SplitN(path, "/", 2)
467 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400468 if len(partition) < 2 {
469 path = ""
470 } else {
471 path = partition[1]
472 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400473 key = field.KeyFromStr(key).(string)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400474 copy(children, rev.GetChildren()[name])
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400475 idx, childRev := n.findRevByKey(children, field.Key, key)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400476 childNode := childRev.GetNode()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400477 newChildRev := childNode.Add(path, data, txid, makeBranch)
478 children[idx] = newChildRev
479 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400480 n.root.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400481 return rev
482 } else {
483 fmt.Errorf("cannot add to non-keyed container\n")
484 }
485 } else {
486 fmt.Errorf("cannot add to non-container field\n")
487 }
488 return nil
489}
490
491//
492// Remove operation
493//
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400494func (n *Node) Remove(path string, txid string, makeBranch t_makeBranch) Revision {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400495 for strings.HasPrefix(path, "/") {
496 path = path[1:]
497 }
498 if path == "" {
499 // TODO raise error
500 fmt.Errorf("cannot remove for non-container mode\n")
501 }
502 var branch *Branch
503 var ok bool
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400504 if txid == "" {
505 branch = n.Branches[NONE]
506 } else if branch, ok = n.Branches[txid]; !ok {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400507 branch = makeBranch(n)
508 }
509
510 rev := branch.Latest
511
512 partition := strings.SplitN(path, "/", 2)
513 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400514 if len(partition) < 2 {
515 path = ""
516 } else {
517 path = partition[1]
518 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400519
520 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400521 var children []Revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400522 postAnnouncement := []ChangeTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400523
524 if field.IsContainer {
525 if path == "" {
526 fmt.Errorf("cannot remove without a key\n")
527 } else if field.Key != "" {
528 partition := strings.SplitN(path, "/", 2)
529 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400530 if len(partition) < 2 {
531 path = ""
532 } else {
533 path = partition[1]
534 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400535 key = field.KeyFromStr(key).(string)
536 if path != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400537 for _, v := range rev.GetChildren()[name] {
538 newV := reflect.ValueOf(v).Interface().(Revision)
539 children = append(children, newV)
540 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400541 idx, childRev := n.findRevByKey(children, field.Key, key)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400542 childNode := childRev.GetNode()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400543 newChildRev := childNode.Remove(path, txid, makeBranch)
544 children[idx] = newChildRev
545 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400546 n.root.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400547 return rev
548 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400549 for _, v := range rev.GetChildren()[name] {
550 newV := reflect.ValueOf(v).Interface().(Revision)
551 children = append(children, newV)
552 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400553 idx, childRev := n.findRevByKey(children, field.Key, key)
554 if n.Proxy != nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400555 data := childRev.GetData()
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400556 n.Proxy.InvokeCallbacks(PRE_REMOVE, data, false)
557 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, data})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400558 } else {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400559 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, childRev.GetData()})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400560 }
561 children = append(children[:idx], children[idx+1:]...)
562 rev := rev.UpdateChildren(name, children, branch)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400563 n.root.MakeLatest(branch, rev, postAnnouncement)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400564 return rev
565 }
566 } else {
567 fmt.Errorf("cannot add to non-keyed container\n")
568 }
569 } else {
570 fmt.Errorf("cannot add to non-container field\n")
571 }
572
573 return nil
574}
575
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400576// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
577
578type t_makeBranch func(*Node) *Branch
579
580func (n *Node) makeTxBranch(txid string) *Branch {
581 branchPoint := n.Branches[NONE].Latest
582 branch := NewBranch(n, txid, branchPoint, true)
583 n.Branches[txid] = branch
584 return branch
585}
586
587func (n *Node) deleteTxBranch(txid string) {
588 delete(n.Branches, txid)
589}
590
591func (n *Node) mergeChild(txid string, dryRun bool) func(Revision) Revision {
592 f := func(rev Revision) Revision {
593 childBranch := rev.GetBranch()
594
595 if childBranch.Txid == txid {
Stephane Barbariee16186c2018-09-11 10:46:34 -0400596 rev, _ = childBranch.Node.mergeTxBranch(txid, dryRun)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400597 }
598
599 return rev
600 }
601 return f
602}
603
Stephane Barbariee16186c2018-09-11 10:46:34 -0400604func (n *Node) mergeTxBranch(txid string, dryRun bool) (Revision, error) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400605 srcBranch := n.Branches[txid]
606 dstBranch := n.Branches[NONE]
607
608 forkRev := srcBranch.Origin
609 srcRev := srcBranch.Latest
610 dstRev := dstBranch.Latest
611
612 rev, changes := Merge3Way(forkRev, srcRev, dstRev, n.mergeChild(txid, dryRun), dryRun)
613
614 if !dryRun {
615 n.root.MakeLatest(dstBranch, rev, changes)
616 delete(n.Branches, txid)
617 }
618
Stephane Barbariee16186c2018-09-11 10:46:34 -0400619 // TODO: return proper error when one occurs
620 return rev, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400621}
622
623// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diff utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
624
625//func (n *Node) diff(hash1, hash2, txid string) {
626// branch := n.Branches[txid]
627// rev1 := branch.get(hash1)
628// rev2 := branch.get(hash2)
629//
630// if rev1.GetHash() == rev2.GetHash() {
631// // empty patch
632// } else {
633// // translate data to json and generate patch
634// patch, err := jsonpatch.MakePatch(rev1.GetData(), rev2.GetData())
635// patch.
636// }
637//}
638
639// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tag utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
640
641// TODO: is tag mgmt used in the python implementation? Need to validate
642
643// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
644
645func (n *Node) hasChildren(data interface{}) bool {
646 for fieldName, field := range ChildrenFields(n.Type) {
647 _, fieldValue := GetAttributeValue(data, fieldName, 0)
648
649 if (field.IsContainer && fieldValue.Len() > 0) || !fieldValue.IsNil() {
650 log.Error("cannot update external children")
651 return true
652 }
653 }
654
655 return false
656}
657
658// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Node Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
659
660func (n *Node) GetProxy(path string, exclusive bool) *Proxy {
661 return n.getProxy(path, n.root, path, exclusive)
662}
663func (n *Node) getProxy(path string, root *Root, fullPath string, exclusive bool) *Proxy {
664 for strings.HasPrefix(path, "/") {
665 path = path[1:]
666 }
667 if path == "" {
668 return n.makeProxy(n.root, path, exclusive)
669 }
670
671 rev := n.Branches[NONE].Latest
672 partition := strings.SplitN(path, "/", 2)
673 name := partition[0]
674 path = partition[1]
675
676 field := ChildrenFields(n.Type)[name]
677 if field.IsContainer {
678 if path == "" {
679 log.Error("cannot proxy a container field")
680 }
681 if field.Key != "" {
682 partition := strings.SplitN(path, "/", 2)
683 key := partition[0]
684 path = partition[1]
685 key = field.KeyFromStr(key).(string)
686 children := rev.GetChildren()[name]
687 _, childRev := n.findRevByKey(children, field.Key, key)
688 childNode := childRev.GetNode()
689 return childNode.getProxy(path, root, fullPath, exclusive)
690 }
691 log.Error("cannot index into container with no keys")
692 } else {
693 childRev := rev.GetChildren()[name][0]
694 childNode := childRev.GetNode()
695 return childNode.getProxy(path, root, fullPath, exclusive)
696 }
697
698 return nil
699}
700
701func (n *Node) makeProxy(root *Root, fullPath string, exclusive bool) *Proxy {
702 if n.Proxy == nil {
703 n.Proxy = NewProxy(root, n, fullPath, exclusive)
704 } else {
705 if n.Proxy.Exclusive {
706 log.Error("node is already owned exclusively")
707 }
708 }
709 return n.Proxy
710}
711
712func (n *Node) makeEventBus() *EventBus {
713 if n.EventBus == nil {
714 n.EventBus = NewEventBus()
715 }
716 return n.EventBus
717}
718
719// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Persistence Loading ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
720
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400721func (n *Node) LoadLatest(kvStore *Backend, hash string) {
722 branch := NewBranch(n, "", nil, n.AutoPrune)
723 pr := &PersistedRevision{}
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400724 rev := pr.Load(branch, kvStore, n.Type, hash)
725 n.MakeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400726 n.Branches[NONE] = branch
727}