Commit 28aa66b2f440f87fcc0cbc25d8df9b335a3086df

Authored by Marko Tikvić
1 parent ec4067ed8b
Exists in master

remove ommitempty because KC depends on it

Showing 1 changed file with 6 additions and 6 deletions   Show diff stats
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 "net/http" 8 "net/http"
9 "strings" 9 "strings"
10 "sync" 10 "sync"
11 "time" 11 "time"
12 ) 12 )
13 13
14 var ( 14 var (
15 mu = &sync.Mutex{} 15 mu = &sync.Mutex{}
16 metadata = make(map[string]Payload) 16 metadata = make(map[string]Payload)
17 17
18 updateQue = make(map[string][]byte) 18 updateQue = make(map[string][]byte)
19 19
20 metadataDB *sql.DB 20 metadataDB *sql.DB
21 activeProject string 21 activeProject string
22 22
23 inited bool 23 inited bool
24 metaDriver string 24 metaDriver string
25 ) 25 )
26 26
27 // LangMap ... 27 // LangMap ...
28 type LangMap map[string]map[string]string 28 type LangMap map[string]map[string]string
29 29
30 // Field ... 30 // Field ...
31 type Field struct { 31 type Field struct {
32 Parameter string `json:"param"` 32 Parameter string `json:"param"`
33 Type string `json:"type"` 33 Type string `json:"type"`
34 Visible bool `json:"visible"` 34 Visible bool `json:"visible"`
35 Editable bool `json:"editable"` 35 Editable bool `json:"editable"`
36 } 36 }
37 37
38 // CorrelationField ... 38 // CorrelationField ...
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 // Translation ... 45 // Translation ...
46 type Translation struct { 46 type Translation struct {
47 Language string `json:"language"` 47 Language string `json:"language"`
48 FieldsLabels map[string]string `json:"fieldsLabels"` 48 FieldsLabels map[string]string `json:"fieldsLabels"`
49 } 49 }
50 50
51 // Payload ... 51 // Payload ...
52 type Payload struct { 52 type Payload struct {
53 Method string `json:"method"` 53 Method string `json:"method"`
54 Params map[string]string `json:"params,omitempty"` 54 Params map[string]string `json:"params"`
55 Lang []Translation `json:"lang,omitempty"` 55 Lang []Translation `json:"lang"`
56 Fields []Field `json:"fields,omitempty"` 56 Fields []Field `json:"fields"`
57 Correlations []CorrelationField `json:"correlationFields,omitempty"` 57 Correlations []CorrelationField `json:"correlationFields"`
58 IDField string `json:"idField,omitempty"` 58 IDField string `json:"idField"`
59 59
60 Links PaginationLinks `json:"_links,omitempty"` 60 Links PaginationLinks `json:"_links"`
61 61
62 // Data holds JSON payload. It can't be used for itteration. 62 // Data holds JSON payload. It can't be used for itteration.
63 Data interface{} `json:"data"` 63 Data interface{} `json:"data"`
64 } 64 }
65 65
66 // NewPayload returs a payload sceleton for entity described with key. 66 // NewPayload returs a payload sceleton for entity described with key.
67 func NewPayload(r *http.Request, key string) Payload { 67 func NewPayload(r *http.Request, key string) Payload {
68 p := metadata[key] 68 p := metadata[key]
69 p.Method = r.Method + " " + r.RequestURI 69 p.Method = r.Method + " " + r.RequestURI
70 return p 70 return p
71 } 71 }
72 72
73 func (p *Payload) addLang(code string, labels map[string]string) { 73 func (p *Payload) addLang(code string, labels map[string]string) {
74 t := Translation{ 74 t := Translation{
75 Language: code, 75 Language: code,
76 FieldsLabels: labels, 76 FieldsLabels: labels,
77 } 77 }
78 p.Lang = append(p.Lang, t) 78 p.Lang = append(p.Lang, t)
79 } 79 }
80 80
81 // SetData ... 81 // SetData ...
82 func (p *Payload) SetData(data interface{}) { 82 func (p *Payload) SetData(data interface{}) {
83 p.Data = data 83 p.Data = data
84 } 84 }
85 85
86 // SetPaginationInfo ... 86 // SetPaginationInfo ...
87 func (p *Payload) SetPaginationInfo(count, total int64, params PaginationParams) { 87 func (p *Payload) SetPaginationInfo(count, total int64, params PaginationParams) {
88 p.Links.Count = count 88 p.Links.Count = count
89 p.Links.Total = total 89 p.Links.Total = total
90 p.Links = params.links() 90 p.Links = params.links()
91 } 91 }
92 92
93 // InitPayloadsMetadata loads all payloads' information into 'metadata' variable. 93 // InitPayloadsMetadata loads all payloads' information into 'metadata' variable.
94 func InitPayloadsMetadata(drv string, db *sql.DB, project string) error { 94 func InitPayloadsMetadata(drv string, db *sql.DB, project string) error {
95 var err error 95 var err error
96 if drv != "ora" && drv != "mysql" { 96 if drv != "ora" && drv != "mysql" {
97 err = errors.New("driver not supported") 97 err = errors.New("driver not supported")
98 return err 98 return err
99 } 99 }
100 100
101 metaDriver = drv 101 metaDriver = drv
102 metadataDB = db 102 metadataDB = db
103 activeProject = project 103 activeProject = project
104 104
105 mu.Lock() 105 mu.Lock()
106 defer mu.Unlock() 106 defer mu.Unlock()
107 err = initMetadata(project) 107 err = initMetadata(project)
108 if err != nil { 108 if err != nil {
109 return err 109 return err
110 } 110 }
111 inited = true 111 inited = true
112 112
113 return nil 113 return nil
114 } 114 }
115 115
116 // EnableHotloading ... 116 // EnableHotloading ...
117 func EnableHotloading(interval int) { 117 func EnableHotloading(interval int) {
118 if interval > 0 { 118 if interval > 0 {
119 go hotload(interval) 119 go hotload(interval)
120 } 120 }
121 } 121 }
122 122
123 // GetMetadataForAllEntities ... 123 // GetMetadataForAllEntities ...
124 func GetMetadataForAllEntities() map[string]Payload { 124 func GetMetadataForAllEntities() map[string]Payload {
125 return metadata 125 return metadata
126 } 126 }
127 127
128 // GetMetadataForEntity ... 128 // GetMetadataForEntity ...
129 func GetMetadataForEntity(t string) (Payload, bool) { 129 func GetMetadataForEntity(t string) (Payload, bool) {
130 p, ok := metadata[t] 130 p, ok := metadata[t]
131 return p, ok 131 return p, ok
132 } 132 }
133 133
134 // QueEntityModelUpdate ... 134 // QueEntityModelUpdate ...
135 func QueEntityModelUpdate(entityType string, v interface{}) { 135 func QueEntityModelUpdate(entityType string, v interface{}) {
136 updateQue[entityType], _ = json.Marshal(v) 136 updateQue[entityType], _ = json.Marshal(v)
137 } 137 }
138 138
139 // UpdateEntityModels ... 139 // UpdateEntityModels ...
140 func UpdateEntityModels(command string) (total, upd, add int, err error) { 140 func UpdateEntityModels(command string) (total, upd, add int, err error) {
141 if command != "force" && command != "missing" { 141 if command != "force" && command != "missing" {
142 return total, 0, 0, errors.New("webutility: unknown command: " + command) 142 return total, 0, 0, errors.New("webutility: unknown command: " + command)
143 } 143 }
144 144
145 if !inited { 145 if !inited {
146 return 0, 0, 0, errors.New("webutility: metadata not initialized but update was tried") 146 return 0, 0, 0, errors.New("webutility: metadata not initialized but update was tried")
147 } 147 }
148 148
149 total = len(updateQue) 149 total = len(updateQue)
150 150
151 toUpdate := make([]string, 0) 151 toUpdate := make([]string, 0)
152 toAdd := make([]string, 0) 152 toAdd := make([]string, 0)
153 153
154 for k := range updateQue { 154 for k := range updateQue {
155 if _, exists := metadata[k]; exists { 155 if _, exists := metadata[k]; exists {
156 if command == "force" { 156 if command == "force" {
157 toUpdate = append(toUpdate, k) 157 toUpdate = append(toUpdate, k)
158 } 158 }
159 } else { 159 } else {
160 toAdd = append(toAdd, k) 160 toAdd = append(toAdd, k)
161 } 161 }
162 } 162 }
163 163
164 var uStmt *sql.Stmt 164 var uStmt *sql.Stmt
165 if metaDriver == "ora" { 165 if metaDriver == "ora" {
166 uStmt, err = metadataDB.Prepare("update entities set entity_model = :1 where entity_type = :2") 166 uStmt, err = metadataDB.Prepare("update entities set entity_model = :1 where entity_type = :2")
167 if err != nil { 167 if err != nil {
168 return 168 return
169 } 169 }
170 } else if metaDriver == "mysql" { 170 } else if metaDriver == "mysql" {
171 uStmt, err = metadataDB.Prepare("update entities set entity_model = ? where entity_type = ?") 171 uStmt, err = metadataDB.Prepare("update entities set entity_model = ? where entity_type = ?")
172 if err != nil { 172 if err != nil {
173 return 173 return
174 } 174 }
175 } 175 }
176 for _, k := range toUpdate { 176 for _, k := range toUpdate {
177 _, err = uStmt.Exec(string(updateQue[k]), k) 177 _, err = uStmt.Exec(string(updateQue[k]), k)
178 if err != nil { 178 if err != nil {
179 return 179 return
180 } 180 }
181 upd++ 181 upd++
182 } 182 }
183 183
184 blankPayload, _ := json.Marshal(Payload{}) 184 blankPayload, _ := json.Marshal(Payload{})
185 var iStmt *sql.Stmt 185 var iStmt *sql.Stmt
186 if metaDriver == "ora" { 186 if metaDriver == "ora" {
187 iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(:1, :2, :3, :4)") 187 iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(:1, :2, :3, :4)")
188 if err != nil { 188 if err != nil {
189 return 189 return
190 } 190 }
191 } else if metaDriver == "mysql" { 191 } else if metaDriver == "mysql" {
192 iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(?, ?, ?, ?)") 192 iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(?, ?, ?, ?)")
193 if err != nil { 193 if err != nil {
194 return 194 return
195 } 195 }
196 } 196 }
197 for _, k := range toAdd { 197 for _, k := range toAdd {
198 _, err = iStmt.Exec(activeProject, string(blankPayload), k, string(updateQue[k])) 198 _, err = iStmt.Exec(activeProject, string(blankPayload), k, string(updateQue[k]))
199 if err != nil { 199 if err != nil {
200 return 200 return
201 } 201 }
202 metadata[k] = Payload{} 202 metadata[k] = Payload{}
203 add++ 203 add++
204 } 204 }
205 205
206 return total, upd, add, nil 206 return total, upd, add, nil
207 } 207 }
208 208
209 func initMetadata(project string) error { 209 func initMetadata(project string) error {
210 rows, err := metadataDB.Query(`select 210 rows, err := metadataDB.Query(`select
211 entity_type, 211 entity_type,
212 metadata 212 metadata
213 from entities 213 from entities
214 where projekat = ` + fmt.Sprintf("'%s'", project)) 214 where projekat = ` + fmt.Sprintf("'%s'", project))
215 if err != nil { 215 if err != nil {
216 return err 216 return err
217 } 217 }
218 defer rows.Close() 218 defer rows.Close()
219 219
220 if len(metadata) > 0 { 220 if len(metadata) > 0 {
221 metadata = nil 221 metadata = nil
222 } 222 }
223 metadata = make(map[string]Payload) 223 metadata = make(map[string]Payload)
224 for rows.Next() { 224 for rows.Next() {
225 var name, load string 225 var name, load string
226 rows.Scan(&name, &load) 226 rows.Scan(&name, &load)
227 227
228 p := Payload{} 228 p := Payload{}
229 err := json.Unmarshal([]byte(load), &p) 229 err := json.Unmarshal([]byte(load), &p)
230 if err != nil { 230 if err != nil {
231 fmt.Printf("webutility: couldn't init: '%s' metadata: %s:\n%s\n", name, err.Error(), load) 231 fmt.Printf("webutility: couldn't init: '%s' metadata: %s:\n%s\n", name, err.Error(), load)
232 } else { 232 } else {
233 metadata[name] = p 233 metadata[name] = p
234 } 234 }
235 } 235 }
236 236
237 return nil 237 return nil
238 } 238 }
239 239
240 // LoadMetadataFromFile expects file in format: 240 // LoadMetadataFromFile expects file in format:
241 // 241 //
242 // [ payload A identifier ] 242 // [ payload A identifier ]
243 // key1 = value1 243 // key1 = value1
244 // key2 = value2 244 // key2 = value2
245 // ... 245 // ...
246 // [ payload B identifier ] 246 // [ payload B identifier ]
247 // key1 = value1 247 // key1 = value1
248 // key2 = value2 248 // key2 = value2
249 // ... 249 // ...
250 // 250 //
251 // TODO(marko): Currently supports only one hardcoded language... 251 // TODO(marko): Currently supports only one hardcoded language...
252 func LoadMetadataFromFile(path string) error { 252 func LoadMetadataFromFile(path string) error {
253 lines, err := ReadFileLines(path) 253 lines, err := ReadFileLines(path)
254 if err != nil { 254 if err != nil {
255 return err 255 return err
256 } 256 }
257 257
258 metadata = make(map[string]Payload) 258 metadata = make(map[string]Payload)
259 259
260 var name string 260 var name string
261 for i, l := range lines { 261 for i, l := range lines {
262 // skip empty lines 262 // skip empty lines
263 if l = strings.TrimSpace(l); len(l) == 0 { 263 if l = strings.TrimSpace(l); len(l) == 0 {
264 continue 264 continue
265 } 265 }
266 266
267 if IsWrappedWith(l, "[", "]") { 267 if IsWrappedWith(l, "[", "]") {
268 name = strings.Trim(l, "[]") 268 name = strings.Trim(l, "[]")
269 p := Payload{} 269 p := Payload{}
270 p.addLang("sr", make(map[string]string)) 270 p.addLang("sr", make(map[string]string))
271 metadata[name] = p 271 metadata[name] = p
272 continue 272 continue
273 } 273 }
274 274
275 if name == "" { 275 if name == "" {
276 return fmt.Errorf("webutility: LoadMetadataFromFile: error on line %d: [no header] [%s]", i+1, l) 276 return fmt.Errorf("webutility: LoadMetadataFromFile: error on line %d: [no header] [%s]", i+1, l)
277 } 277 }
278 278
279 parts := strings.Split(l, "=") 279 parts := strings.Split(l, "=")
280 if len(parts) != 2 { 280 if len(parts) != 2 {
281 return fmt.Errorf("webutility: LoadMetadataFromFile: error on line %d: [invalid format] [%s]", i+1, l) 281 return fmt.Errorf("webutility: LoadMetadataFromFile: error on line %d: [invalid format] [%s]", i+1, l)
282 } 282 }
283 283
284 k := strings.TrimSpace(parts[0]) 284 k := strings.TrimSpace(parts[0])
285 v := strings.TrimSpace(parts[1]) 285 v := strings.TrimSpace(parts[1])
286 if v != "-" { 286 if v != "-" {
287 metadata[name].Lang[0].FieldsLabels[k] = v 287 metadata[name].Lang[0].FieldsLabels[k] = v
288 } 288 }
289 } 289 }
290 290
291 return nil 291 return nil
292 } 292 }
293 293
294 func hotload(n int) { 294 func hotload(n int) {
295 entityScan := make(map[string]int64) 295 entityScan := make(map[string]int64)
296 firstCheck := true 296 firstCheck := true
297 for { 297 for {
298 time.Sleep(time.Duration(n) * time.Second) 298 time.Sleep(time.Duration(n) * time.Second)
299 rows, err := metadataDB.Query(`select 299 rows, err := metadataDB.Query(`select
300 ora_rowscn, 300 ora_rowscn,
301 entity_type 301 entity_type
302 from entities where projekat = ` + fmt.Sprintf("'%s'", activeProject)) 302 from entities where projekat = ` + fmt.Sprintf("'%s'", activeProject))
303 if err != nil { 303 if err != nil {
304 fmt.Printf("webutility: hotload failed: %v\n", err) 304 fmt.Printf("webutility: hotload failed: %v\n", err)
305 time.Sleep(time.Duration(n) * time.Second) 305 time.Sleep(time.Duration(n) * time.Second)
306 continue 306 continue
307 } 307 }
308 308
309 var toRefresh []string 309 var toRefresh []string
310 for rows.Next() { 310 for rows.Next() {
311 var scanID int64 311 var scanID int64
312 var entity string 312 var entity string
313 rows.Scan(&scanID, &entity) 313 rows.Scan(&scanID, &entity)
314 oldID, ok := entityScan[entity] 314 oldID, ok := entityScan[entity]
315 if !ok || oldID != scanID { 315 if !ok || oldID != scanID {
316 entityScan[entity] = scanID 316 entityScan[entity] = scanID
317 toRefresh = append(toRefresh, entity) 317 toRefresh = append(toRefresh, entity)
318 } 318 }
319 } 319 }
320 rows.Close() 320 rows.Close()
321 321
322 if rows.Err() != nil { 322 if rows.Err() != nil {
323 fmt.Printf("webutility: hotload rset error: %v\n", rows.Err()) 323 fmt.Printf("webutility: hotload rset error: %v\n", rows.Err())
324 time.Sleep(time.Duration(n) * time.Second) 324 time.Sleep(time.Duration(n) * time.Second)
325 continue 325 continue
326 } 326 }
327 327
328 if len(toRefresh) > 0 && !firstCheck { 328 if len(toRefresh) > 0 && !firstCheck {
329 mu.Lock() 329 mu.Lock()
330 refreshMetadata(toRefresh) 330 refreshMetadata(toRefresh)
331 mu.Unlock() 331 mu.Unlock()
332 } 332 }
333 if firstCheck { 333 if firstCheck {
334 firstCheck = false 334 firstCheck = false
335 } 335 }
336 } 336 }
337 } 337 }
338 338
339 func refreshMetadata(entities []string) { 339 func refreshMetadata(entities []string) {
340 for _, e := range entities { 340 for _, e := range entities {
341 fmt.Printf("refreshing %s\n", e) 341 fmt.Printf("refreshing %s\n", e)
342 rows, err := metadataDB.Query(`select 342 rows, err := metadataDB.Query(`select
343 metadata 343 metadata
344 from entities 344 from entities
345 where projekat = ` + fmt.Sprintf("'%s'", activeProject) + 345 where projekat = ` + fmt.Sprintf("'%s'", activeProject) +
346 ` and entity_type = ` + fmt.Sprintf("'%s'", e)) 346 ` and entity_type = ` + fmt.Sprintf("'%s'", e))
347 347
348 if err != nil { 348 if err != nil {
349 fmt.Printf("webutility: refresh: prep: %v\n", err) 349 fmt.Printf("webutility: refresh: prep: %v\n", err)
350 rows.Close() 350 rows.Close()
351 continue 351 continue
352 } 352 }
353 353
354 for rows.Next() { 354 for rows.Next() {
355 var load string 355 var load string
356 rows.Scan(&load) 356 rows.Scan(&load)
357 p := Payload{} 357 p := Payload{}
358 err := json.Unmarshal([]byte(load), &p) 358 err := json.Unmarshal([]byte(load), &p)
359 if err != nil { 359 if err != nil {
360 fmt.Printf("webutility: couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), load) 360 fmt.Printf("webutility: couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), load)
361 } else { 361 } else {
362 metadata[e] = p 362 metadata[e] = p
363 } 363 }
364 } 364 }
365 rows.Close() 365 rows.Close()
366 } 366 }
367 } 367 }
368 368
369 /* 369 /*
370 func ModifyMetadataForEntity(entityType string, p *Payload) error { 370 func ModifyMetadataForEntity(entityType string, p *Payload) error {
371 md, err := json.Marshal(*p) 371 md, err := json.Marshal(*p)
372 if err != nil { 372 if err != nil {
373 return err 373 return err
374 } 374 }
375 375
376 mu.Lock() 376 mu.Lock()
377 defer mu.Unlock() 377 defer mu.Unlock()
378 _, err = metadataDB.PrepAndExe(`update entities set 378 _, err = metadataDB.PrepAndExe(`update entities set
379 metadata = :1 379 metadata = :1
380 where projekat = :2 380 where projekat = :2
381 and entity_type = :3`, 381 and entity_type = :3`,
382 string(md), 382 string(md),
383 activeProject, 383 activeProject,
384 entityType) 384 entityType)
385 if err != nil { 385 if err != nil {
386 return err 386 return err
387 } 387 }
388 return nil 388 return nil
389 } 389 }
390 390
391 func DeleteEntityModel(entityType string) error { 391 func DeleteEntityModel(entityType string) error {
392 _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType) 392 _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType)
393 if err == nil { 393 if err == nil {
394 mu.Lock() 394 mu.Lock()
395 delete(metadata, entityType) 395 delete(metadata, entityType)
396 mu.Unlock() 396 mu.Unlock()
397 } 397 }
398 return err 398 return err
399 } 399 }
400 */ 400 */
401 401