blob: 7e703ff754eacdd5fa7db4a24f38e254d910b7f9 [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 (
Stephane Barbarieef6650d2019-07-18 12:15:09 -040023 "context"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040024 "fmt"
25 "github.com/golang/protobuf/proto"
Scott Baker807addd2019-10-24 15:16:21 -070026 "github.com/opencord/voltha-lib-go/v2/pkg/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040027 "reflect"
28 "strings"
Stephane Barbariedc5022d2018-11-19 15:21:44 -050029 "sync"
Stephane Barbarie802aca42019-05-21 12:19:28 -040030 "time"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040031)
32
Stephane Barbariedc5022d2018-11-19 15:21:44 -050033// When a branch has no transaction id, everything gets stored in NONE
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040034const (
35 NONE string = "none"
36)
37
Stephane Barbariedc5022d2018-11-19 15:21:44 -050038// Node interface is an abstraction of the node data structure
Stephane Barbarie06c4a742018-10-01 11:09:32 -040039type Node interface {
40 MakeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple)
41
42 // CRUD functions
Stephane Barbarieef6650d2019-07-18 12:15:09 -040043 Add(ctx context.Context, path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision
Thomas Lee Se5a44012019-11-07 20:32:24 +053044 Get(ctx context.Context, path string, hash string, depth int, deep bool, txid string) (interface{}, error)
45 List(ctx context.Context, path string, hash string, depth int, deep bool, txid string) (interface{}, error)
Stephane Barbarieef6650d2019-07-18 12:15:09 -040046 Update(ctx context.Context, path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision
47 Remove(ctx context.Context, path string, txid string, makeBranch MakeBranchFunction) Revision
Thomas Lee Se5a44012019-11-07 20:32:24 +053048 CreateProxy(ctx context.Context, path string, exclusive bool) (*Proxy, error)
Stephane Barbarieef6650d2019-07-18 12:15:09 -040049
50 GetProxy() *Proxy
Stephane Barbarie06c4a742018-10-01 11:09:32 -040051
52 MakeBranch(txid string) *Branch
53 DeleteBranch(txid string)
54 MergeBranch(txid string, dryRun bool) (Revision, error)
55
56 MakeTxBranch() string
57 DeleteTxBranch(txid string)
58 FoldTxBranch(txid string)
Stephane Barbarie06c4a742018-10-01 11:09:32 -040059}
60
61type node struct {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040062 mutex sync.RWMutex
63 Root *root
64 Type interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040065 Branches map[string]*Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -040066 Tags map[string]Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040067 Proxy *Proxy
68 EventBus *EventBus
69 AutoPrune bool
70}
71
Stephane Barbariedc5022d2018-11-19 15:21:44 -050072// ChangeTuple holds details of modifications made to a revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -040073type ChangeTuple struct {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -040074 Type CallbackType
75 PreviousData interface{}
76 LatestData interface{}
Stephane Barbarie694e2b92018-09-07 12:17:36 -040077}
78
Stephane Barbariedc5022d2018-11-19 15:21:44 -050079// NewNode creates a new instance of the node data structure
Stephane Barbarie06c4a742018-10-01 11:09:32 -040080func NewNode(root *root, initialData interface{}, autoPrune bool, txid string) *node {
81 n := &node{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040082
Stephane Barbarie126101e2018-10-11 16:18:48 -040083 n.Root = root
Stephane Barbarieec0919b2018-09-05 14:14:29 -040084 n.Branches = make(map[string]*Branch)
85 n.Tags = make(map[string]Revision)
86 n.Proxy = nil
87 n.EventBus = nil
88 n.AutoPrune = autoPrune
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040089
90 if IsProtoMessage(initialData) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -040091 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040092 dataCopy := proto.Clone(initialData.(proto.Message))
Stephane Barbarieec0919b2018-09-05 14:14:29 -040093 n.initialize(dataCopy, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040094 } else if reflect.ValueOf(initialData).IsValid() {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050095 // FIXME: this block does not reflect the original implementation
96 // it should be checking if the provided initial_data is already a type!??!
97 // it should be checked before IsProtoMessage
Stephane Barbarieec0919b2018-09-05 14:14:29 -040098 n.Type = reflect.ValueOf(initialData).Interface()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040099 } else {
100 // not implemented error
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400101 log.Errorf("cannot process initial data - %+v", initialData)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400102 }
103
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400104 return n
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400105}
106
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500107// MakeNode creates a new node in the tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400108func (n *node) MakeNode(data interface{}, txid string) *node {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400109 return NewNode(n.Root, data, true, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400110}
111
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500112// MakeRevision create a new revision of the node in the tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400113func (n *node) MakeRevision(branch *Branch, data interface{}, children map[string][]Revision) Revision {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500114 return n.GetRoot().MakeRevision(branch, data, children)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400115}
116
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500117// makeLatest will mark the revision of a node as being the latest
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400118func (n *node) makeLatest(branch *Branch, revision Revision, changeAnnouncement []ChangeTuple) {
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() != "" {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400130 log.Debugw("saving-latest-data", log.Fields{"hash": revision.GetHash(), "data": revision.GetData()})
131 // Tag a timestamp to that revision
132 revision.SetLastUpdate()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400133 GetRevCache().Set(revision.GetName(), revision)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400134 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500135 branch.SetLatest(revision)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400136 }
137
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400138 // Delete the previous revision if anything has changed
139 if previous != "" && previous != branch.GetLatest().GetHash() {
140 branch.DeleteRevision(previous)
141 }
142
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400143 if changeAnnouncement != nil && branch.Txid == "" {
Stephane Barbarie260a5632019-02-26 16:12:49 -0500144 if n.Proxy != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400145 for _, change := range changeAnnouncement {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400146 log.Debugw("adding-callback",
147 log.Fields{
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400148 "callbacks": n.GetProxy().getCallbacks(change.Type),
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400149 "type": change.Type,
150 "previousData": change.PreviousData,
151 "latestData": change.LatestData,
152 })
Stephane Barbarie260a5632019-02-26 16:12:49 -0500153 n.Root.AddCallback(
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400154 n.GetProxy().InvokeCallbacks,
Stephane Barbarie126101e2018-10-11 16:18:48 -0400155 change.Type,
156 true,
157 change.PreviousData,
158 change.LatestData)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400159 }
160 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400161 }
162}
163
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500164// Latest returns the latest revision of node with or without the transaction id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400165func (n *node) Latest(txid ...string) Revision {
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400166 var branch *Branch
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400167
168 if len(txid) > 0 && txid[0] != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500169 if branch = n.GetBranch(txid[0]); branch != nil {
170 return branch.GetLatest()
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400171 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500172 } else if branch = n.GetBranch(NONE); branch != nil {
173 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400174 }
175 return nil
176}
177
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500178// initialize prepares the content of a node along with its possible ramifications
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400179func (n *node) initialize(data interface{}, txid string) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500180 children := make(map[string][]Revision)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400181 for fieldName, field := range ChildrenFields(n.Type) {
182 _, fieldValue := GetAttributeValue(data, fieldName, 0)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400183
184 if fieldValue.IsValid() {
185 if field.IsContainer {
186 if field.Key != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400187 for i := 0; i < fieldValue.Len(); i++ {
188 v := fieldValue.Index(i)
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400189
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500190 if rev := n.MakeNode(v.Interface(), txid).Latest(txid); rev != nil {
191 children[fieldName] = append(children[fieldName], rev)
192 }
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400193
Stephane Barbarie1e28f3e2019-02-08 15:45:20 -0500194 // TODO: The following logic was ported from v1.0. Need to verify if it is required
195 //var keysSeen []string
196 //_, key := GetAttributeValue(v.Interface(), field.Key, 0)
197 //for _, k := range keysSeen {
198 // if k == key.String() {
199 // //log.Errorf("duplicate key - %s", k)
200 // }
201 //}
202 //keysSeen = append(keysSeen, key.String())
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400203 }
204
205 } else {
206 for i := 0; i < fieldValue.Len(); i++ {
207 v := fieldValue.Index(i)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500208 if newNodeRev := n.MakeNode(v.Interface(), txid).Latest(); newNodeRev != nil {
209 children[fieldName] = append(children[fieldName], newNodeRev)
210 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400211 }
212 }
213 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500214 if newNodeRev := n.MakeNode(fieldValue.Interface(), txid).Latest(); newNodeRev != nil {
215 children[fieldName] = append(children[fieldName], newNodeRev)
216 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400217 }
218 } else {
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400219 log.Errorf("field is invalid - %+v", fieldValue)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400220 }
221 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500222
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400223 branch := NewBranch(n, "", nil, n.AutoPrune)
224 rev := n.MakeRevision(branch, data, children)
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400225 n.makeLatest(branch, rev, nil)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400226
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400227 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500228 n.SetBranch(NONE, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400229 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500230 n.SetBranch(txid, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400231 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400232}
233
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500234// findRevByKey retrieves a specific revision from a node tree
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400235func (n *node) findRevByKey(revs []Revision, keyName string, value interface{}) (int, Revision) {
236 for i, rev := range revs {
237 dataValue := reflect.ValueOf(rev.GetData())
238 dataStruct := GetAttributeStructure(rev.GetData(), keyName, 0)
239
240 fieldValue := dataValue.Elem().FieldByName(dataStruct.Name)
241
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400242 a := fmt.Sprintf("%s", fieldValue.Interface())
243 b := fmt.Sprintf("%s", value)
244 if a == b {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500245 return i, revs[i]
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400246 }
247 }
248
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400249 return -1, nil
250}
251
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500252// Get retrieves the data from a node tree that resides at the specified path
Thomas Lee Se5a44012019-11-07 20:32:24 +0530253func (n *node) List(ctx context.Context, path string, hash string, depth int, deep bool, txid string) (interface{}, error) {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400254 n.mutex.Lock()
255 defer n.mutex.Unlock()
256
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500257 log.Debugw("node-list-request", log.Fields{"path": path, "hash": hash, "depth": depth, "deep": deep, "txid": txid})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500258 if deep {
259 depth = -1
260 }
261
262 for strings.HasPrefix(path, "/") {
263 path = path[1:]
264 }
265
266 var branch *Branch
267 var rev Revision
268
269 if branch = n.GetBranch(txid); txid == "" || branch == nil {
270 branch = n.GetBranch(NONE)
271 }
272
273 if hash != "" {
274 rev = branch.GetRevision(hash)
275 } else {
276 rev = branch.GetLatest()
277 }
278
279 var result interface{}
280 var prList []interface{}
Thomas Lee Se5a44012019-11-07 20:32:24 +0530281
282 pr, err := rev.LoadFromPersistence(ctx, path, txid, nil)
283 if err != nil {
284 log.Errorf("failed-to-load-from-persistence")
285 return nil, err
286 }
287 if pr != nil {
Stephane Barbarieaa467942019-02-06 14:09:44 -0500288 for _, revEntry := range pr {
289 prList = append(prList, revEntry.GetData())
290 }
291 result = prList
292 }
Thomas Lee Se5a44012019-11-07 20:32:24 +0530293 return result, nil
Stephane Barbarieaa467942019-02-06 14:09:44 -0500294}
295
296// Get retrieves the data from a node tree that resides at the specified path
Thomas Lee Se5a44012019-11-07 20:32:24 +0530297func (n *node) Get(ctx context.Context, path string, hash string, depth int, reconcile bool, txid string) (interface{}, error) {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400298 n.mutex.Lock()
299 defer n.mutex.Unlock()
300
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400301 log.Debugw("node-get-request", log.Fields{"path": path, "hash": hash, "depth": depth, "reconcile": reconcile, "txid": txid})
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400302
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400303 for strings.HasPrefix(path, "/") {
304 path = path[1:]
305 }
306
307 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400308 var rev Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400309
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500310 if branch = n.GetBranch(txid); txid == "" || branch == nil {
311 branch = n.GetBranch(NONE)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400312 }
313
314 if hash != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500315 rev = branch.GetRevision(hash)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400316 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500317 rev = branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400318 }
319
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500320 var result interface{}
Stephane Barbarie260a5632019-02-26 16:12:49 -0500321
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400322 // If there is no request to reconcile, try to get it from memory
Stephane Barbarie260a5632019-02-26 16:12:49 -0500323 if !reconcile {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400324 // Try to find an entry matching the path value from one of these sources
325 // 1. Start with the cache which stores revisions by watch names
326 // 2. Then look in the revision tree, especially if it's a sub-path such as /devices/1234/flows
Stephane Barbarie802aca42019-05-21 12:19:28 -0400327 // 3. Move on to the KV store if that path cannot be found or if the entry has expired
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400328 if entry, exists := GetRevCache().Get(path); exists && entry.(Revision) != nil {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400329 entryAge := time.Now().Sub(entry.(Revision).GetLastUpdate()).Nanoseconds() / int64(time.Millisecond)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400330 if entryAge < DataRefreshPeriod {
Stephane Barbariec92d1072019-06-07 16:21:49 -0400331 log.Debugw("using-cache-entry", log.Fields{
332 "path": path,
333 "hash": hash,
334 "age": entryAge,
335 })
Thomas Lee Se5a44012019-11-07 20:32:24 +0530336 return proto.Clone(entry.(Revision).GetData().(proto.Message)), nil
Stephane Barbarie802aca42019-05-21 12:19:28 -0400337 } else {
338 log.Debugw("cache-entry-expired", log.Fields{"path": path, "hash": hash, "age": entryAge})
339 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400340 } else if result = n.getPath(ctx, rev.GetBranch().GetLatest(), path, depth); result != nil && reflect.ValueOf(result).IsValid() && !reflect.ValueOf(result).IsNil() {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400341 log.Debugw("using-rev-tree-entry", log.Fields{"path": path, "hash": hash, "depth": depth, "reconcile": reconcile, "txid": txid})
Thomas Lee Se5a44012019-11-07 20:32:24 +0530342 return result, nil
Stephane Barbarie802aca42019-05-21 12:19:28 -0400343 } else {
344 log.Debugw("not-using-cache-entry", log.Fields{
345 "path": path,
346 "hash": hash, "depth": depth,
347 "reconcile": reconcile,
348 "txid": txid,
349 })
Stephane Barbarie260a5632019-02-26 16:12:49 -0500350 }
Stephane Barbarie802aca42019-05-21 12:19:28 -0400351 } else {
352 log.Debugw("reconcile-requested", log.Fields{
353 "path": path,
354 "hash": hash,
355 "reconcile": reconcile,
356 })
Stephane Barbarie260a5632019-02-26 16:12:49 -0500357 }
358
Stephane Barbarie802aca42019-05-21 12:19:28 -0400359 // If we got to this point, we are either trying to reconcile with the db
Stephane Barbarie260a5632019-02-26 16:12:49 -0500360 // or we simply failed at getting information from memory
361 if n.Root.KvStore != nil {
Thomas Lee Se5a44012019-11-07 20:32:24 +0530362 if pr, err := rev.LoadFromPersistence(ctx, path, txid, nil); err != nil {
363 log.Errorf("failed-to-load-from-persistence")
364 return nil, err
365 } else if len(pr) > 0 {
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500366 // Did we receive a single or multiple revisions?
367 if len(pr) > 1 {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400368 var revs []interface{}
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500369 for _, revEntry := range pr {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400370 revs = append(revs, revEntry.GetData())
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500371 }
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400372 result = revs
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500373 } else {
374 result = pr[0].GetData()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500375 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500376 }
377 }
Thomas Lee Se5a44012019-11-07 20:32:24 +0530378 return result, nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400379}
380
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400381//getPath traverses the specified path and retrieves the data associated to it
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400382func (n *node) getPath(ctx context.Context, rev Revision, path string, depth int) interface{} {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400383 if path == "" {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400384 return n.getData(rev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400385 }
386
387 partition := strings.SplitN(path, "/", 2)
388 name := partition[0]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400389
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400390 if len(partition) < 2 {
391 path = ""
392 } else {
393 path = partition[1]
394 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400395
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400396 names := ChildrenFields(n.Type)
397 field := names[name]
398
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500399 if field != nil && field.IsContainer {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500400 children := make([]Revision, len(rev.GetChildren(name)))
401 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500402
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400403 if field.Key != "" {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400404 if path != "" {
405 partition = strings.SplitN(path, "/", 2)
406 key := partition[0]
407 path = ""
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400408 keyValue := field.KeyFromStr(key)
409 if _, childRev := n.findRevByKey(children, field.Key, keyValue); childRev == nil {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400410 return nil
411 } else {
412 childNode := childRev.GetNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400413 return childNode.getPath(ctx, childRev, path, depth)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400414 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400415 } else {
416 var response []interface{}
417 for _, childRev := range children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400418 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400419 value := childNode.getData(childRev, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400420 response = append(response, value)
421 }
422 return response
423 }
424 } else {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400425 var response []interface{}
426 if path != "" {
427 // TODO: raise error
428 return response
429 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500430 for _, childRev := range children {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400431 childNode := childRev.GetNode()
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400432 value := childNode.getData(childRev, depth)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400433 response = append(response, value)
434 }
435 return response
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400436 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400437 } else if children := rev.GetChildren(name); children != nil && len(children) > 0 {
438 childRev := children[0]
439 childNode := childRev.GetNode()
440 return childNode.getPath(ctx, childRev, path, depth)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400441 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500442
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400443 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400444}
445
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500446// getData retrieves the data from a node revision
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400447func (n *node) getData(rev Revision, depth int) interface{} {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500448 msg := rev.GetBranch().GetLatest().Get(depth)
Stephane Barbariea188d942018-10-16 16:43:04 -0400449 var modifiedMsg interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400450
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500451 if n.GetProxy() != nil {
Stephane Barbarieaa467942019-02-06 14:09:44 -0500452 log.Debugw("invoking-get-callbacks", log.Fields{"data": msg})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500453 if modifiedMsg = n.GetProxy().InvokeCallbacks(GET, false, msg); modifiedMsg != nil {
Stephane Barbariea188d942018-10-16 16:43:04 -0400454 msg = modifiedMsg
455 }
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400456
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400457 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500458
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400459 return msg
460}
461
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500462// Update changes the content of a node at the specified path with the provided data
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400463func (n *node) Update(ctx context.Context, path string, data interface{}, strict bool, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400464 n.mutex.Lock()
465 defer n.mutex.Unlock()
466
khenaidoob3244212019-08-27 14:32:27 -0400467 log.Debugw("node-update-request", log.Fields{"path": path, "strict": strict, "txid": txid})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500468
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400469 for strings.HasPrefix(path, "/") {
470 path = path[1:]
471 }
472
473 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400474 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500475 branch = n.GetBranch(NONE)
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500476 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400477 branch = makeBranch(n)
478 }
479
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500480 if branch.GetLatest() != nil {
481 log.Debugf("Branch data : %+v, Passed data: %+v", branch.GetLatest().GetData(), data)
482 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400483 if path == "" {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400484 return n.doUpdate(ctx, branch, data, strict)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400485 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400486
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500487 rev := branch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400488
489 partition := strings.SplitN(path, "/", 2)
490 name := partition[0]
491
492 if len(partition) < 2 {
493 path = ""
494 } else {
495 path = partition[1]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400496 }
497
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400498 field := ChildrenFields(n.Type)[name]
499 var children []Revision
500
Stephane Barbarieaa467942019-02-06 14:09:44 -0500501 if field == nil {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400502 return n.doUpdate(ctx, branch, data, strict)
Stephane Barbarieaa467942019-02-06 14:09:44 -0500503 }
504
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400505 if field.IsContainer {
506 if path == "" {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400507 log.Errorf("cannot update a list")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400508 } else if field.Key != "" {
509 partition := strings.SplitN(path, "/", 2)
510 key := partition[0]
511 if len(partition) < 2 {
512 path = ""
513 } else {
514 path = partition[1]
515 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400516 keyValue := field.KeyFromStr(key)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500517
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500518 children = make([]Revision, len(rev.GetChildren(name)))
519 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500520
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400521 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400522
523 if childRev == nil {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400524 log.Debugw("child-revision-is-nil", log.Fields{"key": keyValue})
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400525 return branch.GetLatest()
526 }
527
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400528 childNode := childRev.GetNode()
Stephane Barbariea188d942018-10-16 16:43:04 -0400529
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500530 // Save proxy in child node to ensure callbacks are called later on
Stephane Barbaried62ac4e2019-02-05 14:08:38 -0500531 // only assign in cases of non sub-folder proxies, i.e. "/"
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400532 if childNode.Proxy == nil && n.Proxy != nil && n.GetProxy().getFullPath() == "" {
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500533 childNode.Proxy = n.Proxy
534 }
535
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400536 newChildRev := childNode.Update(ctx, path, data, strict, txid, makeBranch)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400537
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400538 if newChildRev.GetHash() == childRev.GetHash() {
539 if newChildRev != childRev {
540 log.Debug("clear-hash - %s %+v", newChildRev.GetHash(), newChildRev)
541 newChildRev.ClearHash()
542 }
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400543 log.Debugw("child-revisions-have-matching-hash", log.Fields{"hash": childRev.GetHash(), "key": keyValue})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500544 return branch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400545 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400546
547 _, newKey := GetAttributeValue(newChildRev.GetData(), field.Key, 0)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500548
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400549 _newKeyType := fmt.Sprintf("%s", newKey)
550 _keyValueType := fmt.Sprintf("%s", keyValue)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500551
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400552 if _newKeyType != _keyValueType {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400553 log.Errorf("cannot change key field")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400554 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500555
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500556 // Prefix the hash value with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400557 newChildRev.SetName(name + "/" + _keyValueType)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400558
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400559 branch.LatestLock.Lock()
560 defer branch.LatestLock.Unlock()
561
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400562 if idx >= 0 {
563 children[idx] = newChildRev
564 } else {
565 children = append(children, newChildRev)
566 }
567
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400568 updatedRev := rev.UpdateChildren(ctx, name, children, branch)
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500569
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500570 n.makeLatest(branch, updatedRev, nil)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400571 updatedRev.ChildDrop(name, childRev.GetHash())
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500572
Stephane Barbariea188d942018-10-16 16:43:04 -0400573 return newChildRev
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500574
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400575 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400576 log.Errorf("cannot index into container with no keys")
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400577 }
578 } else {
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500579 childRev := rev.GetChildren(name)[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400580 childNode := childRev.GetNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400581 newChildRev := childNode.Update(ctx, path, data, strict, txid, makeBranch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400582
583 branch.LatestLock.Lock()
584 defer branch.LatestLock.Unlock()
585
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400586 updatedRev := rev.UpdateChildren(ctx, name, []Revision{newChildRev}, branch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500587 n.makeLatest(branch, updatedRev, nil)
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500588
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400589 updatedRev.ChildDrop(name, childRev.GetHash())
590
Stephane Barbariea188d942018-10-16 16:43:04 -0400591 return newChildRev
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400592 }
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500593
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400594 return nil
595}
596
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400597func (n *node) doUpdate(ctx context.Context, branch *Branch, data interface{}, strict bool) Revision {
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400598 log.Debugw("comparing-types", log.Fields{"expected": reflect.ValueOf(n.Type).Type(), "actual": reflect.TypeOf(data)})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400599
600 if reflect.TypeOf(data) != reflect.ValueOf(n.Type).Type() {
601 // TODO raise error
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400602 log.Errorw("types-do-not-match: %+v", log.Fields{"actual": reflect.TypeOf(data), "expected": n.Type})
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400603 return nil
604 }
605
606 // TODO: validate that this actually works
607 //if n.hasChildren(data) {
608 // return nil
609 //}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400610
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_UPDATE Callbacks")
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500613 n.GetProxy().InvokeCallbacks(PRE_UPDATE, false, branch.GetLatest(), data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400614 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500615
616 if branch.GetLatest().GetData().(proto.Message).String() != data.(proto.Message).String() {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400617 if strict {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400618 // TODO: checkAccessViolations(data, Branch.GetLatest.data)
Stephane Barbarie8c48b5c2018-10-02 09:45:17 -0400619 log.Debugf("checking access violations")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400620 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500621
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400622 rev := branch.GetLatest().UpdateData(ctx, data, branch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500623 changes := []ChangeTuple{{POST_UPDATE, branch.GetLatest().GetData(), rev.GetData()}}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500624 n.makeLatest(branch, rev, changes)
625
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400626 return rev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400627 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500628 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400629}
630
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500631// Add inserts a new node at the specified path with the provided data
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400632func (n *node) Add(ctx context.Context, path string, data interface{}, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400633 n.mutex.Lock()
634 defer n.mutex.Unlock()
635
khenaidoob3244212019-08-27 14:32:27 -0400636 log.Debugw("node-add-request", log.Fields{"path": path, "txid": txid})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500637
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400638 for strings.HasPrefix(path, "/") {
639 path = path[1:]
640 }
641 if path == "" {
642 // TODO raise error
Stephane Barbarie126101e2018-10-11 16:18:48 -0400643 log.Errorf("cannot add for non-container mode")
Stephane Barbarieaa467942019-02-06 14:09:44 -0500644 return nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400645 }
646
647 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400648 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500649 branch = n.GetBranch(NONE)
650 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400651 branch = makeBranch(n)
652 }
653
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500654 rev := branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400655
656 partition := strings.SplitN(path, "/", 2)
657 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400658
659 if len(partition) < 2 {
660 path = ""
661 } else {
662 path = partition[1]
663 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400664
665 field := ChildrenFields(n.Type)[name]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500666
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400667 var children []Revision
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400668
669 if field.IsContainer {
670 if path == "" {
671 if field.Key != "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500672 if n.GetProxy() != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400673 log.Debug("invoking proxy PRE_ADD Callbacks")
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500674 n.GetProxy().InvokeCallbacks(PRE_ADD, false, data)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400675 }
676
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500677 children = make([]Revision, len(rev.GetChildren(name)))
678 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500679
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400680 _, key := GetAttributeValue(data, field.Key, 0)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500681
Stephane Barbarie126101e2018-10-11 16:18:48 -0400682 if _, exists := n.findRevByKey(children, field.Key, key.String()); exists != nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400683 // TODO raise error
Stephane Barbarie260a5632019-02-26 16:12:49 -0500684 log.Warnw("duplicate-key-found", log.Fields{"key": key.String()})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500685 return exists
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400686 }
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500687 childRev := n.MakeNode(data, "").Latest()
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500688
689 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400690 childRev.SetName(name + "/" + key.String())
Stephane Barbariee0a4c792019-01-16 11:26:29 -0500691
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400692 branch.LatestLock.Lock()
693 defer branch.LatestLock.Unlock()
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500694
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500695 children = append(children, childRev)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400696
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400697 updatedRev := rev.UpdateChildren(ctx, name, children, branch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400698 changes := []ChangeTuple{{POST_ADD, nil, childRev.GetData()}}
699 childRev.SetupWatch(childRev.GetName())
700
701 n.makeLatest(branch, updatedRev, changes)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500702
703 return childRev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400704 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500705 log.Errorf("cannot add to non-keyed container")
706
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400707 } else if field.Key != "" {
708 partition := strings.SplitN(path, "/", 2)
709 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400710 if len(partition) < 2 {
711 path = ""
712 } else {
713 path = partition[1]
714 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400715 keyValue := field.KeyFromStr(key)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500716
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500717 children = make([]Revision, len(rev.GetChildren(name)))
718 copy(children, rev.GetChildren(name))
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500719
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400720 idx, childRev := n.findRevByKey(children, field.Key, keyValue)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500721
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400722 if childRev == nil {
723 return branch.GetLatest()
724 }
725
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400726 childNode := childRev.GetNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400727 newChildRev := childNode.Add(ctx, path, data, txid, makeBranch)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500728
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500729 // Prefix the hash with the data type (e.g. devices, logical_devices, adapters)
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400730 newChildRev.SetName(name + "/" + keyValue.(string))
731
732 branch.LatestLock.Lock()
733 defer branch.LatestLock.Unlock()
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500734
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400735 if idx >= 0 {
736 children[idx] = newChildRev
737 } else {
738 children = append(children, newChildRev)
739 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500740
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400741 updatedRev := rev.UpdateChildren(ctx, name, children, branch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400742 n.makeLatest(branch, updatedRev, nil)
743
744 updatedRev.ChildDrop(name, childRev.GetHash())
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500745
746 return newChildRev
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400747 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400748 log.Errorf("cannot add to non-keyed container")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400749 }
750 } else {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400751 log.Errorf("cannot add to non-container field")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400752 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500753
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400754 return nil
755}
756
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500757// Remove eliminates a node at the specified path
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400758func (n *node) Remove(ctx context.Context, path string, txid string, makeBranch MakeBranchFunction) Revision {
Stephane Barbarie802aca42019-05-21 12:19:28 -0400759 n.mutex.Lock()
760 defer n.mutex.Unlock()
761
Stephane Barbarie11b88e72019-02-07 12:28:29 -0500762 log.Debugw("node-remove-request", log.Fields{"path": path, "txid": txid, "makeBranch": makeBranch})
Stephane Barbarieaa467942019-02-06 14:09:44 -0500763
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400764 for strings.HasPrefix(path, "/") {
765 path = path[1:]
766 }
767 if path == "" {
768 // TODO raise error
Stephane Barbarie126101e2018-10-11 16:18:48 -0400769 log.Errorf("cannot remove for non-container mode")
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400770 }
771 var branch *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400772 if txid == "" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500773 branch = n.GetBranch(NONE)
774 } else if branch = n.GetBranch(txid); branch == nil {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400775 branch = makeBranch(n)
776 }
777
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500778 rev := branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400779
780 partition := strings.SplitN(path, "/", 2)
781 name := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400782 if len(partition) < 2 {
783 path = ""
784 } else {
785 path = partition[1]
786 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400787
788 field := ChildrenFields(n.Type)[name]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400789 var children []Revision
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400790 postAnnouncement := []ChangeTuple{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400791
792 if field.IsContainer {
793 if path == "" {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500794 log.Errorw("cannot-remove-without-key", log.Fields{"name": name, "key": path})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400795 } else if field.Key != "" {
796 partition := strings.SplitN(path, "/", 2)
797 key := partition[0]
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400798 if len(partition) < 2 {
799 path = ""
800 } else {
801 path = partition[1]
802 }
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500803
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400804 keyValue := field.KeyFromStr(key)
Stephane Barbarie3cb01222019-01-16 17:15:56 -0500805 children = make([]Revision, len(rev.GetChildren(name)))
806 copy(children, rev.GetChildren(name))
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500807
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400808 if path != "" {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400809 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil {
810 childNode := childRev.GetNode()
811 if childNode.Proxy == nil {
812 childNode.Proxy = n.Proxy
813 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400814 newChildRev := childNode.Remove(ctx, path, txid, makeBranch)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400815
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400816 branch.LatestLock.Lock()
817 defer branch.LatestLock.Unlock()
818
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400819 if idx >= 0 {
820 children[idx] = newChildRev
821 } else {
822 children = append(children, newChildRev)
823 }
824
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400825 rev.SetChildren(name, children)
826 branch.GetLatest().Drop(txid, false)
827 n.makeLatest(branch, rev, nil)
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500828 }
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400829 return branch.GetLatest()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400830 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500831
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400832 if idx, childRev := n.findRevByKey(children, field.Key, keyValue); childRev != nil && idx >= 0 {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500833 if n.GetProxy() != nil {
834 data := childRev.GetData()
835 n.GetProxy().InvokeCallbacks(PRE_REMOVE, false, data)
836 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, data, nil})
837 } else {
838 postAnnouncement = append(postAnnouncement, ChangeTuple{POST_REMOVE, childRev.GetData(), nil})
839 }
840
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400841 childRev.StorageDrop(txid, true)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400842 GetRevCache().Delete(childRev.GetName())
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400843
844 branch.LatestLock.Lock()
845 defer branch.LatestLock.Unlock()
846
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500847 children = append(children[:idx], children[idx+1:]...)
848 rev.SetChildren(name, children)
849
850 branch.GetLatest().Drop(txid, false)
851 n.makeLatest(branch, rev, postAnnouncement)
852
853 return rev
854 } else {
855 log.Errorw("failed-to-find-revision", log.Fields{"name": name, "key": keyValue.(string)})
856 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400857 }
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500858 log.Errorw("cannot-add-to-non-keyed-container", log.Fields{"name": name, "path": path, "fieldKey": field.Key})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500859
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400860 } else {
Stephane Barbarieb0c79892019-02-13 11:29:59 -0500861 log.Errorw("cannot-add-to-non-container-field", log.Fields{"name": name, "path": path})
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400862 }
863
864 return nil
865}
866
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400867// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Branching ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
868
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500869// MakeBranchFunction is a type for function references intented to create a branch
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400870type MakeBranchFunction func(*node) *Branch
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400871
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500872// MakeBranch creates a new branch for the provided transaction id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400873func (n *node) MakeBranch(txid string) *Branch {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500874 branchPoint := n.GetBranch(NONE).GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400875 branch := NewBranch(n, txid, branchPoint, true)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500876 n.SetBranch(txid, branch)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400877 return branch
878}
879
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500880// DeleteBranch removes a branch with the specified id
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400881func (n *node) DeleteBranch(txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400882 delete(n.Branches, txid)
883}
884
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400885func (n *node) mergeChild(txid string, dryRun bool) func(Revision) Revision {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400886 f := func(rev Revision) Revision {
887 childBranch := rev.GetBranch()
888
889 if childBranch.Txid == txid {
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400890 rev, _ = childBranch.Node.MergeBranch(txid, dryRun)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400891 }
892
893 return rev
894 }
895 return f
896}
897
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500898// MergeBranch will integrate the contents of a transaction branch within the latest branch of a given node
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400899func (n *node) MergeBranch(txid string, dryRun bool) (Revision, error) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500900 srcBranch := n.GetBranch(txid)
901 dstBranch := n.GetBranch(NONE)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400902
903 forkRev := srcBranch.Origin
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500904 srcRev := srcBranch.GetLatest()
905 dstRev := dstBranch.GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400906
907 rev, changes := Merge3Way(forkRev, srcRev, dstRev, n.mergeChild(txid, dryRun), dryRun)
908
909 if !dryRun {
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500910 if rev != nil {
Stephane Barbarief7fc1782019-03-28 22:33:41 -0400911 rev.SetName(dstRev.GetName())
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500912 n.makeLatest(dstBranch, rev, changes)
913 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500914 n.DeleteBranch(txid)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400915 }
916
Stephane Barbariee16186c2018-09-11 10:46:34 -0400917 // TODO: return proper error when one occurs
918 return rev, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400919}
920
921// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Diff utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
922
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400923//func (n *node) diff(hash1, hash2, txid string) {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400924// branch := n.Branches[txid]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500925// rev1 := branch.GetHash(hash1)
926// rev2 := branch.GetHash(hash2)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400927//
928// if rev1.GetHash() == rev2.GetHash() {
929// // empty patch
930// } else {
931// // translate data to json and generate patch
932// patch, err := jsonpatch.MakePatch(rev1.GetData(), rev2.GetData())
933// patch.
934// }
935//}
936
937// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tag utility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
938
939// TODO: is tag mgmt used in the python implementation? Need to validate
940
941// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Internals ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
942
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400943func (n *node) hasChildren(data interface{}) bool {
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400944 for fieldName, field := range ChildrenFields(n.Type) {
945 _, fieldValue := GetAttributeValue(data, fieldName, 0)
946
947 if (field.IsContainer && fieldValue.Len() > 0) || !fieldValue.IsNil() {
948 log.Error("cannot update external children")
949 return true
950 }
951 }
952
953 return false
954}
955
Stephane Barbarie06c4a742018-10-01 11:09:32 -0400956// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ node Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400957
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500958// CreateProxy returns a reference to a sub-tree of the data model
Thomas Lee Se5a44012019-11-07 20:32:24 +0530959func (n *node) CreateProxy(ctx context.Context, path string, exclusive bool) (*Proxy, error) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400960 return n.createProxy(ctx, path, path, n, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400961}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500962
Thomas Lee Se5a44012019-11-07 20:32:24 +0530963func (n *node) createProxy(ctx context.Context, path string, fullPath string, parentNode *node, exclusive bool) (*Proxy, error) {
Stephane Barbariec92d1072019-06-07 16:21:49 -0400964 log.Debugw("node-create-proxy", log.Fields{
965 "node-type": reflect.ValueOf(n.Type).Type(),
966 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
967 "path": path,
968 "fullPath": fullPath,
969 })
970
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400971 for strings.HasPrefix(path, "/") {
972 path = path[1:]
973 }
974 if path == "" {
Thomas Lee Se5a44012019-11-07 20:32:24 +0530975 return n.makeProxy(path, fullPath, parentNode, exclusive), nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400976 }
977
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500978 rev := n.GetBranch(NONE).GetLatest()
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400979 partition := strings.SplitN(path, "/", 2)
980 name := partition[0]
Stephane Barbariec92d1072019-06-07 16:21:49 -0400981 var nodeType interface{}
Stephane Barbarie126101e2018-10-11 16:18:48 -0400982 if len(partition) < 2 {
983 path = ""
Stephane Barbariec92d1072019-06-07 16:21:49 -0400984 nodeType = n.Type
Stephane Barbarie126101e2018-10-11 16:18:48 -0400985 } else {
986 path = partition[1]
Stephane Barbariec92d1072019-06-07 16:21:49 -0400987 nodeType = parentNode.Type
Stephane Barbarie126101e2018-10-11 16:18:48 -0400988 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -0400989
Stephane Barbariec92d1072019-06-07 16:21:49 -0400990 field := ChildrenFields(nodeType)[name]
991
992 if field != nil {
993 if field.IsContainer {
994 log.Debugw("container-field", log.Fields{
995 "node-type": reflect.ValueOf(n.Type).Type(),
996 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
997 "path": path,
998 "name": name,
999 })
1000 if path == "" {
1001 log.Debugw("folder-proxy", log.Fields{
1002 "node-type": reflect.ValueOf(n.Type).Type(),
1003 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1004 "fullPath": fullPath,
1005 "name": name,
1006 })
1007 newNode := n.MakeNode(reflect.New(field.ClassType.Elem()).Interface(), "")
Thomas Lee Se5a44012019-11-07 20:32:24 +05301008 return newNode.makeProxy(path, fullPath, parentNode, exclusive), nil
Stephane Barbariec92d1072019-06-07 16:21:49 -04001009 } else if field.Key != "" {
1010 log.Debugw("key-proxy", log.Fields{
1011 "node-type": reflect.ValueOf(n.Type).Type(),
1012 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1013 "fullPath": fullPath,
1014 "name": name,
1015 })
1016 partition := strings.SplitN(path, "/", 2)
1017 key := partition[0]
1018 if len(partition) < 2 {
1019 path = ""
1020 } else {
1021 path = partition[1]
1022 }
1023 keyValue := field.KeyFromStr(key)
1024 var children []Revision
1025 children = make([]Revision, len(rev.GetChildren(name)))
1026 copy(children, rev.GetChildren(name))
1027
Stephane Barbariec92d1072019-06-07 16:21:49 -04001028 var childRev Revision
1029 if _, childRev = n.findRevByKey(children, field.Key, keyValue); childRev != nil {
1030 log.Debugw("found-revision-matching-key-in-memory", log.Fields{
1031 "node-type": reflect.ValueOf(n.Type).Type(),
1032 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1033 "fullPath": fullPath,
1034 "name": name,
1035 })
Thomas Lee Se5a44012019-11-07 20:32:24 +05301036 } else if revs, err := n.GetBranch(NONE).GetLatest().LoadFromPersistence(ctx, fullPath, "", nil); err != nil {
1037 log.Errorf("failed-to-load-from-persistence")
1038 return nil, err
1039 } else if revs != nil && len(revs) > 0 {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001040 log.Debugw("found-revision-matching-key-in-db", log.Fields{
1041 "node-type": reflect.ValueOf(n.Type).Type(),
1042 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1043 "fullPath": fullPath,
1044 "name": name,
1045 })
1046 childRev = revs[0]
1047 } else {
1048 log.Debugw("no-revision-matching-key", log.Fields{
1049 "node-type": reflect.ValueOf(n.Type).Type(),
1050 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1051 "fullPath": fullPath,
1052 "name": name,
1053 })
1054 }
1055 if childRev != nil {
1056 childNode := childRev.GetNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001057 return childNode.createProxy(ctx, path, fullPath, n, exclusive)
Stephane Barbariec92d1072019-06-07 16:21:49 -04001058 }
Stephane Barbarie06c4a742018-10-01 11:09:32 -04001059 } else {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001060 log.Errorw("cannot-access-index-of-empty-container", log.Fields{
1061 "node-type": reflect.ValueOf(n.Type).Type(),
1062 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1063 "path": path,
1064 "name": name,
1065 })
Stephane Barbarie933b09b2019-01-09 11:12:09 -05001066 }
Stephane Barbarie126101e2018-10-11 16:18:48 -04001067 } else {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001068 log.Debugw("non-container-field", log.Fields{
1069 "node-type": reflect.ValueOf(n.Type).Type(),
1070 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1071 "path": path,
1072 "name": name,
1073 })
1074 childRev := rev.GetChildren(name)[0]
1075 childNode := childRev.GetNode()
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001076 return childNode.createProxy(ctx, path, fullPath, n, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001077 }
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001078 } else {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001079 log.Debugw("field-object-is-nil", log.Fields{
1080 "node-type": reflect.ValueOf(n.Type).Type(),
1081 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1082 "fullPath": fullPath,
1083 "name": name,
1084 })
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001085 }
1086
Stephane Barbariec92d1072019-06-07 16:21:49 -04001087 log.Warnw("cannot-create-proxy", log.Fields{
1088 "node-type": reflect.ValueOf(n.Type).Type(),
1089 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1090 "path": path,
1091 "fullPath": fullPath,
1092 "latest-rev": rev.GetHash(),
1093 })
Thomas Lee Se5a44012019-11-07 20:32:24 +05301094 return nil, nil
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001095}
1096
Stephane Barbarie1ab43272018-12-08 21:42:13 -05001097func (n *node) makeProxy(path string, fullPath string, parentNode *node, exclusive bool) *Proxy {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001098 log.Debugw("node-make-proxy", log.Fields{
1099 "node-type": reflect.ValueOf(n.Type).Type(),
1100 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1101 "path": path,
1102 "fullPath": fullPath,
1103 })
1104
Stephane Barbarie126101e2018-10-11 16:18:48 -04001105 r := &root{
1106 node: n,
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001107 Callbacks: n.Root.GetCallbacks(),
1108 NotificationCallbacks: n.Root.GetNotificationCallbacks(),
Stephane Barbarie126101e2018-10-11 16:18:48 -04001109 DirtyNodes: n.Root.DirtyNodes,
1110 KvStore: n.Root.KvStore,
1111 Loading: n.Root.Loading,
1112 RevisionClass: n.Root.RevisionClass,
1113 }
1114
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001115 if n.Proxy == nil {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001116 log.Debugw("constructing-new-proxy", log.Fields{
1117 "node-type": reflect.ValueOf(n.Type).Type(),
1118 "parent-node-type": reflect.ValueOf(parentNode.Type).Type(),
1119 "path": path,
1120 "fullPath": fullPath,
1121 })
Stephane Barbarie1ab43272018-12-08 21:42:13 -05001122 n.Proxy = NewProxy(r, n, parentNode, path, fullPath, exclusive)
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001123 } else {
Stephane Barbariec92d1072019-06-07 16:21:49 -04001124 log.Debugw("node-has-existing-proxy", log.Fields{
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001125 "node-type": reflect.ValueOf(n.GetProxy().Node.Type).Type(),
1126 "parent-node-type": reflect.ValueOf(n.GetProxy().ParentNode.Type).Type(),
1127 "path": n.GetProxy().Path,
1128 "fullPath": n.GetProxy().FullPath,
Stephane Barbariec92d1072019-06-07 16:21:49 -04001129 })
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001130 if n.GetProxy().Exclusive {
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001131 log.Error("node is already owned exclusively")
1132 }
1133 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001134
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001135 return n.Proxy
1136}
1137
Stephane Barbarie06c4a742018-10-01 11:09:32 -04001138func (n *node) makeEventBus() *EventBus {
Stephane Barbarieec0919b2018-09-05 14:14:29 -04001139 if n.EventBus == nil {
1140 n.EventBus = NewEventBus()
1141 }
1142 return n.EventBus
1143}
1144
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001145func (n *node) SetProxy(proxy *Proxy) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001146 n.Proxy = proxy
1147}
1148
1149func (n *node) GetProxy() *Proxy {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001150 return n.Proxy
1151}
1152
1153func (n *node) GetBranch(key string) *Branch {
Stephane Barbarie1ab43272018-12-08 21:42:13 -05001154 if n.Branches != nil {
1155 if branch, exists := n.Branches[key]; exists {
1156 return branch
1157 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001158 }
1159 return nil
1160}
1161
1162func (n *node) SetBranch(key string, branch *Branch) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001163 n.Branches[key] = branch
1164}
1165
1166func (n *node) GetRoot() *root {
Stephane Barbariedc5022d2018-11-19 15:21:44 -05001167 return n.Root
Stephane Barbarie06c4a742018-10-01 11:09:32 -04001168}
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001169func (n *node) SetRoot(root *root) {
1170 n.Root = root
1171}