Blame view

payload.go 9.71 KB
ea858b8a7   Marko Tikvić   refactoring
1
  package webutility
64041a2ea   Marko Tikvić   first commit
2
3
  
  import (
79071a5d4   Marko Tikvić   Using database/sq...
4
  	"database/sql"
8dbe745c3   Marko Tikvić   merged tables uti...
5
  	"encoding/json"
f84e7607d   Marko Tikvić   added dictionary;...
6
  	"errors"
66478e04f   Marko Tikvić   payload now retur...
7
  	"fmt"
8dbe745c3   Marko Tikvić   merged tables uti...
8
  	"io"
d2ddf82ef   Marko Tikvić   started on new rbac
9
  	"net/http"
68e590a60   Marko Tikvić   load metadata fro...
10
  	"strings"
17a4d0447   Marko Tikvić   mutex lock for pa...
11
  	"sync"
2ea67927f   Marko Tikvić   added support for...
12
  	"time"
1b7dfab73   Marko Tikvić   Payload changed t...
13
14
  
  	"git.to-net.rs/marko.tikvic/gologger"
64041a2ea   Marko Tikvić   first commit
15
  )
2ea67927f   Marko Tikvić   added support for...
16
  var (
3fffcb954   Marko Tikvić   removed old http API
17
18
  	mu       = &sync.Mutex{}
  	metadata = make(map[string]Payload)
67337ffa8   Marko Tikvić   payload editing
19
  	updateQue = make(map[string][]byte)
2ea67927f   Marko Tikvić   added support for...
20

79071a5d4   Marko Tikvić   Using database/sq...
21
  	metadataDB    *sql.DB
2ea67927f   Marko Tikvić   added support for...
22
  	activeProject string
18fcd6d6b   Marko Tikvić   merged with util ...
23
24
25
  	inited     bool
  	metaDriver string
  	logger     *gologger.Logger
2ea67927f   Marko Tikvić   added support for...
26
  )
8dbe745c3   Marko Tikvić   merged tables uti...
27

707782344   Marko Tikvić   lint; vet
28
  // LangMap ...
64041a2ea   Marko Tikvić   first commit
29
  type LangMap map[string]map[string]string
707782344   Marko Tikvić   lint; vet
30
  // Field ...
64041a2ea   Marko Tikvić   first commit
31
  type Field struct {
d2ddf82ef   Marko Tikvić   started on new rbac
32
33
34
35
  	Parameter string `json:"param"`
  	Type      string `json:"type"`
  	Visible   bool   `json:"visible"`
  	Editable  bool   `json:"editable"`
64041a2ea   Marko Tikvić   first commit
36
  }
707782344   Marko Tikvić   lint; vet
37
  // CorrelationField ...
8dbe745c3   Marko Tikvić   merged tables uti...
38
39
40
41
42
  type CorrelationField struct {
  	Result   string   `json:"result"`
  	Elements []string `json:"elements"`
  	Type     string   `json:"type"`
  }
707782344   Marko Tikvić   lint; vet
43
  // Translation ...
8dbe745c3   Marko Tikvić   merged tables uti...
44
  type Translation struct {
ecec68b18   Marko Tikvić   updated todo list
45
  	Language     string            `json:"language"`
8dbe745c3   Marko Tikvić   merged tables uti...
46
  	FieldsLabels map[string]string `json:"fieldsLabels"`
64041a2ea   Marko Tikvić   first commit
47
  }
707782344   Marko Tikvić   lint; vet
48
  // PaginationLinks ...
31a4e1302   Marko Tikvić   started work on p...
49
50
51
52
53
54
  type PaginationLinks struct {
  	Base string `json:"base"`
  	Next string `json:"next"`
  	Prev string `json:"prev"`
  	Self string `json:"self"`
  }
707782344   Marko Tikvić   lint; vet
55
  // PaginationParameters ...
