Commit 3f8e3c437433052f24b067ed433df8e83b98ec12
1 parent
34436d11e2
Exists in
master
and in
1 other branch
minor changes
Showing
3 changed files
with
197 additions
and
134 deletions
Show diff stats
auth.go
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 appName = "webutility" | 15 | var appName = "webutility" |
16 | var secret = "webutility" | 16 | var secret = "webutility" |
17 | 17 | ||
18 | type Role struct { | 18 | type Role struct { |
19 | Name string `json:"name"` | 19 | Name string `json:"name"` |
20 | ID int64 `json:"id"` | 20 | ID int64 `json:"id"` |
21 | } | 21 | } |
22 | 22 | ||
23 | // TokenClaims are JWT token claims. | 23 | // TokenClaims are JWT token claims. |
24 | type TokenClaims struct { | 24 | type TokenClaims struct { |
25 | // extending a struct | 25 | // extending a struct |
26 | jwt.StandardClaims | 26 | jwt.StandardClaims |
27 | 27 | ||
28 | // custom claims | 28 | // custom claims |
29 | Token string `json:"access_token"` | 29 | Token string `json:"access_token"` |
30 | TokenType string `json:"token_type"` | 30 | TokenType string `json:"token_type"` |
31 | Username string `json:"username"` | 31 | Username string `json:"username"` |
32 | Role string `json:"role"` | 32 | Role string `json:"role"` |
33 | RoleID int64 `json:"role_id"` | 33 | RoleID int64 `json:"role_id"` |
34 | ExpiresIn int64 `json:"expires_in"` | 34 | ExpiresIn int64 `json:"expires_in"` |
35 | } | 35 | } |
36 | 36 | ||
37 | func InitJWT(appName, secret string) { | 37 | func InitJWT(appName, secret string) { |
38 | appName = appName | 38 | appName = appName |
39 | secret = secret | 39 | secret = secret |
40 | } | 40 | } |
41 | 41 | ||
42 | // ValidateCredentials hashes pass and salt and returns comparison result with resultHash | 42 | // ValidateCredentials hashes pass and salt and returns comparison result with resultHash |
43 | func ValidateCredentials(pass, salt, resultHash string) (bool, error) { | 43 | func ValidateCredentials(pass, salt, resultHash string) (bool, error) { |
44 | hash, _, err := CreateHash(pass, salt) | 44 | hash, _, err := CreateHash(pass, salt) |
45 | if err != nil { | 45 | if err != nil { |
46 | return false, err | 46 | return false, err |
47 | } | 47 | } |
48 | res := hash == resultHash | 48 | res := hash == resultHash |
49 | return res, nil | 49 | return res, nil |
50 | } | 50 | } |
51 | 51 | ||
52 | // CreateHash hashes str using SHA256. | 52 | // CreateHash hashes str using SHA256. |
53 | // If the presalt parameter is not provided CreateHash will generate new salt string. | 53 | // 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. | 54 | // Returns hash and salt strings or an error if it fails. |
55 | func CreateHash(str, presalt string) (hash, salt string, err error) { | 55 | func CreateHash(str, presalt string) (hash, salt string, err error) { |
56 | // chech if message is presalted | 56 | // chech if message is presalted |
57 | if presalt == "" { | 57 | if presalt == "" { |
58 | salt, err = randomSalt() | 58 | salt, err = randomSalt() |
59 | if err != nil { | 59 | if err != nil { |
60 | return "", "", err | 60 | return "", "", err |
61 | } | 61 | } |
62 | } else { | 62 | } else { |
63 | salt = presalt | 63 | salt = presalt |
64 | } | 64 | } |
65 | 65 | ||
66 | // convert strings to raw byte slices | 66 | // convert strings to raw byte slices |
67 | rawstr := []byte(str) | 67 | rawstr := []byte(str) |
68 | rawsalt, err := hex.DecodeString(salt) | 68 | rawsalt, err := hex.DecodeString(salt) |
69 | if err != nil { | 69 | if err != nil { |
70 | return "", "", err | 70 | return "", "", err |
71 | } | 71 | } |
72 | 72 | ||
73 | rawdata := make([]byte, len(rawstr)+len(rawsalt)) | 73 | rawdata := make([]byte, len(rawstr)+len(rawsalt)) |
74 | rawdata = append(rawdata, rawstr...) | 74 | rawdata = append(rawdata, rawstr...) |
75 | rawdata = append(rawdata, rawsalt...) | 75 | rawdata = append(rawdata, rawsalt...) |
76 | 76 | ||
77 | // hash message + salt | 77 | // hash message + salt |
78 | hasher := sha256.New() | 78 | hasher := sha256.New() |
79 | hasher.Write(rawdata) | 79 | hasher.Write(rawdata) |
80 | rawhash := hasher.Sum(nil) | 80 | rawhash := hasher.Sum(nil) |
81 | 81 | ||
82 | hash = hex.EncodeToString(rawhash) | 82 | hash = hex.EncodeToString(rawhash) |
83 | return hash, salt, nil | 83 | return hash, salt, nil |
84 | } | 84 | } |
85 | 85 | ||
86 | // CreateAuthToken returns JWT token with encoded username, role, expiration date and issuer claims. | 86 | // CreateAuthToken returns JWT token with encoded username, role, expiration date and issuer claims. |
87 | // It returns an error if it fails. | 87 | // It returns an error if it fails. |
88 | func CreateAuthToken(username string, role Role) (TokenClaims, error) { | 88 | func CreateAuthToken(username string, role Role) (TokenClaims, error) { |
89 | t0 := (time.Now()).Unix() | 89 | t0 := (time.Now()).Unix() |
90 | t1 := (time.Now().Add(time.Hour * 24 * 7)).Unix() | 90 | t1 := (time.Now().Add(time.Hour * 24 * 7)).Unix() |
91 | claims := TokenClaims{ | 91 | claims := TokenClaims{ |
92 | TokenType: "Bearer", | 92 | TokenType: "Bearer", |
93 | Username: username, | 93 | Username: username, |
94 | Role: role.Name, | 94 | Role: role.Name, |
95 | RoleID: role.ID, | 95 | RoleID: role.ID, |
96 | ExpiresIn: t1 - t0, | 96 | ExpiresIn: t1 - t0, |
97 | } | 97 | } |
98 | // initialize jwt.StandardClaims fields (anonymous struct) | 98 | // initialize jwt.StandardClaims fields (anonymous struct) |
99 | claims.IssuedAt = t0 | 99 | claims.IssuedAt = t0 |
100 | claims.ExpiresAt = t1 | 100 | claims.ExpiresAt = t1 |
101 | claims.Issuer = appName | 101 | claims.Issuer = appName |
102 | 102 | ||
103 | jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) | 103 | jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) |
104 | token, err := jwtToken.SignedString([]byte(secret)) | 104 | token, err := jwtToken.SignedString([]byte(secret)) |
105 | if err != nil { | 105 | if err != nil { |
106 | return TokenClaims{}, err | 106 | return TokenClaims{}, err |
107 | } | 107 | } |
108 | claims.Token = token | 108 | claims.Token = token |
109 | return claims, nil | 109 | return claims, nil |
110 | } | 110 | } |
111 | 111 | ||
112 | // RefreshAuthToken returns new JWT token with same claims contained in tok but with prolonged expiration date. | 112 | // RefreshAuthToken returns new JWT token with same claims contained in tok but with prolonged expiration date. |
113 | // It returns an error if it fails. | 113 | // It returns an error if it fails. |
114 | func RefreshAuthToken(tok string) (TokenClaims, error) { | 114 | func RefreshAuthToken(tok string) (TokenClaims, error) { |
115 | token, err := jwt.ParseWithClaims(tok, &TokenClaims{}, secretFunc) | 115 | token, err := jwt.ParseWithClaims(tok, &TokenClaims{}, secretFunc) |
116 | if err != nil { | 116 | if err != nil { |
117 | if validation, ok := err.(*jwt.ValidationError); ok { | 117 | if validation, ok := err.(*jwt.ValidationError); ok { |
118 | // don't return error if token is expired | 118 | // don't return error if token is expired |
119 | // just extend it | 119 | // just extend it |
120 | if !(validation.Errors&jwt.ValidationErrorExpired != 0) { | 120 | if !(validation.Errors&jwt.ValidationErrorExpired != 0) { |
121 | return TokenClaims{}, err | 121 | return TokenClaims{}, err |
122 | } | 122 | } |
123 | } else { | 123 | } else { |
124 | return TokenClaims{}, err | 124 | return TokenClaims{}, err |
125 | } | 125 | } |
126 | } | 126 | } |
127 | 127 | ||
128 | // type assertion | 128 | // type assertion |
129 | claims, ok := token.Claims.(*TokenClaims) | 129 | claims, ok := token.Claims.(*TokenClaims) |
130 | if !ok { | 130 | if !ok { |
131 | return TokenClaims{}, errors.New("token is not valid") | 131 | return TokenClaims{}, errors.New("token is not valid") |
132 | } | 132 | } |
133 | 133 | ||
134 | // extend token expiration date | 134 | // extend token expiration date |
135 | return CreateAuthToken(claims.Username, Role{claims.Role, claims.RoleID}) | 135 | return CreateAuthToken(claims.Username, Role{claims.Role, claims.RoleID}) |
136 | } | 136 | } |
137 | 137 | ||
138 | // AuthCheck returns JWT claims and boolean result of a check if req contains any role from roles. | 138 | func AuthCheck(req *http.Request, roles string) (*TokenClaims, error) { |
139 | // It checks if role extracted from reqest's Authorization header (JWT claims) matches any of | ||
140 | // provided comma-separated roles in roles. If roles is empty string check is skipped, | ||
141 | // otherwise role is extracted from token claims and compared against roles. | ||
142 | // If roles is "*" the check is automatically validated. | ||
143 | func AuthCheck(req *http.Request, roles string) (*TokenClaims, bool) { | ||
144 | if roles == "" { | ||
145 | return nil, true | ||
146 | } | ||
147 | |||
148 | // validate token and check expiration date | 139 | // validate token and check expiration date |
149 | claims, err := GetTokenClaims(req) | 140 | claims, err := GetTokenClaims(req) |
150 | if err != nil { | 141 | if err != nil { |
151 | return claims, false | 142 | return claims, err |
143 | } | ||
144 | |||
145 | if roles == "" { | ||
146 | return claims, nil | ||
152 | } | 147 | } |
148 | |||
153 | // check if token has expired | 149 | // check if token has expired |
154 | if claims.ExpiresAt < (time.Now()).Unix() { | 150 | if claims.ExpiresAt < (time.Now()).Unix() { |
155 | return claims, false | 151 | return claims, errors.New("token has expired") |
156 | } | 152 | } |
157 | 153 | ||
158 | if roles == "*" { | 154 | if roles == "*" { |
159 | return claims, true | 155 | return claims, nil |
160 | } | 156 | } |
161 | 157 | ||
162 | parts := strings.Split(roles, ",") | 158 | parts := strings.Split(roles, ",") |
163 | for i, _ := range parts { | 159 | for i, _ := range parts { |
164 | r := strings.Trim(parts[i], " ") | 160 | r := strings.Trim(parts[i], " ") |
165 | if claims.Role == r { | 161 | if claims.Role == r { |
166 | return claims, true | 162 | return claims, nil |
167 | } | 163 | } |
168 | } | 164 | } |
169 | 165 | ||
170 | return claims, false | 166 | return claims, nil |
171 | } | 167 | } |
172 | 168 | ||
173 | // GetTokenClaims extracts JWT claims from Authorization header of req. | 169 | // GetTokenClaims extracts JWT claims from Authorization header of req. |
174 | // Returns token claims or an error. | 170 | // Returns token claims or an error. |
175 | func GetTokenClaims(req *http.Request) (*TokenClaims, error) { | 171 | func GetTokenClaims(req *http.Request) (*TokenClaims, error) { |
176 | // check for and strip 'Bearer' prefix | 172 | // check for and strip 'Bearer' prefix |
177 | var tokstr string | 173 | var tokstr string |
178 | authHead := req.Header.Get("Authorization") | 174 | authHead := req.Header.Get("Authorization") |
179 | if ok := strings.HasPrefix(authHead, "Bearer "); ok { | 175 | if ok := strings.HasPrefix(authHead, "Bearer "); ok { |
180 | tokstr = strings.TrimPrefix(authHead, "Bearer ") | 176 | tokstr = strings.TrimPrefix(authHead, "Bearer ") |
181 | } else { | 177 | } else { |
182 | return &TokenClaims{}, errors.New("authorization header in incomplete") | 178 | return &TokenClaims{}, errors.New("authorization header in incomplete") |
183 | } | 179 | } |
184 | 180 | ||
185 | token, err := jwt.ParseWithClaims(tokstr, &TokenClaims{}, secretFunc) | 181 | token, err := jwt.ParseWithClaims(tokstr, &TokenClaims{}, secretFunc) |
186 | if err != nil { | 182 | if err != nil { |
187 | return &TokenClaims{}, err | 183 | return &TokenClaims{}, err |
188 | } | 184 | } |
189 | 185 | ||
190 | // type assertion | 186 | // type assertion |
191 | claims, ok := token.Claims.(*TokenClaims) | 187 | claims, ok := token.Claims.(*TokenClaims) |
192 | if !ok || !token.Valid { | 188 | if !ok || !token.Valid { |
193 | return &TokenClaims{}, errors.New("token is not valid") | 189 | return &TokenClaims{}, errors.New("token is not valid") |
194 | } | 190 | } |
195 | 191 | ||
196 | return claims, nil | 192 | return claims, nil |
197 | } | 193 | } |
198 | 194 | ||
199 | // randomSalt returns a string of 32 random characters. | 195 | // randomSalt returns a string of 32 random characters. |
200 | const saltSize = 32 | 196 | const saltSize = 32 |
201 | 197 | ||
202 | func randomSalt() (s string, err error) { | 198 | func randomSalt() (s string, err error) { |
203 | rawsalt := make([]byte, saltSize) | 199 | rawsalt := make([]byte, saltSize) |
204 | 200 | ||
205 | _, err = rand.Read(rawsalt) | 201 | _, err = rand.Read(rawsalt) |
206 | if err != nil { | 202 | if err != nil { |
207 | return "", err | 203 | return "", err |
208 | } | 204 | } |
209 | 205 | ||
210 | s = hex.EncodeToString(rawsalt) | 206 | s = hex.EncodeToString(rawsalt) |
211 | return s, nil | 207 | return s, nil |
212 | } | 208 | } |
213 | 209 |
list_config.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "database/sql" | 4 | "database/sql" |
5 | "fmt" | 5 | "fmt" |
6 | ) | 6 | ) |
7 | 7 | ||
8 | type ListOptions struct { | 8 | type ListOptions struct { |
9 | GlobalFilter bool `json:"globalFilter"` | 9 | GlobalFilter bool `json:"globalFilter"` |
10 | LocalFilters bool `json:"localFilters"` | 10 | LocalFilters bool `json:"localFilters"` |
11 | RemoteFilters bool `json:"remoteFilters"` | 11 | RemoteFilters bool `json:"remoteFilters"` |
12 | Pagination bool `json:"pagination"` | 12 | Pagination bool `json:"pagination"` |
13 | PageSize uint32 `json:"pageSize"` | 13 | PageSize uint32 `json:"pageSize"` |
14 | Pivot bool `json:"pivot"` | 14 | Pivot bool `json:"pivot"` |
15 | Detail bool `json:"detail"` | 15 | Detail bool `json:"detail"` |
16 | Total bool `json:"total"` | 16 | Total bool `json:"total"` |
17 | } | 17 | } |
18 | 18 | ||
19 | type ListFilter struct { | 19 | type ListFilter struct { |
20 | Position uint32 `json:"-"` | 20 | Position uint32 `json:"-"` |
21 | ObjectType string `json:"-"` | 21 | ObjectType string `json:"-"` |
22 | FiltersField string `json:"filtersField"` | 22 | FiltersField string `json:"filtersField"` |
23 | DefaultValues string `json:"defaultValues"` | 23 | DefaultValues string `json:"defaultValues"` |
24 | FiltersType string `json:"filtersType"` | 24 | FiltersType string `json:"filtersType"` |
25 | FiltersLabel string `json:"filtersLabel"` | 25 | FiltersLabel string `json:"filtersLabel"` |
26 | DropdownConfig Dropdown `json:"dropdownConfig"` | 26 | DropdownConfig Dropdown `json:"dropdownConfig"` |
27 | } | 27 | } |
28 | 28 | ||
29 | type Dropdown struct { | 29 | type Dropdown struct { |
30 | ObjectType string `json:"objectType"` | 30 | ObjectType string `json:"objectType"` |
31 | FiltersField string `json:"filtersField"` | 31 | FiltersField string `json:"filtersField"` |
32 | IDField string `json:"idField"` | 32 | IDField string `json:"idField"` |
33 | LabelField string `json:"labelField"` | 33 | LabelField string `json:"labelField"` |
34 | } | 34 | } |
35 | 35 | ||
36 | type ListGraph struct { | 36 | type ListGraph struct { |
37 | ObjectType string `json:"objectType"` | 37 | ObjectType string `json:"objectType"` |
38 | X string `json:"xField"` | 38 | X string `json:"xField"` |
39 | Y string `json:"yField"` | 39 | Y string `json:"yField"` |
40 | GroupField string `json:"groupField"` | 40 | GroupField string `json:"groupField"` |
41 | Label string `json:"label"` | 41 | Label string `json:"label"` |
42 | } | 42 | } |
43 | 43 | ||
44 | type ListActions struct { | 44 | type ListActions struct { |
45 | Create bool `json:"create"` | 45 | Create bool `json:"create"` |
46 | Update bool `json:"update"` | 46 | Update bool `json:"update"` |
47 | Delete bool `json:"delete"` | 47 | Delete bool `json:"delete"` |
48 | Export bool `json:"export"` | 48 | Export bool `json:"export"` |
49 | Print bool `json:"print"` | 49 | Print bool `json:"print"` |
50 | Graph bool `json:"graph"` | 50 | Graph bool `json:"graph"` |
51 | LiveGraph bool `json:"liveGraph"` | 51 | LiveGraph bool `json:"liveGraph"` |
52 | SaveFile bool `json:"saveFile"` | ||
53 | ShowFile bool `json:"showFile"` | ||
52 | } | 54 | } |
53 | 55 | ||
54 | type ListNavNode struct { | 56 | type ListNavNode struct { |
55 | ObjectType string `json:"objectType"` | 57 | ObjectType string `json:"objectType"` |
56 | LabelField string `json:"label"` | 58 | LabelField string `json:"label"` |
57 | Icon string `json:"icon"` | 59 | Icon string `json:"icon"` |
58 | ParentObjectType string `json:"parentObjectType"` | 60 | ParentObjectType string `json:"parentObjectType"` |
59 | ParentIDField string `json:"parentIdField"` | 61 | ParentIDField string `json:"parentIdField"` |
60 | ParentFilterField string `json:"parentFilterField"` | 62 | ParentFilterField string `json:"parentFilterField"` |
61 | } | 63 | } |
62 | 64 | ||
63 | type ListParentNode struct { | 65 | type ListParentNode struct { |
64 | ObjectType string `json:"objectType"` | 66 | ObjectType string `json:"objectType"` |
65 | LabelField string `json:"labelField"` | 67 | LabelField string `json:"labelField"` |
66 | FilterField string `json:"filterField"` | 68 | FilterField string `json:"filterField"` |
67 | } | 69 | } |
68 | 70 | ||
69 | type ListPivot struct { | 71 | type ListPivot struct { |
70 | ObjectType string `json:"objectType"` | 72 | ObjectType string `json:"objectType"` |
71 | GroupField string `json:"groupField"` | 73 | GroupField string `json:"groupField"` |
72 | DistinctField string `json:"distinctField"` | 74 | DistinctField string `json:"distinctField"` |
73 | Value string `json:"valueField"` | 75 | Value string `json:"valueField"` |
74 | } | 76 | } |
75 | 77 | ||
76 | type ListDetails struct { | 78 | type ListDetails struct { |
77 | ObjectType string `json:"objectType"` | 79 | ObjectType string `json:"objectType"` |
78 | ParentObjectType string `json:"parentObjectType"` | 80 | ParentObjectType string `json:"parentObjectType"` |
79 | ParentFilterField string `json:"parentFilterField"` | 81 | ParentFilterField string `json:"parentFilterField"` |
80 | SingleDetail bool `json:"singleDetail"` | 82 | SingleDetail bool `json:"singleDetail"` |
81 | } | 83 | } |
82 | 84 | ||
83 | type ListLiveGraph struct { | 85 | type ListLiveGraph struct { |
84 | ObjectType string `json:"objectType"` | 86 | ObjectType string `json:"objectType"` |
85 | ValueFields string `json:"valueFields"` | 87 | ValueFields string `json:"valueFields"` |
86 | LabelFields string `json:"labelFields"` | 88 | LabelFields string `json:"labelFields"` |
87 | } | 89 | } |
88 | 90 | ||
89 | type ListConfig struct { | 91 | type ListConfig struct { |
90 | ObjectType string `json:"objectType"` | 92 | ObjectType string `json:"objectType"` |
91 | Title string `json:"title"` | 93 | Title string `json:"title"` |
92 | LazyLoad bool `json:"lazyLoad"` | 94 | LazyLoad bool `json:"lazyLoad"` |
93 | InlineEdit bool `json:"inlineEdit"` | 95 | InlineEdit bool `json:"inlineEdit"` |
94 | Options ListOptions `json:"options"` | 96 | Options ListOptions `json:"options"` |
95 | Filters []ListFilter `json:"defaultFilters"` | 97 | Filters []ListFilter `json:"defaultFilters"` |
96 | Graphs []ListGraph `json:"graphs"` | 98 | Graphs []ListGraph `json:"graphs"` |
97 | Actions ListActions `json:"actions"` | 99 | Actions ListActions `json:"actions"` |
98 | Parent []ListParentNode `json:"parent"` | 100 | Parent []ListParentNode `json:"parent"` |
99 | Navigation []ListNavNode `json:"navigation"` | 101 | Navigation []ListNavNode `json:"navigation"` |
100 | Pivots []ListPivot `json:"pivots"` | 102 | Pivots []ListPivot `json:"pivots"` |
101 | Details ListDetails `json:"details"` | 103 | Details ListDetails `json:"details"` |
102 | LiveGraph ListLiveGraph `json:"liveGraphs"` | 104 | LiveGraph ListLiveGraph `json:"liveGraphs"` |
103 | } | 105 | } |
104 | 106 | ||
105 | // GetListConfig returns list configuration for the provided object type for the front-end application | 107 | // GetListConfig returns list configuration for the provided object type for the front-end application |
106 | // or an error if it fails. | 108 | // or an error if it fails. |
107 | func GetListConfig(db *sql.DB, objType string) (ListConfig, error) { | 109 | func GetListConfig(db *sql.DB, objType string) (ListConfig, error) { |
108 | resp := newDefaultList(objType) | 110 | list := NewListConfig(objType) |
109 | var err error | 111 | |
110 | 112 | err := list.setParams(db, objType) | |
111 | err = setListParams(db, &resp, objType) | 113 | err = list.SetNavigation(db, objType) |
112 | resp.Navigation, err = getListNavigation(db, objType) | 114 | err = list.SetActions(db, objType) |
113 | resp.Actions, err = getListActions(db, objType) | 115 | err = list.SetFilters(db, objType) |
114 | resp.Filters, err = getListFilters(db, objType) | 116 | err = list.SetOptions(db, objType) |
115 | resp.Options, err = getListOptions(db, objType) | 117 | err = list.SetParent(db, objType) |
116 | resp.Parent, err = getListParent(db, objType) | 118 | err = list.SetPivot(db, objType) |
117 | resp.Graphs, err = getListGraph(db, objType) | 119 | err = list.SetGraph(db, objType) |
118 | resp.Pivots, err = getListPivot(db, objType) | 120 | err = list.SetDetails(db, objType) |
119 | resp.Details, err = getListDetails(db, objType) | 121 | err = list.SetLiveGraph(db, objType) |
120 | resp.LiveGraph, err = getListLiveGraph(db, objType) | ||
121 | 122 | ||
122 | if err != nil { | 123 | if err != nil { |
123 | return ListConfig{}, err | 124 | return list, err |
124 | } | 125 | } |
125 | 126 | ||
126 | return resp, nil | 127 | return list, nil |
127 | } | 128 | } |
128 | 129 | ||
129 | // GetListConfigObjectIDField takes in database connection and an object type and it returns the | 130 | // GetListConfigObjectIDField takes in database connection and an object type and it returns the |
130 | // ID field name for the provided object type. | 131 | // ID field name for the provided object type. |
131 | func GetListConfigObjectIDField(db *sql.DB, otype string) string { | 132 | func GetListConfigObjectIDField(db *sql.DB, otype string) string { |
132 | var resp string | 133 | var resp string |
133 | 134 | ||
134 | rows, err := db.Query(`SELECT | 135 | rows, err := db.Query(`SELECT |
135 | ID_FIELD | 136 | ID_FIELD |
136 | FROM LIST_CONFIG_ID_FIELD | 137 | FROM LIST_CONFIG_ID_FIELD |
137 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", otype)) | 138 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", otype)) |
138 | if err != nil { | 139 | if err != nil { |
139 | return "" | 140 | return "" |
140 | } | 141 | } |
141 | defer rows.Close() | 142 | defer rows.Close() |
142 | 143 | ||
143 | if rows.Next() { | 144 | if rows.Next() { |
144 | rows.Scan(&resp) | 145 | rows.Scan(&resp) |
145 | } | 146 | } |
146 | 147 | ||
147 | if rows.Err() != nil { | 148 | if rows.Err() != nil { |
148 | return "" | 149 | return "" |
149 | } | 150 | } |
150 | 151 | ||
151 | return resp | 152 | return resp |
152 | } | 153 | } |
153 | 154 | ||
154 | // newDefaultList returns default configuration for the provided object type. | 155 | // newDefaultList returns default configuration for the provided object type. |
155 | func newDefaultList(objType string) ListConfig { | 156 | func NewListConfig(objType string) ListConfig { |
156 | list := ListConfig{ | 157 | list := ListConfig{ |
157 | ObjectType: objType, | 158 | ObjectType: objType, |
158 | Title: objType, | 159 | Title: objType, |
159 | LazyLoad: false, | 160 | LazyLoad: false, |
160 | Options: ListOptions{ | 161 | Options: ListOptions{ |
161 | GlobalFilter: true, | 162 | GlobalFilter: true, |
162 | LocalFilters: true, | 163 | LocalFilters: true, |
163 | RemoteFilters: false, | 164 | RemoteFilters: false, |
164 | Pagination: true, | 165 | Pagination: true, |
165 | PageSize: 20, | 166 | PageSize: 20, |
166 | }, | 167 | }, |
167 | Filters: nil, | 168 | Filters: nil, |
168 | Actions: ListActions{ | 169 | Actions: ListActions{ |
169 | Create: false, | 170 | Create: false, |
170 | Update: false, | 171 | Update: false, |
171 | Delete: false, | 172 | Delete: false, |
172 | Export: false, | 173 | Export: false, |
173 | Print: false, | 174 | Print: false, |
174 | Graph: false, | 175 | Graph: false, |
175 | LiveGraph: false, | 176 | LiveGraph: false, |
176 | }, | 177 | }, |
177 | Parent: nil, | 178 | Parent: nil, |
178 | Navigation: nil, | 179 | Navigation: nil, |
179 | } | 180 | } |
180 | 181 | ||
181 | return list | 182 | return list |
182 | } | 183 | } |
183 | 184 | ||
184 | // setListParams sets the default parameters of the provided configuration list for the provided object type. | 185 | // setListParams sets the default parameters of the provided configuration list for the provided object type. |
185 | func setListParams(db *sql.DB, list *ListConfig, objType string) error { | 186 | func (list *ListConfig) setParams(db *sql.DB, objType string) error { |
186 | rows, err := db.Query(`SELECT | 187 | rows, err := db.Query(`SELECT |
187 | OBJECT_TYPE, TITLE, LAZY_LOAD, INLINE_EDIT | 188 | OBJECT_TYPE, |
189 | TITLE, | ||
190 | LAZY_LOAD, | ||
191 | INLINE_EDIT | ||
188 | FROM LIST_CONFIG | 192 | FROM LIST_CONFIG |
189 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 193 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
190 | if err != nil { | 194 | if err != nil { |
191 | return err | 195 | return err |
192 | } | 196 | } |
193 | defer rows.Close() | 197 | defer rows.Close() |
194 | if rows.Next() { | 198 | if rows.Next() { |
195 | otype, title := "", "" | 199 | otype, title := "", "" |
196 | lazyLoad, inlineEdit := 0, 0 | 200 | lazyLoad, inlineEdit := 0, 0 |
197 | rows.Scan(&otype, &title, &lazyLoad, &inlineEdit) | 201 | rows.Scan(&otype, &title, &lazyLoad, &inlineEdit) |
198 | 202 | ||
199 | if otype != "" { | 203 | if otype != "" { |
200 | list.ObjectType = otype | 204 | list.ObjectType = otype |
201 | } | 205 | } |
202 | if title != "" { | 206 | if title != "" { |
203 | list.Title = title | 207 | list.Title = title |
204 | } | 208 | } |
205 | list.LazyLoad = lazyLoad != 0 | 209 | list.LazyLoad = lazyLoad != 0 |
206 | list.InlineEdit = inlineEdit != 0 | 210 | list.InlineEdit = inlineEdit != 0 |
207 | } | 211 | } |
208 | if rows.Err() != nil { | 212 | if rows.Err() != nil { |
209 | return rows.Err() | 213 | return rows.Err() |
210 | } | 214 | } |
211 | return nil | 215 | return nil |
212 | } | 216 | } |
213 | 217 | ||
214 | // getListNavigation returns list navigation node slice for the provided objectType. | 218 | // ListNavigation returns list navigation node slice for the provided objectType. |
215 | func getListNavigation(db *sql.DB, listObjType string) ([]ListNavNode, error) { | 219 | func (list *ListConfig) SetNavigation(db *sql.DB, listObjType string) error { |
216 | resp := make([]ListNavNode, 0) | 220 | list.Navigation = make([]ListNavNode, 0) |
217 | rows, err := db.Query(`SELECT | 221 | rows, err := db.Query(`SELECT |
218 | a.OBJECT_TYPE, a.PARENT_OBJECT_TYPE, a.LABEL, a.ICON, a.PARENT_FILTER_FIELD, b.PARENT_ID_FIELD | 222 | a.OBJECT_TYPE, |
223 | a.PARENT_OBJECT_TYPE, | ||
224 | a.LABEL, | ||
225 | a.ICON, | ||
226 | a.PARENT_FILTER_FIELD, | ||
227 | b.PARENT_ID_FIELD | ||
219 | FROM LIST_CONFIG_NAVIGATION b | 228 | FROM LIST_CONFIG_NAVIGATION b |
220 | JOIN LIST_CONFIG_CHILD a ON b.PARENT_CHILD_ID = a.PARENT_CHILD_ID | 229 | JOIN LIST_CONFIG_CHILD a ON b.PARENT_CHILD_ID = a.PARENT_CHILD_ID |
221 | WHERE b.LIST_OBJECT_TYPE = ` + fmt.Sprintf("'%s'", listObjType) + | 230 | WHERE b.LIST_OBJECT_TYPE = ` + fmt.Sprintf("'%s'", listObjType) + |
222 | ` ORDER BY b.RB ASC`) | 231 | ` ORDER BY b.RB ASC`) |
223 | if err != nil { | 232 | if err != nil { |
224 | return resp, err | 233 | return err |
225 | } | 234 | } |
226 | defer rows.Close() | 235 | defer rows.Close() |
227 | 236 | ||
228 | var node ListNavNode | 237 | var node ListNavNode |
229 | for rows.Next() { | 238 | for rows.Next() { |
230 | rows.Scan(&node.ObjectType, &node.ParentObjectType, &node.LabelField, &node.Icon, | 239 | rows.Scan(&node.ObjectType, &node.ParentObjectType, &node.LabelField, &node.Icon, |
231 | &node.ParentFilterField, &node.ParentIDField) | 240 | &node.ParentFilterField, &node.ParentIDField) |
232 | resp = append(resp, node) | 241 | list.Navigation = append(list.Navigation, node) |
233 | } | 242 | } |
234 | if rows.Err() != nil { | 243 | if rows.Err() != nil { |
235 | return nil, rows.Err() | 244 | return rows.Err() |
236 | } | 245 | } |
237 | 246 | ||
238 | return resp, nil | 247 | return nil |
239 | } | 248 | } |
240 | 249 | ||
241 | // getListActions returns list actions for the provided object type. | 250 | // getListActions returns list actions for the provided object type. |
242 | func getListActions(db *sql.DB, objType string) (ListActions, error) { | 251 | func (list *ListConfig) SetActions(db *sql.DB, objType string) error { |
243 | var resp ListActions | ||
244 | rows, err := db.Query(`SELECT | 252 | rows, err := db.Query(`SELECT |
245 | ACTION_CREATE, ACTION_UPDATE, ACTION_DELETE, ACTION_EXPORT, | 253 | ACTION_CREATE, |
246 | ACTION_PRINT, ACTION_GRAPH, ACTION_LIVE_GRAPH | 254 | ACTION_UPDATE, |
255 | ACTION_DELETE, | ||
256 | ACTION_EXPORT, | ||
257 | ACTION_PRINT, | ||
258 | ACTION_GRAPH, | ||
259 | ACTION_LIVE_GRAPH, | ||
260 | ACTION_SAVE_FILE, | ||
261 | ACTION_SHOW_FILE | ||
247 | FROM LIST_CONFIG | 262 | FROM LIST_CONFIG |
248 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 263 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
249 | if err != nil { | 264 | if err != nil { |
250 | return ListActions{}, err | 265 | return err |
251 | } | 266 | } |
252 | defer rows.Close() | 267 | defer rows.Close() |
253 | 268 | ||
254 | var create, update, delete, export, print, graph, liveGraph uint32 | 269 | var create, update, delete, export, print, graph, liveGraph, saveFile, showFile uint32 |
255 | if rows.Next() { | 270 | if rows.Next() { |
256 | rows.Scan(&create, &update, &delete, &export, &print, &graph, &liveGraph) | 271 | rows.Scan(&create, &update, &delete, &export, &print, &graph, &liveGraph, &saveFile, &showFile) |
257 | resp.Create = create != 0 | 272 | list.Actions.Create = create != 0 |
258 | resp.Update = update != 0 | 273 | list.Actions.Update = update != 0 |
259 | resp.Delete = delete != 0 | 274 | list.Actions.Delete = delete != 0 |
260 | resp.Export = export != 0 | 275 | list.Actions.Export = export != 0 |
261 | resp.Print = print != 0 | 276 | list.Actions.Print = print != 0 |
262 | resp.Graph = graph != 0 | 277 | list.Actions.Graph = graph != 0 |
263 | resp.LiveGraph = liveGraph != 0 | 278 | list.Actions.LiveGraph = liveGraph != 0 |
279 | list.Actions.SaveFile = saveFile != 0 | ||
280 | list.Actions.ShowFile = showFile != 0 | ||
264 | } | 281 | } |
265 | if rows.Err() != nil { | 282 | if rows.Err() != nil { |
266 | return ListActions{}, rows.Err() | 283 | return rows.Err() |
267 | } | 284 | } |
268 | return resp, nil | 285 | |
286 | return nil | ||
269 | } | 287 | } |
270 | 288 | ||
271 | // getListFiters returns list filter slice for the provided object type. | 289 | // getListFiters returns list filter slice for the provided object type. |
272 | func getListFilters(db *sql.DB, objType string) ([]ListFilter, error) { | 290 | func (list *ListConfig) SetFilters(db *sql.DB, objType string) error { |
273 | resp := make([]ListFilter, 0) | 291 | list.Filters = make([]ListFilter, 0) |
274 | filtersFields, err := getFilterFieldsAndPosition(db, objType) | 292 | filtersFields, err := list.GetFilterFieldsAndPosition(db, objType) |
275 | if err != nil { | 293 | if err != nil { |
276 | return nil, err | 294 | return err |
277 | } | 295 | } |
278 | for field, pos := range filtersFields { | 296 | for field, pos := range filtersFields { |
279 | filters, _ := getFiltersByFilterField(db, field) | 297 | filters, _ := list.GetFiltersByFilterField(db, field) |
280 | for _, filter := range filters { | 298 | for _, filter := range filters { |
281 | var f ListFilter | 299 | var f ListFilter |
282 | f.Position = pos | 300 | f.Position = pos |
283 | f.ObjectType = objType | 301 | f.ObjectType = objType |
284 | f.FiltersField = field | 302 | f.FiltersField = field |
285 | f.DefaultValues = filter.DefaultValues | 303 | f.DefaultValues = filter.DefaultValues |
286 | f.FiltersLabel = filter.Label | 304 | f.FiltersLabel = filter.Label |
287 | f.FiltersType = filter.Type | 305 | f.FiltersType = filter.Type |
288 | if filter.Type == "dropdown" { | 306 | if filter.Type == "dropdown" { |
289 | f.DropdownConfig, err = getFilterDropdownConfig(db, field) | 307 | err := f.SetDropdownConfig(db, field) |
290 | if err != nil { | 308 | if err != nil { |
291 | return nil, err | 309 | return err |
292 | } | 310 | } |
293 | } | 311 | } |
294 | resp = append(resp, f) | 312 | list.Filters = append(list.Filters, f) |
295 | } | 313 | } |
296 | } | 314 | } |
297 | 315 | ||
298 | sortFilters(resp) | 316 | list.sortFilters() |
299 | 317 | ||
300 | return resp, nil | 318 | return nil |
301 | } | 319 | } |
302 | 320 | ||
303 | // getFilterFieldsAndPosition returns a map of filter fields and their respective position in the menu. | 321 | // getFilterFieldsAndPosition returns a map of filter fields and their respective position in the menu. |
304 | func getFilterFieldsAndPosition(db *sql.DB, objType string) (map[string]uint32, error) { | 322 | func (list *ListConfig) GetFilterFieldsAndPosition(db *sql.DB, objType string) (map[string]uint32, error) { |
305 | filtersField := make(map[string]uint32, 0) | 323 | filtersField := make(map[string]uint32, 0) |
306 | rows, err := db.Query(`SELECT | 324 | rows, err := db.Query(`SELECT |
307 | FILTERS_FIELD, RB | 325 | FILTERS_FIELD, |
326 | RB | ||
308 | FROM LIST_CONFIG_FILTERS | 327 | FROM LIST_CONFIG_FILTERS |
309 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 328 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
310 | if err != nil { | 329 | if err != nil { |
311 | return nil, err | 330 | return nil, err |
312 | } | 331 | } |
313 | defer rows.Close() | 332 | defer rows.Close() |
314 | 333 | ||
315 | for rows.Next() { | 334 | for rows.Next() { |
316 | var field string | 335 | var field string |
317 | var rb uint32 | 336 | var rb uint32 |
318 | rows.Scan(&field, &rb) | 337 | rows.Scan(&field, &rb) |
319 | filtersField[field] = rb | 338 | filtersField[field] = rb |
320 | } | 339 | } |
321 | if rows.Err() != nil { | 340 | if rows.Err() != nil { |
322 | return nil, rows.Err() | 341 | return nil, rows.Err() |
323 | } | 342 | } |
324 | return filtersField, nil | 343 | return filtersField, nil |
325 | } | 344 | } |
326 | 345 | ||
327 | type _filter struct { | 346 | type _filter struct { |
328 | DefaultValues string | 347 | DefaultValues string |
329 | Label string | 348 | Label string |
330 | Type string | 349 | Type string |
331 | } | 350 | } |
332 | 351 | ||
333 | // getFiltersByFilterField returns filter slice for the provided filter field. | 352 | // getFiltersByFilterField returns filter slice for the provided filter field. |
334 | func getFiltersByFilterField(db *sql.DB, filtersField string) ([]_filter, error) { | 353 | func (list *ListConfig) GetFiltersByFilterField(db *sql.DB, filtersField string) ([]_filter, error) { |
335 | resp := make([]_filter, 0) | 354 | resp := make([]_filter, 0) |
336 | rows, err := db.Query(`SELECT | 355 | rows, err := db.Query(`SELECT |
337 | FILTERS_TYPE, FILTERS_LABEL, DEFAULT_VALUES | 356 | FILTERS_TYPE, |
357 | FILTERS_LABEL, | ||
358 | DEFAULT_VALUES | ||
338 | FROM LIST_FILTERS_FIELD | 359 | FROM LIST_FILTERS_FIELD |
339 | WHERE FILTERS_FIELD = ` + fmt.Sprintf("'%s'", filtersField)) | 360 | WHERE FILTERS_FIELD = ` + fmt.Sprintf("'%s'", filtersField)) |
340 | if err != nil { | 361 | if err != nil { |
341 | return resp, err | 362 | return resp, err |
342 | } | 363 | } |
343 | defer rows.Close() | 364 | defer rows.Close() |
344 | 365 | ||
345 | var f _filter | 366 | var f _filter |
346 | for rows.Next() { | 367 | for rows.Next() { |
347 | rows.Scan(&f.Type, &f.Label, &f.DefaultValues) | 368 | rows.Scan(&f.Type, &f.Label, &f.DefaultValues) |
348 | resp = append(resp, f) | 369 | resp = append(resp, f) |
349 | } | 370 | } |
350 | if rows.Err() != nil { | 371 | if rows.Err() != nil { |
351 | return resp, rows.Err() | 372 | return resp, rows.Err() |
352 | } | 373 | } |
353 | return resp, nil | 374 | return resp, nil |
354 | } | 375 | } |
355 | 376 | ||
356 | // getFilterDropdownConfig returns dropdown menu for the provided filter field. | 377 | // getFilterDropdownConfig returns dropdown menu for the provided filter field. |
357 | func getFilterDropdownConfig(db *sql.DB, filtersField string) (Dropdown, error) { | 378 | func (f *ListFilter) SetDropdownConfig(db *sql.DB, filtersField string) error { |
358 | var resp Dropdown | 379 | var resp Dropdown |
359 | rows, err := db.Query(`SELECT | 380 | rows, err := db.Query(`SELECT |
360 | FILTERS_FIELD, OBJECT_TYPE, ID_FIELD, LABEL_FIELD | 381 | FILTERS_FIELD, |
382 | OBJECT_TYPE, | ||
383 | ID_FIELD, | ||
384 | LABEL_FIELD | ||
361 | FROM LIST_DROPDOWN_FILTER | 385 | FROM LIST_DROPDOWN_FILTER |
362 | WHERE FILTERS_FIELD = ` + fmt.Sprintf("'%s'", filtersField)) | 386 | WHERE FILTERS_FIELD = ` + fmt.Sprintf("'%s'", filtersField)) |
363 | if err != nil { | 387 | if err != nil { |
364 | return resp, err | 388 | return err |
365 | } | 389 | } |
366 | defer rows.Close() | 390 | defer rows.Close() |
367 | if rows.Next() { | 391 | if rows.Next() { |
368 | rows.Scan(&resp.FiltersField, &resp.ObjectType, &resp.IDField, &resp.LabelField) | 392 | rows.Scan(&resp.FiltersField, &resp.ObjectType, &resp.IDField, &resp.LabelField) |
369 | } | 393 | } |
370 | if rows.Err() != nil { | 394 | if rows.Err() != nil { |
371 | return resp, rows.Err() | 395 | return rows.Err() |
372 | } | 396 | } |
373 | return resp, nil | 397 | |
398 | f.DropdownConfig = resp | ||
399 | |||
400 | return nil | ||
374 | } | 401 | } |
375 | 402 | ||
376 | // sortFilters bubble sorts provided filters slice by position field. | 403 | // sortFilters bubble sorts provided filters slice by position field. |
377 | func sortFilters(filters []ListFilter) { | 404 | func (list *ListConfig) sortFilters() { |
378 | done := false | 405 | done := false |
379 | var temp ListFilter | 406 | var temp ListFilter |
380 | for !done { | 407 | for !done { |
381 | done = true | 408 | done = true |
382 | for i := 0; i < len(filters)-1; i++ { | 409 | for i := 0; i < len(list.Filters)-1; i++ { |
383 | if filters[i].Position > filters[i+1].Position { | 410 | if list.Filters[i].Position > list.Filters[i+1].Position { |
384 | done = false | 411 | done = false |
385 | temp = filters[i] | 412 | temp = list.Filters[i] |
386 | filters[i] = filters[i+1] | 413 | list.Filters[i] = list.Filters[i+1] |
387 | filters[i+1] = temp | 414 | list.Filters[i+1] = temp |
388 | } | 415 | } |
389 | } | 416 | } |
390 | } | 417 | } |
391 | } | 418 | } |
392 | 419 | ||
393 | // getListGraph return list graph slice for the provided object type. | 420 | // getListGraph return list graph slice for the provided object type. |
394 | func getListGraph(db *sql.DB, objType string) ([]ListGraph, error) { | 421 | func (list *ListConfig) SetGraph(db *sql.DB, objType string) error { |
395 | resp := make([]ListGraph, 0) | 422 | list.Graphs = make([]ListGraph, 0) |
396 | rows, err := db.Query(`SELECT | 423 | rows, err := db.Query(`SELECT |
397 | OBJECT_TYPE, X_FIELD, Y_FIELD, GROUP_FIELD, LABEL | 424 | OBJECT_TYPE, |
425 | X_FIELD, | ||
426 | Y_FIELD, | ||
427 | GROUP_FIELD, | ||
428 | LABEL | ||
398 | FROM LIST_GRAPHS | 429 | FROM LIST_GRAPHS |
399 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 430 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
400 | if err != nil { | 431 | if err != nil { |
401 | return resp, err | 432 | return err |
402 | } | 433 | } |
403 | defer rows.Close() | 434 | defer rows.Close() |
404 | 435 | ||
405 | var lg ListGraph | 436 | var lg ListGraph |
406 | for rows.Next() { | 437 | for rows.Next() { |
407 | rows.Scan(&lg.ObjectType, &lg.X, &lg.Y, &lg.GroupField, &lg.Label) | 438 | rows.Scan(&lg.ObjectType, &lg.X, &lg.Y, &lg.GroupField, &lg.Label) |
408 | resp = append(resp, lg) | 439 | list.Graphs = append(list.Graphs, lg) |
409 | } | 440 | } |
410 | if rows.Err() != nil { | 441 | if rows.Err() != nil { |
411 | return resp, rows.Err() | 442 | return rows.Err() |
412 | } | 443 | } |
413 | return resp, nil | 444 | |
445 | return nil | ||
414 | } | 446 | } |
415 | 447 | ||
416 | // getListOptions returns list options for the provided object type. | 448 | // getListOptions returns list options for the provided object type. |
417 | func getListOptions(db *sql.DB, objType string) (ListOptions, error) { | 449 | func (list *ListConfig) SetOptions(db *sql.DB, objType string) error { |
418 | var resp ListOptions | ||
419 | rows, err := db.Query(`SELECT | 450 | rows, err := db.Query(`SELECT |
420 | GLOBAL_FILTER, LOCAL_FILTER, REMOTE_FILTER, PAGINATION, | 451 | GLOBAL_FILTER, |
421 | PAGE_SIZE, PIVOT, DETAIL, TOTAL | 452 | LOCAL_FILTER, |
453 | REMOTE_FILTER, | ||
454 | PAGINATION, | ||
455 | PAGE_SIZE, | ||
456 | PIVOT, | ||
457 | DETAIL, | ||
458 | TOTAL | ||
422 | FROM LIST_CONFIG | 459 | FROM LIST_CONFIG |
423 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 460 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
424 | if err != nil { | 461 | if err != nil { |
425 | return ListOptions{}, err | 462 | return err |
426 | } | 463 | } |
427 | defer rows.Close() | 464 | defer rows.Close() |
465 | |||
428 | if rows.Next() { | 466 | if rows.Next() { |
429 | var gfilter, lfilters, rfilters, pagination, pageSize, pivot, detail, total uint32 | 467 | var gfilter, lfilters, rfilters, pagination, pageSize, pivot, detail, total uint32 |
430 | rows.Scan(&gfilter, &lfilters, &rfilters, &pagination, &pageSize, &pivot, &detail, &total) | 468 | rows.Scan(&gfilter, &lfilters, &rfilters, &pagination, &pageSize, &pivot, &detail, &total) |
431 | resp.GlobalFilter = gfilter != 0 | 469 | list.Options.GlobalFilter = gfilter != 0 |
432 | resp.LocalFilters = lfilters != 0 | 470 | list.Options.LocalFilters = lfilters != 0 |
433 | resp.RemoteFilters = rfilters != 0 | 471 | list.Options.RemoteFilters = rfilters != 0 |
434 | resp.Pagination = pagination != 0 | 472 | list.Options.Pagination = pagination != 0 |
435 | resp.PageSize = pageSize | 473 | list.Options.PageSize = pageSize |
436 | resp.Pivot = pivot != 0 | 474 | list.Options.Pivot = pivot != 0 |
437 | resp.Detail = detail != 0 | 475 | list.Options.Detail = detail != 0 |
438 | resp.Total = total != 0 | 476 | list.Options.Total = total != 0 |
439 | } | 477 | } |
440 | if rows.Err() != nil { | 478 | if rows.Err() != nil { |
441 | return ListOptions{}, rows.Err() | 479 | return rows.Err() |
442 | } | 480 | } |
443 | return resp, nil | 481 | |
482 | return nil | ||
444 | } | 483 | } |
445 | 484 | ||
446 | // getListParent returns list parent node slice for the provided object type. | 485 | // getListParent returns list parent node slice for the provided object type. |
447 | func getListParent(db *sql.DB, objType string) ([]ListParentNode, error) { | 486 | func (list *ListConfig) SetParent(db *sql.DB, objType string) error { |
448 | resp := make([]ListParentNode, 0) | 487 | list.Parent = make([]ListParentNode, 0) |
449 | rows, err := db.Query(`SELECT | 488 | rows, err := db.Query(`SELECT |
450 | PARENT_OBJECT_TYPE, PARENT_LABEL_FIELD, PARENT_FILTER_FIELD | 489 | PARENT_OBJECT_TYPE, |
490 | PARENT_LABEL_FIELD, | ||
491 | PARENT_FILTER_FIELD | ||
451 | FROM LIST_CONFIG_CHILD | 492 | FROM LIST_CONFIG_CHILD |
452 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 493 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
453 | if err != nil { | 494 | if err != nil { |
454 | return resp, err | 495 | return err |
455 | } | 496 | } |
456 | defer rows.Close() | 497 | defer rows.Close() |
457 | 498 | ||
458 | var pnode ListParentNode | 499 | var pnode ListParentNode |
459 | for rows.Next() { | 500 | for rows.Next() { |
460 | rows.Scan(&pnode.ObjectType, &pnode.LabelField, &pnode.FilterField) | 501 | rows.Scan(&pnode.ObjectType, &pnode.LabelField, &pnode.FilterField) |
461 | resp = append(resp, pnode) | 502 | list.Parent = append(list.Parent, pnode) |
462 | } | 503 | } |
463 | if rows.Err() != nil { | 504 | if rows.Err() != nil { |
464 | return nil, rows.Err() | 505 | return rows.Err() |
465 | } | 506 | } |
466 | 507 | ||
467 | return resp, nil | 508 | return nil |
468 | } | 509 | } |
469 | 510 | ||
470 | // getListPivot list pivot slice for the provided object type. | 511 | // getListPivot list pivot slice for the provided object type. |
471 | func getListPivot(db *sql.DB, objType string) ([]ListPivot, error) { | 512 | func (list *ListConfig) SetPivot(db *sql.DB, objType string) error { |
472 | resp := make([]ListPivot, 0) | 513 | list.Pivots = make([]ListPivot, 0) |
473 | rows, err := db.Query(`SELECT | 514 | rows, err := db.Query(`SELECT |
474 | OBJECT_TYPE, GROUP_FIELD, DISTINCT_FIELD, VALUE_FIELD | 515 | OBJECT_TYPE, |
516 | GROUP_FIELD, | ||
517 | DISTINCT_FIELD, | ||
518 | VALUE_FIELD | ||
475 | FROM LIST_PIVOTS | 519 | FROM LIST_PIVOTS |
476 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 520 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
477 | if err != nil { | 521 | if err != nil { |
478 | return resp, err | 522 | return err |
479 | } | 523 | } |
480 | defer rows.Close() | 524 | defer rows.Close() |
481 | 525 | ||
482 | var p ListPivot | 526 | var p ListPivot |
483 | for rows.Next() { | 527 | for rows.Next() { |
484 | rows.Scan(&p.ObjectType, &p.GroupField, &p.DistinctField, &p.Value) | 528 | rows.Scan(&p.ObjectType, &p.GroupField, &p.DistinctField, &p.Value) |
485 | resp = append(resp, p) | 529 | list.Pivots = append(list.Pivots, p) |
486 | } | 530 | } |
487 | if rows.Err() != nil { | 531 | if rows.Err() != nil { |
488 | return nil, rows.Err() | 532 | return rows.Err() |
489 | } | 533 | } |
490 | 534 | ||
491 | return resp, nil | 535 | return nil |
492 | } | 536 | } |
493 | 537 | ||
494 | // getListDetails returns list details for the provided object type. | 538 | // getListDetails returns list details for the provided object type. |
495 | func getListDetails(db *sql.DB, objType string) (ListDetails, error) { | 539 | func (list *ListConfig) SetDetails(db *sql.DB, objType string) error { |
496 | var resp ListDetails | 540 | var resp ListDetails |
497 | rows, err := db.Query(`SELECT | 541 | rows, err := db.Query(`SELECT |
498 | OBJECT_TYPE, PARENT_OBJECT_TYPE, PARENT_FILTER_FIELD, SINGLE_DETAIL | 542 | OBJECT_TYPE, |
543 | PARENT_OBJECT_TYPE, | ||
544 | PARENT_FILTER_FIELD, | ||
545 | SINGLE_DETAIL | ||
499 | FROM LIST_CONFIG_DETAIL | 546 | FROM LIST_CONFIG_DETAIL |
500 | WHERE PARENT_OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 547 | WHERE PARENT_OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
501 | if err != nil { | 548 | if err != nil { |
502 | return resp, err | 549 | return err |
503 | } | 550 | } |
504 | defer rows.Close() | 551 | defer rows.Close() |
505 | if rows.Next() { | 552 | if rows.Next() { |
506 | var singleDetail uint32 | 553 | var singleDetail uint32 |
507 | rows.Scan(&resp.ObjectType, &resp.ParentObjectType, &resp.ParentFilterField, &singleDetail) | 554 | rows.Scan(&resp.ObjectType, &resp.ParentObjectType, &resp.ParentFilterField, &singleDetail) |
508 | resp.SingleDetail = singleDetail != 0 | 555 | resp.SingleDetail = singleDetail != 0 |
509 | } | 556 | } |
510 | if rows.Err() != nil { | 557 | if rows.Err() != nil { |
511 | return resp, rows.Err() | 558 | return rows.Err() |
512 | } | 559 | } |
513 | 560 | ||
514 | return resp, nil | 561 | list.Details = resp |
562 | |||
563 | return nil | ||
515 | } | 564 | } |
516 | 565 | ||
517 | // getListLiveGraph returns live graph for the provided object type. | 566 | // getListLiveGraph returns live graph for the provided object type. |
518 | func getListLiveGraph(db *sql.DB, objType string) (ListLiveGraph, error) { | 567 | func (list *ListConfig) SetLiveGraph(db *sql.DB, objType string) error { |
519 | var resp ListLiveGraph | 568 | var resp ListLiveGraph |
520 | rows, err := db.Query(`SELECT | 569 | rows, err := db.Query(`SELECT |
521 | OBJECT_TYPE, VALUE_FIELDS, LABEL_FIELDS | 570 | OBJECT_TYPE, |
571 | VALUE_FIELDS, | ||
572 | LABEL_FIELDS | ||
522 | FROM LIST_LIVE_GRAPH | 573 | FROM LIST_LIVE_GRAPH |
523 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) | 574 | WHERE OBJECT_TYPE = ` + fmt.Sprintf("'%s'", objType)) |
524 | if err != nil { | 575 | if err != nil { |
525 | return resp, err | 576 | return err |
526 | } | 577 | } |
527 | defer rows.Close() | 578 | defer rows.Close() |
528 | if rows.Next() { | 579 | if rows.Next() { |
529 | rows.Scan(&resp.ObjectType, &resp.ValueFields, &resp.LabelFields) | 580 | rows.Scan(&resp.ObjectType, &resp.ValueFields, &resp.LabelFields) |
530 | } | 581 | } |
531 | if rows.Err() != nil { | 582 | if rows.Err() != nil { |
532 | return resp, rows.Err() | 583 | return rows.Err() |
533 | } | 584 | } |
534 | 585 | ||
535 | return resp, nil | 586 | list.LiveGraph = resp |
587 |
middleware.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "net/http" | 4 | "net/http" |
5 | "time" | 5 | "time" |
6 | 6 | ||
7 | "git.to-net.rs/marko.tikvic/gologger" | 7 | "git.to-net.rs/marko.tikvic/gologger" |
8 | ) | 8 | ) |
9 | 9 | ||
10 | func SetHeaders(h http.HandlerFunc) http.HandlerFunc { | 10 | func SetHeaders(h http.HandlerFunc) http.HandlerFunc { |
11 | return func(w http.ResponseWriter, req *http.Request) { | 11 | return func(w http.ResponseWriter, req *http.Request) { |
12 | SetDefaultHeaders(w) | 12 | SetDefaultHeaders(w) |
13 | if req.Method == http.MethodOptions { | 13 | if req.Method == http.MethodOptions { |
14 | return | 14 | return |
15 | } | 15 | } |
16 | h(w, req) | 16 | h(w, req) |
17 | } | 17 | } |
18 | } | 18 | } |
19 | 19 | ||
20 | func ParseForm(h http.HandlerFunc) http.HandlerFunc { | 20 | func ParseForm(h http.HandlerFunc) http.HandlerFunc { |
21 | return func(w http.ResponseWriter, req *http.Request) { | 21 | return func(w http.ResponseWriter, req *http.Request) { |
22 | err := req.ParseForm() | 22 | err := req.ParseForm() |
23 | if err != nil { | 23 | if err != nil { |
24 | BadRequest(w, req, err.Error()) | 24 | BadRequest(w, req, err.Error()) |
25 | return | 25 | return |
26 | } | 26 | } |
27 | h(w, req) | 27 | h(w, req) |
28 | } | 28 | } |
29 | } | 29 | } |
30 | 30 | ||
31 | func ParseMultipartForm(h http.HandlerFunc) http.HandlerFunc { | ||
32 | return func(w http.ResponseWriter, req *http.Request) { | ||
33 | err := req.ParseMultipartForm(32 << 20) | ||
34 | if err != nil { | ||
35 | BadRequest(w, req, err.Error()) | ||
36 | return | ||
37 | } | ||
38 | h(w, req) | ||
39 | } | ||
40 | } | ||
41 | |||
31 | var trafficLogger *gologger.Logger | 42 | var trafficLogger *gologger.Logger |
32 | 43 | ||
33 | func EnableLogging(log string) error { | 44 | func EnableLogging(log string) error { |
34 | var err error | 45 | var err error |
35 | trafficLogger, err = gologger.New(log, gologger.MaxLogSize5MB) | 46 | trafficLogger, err = gologger.New(log, gologger.MaxLogSize5MB) |
36 | return err | 47 | return err |
37 | } | 48 | } |
38 | 49 | ||
39 | func Log(h http.HandlerFunc) http.HandlerFunc { | 50 | func Log(h http.HandlerFunc) http.HandlerFunc { |
40 | return func(w http.ResponseWriter, req *http.Request) { | 51 | return func(w http.ResponseWriter, req *http.Request) { |
52 | t1 := time.Now() | ||
53 | |||
41 | in := trafficLogger.RequestLog(req, "") | 54 | in := trafficLogger.RequestLog(req, "") |
42 | 55 | ||
43 | w2 := WrapWithStatusRecorder(w) | 56 | wRec := WrapWithStatusRecorder(w) |
44 | t1 := time.Now() | 57 | h(wRec, req) |
45 | h(w2, req) | 58 | |
46 | t2 := time.Now() | 59 | t2 := time.Now() |
47 | 60 | ||
48 | out := trafficLogger.ResponseLog(w2.Status(), t2.Sub(t1)) | 61 | out := trafficLogger.ResponseLog(wRec.Status(), t2.Sub(t1), 0) |
62 | |||
49 | trafficLogger.LogHTTPTraffic(in, out) | 63 | trafficLogger.LogHTTPTraffic(in, out) |
50 | } | 64 | } |
51 | } | 65 | } |
52 | 66 | ||
53 | func Auth(roles string, h http.HandlerFunc) http.HandlerFunc { | 67 | func Auth(roles string, h http.HandlerFunc) http.HandlerFunc { |
54 | return func(w http.ResponseWriter, req *http.Request) { | 68 | return func(w http.ResponseWriter, req *http.Request) { |
55 | if _, ok := AuthCheck(req, roles); !ok { | 69 | if _, err := AuthCheck(req, roles); err != nil { |
56 | Unauthorized(w, req, "") | 70 | Unauthorized(w, req, err.Error()) |
57 | return | 71 | return |
58 | } | 72 | } |
59 | h(w, req) | 73 | h(w, req) |
60 | } | 74 | } |
61 | } | 75 | } |
62 | 76 |