Commit e1fbb41f916dcaa9b5440a16cb5a0268a43ca18c
1 parent
c2ed249111
Exists in
master
and in
1 other branch
added comments
Showing
7 changed files
with
65 additions
and
26 deletions
Show diff stats
auth_utility.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "errors" | 4 | "errors" |
5 | "time" | 5 | "time" |
6 | "crypto/sha256" | 6 | "crypto/sha256" |
7 | "crypto/rand" | 7 | "crypto/rand" |
8 | "encoding/hex" | 8 | "encoding/hex" |
9 | "strings" | 9 | "strings" |
10 | "github.com/dgrijalva/jwt-go" | 10 | "github.com/dgrijalva/jwt-go" |
11 | ) | 11 | ) |
12 | 12 | ||
13 | const OneDay = time.Hour*24 | 13 | const OneDay = time.Hour*24 |
14 | const OneWeek = OneDay*7 | 14 | const OneWeek = OneDay*7 |
15 | const saltSize = 32 | 15 | const saltSize = 32 |
16 | const appName = "korisnicki-centar" | 16 | const appName = "korisnicki-centar" |
17 | const secret = "korisnicki-centar-api" | 17 | const secret = "korisnicki-centar-api" |
18 | 18 | ||
19 | type TokenClaims struct { | 19 | type TokenClaims struct { |
20 | Username string `json:"username"` | 20 | Username string `json:"username"` |
21 | Role string `json:"role"` | 21 | Role string `json:"role"` |
22 | jwt.StandardClaims | 22 | jwt.StandardClaims |
23 | } | 23 | } |
24 | 24 | ||
25 | type CredentialsStruct struct { | 25 | type CredentialsStruct struct { |
26 | Username string `json:"username"` | 26 | Username string `json:"username"` |
27 | Password string `json:"password"` | 27 | Password string `json:"password"` |
28 | } | 28 | } |
29 | 29 | ||
30 | // generateSalt returns a random string of 'saltSize' length to be used for hashing. | ||
30 | func generateSalt() (salt string, err error) { | 31 | func generateSalt() (salt string, err error) { |
31 | rawsalt := make([]byte, saltSize) | 32 | rawsalt := make([]byte, saltSize) |
32 | 33 | ||
33 | _, err = rand.Read(rawsalt) | 34 | _, err = rand.Read(rawsalt) |
34 | if err != nil { | 35 | if err != nil { |
35 | return "", err | 36 | return "", err |
36 | } | 37 | } |
37 | 38 | ||
38 | salt = hex.EncodeToString(rawsalt) | 39 | salt = hex.EncodeToString(rawsalt) |
39 | return salt, nil | 40 | return salt, nil |
40 | } | 41 | } |
41 | 42 | ||
43 | // HashString hashes input string with SHA256 algorithm. | ||
44 | // If the presalt parameter is not provided HashString will generate new salt string. | ||
42 | func HashString(str string, presalt string) (hash, salt string, err error) { | 45 | func HashString(str string, presalt string) (hash, salt string, err error) { |
43 | // chech if message is presalted | 46 | // chech if message is presalted |
44 | if presalt == "" { | 47 | if presalt == "" { |
45 | salt, err = generateSalt() | 48 | salt, err = generateSalt() |
46 | if err != nil { | 49 | if err != nil { |
47 | return "", "", err | 50 | return "", "", err |
48 | } | 51 | } |
49 | } else { | 52 | } else { |
50 | salt = presalt | 53 | salt = presalt |
51 | } | 54 | } |
52 | 55 | ||
53 | // convert strings to raw byte slices | 56 | // convert strings to raw byte slices |
54 | rawstr := []byte(str) | 57 | rawstr := []byte(str) |
55 | rawsalt, err := hex.DecodeString(salt) | 58 | rawsalt, err := hex.DecodeString(salt) |
56 | if err != nil { | 59 | if err != nil { |
57 | return "", "", err | 60 | return "", "", err |
58 | } | 61 | } |
59 | 62 | ||
60 | rawdata := make([]byte, len(rawstr) + len(rawsalt)) | 63 | rawdata := make([]byte, len(rawstr) + len(rawsalt)) |
61 | rawdata = append(rawdata, rawstr...) | 64 | rawdata = append(rawdata, rawstr...) |
62 | rawdata = append(rawdata, rawsalt...) | 65 | rawdata = append(rawdata, rawsalt...) |
63 | 66 | ||
64 | // hash message + salt | 67 | // hash message + salt |
65 | hasher := sha256.New() | 68 | hasher := sha256.New() |
66 | hasher.Write(rawdata) | 69 | hasher.Write(rawdata) |
67 | rawhash := hasher.Sum(nil) | 70 | rawhash := hasher.Sum(nil) |
68 | 71 | ||
69 | hash = hex.EncodeToString(rawhash) | 72 | hash = hex.EncodeToString(rawhash) |
70 | return hash, salt, nil | 73 | return hash, salt, nil |
71 | } | 74 | } |
72 | 75 | ||
76 | // CreateAPIToken creates JWT token encoding username, role, | ||
77 | // expiration date and issuer claims in it. | ||
73 | func CreateAPIToken(username, role string) (string, error) { | 78 | func CreateAPIToken(username, role string) (string, error) { |
74 | var apiToken string | 79 | var apiToken string |
75 | var err error | 80 | var err error |
76 | 81 | ||
77 | if err != nil { | 82 | if err != nil { |
78 | return "", err | 83 | return "", err |
79 | } | 84 | } |
80 | 85 | ||
81 | claims := TokenClaims{ | 86 | claims := TokenClaims{ |
82 | username, | 87 | username, |
83 | role, | 88 | role, |
84 | jwt.StandardClaims{ | 89 | jwt.StandardClaims{ |
85 | ExpiresAt: (time.Now().Add(OneWeek)).Unix(), | 90 | ExpiresAt: (time.Now().Add(OneWeek)).Unix(), |
86 | Issuer: appName, | 91 | Issuer: appName, |
87 | }, | 92 | }, |
88 | } | 93 | } |
89 | 94 | ||
90 | jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) | 95 | jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) |
91 | apiToken, err = jwtToken.SignedString([]byte(secret)) | 96 | apiToken, err = jwtToken.SignedString([]byte(secret)) |
92 | if err != nil { | 97 | if err != nil { |
93 | return "", err | 98 | return "", err |
94 | } | 99 | } |
95 | return apiToken, nil | 100 | return apiToken, nil |
96 | } | 101 | } |
97 | 102 | ||
103 | // RefreshAPIToken prolongs JWT token's expiration date. | ||
98 | func RefreshAPIToken(tokenString string) (string, error) { | 104 | func RefreshAPIToken(tokenString string) (string, error) { |
99 | var newToken string | 105 | var newToken string |
100 | tokenString = strings.TrimPrefix(tokenString, "Bearer ") | 106 | tokenString = strings.TrimPrefix(tokenString, "Bearer ") |
101 | token, err := parseTokenFunc(tokenString) | 107 | token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc) |
102 | if err != nil { | 108 | if err != nil { |
103 | return "", err | 109 | return "", err |
104 | } | 110 | } |
105 | 111 | ||
106 | // type assertion | 112 | // type assertion |
107 | claims, ok := token.Claims.(*TokenClaims) | 113 | claims, ok := token.Claims.(*TokenClaims) |
108 | if !ok || !token.Valid { | 114 | if !ok || !token.Valid { |
109 | return "", errors.New("token is not valid") | 115 | return "", errors.New("token is not valid") |
110 | } | 116 | } |
111 | 117 | ||
112 | claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix() | 118 | claims.ExpiresAt = (time.Now().Add(OneWeek)).Unix() |
113 | jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) | 119 | jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) |
114 | 120 | ||
115 | newToken, err = jwtToken.SignedString([]byte(secret)) | 121 | newToken, err = jwtToken.SignedString([]byte(secret)) |
116 | if err != nil { | 122 | if err != nil { |
117 | return "", err | 123 | return "", err |
118 | } | 124 | } |
119 | 125 | ||
120 | return newToken, nil | 126 | return newToken, nil |
121 | } | 127 | } |
122 | 128 | ||
129 | // ParseAPIToken parses JWT token claims. | ||
123 | func ParseAPIToken(tokenString string) (*TokenClaims, error) { | 130 | func ParseAPIToken(tokenString string) (*TokenClaims, error) { |
124 | if ok := strings.HasPrefix(tokenString, "Bearer"); ok { | 131 | if ok := strings.HasPrefix(tokenString, "Bearer "); ok { |
125 | tokenString = strings.TrimPrefix(tokenString, "Bearer ") | 132 | tokenString = strings.TrimPrefix(tokenString, "Bearer ") |
126 | } else { | 133 | } else { |
127 | return &TokenClaims{}, errors.New("Authorization header is incomplete") | 134 | return &TokenClaims{}, errors.New("Authorization header is incomplete") |
128 | } | 135 | } |
129 | 136 | ||
130 | token, err := parseTokenFunc(tokenString) | 137 | token, err := jwt.ParseWithClaims(tokenString, &TokenClaims{}, secretFunc) |
131 | if err != nil { | 138 | if err != nil { |
132 | return &TokenClaims{}, err | 139 | return &TokenClaims{}, err |
133 | } | 140 | } |
134 | 141 | ||
135 | // type assertion | 142 | // type assertion |
136 | claims, ok := token.Claims.(*TokenClaims) | 143 | claims, ok := token.Claims.(*TokenClaims) |
137 | if !ok || !token.Valid { | 144 | if !ok || !token.Valid { |
138 | return &TokenClaims{}, errors.New("token is not valid") | 145 | return &TokenClaims{}, errors.New("token is not valid") |
139 | } | 146 | } |
140 | return claims, nil | 147 | return claims, nil |
141 | } | 148 | } |
142 | 149 | ||
143 | func parseTokenFunc(tokenString string) (*jwt.Token, error) { | 150 | // secretFunc returns byte slice of 'secret'. |
144 | token, err := jwt.ParseWithClaims(tokenString, | 151 | func secretFunc(token *jwt.Token) (interface{}, error) { |
145 | &TokenClaims{}, | 152 | return []byte(secret), nil |
146 | func(token *jwt.Token) (interface{}, error) { | ||
147 | return []byte(secret), nil |
format_utility.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "time" | 4 | "time" |
5 | "fmt" | 5 | "fmt" |
6 | ) | 6 | ) |
7 | 7 | ||
8 | // UnixToDate converts given Unix time to local time in format and returns result: | ||
9 | // YYYY-MM-DD hh:mm:ss +zzzz UTC | ||
8 | func UnixToDate(unix int64) time.Time { | 10 | func UnixToDate(unix int64) time.Time { |
9 | return time.Unix(unix, 0) | 11 | return time.Unix(unix, 0) |
10 | } | 12 | } |
11 | 13 | ||
14 | // DateToUnix converts given date in Unix timestamp. | ||
12 | func DateToUnix(date interface{}) int64 { | 15 | func DateToUnix(date interface{}) int64 { |
13 | if date != nil { | 16 | if date != nil { |
14 | t := date.(time.Time) | 17 | t := date.(time.Time) |
15 | return t.Unix() | 18 | return t.Unix() |
16 | 19 | ||
17 | } | 20 | } |
18 | return 0 | 21 | return 0 |
19 | } | 22 | } |
20 | 23 | ||
24 | // EqualQuotes encapsulates given string in SQL 'equal' statement and returns result. | ||
25 | // Example: "hello" -> " = 'hello'" | ||
21 | func EqualQuotes(stmt string) string { | 26 | func EqualQuotes(stmt string) string { |
22 | if stmt != "" { | 27 | if stmt != "" { |
23 | stmt = fmt.Sprintf(" = '%s'", stmt) | 28 | stmt = fmt.Sprintf(" = '%s'", stmt) |
24 | } | 29 | } |
25 | return stmt | 30 | return stmt |
26 | } | 31 | } |
27 | 32 | ||
33 | // LikeQuotes encapsulates given string in SQL 'like' statement and returns result. | ||
34 | // Example: "hello" -> " LIKE UPPER('%hello%')" | ||
28 | func LikeQuotes(stmt string) string { | 35 | func LikeQuotes(stmt string) string { |
29 | if stmt != "" { | 36 | if stmt != "" { |
30 | stmt = fmt.Sprintf(" LIKE UPPER('%s%s%s')", "%", stmt, "%") | 37 | stmt = fmt.Sprintf(" LIKE UPPER('%s%s%s')", "%", stmt, "%") |
31 | } | 38 | } |
32 | return stmt | 39 | return stmt |
33 | } | 40 | } |
34 | 41 | ||
35 | 42 |
http_utility.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "net/http" | 4 | "net/http" |
5 | "encoding/json" | 5 | "encoding/json" |
6 | ) | 6 | ) |
7 | 7 | ||
8 | const templateHttpErr500_EN = "An internal server error has occurred." | 8 | const templateHttpErr500_EN = "An internal server error has occurred." |
9 | const templateHttpErr500_RS = "Došlo je do greške na serveru." | 9 | const templateHttpErr500_RS = "Došlo je do greške na serveru." |
10 | const templateHttpErr400_EN = "Bad request: invalid request body." | 10 | const templateHttpErr400_EN = "Bad request: invalid request body." |
11 | const templateHttpErr400_RS = "Neispravan zahtev." | 11 | const templateHttpErr400_RS = "Neispravan zahtev." |
12 | const templateHttpErr401_EN = "Unauthorized request." | 12 | const templateHttpErr401_EN = "Unauthorized request." |
13 | const templateHttpErr401_RS = "Neautorizovan zahtev." | 13 | const templateHttpErr401_RS = "Neautorizovan zahtev." |
14 | 14 | ||
15 | type httpError struct { | 15 | type httpError struct { |
16 | Error []HttpErrorDesc `json:"error"` | 16 | Error []HttpErrorDesc `json:"error"` |
17 | Request string `json:"request"` | 17 | Request string `json:"request"` |
18 | } | 18 | } |
19 | 19 | ||
20 | type HttpErrorDesc struct { | 20 | type HttpErrorDesc struct { |
21 | Lang string `json:"lang"` | 21 | Lang string `json:"lang"` |
22 | Desc string `json:"description"` | 22 | Desc string `json:"description"` |
23 | } | 23 | } |
24 | 24 | ||
25 | // ErrorResponse writes HTTP error to w. | ||
25 | func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) { | 26 | func ErrorResponse(w http.ResponseWriter, r *http.Request, code int, desc []HttpErrorDesc) { |
26 | err := httpError{ desc, r.Method + " " + r.URL.Path } | 27 | err := httpError{ desc, r.Method + " " + r.URL.Path } |
27 | w.WriteHeader(code) | 28 | w.WriteHeader(code) |
28 | json.NewEncoder(w).Encode(err) | 29 | json.NewEncoder(w).Encode(err) |
29 | } | 30 | } |
30 | 31 | ||
32 | // BadRequestResponse writes HTTP error 400 to w. | ||
31 | func BadRequestResponse(w http.ResponseWriter, req *http.Request) { | 33 | func BadRequestResponse(w http.ResponseWriter, req *http.Request) { |
32 | ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{ | 34 | ErrorResponse(w, req, http.StatusBadRequest, []HttpErrorDesc{ |
33 | { "en", templateHttpErr400_EN }, | 35 | { "en", templateHttpErr400_EN }, |
34 | { "rs", templateHttpErr400_RS }, | 36 | { "rs", templateHttpErr400_RS }, |
35 | }) | 37 | }) |
36 | } | 38 | } |
37 | 39 | ||
40 | // InternalSeverErrorResponse writes HTTP error 500 to w. | ||
38 | func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) { | 41 | func InternalServerErrorResponse(w http.ResponseWriter, req *http.Request) { |
39 | ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{ | 42 | ErrorResponse(w, req, http.StatusInternalServerError, []HttpErrorDesc{ |
40 | { "en", templateHttpErr500_EN }, | 43 | { "en", templateHttpErr500_EN }, |
41 | { "rs", templateHttpErr500_RS }, | 44 | { "rs", templateHttpErr500_RS }, |
42 | }) | 45 | }) |
43 | } | 46 | } |
44 | 47 | ||
48 | // UnauthorizedError writes HTTP error 401 to w. | ||
45 | func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) { | 49 | func UnauthorizedResponse(w http.ResponseWriter, req *http.Request) { |
46 | ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{ | 50 | ErrorResponse(w, req, http.StatusUnauthorized, []HttpErrorDesc{ |
47 | { "en", templateHttpErr500_EN }, | 51 | { "en", templateHttpErr500_EN }, |
48 | { "rs", templateHttpErr500_RS }, | 52 | { "rs", templateHttpErr500_RS }, |
49 | }) | 53 | }) |
50 | } | 54 | } |
51 | 55 | ||
52 | // TODO: Check for content type | 56 | // TODO: Check for content type |
53 | // Sets common headers and checks for token validity. | 57 | // Sets common headers, checks for token validity and performs access control. |
54 | func WrapHandler(handlerFunc http.HandlerFunc, auth bool) http.HandlerFunc { | 58 | func WrapHandler(handlerFunc http.HandlerFunc, auth bool) http.HandlerFunc { |
55 | return func(w http.ResponseWriter, req *http.Request) { | 59 | return func(w http.ResponseWriter, req *http.Request) { |
56 | w.Header().Set("Access-Control-Allow-Origin", "*") | 60 | w.Header().Set("Access-Control-Allow-Origin", "*") |
57 | 61 | ||
58 | w.Header().Set("Access-Control-Allow-Methods", | 62 | w.Header().Set("Access-Control-Allow-Methods", |
59 | "POST, GET, PUT, DELETE, OPTIONS") | 63 | "POST, GET, PUT, DELETE, OPTIONS") |
60 | 64 | ||
61 | w.Header().Set("Access-Control-Allow-Headers", | 65 | w.Header().Set("Access-Control-Allow-Headers", |
62 | `Accept, Content-Type, Content-Length, | 66 | `Accept, Content-Type, Content-Length, |
63 | Accept-Encoding, X-CSRF-Token, Authorization`) | 67 | Accept-Encoding, X-CSRF-Token, Authorization`) |
64 | 68 | ||
65 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | 69 | w.Header().Set("Content-Type", "application/json; charset=utf-8") |
66 | 70 | ||
67 | if req.Method == "OPTIONS" { | 71 | if req.Method == "OPTIONS" { |
68 | return | 72 | return |
69 | } | 73 | } |
70 | 74 | ||
71 | if auth { | 75 | if auth { |
72 | token := req.Header.Get("Authorization") | 76 | token := req.Header.Get("Authorization") |
73 | if _, err := ParseAPIToken(token); err != nil { | 77 | if _, err := ParseAPIToken(token); err != nil { |
74 | UnauthorizedResponse(w, req) | 78 | UnauthorizedResponse(w, req) |
75 | return | 79 | return |
76 | } | 80 | } |
77 | } | 81 | } |
78 | 82 | ||
79 | err := req.ParseForm() | 83 | err := req.ParseForm() |
80 | if err != nil { | 84 | if err != nil { |
81 | BadRequestResponse(w, req) | 85 | BadRequestResponse(w, req) |
82 | return | 86 | return |
83 | } | 87 | } |
84 | 88 | ||
85 | // execute HandlerFunc | 89 | // execute HandlerFunc |
86 | handlerFunc(w, req) | 90 | handlerFunc(w, req) |
87 | } | 91 | } |
88 | } | 92 | } |
89 | 93 | ||
94 | // NotFoundHandler writes HTTP error 404 to w. | ||
90 | func NotFoundHandler(w http.ResponseWriter, req *http.Request) { | 95 | func NotFoundHandler(w http.ResponseWriter, req *http.Request) { |
91 | ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{ | 96 | ErrorResponse(w, req, http.StatusNotFound, []HttpErrorDesc{ |
92 | { "en", "Not found." }, | 97 | { "en", "Not found." }, |
93 | { "rs", "Traženi resurs ne postoji." }, | 98 | { "rs", "Traženi resurs ne postoji." }, |
94 | }) | 99 | }) |
95 | } | 100 | } |
96 | 101 |
json_utility.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "net/http" | 4 | "net/http" |
5 | "encoding/json" | 5 | "encoding/json" |
6 | "errors" | 6 | "errors" |
7 | "gopkg.in/rana/ora.v3" | 7 | "gopkg.in/rana/ora.v3" |
8 | "io" | 8 | "io" |
9 | "io/ioutil" | 9 | "io/ioutil" |
10 | "sync" | 10 | "sync" |
11 | ) | 11 | ) |
12 | 12 | ||
13 | var mu = &sync.Mutex{} | 13 | var mu = &sync.Mutex{} |
14 | var allPayloads []payloadBuff | 14 | var allPayloads []payloadBuff |
15 | 15 | ||
16 | type LangMap map[string]map[string]string | 16 | type LangMap map[string]map[string]string |
17 | 17 | ||
18 | type Field struct { | 18 | type Field struct { |
19 | Parameter string `json:"param"` | 19 | Parameter string `json:"param"` |
20 | Type string `json:"type"` | 20 | Type string `json:"type"` |
21 | Visible bool `json:"visible"` | 21 | Visible bool `json:"visible"` |
22 | Editable bool `json:"editable"` | 22 | Editable bool `json:"editable"` |
23 | } | 23 | } |
24 | 24 | ||
25 | type CorrelationField struct { | 25 | type CorrelationField struct { |
26 | Result string `json:"result"` | 26 | Result string `json:"result"` |
27 | Elements []string `json:"elements"` | 27 | Elements []string `json:"elements"` |
28 | Type string `json:"type"` | 28 | Type string `json:"type"` |
29 | } | 29 | } |
30 | 30 | ||
31 | type Translation struct { | 31 | type Translation struct { |
32 | Language string `json:"language"` | 32 | Language string `json:"language"` |
33 | FieldsLabels map[string]string `json:"fieldsLabels"` | 33 | FieldsLabels map[string]string `json:"fieldsLabels"` |
34 | } | 34 | } |
35 | 35 | ||
36 | // Field 'Type' is not required in payload. | ||
37 | // 'payloadBuff' type is only a bridge between ORACLE CLOB and 'Payload' type. | ||
38 | type payloadBuff struct { | 36 | type payloadBuff struct { |
39 | Type string `json:"tableType"` | 37 | Type string `json:"tableType"` |
40 | Method string `json:"method"` | 38 | Method string `json:"method"` |
41 | Params map[string]string `json:"params"` | 39 | Params map[string]string `json:"params"` |
42 | Lang []Translation `json:"lang"` | 40 | Lang []Translation `json:"lang"` |
43 | Fields []Field `json:"fields"` | 41 | Fields []Field `json:"fields"` |
44 | Correlations []CorrelationField `json:"correlationFields"` | 42 | Correlations []CorrelationField `json:"correlationFields"` |
45 | IdField string `json:"idField"` | 43 | IdField string `json:"idField"` |
46 | // Data can only hold slices of any type. It can't be used for itteration | 44 | // Data can only hold slices of any type. It can't be used for itteration |
47 | Data interface{} `json:"data"` | 45 | Data interface{} `json:"data"` |
48 | } | 46 | } |
49 | 47 | ||
50 | type Payload struct { | 48 | type Payload struct { |
51 | Method string `json:"method"` | 49 | Method string `json:"method"` |
52 | Params map[string]string `json:"params"` | 50 | Params map[string]string `json:"params"` |
53 | Lang []Translation `json:"lang"` | 51 | Lang []Translation `json:"lang"` |
54 | Fields []Field `json:"fields"` | 52 | Fields []Field `json:"fields"` |
55 | Correlations []CorrelationField `json:"correlationFields"` | 53 | Correlations []CorrelationField `json:"correlationFields"` |
56 | IdField string `json:"idField"` | 54 | IdField string `json:"idField"` |
55 | |||
57 | // Data can only hold slices of any type. It can't be used for itteration | 56 | // Data can only hold slices of any type. It can't be used for itteration |
58 | Data interface{} `json:"data"` | 57 | Data interface{} `json:"data"` |
59 | } | 58 | } |
60 | 59 | ||
60 | // NewPayload returs payload for provided table. | ||
61 | func NewPayload(r *http.Request, table string) Payload { | 61 | func NewPayload(r *http.Request, table string) Payload { |
62 | var pload Payload | 62 | var pload Payload |
63 | 63 | ||
64 | pload.Method = r.Method + " " + r.URL.Path | 64 | pload.Method = r.Method + " " + r.URL.Path |
65 | |||
66 | if table != "" { | 65 | if table != "" { |
67 | pload.Params = make(map[string]string, 0) | 66 | pload.Params = make(map[string]string, 0) |
68 | pload.Lang = loadTranslations(table) | 67 | pload.Lang = loadTranslations(table) |
69 | pload.Fields = loadFields(table) | 68 | pload.Fields = loadFields(table) |
70 | pload.IdField = loadIdField(table) | 69 | pload.IdField = loadIdField(table) |
71 | pload.Correlations = loadCorrelations(table) | 70 | pload.Correlations = loadCorrelations(table) |
72 | } | 71 | } |
73 | |||
74 | return pload | 72 | return pload |
75 | } | 73 | } |
76 | 74 | ||
75 | // DeliverPayload writes payload to w. | ||
77 | func DeliverPayload(w http.ResponseWriter, payload Payload) { | 76 | func DeliverPayload(w http.ResponseWriter, payload Payload) { |
78 | json.NewEncoder(w).Encode(payload) | 77 | json.NewEncoder(w).Encode(payload) |
79 | payload.Data = nil | 78 | payload.Data = nil |
80 | } | 79 | } |
81 | 80 | ||
81 | // loadTranslations loads translations for a payload of the given data type. | ||
82 | func loadTranslations(id string) []Translation { | 82 | func loadTranslations(id string) []Translation { |
83 | translations := make([]Translation, 0) | 83 | translations := make([]Translation, 0) |
84 | 84 | ||
85 | for _, pload := range allPayloads { | 85 | for _, pload := range allPayloads { |
86 | if pload.Type == id { | 86 | if pload.Type == id { |
87 | for _, t := range pload.Lang { | 87 | for _, t := range pload.Lang { |
88 | //translations[t.Language] = t.FieldsLabels | 88 | //translations[t.Language] = t.FieldsLabels |
89 | translations = append(translations, Translation{ | 89 | translations = append(translations, Translation{ |
90 | Language: t.Language, | 90 | Language: t.Language, |
91 | FieldsLabels: t.FieldsLabels, | 91 | FieldsLabels: t.FieldsLabels, |
92 | }) | 92 | }) |
93 | } | 93 | } |
94 | } | 94 | } |
95 | } | 95 | } |
96 | 96 | ||
97 | return translations | 97 | return translations |
98 | } | 98 | } |
99 | 99 | ||
100 | // loadFields loads fields for a payload of the given data type. | ||
100 | func loadFields(id string) []Field { | 101 | func loadFields(id string) []Field { |
101 | fields := make([]Field, 0) | 102 | fields := make([]Field, 0) |
102 | 103 | ||
103 | for _, pload := range allPayloads { | 104 | for _, pload := range allPayloads { |
104 | if pload.Type == id{ | 105 | if pload.Type == id{ |
105 | for _, f := range pload.Fields { | 106 | for _, f := range pload.Fields { |
106 | fields = append(fields, f) | 107 | fields = append(fields, f) |
107 | } | 108 | } |
108 | } | 109 | } |
109 | } | 110 | } |
110 | 111 | ||
111 | return fields | 112 | return fields |
112 | } | 113 | } |
113 | 114 | ||
115 | // loadIdField loads ID field for a payload of the given data type. | ||
114 | func loadIdField(id string) string { | 116 | func loadIdField(id string) string { |
115 | for _, pload := range allPayloads { | 117 | for _, pload := range allPayloads { |
116 | if pload.Type == id { | 118 | if pload.Type == id { |
117 | return pload.IdField | 119 | return pload.IdField |
118 | } | 120 | } |
119 | } | 121 | } |
120 | return "" | 122 | return "" |
121 | } | 123 | } |
122 | 124 | ||
125 | // loadCorrelations loads correlations field for a payload of the given data type. | ||
123 | func loadCorrelations(id string) []CorrelationField { | 126 | func loadCorrelations(id string) []CorrelationField { |
124 | resp := make([]CorrelationField, 0) | 127 | resp := make([]CorrelationField, 0) |
125 | 128 | ||
126 | for _, pload := range allPayloads { | 129 | for _, pload := range allPayloads { |
127 | if pload.Type == id { | 130 | if pload.Type == id { |
128 | for _, f := range pload.Correlations { | 131 | for _, f := range pload.Correlations { |
129 | resp = append(resp, f) | 132 | resp = append(resp, f) |
130 | } | 133 | } |
131 | } | 134 | } |
132 | } | 135 | } |
133 | 136 | ||
134 | return resp | 137 | return resp |
135 | } | 138 | } |
136 | 139 | ||
140 | // InitTables loads all payloads in the memory. | ||
137 | func InitTables(db *ora.Ses, project string) error { | 141 | func InitTables(db *ora.Ses, project string) error { |
138 | jsonbuf, _ := fetchJSON(db, EqualQuotes(project)) | 142 | jsonbuf, _ := fetchJSON(db, EqualQuotes(project)) |
139 | mu.Lock() | 143 | mu.Lock() |
140 | defer mu.Unlock() | 144 | defer mu.Unlock() |
141 | json.Unmarshal(jsonbuf, &allPayloads) | 145 | json.Unmarshal(jsonbuf, &allPayloads) |
142 | if len(allPayloads) == 0 { | 146 | if len(allPayloads) == 0 { |
143 | return errors.New("tables config is corrupt") | 147 | return errors.New("tables config is corrupt") |
144 | } | 148 | } |
145 | return nil | 149 | return nil |
146 | } | 150 | } |
147 | 151 | ||
152 | // fetchJSON fetches JSON configuration file from TABLES_CONFIG table. | ||
148 | func fetchJSON(db *ora.Ses, project string) ([]byte, error) { | 153 | func fetchJSON(db *ora.Ses, project string) ([]byte, error) { |
149 | stmt, err := db.Prep(`SELECT | 154 | stmt, err := db.Prep(`SELECT |
150 | JSON_CLOB | 155 | JSON_CLOB |
151 | FROM TABLES_CONFIG | 156 | FROM TABLES_CONFIG |
152 | WHERE PROJEKAT` + project, ora.S) | 157 | WHERE PROJEKAT` + project, ora.S) |
153 | defer stmt.Close() | 158 | defer stmt.Close() |
154 | 159 | ||
155 | if err != nil { | 160 | if err != nil { |
156 | return nil, err | 161 | return nil, err |
157 | } | 162 | } |
158 | 163 | ||
159 | rset, err := stmt.Qry() | 164 | rset, err := stmt.Qry() |
160 | if err != nil { | 165 | if err != nil { |
161 | return nil, err | 166 | return nil, err |
162 | } | 167 | } |
163 | 168 | ||
164 | bytes := make([]byte, 0) | 169 | bytes := make([]byte, 0) |
165 | if rset.Next() { | 170 | if rset.Next() { |
166 | lob := rset.Row[0].(io.Reader) | 171 | lob := rset.Row[0].(io.Reader) |
167 | bytes, err = ioutil.ReadAll(lob) | 172 | bytes, err = ioutil.ReadAll(lob) |
168 | if err != nil { | 173 | if err != nil { |
169 | // Ignore for now, it's some weird streaming read/write LOB error. | 174 | // Ignore for now, it's some weird streaming read/write LOB error. |
170 | // TODO: Find a fix for this. | 175 | // TODO: Find a fix for this. |
171 | //return nil, err | 176 | //return nil, err |
172 | } | 177 | } |
173 | } | 178 | } |
174 | 179 | ||
175 | return bytes, nil | 180 | return bytes, nil |
176 | } | 181 | } |
177 | 182 | ||
183 | // DecodeJSON decodes JSON data from r to v and returns any error | ||
184 | // that happens during decoding process. |
list_config.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "gopkg.in/rana/ora.v3" | 4 | "gopkg.in/rana/ora.v3" |
5 | ) | 5 | ) |
6 | 6 | ||
7 | type ListOptions struct { | 7 | type ListOptions struct { |
8 | GlobalFilter bool `json:"globalFilter"` | 8 | GlobalFilter bool `json:"globalFilter"` |
9 | LocalFilters bool `json:"localFilters"` | 9 | LocalFilters bool `json:"localFilters"` |
10 | RemoteFilters bool `json:"remoteFilters"` | 10 | RemoteFilters bool `json:"remoteFilters"` |
11 | Pagination bool `json:"pagination"` | 11 | Pagination bool `json:"pagination"` |
12 | PageSize uint64 `json:"pageSize"` | 12 | PageSize uint64 `json:"pageSize"` |
13 | Pivot bool `json:"pivot"` | 13 | Pivot bool `json:"pivot"` |
14 | Detail bool `json:"detail"` | 14 | Detail bool `json:"detail"` |
15 | Total bool `json:"total"` | 15 | Total bool `json:"total"` |
16 | } | 16 | } |
17 | 17 | ||
18 | type ListFilter struct { | 18 | type ListFilter struct { |
19 | Position uint32 `json:"-"` | 19 | Position uint32 `json:"-"` |
20 | ObjectType string `json:"-"` | 20 | ObjectType string `json:"-"` |
21 | FiltersField string `json:"filtersField"` | 21 | FiltersField string `json:"filtersField"` |
22 | DefaultValues string `json:"defaultValues"` | 22 | DefaultValues string `json:"defaultValues"` |
23 | FiltersType string `json:"filtersType"` | 23 | FiltersType string `json:"filtersType"` |
24 | FiltersLabel string `json:"filtersLabel"` | 24 | FiltersLabel string `json:"filtersLabel"` |
25 | DropdownConfig Dropdown `json:"dropdownConfig"` | 25 | DropdownConfig Dropdown `json:"dropdownConfig"` |
26 | } | 26 | } |
27 | 27 | ||
28 | type Dropdown struct { | 28 | type Dropdown struct { |
29 | ObjectType string `json:"objectType"` | 29 | ObjectType string `json:"objectType"` |
30 | FiltersField string `json:"filtersField"` | 30 | FiltersField string `json:"filtersField"` |
31 | IdField string `json:"idField"` | 31 | IDField string `json:"idField"` |
32 | LabelField string `json:"labelField"` | 32 | LabelField string `json:"labelField"` |
33 | 33 | ||
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 | } | 51 | } |
52 | 52 | ||
53 | type ListNavNode struct { | 53 | type ListNavNode struct { |
54 | ObjectType string `json:"objectType"` | 54 | ObjectType string `json:"objectType"` |
55 | LabelField string `json:"label"` | 55 | LabelField string `json:"label"` |
56 | Icon string `json:"icon"` | 56 | Icon string `json:"icon"` |
57 | ParentObjectType string `json:"parentObjectType"` | 57 | ParentObjectType string `json:"parentObjectType"` |
58 | ParentIdField string `json:"parentIdField"` | 58 | ParentIDField string `json:"parentIdField"` |
59 | } | 59 | } |
60 | 60 | ||
61 | type ListParentNode struct { | 61 | type ListParentNode struct { |
62 | ObjectType string `json:"objectType"` | 62 | ObjectType string `json:"objectType"` |
63 | LabelField string `json:"labelField"` | 63 | LabelField string `json:"labelField"` |
64 | FilterField string `json:"filterField"` | 64 | FilterField string `json:"filterField"` |
65 | } | 65 | } |
66 | 66 | ||
67 | type ListPivot struct { | 67 | type ListPivot struct { |
68 | ObjectType string `json:"objectType"` | 68 | ObjectType string `json:"objectType"` |
69 | GroupField string `json:"groupField"` | 69 | GroupField string `json:"groupField"` |
70 | DistinctField string `json:"distinctField"` | 70 | DistinctField string `json:"distinctField"` |
71 | Value string `json:"valueField"` | 71 | Value string `json:"valueField"` |
72 | } | 72 | } |
73 | 73 | ||
74 | type ListDetails struct { | 74 | type ListDetails struct { |
75 | ObjectType string `json:"objectType"` | 75 | ObjectType string `json:"objectType"` |
76 | ParentObjectType string `json:"parentObjectType"` | 76 | ParentObjectType string `json:"parentObjectType"` |
77 | ParentFilterField string `json:"parentFilterField"` | 77 | ParentFilterField string `json:"parentFilterField"` |
78 | SingleDetail bool `json:"singleDetail"` | 78 | SingleDetail bool `json:"singleDetail"` |
79 | } | 79 | } |
80 | 80 | ||
81 | type ListConfig struct { | 81 | type ListConfig struct { |
82 | ObjectType string `json:"objectType"` | 82 | ObjectType string `json:"objectType"` |
83 | Title string `json:"title"` | 83 | Title string `json:"title"` |
84 | LazyLoad bool `json:"lazyLoad"` | 84 | LazyLoad bool `json:"lazyLoad"` |
85 | InlineEdit bool `json:"inlineEdit"` | 85 | InlineEdit bool `json:"inlineEdit"` |
86 | Options ListOptions `json:"options"` | 86 | Options ListOptions `json:"options"` |
87 | Filters []ListFilter `json:"defaultFilters"` | 87 | Filters []ListFilter `json:"defaultFilters"` |
88 | Graphs []ListGraph `json:"graphs"` | 88 | Graphs []ListGraph `json:"graphs"` |
89 | Actions ListActions `json:"actions"` | 89 | Actions ListActions `json:"actions"` |
90 | Parent []ListParentNode `json:"parent"` | 90 | Parent []ListParentNode `json:"parent"` |
91 | Navigation []ListNavNode `json:"navigation"` | 91 | Navigation []ListNavNode `json:"navigation"` |
92 | Pivots []ListPivot `json:"pivots"` | 92 | Pivots []ListPivot `json:"pivots"` |
93 | Details ListDetails `json:"details"` | 93 | Details ListDetails `json:"details"` |
94 | } | 94 | } |
95 | 95 | ||
96 | // GetListConfig returns list configuration for the given object type for the front-end application. | ||
96 | func GetListConfig(db *ora.Ses, objType string) (ListConfig, error) { | 97 | func GetListConfig(db *ora.Ses, objType string) (ListConfig, error) { |
97 | resp := NewDefaultList(objType) | 98 | resp := newDefaultList(objType) |
98 | var err error | 99 | var err error |
99 | 100 | ||
100 | err = setListParams(db, &resp, objType) | 101 | err = setListParams(db, &resp, objType) |
101 | resp.Navigation, err = getListNavigation(db, objType) | 102 | resp.Navigation, err = getListNavigation(db, objType) |
102 | resp.Actions, err = getListActions(db, objType) | 103 | resp.Actions, err = getListActions(db, objType) |
103 | resp.Filters, err = getListFilters(db, objType) | 104 | resp.Filters, err = getListFilters(db, objType) |
104 | resp.Options, err = getListOptions(db, objType) | 105 | resp.Options, err = getListOptions(db, objType) |
105 | resp.Parent, err = getListParent(db, objType) | 106 | resp.Parent, err = getListParent(db, objType) |
106 | resp.Graphs, err = getListGraph(db, objType) | 107 | resp.Graphs, err = getListGraph(db, objType) |
107 | resp.Pivots, err = getListPivot(db, objType) | 108 | resp.Pivots, err = getListPivot(db, objType) |
108 | resp.Details, err = getListDetails(db, objType) | 109 | resp.Details, err = getListDetails(db, objType) |
109 | 110 | ||
110 | if err != nil { | 111 | if err != nil { |
111 | return ListConfig{}, err | 112 | return ListConfig{}, err |
112 | } | 113 | } |
113 | 114 | ||
114 | return resp, nil | 115 | return resp, nil |
115 | } | 116 | } |
116 | 117 | ||
117 | func GetListConfigObjectIdField(db *ora.Ses, otype string) string { | 118 | // GetListConfigObjectIDField returns ID field for the given object type. |
119 | func GetListConfigObjectIDField(db *ora.Ses, otype string) string { | ||
118 | var resp string | 120 | var resp string |
119 | var err error | 121 | var err error |
120 | var stmt *ora.Stmt | 122 | var stmt *ora.Stmt |
121 | 123 | ||
122 | stmt, err = db.Prep(`SELECT | 124 | stmt, err = db.Prep(`SELECT |
123 | ID_FIELD | 125 | ID_FIELD |
124 | FROM LIST_CONFIG_ID_FIELD | 126 | FROM LIST_CONFIG_ID_FIELD |
125 | WHERE OBJECT_TYPE = '` + otype + `'`, | 127 | WHERE OBJECT_TYPE = '` + otype + `'`, |
126 | ora.S) | 128 | ora.S) |
127 | 129 | ||
128 | defer stmt.Close() | 130 | defer stmt.Close() |
129 | 131 | ||
130 | if err != nil { | 132 | if err != nil { |
131 | return "" | 133 | return "" |
132 | } | 134 | } |
133 | 135 | ||
134 | rset, err := stmt.Qry() | 136 | rset, err := stmt.Qry() |
135 | if rset.Next() { | 137 | if rset.Next() { |
136 | resp = rset.Row[0].(string) | 138 | resp = rset.Row[0].(string) |
137 | } | 139 | } |
138 | 140 | ||
139 | if rset.Err != nil { | 141 | if rset.Err != nil { |
140 | return "" | 142 | return "" |
141 | } | 143 | } |
142 | 144 | ||
143 | return resp | 145 | return resp |
144 | } | 146 | } |
145 | 147 | ||
146 | func NewDefaultList(objType string) ListConfig { | 148 | // newDefaultList returns default configuration for the given object type. |
149 | func newDefaultList(objType string) ListConfig { | ||
147 | list := ListConfig{ | 150 | list := ListConfig{ |
148 | ObjectType: objType, | 151 | ObjectType: objType, |
149 | Title: objType, | 152 | Title: objType, |
150 | LazyLoad: false, | 153 | LazyLoad: false, |
151 | Options: ListOptions{ | 154 | Options: ListOptions{ |
152 | GlobalFilter: true, | 155 | GlobalFilter: true, |
153 | LocalFilters: true, | 156 | LocalFilters: true, |
154 | RemoteFilters: false, | 157 | RemoteFilters: false, |
155 | Pagination: true, | 158 | Pagination: true, |
156 | PageSize: 20, | 159 | PageSize: 20, |
157 | }, | 160 | }, |
158 | Filters: nil, | 161 | Filters: nil, |
159 | Actions: ListActions{ | 162 | Actions: ListActions{ |
160 | Create: false, | 163 | Create: false, |
161 | Update: false, | 164 | Update: false, |
162 | Delete: false, | 165 | Delete: false, |
163 | Export: false, | 166 | Export: false, |
164 | Print: false, | 167 | Print: false, |
165 | Graph: false, | 168 | Graph: false, |
166 | }, | 169 | }, |
167 | Parent: nil, | 170 | Parent: nil, |
168 | Navigation: nil, | 171 | Navigation: nil, |
169 | } | 172 | } |
170 | 173 | ||
171 | return list | 174 | return list |
172 | } | 175 | } |
173 | 176 | ||
177 | // setListParams sets the default parameters of the provided configuration list for the given object type. | ||
174 | func setListParams(db *ora.Ses, list *ListConfig, objType string) error { | 178 | func setListParams(db *ora.Ses, list *ListConfig, objType string) error { |
175 | var err error | 179 | var err error |
176 | var stmt *ora.Stmt | 180 | var stmt *ora.Stmt |
177 | query := `SELECT | 181 | query := `SELECT |
178 | OBJECT_TYPE, TITLE, LAZY_LOAD, INLINE_EDIT | 182 | OBJECT_TYPE, TITLE, LAZY_LOAD, INLINE_EDIT |
179 | FROM LIST_CONFIG | 183 | FROM LIST_CONFIG |
180 | WHERE OBJECT_TYPE = '` + objType + `'` | 184 | WHERE OBJECT_TYPE = '` + objType + `'` |
181 | 185 | ||
182 | stmt, err = db.Prep(query, ora.S, ora.S, ora.U32, ora.U32) | 186 | stmt, err = db.Prep(query, ora.S, ora.S, ora.U32, ora.U32) |
183 | if err != nil { | 187 | if err != nil { |
184 | return err | 188 | return err |
185 | } | 189 | } |
186 | defer stmt.Close() | 190 | defer stmt.Close() |
187 | 191 | ||
188 | rset, err := stmt.Qry() | 192 | rset, err := stmt.Qry() |
189 | if err != nil { | 193 | if err != nil { |
190 | return err | 194 | return err |
191 | } | 195 | } |
192 | if rset.Next() { | 196 | if rset.Next() { |
193 | otype := rset.Row[0].(string) | 197 | otype := rset.Row[0].(string) |
194 | if otype != "" { | 198 | if otype != "" { |
195 | list.ObjectType = otype | 199 | list.ObjectType = otype |
196 | } | 200 | } |
197 | 201 | ||
198 | title := rset.Row[1].(string) | 202 | title := rset.Row[1].(string) |
199 | if title != "" { | 203 | if title != "" { |
200 | list.Title = title | 204 | list.Title = title |
201 | } | 205 | } |
202 | list.LazyLoad = rset.Row[2].(uint32) != 0 | 206 | list.LazyLoad = rset.Row[2].(uint32) != 0 |
203 | list.InlineEdit = rset.Row[3].(uint32) != 0 | 207 | list.InlineEdit = rset.Row[3].(uint32) != 0 |
204 | } | 208 | } |
205 | if rset.Err != nil { | 209 | if rset.Err != nil { |
206 | return rset.Err | 210 | return rset.Err |
207 | } | 211 | } |
208 | return nil | 212 | return nil |
209 | } | 213 | } |
210 | 214 | ||
215 | // getListNavigation returns list navigation node slice for the given objectType. | ||
211 | func getListNavigation(db *ora.Ses, listObjType string) ([]ListNavNode, error) { | 216 | func getListNavigation(db *ora.Ses, listObjType string) ([]ListNavNode, error) { |
212 | resp := make([]ListNavNode, 0) | 217 | resp := make([]ListNavNode, 0) |
213 | var err error | 218 | var err error |
214 | var stmt *ora.Stmt | 219 | var stmt *ora.Stmt |
215 | query := `SELECT | 220 | query := `SELECT |
216 | a.OBJECT_TYPE, a.PARENT_OBJECT_TYPE, a.LABEL, a.ICON, b.PARENT_ID_FIELD, b.RB | 221 | a.OBJECT_TYPE, a.PARENT_OBJECT_TYPE, a.LABEL, a.ICON, b.PARENT_ID_FIELD, b.RB |
217 | FROM LIST_CONFIG_NAVIGATION b | 222 | FROM LIST_CONFIG_NAVIGATION b |
218 | JOIN LIST_CONFIG_CHILD a ON b.PARENT_CHILD_ID = a.PARENT_CHILD_ID | 223 | JOIN LIST_CONFIG_CHILD a ON b.PARENT_CHILD_ID = a.PARENT_CHILD_ID |
219 | WHERE b.LIST_OBJECT_TYPE = '`+listObjType+`' | 224 | WHERE b.LIST_OBJECT_TYPE = '`+listObjType+`' |
220 | ORDER BY b.RB ASC` | 225 | ORDER BY b.RB ASC` |
221 | 226 | ||
222 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S, ora.S) | 227 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S, ora.S) |
223 | if err != nil { | 228 | if err != nil { |
224 | return resp, err | 229 | return resp, err |
225 | } | 230 | } |
226 | defer stmt.Close() | 231 | defer stmt.Close() |
227 | 232 | ||
228 | rset, err := stmt.Qry() | 233 | rset, err := stmt.Qry() |
229 | if err != nil { | 234 | if err != nil { |
230 | return resp, err | 235 | return resp, err |
231 | } | 236 | } |
232 | for rset.Next() { | 237 | for rset.Next() { |
233 | resp = append(resp, ListNavNode{ | 238 | resp = append(resp, ListNavNode{ |
234 | ObjectType: rset.Row[0].(string), | 239 | ObjectType: rset.Row[0].(string), |
235 | ParentObjectType: rset.Row[1].(string), | 240 | ParentObjectType: rset.Row[1].(string), |
236 | LabelField: rset.Row[2].(string), | 241 | LabelField: rset.Row[2].(string), |
237 | Icon: rset.Row[3].(string), | 242 | Icon: rset.Row[3].(string), |
238 | ParentIdField: rset.Row[4].(string), | 243 | ParentIDField: rset.Row[4].(string), |
239 | }) | 244 | }) |
240 | } | 245 | } |
241 | if rset.Err != nil { | 246 | if rset.Err != nil { |
242 | return nil, rset.Err | 247 | return nil, rset.Err |
243 | } | 248 | } |
244 | 249 | ||
245 | return resp, nil | 250 | return resp, nil |
246 | } | 251 | } |
247 | 252 | ||
253 | // getListActions returns list actions for the given objectType. | ||
248 | func getListActions(db *ora.Ses, objType string) (ListActions, error) { | 254 | func getListActions(db *ora.Ses, objType string) (ListActions, error) { |
249 | var resp ListActions | 255 | var resp ListActions |
250 | var err error | 256 | var err error |
251 | var stmt *ora.Stmt | 257 | var stmt *ora.Stmt |
252 | query := `SELECT | 258 | query := `SELECT |
253 | ACTION_CREATE, ACTION_UPDATE, ACTION_DELETE, ACTION_EXPORT, | 259 | ACTION_CREATE, ACTION_UPDATE, ACTION_DELETE, ACTION_EXPORT, |
254 | ACTION_PRINT, ACTION_GRAPH | 260 | ACTION_PRINT, ACTION_GRAPH |
255 | FROM LIST_CONFIG | 261 | FROM LIST_CONFIG |
256 | WHERE OBJECT_TYPE = '` + objType + `'` | 262 | WHERE OBJECT_TYPE = '` + objType + `'` |
257 | 263 | ||
258 | stmt, err = db.Prep(query, ora.U32, ora.U32, ora.U32, ora.U32, | 264 | stmt, err = db.Prep(query, ora.U32, ora.U32, ora.U32, ora.U32, |
259 | ora.U32, ora.U32) | 265 | ora.U32, ora.U32) |
260 | if err != nil { | 266 | if err != nil { |
261 | return ListActions{}, err | 267 | return ListActions{}, err |
262 | } | 268 | } |
263 | defer stmt.Close() | 269 | defer stmt.Close() |
264 | 270 | ||
265 | rset, err := stmt.Qry() | 271 | rset, err := stmt.Qry() |
266 | if err != nil { | 272 | if err != nil { |
267 | return ListActions{}, err | 273 | return ListActions{}, err |
268 | } | 274 | } |
269 | if rset.Next() { | 275 | if rset.Next() { |
270 | resp.Create = rset.Row[0].(uint32) != 0 | 276 | resp.Create = rset.Row[0].(uint32) != 0 |
271 | resp.Update = rset.Row[1].(uint32) != 0 | 277 | resp.Update = rset.Row[1].(uint32) != 0 |
272 | resp.Delete = rset.Row[2].(uint32) != 0 | 278 | resp.Delete = rset.Row[2].(uint32) != 0 |
273 | resp.Export = rset.Row[3].(uint32) != 0 | 279 | resp.Export = rset.Row[3].(uint32) != 0 |
274 | resp.Print = rset.Row[4].(uint32) != 0 | 280 | resp.Print = rset.Row[4].(uint32) != 0 |
275 | resp.Graph = rset.Row[5].(uint32) != 0 | 281 | resp.Graph = rset.Row[5].(uint32) != 0 |
276 | } | 282 | } |
277 | if rset.Err != nil { | 283 | if rset.Err != nil { |
278 | return ListActions{}, rset.Err | 284 | return ListActions{}, rset.Err |
279 | } | 285 | } |
280 | return resp, nil | 286 | return resp, nil |
281 | } | 287 | } |
282 | 288 | ||
289 | // getListFiters returns list filter slice for the given object type. | ||
283 | func getListFilters(db *ora.Ses, objType string) ([]ListFilter, error) { | 290 | func getListFilters(db *ora.Ses, objType string) ([]ListFilter, error) { |
284 | resp := make([]ListFilter, 0) | 291 | resp := make([]ListFilter, 0) |
285 | filtersFields, err := getFilterFieldsAndPosition(db, objType) | 292 | filtersFields, err := getFilterFieldsAndPosition(db, objType) |
286 | if err != nil { | 293 | if err != nil { |
287 | return nil, err | 294 | return nil, err |
288 | } | 295 | } |
289 | for field, pos := range filtersFields { | 296 | for field, pos := range filtersFields { |
290 | filters, _ := getFiltersByFilterField(db, field) | 297 | filters, _ := getFiltersByFilterField(db, field) |
291 | for _, filter := range filters { | 298 | for _, filter := range filters { |
292 | var f ListFilter | 299 | var f ListFilter |
293 | f.Position = pos | 300 | f.Position = pos |
294 | f.ObjectType = objType | 301 | f.ObjectType = objType |
295 | f.FiltersField = field | 302 | f.FiltersField = field |
296 | f.DefaultValues = filter.DefaultValues | 303 | f.DefaultValues = filter.DefaultValues |
297 | f.FiltersLabel = filter.Label | 304 | f.FiltersLabel = filter.Label |
298 | f.FiltersType = filter.Type | 305 | f.FiltersType = filter.Type |
299 | if filter.Type == "dropdown" { | 306 | if filter.Type == "dropdown" { |
300 | f.DropdownConfig, err = getFilterDropdownConfig(db, field) | 307 | f.DropdownConfig, err = getFilterDropdownConfig(db, field) |
301 | if err != nil { | 308 | if err != nil { |
302 | return nil, err | 309 | return nil, err |
303 | } | 310 | } |
304 | } | 311 | } |
305 | resp = append(resp, f) | 312 | resp = append(resp, f) |
306 | } | 313 | } |
307 | } | 314 | } |
308 | 315 | ||
309 | sortFilters(resp) | 316 | sortFilters(resp) |
310 | 317 | ||
311 | return resp, nil | 318 | return resp, nil |
312 | } | 319 | } |
313 | 320 | ||
321 | // getFilterFieldsAndPosition returns a map of filter fields and their respective position in the menu. | ||
314 | func getFilterFieldsAndPosition(db *ora.Ses, objType string) (map[string]uint32, error) { | 322 | func getFilterFieldsAndPosition(db *ora.Ses, objType string) (map[string]uint32, error) { |
315 | filtersField := make(map[string]uint32, 0) | 323 | filtersField := make(map[string]uint32, 0) |
316 | var err error | 324 | var err error |
317 | var stmt *ora.Stmt | 325 | var stmt *ora.Stmt |
318 | query := `SELECT | 326 | query := `SELECT |
319 | FILTERS_FIELD, RB | 327 | FILTERS_FIELD, RB |
320 | FROM LIST_CONFIG_FILTERS | 328 | FROM LIST_CONFIG_FILTERS |
321 | WHERE OBJECT_TYPE = '` + objType + `'` | 329 | WHERE OBJECT_TYPE = '` + objType + `'` |
322 | 330 | ||
323 | stmt, err = db.Prep(query, ora.S, ora.U32) | 331 | stmt, err = db.Prep(query, ora.S, ora.U32) |
324 | if err != nil { | 332 | if err != nil { |
325 | return nil, err | 333 | return nil, err |
326 | } | 334 | } |
327 | defer stmt.Close() | 335 | defer stmt.Close() |
328 | 336 | ||
329 | rset, err := stmt.Qry() | 337 | rset, err := stmt.Qry() |
330 | if err != nil { | 338 | if err != nil { |
331 | return nil, err | 339 | return nil, err |
332 | } | 340 | } |
333 | for rset.Next() { | 341 | for rset.Next() { |
334 | filtersField[rset.Row[0].(string)] = rset.Row[1].(uint32) | 342 | filtersField[rset.Row[0].(string)] = rset.Row[1].(uint32) |
335 | } | 343 | } |
336 | if rset.Err != nil { | 344 | if rset.Err != nil { |
337 | return nil, rset.Err | 345 | return nil, rset.Err |
338 | } | 346 | } |
339 | return filtersField, nil | 347 | return filtersField, nil |
340 | } | 348 | } |
341 | 349 | ||
342 | type _filter struct { | 350 | type _filter struct { |
343 | DefaultValues string | 351 | DefaultValues string |
344 | Label string | 352 | Label string |
345 | Type string | 353 | Type string |
346 | } | 354 | } |
347 | 355 | ||
356 | // getFiltersByFilterField returns filter slice for the given filter field. | ||
348 | func getFiltersByFilterField(db *ora.Ses, filtersField string) ([]_filter, error) { | 357 | func getFiltersByFilterField(db *ora.Ses, filtersField string) ([]_filter, error) { |
349 | resp := make([]_filter, 0) | 358 | resp := make([]_filter, 0) |
350 | var err error | 359 | var err error |
351 | var stmt *ora.Stmt | 360 | var stmt *ora.Stmt |
352 | query := `SELECT | 361 | query := `SELECT |
353 | FILTERS_TYPE, FILTERS_LABEL, DEFAULT_VALUES | 362 | FILTERS_TYPE, FILTERS_LABEL, DEFAULT_VALUES |
354 | FROM LIST_FILTERS_FIELD | 363 | FROM LIST_FILTERS_FIELD |
355 | WHERE FILTERS_FIELD = '` + filtersField + `'` | 364 | WHERE FILTERS_FIELD = '` + filtersField + `'` |
356 | 365 | ||
357 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S) | 366 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S) |
358 | if err != nil { | 367 | if err != nil { |
359 | return resp, err | 368 | return resp, err |
360 | } | 369 | } |
361 | defer stmt.Close() | 370 | defer stmt.Close() |
362 | 371 | ||
363 | rset, err := stmt.Qry() | 372 | rset, err := stmt.Qry() |
364 | if err != nil { | 373 | if err != nil { |
365 | return resp, err | 374 | return resp, err |
366 | } | 375 | } |
367 | for rset.Next() { | 376 | for rset.Next() { |
368 | resp = append(resp, _filter{ | 377 | resp = append(resp, _filter{ |
369 | Type: rset.Row[0].(string), | 378 | Type: rset.Row[0].(string), |
370 | Label: rset.Row[1].(string), | 379 | Label: rset.Row[1].(string), |
371 | DefaultValues: rset.Row[2].(string), | 380 | DefaultValues: rset.Row[2].(string), |
372 | }) | 381 | }) |
373 | } | 382 | } |
374 | if rset.Err != nil { | 383 | if rset.Err != nil { |
375 | return resp, rset.Err | 384 | return resp, rset.Err |
376 | } | 385 | } |
377 | return resp, nil | 386 | return resp, nil |
378 | } | 387 | } |
379 | 388 | ||
389 | // getFilterDropdownConfig returns dropdown menu for the given filter field. | ||
380 | func getFilterDropdownConfig(db *ora.Ses, filtersField string) (Dropdown, error) { | 390 | func getFilterDropdownConfig(db *ora.Ses, filtersField string) (Dropdown, error) { |
381 | var resp Dropdown | 391 | var resp Dropdown |
382 | var err error | 392 | var err error |
383 | var stmt *ora.Stmt | 393 | var stmt *ora.Stmt |
384 | query := `SELECT | 394 | query := `SELECT |
385 | FILTERS_FIELD, OBJECT_TYPE, ID_FIELD, LABEL_FIELD | 395 | FILTERS_FIELD, OBJECT_TYPE, ID_FIELD, LABEL_FIELD |
386 | FROM LIST_DROPDOWN_FILTER | 396 | FROM LIST_DROPDOWN_FILTER |
387 | WHERE FILTERS_FIELD = '` + filtersField + `'` | 397 | WHERE FILTERS_FIELD = '` + filtersField + `'` |
388 | 398 | ||
389 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S) | 399 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S) |
390 | if err != nil { | 400 | if err != nil { |
391 | return resp, err | 401 | return resp, err |
392 | } | 402 | } |
393 | defer stmt.Close() | 403 | defer stmt.Close() |
394 | 404 | ||
395 | rset, err := stmt.Qry() | 405 | rset, err := stmt.Qry() |
396 | if err != nil { | 406 | if err != nil { |
397 | return resp, err | 407 | return resp, err |
398 | } | 408 | } |
399 | if rset.Next() { | 409 | if rset.Next() { |
400 | resp.FiltersField = rset.Row[0].(string) | 410 | resp.FiltersField = rset.Row[0].(string) |
401 | resp.ObjectType = rset.Row[1].(string) | 411 | resp.ObjectType = rset.Row[1].(string) |
402 | resp.IdField = rset.Row[2].(string) | 412 | resp.IDField = rset.Row[2].(string) |
403 | resp.LabelField = rset.Row[3].(string) | 413 | resp.LabelField = rset.Row[3].(string) |
404 | } | 414 | } |
405 | if rset.Err != nil { | 415 | if rset.Err != nil { |
406 | return resp, rset.Err | 416 | return resp, rset.Err |
407 | } | 417 | } |
408 | return resp, nil | 418 | return resp, nil |
409 | } | 419 | } |
410 | 420 | ||
421 | // sortFilters bubble sorts provided filters slice by position field. | ||
411 | func sortFilters(filters []ListFilter) { | 422 | func sortFilters(filters []ListFilter) { |
412 | done := false | 423 | done := false |
413 | var temp ListFilter | 424 | var temp ListFilter |
414 | for !done { | 425 | for !done { |
415 | done = true | 426 | done = true |
416 | for i := 0; i < len(filters) - 1; i++ { | 427 | for i := 0; i < len(filters) - 1; i++ { |
417 | if filters[i].Position > filters[i+1].Position { | 428 | if filters[i].Position > filters[i+1].Position { |
418 | done = false | 429 | done = false |
419 | temp = filters[i] | 430 | temp = filters[i] |
420 | filters[i] = filters[i+1] | 431 | filters[i] = filters[i+1] |
421 | filters[i+1] = temp | 432 | filters[i+1] = temp |
422 | } | 433 | } |
423 | } | 434 | } |
424 | } | 435 | } |
425 | } | 436 | } |
426 | 437 | ||
438 | // getListGraph return list graph slice for the given object type. | ||
427 | func getListGraph(db *ora.Ses, objType string) ([]ListGraph, error) { | 439 | func getListGraph(db *ora.Ses, objType string) ([]ListGraph, error) { |
428 | resp := make([]ListGraph, 0) | 440 | resp := make([]ListGraph, 0) |
429 | var err error | 441 | var err error |
430 | var stmt *ora.Stmt | 442 | var stmt *ora.Stmt |
431 | query := `SELECT | 443 | query := `SELECT |
432 | OBJECT_TYPE, X_FIELD, Y_FIELD, GROUP_FIELD, LABEL | 444 | OBJECT_TYPE, X_FIELD, Y_FIELD, GROUP_FIELD, LABEL |
433 | FROM LIST_GRAPHS | 445 | FROM LIST_GRAPHS |
434 | WHERE OBJECT_TYPE = '` + objType + `'` | 446 | WHERE OBJECT_TYPE = '` + objType + `'` |
435 | 447 | ||
436 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S) | 448 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S) |
437 | if err != nil { | 449 | if err != nil { |
438 | return resp, err | 450 | return resp, err |
439 | } | 451 | } |
440 | defer stmt.Close() | 452 | defer stmt.Close() |
441 | 453 | ||
442 | rset, err := stmt.Qry() | 454 | rset, err := stmt.Qry() |
443 | if err != nil { | 455 | if err != nil { |
444 | return resp, err | 456 | return resp, err |
445 | } | 457 | } |
446 | for rset.Next() { | 458 | for rset.Next() { |
447 | resp = append(resp, ListGraph{ | 459 | resp = append(resp, ListGraph{ |
448 | ObjectType: rset.Row[0].(string), | 460 | ObjectType: rset.Row[0].(string), |
449 | X: rset.Row[1].(string), | 461 | X: rset.Row[1].(string), |
450 | Y: rset.Row[2].(string), | 462 | Y: rset.Row[2].(string), |
451 | GroupField: rset.Row[3].(string), | 463 | GroupField: rset.Row[3].(string), |
452 | Label: rset.Row[4].(string), | 464 | Label: rset.Row[4].(string), |
453 | }) | 465 | }) |
454 | } | 466 | } |
455 | if rset.Err != nil { | 467 | if rset.Err != nil { |
456 | return resp, rset.Err | 468 | return resp, rset.Err |
457 | } | 469 | } |
458 | return resp, nil | 470 | return resp, nil |
459 | } | 471 | } |
460 | 472 | ||
473 | // getListOptions returns list options for the given object type. | ||
461 | func getListOptions(db *ora.Ses, objType string) (ListOptions, error) { | 474 | func getListOptions(db *ora.Ses, objType string) (ListOptions, error) { |
462 | var resp ListOptions | 475 | var resp ListOptions |
463 | var err error | 476 | var err error |
464 | var stmt *ora.Stmt | 477 | var stmt *ora.Stmt |
465 | query := `SELECT | 478 | query := `SELECT |
466 | GLOBAL_FILTER, LOCAL_FILTER, REMOTE_FILTER, PAGINATION, | 479 | GLOBAL_FILTER, LOCAL_FILTER, REMOTE_FILTER, PAGINATION, |
467 | PAGE_SIZE, PIVOT, DETAIL, TOTAL | 480 | PAGE_SIZE, PIVOT, DETAIL, TOTAL |
468 | FROM LIST_CONFIG | 481 | FROM LIST_CONFIG |
469 | WHERE OBJECT_TYPE = '` + objType + `'` | 482 | WHERE OBJECT_TYPE = '` + objType + `'` |
470 | 483 | ||
471 | stmt, err = db.Prep(query, ora.U32, ora.U32, ora.U32, ora.U32, | 484 | stmt, err = db.Prep(query, ora.U32, ora.U32, ora.U32, ora.U32, |
472 | ora.U64, ora.U64, ora.U32, ora.U32) | 485 | ora.U64, ora.U64, ora.U32, ora.U32) |
473 | if err != nil { | 486 | if err != nil { |
474 | return ListOptions{}, err | 487 | return ListOptions{}, err |
475 | } | 488 | } |
476 | defer stmt.Close() | 489 | defer stmt.Close() |
477 | 490 | ||
478 | rset, err := stmt.Qry() | 491 | rset, err := stmt.Qry() |
479 | if err != nil { | 492 | if err != nil { |
480 | return ListOptions{}, err | 493 | return ListOptions{}, err |
481 | } | 494 | } |
482 | if rset.Next() { | 495 | if rset.Next() { |
483 | resp.GlobalFilter = rset.Row[0].(uint32) != 0 | 496 | resp.GlobalFilter = rset.Row[0].(uint32) != 0 |
484 | resp.LocalFilters = rset.Row[1].(uint32) != 0 | 497 | resp.LocalFilters = rset.Row[1].(uint32) != 0 |
485 | resp.RemoteFilters = rset.Row[2].(uint32) != 0 | 498 | resp.RemoteFilters = rset.Row[2].(uint32) != 0 |
486 | resp.Pagination = rset.Row[3].(uint32) != 0 | 499 | resp.Pagination = rset.Row[3].(uint32) != 0 |
487 | resp.PageSize = rset.Row[4].(uint64) | 500 | resp.PageSize = rset.Row[4].(uint64) |
488 | resp.Pivot = rset.Row[5].(uint64) != 0 | 501 | resp.Pivot = rset.Row[5].(uint64) != 0 |
489 | resp.Detail = rset.Row[6].(uint32) != 0 | 502 | resp.Detail = rset.Row[6].(uint32) != 0 |
490 | resp.Total = rset.Row[7].(uint32) != 0 | 503 | resp.Total = rset.Row[7].(uint32) != 0 |
491 | } | 504 | } |
492 | if rset.Err != nil { | 505 | if rset.Err != nil { |
493 | return ListOptions{}, rset.Err | 506 | return ListOptions{}, rset.Err |
494 | } | 507 | } |
495 | return resp, nil | 508 | return resp, nil |
496 | } | 509 | } |
497 | 510 | ||
511 | // getListParent returns list parent node slice for the given object type. | ||
498 | func getListParent(db *ora.Ses, objType string) ([]ListParentNode, error) { | 512 | func getListParent(db *ora.Ses, objType string) ([]ListParentNode, error) { |
499 | resp := make([]ListParentNode, 0) | 513 | resp := make([]ListParentNode, 0) |
500 | var err error | 514 | var err error |
501 | var stmt *ora.Stmt | 515 | var stmt *ora.Stmt |
502 | query := `SELECT | 516 | query := `SELECT |
503 | PARENT_OBJECT_TYPE, PARENT_LABEL_FIELD, PARENT_FILTER_FIELD | 517 | PARENT_OBJECT_TYPE, PARENT_LABEL_FIELD, PARENT_FILTER_FIELD |
504 | FROM LIST_CONFIG_CHILD | 518 | FROM LIST_CONFIG_CHILD |
505 | WHERE OBJECT_TYPE = '` + objType + `'` | 519 | WHERE OBJECT_TYPE = '` + objType + `'` |
506 | 520 | ||
507 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S) | 521 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S) |
508 | if err != nil { | 522 | if err != nil { |
509 | return resp, err | 523 | return resp, err |
510 | } | 524 | } |
511 | defer stmt.Close() | 525 | defer stmt.Close() |
512 | 526 | ||
513 | rset, err := stmt.Qry() | 527 | rset, err := stmt.Qry() |
514 | if err != nil { | 528 | if err != nil { |
515 | return resp, err | 529 | return resp, err |
516 | } | 530 | } |
517 | for rset.Next() { | 531 | for rset.Next() { |
518 | resp = append(resp, ListParentNode{ | 532 | resp = append(resp, ListParentNode{ |
519 | ObjectType: rset.Row[0].(string), | 533 | ObjectType: rset.Row[0].(string), |
520 | LabelField: rset.Row[1].(string), | 534 | LabelField: rset.Row[1].(string), |
521 | FilterField: rset.Row[2].(string), | 535 | FilterField: rset.Row[2].(string), |
522 | }) | 536 | }) |
523 | } | 537 | } |
524 | if rset.Err != nil { | 538 | if rset.Err != nil { |
525 | return nil, rset.Err | 539 | return nil, rset.Err |
526 | } | 540 | } |
527 | 541 | ||
528 | return resp, nil | 542 | return resp, nil |
529 | } | 543 | } |
530 | 544 | ||
545 | // getListPivot list pivot slice for the given object type. | ||
531 | func getListPivot(db *ora.Ses, objType string) ([]ListPivot, error) { | 546 | func getListPivot(db *ora.Ses, objType string) ([]ListPivot, error) { |
532 | resp := make([]ListPivot, 0) | 547 | resp := make([]ListPivot, 0) |
533 | var err error | 548 | var err error |
534 | var stmt *ora.Stmt | 549 | var stmt *ora.Stmt |
535 | query := `SELECT | 550 | query := `SELECT |
536 | OBJECT_TYPE, GROUP_FIELD, DISTINCT_FIELD, VALUE_FIELD | 551 | OBJECT_TYPE, GROUP_FIELD, DISTINCT_FIELD, VALUE_FIELD |
537 | FROM LIST_PIVOTS | 552 | FROM LIST_PIVOTS |
538 | WHERE OBJECT_TYPE = '` + objType + `'` | 553 | WHERE OBJECT_TYPE = '` + objType + `'` |
539 | 554 | ||
540 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S) | 555 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S) |
541 | if err != nil { | 556 | if err != nil { |
542 | return resp, err | 557 | return resp, err |
543 | } | 558 | } |
544 | defer stmt.Close() | 559 | defer stmt.Close() |
545 | 560 | ||
546 | rset, err := stmt.Qry() | 561 | rset, err := stmt.Qry() |
547 | if err != nil { | 562 | if err != nil { |
548 | return resp, err | 563 | return resp, err |
549 | } | 564 | } |
550 | for rset.Next() { | 565 | for rset.Next() { |
551 | resp = append(resp, ListPivot{ | 566 | resp = append(resp, ListPivot{ |
552 | ObjectType: rset.Row[0].(string), | 567 | ObjectType: rset.Row[0].(string), |
553 | GroupField: rset.Row[1].(string), | 568 | GroupField: rset.Row[1].(string), |
554 | DistinctField: rset.Row[2].(string), | 569 | DistinctField: rset.Row[2].(string), |
555 | Value: rset.Row[3].(string), | 570 | Value: rset.Row[3].(string), |
556 | }) | 571 | }) |
557 | } | 572 | } |
558 | if rset.Err != nil { | 573 | if rset.Err != nil { |
559 | return nil, rset.Err | 574 | return nil, rset.Err |
560 | } | 575 | } |
561 | 576 | ||
562 | return resp, nil | 577 | return resp, nil |
563 | } | 578 | } |
564 | 579 | ||
580 | // getListDetails returns list details for the given object type. | ||
565 | func getListDetails(db *ora.Ses, objType string) (ListDetails, error) { | 581 | func getListDetails(db *ora.Ses, objType string) (ListDetails, error) { |
566 | var resp ListDetails | 582 | var resp ListDetails |
567 | var err error | 583 | var err error |
568 | var stmt *ora.Stmt | 584 | var stmt *ora.Stmt |
569 | query := `SELECT | 585 | query := `SELECT |
570 | OBJECT_TYPE, PARENT_OBJECT_TYPE, PARENT_FILTER_FIELD, SINGLE_DETAIL | 586 | OBJECT_TYPE, PARENT_OBJECT_TYPE, PARENT_FILTER_FIELD, SINGLE_DETAIL |
571 | FROM LIST_CONFIG_DETAIL | 587 | FROM LIST_CONFIG_DETAIL |
572 | WHERE PARENT_OBJECT_TYPE = '` + objType + `'` | 588 | WHERE PARENT_OBJECT_TYPE = '` + objType + `'` |
573 | 589 | ||
574 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.U32) | 590 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.U32) |
575 | if err != nil { | 591 | if err != nil { |
576 | return resp, err | 592 | return resp, err |
577 | } | 593 | } |
578 | defer stmt.Close() | 594 | defer stmt.Close() |
579 | 595 | ||
580 | rset, err := stmt.Qry() | 596 | rset, err := stmt.Qry() |
581 | if err != nil { | 597 | if err != nil { |
582 | return resp, err | 598 | return resp, err |
583 | } | 599 | } |
584 | if rset.Next() { | 600 | if rset.Next() { |
585 | resp.ObjectType = rset.Row[0].(string) | 601 | resp.ObjectType = rset.Row[0].(string) |
586 | resp.ParentObjectType = rset.Row[1].(string) | 602 | resp.ParentObjectType = rset.Row[1].(string) |
587 | resp.ParentFilterField = rset.Row[2].(string) | 603 | resp.ParentFilterField = rset.Row[2].(string) |
588 | resp.SingleDetail = rset.Row[3].(uint32) != 0 | 604 | resp.SingleDetail = rset.Row[3].(uint32) != 0 |
589 | } | 605 | } |
590 | if rset.Err != nil { | 606 | if rset.Err != nil { |
591 | return resp, rset.Err | 607 | return resp, rset.Err |
592 | } | 608 | } |
593 | 609 | ||
594 | return resp, nil | 610 | return resp, nil |
595 | } | 611 | } |
596 | 612 |
select_config.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "gopkg.in/rana/ora.v3" | 4 | "gopkg.in/rana/ora.v3" |
5 | ) | 5 | ) |
6 | 6 | ||
7 | type SelectConfig struct { | 7 | type SelectConfig struct { |
8 | ListObjType string `json:"listObjectType"` | 8 | ListObjType string `json:"listObjectType"` |
9 | ObjType string `json:"objectType"` | 9 | ObjType string `json:"objectType"` |
10 | Type string `json:"type"` | 10 | Type string `json:"type"` |
11 | IdField string `json:"idField"` | 11 | IdField string `json:"idField"` |
12 | LabelField string `json:"labelField"` | 12 | LabelField string `json:"labelField"` |
13 | ValueField string `json:"valueField"` | 13 | ValueField string `json:"valueField"` |
14 | } | 14 | } |
15 | 15 | ||
16 | // GetSelectConfig returns select configuration slice for the given object type. | ||
16 | func GetSelectConfig(db *ora.Ses, otype string) ([]SelectConfig, error) { | 17 | func GetSelectConfig(db *ora.Ses, otype string) ([]SelectConfig, error) { |
17 | resp := make([]SelectConfig, 0) | 18 | resp := make([]SelectConfig, 0) |
18 | var err error | 19 | var err error |
19 | var stmt *ora.Stmt | 20 | var stmt *ora.Stmt |
20 | query := `SELECT a.LIST_OBJECT_TYPE, a.OBJECT_TYPE, a.ID_FIELD, | 21 | query := `SELECT a.LIST_OBJECT_TYPE, a.OBJECT_TYPE, a.ID_FIELD, |
21 | a.LABEL_FIELD, a.TYPE, b.FIELD | 22 | a.LABEL_FIELD, a.TYPE, b.FIELD |
22 | FROM LIST_SELECT_CONFIG a, LIST_VALUE_FIELD b | 23 | FROM LIST_SELECT_CONFIG a, LIST_VALUE_FIELD b |
23 | WHERE a.LIST_OBJECT_TYPE` + otype + ` | 24 | WHERE a.LIST_OBJECT_TYPE` + otype + ` |
24 | AND b.LIST_TYPE = a.LIST_OBJECT_TYPE | 25 | AND b.LIST_TYPE = a.LIST_OBJECT_TYPE |
25 | AND b.OBJECT_TYPE = a.OBJECT_TYPE` | 26 | AND b.OBJECT_TYPE = a.OBJECT_TYPE` |
26 | 27 | ||
27 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S, ora.S, ora.S) | 28 | stmt, err = db.Prep(query, ora.S, ora.S, ora.S, ora.S, ora.S, ora.S) |
28 | defer stmt.Close() | 29 | defer stmt.Close() |
29 | if err != nil { | 30 | if err != nil { |
30 | return nil, err | 31 | return nil, err |
31 | } | 32 | } |
32 | 33 | ||
33 | rset, err := stmt.Qry() | 34 | rset, err := stmt.Qry() |
34 | if err != nil { | 35 | if err != nil { |
35 | return nil, err | 36 | return nil, err |
36 | } | 37 | } |
37 | for rset.Next() { | 38 | for rset.Next() { |
38 | resp = append(resp, SelectConfig{ | 39 | resp = append(resp, SelectConfig{ |
39 | ListObjType: rset.Row[0].(string), | 40 | ListObjType: rset.Row[0].(string), |
40 | ObjType: rset.Row[1].(string), | 41 | ObjType: rset.Row[1].(string), |
41 | IdField: rset.Row[2].(string), | 42 | IdField: rset.Row[2].(string), |
42 | LabelField: rset.Row[3].(string), | 43 | LabelField: rset.Row[3].(string), |
43 | Type: rset.Row[4].(string), | 44 | Type: rset.Row[4].(string), |
44 | ValueField: rset.Row[5].(string), | 45 | ValueField: rset.Row[5].(string), |
45 | }) | 46 | }) |
46 | } | 47 | } |
47 | if rset.Err != nil { | 48 | if rset.Err != nil { |
48 | return nil, rset.Err | 49 | return nil, rset.Err |
49 | } | 50 | } |
50 | 51 | ||
51 | return resp, nil | 52 | return resp, nil |
52 | } | 53 | } |
53 | 54 |
sql_sequrity.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "strings" | 4 | "strings" |
5 | ) | 5 | ) |
6 | 6 | ||
7 | var patern string = "\"';&*<>=\\`:" | 7 | var patern string = "\"';&*<>=\\`:" |
8 | 8 | ||
9 | func SQLSafeString(s string) string { | 9 | // SQLSafeString removes characters from s found in patern and returns new modified string. |
10 | func SQLSafeString(s string) (safe string) { | ||
10 | for _, c := range patern { | 11 | for _, c := range patern { |
11 | s = strings.Replace(s, string(c), "", -1) | 12 | safe = strings.Replace(s, string(c), "", -1) |
12 | } | 13 | } |
13 | return s | 14 | return safe |
14 | } | 15 | } |
15 | 16 |