31a4e1302   Marko Tikvić   started work on p...
56
  type PaginationParameters struct {
368c7f87b   Marko Tikvić   pagination work
57
  	URL    string `json:"-"`
31a4e1302   Marko Tikvić   started work on p...
58
59
60
61
62
  	Offset int64  `json:"offset"`
  	Limit  int64  `json:"limit"`
  	SortBy string `json:"sortBy"`
  	Order  string `json:"order"`
  }
707782344   Marko Tikvić   lint; vet
63
  // GetPaginationParameters ...
368c7f87b   Marko Tikvić   pagination work
64
65
66
67
68
69
70
71
72
  // TODO(marko)
  func GetPaginationParameters(req *http.Request) (p PaginationParameters) {
  	return p
  }
  
  // TODO(marko)
  func (p *PaginationParameters) paginationLinks() (links PaginationLinks) {
  	return links
  }
707782344   Marko Tikvić   lint; vet
73
  // Payload ...
4a51e54d7   Marko Tikvić   simplified
74
  type Payload struct {
d2ddf82ef   Marko Tikvić   started on new rbac
75
76
77
78
  	Method       string             `json:"method"`
  	Params       map[string]string  `json:"params"`
  	Lang         []Translation      `json:"lang"`
  	Fields       []Field            `json:"fields"`
4a51e54d7   Marko Tikvić   simplified
79
  	Correlations []CorrelationField `json:"correlationFields"`
707782344   Marko Tikvić   lint; vet
80
  	IDField      string             `json:"idField"`
e1fbb41f9   Marko Tikvić   added comments
81

31a4e1302   Marko Tikvić   started work on p...
82
  	// Pagination
368c7f87b   Marko Tikvić   pagination work
83
84
85
  	Count int64           `json:"count"`
  	Total int64           `json:"total"`
  	Links PaginationLinks `json:"_links"`
31a4e1302   Marko Tikvić   started work on p...
86
87
  
  	// Data holds JSON payload. It can't be used for itteration.
d2ddf82ef   Marko Tikvić   started on new rbac
88
  	Data interface{} `json:"data"`
4a51e54d7   Marko Tikvić   simplified
89
  }
8a070abe2   Marko Tikvić   improved
90
91
92
93
94
95
96
  func (p *Payload) addLang(code string, labels map[string]string) {
  	t := Translation{
  		Language:     code,
  		FieldsLabels: labels,
  	}
  	p.Lang = append(p.Lang, t)
  }
707782344   Marko Tikvić   lint; vet
97
  // SetData ...
ad8e9dd2a   Marko Tikvić   added middleware ...
98
99
100
  func (p *Payload) SetData(data interface{}) {
  	p.Data = data
  }
707782344   Marko Tikvić   lint; vet
101
  // SetPaginationInfo ...
368c7f87b   Marko Tikvić   pagination work
102
  func (p *Payload) SetPaginationInfo(count, total int64, params PaginationParameters) {
31a4e1302   Marko Tikvić   started work on p...
103
104
  	p.Count = count
  	p.Total = total
368c7f87b   Marko Tikvić   pagination work
105
  	p.Links = params.paginationLinks()
31a4e1302   Marko Tikvić   started work on p...
106
  }
368c7f87b   Marko Tikvić   pagination work
107
108
109
110
111
  // NewPayload returs a payload sceleton for entity described with key.
  func NewPayload(r *http.Request, key string) Payload {
  	p := metadata[key]
  	p.Method = r.Method + " " + r.RequestURI
  	return p
79071a5d4   Marko Tikvić   Using database/sq...
112
113
114
115
116
117
118
  }
  
  // DecodeJSON decodes JSON data from r to v.
  // Returns an error if it fails.
  func DecodeJSON(r io.Reader, v interface{}) error {
  	return json.NewDecoder(r).Decode(v)
  }
