Commit 6620591d867ec0a4fc4a3dcc5ed206061d829b1c

Authored by Marko Tikvić
1 parent 62a69bedaa
Exists in master and in 1 other branch v2

moved DeliverPaylaod to http_utility

1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "crypto/rand" 4 "crypto/rand"
5 "crypto/sha256" 5 "crypto/sha256"
6 "encoding/hex" 6 "encoding/hex"
7 "errors" 7 "errors"
8 "net/http" 8 "net/http"
9 "strings" 9 "strings"
10 "time" 10 "time"
11 11
12 "github.com/dgrijalva/jwt-go" 12 "github.com/dgrijalva/jwt-go"
13 ) 13 )
14 14
15 const OneDay = time.Hour * 24 15 const OneDay = time.Hour * 24
16 const OneWeek = OneDay * 7 16 const OneWeek = OneDay * 7
17 const saltSize = 32 17 const saltSize = 32
18 const appName = "korisnicki-centar" 18 const appName = "korisnicki-centar"
19 const secret = "korisnicki-centar-api" 19 const secret = "korisnicki-centar-api"
20 20
21 type Role struct { 21 type Role struct {
22 Name string `json:"name"` 22 Name string `json:"name"`
23 ID uint32 `json:"id"` 23 ID uint32 `json:"id"`
24 } 24 }
25 25
26 // TokenClaims are JWT token claims. 26 // TokenClaims are JWT token claims.
27 type TokenClaims struct { 27 type TokenClaims struct {
28 Token string `json:"access_token"` 28 Token string `json:"access_token"`
29 TokenType string `json:"token_type"` 29 TokenType string `json:"token_type"`
30 Username string `json:"username"` 30 Username string `json:"username"`
31 Role string `json:"role"` 31 Role string `json:"role"`
32 RoleID uint32 `json:"role_id"` 32 RoleID uint32 `json:"role_id"`
33 ExpiresIn int64 `json:"expires_in"` 33 ExpiresIn int64 `json:"expires_in"`
34 jwt.StandardClaims // extending a struct 34
35 // extending a struct
36 jwt.StandardClaims
35 } 37 }
36 38
37 // CredentialsStruct is an instace of username/password values. 39 // CredentialsStruct is an instace of username/password values.
38 type CredentialsStruct struct { 40 type CredentialsStruct struct {
39 Username string `json:"username"` 41 Username string `json:"username"`
40 Password string `json:"password"` 42 Password string `json:"password"`
41 } 43 }
42 44
43 // ValidateCredentials hashes pass and salt and returns comparison result with resultHash 45 // ValidateCredentials hashes pass and salt and returns comparison result with resultHash
44 func ValidateCredentials(pass, salt, resultHash string) bool { 46 func ValidateCredentials(pass, salt, resultHash string) bool {
45 hash, _, err := CreateHash(pass, salt) 47 hash, _, err := CreateHash(pass, salt)
46 if err != nil { 48 if err != nil {
47 return false 49 return false
48 } 50 }
49 return hash == resultHash 51 return hash == resultHash
50 } 52 }
51 53
52 // CreateHash hashes str using SHA256. 54 // CreateHash hashes str using SHA256.
53 // If the presalt parameter is not provided CreateHash will generate new salt string. 55 // If the presalt parameter is not provided CreateHash will generate new salt string.
54 // Returns hash and salt strings or an error if it fails. 56 // Returns hash and salt strings or an error if it fails.
55 func CreateHash(str, presalt string) (hash, salt string, err error) { 57 func CreateHash(str, presalt string) (hash, salt string, err error) {
56 // chech if message is presalted 58 // chech if message is presalted
57 if presalt == "" { 59 if presalt == "" {
58 salt, err = randomSalt() 60 salt, err = randomSalt()
59 if err != nil { 61 if err != nil {
60 return "", "", err 62 return "", "", err
61 } 63 }
62 } else { 64 } else {
63 salt = presalt 65 salt = presalt
64 } 66 }
65 67
66 // convert strings to raw byte slices 68 // convert strings to raw byte slices
67 rawstr := []byte(str) 69 rawstr := []byte(str)
68 rawsalt, err := hex.DecodeString(salt) 70 rawsalt, err := hex.DecodeString(salt)
69 if err != nil { 71 if err != nil {
70 return "", "", err 72 return "", "", err
71 } 73 }
72 74
73 rawdata := make([]byte, len(rawstr)+len(rawsalt)) 75 rawdata := make([]byte, len(rawstr)+len(rawsalt))
74 rawdata = append(rawdata, rawstr...) 76 rawdata = append(rawdata, rawstr...)
75 rawdata = append(rawdata, rawsalt...) 77 rawdata = append(rawdata, rawsalt...)
76 78
77 // hash message + salt 79 // hash message + salt
78 hasher := sha256.New() 80 hasher := sha256.New()
79 hasher.Write(rawdata) 81 hasher.Write(rawdata)
80 rawhash := hasher.Sum(nil) 82 rawhash := hasher.Sum(nil)
81 83
82 hash = hex.EncodeToString(rawhash) 84 hash = hex.EncodeToString(rawhash)
83 return hash, salt, nil 85 return hash, salt, nil
84 } 86 }
85 87
86 // CreateAuthToken returns JWT token with encoded username, role, expiration date and issuer claims. 88 // CreateAuthToken returns JWT token with encoded username, role, expiration date and issuer claims.
87 // It returns an error if it fails. 89 // It returns an error if it fails.
88 func CreateAuthToken(username string, role Role) (TokenClaims, error) { 90 func CreateAuthToken(username string, role Role) (TokenClaims, error) {
89 t0 := (time.Now()).Unix() 91 t0 := (time.Now()).Unix()
90 t1 := (time.Now().Add(OneWeek)).Unix() 92 t1 := (time.Now().Add(OneWeek)).Unix()
91 claims := TokenClaims{ 93 claims := TokenClaims{
92 TokenType: "Bearer", 94 TokenType: "Bearer",
93 Username: username, 95 Username: username,
94 Role: role.Name, 96 Role: role.Name,
95 RoleID: role.ID, 97 RoleID: role.ID,
96 ExpiresIn: t1 - t0, 98 ExpiresIn: t1 - t0,
97 } 99 }
98 // initialize jwt.StandardClaims fields (anonymous struct) 100 // initialize jwt.StandardClaims fields (anonymous struct)
99 claims.IssuedAt = t0 101 claims.IssuedAt = t0
100 claims.ExpiresAt = t1 102 claims.ExpiresAt = t1
101 claims.Issuer = appName 103 claims.Issuer = appName
102 104
103 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 105 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
104 token, err := jwtToken.SignedString([]byte(secret)) 106 token, err := jwtToken.SignedString([]byte(secret))
105 if err != nil { 107 if err != nil {
106 return TokenClaims{}, err 108 return TokenClaims{}, err
107 } 109 }
108 claims.Token = token 110 claims.Token = token
109 return claims, nil 111 return claims, nil
110 } 112 }
111 113
112 // RefreshAuthToken prolongs JWT token's expiration date for one week. 114 // RefreshAuthToken prolongs JWT token's expiration date for one week.
113 // It returns new JWT token or an error if it fails. 115 // It returns new JWT token or an error if it fails.
114 func RefreshAuthToken(req *http.Request) (TokenClaims, error) { 116 func RefreshAuthToken(req *http.Request) (TokenClaims, error) {
115 authHead := req.Header.Get("Authorization") 117 authHead := req.Header.Get("Authorization")
116 tokenstr := strings.TrimPrefix(authHead, "Bearer ") 118 tokenstr := strings.TrimPrefix(authHead, "Bearer ")
117 token, err := jwt.ParseWithClaims(tokenstr, &TokenClaims{}, secretFunc) 119 token, err := jwt.ParseWithClaims(tokenstr, &TokenClaims{}, secretFunc)
118 if err != nil { 120 if err != nil {
119 return TokenClaims{}, err 121 return TokenClaims{}, err
120 } 122 }
121 123
122 // type assertion 124 // type assertion
123 claims, ok := token.Claims.(*TokenClaims) 125 claims, ok := token.Claims.(*TokenClaims)
124 if !ok || !token.Valid { 126 if !ok || !token.Valid {
125 return TokenClaims{}, errors.New("token is not valid") 127 return TokenClaims{}, errors.New("token is not valid")
126 } 128 }
127 129
128 // extend token expiration date 130 // extend token expiration date
129 return CreateAuthToken(claims.Username, Role{claims.Role, claims.RoleID}) 131 return CreateAuthToken(claims.Username, Role{claims.Role, claims.RoleID})
130 } 132 }
131 133
132 // RbacCheck returns true if role that made HTTP request is authorized to 134 // RbacCheck returns true if role that made HTTP request is authorized to
133 // access the resource it is targeting. 135 // access the resource it is targeting.
134 // It exctracts user's role from the JWT token located in Authorization header of 136 // It exctracts user's role from the JWT token located in Authorization header of
135 // http.Request and then compares it with the list of supplied roles and returns 137 // http.Request and then compares it with the list of supplied roles and returns
136 // true if there's a match, if "*" is provided or if the authRoles is nil. 138 // true if there's a match, if "*" is provided or if the authRoles is nil.
137 // Otherwise it returns false. 139 // Otherwise it returns false.
138 func RbacCheck(req *http.Request, authRoles []string) bool { 140 func RbacCheck(req *http.Request, authRoles []string) bool {
139 if authRoles == nil { 141 if authRoles == nil {
140 return true 142 return true
141 } 143 }
142 144
143 // validate token and check expiration date 145 // validate token and check expiration date
144 claims, err := GetTokenClaims(req) 146 claims, err := GetTokenClaims(req)
145 if err != nil { 147 if err != nil {
146 return false 148 return false
147 } 149 }
148 // check if token has expired 150 // check if token has expired
149 if claims.ExpiresAt < (time.Now()).Unix() { 151 if claims.ExpiresAt < (time.Now()).Unix() {
150 return false 152 return false
151 } 153 }
152 154
153 // check if role extracted from token matches 155 // check if role extracted from token matches
154 // any of the provided (allowed) ones 156 // any of the provided (allowed) ones
155 for _, r := range authRoles { 157 for _, r := range authRoles {
156 if claims.Role == r || r == "*" { 158 if claims.Role == r || r == "*" {
157 return true 159 return true
158 } 160 }
159 } 161 }
160 162
161 return false 163 return false
162 } 164 }
163 165
164 // TODO 166 // TODO
165 func AuthCheck(req *http.Request, authRoles []string) (*TokenClaims, error) { 167 func AuthCheck(req *http.Request, authRoles []string) (*TokenClaims, error) {
166 if authRoles == nil { 168 if authRoles == nil {
167 return &TokenClaims{}, nil 169 return &TokenClaims{}, nil
168 } 170 }
169 171
170 // validate token and check expiration date 172 // validate token and check expiration date
171 claims, err := GetTokenClaims(req) 173 claims, err := GetTokenClaims(req)
172 if err != nil { 174 if err != nil {
173 return &TokenClaims{}, err 175 return &TokenClaims{}, err
174 } 176 }
175 // check if token has expired 177 // check if token has expired
176 if claims.ExpiresAt < (time.Now()).Unix() { 178 if claims.ExpiresAt < (time.Now()).Unix() {
177 return &TokenClaims{}, errors.New("token has expired") 179 return &TokenClaims{}, errors.New("token has expired")
178 } 180 }
179 181
180 // check if role extracted from token matches 182 // check if role extracted from token matches
181 // any of the provided (allowed) ones 183 // any of the provided (allowed) ones
182 for _, r := range authRoles { 184 for _, r := range authRoles {
183 if claims.Role == r || r == "*" { 185 if claims.Role == r || r == "*" {
184 return claims, nil 186 return claims, nil
185 } 187 }
186 } 188 }
187 189
188 return claims, errors.New("role is not authorized") 190 return claims, errors.New("role is not authorized")
189 } 191 }
190 192
191 // GetTokenClaims extracts JWT claims from Authorization header of the request. 193 // GetTokenClaims extracts JWT claims from Authorization header of the request.
192 // Returns token claims or an error. 194 // Returns token claims or an error.
193 func GetTokenClaims(req *http.Request) (*TokenClaims, error) { 195 func GetTokenClaims(req *http.Request) (*TokenClaims, error) {
194 // check for and strip 'Bearer' prefix 196 // check for and strip 'Bearer' prefix
195 var tokstr string 197 var tokstr string
196 authHead := req.Header.Get("Authorization") 198 authHead := req.Header.Get("Authorization")
197 if ok := strings.HasPrefix(authHead, "Bearer "); ok { 199 if ok := strings.HasPrefix(authHead, "Bearer "); ok {
198 tokstr = strings.TrimPrefix(authHead, "Bearer ") 200 tokstr = strings.TrimPrefix(authHead, "Bearer ")
199 } else { 201 } else {
200 return &TokenClaims{}, errors.New("authorization header in incomplete") 202 return &TokenClaims{}, errors.New("authorization header in incomplete")
201 } 203 }
202 204
203 token, err := jwt.ParseWithClaims(tokstr, &TokenClaims{}, secretFunc) 205 token, err := jwt.ParseWithClaims(tokstr, &TokenClaims{}, secretFunc)
204 if err != nil { 206 if err != nil {
205 return &TokenClaims{}, err 207 return &TokenClaims{}, err
206 } 208 }
207 209
208 // type assertion 210 // type assertion
209 claims, ok := token.Claims.(*TokenClaims) 211 claims, ok := token.Claims.(*TokenClaims)
210 if !ok || !token.Valid { 212 if !ok || !token.Valid {
211 return &TokenClaims{}, errors.New("token is not valid") 213 return &TokenClaims{}, errors.New("token is not valid")
212 } 214 }
213 215
214 return claims, nil 216 return claims, nil
215 } 217 }
216 218
217 // randomSalt returns a string of random characters of 'saltSize' length. 219 // randomSalt returns a string of random characters of 'saltSize' length.
218 func randomSalt() (s string, err error) { 220 func randomSalt() (s string, err error) {
219 rawsalt := make([]byte, saltSize) 221 rawsalt := make([]byte, saltSize)
220 222
221 _, err = rand.Read(rawsalt) 223 _, err = rand.Read(rawsalt)
222 if err != nil { 224 if err != nil {
223 return "", err 225 return "", err
224 } 226 }
225 227
226 s = hex.EncodeToString(rawsalt) 228 s = hex.EncodeToString(rawsalt)
227 return s, nil 229 return s, nil
228 } 230 }
229 231
230 // secretFunc returns byte slice of API secret keyword. 232 // secretFunc returns byte slice of API secret keyword.
231 func secretFunc(token *jwt.Token) (interface{}, error) { 233 func secretFunc(token *jwt.Token) (interface{}, error) {
232 return []byte(secret), nil 234 return []byte(secret), nil
233 } 235 }
234 236
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "encoding/json" 4 "encoding/json"
5 "net/http" 5 "net/http"
6 ) 6 )
7 7
8 const ( 8 const (
9 templateHttpErr500_EN = "An internal server error has occurred." 9 templateHttpErr500_EN = "An internal server error has occurred."
10 templateHttpErr500_RS = "Došlo je do greške na serveru." 10 templateHttpErr500_RS = "Došlo je do greške na serveru."
11 templateHttpErr400_EN = "Bad request: invalid request body." 11 templateHttpErr400_EN = "Bad request: invalid request body."
12 templateHttpErr400_RS = "Neispravan zahtev." 12 templateHttpErr400_RS = "Neispravan zahtev."
13 templateHttpErr404_EN = "Resource not found."
14 templateHttpErr404_RS = "Objekat nije pronadjen."
13 templateHttpErr401_EN = "Unauthorized request." 15 templateHttpErr401_EN = "Unauthorized request."
14 templateHttpErr401_RS = "Neautorizovan zahtev." 16 templateHttpErr401_RS = "Neautorizovan zahtev."
15 ) 17 )
16 18
17 type httpError struct { 19 type httpError struct {
18 Error []HttpErrorDesc `json:"error"` 20 Error []HttpErrorDesc `json:"error"`
19 Request string `json:"request"` 21 Request string `json:"request"`
20 } 22 }
21 23
22 type HttpErrorDesc struct { 24 type HttpErrorDesc struct {
23 Lang string `json:"lang"` 25 Lang string `json:"lang"`
24 Desc string `json:"description"` 26 Desc string `json:"description"`
25 } 27 }
26 28
29 // DeliverPayload encodes payload as JSON to w.
30 func DeliverPayload(w http.ResponseWriter, payload Payload) {
31 // Don't write status OK in the headers here. Leave it up for the caller.
32 // E.g. Status 201.
33 json.NewEncoder(w).Encode(payload)
34 payload.Data = nil
35 }
36
27 // ErrorResponse writes HTTP error to w. 37 // ErrorResponse writes HTTP error to w.
28 func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) { 38 func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) {
29 //err := httpError{desc, r.Method + " " + r.URL.Path}
30 err := httpError{desc, r.Method + " " + r.RequestURI} 39 err := httpError{desc, r.Method + " " + r.RequestURI}
31 w.WriteHeader(code) 40 w.WriteHeader(code)
32 json.NewEncoder(w).Encode(err) 41 json.NewEncoder(w).Encode(err)
33 } 42 }
34 43
44 // NotFoundResponse writes HTTP error 404 to w.
45 func NotFoundResponse(w http.ResponseWriter, req *http.Request) {
46 ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{
47 {"en", templateHttpErr404_EN},
48 {"rs", templateHttpErr404_RS},
49 })
50 }
51
35 // BadRequestResponse writes HTTP error 400 to w. 52 // BadRequestResponse writes HTTP error 400 to w.
36 func BadRequestResponse(w http.ResponseWriter, req *http.Request) { 53 func BadRequestResponse(w http.ResponseWriter, req *http.Request) {
37 ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{ 54 ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{
38 {"en", templateHttpErr400_EN}, 55 {"en", templateHttpErr400_EN},
39 {"rs", templateHttpErr400_RS}, 56 {"rs", templateHttpErr400_RS},
40 }) 57 })
41 } 58 }
42 59
43 // InternalSeverErrorResponse writes HTTP error 500 to w. 60 // InternalSeverErrorResponse writes HTTP error 500 to w.
44 func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) { 61 func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) {
45 ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{ 62 ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{
46 {"en", templateHttpErr500_EN}, 63 {"en", templateHttpErr500_EN},
47 {"rs", templateHttpErr500_RS}, 64 {"rs", templateHttpErr500_RS},
48 }) 65 })
49 } 66 }
50 67
51 // UnauthorizedError writes HTTP error 401 to w. 68 // UnauthorizedError writes HTTP error 401 to w.
52 func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) { 69 func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) {
53 w.Header().Set("WWW-Authenticate", "Bearer") 70 w.Header().Set("WWW-Authenticate", "Bearer")
54 ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{ 71 ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{
55 {"en", templateHttpErr401_EN}, 72 {"en", templateHttpErr401_EN},
56 {"rs", templateHttpErr401_RS}, 73 {"rs", templateHttpErr401_RS},
57 }) 74 })
58 } 75 }
59 76
60 // NotFoundHandler writes HTTP error 404 to w. 77 // NotFoundHandler writes HTTP error 404 to w.
61 func NotFoundHandler(w http.ResponseWriter, req *http.Request) { 78 func NotFoundHandler(w http.ResponseWriter, req *http.Request) {
62 SetDefaultHeaders(w) 79 SetDefaultHeaders(w)
63 if req.Method == "OPTIONS" { 80 if req.Method == "OPTIONS" {
64 return 81 return
65 } 82 }
66 ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{ 83 ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{
67 {"en", "Not found."}, 84 {"en", "Not found."},
68 {"rs", "Traženi resurs ne postoji."}, 85 {"rs", "Traženi resurs ne postoji."},
69 }) 86 })
70 } 87 }
71 88
72 // SetDefaultHeaders set's default headers for an HTTP response. 89 // SetDefaultHeaders set's default headers for an HTTP response.
73 func SetDefaultHeaders(w http.ResponseWriter) { 90 func SetDefaultHeaders(w http.ResponseWriter) {
74 w.Header().Set("Access-Control-Allow-Origin", "*") 91 w.Header().Set("Access-Control-Allow-Origin", "*")
75 w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS") 92 w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
76 w.Header().Set("Access-Control-Allow-Headers", `Accept, Content-Type, 93 w.Header().Set("Access-Control-Allow-Headers", `Accept, Content-Type,
77 Content-Length, Accept-Encoding, X-CSRF-Token, Authorization`) 94 Content-Length, Accept-Encoding, X-CSRF-Token, Authorization`)
78 w.Header().Set("Content-Type", "application/json; charset=utf-8") 95 w.Header().Set("Content-Type", "application/json; charset=utf-8")
79 } 96 }
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "encoding/json" 4 "encoding/json"
5 "errors" 5 "errors"
6 "fmt" 6 "fmt"
7 "io" 7 "io"
8 "net/http" 8 "net/http"
9 "sync" 9 "sync"
10 10
11 "gopkg.in/rana/ora.v4" 11 "gopkg.in/rana/ora.v4"
12 ) 12 )
13 13
14 var mu = &sync.Mutex{} 14 var mu = &sync.Mutex{}
15 var payloads []Payload 15 var payloads []Payload
16 16
17 type LangMap map[string]map[string]string 17 type LangMap map[string]map[string]string
18 18
19 type Field struct { 19 type Field struct {
20 Parameter string `json:"param"` 20 Parameter string `json:"param"`
21 Type string `json:"type"` 21 Type string `json:"type"`
22 Visible bool `json:"visible"` 22 Visible bool `json:"visible"`
23 Editable bool `json:"editable"` 23 Editable bool `json:"editable"`
24 } 24 }
25 25
26 type CorrelationField struct { 26 type CorrelationField struct {
27 Result string `json:"result"` 27 Result string `json:"result"`
28 Elements []string `json:"elements"` 28 Elements []string `json:"elements"`
29 Type string `json:"type"` 29 Type string `json:"type"`
30 } 30 }
31 31
32 type Translation struct { 32 type Translation struct {
33 Language string `json:"language"` 33 Language string `json:"language"`
34 FieldsLabels map[string]string `json:"fieldsLabels"` 34 FieldsLabels map[string]string `json:"fieldsLabels"`
35 } 35 }
36 36
37 type Payload struct { 37 type Payload struct {
38 Type string `json:"type"` 38 Type string `json:"type"`
39 Method string `json:"method"` 39 Method string `json:"method"`
40 Params map[string]string `json:"params"` 40 Params map[string]string `json:"params"`
41 Lang []Translation `json:"lang"` 41 Lang []Translation `json:"lang"`
42 Fields []Field `json:"fields"` 42 Fields []Field `json:"fields"`
43 Correlations []CorrelationField `json:"correlationFields"` 43 Correlations []CorrelationField `json:"correlationFields"`
44 IdField string `json:"idField"` 44 IdField string `json:"idField"`
45 45
46 // Data holds JSON payload. It can't be used for itteration. 46 // Data holds JSON payload. It can't be used for itteration.
47 Data interface{} `json:"data"` 47 Data interface{} `json:"data"`
48 } 48 }
49 49
50 // InitPayloadsMetaData loads all payloads in the payloads variable. 50 // InitPayloadsMetaData loads all payloads in the payloads variable.
51 // Returns an error if it fails. 51 // Returns an error if it fails.
52 func InitPayloadsMetaData(db *ora.Ses, project string) error { 52 func InitPayloadsMetaData(db *ora.Ses, project string) error {
53 payloads = make([]Payload, 0) 53 payloads = make([]Payload, 0)
54 54
55 jsonbuf, err := fetchJSON(db, project) 55 jsonbuf, err := fetchJSON(db, project)
56 if err != nil { 56 if err != nil {
57 return err 57 return err
58 } 58 }
59 59
60 mu.Lock() 60 mu.Lock()
61 defer mu.Unlock() 61 defer mu.Unlock()
62 json.Unmarshal(jsonbuf, &payloads) 62 json.Unmarshal(jsonbuf, &payloads)
63 if len(payloads) == 0 { 63 if len(payloads) == 0 {
64 return errors.New("tables config is corrupt") 64 return errors.New("tables config is corrupt")
65 } 65 }
66 return nil 66 return nil
67 } 67 }
68 68
69 // DecodeJSON decodes JSON data from r to v. 69 // DecodeJSON decodes JSON data from r to v.
70 // Returns an error if it fails. 70 // Returns an error if it fails.
71 func DecodeJSON(r io.Reader, v interface{}) error { 71 func DecodeJSON(r io.Reader, v interface{}) error {
72 return json.NewDecoder(r).Decode(v) 72 return json.NewDecoder(r).Decode(v)
73 } 73 }
74 74
75 // NewPayload returs a payload sceleton for provided table. 75 // NewPayload returs a payload sceleton for provided table.
76 func NewPayload(r *http.Request, table string) Payload { 76 func NewPayload(r *http.Request, table string) Payload {
77 var pload Payload 77 var pload Payload
78 78
79 pload.Method = r.Method + " " + r.RequestURI 79 pload.Method = r.Method + " " + r.RequestURI
80 pload.Type = table 80 pload.Type = table
81 if table != "" { 81 if table != "" {
82 pload.Params = make(map[string]string, 0) 82 pload.Params = make(map[string]string, 0)
83 pload.Lang = translations(table) 83 pload.Lang = translations(table)
84 pload.Fields = fields(table) 84 pload.Fields = fields(table)
85 pload.IdField = id(table) 85 pload.IdField = id(table)
86 pload.Correlations = correlations(table) 86 pload.Correlations = correlations(table)
87 } 87 }
88 return pload 88 return pload
89 } 89 }
90 90
91 // DeliverPayload encodes payload to w.
92 func DeliverPayload(w http.ResponseWriter, payload Payload) {
93 json.NewEncoder(w).Encode(payload)
94 payload.Data = nil
95 }
96
97 // translations returns a slice of translations for a payload/table of ptype type. 91 // translations returns a slice of translations for a payload/table of ptype type.
98 func translations(ptype string) []Translation { 92 func translations(ptype string) []Translation {
99 var translations []Translation 93 var translations []Translation
100 94
101 for _, pload := range payloads { 95 for _, pload := range payloads {
102 if pload.Type == ptype { 96 if pload.Type == ptype {
103 for _, t := range pload.Lang { 97 for _, t := range pload.Lang {
104 translations = append(translations, Translation{ 98 translations = append(translations, Translation{
105 Language: t.Language, 99 Language: t.Language,
106 FieldsLabels: t.FieldsLabels, 100 FieldsLabels: t.FieldsLabels,
107 }) 101 })
108 } 102 }
109 } 103 }
110 } 104 }
111 105
112 return translations 106 return translations
113 } 107 }
114 108
115 // fields returns a slice of fields for a payload/table of ptype type. 109 // fields returns a slice of fields for a payload/table of ptype type.
116 func fields(ptype string) []Field { 110 func fields(ptype string) []Field {
117 var fields []Field 111 var fields []Field
118 112
119 for _, pload := range payloads { 113 for _, pload := range payloads {
120 if pload.Type == ptype { 114 if pload.Type == ptype {
121 for _, f := range pload.Fields { 115 for _, f := range pload.Fields {
122 fields = append(fields, f) 116 fields = append(fields, f)
123 } 117 }
124 } 118 }
125 } 119 }
126 120
127 return fields 121 return fields
128 } 122 }
129 123
130 // id returns the name of ID field of a payload/table of ptype type. 124 // id returns the name of ID field of a payload/table of ptype type.
131 func id(ptype string) string { 125 func id(ptype string) string {
132 for _, pload := range payloads { 126 for _, pload := range payloads {
133 if pload.Type == ptype { 127 if pload.Type == ptype {
134 return pload.IdField 128 return pload.IdField
135 } 129 }
136 } 130 }
137 return "" 131 return ""
138 } 132 }
139 133
140 // correlations returns a slice of correlation fields for a payload/table of ptype type. 134 // correlations returns a slice of correlation fields for a payload/table of ptype type.
141 func correlations(ptype string) []CorrelationField { 135 func correlations(ptype string) []CorrelationField {
142 var corr []CorrelationField 136 var corr []CorrelationField
143 137
144 for _, pload := range payloads { 138 for _, pload := range payloads {
145 if pload.Type == ptype { 139 if pload.Type == ptype {
146 for _, c := range pload.Correlations { 140 for _, c := range pload.Correlations {
147 corr = append(corr, c) 141 corr = append(corr, c)
148 } 142 }
149 } 143 }
150 } 144 }
151 145
152 return corr 146 return corr
153 } 147 }
154 148
155 // fetchJSON returns a byte slice of JSON configuration file from TABLES_CONFIG table. 149 // fetchJSON returns a byte slice of JSON configuration file from TABLES_CONFIG table.
156 // Returns an error if it fails. 150 // Returns an error if it fails.
157 func fetchJSON(db *ora.Ses, project string) ([]byte, error) { 151 func fetchJSON(db *ora.Ses, project string) ([]byte, error) {
158 db.SetCfg(db.Cfg().SetClob(ora.S)) 152 db.SetCfg(db.Cfg().SetClob(ora.S))
159 stmt, err := db.Prep(`SELECT JSON_NCLOB FROM TABLES_CONFIG WHERE PROJEKAT = `+fmt.Sprintf("'%s'", project), ora.S) 153 stmt, err := db.Prep(`SELECT JSON_NCLOB FROM TABLES_CONFIG WHERE PROJEKAT = `+fmt.Sprintf("'%s'", project), ora.S)
160 defer stmt.Close() 154 defer stmt.Close()
161 if err != nil { 155 if err != nil {
162 return nil, err 156 return nil, err
163 } 157 }
164 158
165 rset, err := stmt.Qry() 159 rset, err := stmt.Qry()
166 if err != nil { 160 if err != nil {
167 return nil, err 161 return nil, err
168 } 162 }
169 163
170 var data string 164 var data string
171 if rset.Next() { 165 if rset.Next() {
172 data = rset.Row[0].(string) 166 data = rset.Row[0].(string)
173 } 167 }
174 168
175 //fmt.Println(data) 169 //fmt.Println(data)
176 return []byte(data), nil 170 return []byte(data), nil
177 } 171 }
178 172