Commit 31a4e13027906b008bb3cb3469d1f09738875288

Authored by Marko Tikvić
1 parent 685dd6223d
Exists in master

started work on pagination

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