blob: 707fe756c171b217265a98740af6fb3d6c16d9c8 [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 Barbariedc5022d2018-11-19 15:21:44 -050016
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040017package model
18
Stephane Barbariee16186c2018-09-11 10:46:34 -040019// TODO: proper error handling
20// TODO: proper logging
21
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040022import (
23 "fmt"
24 "github.com/golang/protobuf/proto"
Stephane Barbarieec0919b2018-09-05 14:14:29 -040025 "github.com/opencord/voltha-go/common/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040026 "reflect"
Stephane Barbariedc5022d2018-11-19 15:21:44 -050027 "runtime/debug"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040028 "strings"
Stephane Barbariedc5022d2018-11-19 15:21:44 -050029 "sync"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040030)
31
Stephane Barbariedc5022d2018-11-19 15:21:44 -050032// When a branch has no transaction id, everything gets stored in NONE
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040033const (
34 NONE string = "none"
35)
36
Stephane Barbariedc5022d2018-11-19 15:21:44 -050037// Node interface is an abstraction of the node data structure
Stephane Barbarie06c4a742018-10-01 11:09:32 -040038type 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
Stephane Barbariedc5022d2018-11-19 15:21:44 -050055 CreateProxy(path string, exclusive bool) *Proxy
56 GetProxy() *Proxy
Stephane Barbarie06c4a742018-10-01 11:09:32 -040057}
58
59type node struct {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050060 sync.RWMutex
Stephane Barbarie126101e2018-10-11 16:18:48 -040061 Root *root
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040062 Type interface{}
63 Branches map[string]*Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -040064 Tags map[string]Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040065 Proxy *Proxy
66 EventBus *EventBus
67 AutoPrune bool
68}
69
Stephane Barbariedc5022d2018-11-19 15:21:44 -050070// ChangeTuple holds details of modifications made to a revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -040071type ChangeTuple struct {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -040072 Type CallbackType
73 PreviousData interface{}
74 LatestData interface{}
Stephane Barbarie694e2b92018-09-07 12:17:36 -040075}
76
Stephane Barbariedc5022d2018-11-19 15:21:44 -050077// NewNode creates a new instance of the node data structure
Stephane Barbarie06c4a742018-10-01 11:09:32 -040078func NewNode(root *root, initialData interface{}, autoPrune bool, txid string) *node {
79 n := &node{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040080
Stephane Barbarie126101e2018-10-11 16:18:48 -040081 n.Root = root
Stephane Barbarieec0919b2018-09-05 14:14:29 -040082 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
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040087
88 if IsProtoMessage(initialData) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040089 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040090 dataCopy := proto.Clone(initialData.(proto.Message))
Stephane Barbarieec0919b2018-09-05 14:14:29 -040091 n.initialize(dataCopy, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040092 } else if reflect.ValueOf(initialData).IsValid() {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050093 // 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
Stephane Barbarieec0919b2018-09-05 14:14:29 -040096 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040097 } else {
98 // not implemented error
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -040099 log.Errorf("cannot process initial data - %+v", initialData)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400100 }
101
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400102 return n
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400103}
104
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500105// MakeNode creates a new node in the tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400106func (n *node) MakeNode(data interface{}, txid string) *node {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400107 return NewNode(n.Root, data, true, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400108}
109
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500110// MakeRevision create a new revision of the node in the tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400111func (n *node) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500112 return n.GetRoot().MakeRevision(branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400113}
114
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500115// makeLatest will mark the revision of a node as being the latest
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400116func (n *node) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
Stephane Barbarie260a5632019-02-26 16:12:49 -0500117 n.Lock()
118 defer n.Unlock()
119
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500120 branch.AddRevision(revision)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400121
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500122 if branch.GetLatest() == nil || revision.GetHash() != branch.GetLatest().GetHash() {
123 branch.SetLatest(revision)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400124 }
125
126 if changeAnnouncement != nil && branch.Txid == "" {
Stephane Barbarie260a5632019-02-26 16:12:49 -0500127 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400128 for _, change := range changeAnnouncement {
Stephane Barbarie260a5632019-02-26 16:12:49 -0500129 //log.Debugw("invoking callback",
130 // log.Fields{
131 // "callbacks": n.Proxy.getCallbacks(change.Type),
132 // "type": change.Type,
133 // "previousData": change.PreviousData,
134 // "latestData": change.LatestData,
135 // })
136 n.Root.AddCallback(
137 n.Proxy.InvokeCallbacks,
Stephane Barbarie126101e2018-10-11 16:18:48 -0400138 change.Type,
139 true,
140 change.PreviousData,
141 change.LatestData)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400142 }
143 }
144
Stephane Barbarie260a5632019-02-26 16:12:49 -0500145 //for _, change := range changeAnnouncement {
146 //log.Debugf("sending notification - changeType: %+v, previous:%+v, latest: %+v",
147 // change.Type,
148 // change.PreviousData,
149 // change.LatestData)
150 //n.Root.AddNotificationCallback(
151 // n.makeEventBus().Advertise,
152 // change.Type,
153 // revision.GetHash(),
154 // change.PreviousData,
155 // change.LatestData)
156 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400157 }
158}
159
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500160// Latest returns the latest revision of node with or without the transaction id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400161func (n *node) Latest(txid ...string) Revision {
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400162 var branch *Branch
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400163
164 if len(txid) > 0 && txid[0] != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500165 if branch = n.GetBranch(txid[0]); branch != nil {
166 return branch.GetLatest()
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400167 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500168 } else if branch = n.GetBranch(NONE); branch != nil {
169 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400170 }
171 return nil
172}
173
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500174// initialize prepares the content of a node along with its possible ramifications
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400175func (n *node) initialize(data interface{}, txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500176 n.Lock()
177 children := make(map[string][]Revision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400178 for fieldName, field := range ChildrenFields(n.Type) {
179 _, fieldValue := GetAttributeValue(data, fieldName, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400180
181 if fieldValue.IsValid() {
182 if field.IsContainer {
183 if field.Key != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400184 for i := 0; i < fieldValue.Len(); i++ {
185 v := fieldValue.Index(i)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400186
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500187 if rev := n.MakeNode(v.Interface(), txid).Latest(txid); rev != nil {
188 children[fieldName] = append(children[fieldName], rev)
189 }
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400190
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500191 // TODO: The following logic was ported from v1.0. Need to verify if it is required
192 //var keysSeen []string
193 //_, key := GetAttributeValue(v.Interface(), field.Key, 0)
194 //for _, k := range keysSeen {
195 // if k == key.String() {
196 // //log.Errorf("duplicate key - %s", k)
197 // }
198 //}
199 //keysSeen = append(keysSeen, key.String())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400200 }
201
202 } else {
203 for i := 0; i < fieldValue.Len(); i++ {
204 v := fieldValue.Index(i)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500205 if newNodeRev := n.MakeNode(v.Interface(), txid).Latest(); newNodeRev != nil {
206 children[fieldName] = append(children[fieldName], newNodeRev)
207 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400208 }
209 }
210 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500211 if newNodeRev := n.MakeNode(fieldValue.Interface(), txid).Latest(); newNodeRev != nil {
212 children[fieldName] = append(children[fieldName], newNodeRev)
213 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400214 }
215 } else {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400216 log.Errorf("field is invalid - %+v", fieldValue)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400217 }
218 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500219 n.Unlock()
220
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400221 branch := NewBranch(n, "", nil, n.AutoPrune)
222 rev := n.MakeRevision(branch, data, children)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400223 n.makeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400224
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400225 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500226 n.SetBranch(NONE, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400227 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500228 n.SetBranch(txid, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400229 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400230}
231
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500232// findRevByKey retrieves a specific revision from a node tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400233func (n *node) findRevByKey(revs []Revision, keyName string, value interface{}) (int, Revision) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500234 n.Lock()
235 defer n.Unlock()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500236
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400237 for i, rev := range revs {
238 dataValue := reflect.ValueOf(rev.GetData())
239 dataStruct := GetAttributeStructure(rev.GetData(), keyName, 0)
240
241 fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
242
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400243 a := fmt.Sprintf("%s", fieldValue.Interface())
244 b := fmt.Sprintf("%s", value)
245 if a == b {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500246 return i, revs[i]
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400247 }
248 }
249
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400250 return -1, nil
251}
252
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500253// Get retrieves the data from a node tree that resides at the specified path
Stephane Barbarieaa467942019-02-06 14:09:44 -0500254func (n *node) List(path string, hash string, depth int, deep bool, txid string) interface{} {
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500255 log.Debugw("node-list-request", log.Fields{"path": path, "hash": hash, "depth": depth, "deep": deep, "txid": txid})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500256 if deep {
257 depth = -1
258 }
259
260 for strings.HasPrefix(path, "/") {
261 path = path[1:]
262 }
263
264 var branch *Branch
265 var rev Revision
266
267 if branch = n.GetBranch(txid); txid == "" || branch == nil {
268 branch = n.GetBranch(NONE)
269 }
270
271 if hash != "" {
272 rev = branch.GetRevision(hash)
273 } else {
274 rev = branch.GetLatest()
275 }
276
277 var result interface{}
278 var prList []interface{}
279 if pr := rev.LoadFromPersistence(path, txid); pr != nil {
280 for _, revEntry := range pr {
281 prList = append(prList, revEntry.GetData())
282 }
283 result = prList
284 }
285
286 return result
287}
288
289// Get retrieves the data from a node tree that resides at the specified path
Stephane Barbarie260a5632019-02-26 16:12:49 -0500290func (n *node) Get(path string, hash string, depth int, reconcile bool, txid string) interface{} {
291 log.Debugw("node-get-request", log.Fields{"path": path, "hash": hash, "depth": depth, "reconcile": reconcile,
292 "txid": txid})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400293 for strings.HasPrefix(path, "/") {
294 path = path[1:]
295 }
296
297 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400298 var rev Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400299
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500300 if branch = n.GetBranch(txid); txid == "" || branch == nil {
301 branch = n.GetBranch(NONE)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400302 }
303
304 if hash != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500305 rev = branch.GetRevision(hash)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400306 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500307 rev = branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400308 }
309
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500310 var result interface{}
Stephane Barbarie260a5632019-02-26 16:12:49 -0500311
312 // If there is not request to reconcile, try to get it from memory
313 if !reconcile {
314 if result = n.getPath(rev.GetBranch().GetLatest(), path, depth);
315 result != nil && reflect.ValueOf(result).IsValid() && !reflect.ValueOf(result).IsNil() {
316 return result
317 }
318 }
319
320 // If we got to this point, we are either trying to reconcile with the db or
321 // or we simply failed at getting information from memory
322 if n.Root.KvStore != nil {
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500323 var prList []interface{}
Stephane Barbarie260a5632019-02-26 16:12:49 -0500324 if pr := rev.LoadFromPersistence(path, txid); pr != nil && len(pr) > 0 {
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500325 // Did we receive a single or multiple revisions?
326 if len(pr) > 1 {
327 for _, revEntry := range pr {
328 prList = append(prList, revEntry.GetData())
329 }
330 result = prList
331 } else {
332 result = pr[0].GetData()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500333 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500334 }
335 }
336
337 return result
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400338}
339
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500340// getPath traverses the specified path and retrieves the data associated to it
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400341func (n *node) getPath(rev Revision, path string, depth int) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400342 if path == "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400343 return n.getData(rev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400344 }
345
346 partition := strings.SplitN(path, "/", 2)
347 name := partition[0]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400348
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400349 if len(partition) < 2 {
350 path = ""
351 } else {
352 path = partition[1]
353 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400354
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400355 names := ChildrenFields(n.Type)
356 field := names[name]
357
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500358 if field != nil && field.IsContainer {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500359 children := make([]Revision, len(rev.GetChildren(name)))
360 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500361
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400362 if field.Key != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400363 if path != "" {
364 partition = strings.SplitN(path, "/", 2)
365 key := partition[0]
366 path = ""
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400367 keyValue := field.KeyFromStr(key)
368 if _, childRev := n.findRevByKey(children, field.Key, keyValue); childRev == nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400369 return nil
370 } else {
371 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400372 return childNode.getPath(childRev, path, depth)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400373 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400374 } else {
375 var response []interface{}
376 for _, childRev := range children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400377 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400378 value := childNode.getData(childRev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400379 response = append(response, value)
380 }
381 return response
382 }
383 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400384 var response []interface{}
385 if path != "" {
386 // TODO: raise error
387 return response
388 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500389 for _, childRev := range children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400390 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400391 value := childNode.getData(childRev, depth)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400392 response = append(response, value)
393 }
394 return response
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400395 }
396 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500397
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500398 childRev := rev.GetChildren(name)[0]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500399 childNode := childRev.GetNode()
400 return childNode.getPath(childRev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400401}
402
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500403// getData retrieves the data from a node revision
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400404func (n *node) getData(rev Revision, depth int) interface{} {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500405 msg := rev.GetBranch().GetLatest().Get(depth)
Stephane Barbariea188d942018-10-16 16:43:04 -0400406 var modifiedMsg interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400407
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500408 if n.GetProxy() != nil {
Stephane Barbarieaa467942019-02-06 14:09:44 -0500409 log.Debugw("invoking-get-callbacks", log.Fields{"data": msg})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500410 if modifiedMsg = n.GetProxy().InvokeCallbacks(GET, false, msg); modifiedMsg != nil {
Stephane Barbariea188d942018-10-16 16:43:04 -0400411 msg = modifiedMsg
412 }
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400413
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400414 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500415
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400416 return msg
417}
418
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500419// Update changes the content of a node at the specified path with the provided data
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400420func (n *node) Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500421 log.Debugw("node-update-request", log.Fields{"path": path, "strict": strict, "txid": txid, "makeBranch": makeBranch})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500422
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400423 for strings.HasPrefix(path, "/") {
424 path = path[1:]
425 }
426
427 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400428 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500429 branch = n.GetBranch(NONE)
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500430 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400431 branch = makeBranch(n)
432 }
433
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500434 if branch.GetLatest() != nil {
435 log.Debugf("Branch data : %+v, Passed data: %+v", branch.GetLatest().GetData(), data)
436 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400437 if path == "" {
438 return n.doUpdate(branch, data, strict)
439 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400440
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500441 rev := branch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400442
443 partition := strings.SplitN(path, "/", 2)
444 name := partition[0]
445
446 if len(partition) < 2 {
447 path = ""
448 } else {
449 path = partition[1]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400450 }
451
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400452 field := ChildrenFields(n.Type)[name]
453 var children []Revision
454
Stephane Barbarieaa467942019-02-06 14:09:44 -0500455 if field == nil {
456 return n.doUpdate(branch, data, strict)
457 }
458
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400459 if field.IsContainer {
460 if path == "" {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400461 log.Errorf("cannot update a list")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400462 } else if field.Key != "" {
463 partition := strings.SplitN(path, "/", 2)
464 key := partition[0]
465 if len(partition) < 2 {
466 path = ""
467 } else {
468 path = partition[1]
469 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400470 keyValue := field.KeyFromStr(key)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500471
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500472 children = make([]Revision, len(rev.GetChildren(name)))
473 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500474
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400475 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400476 childNode := childRev.GetNode()
Stephane Barbariea188d942018-10-16 16:43:04 -0400477
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500478 // Save proxy in child node to ensure callbacks are called later on
Stephane Barbaried62ac4e2019-02-05 14:08:38 -0500479 // only assign in cases of non sub-folder proxies, i.e. "/"
480 if childNode.Proxy == nil && n.Proxy != nil && n.Proxy.getFullPath() == "" {
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500481 childNode.Proxy = n.Proxy
482 }
483
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400484 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400485
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400486 if newChildRev.GetHash() == childRev.GetHash() {
487 if newChildRev != childRev {
488 log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
489 newChildRev.ClearHash()
490 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500491 return branch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400492 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400493
494 _, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500495
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400496 _newKeyType := fmt.Sprintf("%s", newKey)
497 _keyValueType := fmt.Sprintf("%s", keyValue)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500498
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400499 if _newKeyType != _keyValueType {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400500 log.Errorf("cannot change key field")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400501 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500502
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500503 // Prefix the hash value with the data type (e.g. devices, logical_devices, adapters)
504 newChildRev.SetHash(name + "/" + _keyValueType)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400505 children[idx] = newChildRev
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500506
507 updatedRev := rev.UpdateChildren(name, children, branch)
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500508
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500509 branch.GetLatest().Drop(txid, false)
510 n.makeLatest(branch, updatedRev, nil)
511
Stephane Barbariea188d942018-10-16 16:43:04 -0400512 return newChildRev
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500513
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400514 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400515 log.Errorf("cannot index into container with no keys")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400516 }
517 } else {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500518 childRev := rev.GetChildren(name)[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400519 childNode := childRev.GetNode()
520 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500521 updatedRev := rev.UpdateChildren(name, []Revision{newChildRev}, branch)
522 rev.Drop(txid, false)
523 n.makeLatest(branch, updatedRev, nil)
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500524
Stephane Barbariea188d942018-10-16 16:43:04 -0400525 return newChildRev
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400526 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500527
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400528 return nil
529}
530
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400531func (n *node) doUpdate(branch *Branch, data interface{}, strict bool) Revision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500532 log.Debugf("Comparing types - expected: %+v, actual: %+v &&&&&& %s", reflect.ValueOf(n.Type).Type(),
533 reflect.TypeOf(data),
534 string(debug.Stack()))
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400535
536 if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
537 // TODO raise error
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400538 log.Errorf("data does not match type: %+v", n.Type)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400539 return nil
540 }
541
542 // TODO: validate that this actually works
543 //if n.hasChildren(data) {
544 // return nil
545 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400546
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500547 if n.GetProxy() != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400548 log.Debug("invoking proxy PRE_UPDATE Callbacks")
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500549 n.GetProxy().InvokeCallbacks(PRE_UPDATE, false, branch.GetLatest(), data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400550 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500551
552 if branch.GetLatest().GetData().(proto.Message).String() != data.(proto.Message).String() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400553 if strict {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400554 // TODO: checkAccessViolations(data, Branch.GetLatest.data)
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400555 log.Debugf("checking access violations")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400556 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500557
558 rev := branch.GetLatest().UpdateData(data, branch)
559 changes := []ChangeTuple{{POST_UPDATE, branch.GetLatest().GetData(), rev.GetData()}}
Stephane Barbariea188d942018-10-16 16:43:04 -0400560
561 // FIXME VOL-1293: the following statement corrupts the kv when using a subproxy (commenting for now)
562 // FIXME VOL-1293 cont'd: need to figure out the required conditions otherwise we are not cleaning up entries
Stephane Barbarie260a5632019-02-26 16:12:49 -0500563 branch.GetLatest().Drop(branch.Txid, false)
Stephane Barbariea188d942018-10-16 16:43:04 -0400564
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500565 n.makeLatest(branch, rev, changes)
566
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400567 return rev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400568 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500569
570 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400571}
572
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500573// Add inserts a new node at the specified path with the provided data
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400574func (n *node) Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500575 log.Debugw("node-add-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500576
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400577 for strings.HasPrefix(path, "/") {
578 path = path[1:]
579 }
580 if path == "" {
581 // TODO raise error
Stephane Barbarie126101e2018-10-11 16:18:48 -0400582 log.Errorf("cannot add for non-container mode")
Stephane Barbarieaa467942019-02-06 14:09:44 -0500583 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400584 }
585
586 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400587 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500588 branch = n.GetBranch(NONE)
589 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400590 branch = makeBranch(n)
591 }
592
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500593 rev := branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400594
595 partition := strings.SplitN(path, "/", 2)
596 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400597
598 if len(partition) < 2 {
599 path = ""
600 } else {
601 path = partition[1]
602 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400603
604 field := ChildrenFields(n.Type)[name]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500605
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400606 var children []Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400607
608 if field.IsContainer {
609 if path == "" {
610 if field.Key != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500611 if n.GetProxy() != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400612 log.Debug("invoking proxy PRE_ADD Callbacks")
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500613 n.GetProxy().InvokeCallbacks(PRE_ADD, false, data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400614 }
615
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500616 children = make([]Revision, len(rev.GetChildren(name)))
617 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500618
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400619 _, key := GetAttributeValue(data, field.Key, 0)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500620
Stephane Barbarie126101e2018-10-11 16:18:48 -0400621 if _, exists := n.findRevByKey(children, field.Key, key.String()); exists != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400622 // TODO raise error
Stephane Barbarie260a5632019-02-26 16:12:49 -0500623 log.Warnw("duplicate-key-found", log.Fields{"key": key.String()})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500624 return exists
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400625 }
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500626 childRev := n.MakeNode(data, "").Latest()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500627
628 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
629 childRev.SetHash(name + "/" + key.String())
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500630
631 // Create watch for <component>/<key>
632 childRev.SetupWatch(childRev.GetHash())
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500633
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500634 children = append(children, childRev)
635 rev = rev.UpdateChildren(name, children, branch)
636 changes := []ChangeTuple{{POST_ADD, nil, childRev.GetData()}}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400637
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500638 rev.Drop(txid, false)
639 n.makeLatest(branch, rev, changes)
640
641 return childRev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400642 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500643 log.Errorf("cannot add to non-keyed container")
644
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400645 } else if field.Key != "" {
646 partition := strings.SplitN(path, "/", 2)
647 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400648 if len(partition) < 2 {
649 path = ""
650 } else {
651 path = partition[1]
652 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400653 keyValue := field.KeyFromStr(key)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500654
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500655 children = make([]Revision, len(rev.GetChildren(name)))
656 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500657
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400658 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500659
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400660 childNode := childRev.GetNode()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400661 newChildRev := childNode.Add(path, data, txid, makeBranch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500662
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500663 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
664 childRev.SetHash(name + "/" + keyValue.(string))
665
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400666 children[idx] = newChildRev
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500667
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500668 rev = rev.UpdateChildren(name, children, branch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500669 rev.Drop(txid, false)
670 n.makeLatest(branch, rev.GetBranch().GetLatest(), nil)
671
672 return newChildRev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400673 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400674 log.Errorf("cannot add to non-keyed container")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400675 }
676 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400677 log.Errorf("cannot add to non-container field")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400678 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500679
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400680 return nil
681}
682
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500683// Remove eliminates a node at the specified path
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400684func (n *node) Remove(path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500685 log.Debugw("node-remove-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500686
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400687 for strings.HasPrefix(path, "/") {
688 path = path[1:]
689 }
690 if path == "" {
691 // TODO raise error
Stephane Barbarie126101e2018-10-11 16:18:48 -0400692 log.Errorf("cannot remove for non-container mode")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400693 }
694 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400695 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500696 branch = n.GetBranch(NONE)
697 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400698 branch = makeBranch(n)
699 }
700
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500701 rev := branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400702
703 partition := strings.SplitN(path, "/", 2)
704 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400705 if len(partition) < 2 {
706 path = ""
707 } else {
708 path = partition[1]
709 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400710
711 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400712 var children []Revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400713 postAnnouncement := []ChangeTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400714
715 if field.IsContainer {
716 if path == "" {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500717 log.Errorw("cannot-remove-without-key", log.Fields{"name": name, "key": path})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400718 } else if field.Key != "" {
719 partition := strings.SplitN(path, "/", 2)
720 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400721 if len(partition) < 2 {
722 path = ""
723 } else {
724 path = partition[1]
725 }
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500726
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400727 keyValue := field.KeyFromStr(key)
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500728 children = make([]Revision, len(rev.GetChildren(name)))
729 copy(children, rev.GetChildren(name))
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500730
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400731 if path != "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400732 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400733 childNode := childRev.GetNode()
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500734 if childNode.Proxy == nil {
735 childNode.Proxy = n.Proxy
736 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400737 newChildRev := childNode.Remove(path, txid, makeBranch)
738 children[idx] = newChildRev
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500739 rev.SetChildren(name, children)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500740 branch.GetLatest().Drop(txid, false)
741 n.makeLatest(branch, rev, nil)
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500742 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400743 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500744
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500745 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
746 if n.GetProxy() != nil {
747 data := childRev.GetData()
748 n.GetProxy().InvokeCallbacks(PRE_REMOVE, false, data)
749 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, data, nil})
750 } else {
751 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, childRev.GetData(), nil})
752 }
753
754 childRev.Drop(txid, true)
755 children = append(children[:idx], children[idx+1:]...)
756 rev.SetChildren(name, children)
757
758 branch.GetLatest().Drop(txid, false)
759 n.makeLatest(branch, rev, postAnnouncement)
760
761 return rev
762 } else {
763 log.Errorw("failed-to-find-revision", log.Fields{"name": name, "key": keyValue.(string)})
764 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400765 }
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500766 log.Errorw("cannot-add-to-non-keyed-container", log.Fields{"name": name, "path": path, "fieldKey": field.Key})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500767
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400768 } else {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500769 log.Errorw("cannot-add-to-non-container-field", log.Fields{"name": name, "path": path})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400770 }
771
772 return nil
773}
774
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400775// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
776
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500777// MakeBranchFunction is a type for function references intented to create a branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400778type MakeBranchFunction func(*node) *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400779
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500780// MakeBranch creates a new branch for the provided transaction id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400781func (n *node) MakeBranch(txid string) *Branch {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500782 branchPoint := n.GetBranch(NONE).GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400783 branch := NewBranch(n, txid, branchPoint, true)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500784 n.SetBranch(txid, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400785 return branch
786}
787
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500788// DeleteBranch removes a branch with the specified id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400789func (n *node) DeleteBranch(txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500790 n.Lock()
791 defer n.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400792 delete(n.Branches, txid)
793}
794
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400795func (n *node) mergeChild(txid string, dryRun bool) func(Revision) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400796 f := func(rev Revision) Revision {
797 childBranch := rev.GetBranch()
798
799 if childBranch.Txid == txid {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400800 rev, _ = childBranch.Node.MergeBranch(txid, dryRun)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400801 }
802
803 return rev
804 }
805 return f
806}
807
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500808// MergeBranch will integrate the contents of a transaction branch within the latest branch of a given node
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400809func (n *node) MergeBranch(txid string, dryRun bool) (Revision, error) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500810 srcBranch := n.GetBranch(txid)
811 dstBranch := n.GetBranch(NONE)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400812
813 forkRev := srcBranch.Origin
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500814 srcRev := srcBranch.GetLatest()
815 dstRev := dstBranch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400816
817 rev, changes := Merge3Way(forkRev, srcRev, dstRev, n.mergeChild(txid, dryRun), dryRun)
818
819 if !dryRun {
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500820 if rev != nil {
Stephane Barbarie260a5632019-02-26 16:12:49 -0500821 rev.SetHash(dstRev.GetHash())
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500822 n.makeLatest(dstBranch, rev, changes)
823 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500824 n.DeleteBranch(txid)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400825 }
826
Stephane Barbariee16186c2018-09-11 10:46:34 -0400827 // TODO: return proper error when one occurs
828 return rev, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400829}
830
831// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diff utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
832
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400833//func (n *node) diff(hash1, hash2, txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400834// branch := n.Branches[txid]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500835// rev1 := branch.GetHash(hash1)
836// rev2 := branch.GetHash(hash2)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400837//
838// if rev1.GetHash() == rev2.GetHash() {
839// // empty patch
840// } else {
841// // translate data to json and generate patch
842// patch, err := jsonpatch.MakePatch(rev1.GetData(), rev2.GetData())
843// patch.
844// }
845//}
846
847// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tag utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
848
849// TODO: is tag mgmt used in the python implementation? Need to validate
850
851// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
852
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400853func (n *node) hasChildren(data interface{}) bool {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400854 for fieldName, field := range ChildrenFields(n.Type) {
855 _, fieldValue := GetAttributeValue(data, fieldName, 0)
856
857 if (field.IsContainer && fieldValue.Len() > 0) || !fieldValue.IsNil() {
858 log.Error("cannot update external children")
859 return true
860 }
861 }
862
863 return false
864}
865
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400866// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ node Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400867
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500868// CreateProxy returns a reference to a sub-tree of the data model
869func (n *node) CreateProxy(path string, exclusive bool) *Proxy {
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500870 return n.createProxy(path, path, n, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400871}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500872
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500873func (n *node) createProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400874 for strings.HasPrefix(path, "/") {
875 path = path[1:]
876 }
877 if path == "" {
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500878 return n.makeProxy(path, fullPath, parentNode, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400879 }
880
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500881 rev := n.GetBranch(NONE).GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400882 partition := strings.SplitN(path, "/", 2)
883 name := partition[0]
Stephane Barbarie126101e2018-10-11 16:18:48 -0400884 if len(partition) < 2 {
885 path = ""
886 } else {
887 path = partition[1]
888 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400889
890 field := ChildrenFields(n.Type)[name]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500891 if field.IsContainer {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400892 if path == "" {
khenaidoo21d51152019-02-01 13:48:37 -0500893 //log.Error("cannot proxy a container field")
Stephane Barbarieaa467942019-02-06 14:09:44 -0500894 newNode := n.MakeNode(reflect.New(field.ClassType.Elem()).Interface(), "")
895 return newNode.makeProxy(path, fullPath, parentNode, exclusive)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400896 } else if field.Key != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400897 partition := strings.SplitN(path, "/", 2)
898 key := partition[0]
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400899 if len(partition) < 2 {
900 path = ""
901 } else {
902 path = partition[1]
903 }
904 keyValue := field.KeyFromStr(key)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400905 var children []Revision
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500906 children = make([]Revision, len(rev.GetChildren(name)))
907 copy(children, rev.GetChildren(name))
Stephane Barbarie933b09b2019-01-09 11:12:09 -0500908 if _, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
909 childNode := childRev.GetNode()
910 return childNode.createProxy(path, fullPath, n, exclusive)
911 }
Stephane Barbarie126101e2018-10-11 16:18:48 -0400912 } else {
913 log.Error("cannot index into container with no keys")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400914 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400915 } else {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500916 childRev := rev.GetChildren(name)[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400917 childNode := childRev.GetNode()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500918 return childNode.createProxy(path, fullPath, n, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400919 }
920
Stephane Barbarie933b09b2019-01-09 11:12:09 -0500921 log.Warnf("Cannot create proxy - latest rev:%s, all revs:%+v", rev.GetHash(), n.GetBranch(NONE).Revisions)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400922 return nil
923}
924
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500925func (n *node) makeProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500926 n.Lock()
927 defer n.Unlock()
Stephane Barbarie126101e2018-10-11 16:18:48 -0400928 r := &root{
929 node: n,
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500930 Callbacks: n.Root.GetCallbacks(),
931 NotificationCallbacks: n.Root.GetNotificationCallbacks(),
Stephane Barbarie126101e2018-10-11 16:18:48 -0400932 DirtyNodes: n.Root.DirtyNodes,
933 KvStore: n.Root.KvStore,
934 Loading: n.Root.Loading,
935 RevisionClass: n.Root.RevisionClass,
936 }
937
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400938 if n.Proxy == nil {
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500939 n.Proxy = NewProxy(r, n, parentNode, path, fullPath, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400940 } else {
941 if n.Proxy.Exclusive {
942 log.Error("node is already owned exclusively")
943 }
944 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500945
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400946 return n.Proxy
947}
948
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400949func (n *node) makeEventBus() *EventBus {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500950 n.Lock()
951 defer n.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400952 if n.EventBus == nil {
953 n.EventBus = NewEventBus()
954 }
955 return n.EventBus
956}
957
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500958func (n *node) SetProxy(proxy *Proxy) {
959 n.Lock()
960 defer n.Unlock()
961 n.Proxy = proxy
962}
963
964func (n *node) GetProxy() *Proxy {
965 n.Lock()
966 defer n.Unlock()
967 return n.Proxy
968}
969
970func (n *node) GetBranch(key string) *Branch {
971 n.Lock()
972 defer n.Unlock()
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500973
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500974 if n.Branches != nil {
975 if branch, exists := n.Branches[key]; exists {
976 return branch
977 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500978 }
979 return nil
980}
981
982func (n *node) SetBranch(key string, branch *Branch) {
983 n.Lock()
984 defer n.Unlock()
985 n.Branches[key] = branch
986}
987
988func (n *node) GetRoot() *root {
989 n.Lock()
990 defer n.Unlock()
991 return n.Root
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400992}