Commit 368c7f87bff1233ad437437804a91bd03833238a

Authored by Marko Tikvić
1 parent fbf92700f4
Exists in master

pagination work

Showing 2 changed files with 25 additions and 12 deletions   Show diff stats
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 var _issuer = "webutility" 15 var _issuer = "webutility"
16 var _secret = "webutility" 16 var _secret = "webutility"
17 17
18 // TokenClaims are JWT token claims. 18 // TokenClaims are JWT token claims.
19 type TokenClaims struct { 19 type TokenClaims struct {
20 // extending a struct 20 // extending a struct
21 jwt.StandardClaims 21 jwt.StandardClaims
22 22
23 // custom claims 23 // custom claims
24 Token string `json:"access_token"` 24 Token string `json:"access_token"`
25 TokenType string `json:"token_type"` 25 TokenType string `json:"token_type"`
26 Username string `json:"username"` 26 Username string `json:"username"`
27 RoleName string `json:"role"` 27 RoleName string `json:"role"`
28 RoleID int64 `json:"role_id"` 28 RoleID int64 `json:"role_id"`
29 ExpiresIn int64 `json:"expires_in"` 29 ExpiresIn int64 `json:"expires_in"`
30 } 30 }
31 31
32 func InitJWT(issuer, secret string) { 32 func InitJWT(issuer, secret string) {
33 _issuer = issuer 33 _issuer = issuer
34 _secret = secret 34 _secret = secret
35 } 35 }
36 36
37 // ValidateHash hashes pass and salt and returns comparison result with resultHash 37 // ValidateHash hashes pass and salt and returns comparison result with resultHash
38 func ValidateHash(pass, salt, resultHash string) (bool, error) { 38 func ValidateHash(pass, salt, resultHash string) (bool, error) {
39 hash, _, err := CreateHash(pass, salt) 39 hash, _, err := CreateHash(pass, salt)
40 if err != nil { 40 if err != nil {
41 return false, err 41 return false, err
42 } 42 }
43 res := hash == resultHash 43 res := hash == resultHash
44 return res, nil 44 return res, nil
45 } 45 }
46 46
47 // CreateHash hashes str using SHA256. 47 // CreateHash hashes str using SHA256.
48 // If the presalt parameter is not provided CreateHash will generate new salt string. 48 // If the presalt parameter is not provided CreateHash will generate new salt string.
49 // Returns hash and salt strings or an error if it fails. 49 // Returns hash and salt strings or an error if it fails.
50 func CreateHash(str, presalt string) (hash, salt string, err error) { 50 func CreateHash(str, presalt string) (hash, salt string, err error) {
51 // chech if message is presalted 51 // chech if message is presalted
52 if presalt == "" { 52 if presalt == "" {
53 salt, err = randomSalt() 53 salt, err = randomSalt()
54 if err != nil { 54 if err != nil {
55 return "", "", err 55 return "", "", err
56 } 56 }
57 } else { 57 } else {
58 salt = presalt 58 salt = presalt
59 } 59 }
60 60
61 // convert strings to raw byte slices 61 // convert strings to raw byte slices
62 rawstr := []byte(str) 62 rawstr := []byte(str)
63 rawsalt, err := hex.DecodeString(salt) 63 rawsalt, err := hex.DecodeString(salt)
64 if err != nil { 64 if err != nil {
65 return "", "", err 65 return "", "", err
66 } 66 }
67 67
68 rawdata := make([]byte, len(rawstr)+len(rawsalt)) 68 rawdata := make([]byte, len(rawstr)+len(rawsalt))
69 rawdata = append(rawdata, rawstr...) 69 rawdata = append(rawdata, rawstr...)
70 rawdata = append(rawdata, rawsalt...) 70 rawdata = append(rawdata, rawsalt...)
71 71
72 // hash message + salt 72 // hash message + salt
73 hasher := sha256.New() 73 hasher := sha256.New()
74 hasher.Write(rawdata) 74 hasher.Write(rawdata)
75 rawhash := hasher.Sum(nil) 75 rawhash := hasher.Sum(nil)
76 76
77 hash = hex.EncodeToString(rawhash) 77 hash = hex.EncodeToString(rawhash)
78 return hash, salt, nil 78 return hash, salt, nil
79 } 79 }
80 80
81 // CreateAuthToken returns JWT token with encoded username, role, expiration date and issuer claims. 81 // CreateAuthToken returns JWT token with encoded username, role, expiration date and issuer claims.
82 // It returns an error if it fails. 82 // It returns an error if it fails.
83 func CreateAuthToken(username string, roleName string, roleID int64) (TokenClaims, error) { 83 func CreateAuthToken(username string, roleName string, roleID int64) (TokenClaims, error) {
84 t0 := (time.Now()).Unix() 84 t0 := (time.Now()).Unix()
85 t1 := (time.Now().Add(time.Hour * 24 * 7)).Unix() 85 t1 := (time.Now().Add(time.Hour * 24 * 7)).Unix()
86 claims := TokenClaims{ 86 claims := TokenClaims{
87 TokenType: "Bearer", 87 TokenType: "Bearer",
88 Username: username, 88 Username: username,
89 RoleName: roleName, 89 RoleName: roleName,
90 RoleID: roleID, 90 RoleID: roleID,
91 ExpiresIn: t1 - t0, 91 ExpiresIn: t1 - t0,
92 } 92 }
93 // initialize jwt.StandardClaims fields (anonymous struct) 93 // initialize jwt.StandardClaims fields (anonymous struct)
94 claims.IssuedAt = t0 94 claims.IssuedAt = t0
95 claims.ExpiresAt = t1 95 claims.ExpiresAt = t1
96 claims.Issuer = _issuer 96 claims.Issuer = _issuer
97 97
98 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 98 jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
99 token, err := jwtToken.SignedString([]byte(_secret)) 99 token, err := jwtToken.SignedString([]byte(_secret))
100 if err != nil { 100 if err != nil {
101 return TokenClaims{}, err 101 return TokenClaims{}, err
102 } 102 }
103 claims.Token = token 103 claims.Token = token
104 return claims, nil 104 return claims, nil
105 } 105 }
106 106
107 // RefreshAuthToken returns new JWT token with same claims contained in tok but with prolonged expiration date. 107 // RefreshAuthToken returns new JWT token with same claims contained in tok but with prolonged expiration date.
108 // It returns an error if it fails. 108 // It returns an error if it fails.
109 func RefreshAuthToken(tok string) (TokenClaims, error) { 109 func RefreshAuthToken(tok string) (TokenClaims, error) {
110 token, err := jwt.ParseWithClaims(tok, &TokenClaims{}, secretFunc) 110 token, err := jwt.ParseWithClaims(tok, &TokenClaims{}, secretFunc)
111 if err != nil { 111 if err != nil {
112 if validation, ok := err.(*jwt.ValidationError); ok { 112 if validation, ok := err.(*jwt.ValidationError); ok {
113 // don't return error if token is expired 113 // don't return error if token is expired
114 // just extend it 114 // just extend it
115 if !(validation.Errors&jwt.ValidationErrorExpired != 0) { 115 if !(validation.Errors&jwt.ValidationErrorExpired != 0) {
116 return TokenClaims{}, err 116 return TokenClaims{}, err
117 } 117 }
118 } else { 118 } else {
119 return TokenClaims{}, err 119 return TokenClaims{}, err
120 } 120 }
121 } 121 }
122 122
123 // type assertion 123 // type assertion
124 claims, ok := token.Claims.(*TokenClaims) 124 claims, ok := token.Claims.(*TokenClaims)
125 if !ok { 125 if !ok {
126 return TokenClaims{}, errors.New("token is not valid") 126 return TokenClaims{}, errors.New("token is not valid")
127 } 127 }
128 128
129 // extend token expiration date 129 // extend token expiration date
130 return CreateAuthToken(claims.Username, claims.RoleName, claims.RoleID) 130 return CreateAuthToken(claims.Username, claims.RoleName, claims.RoleID)
131 } 131 }
132 132
133 func AuthCheck(req *http.Request, roles string) (*TokenClaims, error) { 133 func AuthCheck(req *http.Request, roles string) (*TokenClaims, error) {
134 // validate token and check expiration date 134 // validate token and check expiration date
135 claims, err := GetTokenClaims(req) 135 claims, err := GetTokenClaims(req)
136 if err != nil { 136 if err != nil {
137 return claims, err 137 return claims, err
138 } 138 }
139 139
140 if roles == "" { 140 if roles == "" {
141 return claims, nil 141 return claims, nil
142 } 142 }
143 143
144 // check if token has expired 144 // check if token has expired
145 if claims.ExpiresAt < (time.Now()).Unix() { 145 if claims.ExpiresAt < (time.Now()).Unix() {
146 return claims, errors.New("token has expired") 146 return claims, errors.New("token has expired")
147 } 147 }
148 148
149 if roles == "*" { 149 if roles == "*" {
150 return claims, nil 150 return claims, nil
151 } 151 }
152 152
153 parts := strings.Split(roles, ",") 153 parts := strings.Split(roles, ",")
154 for i, _ := range parts { 154 for i, _ := range parts {
155 r := strings.Trim(parts[i], " ") 155 r := strings.Trim(parts[i], " ")
156 if claims.RoleName == r { 156 if claims.RoleName == r {
157 return claims, nil 157 return claims, nil
158 } 158 }
159 } 159 }
160 160
161 return claims, nil 161 return claims, nil
162 } 162 }
163 163
164 // GetTokenClaims extracts JWT claims from Authorization header of req. 164 // GetTokenClaims extracts JWT claims from Authorization header of req.
165 // Returns token claims or an error. 165 // Returns token claims or an error.
166 func GetTokenClaims(req *http.Request) (*TokenClaims, error) { 166 func GetTokenClaims(req *http.Request) (*TokenClaims, error) {
167 // check for and strip 'Bearer' prefix 167 // check for and strip 'Bearer' prefix
168 var tokstr string 168 var tokstr string
169 authHead := req.Header.Get("Authorization") 169 authHead := req.Header.Get("Authorization")
170 if ok := strings.HasPrefix(authHead, "Bearer "); ok { 170 if ok := strings.HasPrefix(authHead, "Bearer "); ok {
171 tokstr = strings.TrimPrefix(authHead, "Bearer ") 171 tokstr = strings.TrimPrefix(authHead, "Bearer ")
172 } else { 172 } else {
173 return &TokenClaims{}, errors.New("authorization header in incomplete") 173 return &TokenClaims{}, errors.New("authorization header in incomplete")
174 } 174 }
175 175
176 token, err := jwt.ParseWithClaims(tokstr, &TokenClaims{}, secretFunc) 176 token, err := jwt.ParseWithClaims(tokstr, &TokenClaims{}, secretFunc)
177 if err != nil { 177 if err != nil {
178 return &TokenClaims{}, err 178 return &TokenClaims{}, err
179 } 179 }
180 180
181 // type assertion 181 // type assertion
182 claims, ok := token.Claims.(*TokenClaims) 182 claims, ok := token.Claims.(*TokenClaims)
183 if !ok || !token.Valid { 183 if !ok || !token.Valid {
184 return &TokenClaims{}, errors.New("token is not valid") 184 return &TokenClaims{}, errors.New("token is not valid")
185 } 185 }
186 186
187 return claims, nil 187 return claims, nil
188 } 188 }
189 189
190 // randomSalt returns a string of 32 random characters. 190 // randomSalt returns a string of 32 random characters.
191 const saltSize = 32
192
193 func randomSalt() (s string, err error) { 191 func randomSalt() (s string, err error) {
192 const saltSize = 32
193
194 rawsalt := make([]byte, saltSize) 194 rawsalt := make([]byte, saltSize)
195 195
196 _, err = rand.Read(rawsalt) 196 _, err = rand.Read(rawsalt)
197 if err != nil { 197 if err != nil {
198 return "", err 198 return "", err
199 } 199 }
200 200
201 s = hex.EncodeToString(rawsalt) 201 s = hex.EncodeToString(rawsalt)
202 return s, nil 202 return s, nil
203 } 203 }
204 204
205 // secretFunc returns byte slice of API secret keyword. 205 // secretFunc returns byte slice of API secret keyword.
206 func secretFunc(token *jwt.Token) (interface{}, error) { 206 func secretFunc(token *jwt.Token) (interface{}, error) {
207 return []byte(_secret), nil 207 return []byte(_secret), nil
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "database/sql" 4 "database/sql"
5 "encoding/json" 5 "encoding/json"
6 "errors" 6 "errors"
7 "fmt" 7 "fmt"
8 "io" 8 "io"
9 "net/http" 9 "net/http"
10 "sync" 10 "sync"
11 "time" 11 "time"
12 12
13 "git.to-net.rs/marko.tikvic/gologger" 13 "git.to-net.rs/marko.tikvic/gologger"
14 ) 14 )
15 15
16 var ( 16 var (
17 mu = &sync.Mutex{} 17 mu = &sync.Mutex{}
18 metadata = make(map[string]Payload) 18 metadata = make(map[string]Payload)
19 19
20 updateQue = make(map[string][]byte) 20 updateQue = make(map[string][]byte)
21 21
22 metadataDB *sql.DB 22 metadataDB *sql.DB
23 activeProject string 23 activeProject string
24 24
25 inited bool 25 inited bool
26 driver string 26 driver string
27 logger *gologger.Logger 27 logger *gologger.Logger
28 ) 28 )
29 29
30 type LangMap map[string]map[string]string 30 type LangMap map[string]map[string]string
31 31
32 type Field struct { 32 type Field struct {
33 Parameter string `json:"param"` 33 Parameter string `json:"param"`
34 Type string `json:"type"` 34 Type string `json:"type"`
35 Visible bool `json:"visible"` 35 Visible bool `json:"visible"`
36 Editable bool `json:"editable"` 36 Editable bool `json:"editable"`
37 } 37 }
38 38
39 type CorrelationField struct { 39 type CorrelationField struct {
40 Result string `json:"result"` 40 Result string `json:"result"`
41 Elements []string `json:"elements"` 41 Elements []string `json:"elements"`
42 Type string `json:"type"` 42 Type string `json:"type"`
43 } 43 }
44 44
45 type Translation struct { 45 type Translation struct {
46 Language string `json:"language"` 46 Language string `json:"language"`
47 FieldsLabels map[string]string `json:"fieldsLabels"` 47 FieldsLabels map[string]string `json:"fieldsLabels"`
48 } 48 }
49 49
50 // output
50 type PaginationLinks struct { 51 type PaginationLinks struct {
51 Base string `json:"base"` 52 Base string `json:"base"`
52 Next string `json:"next"` 53 Next string `json:"next"`
53 Prev string `json:"prev"` 54 Prev string `json:"prev"`
54 Self string `json:"self"` 55 Self string `json:"self"`
55 } 56 }
56 57
58 // input
57 type PaginationParameters struct { 59 type PaginationParameters struct {
60 URL string `json:"-"`
58 Offset int64 `json:"offset"` 61 Offset int64 `json:"offset"`
59 Limit int64 `json:"limit"` 62 Limit int64 `json:"limit"`
60 SortBy string `json:"sortBy"` 63 SortBy string `json:"sortBy"`
61 Order string `json:"order"` 64 Order string `json:"order"`
62 } 65 }
63 66
67 // TODO(marko)
68 func GetPaginationParameters(req *http.Request) (p PaginationParameters) {
69 return p
70 }
71
72 // TODO(marko)
73 func (p *PaginationParameters) paginationLinks() (links PaginationLinks) {
74 return links
75 }
76
64 type Payload struct { 77 type Payload struct {
65 Method string `json:"method"` 78 Method string `json:"method"`
66 Params map[string]string `json:"params"` 79 Params map[string]string `json:"params"`
67 Lang []Translation `json:"lang"` 80 Lang []Translation `json:"lang"`
68 Fields []Field `json:"fields"` 81 Fields []Field `json:"fields"`
69 Correlations []CorrelationField `json:"correlationFields"` 82 Correlations []CorrelationField `json:"correlationFields"`
70 IdField string `json:"idField"` 83 IdField string `json:"idField"`
71 84
72 // Pagination 85 // Pagination
73 Count int64 `json:"count"` 86 Count int64 `json:"count"`
74 Total int64 `json:"total"` 87 Total int64 `json:"total"`
75 Links *PaginationLinks `json:"_links"` 88 Links PaginationLinks `json:"_links"`
76 89
77 // Data holds JSON payload. It can't be used for itteration. 90 // Data holds JSON payload. It can't be used for itteration.
78 Data interface{} `json:"data"` 91 Data interface{} `json:"data"`
79 } 92 }
80 93
81 func (p *Payload) SetData(data interface{}) { 94 func (p *Payload) SetData(data interface{}) {
82 p.Data = data 95 p.Data = data
83 } 96 }
84 97
85 func (p *Payload) SetPaginationInfo(reqUrl string, count, total int64, params PaginationParameters) { 98 func (p *Payload) SetPaginationInfo(count, total int64, params PaginationParameters) {
86 p.Count = count 99 p.Count = count
87 p.Total = total 100 p.Total = total
88 101 p.Links = params.paginationLinks()
89 } 102 }
90 103
91 // NewPayload returs a payload sceleton for entity described with etype. 104 // NewPayload returs a payload sceleton for entity described with key.
92 func NewPayload(r *http.Request, etype string) Payload { 105 func NewPayload(r *http.Request, key string) Payload {
93 pload := metadata[etype] 106 p := metadata[key]
94 pload.Method = r.Method + " " + r.RequestURI 107 p.Method = r.Method + " " + r.RequestURI
95 return pload 108 return p
96 } 109 }
97 110
98 // DecodeJSON decodes JSON data from r to v. 111 // DecodeJSON decodes JSON data from r to v.
99 // Returns an error if it fails. 112 // Returns an error if it fails.
100 func DecodeJSON(r io.Reader, v interface{}) error { 113 func DecodeJSON(r io.Reader, v interface{}) error {
101 return json.NewDecoder(r).Decode(v) 114 return json.NewDecoder(r).Decode(v)
102 } 115 }
103 116
104 // InitPayloadsMetadata loads all payloads' information into 'metadata' variable. 117 // InitPayloadsMetadata loads all payloads' information into 'metadata' variable.
105 func InitPayloadsMetadata(drv string, db *sql.DB, project string) error { 118 func InitPayloadsMetadata(drv string, db *sql.DB, project string) error {
106 var err error 119 var err error
107 if drv != "ora" && drv != "mysql" { 120 if drv != "ora" && drv != "mysql" {
108 err = errors.New("driver not supported") 121 err = errors.New("driver not supported")
109 return err 122 return err
110 } 123 }
111 124
112 driver = drv 125 driver = drv
113 metadataDB = db 126 metadataDB = db
114 activeProject = project 127 activeProject = project
115 128
116 logger, err = gologger.New("metadata", gologger.MaxLogSize100KB) 129 logger, err = gologger.New("metadata", gologger.MaxLogSize100KB)
117 if err != nil { 130 if err != nil {
118 fmt.Printf("webutility: %s\n", err.Error()) 131 fmt.Printf("webutility: %s\n", err.Error())
119 } 132 }
120 133
121 mu.Lock() 134 mu.Lock()
122 defer mu.Unlock() 135 defer mu.Unlock()
123 err = initMetadata(project) 136 err = initMetadata(project)
124 if err != nil { 137 if err != nil {
125 return err 138 return err
126 } 139 }
127 inited = true 140 inited = true
128 141
129 return nil 142 return nil
130 } 143 }
131 144
132 func EnableHotloading(interval int) { 145 func EnableHotloading(interval int) {
133 if interval > 0 { 146 if interval > 0 {
134 go hotload(interval) 147 go hotload(interval)
135 } 148 }
136 } 149 }
137 150
138 func GetMetadataForAllEntities() map[string]Payload { 151 func GetMetadataForAllEntities() map[string]Payload {
139 return metadata 152 return metadata
140 } 153 }
141 154
142 func GetMetadataForEntity(t string) (Payload, bool) { 155 func GetMetadataForEntity(t string) (Payload, bool) {
143 p, ok := metadata[t] 156 p, ok := metadata[t]
144 return p, ok 157 return p, ok
145 } 158 }
146 159
147 func QueEntityModelUpdate(entityType string, v interface{}) { 160 func QueEntityModelUpdate(entityType string, v interface{}) {
148 updateQue[entityType], _ = json.Marshal(v) 161 updateQue[entityType], _ = json.Marshal(v)
149 } 162 }
150 163
151 func UpdateEntityModels(command string) (total, upd, add int, err error) { 164 func UpdateEntityModels(command string) (total, upd, add int, err error) {
152 if command != "force" && command != "missing" { 165 if command != "force" && command != "missing" {
153 return total, 0, 0, errors.New("webutility: unknown command: " + command) 166 return total, 0, 0, errors.New("webutility: unknown command: " + command)
154 } 167 }
155 168
156 if !inited { 169 if !inited {
157 return 0, 0, 0, errors.New("webutility: metadata not initialized but update was tried.") 170 return 0, 0, 0, errors.New("webutility: metadata not initialized but update was tried.")
158 } 171 }
159 172
160 total = len(updateQue) 173 total = len(updateQue)
161 174
162 toUpdate := make([]string, 0) 175 toUpdate := make([]string, 0)
163 toAdd := make([]string, 0) 176 toAdd := make([]string, 0)
164 177
165 for k, _ := range updateQue { 178 for k, _ := range updateQue {
166 if _, exists := metadata[k]; exists { 179 if _, exists := metadata[k]; exists {
167 if command == "force" { 180 if command == "force" {
168 toUpdate = append(toUpdate, k) 181 toUpdate = append(toUpdate, k)
169 } 182 }
170 } else { 183 } else {
171 toAdd = append(toAdd, k) 184 toAdd = append(toAdd, k)
172 } 185 }
173 } 186 }
174 187
175 var uStmt *sql.Stmt 188 var uStmt *sql.Stmt
176 if driver == "ora" { 189 if driver == "ora" {
177 uStmt, err = metadataDB.Prepare("update entities set entity_model = :1 where entity_type = :2") 190 uStmt, err = metadataDB.Prepare("update entities set entity_model = :1 where entity_type = :2")
178 if err != nil { 191 if err != nil {
179 logger.Trace(err.Error()) 192 logger.Trace(err.Error())
180 return 193 return
181 } 194 }
182 } else if driver == "mysql" { 195 } else if driver == "mysql" {
183 uStmt, err = metadataDB.Prepare("update entities set entity_model = ? where entity_type = ?") 196 uStmt, err = metadataDB.Prepare("update entities set entity_model = ? where entity_type = ?")
184 if err != nil { 197 if err != nil {
185 logger.Trace(err.Error()) 198 logger.Trace(err.Error())
186 return 199 return
187 } 200 }
188 } 201 }
189 for _, k := range toUpdate { 202 for _, k := range toUpdate {
190 _, err = uStmt.Exec(string(updateQue[k]), k) 203 _, err = uStmt.Exec(string(updateQue[k]), k)
191 if err != nil { 204 if err != nil {
192 logger.Trace(err.Error()) 205 logger.Trace(err.Error())
193 return 206 return
194 } 207 }
195 upd++ 208 upd++
196 } 209 }
197 210
198 blankPayload, _ := json.Marshal(Payload{}) 211 blankPayload, _ := json.Marshal(Payload{})
199 var iStmt *sql.Stmt 212 var iStmt *sql.Stmt
200 if driver == "ora" { 213 if driver == "ora" {
201 iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(:1, :2, :3, :4)") 214 iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(:1, :2, :3, :4)")
202 if err != nil { 215 if err != nil {
203 logger.Trace(err.Error()) 216 logger.Trace(err.Error())
204 return 217 return
205 } 218 }
206 } else if driver == "mysql" { 219 } else if driver == "mysql" {
207 iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(?, ?, ?, ?)") 220 iStmt, err = metadataDB.Prepare("insert into entities(projekat, metadata, entity_type, entity_model) values(?, ?, ?, ?)")
208 if err != nil { 221 if err != nil {
209 logger.Trace(err.Error()) 222 logger.Trace(err.Error())
210 return 223 return
211 } 224 }
212 } 225 }
213 for _, k := range toAdd { 226 for _, k := range toAdd {
214 _, err = iStmt.Exec(activeProject, string(blankPayload), k, string(updateQue[k])) 227 _, err = iStmt.Exec(activeProject, string(blankPayload), k, string(updateQue[k]))
215 if err != nil { 228 if err != nil {
216 logger.Trace(err.Error()) 229 logger.Trace(err.Error())
217 return 230 return
218 } 231 }
219 metadata[k] = Payload{} 232 metadata[k] = Payload{}
220 add++ 233 add++
221 } 234 }
222 235
223 return total, upd, add, nil 236 return total, upd, add, nil
224 } 237 }
225 238
226 func initMetadata(project string) error { 239 func initMetadata(project string) error {
227 rows, err := metadataDB.Query(`select 240 rows, err := metadataDB.Query(`select
228 entity_type, 241 entity_type,
229 metadata 242 metadata
230 from entities 243 from entities
231 where projekat = ` + fmt.Sprintf("'%s'", project)) 244 where projekat = ` + fmt.Sprintf("'%s'", project))
232 if err != nil { 245 if err != nil {
233 return err 246 return err
234 } 247 }
235 defer rows.Close() 248 defer rows.Close()
236 249
237 if len(metadata) > 0 { 250 if len(metadata) > 0 {
238 metadata = nil 251 metadata = nil
239 } 252 }
240 metadata = make(map[string]Payload) 253 metadata = make(map[string]Payload)
241 for rows.Next() { 254 for rows.Next() {
242 var name, load string 255 var name, load string
243 rows.Scan(&name, &load) 256 rows.Scan(&name, &load)
244 257
245 p := Payload{} 258 p := Payload{}
246 err := json.Unmarshal([]byte(load), &p) 259 err := json.Unmarshal([]byte(load), &p)
247 if err != nil { 260 if err != nil {
248 logger.Log("webutility: couldn't init: '%s' metadata: %s:\n%s\n", name, err.Error(), load) 261 logger.Log("webutility: couldn't init: '%s' metadata: %s:\n%s\n", name, err.Error(), load)
249 } else { 262 } else {
250 metadata[name] = p 263 metadata[name] = p
251 } 264 }
252 } 265 }
253 266
254 return nil 267 return nil
255 } 268 }
256 269
257 func hotload(n int) { 270 func hotload(n int) {
258 entityScan := make(map[string]int64) 271 entityScan := make(map[string]int64)
259 firstCheck := true 272 firstCheck := true
260 for { 273 for {
261 time.Sleep(time.Duration(n) * time.Second) 274 time.Sleep(time.Duration(n) * time.Second)
262 rows, err := metadataDB.Query(`select 275 rows, err := metadataDB.Query(`select
263 ora_rowscn, 276 ora_rowscn,
264 entity_type 277 entity_type
265 from entities where projekat = ` + fmt.Sprintf("'%s'", activeProject)) 278 from entities where projekat = ` + fmt.Sprintf("'%s'", activeProject))
266 if err != nil { 279 if err != nil {
267 logger.Log("webutility: hotload failed: %v\n", err) 280 logger.Log("webutility: hotload failed: %v\n", err)
268 time.Sleep(time.Duration(n) * time.Second) 281 time.Sleep(time.Duration(n) * time.Second)
269 continue 282 continue
270 } 283 }
271 284
272 var toRefresh []string 285 var toRefresh []string
273 for rows.Next() { 286 for rows.Next() {
274 var scanID int64 287 var scanID int64
275 var entity string 288 var entity string
276 rows.Scan(&scanID, &entity) 289 rows.Scan(&scanID, &entity)
277 oldID, ok := entityScan[entity] 290 oldID, ok := entityScan[entity]
278 if !ok || oldID != scanID { 291 if !ok || oldID != scanID {
279 entityScan[entity] = scanID 292 entityScan[entity] = scanID
280 toRefresh = append(toRefresh, entity) 293 toRefresh = append(toRefresh, entity)
281 } 294 }
282 } 295 }
283 rows.Close() 296 rows.Close()
284 297
285 if rows.Err() != nil { 298 if rows.Err() != nil {
286 logger.Log("webutility: hotload rset error: %v\n", rows.Err()) 299 logger.Log("webutility: hotload rset error: %v\n", rows.Err())
287 time.Sleep(time.Duration(n) * time.Second) 300 time.Sleep(time.Duration(n) * time.Second)
288 continue 301 continue
289 } 302 }
290 303
291 if len(toRefresh) > 0 && !firstCheck { 304 if len(toRefresh) > 0 && !firstCheck {
292 mu.Lock() 305 mu.Lock()
293 refreshMetadata(toRefresh) 306 refreshMetadata(toRefresh)
294 mu.Unlock() 307 mu.Unlock()
295 } 308 }
296 if firstCheck { 309 if firstCheck {
297 firstCheck = false 310 firstCheck = false
298 } 311 }
299 } 312 }
300 } 313 }
301 314
302 func refreshMetadata(entities []string) { 315 func refreshMetadata(entities []string) {
303 for _, e := range entities { 316 for _, e := range entities {
304 fmt.Printf("refreshing %s\n", e) 317 fmt.Printf("refreshing %s\n", e)
305 rows, err := metadataDB.Query(`select 318 rows, err := metadataDB.Query(`select
306 metadata 319 metadata
307 from entities 320 from entities
308 where projekat = ` + fmt.Sprintf("'%s'", activeProject) + 321 where projekat = ` + fmt.Sprintf("'%s'", activeProject) +
309 ` and entity_type = ` + fmt.Sprintf("'%s'", e)) 322 ` and entity_type = ` + fmt.Sprintf("'%s'", e))
310 323
311 if err != nil { 324 if err != nil {
312 logger.Log("webutility: refresh: prep: %v\n", err) 325 logger.Log("webutility: refresh: prep: %v\n", err)
313 rows.Close() 326 rows.Close()
314 continue 327 continue
315 } 328 }
316 329
317 for rows.Next() { 330 for rows.Next() {
318 var load string 331 var load string
319 rows.Scan(&load) 332 rows.Scan(&load)
320 p := Payload{} 333 p := Payload{}
321 err := json.Unmarshal([]byte(load), &p) 334 err := json.Unmarshal([]byte(load), &p)
322 if err != nil { 335 if err != nil {
323 logger.Log("webutility: couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), load) 336 logger.Log("webutility: couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), load)
324 } else { 337 } else {
325 metadata[e] = p 338 metadata[e] = p
326 } 339 }
327 } 340 }
328 rows.Close() 341 rows.Close()
329 } 342 }
330 } 343 }
331 344
332 /* 345 /*
333 func ModifyMetadataForEntity(entityType string, p *Payload) error { 346 func ModifyMetadataForEntity(entityType string, p *Payload) error {
334 md, err := json.Marshal(*p) 347 md, err := json.Marshal(*p)
335 if err != nil { 348 if err != nil {
336 return err 349 return err
337 } 350 }
338 351
339 mu.Lock() 352 mu.Lock()
340 defer mu.Unlock() 353 defer mu.Unlock()
341 _, err = metadataDB.PrepAndExe(`update entities set 354 _, err = metadataDB.PrepAndExe(`update entities set
342 metadata = :1 355 metadata = :1
343 where projekat = :2 356 where projekat = :2
344 and entity_type = :3`, 357 and entity_type = :3`,
345 string(md), 358 string(md),
346 activeProject, 359 activeProject,
347 entityType) 360 entityType)
348 if err != nil { 361 if err != nil {
349 return err 362 return err
350 } 363 }
351 return nil 364 return nil
352 } 365 }
353 366
354 func DeleteEntityModel(entityType string) error { 367 func DeleteEntityModel(entityType string) error {
355 _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType) 368 _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType)
356 if err == nil { 369 if err == nil {
357 mu.Lock() 370 mu.Lock()
358 delete(metadata, entityType) 371 delete(metadata, entityType)
359 mu.Unlock() 372 mu.Unlock()
360 } 373 }
361 return err 374 return err
362 } 375 }
363 */ 376 */
364 377