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