blob: 1621b6f34c616be1db817223cfdb564f2845cc50 [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"
27 "strings"
Stephane Barbariedc5022d2018-11-19 15:21:44 -050028 "sync"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040029)
30
Stephane Barbariedc5022d2018-11-19 15:21:44 -050031// When a branch has no transaction id, everything gets stored in NONE
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040032const (
33 NONE string = "none"
34)
35
Stephane Barbariedc5022d2018-11-19 15:21:44 -050036// Node interface is an abstraction of the node data structure
Stephane Barbarie06c4a742018-10-01 11:09:32 -040037type Node interface {
38 MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple)
39
40 // CRUD functions
41 Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision
42 Get(path string, hash string, depth int, deep bool, txid string) interface{}
43 Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision
44 Remove(path string, txid string, makeBranch MakeBranchFunction) Revision
45
46 MakeBranch(txid string) *Branch
47 DeleteBranch(txid string)
48 MergeBranch(txid string, dryRun bool) (Revision, error)
49
50 MakeTxBranch() string
51 DeleteTxBranch(txid string)
52 FoldTxBranch(txid string)
53
Stephane Barbariedc5022d2018-11-19 15:21:44 -050054 CreateProxy(path string, exclusive bool) *Proxy
55 GetProxy() *Proxy
Stephane Barbarie06c4a742018-10-01 11:09:32 -040056}
57
58type node struct {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050059 sync.RWMutex
Stephane Barbarie126101e2018-10-11 16:18:48 -040060 Root *root
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040061 Type interface{}
62 Branches map[string]*Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -040063 Tags map[string]Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040064 Proxy *Proxy
65 EventBus *EventBus
66 AutoPrune bool
67}
68
Stephane Barbariedc5022d2018-11-19 15:21:44 -050069// ChangeTuple holds details of modifications made to a revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -040070type ChangeTuple struct {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -040071 Type CallbackType
72 PreviousData interface{}
73 LatestData interface{}
Stephane Barbarie694e2b92018-09-07 12:17:36 -040074}
75
Stephane Barbariedc5022d2018-11-19 15:21:44 -050076// NewNode creates a new instance of the node data structure
Stephane Barbarie06c4a742018-10-01 11:09:32 -040077func NewNode(root *root, initialData interface{}, autoPrune bool, txid string) *node {
78 n := &node{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040079
Stephane Barbarie126101e2018-10-11 16:18:48 -040080 n.Root = root
Stephane Barbarieec0919b2018-09-05 14:14:29 -040081 n.Branches = make(map[string]*Branch)
82 n.Tags = make(map[string]Revision)
83 n.Proxy = nil
84 n.EventBus = nil
85 n.AutoPrune = autoPrune
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040086
87 if IsProtoMessage(initialData) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040088 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040089 dataCopy := proto.Clone(initialData.(proto.Message))
Stephane Barbarieec0919b2018-09-05 14:14:29 -040090 n.initialize(dataCopy, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040091 } else if reflect.ValueOf(initialData).IsValid() {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050092 // FIXME: this block does not reflect the original implementation
93 // it should be checking if the provided initial_data is already a type!??!
94 // it should be checked before IsProtoMessage
Stephane Barbarieec0919b2018-09-05 14:14:29 -040095 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040096 } else {
97 // not implemented error
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -040098 log.Errorf("cannot process initial data - %+v", initialData)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040099 }
100
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400101 return n
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400102}
103
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500104// MakeNode creates a new node in the tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400105func (n *node) MakeNode(data interface{}, txid string) *node {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400106 return NewNode(n.Root, data, true, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400107}
108
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500109// MakeRevision create a new revision of the node in the tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400110func (n *node) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500111 return n.GetRoot().MakeRevision(branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400112}
113
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500114// makeLatest will mark the revision of a node as being the latest
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400115func (n *node) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
Stephane Barbarie260a5632019-02-26 16:12:49 -0500116 n.Lock()
117 defer n.Unlock()
118
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400119 // Keep a reference to the current revision
120 var previous string
121 if branch.GetLatest() != nil {
122 previous = branch.GetLatest().GetHash()
123 }
124
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500125 branch.AddRevision(revision)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400126
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400127 // If anything is new, then set the revision as the latest
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500128 if branch.GetLatest() == nil || revision.GetHash() != branch.GetLatest().GetHash() {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400129 if revision.GetName() != "" {
130 GetRevCache().Cache.Store(revision.GetName(), revision)
131 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500132 branch.SetLatest(revision)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400133 }
134
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400135 // Delete the previous revision if anything has changed
136 if previous != "" && previous != branch.GetLatest().GetHash() {
137 branch.DeleteRevision(previous)
138 }
139
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400140 if changeAnnouncement != nil && branch.Txid == "" {
Stephane Barbarie260a5632019-02-26 16:12:49 -0500141 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400142 for _, change := range changeAnnouncement {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400143 log.Debugw("adding-callback",
144 log.Fields{
145 "callbacks": n.Proxy.getCallbacks(change.Type),
146 "type": change.Type,
147 "previousData": change.PreviousData,
148 "latestData": change.LatestData,
149 })
Stephane Barbarie260a5632019-02-26 16:12:49 -0500150 n.Root.AddCallback(
151 n.Proxy.InvokeCallbacks,
Stephane Barbarie126101e2018-10-11 16:18:48 -0400152 change.Type,
153 true,
154 change.PreviousData,
155 change.LatestData)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400156 }
157 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400158 }
159}
160
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500161// Latest returns the latest revision of node with or without the transaction id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400162func (n *node) Latest(txid ...string) Revision {
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400163 var branch *Branch
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400164
165 if len(txid) > 0 && txid[0] != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500166 if branch = n.GetBranch(txid[0]); branch != nil {
167 return branch.GetLatest()
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400168 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500169 } else if branch = n.GetBranch(NONE); branch != nil {
170 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400171 }
172 return nil
173}
174
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500175// initialize prepares the content of a node along with its possible ramifications
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400176func (n *node) initialize(data interface{}, txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500177 n.Lock()
178 children := make(map[string][]Revision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400179 for fieldName, field := range ChildrenFields(n.Type) {
180 _, fieldValue := GetAttributeValue(data, fieldName, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400181
182 if fieldValue.IsValid() {
183 if field.IsContainer {
184 if field.Key != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400185 for i := 0; i < fieldValue.Len(); i++ {
186 v := fieldValue.Index(i)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400187
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500188 if rev := n.MakeNode(v.Interface(), txid).Latest(txid); rev != nil {
189 children[fieldName] = append(children[fieldName], rev)
190 }
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400191
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500192 // TODO: The following logic was ported from v1.0. Need to verify if it is required
193 //var keysSeen []string
194 //_, key := GetAttributeValue(v.Interface(), field.Key, 0)
195 //for _, k := range keysSeen {
196 // if k == key.String() {
197 // //log.Errorf("duplicate key - %s", k)
198 // }
199 //}
200 //keysSeen = append(keysSeen, key.String())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400201 }
202
203 } else {
204 for i := 0; i < fieldValue.Len(); i++ {
205 v := fieldValue.Index(i)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500206 if newNodeRev := n.MakeNode(v.Interface(), txid).Latest(); newNodeRev != nil {
207 children[fieldName] = append(children[fieldName], newNodeRev)
208 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400209 }
210 }
211 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500212 if newNodeRev := n.MakeNode(fieldValue.Interface(), txid).Latest(); newNodeRev != nil {
213 children[fieldName] = append(children[fieldName], newNodeRev)
214 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400215 }
216 } else {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400217 log.Errorf("field is invalid - %+v", fieldValue)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400218 }
219 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500220 n.Unlock()
221
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400222 branch := NewBranch(n, "", nil, n.AutoPrune)
223 rev := n.MakeRevision(branch, data, children)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400224 n.makeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400225
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400226 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500227 n.SetBranch(NONE, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400228 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500229 n.SetBranch(txid, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400230 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400231}
232
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500233// findRevByKey retrieves a specific revision from a node tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400234func (n *node) findRevByKey(revs []Revision, keyName string, value interface{}) (int, Revision) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500235 n.Lock()
236 defer n.Unlock()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500237
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400238 for i, rev := range revs {
239 dataValue := reflect.ValueOf(rev.GetData())
240 dataStruct := GetAttributeStructure(rev.GetData(), keyName, 0)
241
242 fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
243
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400244 a := fmt.Sprintf("%s", fieldValue.Interface())
245 b := fmt.Sprintf("%s", value)
246 if a == b {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500247 return i, revs[i]
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400248 }
249 }
250
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400251 return -1, nil
252}
253
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500254// Get retrieves the data from a node tree that resides at the specified path
Stephane Barbarieaa467942019-02-06 14:09:44 -0500255func (n *node) List(path string, hash string, depth int, deep bool, txid string) interface{} {
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500256 log.Debugw("node-list-request", log.Fields{"path": path, "hash": hash, "depth": depth, "deep": deep, "txid": txid})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500257 if deep {
258 depth = -1
259 }
260
261 for strings.HasPrefix(path, "/") {
262 path = path[1:]
263 }
264
265 var branch *Branch
266 var rev Revision
267
268 if branch = n.GetBranch(txid); txid == "" || branch == nil {
269 branch = n.GetBranch(NONE)
270 }
271
272 if hash != "" {
273 rev = branch.GetRevision(hash)
274 } else {
275 rev = branch.GetLatest()
276 }
277
278 var result interface{}
279 var prList []interface{}
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400280 if pr := rev.LoadFromPersistence(path, txid, nil); pr != nil {
Stephane Barbarieaa467942019-02-06 14:09:44 -0500281 for _, revEntry := range pr {
282 prList = append(prList, revEntry.GetData())
283 }
284 result = prList
285 }
286
287 return result
288}
289
290// Get retrieves the data from a node tree that resides at the specified path
Stephane Barbarie260a5632019-02-26 16:12:49 -0500291func (n *node) Get(path string, hash string, depth int, reconcile bool, txid string) interface{} {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400292 log.Debugw("node-get-request", log.Fields{"path": path, "hash": hash, "depth": depth, "reconcile": reconcile, "txid": txid})
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400293
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400294 for strings.HasPrefix(path, "/") {
295 path = path[1:]
296 }
297
298 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400299 var rev Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400300
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500301 if branch = n.GetBranch(txid); txid == "" || branch == nil {
302 branch = n.GetBranch(NONE)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400303 }
304
305 if hash != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500306 rev = branch.GetRevision(hash)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400307 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500308 rev = branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400309 }
310
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500311 var result interface{}
Stephane Barbarie260a5632019-02-26 16:12:49 -0500312
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400313 // If there is no request to reconcile, try to get it from memory
Stephane Barbarie260a5632019-02-26 16:12:49 -0500314 if !reconcile {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400315 // Try to find an entry matching the path value from one of these sources
316 // 1. Start with the cache which stores revisions by watch names
317 // 2. Then look in the revision tree, especially if it's a sub-path such as /devices/1234/flows
318 // 3. As a last effort, move on to the KV store
319 if entry, exists := GetRevCache().Cache.Load(path); exists && entry.(Revision) != nil {
320 return proto.Clone(entry.(Revision).GetData().(proto.Message))
321 } else if result = n.getPath(rev.GetBranch().GetLatest(), path, depth); result != nil && reflect.ValueOf(result).IsValid() && !reflect.ValueOf(result).IsNil() {
Stephane Barbarie260a5632019-02-26 16:12:49 -0500322 return result
323 }
324 }
325
326 // If we got to this point, we are either trying to reconcile with the db or
327 // or we simply failed at getting information from memory
328 if n.Root.KvStore != nil {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400329 if pr := rev.LoadFromPersistence(path, txid, nil); pr != nil && len(pr) > 0 {
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500330 // Did we receive a single or multiple revisions?
331 if len(pr) > 1 {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400332 var revs []interface{}
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500333 for _, revEntry := range pr {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400334 revs = append(revs, revEntry.GetData())
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500335 }
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400336 result = revs
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500337 } else {
338 result = pr[0].GetData()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500339 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500340 }
341 }
342
343 return result
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400344}
345
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400346//getPath traverses the specified path and retrieves the data associated to it
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400347func (n *node) getPath(rev Revision, path string, depth int) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400348 if path == "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400349 return n.getData(rev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400350 }
351
352 partition := strings.SplitN(path, "/", 2)
353 name := partition[0]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400354
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400355 if len(partition) < 2 {
356 path = ""
357 } else {
358 path = partition[1]
359 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400360
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400361 names := ChildrenFields(n.Type)
362 field := names[name]
363
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500364 if field != nil && field.IsContainer {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500365 children := make([]Revision, len(rev.GetChildren(name)))
366 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500367
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400368 if field.Key != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400369 if path != "" {
370 partition = strings.SplitN(path, "/", 2)
371 key := partition[0]
372 path = ""
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400373 keyValue := field.KeyFromStr(key)
374 if _, childRev := n.findRevByKey(children, field.Key, keyValue); childRev == nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400375 return nil
376 } else {
377 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400378 return childNode.getPath(childRev, path, depth)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400379 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400380 } else {
381 var response []interface{}
382 for _, childRev := range children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400383 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400384 value := childNode.getData(childRev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400385 response = append(response, value)
386 }
387 return response
388 }
389 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400390 var response []interface{}
391 if path != "" {
392 // TODO: raise error
393 return response
394 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500395 for _, childRev := range children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400396 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400397 value := childNode.getData(childRev, depth)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400398 response = append(response, value)
399 }
400 return response
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400401 }
402 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500403
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500404 childRev := rev.GetChildren(name)[0]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500405 childNode := childRev.GetNode()
406 return childNode.getPath(childRev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400407}
408
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500409// getData retrieves the data from a node revision
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400410func (n *node) getData(rev Revision, depth int) interface{} {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500411 msg := rev.GetBranch().GetLatest().Get(depth)
Stephane Barbariea188d942018-10-16 16:43:04 -0400412 var modifiedMsg interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400413
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500414 if n.GetProxy() != nil {
Stephane Barbarieaa467942019-02-06 14:09:44 -0500415 log.Debugw("invoking-get-callbacks", log.Fields{"data": msg})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500416 if modifiedMsg = n.GetProxy().InvokeCallbacks(GET, false, msg); modifiedMsg != nil {
Stephane Barbariea188d942018-10-16 16:43:04 -0400417 msg = modifiedMsg
418 }
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400419
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400420 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500421
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400422 return msg
423}
424
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500425// Update changes the content of a node at the specified path with the provided data
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400426func (n *node) Update(path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500427 log.Debugw("node-update-request", log.Fields{"path": path, "strict": strict, "txid": txid, "makeBranch": makeBranch})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500428
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400429 for strings.HasPrefix(path, "/") {
430 path = path[1:]
431 }
432
433 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400434 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500435 branch = n.GetBranch(NONE)
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500436 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400437 branch = makeBranch(n)
438 }
439
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500440 if branch.GetLatest() != nil {
441 log.Debugf("Branch data : %+v, Passed data: %+v", branch.GetLatest().GetData(), data)
442 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400443 if path == "" {
444 return n.doUpdate(branch, data, strict)
445 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400446
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500447 rev := branch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400448
449 partition := strings.SplitN(path, "/", 2)
450 name := partition[0]
451
452 if len(partition) < 2 {
453 path = ""
454 } else {
455 path = partition[1]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400456 }
457
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400458 field := ChildrenFields(n.Type)[name]
459 var children []Revision
460
Stephane Barbarieaa467942019-02-06 14:09:44 -0500461 if field == nil {
462 return n.doUpdate(branch, data, strict)
463 }
464
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400465 if field.IsContainer {
466 if path == "" {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400467 log.Errorf("cannot update a list")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400468 } else if field.Key != "" {
469 partition := strings.SplitN(path, "/", 2)
470 key := partition[0]
471 if len(partition) < 2 {
472 path = ""
473 } else {
474 path = partition[1]
475 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400476 keyValue := field.KeyFromStr(key)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500477
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500478 children = make([]Revision, len(rev.GetChildren(name)))
479 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500480
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400481 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400482
483 if childRev == nil {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400484 log.Debugw("child-revision-is-nil", log.Fields{"key": keyValue})
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400485 return branch.GetLatest()
486 }
487
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400488 childNode := childRev.GetNode()
Stephane Barbariea188d942018-10-16 16:43:04 -0400489
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500490 // Save proxy in child node to ensure callbacks are called later on
Stephane Barbaried62ac4e2019-02-05 14:08:38 -0500491 // only assign in cases of non sub-folder proxies, i.e. "/"
492 if childNode.Proxy == nil && n.Proxy != nil && n.Proxy.getFullPath() == "" {
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500493 childNode.Proxy = n.Proxy
494 }
495
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400496 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400497
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400498 if newChildRev.GetHash() == childRev.GetHash() {
499 if newChildRev != childRev {
500 log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
501 newChildRev.ClearHash()
502 }
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400503 log.Debugw("child-revisions-have-matching-hash", log.Fields{"hash": childRev.GetHash(), "key": keyValue})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500504 return branch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400505 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400506
507 _, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500508
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400509 _newKeyType := fmt.Sprintf("%s", newKey)
510 _keyValueType := fmt.Sprintf("%s", keyValue)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500511
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400512 if _newKeyType != _keyValueType {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400513 log.Errorf("cannot change key field")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400514 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500515
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500516 // Prefix the hash value with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400517 newChildRev.SetName(name + "/" + _keyValueType)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400518
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400519 branch.LatestLock.Lock()
520 defer branch.LatestLock.Unlock()
521
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400522 if idx >= 0 {
523 children[idx] = newChildRev
524 } else {
525 children = append(children, newChildRev)
526 }
527
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500528 updatedRev := rev.UpdateChildren(name, children, branch)
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500529
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500530 n.makeLatest(branch, updatedRev, nil)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400531 updatedRev.ChildDrop(name, childRev.GetHash())
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500532
Stephane Barbariea188d942018-10-16 16:43:04 -0400533 return newChildRev
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500534
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400535 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400536 log.Errorf("cannot index into container with no keys")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400537 }
538 } else {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500539 childRev := rev.GetChildren(name)[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400540 childNode := childRev.GetNode()
541 newChildRev := childNode.Update(path, data, strict, txid, makeBranch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400542
543 branch.LatestLock.Lock()
544 defer branch.LatestLock.Unlock()
545
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500546 updatedRev := rev.UpdateChildren(name, []Revision{newChildRev}, branch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500547 n.makeLatest(branch, updatedRev, nil)
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500548
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400549 updatedRev.ChildDrop(name, childRev.GetHash())
550
Stephane Barbariea188d942018-10-16 16:43:04 -0400551 return newChildRev
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400552 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500553
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400554 return nil
555}
556
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400557func (n *node) doUpdate(branch *Branch, data interface{}, strict bool) Revision {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400558 log.Debugw("comparing-types", log.Fields{"expected": reflect.ValueOf(n.Type).Type(), "actual": reflect.TypeOf(data)})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400559
560 if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
561 // TODO raise error
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400562 log.Errorw("types-do-not-match: %+v", log.Fields{"actual": reflect.TypeOf(data), "expected": n.Type})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400563 return nil
564 }
565
566 // TODO: validate that this actually works
567 //if n.hasChildren(data) {
568 // return nil
569 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400570
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500571 if n.GetProxy() != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400572 log.Debug("invoking proxy PRE_UPDATE Callbacks")
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500573 n.GetProxy().InvokeCallbacks(PRE_UPDATE, false, branch.GetLatest(), data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400574 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500575
576 if branch.GetLatest().GetData().(proto.Message).String() != data.(proto.Message).String() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400577 if strict {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400578 // TODO: checkAccessViolations(data, Branch.GetLatest.data)
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400579 log.Debugf("checking access violations")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400580 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500581
582 rev := branch.GetLatest().UpdateData(data, branch)
583 changes := []ChangeTuple{{POST_UPDATE, branch.GetLatest().GetData(), rev.GetData()}}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500584 n.makeLatest(branch, rev, changes)
585
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400586 return rev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400587 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500588 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400589}
590
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500591// Add inserts a new node at the specified path with the provided data
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400592func (n *node) Add(path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500593 log.Debugw("node-add-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500594
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400595 for strings.HasPrefix(path, "/") {
596 path = path[1:]
597 }
598 if path == "" {
599 // TODO raise error
Stephane Barbarie126101e2018-10-11 16:18:48 -0400600 log.Errorf("cannot add for non-container mode")
Stephane Barbarieaa467942019-02-06 14:09:44 -0500601 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400602 }
603
604 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400605 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500606 branch = n.GetBranch(NONE)
607 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400608 branch = makeBranch(n)
609 }
610
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500611 rev := branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400612
613 partition := strings.SplitN(path, "/", 2)
614 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400615
616 if len(partition) < 2 {
617 path = ""
618 } else {
619 path = partition[1]
620 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400621
622 field := ChildrenFields(n.Type)[name]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500623
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400624 var children []Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400625
626 if field.IsContainer {
627 if path == "" {
628 if field.Key != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500629 if n.GetProxy() != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400630 log.Debug("invoking proxy PRE_ADD Callbacks")
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500631 n.GetProxy().InvokeCallbacks(PRE_ADD, false, data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400632 }
633
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500634 children = make([]Revision, len(rev.GetChildren(name)))
635 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500636
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400637 _, key := GetAttributeValue(data, field.Key, 0)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500638
Stephane Barbarie126101e2018-10-11 16:18:48 -0400639 if _, exists := n.findRevByKey(children, field.Key, key.String()); exists != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400640 // TODO raise error
Stephane Barbarie260a5632019-02-26 16:12:49 -0500641 log.Warnw("duplicate-key-found", log.Fields{"key": key.String()})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500642 return exists
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400643 }
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500644 childRev := n.MakeNode(data, "").Latest()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500645
646 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400647 childRev.SetName(name + "/" + key.String())
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500648
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400649 branch.LatestLock.Lock()
650 defer branch.LatestLock.Unlock()
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500651
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500652 children = append(children, childRev)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400653
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400654 updatedRev := rev.UpdateChildren(name, children, branch)
655 changes := []ChangeTuple{{POST_ADD, nil, childRev.GetData()}}
656 childRev.SetupWatch(childRev.GetName())
657
658 n.makeLatest(branch, updatedRev, changes)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500659
660 return childRev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400661 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500662 log.Errorf("cannot add to non-keyed container")
663
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400664 } else if field.Key != "" {
665 partition := strings.SplitN(path, "/", 2)
666 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400667 if len(partition) < 2 {
668 path = ""
669 } else {
670 path = partition[1]
671 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400672 keyValue := field.KeyFromStr(key)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500673
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500674 children = make([]Revision, len(rev.GetChildren(name)))
675 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500676
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400677 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500678
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400679 if childRev == nil {
680 return branch.GetLatest()
681 }
682
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400683 childNode := childRev.GetNode()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400684 newChildRev := childNode.Add(path, data, txid, makeBranch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500685
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500686 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400687 newChildRev.SetName(name + "/" + keyValue.(string))
688
689 branch.LatestLock.Lock()
690 defer branch.LatestLock.Unlock()
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500691
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400692 if idx >= 0 {
693 children[idx] = newChildRev
694 } else {
695 children = append(children, newChildRev)
696 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500697
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400698 updatedRev := rev.UpdateChildren(name, children, branch)
699 n.makeLatest(branch, updatedRev, nil)
700
701 updatedRev.ChildDrop(name, childRev.GetHash())
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500702
703 return newChildRev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400704 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400705 log.Errorf("cannot add to non-keyed container")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400706 }
707 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400708 log.Errorf("cannot add to non-container field")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400709 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500710
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400711 return nil
712}
713
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500714// Remove eliminates a node at the specified path
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400715func (n *node) Remove(path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500716 log.Debugw("node-remove-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500717
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400718 for strings.HasPrefix(path, "/") {
719 path = path[1:]
720 }
721 if path == "" {
722 // TODO raise error
Stephane Barbarie126101e2018-10-11 16:18:48 -0400723 log.Errorf("cannot remove for non-container mode")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400724 }
725 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400726 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500727 branch = n.GetBranch(NONE)
728 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400729 branch = makeBranch(n)
730 }
731
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500732 rev := branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400733
734 partition := strings.SplitN(path, "/", 2)
735 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400736 if len(partition) < 2 {
737 path = ""
738 } else {
739 path = partition[1]
740 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400741
742 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400743 var children []Revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400744 postAnnouncement := []ChangeTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400745
746 if field.IsContainer {
747 if path == "" {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500748 log.Errorw("cannot-remove-without-key", log.Fields{"name": name, "key": path})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400749 } else if field.Key != "" {
750 partition := strings.SplitN(path, "/", 2)
751 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400752 if len(partition) < 2 {
753 path = ""
754 } else {
755 path = partition[1]
756 }
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500757
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400758 keyValue := field.KeyFromStr(key)
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500759 children = make([]Revision, len(rev.GetChildren(name)))
760 copy(children, rev.GetChildren(name))
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500761
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400762 if path != "" {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400763 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
764 childNode := childRev.GetNode()
765 if childNode.Proxy == nil {
766 childNode.Proxy = n.Proxy
767 }
768 newChildRev := childNode.Remove(path, txid, makeBranch)
769
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400770 branch.LatestLock.Lock()
771 defer branch.LatestLock.Unlock()
772
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400773 if idx >= 0 {
774 children[idx] = newChildRev
775 } else {
776 children = append(children, newChildRev)
777 }
778
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400779 rev.SetChildren(name, children)
780 branch.GetLatest().Drop(txid, false)
781 n.makeLatest(branch, rev, nil)
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500782 }
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400783 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400784 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500785
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400786 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil && idx >= 0 {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500787 if n.GetProxy() != nil {
788 data := childRev.GetData()
789 n.GetProxy().InvokeCallbacks(PRE_REMOVE, false, data)
790 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, data, nil})
791 } else {
792 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, childRev.GetData(), nil})
793 }
794
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400795 childRev.StorageDrop(txid, true)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400796 GetRevCache().Cache.Delete(childRev.GetName())
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400797
798 branch.LatestLock.Lock()
799 defer branch.LatestLock.Unlock()
800
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500801 children = append(children[:idx], children[idx+1:]...)
802 rev.SetChildren(name, children)
803
804 branch.GetLatest().Drop(txid, false)
805 n.makeLatest(branch, rev, postAnnouncement)
806
807 return rev
808 } else {
809 log.Errorw("failed-to-find-revision", log.Fields{"name": name, "key": keyValue.(string)})
810 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400811 }
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500812 log.Errorw("cannot-add-to-non-keyed-container", log.Fields{"name": name, "path": path, "fieldKey": field.Key})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500813
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400814 } else {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500815 log.Errorw("cannot-add-to-non-container-field", log.Fields{"name": name, "path": path})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400816 }
817
818 return nil
819}
820
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400821// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
822
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500823// MakeBranchFunction is a type for function references intented to create a branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400824type MakeBranchFunction func(*node) *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400825
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500826// MakeBranch creates a new branch for the provided transaction id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400827func (n *node) MakeBranch(txid string) *Branch {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500828 branchPoint := n.GetBranch(NONE).GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400829 branch := NewBranch(n, txid, branchPoint, true)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500830 n.SetBranch(txid, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400831 return branch
832}
833
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500834// DeleteBranch removes a branch with the specified id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400835func (n *node) DeleteBranch(txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500836 n.Lock()
837 defer n.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400838 delete(n.Branches, txid)
839}
840
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400841func (n *node) mergeChild(txid string, dryRun bool) func(Revision) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400842 f := func(rev Revision) Revision {
843 childBranch := rev.GetBranch()
844
845 if childBranch.Txid == txid {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400846 rev, _ = childBranch.Node.MergeBranch(txid, dryRun)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400847 }
848
849 return rev
850 }
851 return f
852}
853
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500854// MergeBranch will integrate the contents of a transaction branch within the latest branch of a given node
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400855func (n *node) MergeBranch(txid string, dryRun bool) (Revision, error) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500856 srcBranch := n.GetBranch(txid)
857 dstBranch := n.GetBranch(NONE)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400858
859 forkRev := srcBranch.Origin
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500860 srcRev := srcBranch.GetLatest()
861 dstRev := dstBranch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400862
863 rev, changes := Merge3Way(forkRev, srcRev, dstRev, n.mergeChild(txid, dryRun), dryRun)
864
865 if !dryRun {
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500866 if rev != nil {
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400867 rev.SetName(dstRev.GetName())
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500868 n.makeLatest(dstBranch, rev, changes)
869 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500870 n.DeleteBranch(txid)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400871 }
872
Stephane Barbariee16186c2018-09-11 10:46:34 -0400873 // TODO: return proper error when one occurs
874 return rev, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400875}
876
877// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diff utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
878
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400879//func (n *node) diff(hash1, hash2, txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400880// branch := n.Branches[txid]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500881// rev1 := branch.GetHash(hash1)
882// rev2 := branch.GetHash(hash2)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400883//
884// if rev1.GetHash() == rev2.GetHash() {
885// // empty patch
886// } else {
887// // translate data to json and generate patch
888// patch, err := jsonpatch.MakePatch(rev1.GetData(), rev2.GetData())
889// patch.
890// }
891//}
892
893// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tag utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
894
895// TODO: is tag mgmt used in the python implementation? Need to validate
896
897// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
898
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400899func (n *node) hasChildren(data interface{}) bool {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400900 for fieldName, field := range ChildrenFields(n.Type) {
901 _, fieldValue := GetAttributeValue(data, fieldName, 0)
902
903 if (field.IsContainer && fieldValue.Len() > 0) || !fieldValue.IsNil() {
904 log.Error("cannot update external children")
905 return true
906 }
907 }
908
909 return false
910}
911
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400912// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ node Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400913
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500914// CreateProxy returns a reference to a sub-tree of the data model
915func (n *node) CreateProxy(path string, exclusive bool) *Proxy {
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500916 return n.createProxy(path, path, n, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400917}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500918
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500919func (n *node) createProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400920 for strings.HasPrefix(path, "/") {
921 path = path[1:]
922 }
923 if path == "" {
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500924 return n.makeProxy(path, fullPath, parentNode, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400925 }
926
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500927 rev := n.GetBranch(NONE).GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400928 partition := strings.SplitN(path, "/", 2)
929 name := partition[0]
Stephane Barbarie126101e2018-10-11 16:18:48 -0400930 if len(partition) < 2 {
931 path = ""
932 } else {
933 path = partition[1]
934 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400935
936 field := ChildrenFields(n.Type)[name]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500937 if field.IsContainer {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400938 if path == "" {
khenaidoo21d51152019-02-01 13:48:37 -0500939 //log.Error("cannot proxy a container field")
Stephane Barbarieaa467942019-02-06 14:09:44 -0500940 newNode := n.MakeNode(reflect.New(field.ClassType.Elem()).Interface(), "")
941 return newNode.makeProxy(path, fullPath, parentNode, exclusive)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400942 } else if field.Key != "" {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400943 partition := strings.SplitN(path, "/", 2)
944 key := partition[0]
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400945 if len(partition) < 2 {
946 path = ""
947 } else {
948 path = partition[1]
949 }
950 keyValue := field.KeyFromStr(key)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400951 var children []Revision
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500952 children = make([]Revision, len(rev.GetChildren(name)))
953 copy(children, rev.GetChildren(name))
Stephane Barbarie933b09b2019-01-09 11:12:09 -0500954 if _, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
955 childNode := childRev.GetNode()
956 return childNode.createProxy(path, fullPath, n, exclusive)
957 }
Stephane Barbarie126101e2018-10-11 16:18:48 -0400958 } else {
959 log.Error("cannot index into container with no keys")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400960 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400961 } else {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500962 childRev := rev.GetChildren(name)[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400963 childNode := childRev.GetNode()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500964 return childNode.createProxy(path, fullPath, n, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400965 }
966
Stephane Barbarie933b09b2019-01-09 11:12:09 -0500967 log.Warnf("Cannot create proxy - latest rev:%s, all revs:%+v", rev.GetHash(), n.GetBranch(NONE).Revisions)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400968 return nil
969}
970
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500971func (n *node) makeProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500972 n.Lock()
973 defer n.Unlock()
Stephane Barbarie126101e2018-10-11 16:18:48 -0400974 r := &root{
975 node: n,
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500976 Callbacks: n.Root.GetCallbacks(),
977 NotificationCallbacks: n.Root.GetNotificationCallbacks(),
Stephane Barbarie126101e2018-10-11 16:18:48 -0400978 DirtyNodes: n.Root.DirtyNodes,
979 KvStore: n.Root.KvStore,
980 Loading: n.Root.Loading,
981 RevisionClass: n.Root.RevisionClass,
982 }
983
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400984 if n.Proxy == nil {
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500985 n.Proxy = NewProxy(r, n, parentNode, path, fullPath, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400986 } else {
987 if n.Proxy.Exclusive {
988 log.Error("node is already owned exclusively")
989 }
990 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500991
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400992 return n.Proxy
993}
994
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400995func (n *node) makeEventBus() *EventBus {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500996 n.Lock()
997 defer n.Unlock()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400998 if n.EventBus == nil {
999 n.EventBus = NewEventBus()
1000 }
1001 return n.EventBus
1002}
1003
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001004func (n *node) SetProxy(proxy *Proxy) {
1005 n.Lock()
1006 defer n.Unlock()
1007 n.Proxy = proxy
1008}
1009
1010func (n *node) GetProxy() *Proxy {
1011 n.Lock()
1012 defer n.Unlock()
1013 return n.Proxy
1014}
1015
1016func (n *node) GetBranch(key string) *Branch {
1017 n.Lock()
1018 defer n.Unlock()
Stephane Barbarie1039ec42019-02-04 10:43:16 -05001019
Stephane Barbarie1ab43272018-12-08 21:42:13 -05001020 if n.Branches != nil {
1021 if branch, exists := n.Branches[key]; exists {
1022 return branch
1023 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001024 }
1025 return nil
1026}
1027
1028func (n *node) SetBranch(key string, branch *Branch) {
1029 n.Lock()
1030 defer n.Unlock()
1031 n.Branches[key] = branch
1032}
1033
1034func (n *node) GetRoot() *root {
1035 n.Lock()
1036 defer n.Unlock()
1037 return n.Root
Stephane Barbarie06c4a742018-10-01 11:09:32 -04001038}