blob: 73ea70d04839939d9cc3bb1618756639f8e97bc2 [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
19import (
Stephane Barbarieef6650d2019-07-18 12:15:09 -040020 "context"
Stephane Barbarie694e2b92018-09-07 12:17:36 -040021 "crypto/md5"
Stephane Barbarie694e2b92018-09-07 12:17:36 -040022 "errors"
khenaidoob9203542018-09-17 22:56:37 -040023 "fmt"
khenaidoob9203542018-09-17 22:56:37 -040024 "reflect"
Stephane Barbarie694e2b92018-09-07 12:17:36 -040025 "runtime"
khenaidoob9203542018-09-17 22:56:37 -040026 "strings"
Stephane Barbariea188d942018-10-16 16:43:04 -040027 "sync"
npujar9a30c702019-11-14 17:06:39 +053028
serkant.uluderya2ae470f2020-01-21 11:13:09 -080029 "github.com/opencord/voltha-lib-go/v3/pkg/log"
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040030)
31
Stephane Barbariedc5022d2018-11-19 15:21:44 -050032// OperationContext holds details on the information used during an operation
Stephane Barbarieec0919b2018-09-05 14:14:29 -040033type OperationContext struct {
34 Path string
35 Data interface{}
36 FieldName string
37 ChildKey string
38}
39
Stephane Barbariedc5022d2018-11-19 15:21:44 -050040// NewOperationContext instantiates a new OperationContext structure
Stephane Barbarieec0919b2018-09-05 14:14:29 -040041func NewOperationContext(path string, data interface{}, fieldName string, childKey string) *OperationContext {
42 oc := &OperationContext{
43 Path: path,
44 Data: data,
45 FieldName: fieldName,
46 ChildKey: childKey,
47 }
48 return oc
49}
50
Stephane Barbariedc5022d2018-11-19 15:21:44 -050051// Update applies new data to the context structure
Stephane Barbarieec0919b2018-09-05 14:14:29 -040052func (oc *OperationContext) Update(data interface{}) *OperationContext {
53 oc.Data = data
54 return oc
55}
56
Stephane Barbariedc5022d2018-11-19 15:21:44 -050057// Proxy holds the information for a specific location with the data model
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040058type Proxy struct {
Stephane Barbarieef6650d2019-07-18 12:15:09 -040059 mutex sync.RWMutex
Stephane Barbariec53a2752019-03-08 17:50:10 -050060 Root *root
61 Node *node
62 ParentNode *node
63 Path string
64 FullPath string
65 Exclusive bool
66 Callbacks map[CallbackType]map[string]*CallbackTuple
Stephane Barbarieef6650d2019-07-18 12:15:09 -040067 operation ProxyOperation
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040068}
69
Stephane Barbariedc5022d2018-11-19 15:21:44 -050070// NewProxy instantiates a new proxy to a specific location
Stephane Barbarie1ab43272018-12-08 21:42:13 -050071func NewProxy(root *root, node *node, parentNode *node, path string, fullPath string, exclusive bool) *Proxy {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050072 callbacks := make(map[CallbackType]map[string]*CallbackTuple)
Stephane Barbariea188d942018-10-16 16:43:04 -040073 if fullPath == "/" {
74 fullPath = ""
75 }
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040076 p := &Proxy{
Stephane Barbariec53a2752019-03-08 17:50:10 -050077 Root: root,
78 Node: node,
79 ParentNode: parentNode,
80 Exclusive: exclusive,
81 Path: path,
82 FullPath: fullPath,
83 Callbacks: callbacks,
Stephane Barbarie4a2564d2018-07-26 11:02:58 -040084 }
85 return p
86}
87
npujar9a30c702019-11-14 17:06:39 +053088// getRoot returns the root attribute of the proxy
89func (p *Proxy) getRoot() *root {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050090 return p.Root
91}
92
93// getPath returns the path attribute of the proxy
94func (p *Proxy) getPath() string {
Stephane Barbariedc5022d2018-11-19 15:21:44 -050095 return p.Path
96}
97
98// getFullPath returns the full path attribute of the proxy
99func (p *Proxy) getFullPath() string {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500100 return p.FullPath
101}
102
103// getCallbacks returns the full list of callbacks associated to the proxy
104func (p *Proxy) getCallbacks(callbackType CallbackType) map[string]*CallbackTuple {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400105 p.mutex.RLock()
106 defer p.mutex.RUnlock()
107
Stephane Barbariec92d1072019-06-07 16:21:49 -0400108 if p != nil {
109 if cb, exists := p.Callbacks[callbackType]; exists {
110 return cb
111 }
112 } else {
Girish Kumarf56a4682020-03-20 20:07:46 +0000113 logger.Debugw("proxy-is-nil", log.Fields{"callback-type": callbackType.String()})
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500114 }
115 return nil
116}
117
118// getCallback returns a specific callback matching the type and function hash
119func (p *Proxy) getCallback(callbackType CallbackType, funcHash string) *CallbackTuple {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400120 p.mutex.Lock()
121 defer p.mutex.Unlock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500122 if tuple, exists := p.Callbacks[callbackType][funcHash]; exists {
123 return tuple
124 }
125 return nil
126}
127
128// setCallbacks applies a callbacks list to a type
129func (p *Proxy) setCallbacks(callbackType CallbackType, callbacks map[string]*CallbackTuple) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400130 p.mutex.Lock()
131 defer p.mutex.Unlock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500132 p.Callbacks[callbackType] = callbacks
133}
134
135// setCallback applies a callback to a type and hash value
136func (p *Proxy) setCallback(callbackType CallbackType, funcHash string, tuple *CallbackTuple) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400137 p.mutex.Lock()
138 defer p.mutex.Unlock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500139 p.Callbacks[callbackType][funcHash] = tuple
140}
141
142// DeleteCallback removes a callback matching the type and hash
143func (p *Proxy) DeleteCallback(callbackType CallbackType, funcHash string) {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400144 p.mutex.Lock()
145 defer p.mutex.Unlock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500146 delete(p.Callbacks[callbackType], funcHash)
147}
148
npujar9a30c702019-11-14 17:06:39 +0530149// ProxyOperation callbackType is an enumerated value to express when a callback should be executed
Stephane Barbariec53a2752019-03-08 17:50:10 -0500150type ProxyOperation uint8
151
152// Enumerated list of callback types
153const (
npujar9a30c702019-11-14 17:06:39 +0530154 ProxyNone ProxyOperation = iota
155 ProxyGet
156 ProxyList
157 ProxyAdd
158 ProxyUpdate
159 ProxyRemove
160 ProxyCreate
161 ProxyWatch
Stephane Barbariec53a2752019-03-08 17:50:10 -0500162)
163
Stephane Barbarie802aca42019-05-21 12:19:28 -0400164var proxyOperationTypes = []string{
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400165 "PROXY_NONE",
Stephane Barbarie802aca42019-05-21 12:19:28 -0400166 "PROXY_GET",
167 "PROXY_LIST",
168 "PROXY_ADD",
169 "PROXY_UPDATE",
170 "PROXY_REMOVE",
171 "PROXY_CREATE",
Stephane Barbariec92d1072019-06-07 16:21:49 -0400172 "PROXY_WATCH",
Stephane Barbarie802aca42019-05-21 12:19:28 -0400173}
174
175func (t ProxyOperation) String() string {
176 return proxyOperationTypes[t]
177}
178
npujar9a30c702019-11-14 17:06:39 +0530179// GetOperation -
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400180func (p *Proxy) GetOperation() ProxyOperation {
181 p.mutex.RLock()
182 defer p.mutex.RUnlock()
183 return p.operation
184}
185
npujar9a30c702019-11-14 17:06:39 +0530186// SetOperation -
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400187func (p *Proxy) SetOperation(operation ProxyOperation) {
188 p.mutex.Lock()
189 defer p.mutex.Unlock()
190 p.operation = operation
191}
192
Stephane Barbarieaa467942019-02-06 14:09:44 -0500193// List will retrieve information from the data model at the specified path location
194// A list operation will force access to persistence storage
Thomas Lee Se5a44012019-11-07 20:32:24 +0530195func (p *Proxy) List(ctx context.Context, path string, depth int, deep bool, txid string) (interface{}, error) {
Stephane Barbarieaa467942019-02-06 14:09:44 -0500196 var effectivePath string
197 if path == "/" {
198 effectivePath = p.getFullPath()
199 } else {
200 effectivePath = p.getFullPath() + path
201 }
202
npujar9a30c702019-11-14 17:06:39 +0530203 p.SetOperation(ProxyList)
204 defer p.SetOperation(ProxyNone)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400205
Girish Kumarf56a4682020-03-20 20:07:46 +0000206 logger.Debugw("proxy-list", log.Fields{
khenaidoo67b22152020-03-02 16:01:25 -0500207 "path": path,
208 "effective": effectivePath,
209 "operation": p.GetOperation(),
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400210 })
npujar9a30c702019-11-14 17:06:39 +0530211 return p.getRoot().List(ctx, path, "", depth, deep, txid)
Stephane Barbarieaa467942019-02-06 14:09:44 -0500212}
213
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500214// Get will retrieve information from the data model at the specified path location
Thomas Lee Se5a44012019-11-07 20:32:24 +0530215func (p *Proxy) Get(ctx context.Context, path string, depth int, deep bool, txid string) (interface{}, error) {
Stephane Barbariea188d942018-10-16 16:43:04 -0400216 var effectivePath string
217 if path == "/" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500218 effectivePath = p.getFullPath()
Stephane Barbariea188d942018-10-16 16:43:04 -0400219 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500220 effectivePath = p.getFullPath() + path
Stephane Barbariea188d942018-10-16 16:43:04 -0400221 }
222
npujar9a30c702019-11-14 17:06:39 +0530223 p.SetOperation(ProxyGet)
224 defer p.SetOperation(ProxyNone)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400225
Girish Kumarf56a4682020-03-20 20:07:46 +0000226 logger.Debugw("proxy-get", log.Fields{
khenaidoo67b22152020-03-02 16:01:25 -0500227 "path": path,
228 "effective": effectivePath,
229 "operation": p.GetOperation(),
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400230 })
Stephane Barbariea188d942018-10-16 16:43:04 -0400231
npujar9a30c702019-11-14 17:06:39 +0530232 return p.getRoot().Get(ctx, path, "", depth, deep, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400233}
234
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500235// Update will modify information in the data model at the specified location with the provided data
Thomas Lee Se5a44012019-11-07 20:32:24 +0530236func (p *Proxy) Update(ctx context.Context, path string, data interface{}, strict bool, txid string) (interface{}, error) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400237 if !strings.HasPrefix(path, "/") {
Girish Kumarf56a4682020-03-20 20:07:46 +0000238 logger.Errorf("invalid path: %s", path)
Thomas Lee Se5a44012019-11-07 20:32:24 +0530239 return nil, fmt.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400240 }
241 var fullPath string
Stephane Barbariea188d942018-10-16 16:43:04 -0400242 var effectivePath string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400243 if path == "/" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500244 fullPath = p.getPath()
245 effectivePath = p.getFullPath()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400246 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500247 fullPath = p.getPath() + path
Stephane Barbarie1039ec42019-02-04 10:43:16 -0500248 effectivePath = p.getFullPath() + path
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400249 }
Stephane Barbarie126101e2018-10-11 16:18:48 -0400250
npujar9a30c702019-11-14 17:06:39 +0530251 p.SetOperation(ProxyUpdate)
252 defer p.SetOperation(ProxyNone)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400253
Girish Kumarf56a4682020-03-20 20:07:46 +0000254 logger.Debugw("proxy-update", log.Fields{
khenaidoo67b22152020-03-02 16:01:25 -0500255 "path": path,
256 "effective": effectivePath,
257 "full": fullPath,
258 "operation": p.GetOperation(),
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400259 })
Stephane Barbariea188d942018-10-16 16:43:04 -0400260
npujar9a30c702019-11-14 17:06:39 +0530261 result := p.getRoot().Update(ctx, fullPath, data, strict, txid, nil)
Stephane Barbariec53a2752019-03-08 17:50:10 -0500262
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400263 if result != nil {
Thomas Lee Se5a44012019-11-07 20:32:24 +0530264 return result.GetData(), nil
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400265 }
266
Thomas Lee Se5a44012019-11-07 20:32:24 +0530267 return nil, nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400268}
269
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500270// AddWithID will insert new data at specified location.
271// This method also allows the user to specify the ID of the data entry to ensure
272// that access control is active while inserting the information.
Thomas Lee Se5a44012019-11-07 20:32:24 +0530273func (p *Proxy) AddWithID(ctx context.Context, path string, id string, data interface{}, txid string) (interface{}, error) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500274 if !strings.HasPrefix(path, "/") {
Girish Kumarf56a4682020-03-20 20:07:46 +0000275 logger.Errorf("invalid path: %s", path)
Thomas Lee Se5a44012019-11-07 20:32:24 +0530276 return nil, fmt.Errorf("invalid path: %s", path)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500277 }
278 var fullPath string
279 var effectivePath string
280 if path == "/" {
281 fullPath = p.getPath()
282 effectivePath = p.getFullPath()
283 } else {
284 fullPath = p.getPath() + path
285 effectivePath = p.getFullPath() + path + "/" + id
286 }
287
npujar9a30c702019-11-14 17:06:39 +0530288 p.SetOperation(ProxyAdd)
289 defer p.SetOperation(ProxyNone)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400290
Girish Kumarf56a4682020-03-20 20:07:46 +0000291 logger.Debugw("proxy-add-with-id", log.Fields{
khenaidoo67b22152020-03-02 16:01:25 -0500292 "path": path,
293 "effective": effectivePath,
294 "full": fullPath,
295 "operation": p.GetOperation(),
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400296 })
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500297
npujar9a30c702019-11-14 17:06:39 +0530298 result := p.getRoot().Add(ctx, fullPath, data, txid, nil)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400299
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400300 if result != nil {
Thomas Lee Se5a44012019-11-07 20:32:24 +0530301 return result.GetData(), nil
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400302 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500303
Thomas Lee Se5a44012019-11-07 20:32:24 +0530304 return nil, nil
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500305}
306
307// Add will insert new data at specified location.
Thomas Lee Se5a44012019-11-07 20:32:24 +0530308func (p *Proxy) Add(ctx context.Context, path string, data interface{}, txid string) (interface{}, error) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400309 if !strings.HasPrefix(path, "/") {
Girish Kumarf56a4682020-03-20 20:07:46 +0000310 logger.Errorf("invalid path: %s", path)
Thomas Lee Se5a44012019-11-07 20:32:24 +0530311 return nil, fmt.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400312 }
313 var fullPath string
Stephane Barbariea188d942018-10-16 16:43:04 -0400314 var effectivePath string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400315 if path == "/" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500316 fullPath = p.getPath()
317 effectivePath = p.getFullPath()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400318 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500319 fullPath = p.getPath() + path
320 effectivePath = p.getFullPath() + path
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400321 }
Stephane Barbariea188d942018-10-16 16:43:04 -0400322
npujar9a30c702019-11-14 17:06:39 +0530323 p.SetOperation(ProxyAdd)
324 defer p.SetOperation(ProxyNone)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400325
Girish Kumarf56a4682020-03-20 20:07:46 +0000326 logger.Debugw("proxy-add", log.Fields{
khenaidoo67b22152020-03-02 16:01:25 -0500327 "path": path,
328 "effective": effectivePath,
329 "full": fullPath,
330 "operation": p.GetOperation(),
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400331 })
Stephane Barbariea188d942018-10-16 16:43:04 -0400332
npujar9a30c702019-11-14 17:06:39 +0530333 result := p.getRoot().Add(ctx, fullPath, data, txid, nil)
Stephane Barbariea188d942018-10-16 16:43:04 -0400334
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400335 if result != nil {
Thomas Lee Se5a44012019-11-07 20:32:24 +0530336 return result.GetData(), nil
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400337 }
Stephane Barbariec53a2752019-03-08 17:50:10 -0500338
Thomas Lee Se5a44012019-11-07 20:32:24 +0530339 return nil, nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400340}
341
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500342// Remove will delete an entry at the specified location
Thomas Lee Se5a44012019-11-07 20:32:24 +0530343func (p *Proxy) Remove(ctx context.Context, path string, txid string) (interface{}, error) {
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400344 if !strings.HasPrefix(path, "/") {
Girish Kumarf56a4682020-03-20 20:07:46 +0000345 logger.Errorf("invalid path: %s", path)
Thomas Lee Se5a44012019-11-07 20:32:24 +0530346 return nil, fmt.Errorf("invalid path: %s", path)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400347 }
348 var fullPath string
Stephane Barbariea188d942018-10-16 16:43:04 -0400349 var effectivePath string
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400350 if path == "/" {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500351 fullPath = p.getPath()
352 effectivePath = p.getFullPath()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400353 } else {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500354 fullPath = p.getPath() + path
355 effectivePath = p.getFullPath() + path
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400356 }
Stephane Barbariea188d942018-10-16 16:43:04 -0400357
npujar9a30c702019-11-14 17:06:39 +0530358 p.SetOperation(ProxyRemove)
359 defer p.SetOperation(ProxyNone)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400360
Girish Kumarf56a4682020-03-20 20:07:46 +0000361 logger.Debugw("proxy-remove", log.Fields{
khenaidoo67b22152020-03-02 16:01:25 -0500362 "path": path,
363 "effective": effectivePath,
364 "full": fullPath,
365 "operation": p.GetOperation(),
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400366 })
Stephane Barbariea188d942018-10-16 16:43:04 -0400367
npujar9a30c702019-11-14 17:06:39 +0530368 result := p.getRoot().Remove(ctx, fullPath, txid, nil)
Stephane Barbariea188d942018-10-16 16:43:04 -0400369
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400370 if result != nil {
Thomas Lee Se5a44012019-11-07 20:32:24 +0530371 return result.GetData(), nil
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400372 }
Stephane Barbariec53a2752019-03-08 17:50:10 -0500373
Thomas Lee Se5a44012019-11-07 20:32:24 +0530374 return nil, nil
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400375}
376
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400377// CreateProxy to interact with specific path directly
Thomas Lee Se5a44012019-11-07 20:32:24 +0530378func (p *Proxy) CreateProxy(ctx context.Context, path string, exclusive bool) (*Proxy, error) {
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400379 if !strings.HasPrefix(path, "/") {
Girish Kumarf56a4682020-03-20 20:07:46 +0000380 logger.Errorf("invalid path: %s", path)
Thomas Lee Se5a44012019-11-07 20:32:24 +0530381 return nil, fmt.Errorf("invalid path: %s", path)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400382 }
383
384 var fullPath string
385 var effectivePath string
386 if path == "/" {
387 fullPath = p.getPath()
388 effectivePath = p.getFullPath()
389 } else {
390 fullPath = p.getPath() + path
391 effectivePath = p.getFullPath() + path
392 }
393
npujar9a30c702019-11-14 17:06:39 +0530394 p.SetOperation(ProxyCreate)
395 defer p.SetOperation(ProxyNone)
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400396
Girish Kumarf56a4682020-03-20 20:07:46 +0000397 logger.Debugw("proxy-create", log.Fields{
khenaidoo67b22152020-03-02 16:01:25 -0500398 "path": path,
399 "effective": effectivePath,
400 "full": fullPath,
401 "operation": p.GetOperation(),
Stephane Barbarie7512fc82019-05-07 12:25:46 -0400402 })
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400403
npujar9a30c702019-11-14 17:06:39 +0530404 return p.getRoot().CreateProxy(ctx, fullPath, exclusive)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400405}
406
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500407// OpenTransaction creates a new transaction branch to isolate operations made to the data model
Stephane Barbarie88fbe7f2018-09-25 12:25:23 -0400408func (p *Proxy) OpenTransaction() *Transaction {
npujar9a30c702019-11-14 17:06:39 +0530409 txid := p.getRoot().MakeTxBranch()
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400410 return NewTransaction(p, txid)
411}
412
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500413// commitTransaction will apply and merge modifications made in the transaction branch to the data model
npujar467fe752020-01-16 20:17:45 +0530414func (p *Proxy) commitTransaction(ctx context.Context, txid string) {
415 p.getRoot().FoldTxBranch(ctx, txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400416}
417
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500418// cancelTransaction will terminate a transaction branch along will all changes within it
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400419func (p *Proxy) cancelTransaction(txid string) {
npujar9a30c702019-11-14 17:06:39 +0530420 p.getRoot().DeleteTxBranch(txid)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400421}
422
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500423// CallbackFunction is a type used to define callback functions
npujar467fe752020-01-16 20:17:45 +0530424type CallbackFunction func(ctx context.Context, args ...interface{}) interface{}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500425
426// CallbackTuple holds the function and arguments details of a callback
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400427type CallbackTuple struct {
428 callback CallbackFunction
429 args []interface{}
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400430}
431
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500432// Execute will process the a callback with its provided arguments
npujar467fe752020-01-16 20:17:45 +0530433func (tuple *CallbackTuple) Execute(ctx context.Context, contextArgs []interface{}) interface{} {
Stephane Barbarie126101e2018-10-11 16:18:48 -0400434 args := []interface{}{}
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500435
npujar9a30c702019-11-14 17:06:39 +0530436 args = append(args, tuple.args...)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500437
npujar9a30c702019-11-14 17:06:39 +0530438 args = append(args, contextArgs...)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500439
npujar467fe752020-01-16 20:17:45 +0530440 return tuple.callback(ctx, args...)
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400441}
442
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500443// RegisterCallback associates a callback to the proxy
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400444func (p *Proxy) RegisterCallback(callbackType CallbackType, callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500445 if p.getCallbacks(callbackType) == nil {
446 p.setCallbacks(callbackType, make(map[string]*CallbackTuple))
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400447 }
448 funcName := runtime.FuncForPC(reflect.ValueOf(callback).Pointer()).Name()
Girish Kumarf56a4682020-03-20 20:07:46 +0000449 logger.Debugf("value of function: %s", funcName)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400450 funcHash := fmt.Sprintf("%x", md5.Sum([]byte(funcName)))[:12]
451
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500452 p.setCallback(callbackType, funcHash, &CallbackTuple{callback, args})
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400453}
454
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500455// UnregisterCallback removes references to a callback within a proxy
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400456func (p *Proxy) UnregisterCallback(callbackType CallbackType, callback CallbackFunction, args ...interface{}) {
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500457 if p.getCallbacks(callbackType) == nil {
Girish Kumarf56a4682020-03-20 20:07:46 +0000458 logger.Errorf("no such callback type - %s", callbackType.String())
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400459 return
460 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500461
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400462 funcName := runtime.FuncForPC(reflect.ValueOf(callback).Pointer()).Name()
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400463 funcHash := fmt.Sprintf("%x", md5.Sum([]byte(funcName)))[:12]
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500464
Girish Kumarf56a4682020-03-20 20:07:46 +0000465 logger.Debugf("value of function: %s", funcName)
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500466
467 if p.getCallback(callbackType, funcHash) == nil {
Girish Kumarf56a4682020-03-20 20:07:46 +0000468 logger.Errorf("function with hash value: '%s' not registered with callback type: '%s'", funcHash, callbackType)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400469 return
470 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500471
472 p.DeleteCallback(callbackType, funcHash)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400473}
474
npujar467fe752020-01-16 20:17:45 +0530475func (p *Proxy) invoke(ctx context.Context, callback *CallbackTuple, context []interface{}) (result interface{}, err error) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400476 defer func() {
477 if r := recover(); r != nil {
478 errStr := fmt.Sprintf("callback error occurred: %+v", r)
479 err = errors.New(errStr)
Girish Kumarf56a4682020-03-20 20:07:46 +0000480 logger.Error(errStr)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400481 }
482 }()
483
npujar467fe752020-01-16 20:17:45 +0530484 result = callback.Execute(ctx, context)
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400485
486 return result, err
487}
488
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500489// InvokeCallbacks executes all callbacks associated to a specific type
npujar467fe752020-01-16 20:17:45 +0530490func (p *Proxy) InvokeCallbacks(ctx context.Context, args ...interface{}) (result interface{}) {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400491 callbackType := args[0].(CallbackType)
Stephane Barbarie126101e2018-10-11 16:18:48 -0400492 proceedOnError := args[1].(bool)
493 context := args[2:]
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400494
495 var err error
496
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500497 if callbacks := p.getCallbacks(callbackType); callbacks != nil {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400498 p.mutex.Lock()
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500499 for _, callback := range callbacks {
npujar467fe752020-01-16 20:17:45 +0530500 if result, err = p.invoke(ctx, callback, context); err != nil {
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400501 if !proceedOnError {
Girish Kumarf56a4682020-03-20 20:07:46 +0000502 logger.Info("An error occurred. Stopping callback invocation")
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400503 break
504 }
Girish Kumarf56a4682020-03-20 20:07:46 +0000505 logger.Info("An error occurred. Invoking next callback")
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400506 }
507 }
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400508 p.mutex.Unlock()
Stephane Barbarie694e2b92018-09-07 12:17:36 -0400509 }
Stephane Barbariedc5022d2018-11-19 15:21:44 -0500510
Stephane Barbariea188d942018-10-16 16:43:04 -0400511 return result
Stephane Barbarie4a2564d2018-07-26 11:02:58 -0400512}