f84e7607d   Marko Tikvić   added dictionary;...
119
120
  // InitPayloadsMetadata loads all payloads' information into 'metadata' variable.
  func InitPayloadsMetadata(drv string, db *sql.DB, project string) error {
1b7dfab73   Marko Tikvić   Payload changed t...
121
  	var err error
f84e7607d   Marko Tikvić   added dictionary;...
122
  	if drv != "ora" && drv != "mysql" {
1b7dfab73   Marko Tikvić   Payload changed t...
123
124
  		err = errors.New("driver not supported")
  		return err
f84e7607d   Marko Tikvić   added dictionary;...
125
  	}
1b7dfab73   Marko Tikvić   Payload changed t...
126

18fcd6d6b   Marko Tikvić   merged with util ...
127
  	metaDriver = drv
2ea67927f   Marko Tikvić   added support for...
128
129
  	metadataDB = db
  	activeProject = project
62a69beda   Marko Tikvić   Init/Reload Table...
130

1b7dfab73   Marko Tikvić   Payload changed t...
131
132
133
134
135
  	logger, err = gologger.New("metadata", gologger.MaxLogSize100KB)
  	if err != nil {
  		fmt.Printf("webutility: %s
  ", err.Error())
  	}
2ea67927f   Marko Tikvić   added support for...
136
137
  	mu.Lock()
  	defer mu.Unlock()
1b7dfab73   Marko Tikvić   Payload changed t...
138
  	err = initMetadata(project)
d66628295   Marko Tikvić   cleaned up
139
140
141
  	if err != nil {
  		return err
  	}
2ea67927f   Marko Tikvić   added support for...
142
143
144
145
  	inited = true
  
  	return nil
  }
707782344   Marko Tikvić   lint; vet
146
  // EnableHotloading ...
61efd58cd   Marko Tikvić   Put hotload enabl...
147
148
149
150
151
  func EnableHotloading(interval int) {
  	if interval > 0 {
  		go hotload(interval)
  	}
  }
707782344   Marko Tikvić   lint; vet
152
  // GetMetadataForAllEntities ...
67337ffa8   Marko Tikvić   payload editing
153
154
155
  func GetMetadataForAllEntities() map[string]Payload {
  	return metadata
  }
707782344   Marko Tikvić   lint; vet
156
  // GetMetadataForEntity ...
67337ffa8   Marko Tikvić   payload editing
157
158
159
160
  func GetMetadataForEntity(t string) (Payload, bool) {
  	p, ok := metadata[t]
  	return p, ok
  }
707782344   Marko Tikvić   lint; vet
161
  // QueEntityModelUpdate ...
67337ffa8   Marko Tikvić   payload editing
162
163
164
  func QueEntityModelUpdate(entityType string, v interface{}) {
  	updateQue[entityType], _ = json.Marshal(v)
  }
707782344   Marko Tikvić   lint; vet
165
  // UpdateEntityModels ...
63b2ae620   Marko Tikvić   renamed files
166
167
168
169
170
171
  func UpdateEntityModels(command string) (total, upd, add int, err error) {
  	if command != "force" && command != "missing" {
  		return total, 0, 0, errors.New("webutility: unknown command: " + command)
  	}
  
  	if !inited {
707782344   Marko Tikvić   lint; vet
172
  		return 0, 0, 0, errors.New("webutility: metadata not initialized but update was tried")
63b2ae620   Marko Tikvić   renamed files
173
174
175
176
177
178
  	}
  
  	total = len(updateQue)
  
  	toUpdate := make([]string, 0)
  	toAdd := make([]string, 0)
707782344   Marko Tikvić   lint; vet
179
  	for k := range updateQue {
63b2ae620   Marko Tikvić   renamed files
180
181
182
183
184
185
186
187
188
189
  		if _, exists := metadata[k]; exists {
  			if command == "force" {
  				toUpdate = append(toUpdate, k)
  			}
  		} else {
  			toAdd = append(toAdd, k)
  		}
  	}
  
  	var uStmt *sql.Stmt
18fcd6d6b   Marko Tikvić   merged with util ...
190
  	if metaDriver == "ora" {
63b2ae620   Marko Tikvić   renamed files
191
192
  		uStmt, err = metadataDB.Prepare("update entities set entity_model = :1 where entity_type = :2")
  		if err != nil {
685dd6223   Marko Tikvić   added error loggi...
193
  			logger.Trace(err.Error())
63b2ae620   Marko Tikvić   renamed files
194
195
  			return
  		}
18fcd6d6b   Marko Tikvić   merged with util ...
196
  	} else if metaDriver == "mysql" {
63b2ae620   Marko Tikvić   renamed files
197
198
  		uStmt, err = metadataDB.Prepare("update entities set entity_model = ? where entity_type = ?")
  		if err != nil {
685dd6223   Marko Tikvić   added error loggi...
199
  			logger.Trace(err.Error())
63b2ae620   Marko Tikvić   renamed files
200
201
202
203
  			return
  		}
  	}
  	for _, k := range toUpdate {
63b2ae620   Marko Tikvić   renamed files
204
205
  		_, err = uStmt.Exec(string(updateQue[k]), k)
  		if err != nil {
685dd6223   Marko Tikvić   added error loggi...
206
  			logger.Trace(err.Error())
63b2ae620   Marko Tikvić   renamed files
207
208
209
210
211
212
213
  			return
  		}
  		upd++
  	}
  
  	blankPayload, _ := json.Marshal(Payload{})
  	var iStmt *sql.Stmt
18fcd6d6b   Marko Tikvić   merged with util ...
214
  	if metaDriver == "ora" {
63b2ae620   Marko Tikvić   renamed files
215
216
  		iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(:1, :2, :3, :4)")
  		if err != nil {
685dd6223   Marko Tikvić   added error loggi...
217
  			logger.Trace(err.Error())
63b2ae620   Marko Tikvić   renamed files
218
219
  			return
  		}
18fcd6d6b   Marko Tikvić   merged with util ...
220
  	} else if metaDriver == "mysql" {
63b2ae620   Marko Tikvić   renamed files
221
222
  		iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(?, ?, ?, ?)")
  		if err != nil {
685dd6223   Marko Tikvić   added error loggi...
223
  			logger.Trace(err.Error())
63b2ae620   Marko Tikvić   renamed files
224
225
226
227
228
229
  			return
  		}
  	}
  	for _, k := range toAdd {
  		_, err = iStmt.Exec(activeProject, string(blankPayload), k, string(updateQue[k]))
  		if err != nil {
685dd6223   Marko Tikvić   added error loggi...
230
  			logger.Trace(err.Error())
63b2ae620   Marko Tikvić   renamed files
231
232
233
234
235
236
237
238
  			return
  		}
  		metadata[k] = Payload{}
  		add++
  	}
  
  	return total, upd, add, nil
  }
79071a5d4   Marko Tikvić   Using database/sq...
239
240
241
242
243
244
245
246
247
248
  func initMetadata(project string) error {
  	rows, err := metadataDB.Query(`select
  		entity_type,
  		metadata
  		from entities
  		where projekat = ` + fmt.Sprintf("'%s'", project))
  	if err != nil {
  		return err
  	}
  	defer rows.Close()
79071a5d4   Marko Tikvić   Using database/sq...
249
250
251
252
253
254
255
256
257
258
259
  	if len(metadata) > 0 {
  		metadata = nil
  	}
  	metadata = make(map[string]Payload)
  	for rows.Next() {
  		var name, load string
  		rows.Scan(&name, &load)
  
  		p := Payload{}
  		err := json.Unmarshal([]byte(load), &p)
  		if err != nil {
63b2ae620   Marko Tikvić   renamed files
260
261
262
  			logger.Log("webutility: couldn't init: '%s' metadata: %s:
  %s
  ", name, err.Error(), load)
79071a5d4   Marko Tikvić   Using database/sq...
263
  		} else {
79071a5d4   Marko Tikvić   Using database/sq...
264
265
  			metadata[name] = p
  		}
79071a5d4   Marko Tikvić   Using database/sq...
266
  	}
79071a5d4   Marko Tikvić   Using database/sq...
267
268
269
  
  	return nil
  }
707782344   Marko Tikvić   lint; vet
270
  // LoadMetadataFromFile expects file in format:
8a070abe2   Marko Tikvić   improved
271
272
  //
  // [ payload A identifier ]
d9855ed8c   Marko Tikvić   : -> =
273
274
  // key1 = value1
  // key2 = value2
8a070abe2   Marko Tikvić   improved
275
276
  // ...
  // [ payload B identifier ]
d9855ed8c   Marko Tikvić   : -> =
277
278
  // key1 = value1
  // key2 = value2
8a070abe2   Marko Tikvić   improved
279
  // ...
707782344   Marko Tikvić   lint; vet
280
281
  //
  // TODO(marko): Currently supports only one hardcoded language...
68e590a60   Marko Tikvić   load metadata fro...
282
  func LoadMetadataFromFile(path string) error {
18fcd6d6b   Marko Tikvić   merged with util ...
283
  	lines, err := ReadFileLines(path)
68e590a60   Marko Tikvić   load metadata fro...
284
285
286
  	if err != nil {
  		return err
  	}
68e590a60   Marko Tikvić   load metadata fro...
287
288
289
  	metadata = make(map[string]Payload)
  
  	var name string
3ad172fb6   Marko Tikvić   refactored and ma...
290
  	for i, l := range lines {
8a070abe2   Marko Tikvić   improved
291
  		// skip empty lines
f74a6c349   Marko Tikvić   refactored
292
  		if l = strings.TrimSpace(l); len(l) == 0 {
3ad172fb6   Marko Tikvić   refactored and ma...
293
294
  			continue
  		}
18fcd6d6b   Marko Tikvić   merged with util ...
295
  		if IsWrappedWith(l, "[", "]") {
68e590a60   Marko Tikvić   load metadata fro...
296
  			name = strings.Trim(l, "[]")
8a070abe2   Marko Tikvić   improved
297
298
299
  			p := Payload{}
  			p.addLang("sr", make(map[string]string))
  			metadata[name] = p
68e590a60   Marko Tikvić   load metadata fro...
300
301
  			continue
  		}
3ad172fb6   Marko Tikvić   refactored and ma...
302
  		if name == "" {
707782344   Marko Tikvić   lint; vet
303
  			return fmt.Errorf("webutility: LoadMetadataFromFile: error on line %d: [no header] [%s]", i+1, l)
3ad172fb6   Marko Tikvić   refactored and ma...
304
  		}
d9855ed8c   Marko Tikvić   : -> =
305
  		parts := strings.Split(l, "=")
8a070abe2   Marko Tikvić   improved
306
  		if len(parts) != 2 {
707782344   Marko Tikvić   lint; vet
307
  			return fmt.Errorf("webutility: LoadMetadataFromFile: error on line %d: [invalid format] [%s]", i+1, l)
3ad172fb6   Marko Tikvić   refactored and ma...
308
  		}
f74a6c349   Marko Tikvić   refactored
309
310
  		k := strings.TrimSpace(parts[0])
  		v := strings.TrimSpace(parts[1])
3ad172fb6   Marko Tikvić   refactored and ma...
311
312
  		if v != "-" {
  			metadata[name].Lang[0].FieldsLabels[k] = v
68e590a60   Marko Tikvić   load metadata fro...
313
314
315
316
317
  		}
  	}
  
  	return nil
  }
79071a5d4   Marko Tikvić   Using database/sq...
318
319
320
321
322
323
324
325
326
327
  func hotload(n int) {
  	entityScan := make(map[string]int64)
  	firstCheck := true
  	for {
  		time.Sleep(time.Duration(n) * time.Second)
  		rows, err := metadataDB.Query(`select
  			ora_rowscn,
  			entity_type
  			from entities where projekat = ` + fmt.Sprintf("'%s'", activeProject))
  		if err != nil {
f84e7607d   Marko Tikvić   added dictionary;...
328
329
  			logger.Log("webutility: hotload failed: %v
  ", err)
79071a5d4   Marko Tikvić   Using database/sq...
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
  			time.Sleep(time.Duration(n) * time.Second)
  			continue
  		}
  
  		var toRefresh []string
  		for rows.Next() {
  			var scanID int64
  			var entity string
  			rows.Scan(&scanID, &entity)
  			oldID, ok := entityScan[entity]
  			if !ok || oldID != scanID {
  				entityScan[entity] = scanID
  				toRefresh = append(toRefresh, entity)
  			}
  		}
  		rows.Close()
  
  		if rows.Err() != nil {
f84e7607d   Marko Tikvić   added dictionary;...
348
349
  			logger.Log("webutility: hotload rset error: %v
  ", rows.Err())
79071a5d4   Marko Tikvić   Using database/sq...
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
  			time.Sleep(time.Duration(n) * time.Second)
  			continue
  		}
  
  		if len(toRefresh) > 0 && !firstCheck {
  			mu.Lock()
  			refreshMetadata(toRefresh)
  			mu.Unlock()
  		}
  		if firstCheck {
  			firstCheck = false
  		}
  	}
  }
  
  func refreshMetadata(entities []string) {
  	for _, e := range entities {
  		fmt.Printf("refreshing %s
  ", e)
  		rows, err := metadataDB.Query(`select
  			metadata
  			from entities
  			where projekat = ` + fmt.Sprintf("'%s'", activeProject) +
  			` and entity_type = ` + fmt.Sprintf("'%s'", e))
  
  		if err != nil {
  			logger.Log("webutility: refresh: prep: %v
  ", err)
  			rows.Close()
  			continue
  		}
  
  		for rows.Next() {
  			var load string
  			rows.Scan(&load)
  			p := Payload{}
  			err := json.Unmarshal([]byte(load), &p)
  			if err != nil {
f84e7607d   Marko Tikvić   added dictionary;...
388
389
390
  				logger.Log("webutility: couldn't refresh: '%s' metadata: %s
  %s
  ", e, err.Error(), load)
79071a5d4   Marko Tikvić   Using database/sq...
391
392
393
394
395
396
397
  			} else {
  				metadata[e] = p
  			}
  		}
  		rows.Close()
  	}
  }
f84e7607d   Marko Tikvić   added dictionary;...
398
  /*
67337ffa8   Marko Tikvić   payload editing
399
400
  func ModifyMetadataForEntity(entityType string, p *Payload) error {
  	md, err := json.Marshal(*p)
2ea67927f   Marko Tikvić   added support for...
401
402
403
  	if err != nil {
  		return err
  	}
67337ffa8   Marko Tikvić   payload editing
404

d66628295   Marko Tikvić   cleaned up
405
406
  	mu.Lock()
  	defer mu.Unlock()
2ea67927f   Marko Tikvić   added support for...
407
408
409
410
411
412
413
414
415
  	_, err = metadataDB.PrepAndExe(`update entities set
  		metadata = :1
  		where projekat = :2
  		and entity_type = :3`,
  		string(md),
  		activeProject,
  		entityType)
  	if err != nil {
  		return err
d66628295   Marko Tikvić   cleaned up
416
417
418
  	}
  	return nil
  }
67337ffa8   Marko Tikvić   payload editing
419
420
421
422
423
424
425
426
  func DeleteEntityModel(entityType string) error {
  	_, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType)
  	if err == nil {
  		mu.Lock()
  		delete(metadata, entityType)
  		mu.Unlock()
  	}
  	return err
d66628295   Marko Tikvić   cleaned up
427
  }
79071a5d4   Marko Tikvić   Using database/sq...
428
  */