Commit 685dd6223d0c84c78e702a9e437417be64d2c170
1 parent
3f8e3c4374
Exists in
master
and in
1 other branch
added error logging everywhere
Showing
1 changed file
with
6 additions
and
0 deletions
Show diff stats
json.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "database/sql" | 4 | "database/sql" |
5 | "encoding/json" | 5 | "encoding/json" |
6 | "errors" | 6 | "errors" |
7 | "fmt" | 7 | "fmt" |
8 | "io" | 8 | "io" |
9 | "net/http" | 9 | "net/http" |
10 | "sync" | 10 | "sync" |
11 | "time" | 11 | "time" |
12 | 12 | ||
13 | "git.to-net.rs/marko.tikvic/gologger" | 13 | "git.to-net.rs/marko.tikvic/gologger" |
14 | ) | 14 | ) |
15 | 15 | ||
16 | var ( | 16 | var ( |
17 | mu = &sync.Mutex{} | 17 | mu = &sync.Mutex{} |
18 | metadata = make(map[string]Payload) | 18 | metadata = make(map[string]Payload) |
19 | 19 | ||
20 | updateQue = make(map[string][]byte) | 20 | updateQue = make(map[string][]byte) |
21 | 21 | ||
22 | metadataDB *sql.DB | 22 | metadataDB *sql.DB |
23 | activeProject string | 23 | activeProject string |
24 | 24 | ||
25 | inited bool | 25 | inited bool |
26 | driver string | 26 | driver string |
27 | logger *gologger.Logger | 27 | logger *gologger.Logger |
28 | ) | 28 | ) |
29 | 29 | ||
30 | type LangMap map[string]map[string]string | 30 | type LangMap map[string]map[string]string |
31 | 31 | ||
32 | type Field struct { | 32 | type Field struct { |
33 | Parameter string `json:"param"` | 33 | Parameter string `json:"param"` |
34 | Type string `json:"type"` | 34 | Type string `json:"type"` |
35 | Visible bool `json:"visible"` | 35 | Visible bool `json:"visible"` |
36 | Editable bool `json:"editable"` | 36 | Editable bool `json:"editable"` |
37 | } | 37 | } |
38 | 38 | ||
39 | type CorrelationField struct { | 39 | type CorrelationField struct { |
40 | Result string `json:"result"` | 40 | Result string `json:"result"` |
41 | Elements []string `json:"elements"` | 41 | Elements []string `json:"elements"` |
42 | Type string `json:"type"` | 42 | Type string `json:"type"` |
43 | } | 43 | } |
44 | 44 | ||
45 | type Translation struct { | 45 | type Translation struct { |
46 | Language string `json:"language"` | 46 | Language string `json:"language"` |
47 | FieldsLabels map[string]string `json:"fieldsLabels"` | 47 | FieldsLabels map[string]string `json:"fieldsLabels"` |
48 | } | 48 | } |
49 | 49 | ||
50 | type Payload struct { | 50 | type Payload struct { |
51 | Method string `json:"method"` | 51 | Method string `json:"method"` |
52 | Params map[string]string `json:"params"` | 52 | Params map[string]string `json:"params"` |
53 | Lang []Translation `json:"lang"` | 53 | Lang []Translation `json:"lang"` |
54 | Fields []Field `json:"fields"` | 54 | Fields []Field `json:"fields"` |
55 | Correlations []CorrelationField `json:"correlationFields"` | 55 | Correlations []CorrelationField `json:"correlationFields"` |
56 | IdField string `json:"idField"` | 56 | IdField string `json:"idField"` |
57 | 57 | ||
58 | // Data holds JSON payload. | 58 | // Data holds JSON payload. |
59 | // It can't be used for itteration. | 59 | // It can't be used for itteration. |
60 | Data interface{} `json:"data"` | 60 | Data interface{} `json:"data"` |
61 | } | 61 | } |
62 | 62 | ||
63 | func (p *Payload) SetData(data interface{}) { | 63 | func (p *Payload) SetData(data interface{}) { |
64 | p.Data = data | 64 | p.Data = data |
65 | } | 65 | } |
66 | 66 | ||
67 | // NewPayload returs a payload sceleton for entity described with etype. | 67 | // NewPayload returs a payload sceleton for entity described with etype. |
68 | func NewPayload(r *http.Request, etype string) Payload { | 68 | func NewPayload(r *http.Request, etype string) Payload { |
69 | pload := metadata[etype] | 69 | pload := metadata[etype] |
70 | pload.Method = r.Method + " " + r.RequestURI | 70 | pload.Method = r.Method + " " + r.RequestURI |
71 | return pload | 71 | return pload |
72 | } | 72 | } |
73 | 73 | ||
74 | // DecodeJSON decodes JSON data from r to v. | 74 | // DecodeJSON decodes JSON data from r to v. |
75 | // Returns an error if it fails. | 75 | // Returns an error if it fails. |
76 | func DecodeJSON(r io.Reader, v interface{}) error { | 76 | func DecodeJSON(r io.Reader, v interface{}) error { |
77 | return json.NewDecoder(r).Decode(v) | 77 | return json.NewDecoder(r).Decode(v) |
78 | } | 78 | } |
79 | 79 | ||
80 | // InitPayloadsMetadata loads all payloads' information into 'metadata' variable. | 80 | // InitPayloadsMetadata loads all payloads' information into 'metadata' variable. |
81 | func InitPayloadsMetadata(drv string, db *sql.DB, project string) error { | 81 | func InitPayloadsMetadata(drv string, db *sql.DB, project string) error { |
82 | var err error | 82 | var err error |
83 | if drv != "ora" && drv != "mysql" { | 83 | if drv != "ora" && drv != "mysql" { |
84 | err = errors.New("driver not supported") | 84 | err = errors.New("driver not supported") |
85 | return err | 85 | return err |
86 | } | 86 | } |
87 | 87 | ||
88 | driver = drv | 88 | driver = drv |
89 | metadataDB = db | 89 | metadataDB = db |
90 | activeProject = project | 90 | activeProject = project |
91 | 91 | ||
92 | logger, err = gologger.New("metadata", gologger.MaxLogSize100KB) | 92 | logger, err = gologger.New("metadata", gologger.MaxLogSize100KB) |
93 | if err != nil { | 93 | if err != nil { |
94 | fmt.Printf("webutility: %s\n", err.Error()) | 94 | fmt.Printf("webutility: %s\n", err.Error()) |
95 | } | 95 | } |
96 | 96 | ||
97 | mu.Lock() | 97 | mu.Lock() |
98 | defer mu.Unlock() | 98 | defer mu.Unlock() |
99 | err = initMetadata(project) | 99 | err = initMetadata(project) |
100 | if err != nil { | 100 | if err != nil { |
101 | return err | 101 | return err |
102 | } | 102 | } |
103 | inited = true | 103 | inited = true |
104 | 104 | ||
105 | return nil | 105 | return nil |
106 | } | 106 | } |
107 | 107 | ||
108 | func EnableHotloading(interval int) { | 108 | func EnableHotloading(interval int) { |
109 | if interval > 0 { | 109 | if interval > 0 { |
110 | go hotload(interval) | 110 | go hotload(interval) |
111 | } | 111 | } |
112 | } | 112 | } |
113 | 113 | ||
114 | func GetMetadataForAllEntities() map[string]Payload { | 114 | func GetMetadataForAllEntities() map[string]Payload { |
115 | return metadata | 115 | return metadata |
116 | } | 116 | } |
117 | 117 | ||
118 | func GetMetadataForEntity(t string) (Payload, bool) { | 118 | func GetMetadataForEntity(t string) (Payload, bool) { |
119 | p, ok := metadata[t] | 119 | p, ok := metadata[t] |
120 | return p, ok | 120 | return p, ok |
121 | } | 121 | } |
122 | 122 | ||
123 | func QueEntityModelUpdate(entityType string, v interface{}) { | 123 | func QueEntityModelUpdate(entityType string, v interface{}) { |
124 | updateQue[entityType], _ = json.Marshal(v) | 124 | updateQue[entityType], _ = json.Marshal(v) |
125 | } | 125 | } |
126 | 126 | ||
127 | func UpdateEntityModels(command string) (total, upd, add int, err error) { | 127 | func UpdateEntityModels(command string) (total, upd, add int, err error) { |
128 | if command != "force" && command != "missing" { | 128 | if command != "force" && command != "missing" { |
129 | return total, 0, 0, errors.New("webutility: unknown command: " + command) | 129 | return total, 0, 0, errors.New("webutility: unknown command: " + command) |
130 | } | 130 | } |
131 | 131 | ||
132 | if !inited { | 132 | if !inited { |
133 | return 0, 0, 0, errors.New("webutility: metadata not initialized but update was tried.") | 133 | return 0, 0, 0, errors.New("webutility: metadata not initialized but update was tried.") |
134 | } | 134 | } |
135 | 135 | ||
136 | total = len(updateQue) | 136 | total = len(updateQue) |
137 | 137 | ||
138 | toUpdate := make([]string, 0) | 138 | toUpdate := make([]string, 0) |
139 | toAdd := make([]string, 0) | 139 | toAdd := make([]string, 0) |
140 | 140 | ||
141 | for k, _ := range updateQue { | 141 | for k, _ := range updateQue { |
142 | if _, exists := metadata[k]; exists { | 142 | if _, exists := metadata[k]; exists { |
143 | if command == "force" { | 143 | if command == "force" { |
144 | toUpdate = append(toUpdate, k) | 144 | toUpdate = append(toUpdate, k) |
145 | } | 145 | } |
146 | } else { | 146 | } else { |
147 | toAdd = append(toAdd, k) | 147 | toAdd = append(toAdd, k) |
148 | } | 148 | } |
149 | } | 149 | } |
150 | 150 | ||
151 | var uStmt *sql.Stmt | 151 | var uStmt *sql.Stmt |
152 | if driver == "ora" { | 152 | if driver == "ora" { |
153 | uStmt, err = metadataDB.Prepare("update entities set entity_model = :1 where entity_type = :2") | 153 | uStmt, err = metadataDB.Prepare("update entities set entity_model = :1 where entity_type = :2") |
154 | if err != nil { | 154 | if err != nil { |
155 | logger.Trace(err.Error()) | ||
155 | return | 156 | return |
156 | } | 157 | } |
157 | } else if driver == "mysql" { | 158 | } else if driver == "mysql" { |
158 | uStmt, err = metadataDB.Prepare("update entities set entity_model = ? where entity_type = ?") | 159 | uStmt, err = metadataDB.Prepare("update entities set entity_model = ? where entity_type = ?") |
159 | if err != nil { | 160 | if err != nil { |
161 | logger.Trace(err.Error()) | ||
160 | return | 162 | return |
161 | } | 163 | } |
162 | } | 164 | } |
163 | for _, k := range toUpdate { | 165 | for _, k := range toUpdate { |
164 | _, err = uStmt.Exec(string(updateQue[k]), k) | 166 | _, err = uStmt.Exec(string(updateQue[k]), k) |
165 | if err != nil { | 167 | if err != nil { |
168 | logger.Trace(err.Error()) | ||
166 | return | 169 | return |
167 | } | 170 | } |
168 | upd++ | 171 | upd++ |
169 | } | 172 | } |
170 | 173 | ||
171 | blankPayload, _ := json.Marshal(Payload{}) | 174 | blankPayload, _ := json.Marshal(Payload{}) |
172 | var iStmt *sql.Stmt | 175 | var iStmt *sql.Stmt |
173 | if driver == "ora" { | 176 | if driver == "ora" { |
174 | iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(:1, :2, :3, :4)") | 177 | iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(:1, :2, :3, :4)") |
175 | if err != nil { | 178 | if err != nil { |
179 | logger.Trace(err.Error()) | ||
176 | return | 180 | return |
177 | } | 181 | } |
178 | } else if driver == "mysql" { | 182 | } else if driver == "mysql" { |
179 | iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(?, ?, ?, ?)") | 183 | iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(?, ?, ?, ?)") |
180 | if err != nil { | 184 | if err != nil { |
185 | logger.Trace(err.Error()) | ||
181 | return | 186 | return |
182 | } | 187 | } |
183 | } | 188 | } |
184 | for _, k := range toAdd { | 189 | for _, k := range toAdd { |
185 | _, err = iStmt.Exec(activeProject, string(blankPayload), k, string(updateQue[k])) | 190 | _, err = iStmt.Exec(activeProject, string(blankPayload), k, string(updateQue[k])) |
186 | if err != nil { | 191 | if err != nil { |
192 | logger.Trace(err.Error()) | ||
187 | return | 193 | return |
188 | } | 194 | } |
189 | metadata[k] = Payload{} | 195 | metadata[k] = Payload{} |
190 | add++ | 196 | add++ |
191 | } | 197 | } |
192 | 198 | ||
193 | return total, upd, add, nil | 199 | return total, upd, add, nil |
194 | } | 200 | } |
195 | 201 | ||
196 | func initMetadata(project string) error { | 202 | func initMetadata(project string) error { |
197 | rows, err := metadataDB.Query(`select | 203 | rows, err := metadataDB.Query(`select |
198 | entity_type, | 204 | entity_type, |
199 | metadata | 205 | metadata |
200 | from entities | 206 | from entities |
201 | where projekat = ` + fmt.Sprintf("'%s'", project)) | 207 | where projekat = ` + fmt.Sprintf("'%s'", project)) |
202 | if err != nil { | 208 | if err != nil { |
203 | return err | 209 | return err |
204 | } | 210 | } |
205 | defer rows.Close() | 211 | defer rows.Close() |
206 | 212 | ||
207 | if len(metadata) > 0 { | 213 | if len(metadata) > 0 { |
208 | metadata = nil | 214 | metadata = nil |
209 | } | 215 | } |
210 | metadata = make(map[string]Payload) | 216 | metadata = make(map[string]Payload) |
211 | for rows.Next() { | 217 | for rows.Next() { |
212 | var name, load string | 218 | var name, load string |
213 | rows.Scan(&name, &load) | 219 | rows.Scan(&name, &load) |
214 | 220 | ||
215 | p := Payload{} | 221 | p := Payload{} |
216 | err := json.Unmarshal([]byte(load), &p) | 222 | err := json.Unmarshal([]byte(load), &p) |
217 | if err != nil { | 223 | if err != nil { |
218 | logger.Log("webutility: couldn't init: '%s' metadata: %s:\n%s\n", name, err.Error(), load) | 224 | logger.Log("webutility: couldn't init: '%s' metadata: %s:\n%s\n", name, err.Error(), load) |
219 | } else { | 225 | } else { |
220 | metadata[name] = p | 226 | metadata[name] = p |
221 | } | 227 | } |
222 | } | 228 | } |
223 | 229 | ||
224 | return nil | 230 | return nil |
225 | } | 231 | } |
226 | 232 | ||
227 | func hotload(n int) { | 233 | func hotload(n int) { |
228 | entityScan := make(map[string]int64) | 234 | entityScan := make(map[string]int64) |
229 | firstCheck := true | 235 | firstCheck := true |
230 | for { | 236 | for { |
231 | time.Sleep(time.Duration(n) * time.Second) | 237 | time.Sleep(time.Duration(n) * time.Second) |
232 | rows, err := metadataDB.Query(`select | 238 | rows, err := metadataDB.Query(`select |
233 | ora_rowscn, | 239 | ora_rowscn, |
234 | entity_type | 240 | entity_type |
235 | from entities where projekat = ` + fmt.Sprintf("'%s'", activeProject)) | 241 | from entities where projekat = ` + fmt.Sprintf("'%s'", activeProject)) |
236 | if err != nil { | 242 | if err != nil { |
237 | logger.Log("webutility: hotload failed: %v\n", err) | 243 | logger.Log("webutility: hotload failed: %v\n", err) |
238 | time.Sleep(time.Duration(n) * time.Second) | 244 | time.Sleep(time.Duration(n) * time.Second) |
239 | continue | 245 | continue |
240 | } | 246 | } |
241 | 247 | ||
242 | var toRefresh []string | 248 | var toRefresh []string |
243 | for rows.Next() { | 249 | for rows.Next() { |
244 | var scanID int64 | 250 | var scanID int64 |
245 | var entity string | 251 | var entity string |
246 | rows.Scan(&scanID, &entity) | 252 | rows.Scan(&scanID, &entity) |
247 | oldID, ok := entityScan[entity] | 253 | oldID, ok := entityScan[entity] |
248 | if !ok || oldID != scanID { | 254 | if !ok || oldID != scanID { |
249 | entityScan[entity] = scanID | 255 | entityScan[entity] = scanID |
250 | toRefresh = append(toRefresh, entity) | 256 | toRefresh = append(toRefresh, entity) |
251 | } | 257 | } |
252 | } | 258 | } |
253 | rows.Close() | 259 | rows.Close() |
254 | 260 | ||
255 | if rows.Err() != nil { | 261 | if rows.Err() != nil { |
256 | logger.Log("webutility: hotload rset error: %v\n", rows.Err()) | 262 | logger.Log("webutility: hotload rset error: %v\n", rows.Err()) |
257 | time.Sleep(time.Duration(n) * time.Second) | 263 | time.Sleep(time.Duration(n) * time.Second) |
258 | continue | 264 | continue |
259 | } | 265 | } |
260 | 266 | ||
261 | if len(toRefresh) > 0 && !firstCheck { | 267 | if len(toRefresh) > 0 && !firstCheck { |
262 | mu.Lock() | 268 | mu.Lock() |
263 | refreshMetadata(toRefresh) | 269 | refreshMetadata(toRefresh) |
264 | mu.Unlock() | 270 | mu.Unlock() |
265 | } | 271 | } |
266 | if firstCheck { | 272 | if firstCheck { |
267 | firstCheck = false | 273 | firstCheck = false |
268 | } | 274 | } |
269 | } | 275 | } |
270 | } | 276 | } |
271 | 277 | ||
272 | func refreshMetadata(entities []string) { | 278 | func refreshMetadata(entities []string) { |
273 | for _, e := range entities { | 279 | for _, e := range entities { |
274 | fmt.Printf("refreshing %s\n", e) | 280 | fmt.Printf("refreshing %s\n", e) |
275 | rows, err := metadataDB.Query(`select | 281 | rows, err := metadataDB.Query(`select |
276 | metadata | 282 | metadata |
277 | from entities | 283 | from entities |
278 | where projekat = ` + fmt.Sprintf("'%s'", activeProject) + | 284 | where projekat = ` + fmt.Sprintf("'%s'", activeProject) + |
279 | ` and entity_type = ` + fmt.Sprintf("'%s'", e)) | 285 | ` and entity_type = ` + fmt.Sprintf("'%s'", e)) |
280 | 286 | ||
281 | if err != nil { | 287 | if err != nil { |
282 | logger.Log("webutility: refresh: prep: %v\n", err) | 288 | logger.Log("webutility: refresh: prep: %v\n", err) |
283 | rows.Close() | 289 | rows.Close() |
284 | continue | 290 | continue |
285 | } | 291 | } |
286 | 292 | ||
287 | for rows.Next() { | 293 | for rows.Next() { |
288 | var load string | 294 | var load string |
289 | rows.Scan(&load) | 295 | rows.Scan(&load) |
290 | p := Payload{} | 296 | p := Payload{} |
291 | err := json.Unmarshal([]byte(load), &p) | 297 | err := json.Unmarshal([]byte(load), &p) |
292 | if err != nil { | 298 | if err != nil { |
293 | logger.Log("webutility: couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), load) | 299 | logger.Log("webutility: couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), load) |
294 | } else { | 300 | } else { |
295 | metadata[e] = p | 301 | metadata[e] = p |
296 | } | 302 | } |
297 | } | 303 | } |
298 | rows.Close() | 304 | rows.Close() |
299 | } | 305 | } |
300 | } | 306 | } |
301 | 307 | ||
302 | /* | 308 | /* |
303 | func ModifyMetadataForEntity(entityType string, p *Payload) error { | 309 | func ModifyMetadataForEntity(entityType string, p *Payload) error { |
304 | md, err := json.Marshal(*p) | 310 | md, err := json.Marshal(*p) |
305 | if err != nil { | 311 | if err != nil { |
306 | return err | 312 | return err |
307 | } | 313 | } |
308 | 314 | ||
309 | mu.Lock() | 315 | mu.Lock() |
310 | defer mu.Unlock() | 316 | defer mu.Unlock() |
311 | _, err = metadataDB.PrepAndExe(`update entities set | 317 | _, err = metadataDB.PrepAndExe(`update entities set |
312 | metadata = :1 | 318 | metadata = :1 |
313 | where projekat = :2 | 319 | where projekat = :2 |
314 | and entity_type = :3`, | 320 | and entity_type = :3`, |
315 | string(md), | 321 | string(md), |
316 | activeProject, | 322 | activeProject, |
317 | entityType) | 323 | entityType) |
318 | if err != nil { | 324 | if err != nil { |
319 | return err | 325 | return err |
320 | } | 326 | } |
321 | return nil | 327 | return nil |
322 | } | 328 | } |
323 | 329 | ||
324 | func DeleteEntityModel(entityType string) error { | 330 | func DeleteEntityModel(entityType string) error { |
325 | _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType) | 331 | _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType) |
326 | if err == nil { | 332 | if err == nil { |
327 | mu.Lock() | 333 | mu.Lock() |
328 | delete(metadata, entityType) | 334 | delete(metadata, entityType) |
329 | mu.Unlock() | 335 | mu.Unlock() |
330 | } | 336 | } |
331 | return err | 337 | return err |
332 | } | 338 | } |
333 | */ | 339 | */ |
334 | 340 |