diff --git a/http_utility.go b/http_utility.go new file mode 100644 index 0000000..c531a7c --- /dev/null +++ b/http_utility.go @@ -0,0 +1,161 @@ +package restutility + +import ( + "net/http" + "encoding/json" +) + +//// +//// ERROR UTILITY +//// + +const templateHttpErr500_EN = "An internal server error has occurred." +const templateHttpErr500_RS = "Došlo je do greške na serveru." +const templateHttpErr400_EN = "Bad request: invalid request body." +const templateHttpErr400_RS = "Neispravan zahtev." + +type HttpError struct { + Error []HttpErrorDesc `json:"error"` + Request string `json:"request"` +} + +type HttpErrorDesc struct { + Lang string `json:"lang"` + Desc string `json:"description"` +} + +func respondWithHttpError(w http.ResponseWriter, + req *http.Request, + code int, + httpErr []HttpErrorDesc) { + + err := HttpError{ + Error: httpErr, + Request: req.Method + " " + req.URL.Path, + } + w.WriteHeader(code) + json.NewEncoder(w).Encode(err) +} + +func respondWithHttpError400(w http.ResponseWriter, req *http.Request) { + respondWithHttpError(w, req, http.StatusBadRequest, + []HttpErrorDesc{ + { + Lang: "en", + Desc: templateHttpErr400_EN, + }, + { + Lang: "rs", + Desc: templateHttpErr400_RS, + }, + }) +} + +func respondWithHttpError500(w http.ResponseWriter, req *http.Request) { + respondWithHttpError(w, req, http.StatusInternalServerError, + []HttpErrorDesc{ + { + Lang: "en", + Desc: templateHttpErr500_EN, + }, + { + Lang: "rs", + Desc: templateHttpErr500_RS, + }, + }) +} + +func deliverPayload(w http.ResponseWriter, payload JSONPayload) { + json.NewEncoder(w).Encode(payload) + payload.Data = nil +} + +//// +//// HANDLER FUNC WRAPPER +//// + +// wrapHandlerFunc is as wrapper function for route handlers. +// Sets common headers and checks for token validity. +func commonHttpWrap(fn http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { +// @TODO: check Content-type header (must be application/json) +// ctype := w.Header.Get("Content-Type") +// if req.Method != "GET" && ctype != "application/json" { +// replyWithHttpError(w, req, http.StatusBadRequest, +// "Not a supported content type: " + ctype) +// } + + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", + `POST, + GET, + PUT, + DELETE, + OPTIONS`) + w.Header().Set("Access-Control-Allow-Headers", + `Accept, + Content-Type, + Content-Length, + Accept-Encoding, + X-CSRF-Token, + Authorization`) + w.Header().Set("Content-Type", "application/json; charset=utf-8") + + if req.Method == "OPTIONS" { + return + } + + if req.URL.Path != APIVersion + "/token/new" { + token := req.Header.Get("Authorization") + if _, err := parseAPIToken(token); err != nil { + respondWithHttpError(w, req, http.StatusUnauthorized, + []HttpErrorDesc{ + { + Lang: "en", + Desc: "Unauthorized request.", + }, + { + Lang: "rs", + Desc: "Neautorizovani zahtev.", + }, + }) + return + } + } + + err := req.ParseForm() + if err != nil { + respondWithHttpError(w, req, http.StatusBadRequest, + []HttpErrorDesc{ + { + Lang: "en", + Desc: templateHttpErr400_EN, + }, + { + Lang: "rs", + Desc: templateHttpErr400_RS, + }, + }) + return + } + fn(w, req) + } +} + +//// +//// NOT FOUND HANDLER +//// + +func notFoundHandler(w http.ResponseWriter, req *http.Request) { + respondWithHttpError(w, req, http.StatusNotFound, + []HttpErrorDesc{ + { + Lang: "en", + Desc: "Not found.", + }, + { + Lang: "rs", + Desc: "Traženi resurs ne postoji.", + }, + }) +} diff --git a/json_utility.go b/json_utility.go new file mode 100644 index 0000000..ebf008c --- /dev/null +++ b/json_utility.go @@ -0,0 +1,70 @@ +package restutility + +import ( + "net/http" + "strings" +) + +type LangMap map[string]map[string]string + +type Field struct { + Parameter string `json:"param"` + Type string `json:"type"` + Visible bool `json:"visible"` + Editable bool `json:"editable"` +} + +type JSONParams struct { + Lang LangMap + Fields []Field + IdField string + Correlations []CorrelationField `json:"correlation_fields"` +} + +type JSONPayload struct { + Method string `json:"method"` + Params map[string]string `json:"params"` + Lang LangMap `json:"lang"` + Fields []Field `json:"fields"` + Correlations []CorrelationField `json:"correlation_fields"` + IdField string `json:"idField"` + // Data can only hold slices of any type. It can't be used for itteration + Data interface{} `json:"data"` +} + +func NewJSONParams(lang LangMap, + fields []Field, + id string, + correlations []CorrelationField) JSONParams { + + var jp JSONParams + + jp.Lang = lang + jp.Fields = fields + jp.IdField = id + jp.Correlations = correlations + + return jp +} + +func NewJSONPayload(r *http.Request, params JSONParams) JSONPayload { + var obj JSONPayload + obj.Method = strings.ToLower(r.Method + " " + r.URL.Path) + obj.Params = make(map[string]string, 0) + obj.Lang = make(map[string]map[string]string, 0) + obj.Fields = make([]Field, 0) + obj.IdField = params.IdField + obj.Correlations = params.Correlations + + for k, m := range params.Lang { + obj.Lang[k] = m + } + for _, f := range params.Fields { + obj.Fields = append(obj.Fields, f) + } + + return obj +} + +func decodeRequestBody(req *http.Request, model interface{}) {} + diff --git a/tables_utility.go b/tables_utility.go new file mode 100644 index 0000000..30e7728 --- /dev/null +++ b/tables_utility.go @@ -0,0 +1,144 @@ +package restutility + +import ( + "encoding/json" + "io" + "io/ioutil" + "errors" + "fmt" + "gopkg.in/rana/ora.v3" +) + +type TableConfig struct { + Tables []Table +} + +type Table struct { + TableType string `json:"tableType"` + Translations []TableTranslation `json:"translations"` + TableFields []Field `json:"tableFields"` + Correlations []CorrelationField `json:"correlation_fields"` + IdField string `json:"idField"` +} + +type CorrelationField struct { + Result string `json:"result"` + Elements []string `json:"elements"` + Type string `json:"type"` +} + +type TableTranslation struct { + Language string `json:"language"` + FieldsLabels map[string]string `json:"fieldsLabels"` +} + +func (tl TableConfig) LoadTranslations(tableType string) LangMap { + translations := make(LangMap, 0) + + for _, table := range tl.Tables { + if tableType == table.TableType { + for _, t := range table.Translations { + translations[t.Language] = t.FieldsLabels + } + } + } + + return translations +} + +func (tl TableConfig) LoadFields(tableType string) []Field { + fields := make([]Field, 0) + + for _, table := range tl.Tables { + if tableType == table.TableType { + for _, f := range table.TableFields { + fields = append(fields, f) + } + } + } + + return fields +} + +func (tl TableConfig) LoadIdField(tableType string) string { + for _, table := range tl.Tables { + if tableType == table.TableType { + return table.IdField + } + } + return "" +} + +func (tl TableConfig) LoadCorrelations(tableType string) []CorrelationField { + resp := make([]CorrelationField, 0) + + for _, table := range tl.Tables { + if tableType == table.TableType { + for _, f := range table.Correlations { + resp = append(resp, f) + } + } + } + + return resp +} + +var _tables TableConfig +var _prevProject string + +func getTablesConfigJSON(project string) error { + _prevProject = project + stmt, err := Oracle.Ses.Prep(`SELECT + JSON_CLOB + FROM TABLES_CONFIG + WHERE PROJEKAT` + project, ora.S) + defer stmt.Close() + + if err != nil { + return err + } + + rset, err := stmt.Qry() + if err != nil { + return err + } + + if rset.Next() { + lob := rset.Row[0].(io.Reader) + bytes, err := ioutil.ReadAll(lob) + if err != nil { + fmt.Printf("mega error: %v\n", err) + } + json.Unmarshal(bytes, &_tables.Tables) + } + + return nil +} + +func loadTablesConfig(project string) error { + err := getTablesConfigJSON(putQuotes(project)) + //file, err := ioutil.ReadFile("./config/tables-config.json") + if err != nil { + fmt.Printf("%v\n", err); + return errors.New("unable to load tables config") + } + + //json.Unmarshal(file, &_TABLES_CONFIG.Tables) + + if len(_TABLES_CONFIG.Tables) == 0 { + return errors.New("tables config is corrupt") + } + + return nil +} + +func loadTable(table string) JSONParams { + return NewJSONParams(_tables.LoadTranslations(table), + _tables.LoadFields(table), + _tables.LoadIdField(table), + _tables.LoadCorrelations(table)) +} + +func refreshTables() error { + return getTablesConfigJSON(_prevProject) +}