Commit 68e590a604fbe8813dfcc79c42a4f3e10432158e

Authored by Marko Tikvić
1 parent d7e8e0a94e
Exists in master

load metadata from file

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