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