Commit ad8e9dd2af2320b920444b98a0d71d8c0a565734
1 parent
63b2ae6201
Exists in
master
and in
1 other branch
added middleware and functions to support getting default values from nil interfaces
Showing
6 changed files
with
130 additions
and
19 deletions
Show diff stats
default_values.go
File was created | 1 | package webutility | |
2 | |||
3 | func Int32ValueOrDefault(val *interface{}) (def int32) { | ||
4 | if *val != nil { | ||
5 | def = (*val).(int32) | ||
6 | } | ||
7 | return def | ||
8 | } | ||
9 | |||
10 | func Int64ValueOrDefault(val *interface{}) (def int64) { | ||
11 | if *val != nil { | ||
12 | def = (*val).(int64) | ||
13 | } | ||
14 | return def | ||
15 | } | ||
16 | |||
17 | func Uint32ValueOrDefault(val *interface{}) (def uint32) { | ||
18 | if *val != nil { | ||
19 | def = (*val).(uint32) | ||
20 | } | ||
21 | return def | ||
22 | } | ||
23 | |||
24 | func Uint64ValueOrDefault(val *interface{}) (def uint64) { | ||
25 | if *val != nil { | ||
26 | def = (*val).(uint64) | ||
27 | } | ||
28 | return def | ||
29 | } | ||
30 | |||
31 | func Float32ValueOrDefault(val *interface{}) (def float32) { | ||
32 | if *val != nil { | ||
33 | def = (*val).(float32) | ||
34 | } | ||
35 | return def | ||
36 | } | ||
37 | |||
38 | func Float64ValueOrDefault(val *interface{}) (def float64) { | ||
39 | if *val != nil { | ||
40 | return (*val).(float64) | ||
41 | } | ||
42 | return def | ||
43 | } | ||
44 | |||
45 | func StringValueOrDefault(val *interface{}) (def string) { | ||
46 | if *val != nil { | ||
47 | def = (*val).(string) | ||
48 | } | ||
49 | return def | ||
50 | } | ||
51 |
http.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "encoding/json" | 4 | "encoding/json" |
5 | "fmt" | ||
5 | "net/http" | 6 | "net/http" |
6 | ) | 7 | ) |
7 | 8 | ||
8 | type webError struct { | 9 | type webError struct { |
9 | Request string `json:"request"` | 10 | Request string `json:"request"` |
10 | Error string `json:"error"` | 11 | Error string `json:"error"` |
11 | } | 12 | } |
12 | 13 | ||
13 | // NotFoundHandler writes HTTP error 404 to w. | 14 | // NotFoundHandlerFunc writes HTTP error 404 to w. |
14 | func NotFoundHandler(w http.ResponseWriter, req *http.Request) { | 15 | func NotFoundHandlerFunc(w http.ResponseWriter, req *http.Request) { |
15 | SetDefaultHeaders(w) | 16 | SetDefaultHeaders(w) |
16 | if req.Method == "OPTIONS" { | 17 | if req.Method == "OPTIONS" { |
17 | return | 18 | return |
18 | } | 19 | } |
19 | NotFound(w, req, "Not found") | 20 | NotFound(w, req, fmt.Sprintf("Resource you requested was not found: %s", req.URL.String())) |
20 | } | 21 | } |
21 | 22 | ||
22 | // SetDefaultHeaders set's default headers for an HTTP response. | 23 | // SetDefaultHeaders set's default headers for an HTTP response. |
23 | func SetDefaultHeaders(w http.ResponseWriter) { | 24 | func SetDefaultHeaders(w http.ResponseWriter) { |
24 | w.Header().Set("Access-Control-Allow-Origin", "*") | 25 | w.Header().Set("Access-Control-Allow-Origin", "*") |
25 | w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS") | 26 | w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS") |
26 | w.Header().Set("Access-Control-Allow-Headers", `Accept, Content-Type, | 27 | w.Header().Set("Access-Control-Allow-Headers", `Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization`) |
27 | Content-Length, Accept-Encoding, X-CSRF-Token, Authorization`) | ||
28 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | 28 | w.Header().Set("Content-Type", "application/json; charset=utf-8") |
29 | } | 29 | } |
30 | 30 | ||
31 | func ReqLocale(req *http.Request, dflt string) string { | 31 | func ReqLocale(req *http.Request, dflt string) string { |
32 | loc := req.FormValue("locale") | 32 | loc := req.FormValue("locale") |
33 | if loc == "" { | 33 | if loc == "" { |
34 | return dflt | 34 | return dflt |
35 | } | 35 | } |
36 | return loc | 36 | return loc |
37 | } | 37 | } |
38 | 38 | ||
39 | // 2xx | 39 | // 2xx |
40 | func Success(w http.ResponseWriter, payload *Payload, code int) { | 40 | func Success(w http.ResponseWriter, payload *Payload, code int) { |
41 | w.WriteHeader(code) | 41 | w.WriteHeader(code) |
42 | if payload != nil { | 42 | if payload != nil { |
43 | json.NewEncoder(w).Encode(*payload) | 43 | json.NewEncoder(w).Encode(*payload) |
44 | } | 44 | } |
45 | } | 45 | } |
46 | 46 | ||
47 | // 200 | 47 | // 200 |
48 | func OK(w http.ResponseWriter, payload *Payload) { | 48 | func OK(w http.ResponseWriter, payload *Payload) { |
49 | Success(w, payload, http.StatusOK) | 49 | Success(w, payload, http.StatusOK) |
50 | } | 50 | } |
51 | 51 | ||
52 | // 201 | 52 | // 201 |
53 | func Created(w http.ResponseWriter, payload *Payload) { | 53 | func Created(w http.ResponseWriter, payload *Payload) { |
54 | Success(w, payload, http.StatusCreated) | 54 | Success(w, payload, http.StatusCreated) |
55 | } | 55 | } |
56 | 56 | ||
57 | // 4xx; 5xx | 57 | // 4xx; 5xx |
58 | func Error(w http.ResponseWriter, r *http.Request, code int, err string) { | 58 | func Error(w http.ResponseWriter, r *http.Request, code int, err string) { |
59 | werr := webError{Error: err, Request: r.Method + " " + r.RequestURI} | 59 | werr := webError{Error: err, Request: r.Method + " " + r.RequestURI} |
60 | w.WriteHeader(code) | 60 | w.WriteHeader(code) |
61 | json.NewEncoder(w).Encode(werr) | 61 | json.NewEncoder(w).Encode(werr) |
62 | } | 62 | } |
63 | 63 | ||
64 | // 400 | 64 | // 400 |
65 | func BadRequest(w http.ResponseWriter, r *http.Request, err string) { | 65 | func BadRequest(w http.ResponseWriter, r *http.Request, err string) { |
66 | Error(w, r, http.StatusBadRequest, err) | 66 | Error(w, r, http.StatusBadRequest, err) |
67 | } | 67 | } |
68 | 68 | ||
69 | // 404 | 69 | // 404 |
70 | func NotFound(w http.ResponseWriter, r *http.Request, err string) { | 70 | func NotFound(w http.ResponseWriter, r *http.Request, err string) { |
71 | Error(w, r, http.StatusNotFound, err) | 71 | Error(w, r, http.StatusNotFound, err) |
72 | } | 72 | } |
73 | 73 | ||
74 | // 401 | 74 | // 401 |
75 | func Unauthorized(w http.ResponseWriter, r *http.Request, err string) { | 75 | func Unauthorized(w http.ResponseWriter, r *http.Request, err string) { |
76 | Error(w, r, http.StatusUnauthorized, err) | 76 | Error(w, r, http.StatusUnauthorized, err) |
77 | } | 77 | } |
78 | 78 | ||
79 | // 403 | 79 | // 403 |
80 | func Forbidden(w http.ResponseWriter, r *http.Request, err string) { | 80 | func Forbidden(w http.ResponseWriter, r *http.Request, err string) { |
81 | Error(w, r, http.StatusForbidden, err) | 81 | Error(w, r, http.StatusForbidden, err) |
82 | } | 82 | } |
83 | 83 | ||
84 | // 403 | 84 | // 403 |
85 | func Conflict(w http.ResponseWriter, r *http.Request, err string) { | 85 | func Conflict(w http.ResponseWriter, r *http.Request, err string) { |
86 | Error(w, r, http.StatusConflict, err) | 86 | Error(w, r, http.StatusConflict, err) |
87 | } | 87 | } |
88 | 88 | ||
89 | // 500 | 89 | // 500 |
90 | func InternalServerError(w http.ResponseWriter, r *http.Request, err string) { | 90 | func InternalServerError(w http.ResponseWriter, r *http.Request, err string) { |
91 | Error(w, r, http.StatusInternalServerError, err) | 91 | Error(w, r, http.StatusInternalServerError, err) |
92 | } | 92 | } |
93 | 93 | ||
94 | /// | 94 | /// |
95 | /// Old API | 95 | /// Old API |
96 | /// | 96 | /// |
97 | 97 | ||
98 | const ( | 98 | const ( |
99 | templateHttpErr500_EN = "An internal server error has occurred." | 99 | templateHttpErr500_EN = "An internal server error has occurred." |
100 | templateHttpErr500_RS = "Došlo je do greške na serveru." | 100 | templateHttpErr500_RS = "Došlo je do greške na serveru." |
101 | templateHttpErr400_EN = "Bad request." | 101 | templateHttpErr400_EN = "Bad request." |
102 | templateHttpErr400_RS = "Neispravan zahtev." | 102 | templateHttpErr400_RS = "Neispravan zahtev." |
103 | templateHttpErr404_EN = "Resource not found." | 103 | templateHttpErr404_EN = "Resource not found." |
104 | templateHttpErr404_RS = "Resurs nije pronadjen." | 104 | templateHttpErr404_RS = "Resurs nije pronadjen." |
105 | templateHttpErr401_EN = "Unauthorized request." | 105 | templateHttpErr401_EN = "Unauthorized request." |
106 | templateHttpErr401_RS = "Neautorizovan zahtev." | 106 | templateHttpErr401_RS = "Neautorizovan zahtev." |
107 | ) | 107 | ) |
108 | 108 | ||
109 | type httpError struct { | 109 | type httpError struct { |
110 | Error []HttpErrorDesc `json:"error"` | 110 | Error []HttpErrorDesc `json:"error"` |
111 | Request string `json:"request"` | 111 | Request string `json:"request"` |
112 | } | 112 | } |
113 | 113 | ||
114 | type HttpErrorDesc struct { | 114 | type HttpErrorDesc struct { |
115 | Lang string `json:"lang"` | 115 | Lang string `json:"lang"` |
116 | Desc string `json:"description"` | 116 | Desc string `json:"description"` |
117 | } | 117 | } |
118 | 118 | ||
119 | // DeliverPayload encodes payload as JSON to w. | 119 | // DeliverPayload encodes payload as JSON to w. |
120 | func DeliverPayload(w http.ResponseWriter, payload Payload) { | 120 | func DeliverPayload(w http.ResponseWriter, payload Payload) { |
121 | // Don't write status OK in the headers here. Leave it up for the caller. | 121 | // Don't write status OK in the headers here. Leave it up for the caller. |
122 | // E.g. Status 201. | 122 | // E.g. Status 201. |
123 | json.NewEncoder(w).Encode(payload) | 123 | json.NewEncoder(w).Encode(payload) |
124 | payload.Data = nil | 124 | payload.Data = nil |
125 | } | 125 | } |
126 | 126 | ||
127 | // ErrorResponse writes HTTP error to w. | 127 | // ErrorResponse writes HTTP error to w. |
128 | func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) { | 128 | func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) { |
129 | err := httpError{desc, r.Method + " " + r.RequestURI} | 129 | err := httpError{desc, r.Method + " " + r.RequestURI} |
130 | w.WriteHeader(code) | 130 | w.WriteHeader(code) |
131 | json.NewEncoder(w).Encode(err) | 131 | json.NewEncoder(w).Encode(err) |
132 | } | 132 | } |
133 | 133 | ||
134 | // NotFoundResponse writes HTTP error 404 to w. | 134 | // NotFoundResponse writes HTTP error 404 to w. |
135 | func NotFoundResponse(w http.ResponseWriter, req *http.Request) { | 135 | func NotFoundResponse(w http.ResponseWriter, req *http.Request) { |
136 | ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{ | 136 | ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{ |
137 | {"en", templateHttpErr404_EN}, | 137 | {"en", templateHttpErr404_EN}, |
138 | {"rs", templateHttpErr404_RS}, | 138 | {"rs", templateHttpErr404_RS}, |
139 | }) | 139 | }) |
140 | } | 140 | } |
141 | 141 | ||
142 | // BadRequestResponse writes HTTP error 400 to w. | 142 | // BadRequestResponse writes HTTP error 400 to w. |
143 | func BadRequestResponse(w http.ResponseWriter, req *http.Request) { | 143 | func BadRequestResponse(w http.ResponseWriter, req *http.Request) { |
144 | ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{ | 144 | ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{ |
145 | {"en", templateHttpErr400_EN}, | 145 | {"en", templateHttpErr400_EN}, |
146 | {"rs", templateHttpErr400_RS}, | 146 | {"rs", templateHttpErr400_RS}, |
147 | }) | 147 | }) |
148 | } | 148 | } |
149 | 149 | ||
150 | // InternalSeverErrorResponse writes HTTP error 500 to w. | 150 | // InternalSeverErrorResponse writes HTTP error 500 to w. |
151 | func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) { | 151 | func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) { |
152 | ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{ | 152 | ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{ |
153 | {"en", templateHttpErr500_EN}, | 153 | {"en", templateHttpErr500_EN}, |
154 | {"rs", templateHttpErr500_RS}, | 154 | {"rs", templateHttpErr500_RS}, |
155 | }) | 155 | }) |
156 | } | 156 | } |
157 | 157 | ||
158 | // UnauthorizedError writes HTTP error 401 to w. | 158 | // UnauthorizedError writes HTTP error 401 to w. |
159 | func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) { | 159 | func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) { |
160 | w.Header().Set("WWW-Authenticate", "Bearer") | 160 | w.Header().Set("WWW-Authenticate", "Bearer") |
161 | ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{ | 161 | ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{ |
162 | {"en", templateHttpErr401_EN}, | 162 | {"en", templateHttpErr401_EN}, |
163 | {"rs", templateHttpErr401_RS}, | 163 | {"rs", templateHttpErr401_RS}, |
164 | }) | 164 | }) |
165 | } | 165 | } |
json.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 | |||
13 | "git.to-net.rs/marko.tikvic/gologger" | ||
14 | ) | 12 | ) |
15 | 13 | ||
16 | var ( | 14 | var ( |
17 | mu = &sync.Mutex{} | 15 | mu = &sync.Mutex{} |
18 | metadata = make(map[string]Payload) | 16 | metadata = make(map[string]Payload) |
19 | updateQue = make(map[string][]byte) | 17 | updateQue = make(map[string][]byte) |
20 | 18 | ||
21 | metadataDB *sql.DB | 19 | metadataDB *sql.DB |
22 | activeProject string | 20 | activeProject string |
23 | 21 | ||
24 | inited bool | 22 | inited bool |
25 | driver string | 23 | driver string |
26 | ) | 24 | ) |
27 | 25 | ||
28 | var logger *gologger.Logger | ||
29 | |||
30 | func init() { | ||
31 | var err error | ||
32 | logger, err = gologger.New("webutility", gologger.MaxLogSize100KB) | ||
33 | if err != nil { | ||
34 | fmt.Printf("webutility: %s\n", err.Error()) | ||
35 | } | ||
36 | } | ||
37 | |||
38 | type LangMap map[string]map[string]string | 26 | type LangMap map[string]map[string]string |
39 | 27 | ||
40 | type Field struct { | 28 | type Field struct { |
41 | Parameter string `json:"param"` | 29 | Parameter string `json:"param"` |
42 | Type string `json:"type"` | 30 | Type string `json:"type"` |
43 | Visible bool `json:"visible"` | 31 | Visible bool `json:"visible"` |
44 | Editable bool `json:"editable"` | 32 | Editable bool `json:"editable"` |
45 | } | 33 | } |
46 | 34 | ||
47 | type CorrelationField struct { | 35 | type CorrelationField struct { |
48 | Result string `json:"result"` | 36 | Result string `json:"result"` |
49 | Elements []string `json:"elements"` | 37 | Elements []string `json:"elements"` |
50 | Type string `json:"type"` | 38 | Type string `json:"type"` |
51 | } | 39 | } |
52 | 40 | ||
53 | type Translation struct { | 41 | type Translation struct { |
54 | Language string `json:"language"` | 42 | Language string `json:"language"` |
55 | FieldsLabels map[string]string `json:"fieldsLabels"` | 43 | FieldsLabels map[string]string `json:"fieldsLabels"` |
56 | } | 44 | } |
57 | 45 | ||
58 | type Payload struct { | 46 | type Payload struct { |
59 | Method string `json:"method"` | 47 | Method string `json:"method"` |
60 | Params map[string]string `json:"params"` | 48 | Params map[string]string `json:"params"` |
61 | Lang []Translation `json:"lang"` | 49 | Lang []Translation `json:"lang"` |
62 | Fields []Field `json:"fields"` | 50 | Fields []Field `json:"fields"` |
63 | Correlations []CorrelationField `json:"correlationFields"` | 51 | Correlations []CorrelationField `json:"correlationFields"` |
64 | IdField string `json:"idField"` | 52 | IdField string `json:"idField"` |
65 | 53 | ||
66 | // Data holds JSON payload. | 54 | // Data holds JSON payload. |
67 | // It can't be used for itteration. | 55 | // It can't be used for itteration. |
68 | Data interface{} `json:"data"` | 56 | Data interface{} `json:"data"` |
69 | } | 57 | } |
70 | 58 | ||
59 | func (p *Payload) SetData(data interface{}) { | ||
60 | p.Data = data | ||
61 | } | ||
62 | |||
71 | // NewPayload returs a payload sceleton for entity described with etype. | 63 | // NewPayload returs a payload sceleton for entity described with etype. |
72 | func NewPayload(r *http.Request, etype string) Payload { | 64 | func NewPayload(r *http.Request, etype string) Payload { |
73 | pload := metadata[etype] | 65 | pload := metadata[etype] |
74 | pload.Method = r.Method + " " + r.RequestURI | 66 | pload.Method = r.Method + " " + r.RequestURI |
75 | return pload | 67 | return pload |
76 | } | 68 | } |
77 | 69 | ||
78 | // DecodeJSON decodes JSON data from r to v. | 70 | // DecodeJSON decodes JSON data from r to v. |
79 | // Returns an error if it fails. | 71 | // Returns an error if it fails. |
80 | func DecodeJSON(r io.Reader, v interface{}) error { | 72 | func DecodeJSON(r io.Reader, v interface{}) error { |
81 | return json.NewDecoder(r).Decode(v) | 73 | return json.NewDecoder(r).Decode(v) |
82 | } | 74 | } |
83 | 75 | ||
84 | // InitPayloadsMetadata loads all payloads' information into 'metadata' variable. | 76 | // InitPayloadsMetadata loads all payloads' information into 'metadata' variable. |
85 | func InitPayloadsMetadata(drv string, db *sql.DB, project string) error { | 77 | func InitPayloadsMetadata(drv string, db *sql.DB, project string) error { |
86 | if drv != "ora" && drv != "mysql" { | 78 | if drv != "ora" && drv != "mysql" { |
87 | return errors.New("driver not supported") | 79 | return errors.New("driver not supported") |
88 | } | 80 | } |
89 | driver = drv | 81 | driver = drv |
90 | metadataDB = db | 82 | metadataDB = db |
91 | activeProject = project | 83 | activeProject = project |
92 | 84 | ||
93 | mu.Lock() | 85 | mu.Lock() |
94 | defer mu.Unlock() | 86 | defer mu.Unlock() |
95 | err := initMetadata(project) | 87 | err := initMetadata(project) |
96 | if err != nil { | 88 | if err != nil { |
97 | return err | 89 | return err |
98 | } | 90 | } |
99 | inited = true | 91 | inited = true |
100 | 92 | ||
101 | return nil | 93 | return nil |
102 | } | 94 | } |
103 | 95 | ||
104 | func EnableHotloading(interval int) { | 96 | func EnableHotloading(interval int) { |
105 | if interval > 0 { | 97 | if interval > 0 { |
106 | go hotload(interval) | 98 | go hotload(interval) |
107 | } | 99 | } |
108 | } | 100 | } |
109 | 101 | ||
110 | func GetMetadataForAllEntities() map[string]Payload { | 102 | func GetMetadataForAllEntities() map[string]Payload { |
111 | return metadata | 103 | return metadata |
112 | } | 104 | } |
113 | 105 | ||
114 | func GetMetadataForEntity(t string) (Payload, bool) { | 106 | func GetMetadataForEntity(t string) (Payload, bool) { |
115 | p, ok := metadata[t] | 107 | p, ok := metadata[t] |
116 | return p, ok | 108 | return p, ok |
117 | } | 109 | } |
118 | 110 | ||
119 | func QueEntityModelUpdate(entityType string, v interface{}) { | 111 | func QueEntityModelUpdate(entityType string, v interface{}) { |
120 | updateQue[entityType], _ = json.Marshal(v) | 112 | updateQue[entityType], _ = json.Marshal(v) |
121 | } | 113 | } |
122 | 114 | ||
123 | func UpdateEntityModels(command string) (total, upd, add int, err error) { | 115 | func UpdateEntityModels(command string) (total, upd, add int, err error) { |
124 | if command != "force" && command != "missing" { | 116 | if command != "force" && command != "missing" { |
125 | return total, 0, 0, errors.New("webutility: unknown command: " + command) | 117 | return total, 0, 0, errors.New("webutility: unknown command: " + command) |
126 | } | 118 | } |
127 | 119 | ||
128 | if !inited { | 120 | if !inited { |
129 | return 0, 0, 0, errors.New("webutility: metadata not initialized but update was tried.") | 121 | return 0, 0, 0, errors.New("webutility: metadata not initialized but update was tried.") |
130 | } | 122 | } |
131 | 123 | ||
132 | total = len(updateQue) | 124 | total = len(updateQue) |
133 | 125 | ||
134 | toUpdate := make([]string, 0) | 126 | toUpdate := make([]string, 0) |
135 | toAdd := make([]string, 0) | 127 | toAdd := make([]string, 0) |
136 | 128 | ||
137 | for k, _ := range updateQue { | 129 | for k, _ := range updateQue { |
138 | if _, exists := metadata[k]; exists { | 130 | if _, exists := metadata[k]; exists { |
139 | if command == "force" { | 131 | if command == "force" { |
140 | toUpdate = append(toUpdate, k) | 132 | toUpdate = append(toUpdate, k) |
141 | } | 133 | } |
142 | } else { | 134 | } else { |
143 | toAdd = append(toAdd, k) | 135 | toAdd = append(toAdd, k) |
144 | } | 136 | } |
145 | } | 137 | } |
146 | 138 | ||
147 | var uStmt *sql.Stmt | 139 | var uStmt *sql.Stmt |
148 | if driver == "ora" { | 140 | if driver == "ora" { |
149 | uStmt, err = metadataDB.Prepare("update entities set entity_model = :1 where entity_type = :2") | 141 | uStmt, err = metadataDB.Prepare("update entities set entity_model = :1 where entity_type = :2") |
150 | if err != nil { | 142 | if err != nil { |
151 | return | 143 | return |
152 | } | 144 | } |
153 | } else if driver == "mysql" { | 145 | } else if driver == "mysql" { |
154 | uStmt, err = metadataDB.Prepare("update entities set entity_model = ? where entity_type = ?") | 146 | uStmt, err = metadataDB.Prepare("update entities set entity_model = ? where entity_type = ?") |
155 | if err != nil { | 147 | if err != nil { |
156 | return | 148 | return |
157 | } | 149 | } |
158 | } | 150 | } |
159 | for _, k := range toUpdate { | 151 | for _, k := range toUpdate { |
160 | //fmt.Printf("Updating: %s\n", k) | 152 | //fmt.Printf("Updating: %s\n", k) |
161 | //fmt.Printf("New model: %s\n", updateQue[k]) | 153 | //fmt.Printf("New model: %s\n", updateQue[k]) |
162 | _, err = uStmt.Exec(string(updateQue[k]), k) | 154 | _, err = uStmt.Exec(string(updateQue[k]), k) |
163 | if err != nil { | 155 | if err != nil { |
164 | logger.Log("webutility: %v\n", err) | 156 | logger.Log("webutility: %v\n", err) |
165 | return | 157 | return |
166 | } | 158 | } |
167 | upd++ | 159 | upd++ |
168 | } | 160 | } |
169 | 161 | ||
170 | blankPayload, _ := json.Marshal(Payload{}) | 162 | blankPayload, _ := json.Marshal(Payload{}) |
171 | var iStmt *sql.Stmt | 163 | var iStmt *sql.Stmt |
172 | if driver == "ora" { | 164 | if driver == "ora" { |
173 | iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(:1, :2, :3, :4)") | 165 | iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(:1, :2, :3, :4)") |
174 | if err != nil { | 166 | if err != nil { |
175 | return | 167 | return |
176 | } | 168 | } |
177 | } else if driver == "mysql" { | 169 | } else if driver == "mysql" { |
178 | iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(?, ?, ?, ?)") | 170 | iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(?, ?, ?, ?)") |
179 | if err != nil { | 171 | if err != nil { |
180 | return | 172 | return |
181 | } | 173 | } |
182 | } | 174 | } |
183 | for _, k := range toAdd { | 175 | for _, k := range toAdd { |
184 | _, err = iStmt.Exec(activeProject, string(blankPayload), k, string(updateQue[k])) | 176 | _, err = iStmt.Exec(activeProject, string(blankPayload), k, string(updateQue[k])) |
185 | if err != nil { | 177 | if err != nil { |
186 | logger.Log("webutility: %v\n", err) | 178 | logger.Log("webutility: %v\n", err) |
187 | return | 179 | return |
188 | } | 180 | } |
189 | metadata[k] = Payload{} | 181 | metadata[k] = Payload{} |
190 | add++ | 182 | add++ |
191 | } | 183 | } |
192 | 184 | ||
193 | return total, upd, add, nil | 185 | return total, upd, add, nil |
194 | } | 186 | } |
195 | 187 | ||
196 | func initMetadata(project string) error { | 188 | func initMetadata(project string) error { |
197 | rows, err := metadataDB.Query(`select | 189 | rows, err := metadataDB.Query(`select |
198 | entity_type, | 190 | entity_type, |
199 | metadata | 191 | metadata |
200 | from entities | 192 | from entities |
201 | where projekat = ` + fmt.Sprintf("'%s'", project)) | 193 | where projekat = ` + fmt.Sprintf("'%s'", project)) |
202 | if err != nil { | 194 | if err != nil { |
203 | return err | 195 | return err |
204 | } | 196 | } |
205 | defer rows.Close() | 197 | defer rows.Close() |
206 | 198 | ||
207 | count := 0 | 199 | count := 0 |
208 | success := 0 | 200 | success := 0 |
209 | if len(metadata) > 0 { | 201 | if len(metadata) > 0 { |
210 | metadata = nil | 202 | metadata = nil |
211 | } | 203 | } |
212 | metadata = make(map[string]Payload) | 204 | metadata = make(map[string]Payload) |
213 | for rows.Next() { | 205 | for rows.Next() { |
214 | var name, load string | 206 | var name, load string |
215 | rows.Scan(&name, &load) | 207 | rows.Scan(&name, &load) |
216 | 208 | ||
217 | p := Payload{} | 209 | p := Payload{} |
218 | err := json.Unmarshal([]byte(load), &p) | 210 | err := json.Unmarshal([]byte(load), &p) |
219 | if err != nil { | 211 | if err != nil { |
220 | logger.Log("webutility: couldn't init: '%s' metadata: %s:\n%s\n", name, err.Error(), load) | 212 | logger.Log("webutility: couldn't init: '%s' metadata: %s:\n%s\n", name, err.Error(), load) |
221 | } else { | 213 | } else { |
222 | success++ | 214 | success++ |
223 | metadata[name] = p | 215 | metadata[name] = p |
224 | } | 216 | } |
225 | count++ | 217 | count++ |
226 | } | 218 | } |
227 | perc := float32(success) / float32(count) * 100.0 | 219 | perc := float32(success) / float32(count) * 100.0 |
228 | logger.Log("webutility: loaded %d/%d (%.1f%%) entities\n", success, count, perc) | 220 | logger.Log("webutility: loaded %d/%d (%.1f%%) entities\n", success, count, perc) |
229 | 221 | ||
230 | return nil | 222 | return nil |
231 | } | 223 | } |
232 | 224 | ||
233 | func hotload(n int) { | 225 | func hotload(n int) { |
234 | entityScan := make(map[string]int64) | 226 | entityScan := make(map[string]int64) |
235 | firstCheck := true | 227 | firstCheck := true |
236 | for { | 228 | for { |
237 | time.Sleep(time.Duration(n) * time.Second) | 229 | time.Sleep(time.Duration(n) * time.Second) |
238 | rows, err := metadataDB.Query(`select | 230 | rows, err := metadataDB.Query(`select |
239 | ora_rowscn, | 231 | ora_rowscn, |
240 | entity_type | 232 | entity_type |
241 | from entities where projekat = ` + fmt.Sprintf("'%s'", activeProject)) | 233 | from entities where projekat = ` + fmt.Sprintf("'%s'", activeProject)) |
242 | if err != nil { | 234 | if err != nil { |
243 | logger.Log("webutility: hotload failed: %v\n", err) | 235 | logger.Log("webutility: hotload failed: %v\n", err) |
244 | time.Sleep(time.Duration(n) * time.Second) | 236 | time.Sleep(time.Duration(n) * time.Second) |
245 | continue | 237 | continue |
246 | } | 238 | } |
247 | 239 | ||
248 | var toRefresh []string | 240 | var toRefresh []string |
249 | for rows.Next() { | 241 | for rows.Next() { |
250 | var scanID int64 | 242 | var scanID int64 |
251 | var entity string | 243 | var entity string |
252 | rows.Scan(&scanID, &entity) | 244 | rows.Scan(&scanID, &entity) |
253 | oldID, ok := entityScan[entity] | 245 | oldID, ok := entityScan[entity] |
254 | if !ok || oldID != scanID { | 246 | if !ok || oldID != scanID { |
255 | entityScan[entity] = scanID | 247 | entityScan[entity] = scanID |
256 | toRefresh = append(toRefresh, entity) | 248 | toRefresh = append(toRefresh, entity) |
257 | } | 249 | } |
258 | } | 250 | } |
259 | rows.Close() | 251 | rows.Close() |
260 | 252 | ||
261 | if rows.Err() != nil { | 253 | if rows.Err() != nil { |
262 | logger.Log("webutility: hotload rset error: %v\n", rows.Err()) | 254 | logger.Log("webutility: hotload rset error: %v\n", rows.Err()) |
263 | time.Sleep(time.Duration(n) * time.Second) | 255 | time.Sleep(time.Duration(n) * time.Second) |
264 | continue | 256 | continue |
265 | } | 257 | } |
266 | 258 | ||
267 | if len(toRefresh) > 0 && !firstCheck { | 259 | if len(toRefresh) > 0 && !firstCheck { |
268 | mu.Lock() | 260 | mu.Lock() |
269 | refreshMetadata(toRefresh) | 261 | refreshMetadata(toRefresh) |
270 | mu.Unlock() | 262 | mu.Unlock() |
271 | } | 263 | } |
272 | if firstCheck { | 264 | if firstCheck { |
273 | firstCheck = false | 265 | firstCheck = false |
274 | } | 266 | } |
275 | } | 267 | } |
276 | } | 268 | } |
277 | 269 | ||
278 | func refreshMetadata(entities []string) { | 270 | func refreshMetadata(entities []string) { |
279 | for _, e := range entities { | 271 | for _, e := range entities { |
280 | fmt.Printf("refreshing %s\n", e) | 272 | fmt.Printf("refreshing %s\n", e) |
281 | rows, err := metadataDB.Query(`select | 273 | rows, err := metadataDB.Query(`select |
282 | metadata | 274 | metadata |
283 | from entities | 275 | from entities |
284 | where projekat = ` + fmt.Sprintf("'%s'", activeProject) + | 276 | where projekat = ` + fmt.Sprintf("'%s'", activeProject) + |
285 | ` and entity_type = ` + fmt.Sprintf("'%s'", e)) | 277 | ` and entity_type = ` + fmt.Sprintf("'%s'", e)) |
286 | 278 | ||
287 | if err != nil { | 279 | if err != nil { |
288 | logger.Log("webutility: refresh: prep: %v\n", err) | 280 | logger.Log("webutility: refresh: prep: %v\n", err) |
289 | rows.Close() | 281 | rows.Close() |
290 | continue | 282 | continue |
291 | } | 283 | } |
292 | 284 | ||
293 | for rows.Next() { | 285 | for rows.Next() { |
294 | var load string | 286 | var load string |
295 | rows.Scan(&load) | 287 | rows.Scan(&load) |
296 | p := Payload{} | 288 | p := Payload{} |
297 | err := json.Unmarshal([]byte(load), &p) | 289 | err := json.Unmarshal([]byte(load), &p) |
298 | if err != nil { | 290 | if err != nil { |
299 | logger.Log("webutility: couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), load) | 291 | logger.Log("webutility: couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), load) |
300 | } else { | 292 | } else { |
301 | metadata[e] = p | 293 | metadata[e] = p |
302 | } | 294 | } |
303 | } | 295 | } |
304 | rows.Close() | 296 | rows.Close() |
305 | } | 297 | } |
306 | } | 298 | } |
307 | 299 | ||
308 | /* | 300 | /* |
309 | func ModifyMetadataForEntity(entityType string, p *Payload) error { | 301 | func ModifyMetadataForEntity(entityType string, p *Payload) error { |
310 | md, err := json.Marshal(*p) | 302 | md, err := json.Marshal(*p) |
311 | if err != nil { | 303 | if err != nil { |
312 | return err | 304 | return err |
313 | } | 305 | } |
314 | 306 | ||
315 | mu.Lock() | 307 | mu.Lock() |
316 | defer mu.Unlock() | 308 | defer mu.Unlock() |
317 | _, err = metadataDB.PrepAndExe(`update entities set | 309 | _, err = metadataDB.PrepAndExe(`update entities set |
318 | metadata = :1 | 310 | metadata = :1 |
319 | where projekat = :2 | 311 | where projekat = :2 |
320 | and entity_type = :3`, | 312 | and entity_type = :3`, |
321 | string(md), | 313 | string(md), |
322 | activeProject, | 314 | activeProject, |
323 | entityType) | 315 | entityType) |
324 | if err != nil { | 316 | if err != nil { |
325 | return err | 317 | return err |
326 | } | 318 | } |
327 | return nil | 319 | return nil |
328 | } | 320 | } |
329 | 321 | ||
330 | func DeleteEntityModel(entityType string) error { | 322 | func DeleteEntityModel(entityType string) error { |
331 | _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType) | 323 | _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType) |
332 | if err == nil { | 324 | if err == nil { |
333 | mu.Lock() | 325 | mu.Lock() |
334 | delete(metadata, entityType) | 326 | delete(metadata, entityType) |
335 | mu.Unlock() | 327 | mu.Unlock() |
336 | } | 328 | } |
localization.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "encoding/json" | 4 | "encoding/json" |
5 | "errors" | 5 | "errors" |
6 | "io/ioutil" | 6 | "io/ioutil" |
7 | ) | 7 | ) |
8 | 8 | ||
9 | type Dictionary struct { | 9 | type Dictionary struct { |
10 | locales map[string]map[string]string | 10 | locales map[string]map[string]string |
11 | supported []string | 11 | supported []string |
12 | defaultLocale string | 12 | defaultLocale string |
13 | } | 13 | } |
14 | 14 | ||
15 | func NewDictionary() Dictionary { | 15 | func NewDictionary() *Dictionary { |
16 | return Dictionary{ | 16 | return &Dictionary{ |
17 | locales: map[string]map[string]string{}, | 17 | locales: map[string]map[string]string{}, |
18 | } | 18 | } |
19 | } | 19 | } |
20 | 20 | ||
21 | func (d *Dictionary) AddLocale(loc, filePath string) error { | 21 | func (d *Dictionary) AddLocale(loc, filePath string) error { |
22 | file, err := ioutil.ReadFile(filePath) | 22 | file, err := ioutil.ReadFile(filePath) |
23 | if err != nil { | 23 | if err != nil { |
24 | return err | 24 | return err |
25 | } | 25 | } |
26 | 26 | ||
27 | var data interface{} | 27 | var data interface{} |
28 | err = json.Unmarshal(file, &data) | 28 | err = json.Unmarshal(file, &data) |
29 | if err != nil { | 29 | if err != nil { |
30 | return err | 30 | return err |
31 | } | 31 | } |
32 | 32 | ||
33 | l := map[string]string{} | 33 | l := map[string]string{} |
34 | for k, v := range data.(map[string]interface{}) { | 34 | for k, v := range data.(map[string]interface{}) { |
35 | l[k] = v.(string) | 35 | l[k] = v.(string) |
36 | } | 36 | } |
37 | d.locales[loc] = l | 37 | d.locales[loc] = l |
38 | d.supported = append(d.supported, loc) | 38 | d.supported = append(d.supported, loc) |
39 | 39 | ||
40 | return nil | 40 | return nil |
41 | } | 41 | } |
42 | 42 | ||
43 | func (d *Dictionary) Translate(loc, key string) string { | 43 | func (d *Dictionary) Translate(loc, key string) string { |
44 | return d.locales[loc][key] | 44 | return d.locales[loc][key] |
45 | } | 45 | } |
46 | 46 | ||
47 | func (d *Dictionary) HasLocale(loc string) bool { | 47 | func (d *Dictionary) HasLocale(loc string) bool { |
48 | for _, v := range d.supported { | 48 | for _, v := range d.supported { |
49 | if v == loc { | 49 | if v == loc { |
50 | return true | 50 | return true |
51 | } | 51 | } |
52 | } | 52 | } |
53 | return false | 53 | return false |
54 | } | 54 | } |
55 | 55 | ||
56 | func (d *Dictionary) SetDefaultLocale(loc string) error { | 56 | func (d *Dictionary) SetDefaultLocale(loc string) error { |
57 | if !d.HasLocale(loc) { | 57 | if !d.HasLocale(loc) { |
58 | return errors.New("dictionary does not contain translations for " + loc) | 58 | return errors.New("dictionary does not contain translations for " + loc) |
59 | } | 59 | } |
60 | d.defaultLocale = loc | 60 | d.defaultLocale = loc |
61 | return nil | 61 | return nil |
62 | } | 62 | } |
63 | 63 | ||
64 | func (d *Dictionary) GetDefaultLocale() string { | 64 | func (d *Dictionary) GetDefaultLocale() string { |
65 | return d.defaultLocale | 65 | return d.defaultLocale |
66 | } | 66 | } |
67 | 67 |
main.go
File was created | 1 | package webutility | |
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "git.to-net.rs/marko.tikvic/gologger" | ||
7 | ) | ||
8 | |||
9 | var logger *gologger.Logger | ||
10 | var reqLogger *gologger.Logger | ||
11 | |||
12 | func init() { | ||
13 | var err error | ||
14 | logger, err = gologger.New("metadata", gologger.MaxLogSize100KB) | ||
15 | if err != nil { | ||
16 | fmt.Printf("webutility: %s\n", err.Error()) | ||
17 | } | ||
18 | reqLogger, err = gologger.New("http", gologger.MaxLogSize5MB) | ||
19 | if err != nil { | ||
20 | fmt.Printf("webutility: %s\n", err.Error()) | ||
21 | } | ||
22 | } | ||
23 |
middleware.go
File was created | 1 | package webutility | |
2 | |||
3 | import ( | ||
4 | "net/http" | ||
5 | "time" | ||
6 | ) | ||
7 | |||
8 | func WithSetHeaders(h http.Handler) http.Handler { | ||
9 | return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||
10 | SetDefaultHeaders(w) | ||
11 | if req.Method == http.MethodOptions { | ||
12 | return | ||
13 | } | ||
14 | h.ServeHTTP(w, req) | ||
15 | }) | ||
16 | } | ||
17 | |||
18 | func WithParseForm(h http.Handler) http.Handler { | ||
19 | return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||
20 | err := req.ParseForm() | ||
21 | if err != nil { | ||
22 | BadRequest(w, req, err.Error()) | ||
23 | return | ||
24 | } | ||
25 | h.ServeHTTP(w, req) | ||
26 | }) | ||
27 | } | ||
28 | |||
29 | func WithLog(h http.Handler) http.Handler { | ||
30 | return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||
31 | reqLogger.LogRequest(req, "") | ||
32 | t1 := time.Now() | ||
33 | h.ServeHTTP(w, req) | ||
34 | t2 := time.Now() | ||
35 | reqLogger.LogResponse(w, t2.Sub(t1)) | ||
36 | }) | ||
37 | } | ||
38 | |||
39 | func WithAuth(h http.Handler, authorizedRoles []string) http.Handler { | ||
40 | return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||
41 | if _, ok := AuthCheck(req, authorizedRoles); !ok { | ||
42 | Unauthorized(w, req, "") | ||
43 | return | ||
44 | } | ||
45 | h.ServeHTTP(w, req) | ||
46 | }) | ||
47 | } | ||
48 |