Commit 11933054acf481aff93c2216d5d233aad635b0f6

Authored by Marko Tikvić
1 parent 46b2215ebd
Exists in master

Added Get, Post and Put methods

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