blob: 722fc94a222e429148f46553178856c8331076af [file] [log] [blame]
Stephane Barbarie4a2564d2018-07-26 11:02:58 -04001package model
2
3import (
4 "encoding/hex"
5 "encoding/json"
6 "fmt"
7 "github.com/google/uuid"
8 "reflect"
9)
10
11type Root struct {
12 *Node
13 DirtyNodes map[string]*Node
14 KvStore *Backend
15 Loading bool
16 RevisionClass interface{}
17 Callbacks []func() interface{}
18 NotificationCallbacks []func() interface{}
19}
20
21func NewRoot(initialData interface{}, kvStore *Backend, revisionClass interface{}) *Root {
22 root := &Root{}
23 root.KvStore = kvStore
24 root.DirtyNodes = make(map[string]*Node)
25 root.Loading = false
26 if kvStore != nil /*&& FIXME: RevisionClass is a subclass of PersistedConfigRevision */ {
27 revisionClass = reflect.TypeOf(PersistedRevision{})
28 }
29 root.RevisionClass = revisionClass
30 root.Callbacks = []func() interface{}{}
31 root.NotificationCallbacks = []func() interface{}{}
32
33 root.Node = NewNode(root, initialData, false, "")
34
35 return root
36}
37
38func (r *Root) makeRevision(branch *Branch, data interface{}, children map[string][]*Revision) *Revision {
39
40 return &Revision{}
41}
42
43func (r *Root) makeTxBranch() string {
44 txid_bin, _ := uuid.New().MarshalBinary()
45 txid := hex.EncodeToString(txid_bin)[:12]
46 r.DirtyNodes[txid] = r.Node
47 r.Node.makeTxBranch(txid)
48 return txid
49}
50
51func (r *Root) deleteTxBranch(txid string) {
52 for _, dirtyNode := range r.DirtyNodes {
53 dirtyNode.deleteTxBranch(txid)
54 }
55 delete(r.DirtyNodes, txid)
56}
57
58func (r *Root) foldTxBranch(txid string) {
59 // TODO: implement foldTxBranch
60 // if err := r.Node.mergeTxBranch(txid, dryRun=true); err != nil {
61 // r.deleteTxBranch(txid)
62 // } else {
63 // r.Node.mergeTxBranch(txid)
64 // r.executeCallbacks()
65 // }
66}
67
68func (r *Root) executeCallbacks() {
69 for len(r.Callbacks) > 0 {
70 callback := r.Callbacks[0]
71 r.Callbacks = r.Callbacks[1:]
72 callback()
73 }
74 for len(r.NotificationCallbacks) > 0 {
75 callback := r.NotificationCallbacks[0]
76 r.NotificationCallbacks = r.NotificationCallbacks[1:]
77 callback()
78 }
79}
80
81func (r *Root) noCallbacks() bool {
82 return len(r.Callbacks) == 0
83}
84
85func (r *Root) addCallback(callback func() interface{}) {
86 r.Callbacks = append(r.Callbacks, callback)
87}
88func (r *Root) addNotificationCallback(callback func() interface{}) {
89 r.NotificationCallbacks = append(r.NotificationCallbacks, callback)
90}
91
92func (r *Root) Update(path string, data interface{}, strict bool, txid string, makeBranch t_makeBranch) *Revision {
93 var result *Revision
94 // FIXME: the more i look at this... i think i need to implement an interface for Node & root
95
96 if makeBranch == nil {
97 // TODO: raise error
98 }
99
100 if r.noCallbacks() {
101 // TODO: raise error
102 }
103
104 if txid != "" {
105 //dirtied := r.DirtyNodes[txid]
106
107 trackDirty := func(node *Node) *Branch {
108 //dirtied.Add(Node)
109 return node.makeTxBranch(txid)
110 }
111 result = r.Node.Update(path, data, strict, txid, trackDirty)
112 } else {
113 result = r.Node.Update(path, data, strict, "", nil)
114 }
115
116 r.executeCallbacks()
117
118 return result
119}
120
121func (r *Root) Add(path string, data interface{}, txid string, makeBranch t_makeBranch) *Revision {
122 var result *Revision
123 // FIXME: the more i look at this... i think i need to implement an interface for Node & root
124
125 if makeBranch == nil {
126 // TODO: raise error
127 }
128
129 if r.noCallbacks() {
130 // TODO: raise error
131 }
132
133 if txid != "" {
134 //dirtied := r.DirtyNodes[txid]
135
136 trackDirty := func(node *Node) *Branch {
137 //dirtied.Add(Node)
138 return node.makeTxBranch(txid)
139 }
140 result = r.Node.Add(path, data, txid, trackDirty)
141 } else {
142 result = r.Node.Add(path, data, "", nil)
143 }
144
145 r.executeCallbacks()
146
147 return result
148}
149
150func (r *Root) Remove(path string, txid string, makeBranch t_makeBranch) *Revision {
151 var result *Revision
152 // FIXME: the more i look at this... i think i need to implement an interface for Node & root
153
154 if makeBranch == nil {
155 // TODO: raise error
156 }
157
158 if r.noCallbacks() {
159 // TODO: raise error
160 }
161
162 if txid != "" {
163 //dirtied := r.DirtyNodes[txid]
164
165 trackDirty := func(node *Node) *Branch {
166 //dirtied.Add(Node)
167 return node.makeTxBranch(txid)
168 }
169 result = r.Node.Remove(path, txid, trackDirty)
170 } else {
171 result = r.Node.Remove(path, "", nil)
172 }
173
174 r.executeCallbacks()
175
176 return result
177}
178
179func (r *Root) Load(rootClass interface{}) *Root {
180 //fakeKvStore := &Backend{}
181 //root := NewRoot(rootClass, fakeKvStore, PersistedRevision{})
182 //r.KvStore = KvStore
183 r.loadFromPersistence(rootClass)
184 return r
185}
186
187func (r *Root) LoadLatest(hash string) {
188 r.Node.LoadLatest(r.KvStore, hash)
189}
190
191type rootData struct {
192 Latest string `json:GetLatest`
193 Tags map[string]string `json:Tags`
194}
195
196func (r *Root) loadFromPersistence(rootClass interface{}) {
197 var data rootData
198
199 r.Loading = true
200 blob, _ := r.KvStore.Get("root")
201
202 if err := json.Unmarshal(blob.Value.([]byte), &data); err != nil {
203 fmt.Errorf("problem to unmarshal blob - error:%s\n", err.Error())
204 }
205
206 for tag, hash := range data.Tags {
207 r.Node.LoadLatest(r.KvStore, hash)
208 r.Node.Tags[tag] = r.Node.Latest()
209 }
210
211 r.Node.LoadLatest(r.KvStore, data.Latest)
212 r.Loading = false
213}