Commit 087f8fb21c3934f226d485880456fa7db4966c83
1 parent
a205e8f40d
Exists in
master
and in
1 other branch
expanded list config with live graph
Showing
2 changed files
with
55 additions
and
21 deletions
Show diff stats
json_utility.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 | updateQue = make(map[string][]byte) | 19 | updateQue = make(map[string][]byte) |
20 | 20 | ||
21 | metadataDB *sql.DB | 21 | metadataDB *sql.DB |
22 | activeProject string | 22 | activeProject string |
23 | 23 | ||
24 | inited bool | 24 | inited bool |
25 | driver string | 25 | driver string |
26 | ) | 26 | ) |
27 | 27 | ||
28 | var logger *gologger.Logger | 28 | var logger *gologger.Logger |
29 | 29 | ||
30 | func init() { | 30 | func init() { |
31 | var err error | 31 | var err error |
32 | logger, err = gologger.New("webutility", gologger.MaxLogSize100KB) | 32 | logger, err = gologger.New("webutility", gologger.MaxLogSize100KB) |
33 | if err != nil { | 33 | if err != nil { |
34 | fmt.Printf("webutility: %s\n", err.Error()) | 34 | fmt.Printf("webutility: %s\n", err.Error()) |
35 | } | 35 | } |
36 | } | 36 | } |
37 | 37 | ||
38 | type LangMap map[string]map[string]string | 38 | type LangMap map[string]map[string]string |
39 | 39 | ||
40 | type Field struct { | 40 | type Field struct { |
41 | Parameter string `json:"param"` | 41 | Parameter string `json:"param"` |
42 | Type string `json:"type"` | 42 | Type string `json:"type"` |
43 | Visible bool `json:"visible"` | 43 | Visible bool `json:"visible"` |
44 | Editable bool `json:"editable"` | 44 | Editable bool `json:"editable"` |
45 | } | 45 | } |
46 | 46 | ||
47 | type CorrelationField struct { | 47 | type CorrelationField struct { |
48 | Result string `json:"result"` | 48 | Result string `json:"result"` |
49 | Elements []string `json:"elements"` | 49 | Elements []string `json:"elements"` |
50 | Type string `json:"type"` | 50 | Type string `json:"type"` |
51 | } | 51 | } |
52 | 52 | ||
53 | type Translation struct { | 53 | type Translation struct { |
54 | Language string `json:"language"` | 54 | Language string `json:"language"` |
55 | FieldsLabels map[string]string `json:"fieldsLabels"` | 55 | FieldsLabels map[string]string `json:"fieldsLabels"` |
56 | } | 56 | } |
57 | 57 | ||
58 | type Payload struct { | 58 | type Payload struct { |
59 | Method string `json:"method"` | 59 | Method string `json:"method"` |
60 | Params map[string]string `json:"params"` | 60 | Params map[string]string `json:"params"` |
61 | Lang []Translation `json:"lang"` | 61 | Lang []Translation `json:"lang"` |
62 | Fields []Field `json:"fields"` | 62 | Fields []Field `json:"fields"` |
63 | Correlations []CorrelationField `json:"correlationFields"` | 63 | Correlations []CorrelationField `json:"correlationFields"` |
64 | IdField string `json:"idField"` | 64 | IdField string `json:"idField"` |
65 | 65 | ||
66 | // Data holds JSON payload. | 66 | // Data holds JSON payload. |
67 | // It can't be used for itteration. | 67 | // It can't be used for itteration. |
68 | Data interface{} `json:"data"` | 68 | Data interface{} `json:"data"` |
69 | } | 69 | } |
70 | 70 | ||
71 | // NewPayload returs a payload sceleton for entity described with etype. | 71 | // NewPayload returs a payload sceleton for entity described with etype. |
72 | func NewPayload(r *http.Request, etype string) Payload { | 72 | func NewPayload(r *http.Request, etype string) Payload { |
73 | pload := metadata[etype] | 73 | pload := metadata[etype] |
74 | pload.Method = r.Method + " " + r.RequestURI | 74 | pload.Method = r.Method + " " + r.RequestURI |
75 | return pload | 75 | return pload |
76 | } | 76 | } |
77 | 77 | ||
78 | // DecodeJSON decodes JSON data from r to v. | 78 | // DecodeJSON decodes JSON data from r to v. |
79 | // Returns an error if it fails. | 79 | // Returns an error if it fails. |
80 | func DecodeJSON(r io.Reader, v interface{}) error { | 80 | func DecodeJSON(r io.Reader, v interface{}) error { |
81 | return json.NewDecoder(r).Decode(v) | 81 | return json.NewDecoder(r).Decode(v) |
82 | } | 82 | } |
83 | 83 | ||
84 | // InitPayloadsMetadata loads all payloads' information into 'metadata' variable. | 84 | // InitPayloadsMetadata loads all payloads' information into 'metadata' variable. |
85 | func InitPayloadsMetadata(drv string, db *sql.DB, project string) error { | 85 | func InitPayloadsMetadata(drv string, db *sql.DB, project string) error { |
86 | if drv != "ora" && drv != "mysql" { | 86 | if drv != "ora" && drv != "mysql" { |
87 | return errors.New("driver not supported") | 87 | return errors.New("driver not supported") |
88 | } | 88 | } |
89 | driver = drv | 89 | driver = drv |
90 | metadataDB = db | 90 | metadataDB = db |
91 | activeProject = project | 91 | activeProject = project |
92 | 92 | ||
93 | mu.Lock() | 93 | mu.Lock() |
94 | defer mu.Unlock() | 94 | defer mu.Unlock() |
95 | err := initMetadata(project) | 95 | err := initMetadata(project) |
96 | if err != nil { | 96 | if err != nil { |
97 | return err | 97 | return err |
98 | } | 98 | } |
99 | inited = true | 99 | inited = true |
100 | 100 | ||
101 | return nil | 101 | return nil |
102 | } | 102 | } |
103 | 103 | ||
104 | func EnableHotloading(interval int) { | 104 | func EnableHotloading(interval int) { |
105 | if interval > 0 { | 105 | if interval > 0 { |
106 | go hotload(interval) | 106 | go hotload(interval) |
107 | } | 107 | } |
108 | } | 108 | } |
109 | 109 | ||
110 | func GetMetadataForAllEntities() map[string]Payload { | 110 | func GetMetadataForAllEntities() map[string]Payload { |
111 | return metadata | 111 | return metadata |
112 | } | 112 | } |
113 | 113 | ||
114 | func GetMetadataForEntity(t string) (Payload, bool) { | 114 | func GetMetadataForEntity(t string) (Payload, bool) { |
115 | p, ok := metadata[t] | 115 | p, ok := metadata[t] |
116 | return p, ok | 116 | return p, ok |
117 | } | 117 | } |
118 | 118 | ||
119 | func QueEntityModelUpdate(entityType string, v interface{}) { | 119 | func QueEntityModelUpdate(entityType string, v interface{}) { |
120 | updateQue[entityType], _ = json.Marshal(v) | 120 | updateQue[entityType], _ = json.Marshal(v) |
121 | } | 121 | } |
122 | 122 | ||
123 | func initMetadata(project string) error { | 123 | func initMetadata(project string) error { |
124 | rows, err := metadataDB.Query(`select | 124 | rows, err := metadataDB.Query(`select |
125 | entity_type, | 125 | entity_type, |
126 | metadata | 126 | metadata |
127 | from entities | 127 | from entities |
128 | where projekat = ` + fmt.Sprintf("'%s'", project)) | 128 | where projekat = ` + fmt.Sprintf("'%s'", project)) |
129 | if err != nil { | 129 | if err != nil { |
130 | return err | 130 | return err |
131 | } | 131 | } |
132 | defer rows.Close() | 132 | defer rows.Close() |
133 | 133 | ||
134 | count := 0 | 134 | count := 0 |
135 | success := 0 | 135 | success := 0 |
136 | if len(metadata) > 0 { | 136 | if len(metadata) > 0 { |
137 | metadata = nil | 137 | metadata = nil |
138 | } | 138 | } |
139 | metadata = make(map[string]Payload) | 139 | metadata = make(map[string]Payload) |
140 | for rows.Next() { | 140 | for rows.Next() { |
141 | var name, load string | 141 | var name, load string |
142 | rows.Scan(&name, &load) | 142 | rows.Scan(&name, &load) |
143 | 143 | ||
144 | p := Payload{} | 144 | p := Payload{} |
145 | err := json.Unmarshal([]byte(load), &p) | 145 | err := json.Unmarshal([]byte(load), &p) |
146 | if err != nil { | 146 | if err != nil { |
147 | logger.Log("webutility: couldn't init: '%s' metadata: %s\n%s\n", name, err.Error(), load) | 147 | logger.Log("webutility: couldn't init: '%s' metadata: %s\n%s\n", name, err.Error(), load) |
148 | } else { | 148 | } else { |
149 | success++ | 149 | success++ |
150 | metadata[name] = p | 150 | metadata[name] = p |
151 | } | 151 | } |
152 | count++ | 152 | count++ |
153 | } | 153 | } |
154 | perc := float32(success/count) * 100.0 | 154 | perc := float32(success/count) * 100.0 |
155 | logger.Log("webutility: loaded %d/%d (%.1f%%) entities\n", success, count, perc) | 155 | logger.Log("webutility: loaded %d/%d (%.1f%%) entities\n", success, count, perc) |
156 | 156 | ||
157 | return nil | 157 | return nil |
158 | } | 158 | } |
159 | 159 | ||
160 | func hotload(n int) { | 160 | func hotload(n int) { |
161 | entityScan := make(map[string]int64) | 161 | entityScan := make(map[string]int64) |
162 | firstCheck := true | 162 | firstCheck := true |
163 | for { | 163 | for { |
164 | time.Sleep(time.Duration(n) * time.Second) | 164 | time.Sleep(time.Duration(n) * time.Second) |
165 | rows, err := metadataDB.Query(`select | 165 | rows, err := metadataDB.Query(`select |
166 | ora_rowscn, | 166 | ora_rowscn, |
167 | entity_type | 167 | entity_type |
168 | from entities where projekat = ` + fmt.Sprintf("'%s'", activeProject)) | 168 | from entities where projekat = ` + fmt.Sprintf("'%s'", activeProject)) |
169 | if err != nil { | 169 | if err != nil { |
170 | logger.Log("webutility: hotload failed: %v\n", err) | 170 | logger.Log("webutility: hotload failed: %v\n", err) |
171 | time.Sleep(time.Duration(n) * time.Second) | 171 | time.Sleep(time.Duration(n) * time.Second) |
172 | continue | 172 | continue |
173 | } | 173 | } |
174 | 174 | ||
175 | var toRefresh []string | 175 | var toRefresh []string |
176 | for rows.Next() { | 176 | for rows.Next() { |
177 | var scanID int64 | 177 | var scanID int64 |
178 | var entity string | 178 | var entity string |
179 | rows.Scan(&scanID, &entity) | 179 | rows.Scan(&scanID, &entity) |
180 | oldID, ok := entityScan[entity] | 180 | oldID, ok := entityScan[entity] |
181 | if !ok || oldID != scanID { | 181 | if !ok || oldID != scanID { |
182 | entityScan[entity] = scanID | 182 | entityScan[entity] = scanID |
183 | toRefresh = append(toRefresh, entity) | 183 | toRefresh = append(toRefresh, entity) |
184 | } | 184 | } |
185 | } | 185 | } |
186 | rows.Close() | 186 | rows.Close() |
187 | 187 | ||
188 | if rows.Err() != nil { | 188 | if rows.Err() != nil { |
189 | logger.Log("webutility: hotload rset error: %v\n", rows.Err()) | 189 | logger.Log("webutility: hotload rset error: %v\n", rows.Err()) |
190 | time.Sleep(time.Duration(n) * time.Second) | 190 | time.Sleep(time.Duration(n) * time.Second) |
191 | continue | 191 | continue |
192 | } | 192 | } |
193 | 193 | ||
194 | if len(toRefresh) > 0 && !firstCheck { | 194 | if len(toRefresh) > 0 && !firstCheck { |
195 | mu.Lock() | 195 | mu.Lock() |
196 | refreshMetadata(toRefresh) | 196 | refreshMetadata(toRefresh) |
197 | mu.Unlock() | 197 | mu.Unlock() |
198 | } | 198 | } |
199 | if firstCheck { | 199 | if firstCheck { |
200 | firstCheck = false | 200 | firstCheck = false |
201 | } | 201 | } |
202 | } | 202 | } |
203 | } | 203 | } |
204 | 204 | ||
205 | func refreshMetadata(entities []string) { | 205 | func refreshMetadata(entities []string) { |
206 | for _, e := range entities { | 206 | for _, e := range entities { |
207 | fmt.Printf("refreshing %s\n", e) | 207 | fmt.Printf("refreshing %s\n", e) |
208 | rows, err := metadataDB.Query(`select | 208 | rows, err := metadataDB.Query(`select |
209 | metadata | 209 | metadata |
210 | from entities | 210 | from entities |
211 | where projekat = ` + fmt.Sprintf("'%s'", activeProject) + | 211 | where projekat = ` + fmt.Sprintf("'%s'", activeProject) + |
212 | ` and entity_type = ` + fmt.Sprintf("'%s'", e)) | 212 | ` and entity_type = ` + fmt.Sprintf("'%s'", e)) |
213 | 213 | ||
214 | if err != nil { | 214 | if err != nil { |
215 | logger.Log("webutility: refresh: prep: %v\n", err) | 215 | logger.Log("webutility: refresh: prep: %v\n", err) |
216 | rows.Close() | 216 | rows.Close() |
217 | continue | 217 | continue |
218 | } | 218 | } |
219 | 219 | ||
220 | for rows.Next() { | 220 | for rows.Next() { |
221 | var load string | 221 | var load string |
222 | rows.Scan(&load) | 222 | rows.Scan(&load) |
223 | p := Payload{} | 223 | p := Payload{} |
224 | err := json.Unmarshal([]byte(load), &p) | 224 | err := json.Unmarshal([]byte(load), &p) |
225 | if err != nil { | 225 | if err != nil { |
226 | logger.Log("webutility: couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), load) | 226 | logger.Log("webutility: couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), load) |
227 | } else { | 227 | } else { |
228 | metadata[e] = p | 228 | metadata[e] = p |
229 | } | 229 | } |
230 | } | 230 | } |
231 | rows.Close() | 231 | rows.Close() |
232 | } | 232 | } |
233 | } | 233 | } |
234 | 234 | ||
235 | func UpdateEntityModels(command string) (total, upd, add int, err error) { | 235 | func UpdateEntityModels(command string) (total, upd, add int, err error) { |
236 | if command != "force" && command != "missing" { | 236 | if command != "force" && command != "missing" { |
237 | return total, 0, 0, errors.New("webutility: unknown command: " + command) | 237 | return total, 0, 0, errors.New("webutility: unknown command: " + command) |
238 | } | 238 | } |
239 | 239 | ||
240 | if !inited { | 240 | if !inited { |
241 | return 0, 0, 0, errors.New("webutility: metadata not initialized but update was tried.") | 241 | return 0, 0, 0, errors.New("webutility: metadata not initialized but update was tried.") |
242 | } | 242 | } |
243 | 243 | ||
244 | total = len(updateQue) | 244 | total = len(updateQue) |
245 | 245 | ||
246 | forUpdate := make([]string, 0) | 246 | toUpdate := make([]string, 0) |
247 | forAdd := make([]string, 0) | 247 | toAdd := make([]string, 0) |
248 | 248 | ||
249 | for k, _ := range updateQue { | 249 | for k, _ := range updateQue { |
250 | if _, exists := metadata[k]; exists { | 250 | if _, exists := metadata[k]; exists { |
251 | if command == "force" { | 251 | if command == "force" { |
252 | forUpdate = append(forUpdate, k) | 252 | toUpdate = append(toUpdate, k) |
253 | } | 253 | } |
254 | } else { | 254 | } else { |
255 | forAdd = append(forAdd, k) | 255 | toAdd = append(toAdd, k) |
256 | } | 256 | } |
257 | } | 257 | } |
258 | 258 | ||
259 | var uStmt *sql.Stmt | 259 | var uStmt *sql.Stmt |
260 | if driver == "ora" { | 260 | if driver == "ora" { |
261 | uStmt, err = metadataDB.Prepare("update entities set entity_model = :1 where entity_type = :2") | 261 | uStmt, err = metadataDB.Prepare("update entities set entity_model = :1 where entity_type = :2") |
262 | if err != nil { | 262 | if err != nil { |
263 | return | 263 | return |
264 | } | 264 | } |
265 | } else if driver == "mysql" { | 265 | } else if driver == "mysql" { |
266 | uStmt, err = metadataDB.Prepare("update entities set entity_model = ? where entity_type = ?") | 266 | uStmt, err = metadataDB.Prepare("update entities set entity_model = ? where entity_type = ?") |
267 | if err != nil { | 267 | if err != nil { |
268 | return | 268 | return |
269 | } | 269 | } |
270 | } | 270 | } |
271 | for _, k := range forUpdate { | 271 | for _, k := range toUpdate { |
272 | //fmt.Printf("Updating: %s\n", k) | ||
273 | //fmt.Printf("New model: %s\n", updateQue[k]) | ||
272 | _, err = uStmt.Exec(string(updateQue[k]), k) | 274 | _, err = uStmt.Exec(string(updateQue[k]), k) |
273 | if err != nil { | 275 | if err != nil { |
274 | logger.Log("webutility: %v\n", err) | 276 | logger.Log("webutility: %v\n", err) |
275 | return | 277 | return |
276 | } | 278 | } |
277 | upd++ | 279 | upd++ |
278 | } | 280 | } |
279 | 281 | ||
280 | blankPayload, _ := json.Marshal(Payload{}) | 282 | blankPayload, _ := json.Marshal(Payload{}) |
281 | var iStmt *sql.Stmt | 283 | var iStmt *sql.Stmt |
282 | if driver == "ora" { | 284 | if driver == "ora" { |
283 | iStmt, err = metadataDB.Prepare("INSERT INTO ENTITIES(PROJEKAT, METADATA, ENTITY_TYPE, ENTITY_MODEL) VALUES(:1, :2, :3, :4)") | 285 | iStmt, err = metadataDB.Prepare("INSERT INTO ENTITIES(PROJEKAT, METADATA, ENTITY_TYPE, ENTITY_MODEL) VALUES(:1, :2, :3, :4)") |
284 | if err != nil { | 286 | if err != nil { |
285 | return | 287 | return |
286 | } | 288 | } |
287 | } else if driver == "mysql" { | 289 | } else if driver == "mysql" { |
288 | iStmt, err = metadataDB.Prepare("INSERT INTO ENTITIES(PROJEKAT, METADATA, ENTITY_TYPE, ENTITY_MODEL) VALUES(?, ?, ?, ?)") | 290 | iStmt, err = metadataDB.Prepare("INSERT INTO ENTITIES(PROJEKAT, METADATA, ENTITY_TYPE, ENTITY_MODEL) VALUES(?, ?, ?, ?)") |
289 | if err != nil { | 291 | if err != nil { |
290 | return | 292 | return |
291 | } | 293 | } |
292 | } | 294 | } |
293 | for _, k := range forAdd { | 295 | for _, k := range toAdd { |
294 | _, err = iStmt.Exec(activeProject, string(blankPayload), k, string(updateQue[k])) | 296 | _, err = iStmt.Exec(activeProject, string(blankPayload), k, string(updateQue[k])) |
295 | if err != nil { | 297 | if err != nil { |
296 | logger.Log("webutility: %v\n", err) | 298 | logger.Log("webutility: %v\n", err) |
297 | return | 299 | return |
298 | } | 300 | } |
299 | metadata[k] = Payload{} | 301 | metadata[k] = Payload{} |
300 | add++ | 302 | add++ |
301 | } | 303 | } |
302 | 304 | ||
303 | return total, upd, add, nil | 305 | return total, upd, add, nil |
304 | } | 306 | } |
305 | 307 | ||
306 | /* | 308 | /* |
307 | func ModifyMetadataForEntity(entityType string, p *Payload) error { | 309 | func ModifyMetadataForEntity(entityType string, p *Payload) error { |
308 | md, err := json.Marshal(*p) | 310 | md, err := json.Marshal(*p) |
309 | if err != nil { | 311 | if err != nil { |
310 | return err | 312 | return err |
311 | } | 313 | } |
312 | 314 | ||
313 | mu.Lock() | 315 | mu.Lock() |
314 | defer mu.Unlock() | 316 | defer mu.Unlock() |
315 | _, err = metadataDB.PrepAndExe(`update entities set | 317 | _, err = metadataDB.PrepAndExe(`update entities set |
316 | metadata = :1 | 318 | metadata = :1 |
317 | where projekat = :2 | 319 | where projekat = :2 |
318 | and entity_type = :3`, | 320 | and entity_type = :3`, |
319 | string(md), | 321 | string(md), |
320 | activeProject, | 322 | activeProject, |
321 | entityType) | 323 | entityType) |
322 | if err != nil { | 324 | if err != nil { |
323 | return err | 325 | return err |
324 | } | 326 | } |
325 | return nil | 327 | return nil |
326 | } | 328 | } |
327 | 329 | ||
328 | func DeleteEntityModel(entityType string) error { | 330 | func DeleteEntityModel(entityType string) error { |
329 | _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType) | 331 | _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType) |
330 | if err == nil { | 332 | if err == nil { |
331 | mu.Lock() | 333 | mu.Lock() |
332 | delete(metadata, entityType) | 334 | delete(metadata, entityType) |
333 | mu.Unlock() | 335 | mu.Unlock() |
334 | } | 336 | } |
335 | return err | 337 | return err |
336 | } | 338 | } |
337 | */ | 339 | */ |
338 | 340 |
list_config.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "database/sql" | 4 | "database/sql" |
5 | "fmt" | 5 | "fmt" |
6 | ) | 6 | ) |
7 | 7 | ||
8 | type ListOptions struct { | 8 | type ListOptions struct { |
9 | GlobalFilter bool `json:"globalFilter"` | 9 | GlobalFilter bool `json:"globalFilter"` |
10 | LocalFilters bool `json:"localFilters"` | 10 | LocalFilters bool `json:"localFilters"` |
11 | RemoteFilters bool `json:"remoteFilters"` | 11 | RemoteFilters bool `json:"remoteFilters"` |
12 | Pagination bool `json:"pagination"` | 12 | Pagination bool `json:"pagination"` |
13 | PageSize uint32 `json:"pageSize"` | 13 | PageSize uint32 `json:"pageSize"` |
14 | Pivot bool `json:"pivot"` | 14 | Pivot bool `json:"pivot"` |
15 | Detail bool `json:"detail"` | 15 | Detail bool `json:"detail"` |
16 | Total bool `json:"total"` | 16 | Total bool `json:"total"` |
17 | } | 17 | } |
18 | 18 | ||
19 | type ListFilter struct { | 19 | type ListFilter struct { |
20 | Position uint32 `json:"-"` | 20 | Position uint32 `json:"-"` |
21 | ObjectType string `json:"-"` | 21 | ObjectType string `json:"-"` |
22 | FiltersField string `json:"filtersField"` | 22 | FiltersField string `json:"filtersField"` |
23 | DefaultValues string `json:"defaultValues"` | 23 | DefaultValues string `json:"defaultValues"` |
24 | FiltersType string `json:"filtersType"` | 24 | FiltersType string `json:"filtersType"` |
25 | FiltersLabel string `json:"filtersLabel"` | 25 | FiltersLabel string `json:"filtersLabel"` |
26 | DropdownConfig Dropdown `json:"dropdownConfig"` | 26 | DropdownConfig Dropdown `json:"dropdownConfig"` |
27 | } | 27 | } |
28 | 28 | ||
29 | type Dropdown struct { | 29 | type Dropdown struct { |
30 | ObjectType string `json:"objectType"` | 30 | ObjectType string `json:"objectType"` |
31 | FiltersField string `json:"filtersField"` | 31 | FiltersField string `json:"filtersField"` |
32 | IDField string `json:"idField"` | 32 | IDField string `json:"idField"` |
33 | LabelField string `json:"labelField"` | 33 | LabelField string `json:"labelField"` |
34 | } | 34 | } |
35 | 35 | ||
36 | type ListGraph struct { | 36 | type ListGraph struct { |
37 | ObjectType string `json:"objectType"` | 37 | ObjectType string `json:"objectType"` |
38 | X string `json:"xField"` | 38 | X string `json:"xField"` |
39 | Y string `json:"yField"` | 39 | Y string `json:"yField"` |
40 | GroupField string `json:"groupField"` | 40 | GroupField string `json:"groupField"` |
41 | Label string `json:"label"` | 41 | Label string `json:"label"` |
42 | } | 42 | } |
43 | 43 | ||
44 | type ListActions struct { | 44 | type ListActions struct { |
45 | Create bool `json:"create"` | 45 | Create bool `json:"create"` |
46 | Update bool `json:"update"` | 46 | Update bool `json:"update"` |
47 | Delete bool `json:"delete"` | 47 | Delete bool `json:"delete"` |
48 | Export bool `json:"export"` | 48 | Export bool `json:"export"` |
49 | Print bool `json:"print"` | 49 | Print bool `json:"print"` |
50 | Graph bool `json:"graph"` | 50 | Graph bool `json:"graph"` |
51 | LiveGraph bool `json:"liveGraph"` | ||
51 | } | 52 | } |
52 | 53 | ||
53 | type ListNavNode struct { | 54 | type ListNavNode struct { |
54 | ObjectType string `json:"objectType"` | 55 | ObjectType string `json:"objectType"` |
55 | LabelField string `json:"label"` | 56 | LabelField string `json:"label"` |
56 | Icon string `json:"icon"` | 57 | Icon string `json:"icon"` |
57 | ParentObjectType string `json:"parentObjectType"` | 58 | ParentObjectType string `json:"parentObjectType"` |
58 | ParentIDField string `json:"parentIdField"` | 59 | ParentIDField string `json:"parentIdField"` |
59 | ParentFilterField string `json:"parentFilterField"` | 60 | ParentFilterField string `json:"parentFilterField"` |
60 | } | 61 | } |
61 | 62 | ||
62 | type ListParentNode struct { | 63 | type ListParentNode struct { |
63 | ObjectType string `json:"objectType"` | 64 | ObjectType string `json:"objectType"` |
64 | LabelField string `json:"labelField"` | 65 | LabelField string `json:"labelField"` |
65 | FilterField string `json:"filterField"` | 66 | FilterField string `json:"filterField"` |
66 | } | 67 | } |
67 | 68 | ||
68 | type ListPivot struct { | 69 | type ListPivot struct { |
69 | ObjectType string `json:"objectType"` | 70 | ObjectType string `json:"objectType"` |
70 | GroupField string `json:"groupField"` | 71 | GroupField string `json:"groupField"` |
71 | DistinctField string `json:"distinctField"` | 72 | DistinctField string `json:"distinctField"` |
72 | Value string `json:"valueField"` | 73 | Value string `json:"valueField"` |
73 | } | 74 | } |
74 | 75 | ||
75 | type ListDetails struct { | 76 | type ListDetails struct { |
76 | ObjectType string `json:"objectType"` | 77 | ObjectType string `json:"objectType"` |
77 | ParentObjectType string `json:"parentObjectType"` | 78 | ParentObjectType string `json:"parentObjectType"` |
78 | ParentFilterField string `json:"parentFilterField"` | 79 | ParentFilterField string `json:"parentFilterField"` |
79 | SingleDetail bool `json:"singleDetail"` | 80 | SingleDetail bool `json:"singleDetail"` |
80 | } | 81 | } |
81 | 82 | ||
83 | type ListLiveGraph struct { | ||
84 | ObjectType string `json:"objectType"` | ||
85 | ValueFields string `json:"valueFields"` | ||
86 | LabelFields string `json:"labelFields"` | ||
87 | } | ||
88 | |||
82 | type ListConfig struct { | 89 | type ListConfig struct { |
83 | ObjectType string `json:"objectType"` | 90 | ObjectType string `json:"objectType"` |
84 | Title string `json:"title"` | 91 | Title string `json:"title"` |
85 | LazyLoad bool `json:"lazyLoad"` | 92 | LazyLoad bool `json:"lazyLoad"` |
86 | InlineEdit bool `json:"inlineEdit"` | 93 | InlineEdit bool `json:"inlineEdit"` |
87 | Options ListOptions `json:"options"` | 94 | Options ListOptions `json:"options"` |
88 | Filters []ListFilter `json:"defaultFilters"` | 95 | Filters []ListFilter `json:"defaultFilters"` |
89 | Graphs []ListGraph `json:"graphs"` | 96 | Graphs []ListGraph `json:"graphs"` |
90 | Actions ListActions `json:"actions"` | 97 | Actions ListActions `json:"actions"` |
91 | Parent []ListParentNode `json:"parent"` | 98 | Parent []ListParentNode `json:"parent"` |
92 | Navigation []ListNavNode `json:"navigation"` | 99 | Navigation []ListNavNode `json:"navigation"` |
93 | Pivots []ListPivot `json:"pivots"` | 100 | Pivots []ListPivot `json:"pivots"` |
94 | Details ListDetails `json:"details"` | 101 | Details ListDetails `json:"details"` |
102 | LiveGraph ListLiveGraph `json:"liveGraphs"` | ||
95 | } | 103 | } |
96 | 104 | ||
97 | // GetListConfig returns list configuration for the provided object type for the front-end application | 105 | // GetListConfig returns list configuration for the provided object type for the front-end application |
98 | // or an error if it fails. | 106 | // or an error if it fails. |
99 | func GetListConfig(db *sql.DB, objType string) (ListConfig, error) { | 107 | func GetListConfig(db *sql.DB, objType string) (ListConfig, error) { |
100 | resp := newDefaultList(objType) | 108 | resp := newDefaultList(objType) |
101 | var err error | 109 | var err error |
102 | 110 | ||
103 | err = setListParams(db, &resp, objType) | 111 | err = setListParams(db, &resp, objType) |
104 | resp.Navigation, err = getListNavigation(db, objType) | 112 | resp.Navigation, err = getListNavigation(db, objType) |
105 | resp.Actions, err = getListActions(db, objType) | 113 | resp.Actions, err = getListActions(db, objType) |
106 | resp.Filters, err = getListFilters(db, objType) | 114 | resp.Filters, err = getListFilters(db, objType) |
107 | resp.Options, err = getListOptions(db, objType) | 115 | resp.Options, err = getListOptions(db, objType) |
108 | resp.Parent, err = getListParent(db, objType) | 116 | resp.Parent, err = getListParent(db, objType) |
109 | resp.Graphs, err = getListGraph(db, objType) | 117 | resp.Graphs, err = getListGraph(db, objType) |
110 | resp.Pivots, err = getListPivot(db, objType) | 118 | resp.Pivots, err = getListPivot(db, objType) |
111 | resp.Details, err = getListDetails(db, objType) | 119 | resp.Details, err = getListDetails(db, objType) |
120 | resp.LiveGraph, err = getListLiveGraph(db, objType) | ||
112 | 121 | ||
113 | if err != nil { | 122 | if err != nil { |
114 | return ListConfig{}, err | 123 | return ListConfig{}, err |
115 | } | 124 | } |
116 | 125 | ||
117 | return resp, nil | 126 | return resp, nil |
118 | } | 127 | } |
119 | 128 | ||
120 | // GetListConfigObjectIDField takes in database connection and an object type and it returns the | 129 | // GetListConfigObjectIDField takes in database connection and an object type and it returns the |
121 | // ID field name for the provided object type. | 130 | // ID field name for the provided object type. |
122 | func GetListConfigObjectIDField(db *sql.DB, otype string) string { | 131 | func GetListConfigObjectIDField(db *sql.DB, otype string) string { |
123 | var resp string | 132 | var resp string |
124 | 133 | ||
125 | rows, err := db.Query(`SELECT | 134 | rows, err := db.Query(`SELECT |
126 | ID_FIELD | 135 | ID_FIELD |
127 | FROM LIST_CONFIG_ID_FIELD | 136 | FROM LIST_CONFIG_ID_FIELD |
128 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", otype)) | 137 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", otype)) |
129 | if err != nil { | 138 | if err != nil { |
130 | return "" | 139 | return "" |
131 | } | 140 | } |
132 | defer rows.Close() | 141 | defer rows.Close() |
133 | 142 | ||
134 | if rows.Next() { | 143 | if rows.Next() { |
135 | rows.Scan(&resp) | 144 | rows.Scan(&resp) |
136 | } | 145 | } |
137 | 146 | ||
138 | if rows.Err() != nil { | 147 | if rows.Err() != nil { |
139 | return "" | 148 | return "" |
140 | } | 149 | } |
141 | 150 | ||
142 | return resp | 151 | return resp |
143 | } | 152 | } |
144 | 153 | ||
145 | // newDefaultList returns default configuration for the provided object type. | 154 | // newDefaultList returns default configuration for the provided object type. |
146 | func newDefaultList(objType string) ListConfig { | 155 | func newDefaultList(objType string) ListConfig { |
147 | list := ListConfig{ | 156 | list := ListConfig{ |
148 | ObjectType: objType, | 157 | ObjectType: objType, |
149 | Title: objType, | 158 | Title: objType, |
150 | LazyLoad: false, | 159 | LazyLoad: false, |
151 | Options: ListOptions{ | 160 | Options: ListOptions{ |
152 | GlobalFilter: true, | 161 | GlobalFilter: true, |
153 | LocalFilters: true, | 162 | LocalFilters: true, |
154 | RemoteFilters: false, | 163 | RemoteFilters: false, |
155 | Pagination: true, | 164 | Pagination: true, |
156 | PageSize: 20, | 165 | PageSize: 20, |
157 | }, | 166 | }, |
158 | Filters: nil, | 167 | Filters: nil, |
159 | Actions: ListActions{ | 168 | Actions: ListActions{ |
160 | Create: false, | 169 | Create: false, |
161 | Update: false, | 170 | Update: false, |
162 | Delete: false, | 171 | Delete: false, |
163 | Export: false, | 172 | Export: false, |
164 | Print: false, | 173 | Print: false, |
165 | Graph: false, | 174 | Graph: false, |
175 | LiveGraph: false, | ||
166 | }, | 176 | }, |
167 | Parent: nil, | 177 | Parent: nil, |
168 | Navigation: nil, | 178 | Navigation: nil, |
169 | } | 179 | } |
170 | 180 | ||
171 | return list | 181 | return list |
172 | } | 182 | } |
173 | 183 | ||
174 | // setListParams sets the default parameters of the provided configuration list for the provided object type. | 184 | // setListParams sets the default parameters of the provided configuration list for the provided object type. |
175 | func setListParams(db *sql.DB, list *ListConfig, objType string) error { | 185 | func setListParams(db *sql.DB, list *ListConfig, objType string) error { |
176 | rows, err := db.Query(`SELECT | 186 | rows, err := db.Query(`SELECT |
177 | OBJECT_TYPE, TITLE, LAZY_LOAD, INLINE_EDIT | 187 | OBJECT_TYPE, TITLE, LAZY_LOAD, INLINE_EDIT |
178 | FROM LIST_CONFIG | 188 | FROM LIST_CONFIG |
179 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 189 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
180 | if err != nil { | 190 | if err != nil { |
181 | return err | 191 | return err |
182 | } | 192 | } |
183 | defer rows.Close() | 193 | defer rows.Close() |
184 | if rows.Next() { | 194 | if rows.Next() { |
185 | otype, title := "", "" | 195 | otype, title := "", "" |
186 | lazyLoad, inlineEdit := 0, 0 | 196 | lazyLoad, inlineEdit := 0, 0 |
187 | rows.Scan(&otype, &title, &lazyLoad, &inlineEdit) | 197 | rows.Scan(&otype, &title, &lazyLoad, &inlineEdit) |
188 | 198 | ||
189 | if otype != "" { | 199 | if otype != "" { |
190 | list.ObjectType = otype | 200 | list.ObjectType = otype |
191 | } | 201 | } |
192 | if title != "" { | 202 | if title != "" { |
193 | list.Title = title | 203 | list.Title = title |
194 | } | 204 | } |
195 | list.LazyLoad = lazyLoad != 0 | 205 | list.LazyLoad = lazyLoad != 0 |
196 | list.InlineEdit = inlineEdit != 0 | 206 | list.InlineEdit = inlineEdit != 0 |
197 | } | 207 | } |
198 | if rows.Err() != nil { | 208 | if rows.Err() != nil { |
199 | return rows.Err() | 209 | return rows.Err() |
200 | } | 210 | } |
201 | return nil | 211 | return nil |
202 | } | 212 | } |
203 | 213 | ||
204 | // getListNavigation returns list navigation node slice for the provided objectType. | 214 | // getListNavigation returns list navigation node slice for the provided objectType. |
205 | func getListNavigation(db *sql.DB, listObjType string) ([]ListNavNode, error) { | 215 | func getListNavigation(db *sql.DB, listObjType string) ([]ListNavNode, error) { |
206 | resp := make([]ListNavNode, 0) | 216 | resp := make([]ListNavNode, 0) |
207 | rows, err := db.Query(`SELECT | 217 | rows, err := db.Query(`SELECT |
208 | a.OBJECT_TYPE, a.PARENT_OBJECT_TYPE, a.LABEL, a.ICON, a.PARENT_FILTER_FIELD, b.PARENT_ID_FIELD | 218 | a.OBJECT_TYPE, a.PARENT_OBJECT_TYPE, a.LABEL, a.ICON, a.PARENT_FILTER_FIELD, b.PARENT_ID_FIELD |
209 | FROM LIST_CONFIG_NAVIGATION b | 219 | FROM LIST_CONFIG_NAVIGATION b |
210 | JOIN LIST_CONFIG_CHILD a ON b.PARENT_CHILD_ID = a.PARENT_CHILD_ID | 220 | JOIN LIST_CONFIG_CHILD a ON b.PARENT_CHILD_ID = a.PARENT_CHILD_ID |
211 | WHERE b.LIST_OBJECT_TYPE = ` + fmt.Sprintf("'%s'", listObjType) + | 221 | WHERE b.LIST_OBJECT_TYPE = ` + fmt.Sprintf("'%s'", listObjType) + |
212 | ` ORDER BY b.RB ASC`) | 222 | ` ORDER BY b.RB ASC`) |
213 | if err != nil { | 223 | if err != nil { |
214 | return resp, err | 224 | return resp, err |
215 | } | 225 | } |
216 | defer rows.Close() | 226 | defer rows.Close() |
217 | 227 | ||
218 | var node ListNavNode | 228 | var node ListNavNode |
219 | for rows.Next() { | 229 | for rows.Next() { |
220 | rows.Scan(&node.ObjectType, &node.ParentObjectType, &node.LabelField, &node.Icon, | 230 | rows.Scan(&node.ObjectType, &node.ParentObjectType, &node.LabelField, &node.Icon, |
221 | &node.ParentFilterField, &node.ParentIDField) | 231 | &node.ParentFilterField, &node.ParentIDField) |
222 | resp = append(resp, node) | 232 | resp = append(resp, node) |
223 | } | 233 | } |
224 | if rows.Err() != nil { | 234 | if rows.Err() != nil { |
225 | return nil, rows.Err() | 235 | return nil, rows.Err() |
226 | } | 236 | } |
227 | 237 | ||
228 | return resp, nil | 238 | return resp, nil |
229 | } | 239 | } |
230 | 240 | ||
231 | // getListActions returns list actions for the provided object type. | 241 | // getListActions returns list actions for the provided object type. |
232 | func getListActions(db *sql.DB, objType string) (ListActions, error) { | 242 | func getListActions(db *sql.DB, objType string) (ListActions, error) { |
233 | var resp ListActions | 243 | var resp ListActions |
234 | rows, err := db.Query(`SELECT | 244 | rows, err := db.Query(`SELECT |
235 | ACTION_CREATE, ACTION_UPDATE, ACTION_DELETE, ACTION_EXPORT, | 245 | ACTION_CREATE, ACTION_UPDATE, ACTION_DELETE, ACTION_EXPORT, |
236 | ACTION_PRINT, ACTION_GRAPH | 246 | ACTION_PRINT, ACTION_GRAPH, ACTION_LIVE_GRAPH |
237 | FROM LIST_CONFIG | 247 | FROM LIST_CONFIG |
238 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 248 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
239 | if err != nil { | 249 | if err != nil { |
240 | return ListActions{}, err | 250 | return ListActions{}, err |
241 | } | 251 | } |
242 | defer rows.Close() | 252 | defer rows.Close() |
243 | 253 | ||
244 | var create, update, delete, export, print, graph uint32 | 254 | var create, update, delete, export, print, graph, liveGraph uint32 |
245 | if rows.Next() { | 255 | if rows.Next() { |
246 | rows.Scan(&create, &update, &delete, &export, &print, &graph) | 256 | rows.Scan(&create, &update, &delete, &export, &print, &graph, &liveGraph) |
247 | resp.Create = create != 0 | 257 | resp.Create = create != 0 |
248 | resp.Update = update != 0 | 258 | resp.Update = update != 0 |
249 | resp.Delete = delete != 0 | 259 | resp.Delete = delete != 0 |
250 | resp.Export = export != 0 | 260 | resp.Export = export != 0 |
251 | resp.Print = print != 0 | 261 | resp.Print = print != 0 |
252 | resp.Graph = graph != 0 | 262 | resp.Graph = graph != 0 |
263 | resp.LiveGraph = liveGraph != 0 | ||
253 | } | 264 | } |
254 | if rows.Err() != nil { | 265 | if rows.Err() != nil { |
255 | return ListActions{}, rows.Err() | 266 | return ListActions{}, rows.Err() |
256 | } | 267 | } |
257 | return resp, nil | 268 | return resp, nil |
258 | } | 269 | } |
259 | 270 | ||
260 | // getListFiters returns list filter slice for the provided object type. | 271 | // getListFiters returns list filter slice for the provided object type. |
261 | func getListFilters(db *sql.DB, objType string) ([]ListFilter, error) { | 272 | func getListFilters(db *sql.DB, objType string) ([]ListFilter, error) { |
262 | resp := make([]ListFilter, 0) | 273 | resp := make([]ListFilter, 0) |
263 | filtersFields, err := getFilterFieldsAndPosition(db, objType) | 274 | filtersFields, err := getFilterFieldsAndPosition(db, objType) |
264 | if err != nil { | 275 | if err != nil { |
265 | return nil, err | 276 | return nil, err |
266 | } | 277 | } |
267 | for field, pos := range filtersFields { | 278 | for field, pos := range filtersFields { |
268 | filters, _ := getFiltersByFilterField(db, field) | 279 | filters, _ := getFiltersByFilterField(db, field) |
269 | for _, filter := range filters { | 280 | for _, filter := range filters { |
270 | var f ListFilter | 281 | var f ListFilter |
271 | f.Position = pos | 282 | f.Position = pos |
272 | f.ObjectType = objType | 283 | f.ObjectType = objType |
273 | f.FiltersField = field | 284 | f.FiltersField = field |
274 | f.DefaultValues = filter.DefaultValues | 285 | f.DefaultValues = filter.DefaultValues |
275 | f.FiltersLabel = filter.Label | 286 | f.FiltersLabel = filter.Label |
276 | f.FiltersType = filter.Type | 287 | f.FiltersType = filter.Type |
277 | if filter.Type == "dropdown" { | 288 | if filter.Type == "dropdown" { |
278 | f.DropdownConfig, err = getFilterDropdownConfig(db, field) | 289 | f.DropdownConfig, err = getFilterDropdownConfig(db, field) |
279 | if err != nil { | 290 | if err != nil { |
280 | return nil, err | 291 | return nil, err |
281 | } | 292 | } |
282 | } | 293 | } |
283 | resp = append(resp, f) | 294 | resp = append(resp, f) |
284 | } | 295 | } |
285 | } | 296 | } |
286 | 297 | ||
287 | sortFilters(resp) | 298 | sortFilters(resp) |
288 | 299 | ||
289 | return resp, nil | 300 | return resp, nil |
290 | } | 301 | } |
291 | 302 | ||
292 | // getFilterFieldsAndPosition returns a map of filter fields and their respective position in the menu. | 303 | // getFilterFieldsAndPosition returns a map of filter fields and their respective position in the menu. |
293 | func getFilterFieldsAndPosition(db *sql.DB, objType string) (map[string]uint32, error) { | 304 | func getFilterFieldsAndPosition(db *sql.DB, objType string) (map[string]uint32, error) { |
294 | filtersField := make(map[string]uint32, 0) | 305 | filtersField := make(map[string]uint32, 0) |
295 | rows, err := db.Query(`SELECT | 306 | rows, err := db.Query(`SELECT |
296 | FILTERS_FIELD, RB | 307 | FILTERS_FIELD, RB |
297 | FROM LIST_CONFIG_FILTERS | 308 | FROM LIST_CONFIG_FILTERS |
298 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 309 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
299 | if err != nil { | 310 | if err != nil { |
300 | return nil, err | 311 | return nil, err |
301 | } | 312 | } |
302 | defer rows.Close() | 313 | defer rows.Close() |
303 | 314 | ||
304 | for rows.Next() { | 315 | for rows.Next() { |
305 | var field string | 316 | var field string |
306 | var rb uint32 | 317 | var rb uint32 |
307 | rows.Scan(&field, &rb) | 318 | rows.Scan(&field, &rb) |
308 | filtersField[field] = rb | 319 | filtersField[field] = rb |
309 | } | 320 | } |
310 | if rows.Err() != nil { | 321 | if rows.Err() != nil { |
311 | return nil, rows.Err() | 322 | return nil, rows.Err() |
312 | } | 323 | } |
313 | return filtersField, nil | 324 | return filtersField, nil |
314 | } | 325 | } |
315 | 326 | ||
316 | type _filter struct { | 327 | type _filter struct { |
317 | DefaultValues string | 328 | DefaultValues string |
318 | Label string | 329 | Label string |
319 | Type string | 330 | Type string |
320 | } | 331 | } |
321 | 332 | ||
322 | // getFiltersByFilterField returns filter slice for the provided filter field. | 333 | // getFiltersByFilterField returns filter slice for the provided filter field. |
323 | func getFiltersByFilterField(db *sql.DB, filtersField string) ([]_filter, error) { | 334 | func getFiltersByFilterField(db *sql.DB, filtersField string) ([]_filter, error) { |
324 | resp := make([]_filter, 0) | 335 | resp := make([]_filter, 0) |
325 | rows, err := db.Query(`SELECT | 336 | rows, err := db.Query(`SELECT |
326 | FILTERS_TYPE, FILTERS_LABEL, DEFAULT_VALUES | 337 | FILTERS_TYPE, FILTERS_LABEL, DEFAULT_VALUES |
327 | FROM LIST_FILTERS_FIELD | 338 | FROM LIST_FILTERS_FIELD |
328 | WHERE FILTERS_FIELD = ` + fmt.Sprintf("'%s'", filtersField)) | 339 | WHERE FILTERS_FIELD = ` + fmt.Sprintf("'%s'", filtersField)) |
329 | if err != nil { | 340 | if err != nil { |
330 | return resp, err | 341 | return resp, err |
331 | } | 342 | } |
332 | defer rows.Close() | 343 | defer rows.Close() |
333 | 344 | ||
334 | var f _filter | 345 | var f _filter |
335 | for rows.Next() { | 346 | for rows.Next() { |
336 | rows.Scan(&f.Type, &f.Label, &f.DefaultValues) | 347 | rows.Scan(&f.Type, &f.Label, &f.DefaultValues) |
337 | resp = append(resp, f) | 348 | resp = append(resp, f) |
338 | } | 349 | } |
339 | if rows.Err() != nil { | 350 | if rows.Err() != nil { |
340 | return resp, rows.Err() | 351 | return resp, rows.Err() |
341 | } | 352 | } |
342 | return resp, nil | 353 | return resp, nil |
343 | } | 354 | } |
344 | 355 | ||
345 | // getFilterDropdownConfig returns dropdown menu for the provided filter field. | 356 | // getFilterDropdownConfig returns dropdown menu for the provided filter field. |
346 | func getFilterDropdownConfig(db *sql.DB, filtersField string) (Dropdown, error) { | 357 | func getFilterDropdownConfig(db *sql.DB, filtersField string) (Dropdown, error) { |
347 | var resp Dropdown | 358 | var resp Dropdown |
348 | rows, err := db.Query(`SELECT | 359 | rows, err := db.Query(`SELECT |
349 | FILTERS_FIELD, OBJECT_TYPE, ID_FIELD, LABEL_FIELD | 360 | FILTERS_FIELD, OBJECT_TYPE, ID_FIELD, LABEL_FIELD |
350 | FROM LIST_DROPDOWN_FILTER | 361 | FROM LIST_DROPDOWN_FILTER |
351 | WHERE FILTERS_FIELD = ` + fmt.Sprintf("'%s'", filtersField)) | 362 | WHERE FILTERS_FIELD = ` + fmt.Sprintf("'%s'", filtersField)) |
352 | if err != nil { | 363 | if err != nil { |
353 | return resp, err | 364 | return resp, err |
354 | } | 365 | } |
355 | defer rows.Close() | 366 | defer rows.Close() |
356 | if rows.Next() { | 367 | if rows.Next() { |
357 | rows.Scan(&resp.FiltersField, &resp.ObjectType, &resp.IDField, &resp.LabelField) | 368 | rows.Scan(&resp.FiltersField, &resp.ObjectType, &resp.IDField, &resp.LabelField) |
358 | } | 369 | } |
359 | if rows.Err() != nil { | 370 | if rows.Err() != nil { |
360 | return resp, rows.Err() | 371 | return resp, rows.Err() |
361 | } | 372 | } |
362 | return resp, nil | 373 | return resp, nil |
363 | } | 374 | } |
364 | 375 | ||
365 | // sortFilters bubble sorts provided filters slice by position field. | 376 | // sortFilters bubble sorts provided filters slice by position field. |
366 | func sortFilters(filters []ListFilter) { | 377 | func sortFilters(filters []ListFilter) { |
367 | done := false | 378 | done := false |
368 | var temp ListFilter | 379 | var temp ListFilter |
369 | for !done { | 380 | for !done { |
370 | done = true | 381 | done = true |
371 | for i := 0; i < len(filters)-1; i++ { | 382 | for i := 0; i < len(filters)-1; i++ { |
372 | if filters[i].Position > filters[i+1].Position { | 383 | if filters[i].Position > filters[i+1].Position { |
373 | done = false | 384 | done = false |
374 | temp = filters[i] | 385 | temp = filters[i] |
375 | filters[i] = filters[i+1] | 386 | filters[i] = filters[i+1] |
376 | filters[i+1] = temp | 387 | filters[i+1] = temp |
377 | } | 388 | } |
378 | } | 389 | } |
379 | } | 390 | } |
380 | } | 391 | } |
381 | 392 | ||
382 | // getListGraph return list graph slice for the provided object type. | 393 | // getListGraph return list graph slice for the provided object type. |
383 | func getListGraph(db *sql.DB, objType string) ([]ListGraph, error) { | 394 | func getListGraph(db *sql.DB, objType string) ([]ListGraph, error) { |
384 | resp := make([]ListGraph, 0) | 395 | resp := make([]ListGraph, 0) |
385 | rows, err := db.Query(`SELECT | 396 | rows, err := db.Query(`SELECT |
386 | OBJECT_TYPE, X_FIELD, Y_FIELD, GROUP_FIELD, LABEL | 397 | OBJECT_TYPE, X_FIELD, Y_FIELD, GROUP_FIELD, LABEL |
387 | FROM LIST_GRAPHS | 398 | FROM LIST_GRAPHS |
388 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 399 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
389 | if err != nil { | 400 | if err != nil { |
390 | return resp, err | 401 | return resp, err |
391 | } | 402 | } |
392 | defer rows.Close() | 403 | defer rows.Close() |
393 | 404 | ||
394 | var lg ListGraph | 405 | var lg ListGraph |
395 | for rows.Next() { | 406 | for rows.Next() { |
396 | rows.Scan(&lg.ObjectType, &lg.X, &lg.Y, &lg.GroupField, &lg.Label) | 407 | rows.Scan(&lg.ObjectType, &lg.X, &lg.Y, &lg.GroupField, &lg.Label) |
397 | resp = append(resp, lg) | 408 | resp = append(resp, lg) |
398 | } | 409 | } |
399 | if rows.Err() != nil { | 410 | if rows.Err() != nil { |
400 | return resp, rows.Err() | 411 | return resp, rows.Err() |
401 | } | 412 | } |
402 | return resp, nil | 413 | return resp, nil |
403 | } | 414 | } |
404 | 415 | ||
405 | // getListOptions returns list options for the provided object type. | 416 | // getListOptions returns list options for the provided object type. |
406 | func getListOptions(db *sql.DB, objType string) (ListOptions, error) { | 417 | func getListOptions(db *sql.DB, objType string) (ListOptions, error) { |
407 | var resp ListOptions | 418 | var resp ListOptions |
408 | rows, err := db.Query(`SELECT | 419 | rows, err := db.Query(`SELECT |
409 | GLOBAL_FILTER, LOCAL_FILTER, REMOTE_FILTER, PAGINATION, | 420 | GLOBAL_FILTER, LOCAL_FILTER, REMOTE_FILTER, PAGINATION, |
410 | PAGE_SIZE, PIVOT, DETAIL, TOTAL | 421 | PAGE_SIZE, PIVOT, DETAIL, TOTAL |
411 | FROM LIST_CONFIG | 422 | FROM LIST_CONFIG |
412 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 423 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
413 | if err != nil { | 424 | if err != nil { |
414 | return ListOptions{}, err | 425 | return ListOptions{}, err |
415 | } | 426 | } |
416 | defer rows.Close() | 427 | defer rows.Close() |
417 | if rows.Next() { | 428 | if rows.Next() { |
418 | var gfilter, lfilters, rfilters, pagination, pageSize, pivot, detail, total uint32 | 429 | var gfilter, lfilters, rfilters, pagination, pageSize, pivot, detail, total uint32 |
419 | rows.Scan(&gfilter, &lfilters, &rfilters, &pagination, &pageSize, &pivot, &detail, &total) | 430 | rows.Scan(&gfilter, &lfilters, &rfilters, &pagination, &pageSize, &pivot, &detail, &total) |
420 | resp.GlobalFilter = gfilter != 0 | 431 | resp.GlobalFilter = gfilter != 0 |
421 | resp.LocalFilters = lfilters != 0 | 432 | resp.LocalFilters = lfilters != 0 |
422 | resp.RemoteFilters = rfilters != 0 | 433 | resp.RemoteFilters = rfilters != 0 |
423 | resp.Pagination = pagination != 0 | 434 | resp.Pagination = pagination != 0 |
424 | resp.PageSize = pageSize | 435 | resp.PageSize = pageSize |
425 | resp.Pivot = pivot != 0 | 436 | resp.Pivot = pivot != 0 |
426 | resp.Detail = detail != 0 | 437 | resp.Detail = detail != 0 |
427 | resp.Total = total != 0 | 438 | resp.Total = total != 0 |
428 | } | 439 | } |
429 | if rows.Err() != nil { | 440 | if rows.Err() != nil { |
430 | return ListOptions{}, rows.Err() | 441 | return ListOptions{}, rows.Err() |
431 | } | 442 | } |
432 | return resp, nil | 443 | return resp, nil |
433 | } | 444 | } |
434 | 445 | ||
435 | // getListParent returns list parent node slice for the provided object type. | 446 | // getListParent returns list parent node slice for the provided object type. |
436 | func getListParent(db *sql.DB, objType string) ([]ListParentNode, error) { | 447 | func getListParent(db *sql.DB, objType string) ([]ListParentNode, error) { |
437 | resp := make([]ListParentNode, 0) | 448 | resp := make([]ListParentNode, 0) |
438 | rows, err := db.Query(`SELECT | 449 | rows, err := db.Query(`SELECT |
439 | PARENT_OBJECT_TYPE, PARENT_LABEL_FIELD, PARENT_FILTER_FIELD | 450 | PARENT_OBJECT_TYPE, PARENT_LABEL_FIELD, PARENT_FILTER_FIELD |
440 | FROM LIST_CONFIG_CHILD | 451 | FROM LIST_CONFIG_CHILD |
441 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 452 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
442 | if err != nil { | 453 | if err != nil { |
443 | return resp, err | 454 | return resp, err |
444 | } | 455 | } |
445 | defer rows.Close() | 456 | defer rows.Close() |
446 | 457 | ||
447 | var pnode ListParentNode | 458 | var pnode ListParentNode |
448 | for rows.Next() { | 459 | for rows.Next() { |
449 | rows.Scan(&pnode.ObjectType, &pnode.LabelField, &pnode.FilterField) | 460 | rows.Scan(&pnode.ObjectType, &pnode.LabelField, &pnode.FilterField) |
450 | resp = append(resp, pnode) | 461 | resp = append(resp, pnode) |
451 | } | 462 | } |
452 | if rows.Err() != nil { | 463 | if rows.Err() != nil { |
453 | return nil, rows.Err() | 464 | return nil, rows.Err() |
454 | } | 465 | } |
455 | 466 | ||
456 | return resp, nil | 467 | return resp, nil |
457 | } | 468 | } |
458 | 469 | ||
459 | // getListPivot list pivot slice for the provided object type. | 470 | // getListPivot list pivot slice for the provided object type. |
460 | func getListPivot(db *sql.DB, objType string) ([]ListPivot, error) { | 471 | func getListPivot(db *sql.DB, objType string) ([]ListPivot, error) { |
461 | resp := make([]ListPivot, 0) | 472 | resp := make([]ListPivot, 0) |
462 | rows, err := db.Query(`SELECT | 473 | rows, err := db.Query(`SELECT |
463 | OBJECT_TYPE, GROUP_FIELD, DISTINCT_FIELD, VALUE_FIELD | 474 | OBJECT_TYPE, GROUP_FIELD, DISTINCT_FIELD, VALUE_FIELD |
464 | FROM LIST_PIVOTS | 475 | FROM LIST_PIVOTS |
465 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 476 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
466 | if err != nil { | 477 | if err != nil { |
467 | return resp, err | 478 | return resp, err |
468 | } | 479 | } |
469 | defer rows.Close() | 480 | defer rows.Close() |
470 | 481 | ||
471 | var p ListPivot | 482 | var p ListPivot |
472 | for rows.Next() { | 483 | for rows.Next() { |
473 | rows.Scan(&p.ObjectType, &p.GroupField, &p.DistinctField, &p.Value) | 484 | rows.Scan(&p.ObjectType, &p.GroupField, &p.DistinctField, &p.Value) |
474 | resp = append(resp, p) | 485 | resp = append(resp, p) |
475 | } | 486 | } |
476 | if rows.Err() != nil { | 487 | if rows.Err() != nil { |
477 | return nil, rows.Err() | 488 | return nil, rows.Err() |
478 | } | 489 | } |
479 | 490 | ||
480 | return resp, nil | 491 | return resp, nil |
481 | } | 492 | } |
482 | 493 | ||
483 | // getListDetails returns list details for the provided object type. | 494 | // getListDetails returns list details for the provided object type. |
484 | func getListDetails(db *sql.DB, objType string) (ListDetails, error) { | 495 | func getListDetails(db *sql.DB, objType string) (ListDetails, error) { |
485 | var resp ListDetails | 496 | var resp ListDetails |
486 | rows, err := db.Query(`SELECT | 497 | rows, err := db.Query(`SELECT |
487 | OBJECT_TYPE, PARENT_OBJECT_TYPE, PARENT_FILTER_FIELD, SINGLE_DETAIL | 498 | OBJECT_TYPE, PARENT_OBJECT_TYPE, PARENT_FILTER_FIELD, SINGLE_DETAIL |
488 | FROM LIST_CONFIG_DETAIL | 499 | FROM LIST_CONFIG_DETAIL |
489 | WHERE PARENT_OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 500 | WHERE PARENT_OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
490 | if err != nil { | 501 | if err != nil { |
491 | return resp, err | 502 | return resp, err |
492 | } | 503 | } |
493 | defer rows.Close() | 504 | defer rows.Close() |
494 | if rows.Next() { | 505 | if rows.Next() { |
495 | var singleDetail uint32 | 506 | var singleDetail uint32 |
496 | rows.Scan(&resp.ObjectType, &resp.ParentObjectType, &resp.ParentFilterField, &singleDetail) | 507 | rows.Scan(&resp.ObjectType, &resp.ParentObjectType, &resp.ParentFilterField, &singleDetail) |
497 | resp.SingleDetail = singleDetail != 0 | 508 | resp.SingleDetail = singleDetail != 0 |
498 | } | 509 | } |
499 | if rows.Err() != nil { | 510 | if rows.Err() != nil { |
500 | return resp, rows.Err() | 511 | return resp, rows.Err() |
501 | } | 512 | } |
502 | 513 | ||
503 | return resp, nil | 514 | return resp, nil |
504 | } | 515 | } |
516 | |||
517 | // getListLiveGraph returns live graph for the provided object type. | ||
518 | func getListLiveGraph(db *sql.DB, objType string) (ListLiveGraph, error) { | ||
519 | var resp ListLiveGraph | ||
520 | rows, err := db.Query(`SELECT | ||
521 | OBJECT_TYPE, VALUE_FIELDS, LABEL_FIELDS | ||
522 | FROM LIST_LIVE_GRAPH | ||
523 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | ||
524 | if err != nil { | ||
525 | return resp, err | ||
526 | } | ||
527 | defer rows.Close() | ||
528 | if rows.Next() { | ||
529 | rows.Scan(&resp.ObjectType, &resp.ValueFields, &resp.LabelFields) | ||
530 | } | ||
531 | if rows.Err() != nil { | ||
532 | return resp, rows.Err() | ||
533 | } | ||
534 | |||
535 | return resp, nil | ||
536 | } | ||
505 | 537 |