Commit e1fbb41f916dcaa9b5440a16cb5a0268a43ca18c

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

added comments

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
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
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.
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
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
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