json_utility.go 4.59 KB
package webutility

import (
	"net/http"
	"encoding/json"
	"errors"
	"gopkg.in/rana/ora.v3"
	"io"
	"io/ioutil"
	"sync"
)

var mu = &sync.Mutex{}
var payloads []payloadBuff

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 CorrelationField struct {
	Result   string   `json:"result"`
	Elements []string `json:"elements"`
	Type     string   `json:"type"`
}

type Translation struct {
	Language     string            `json:"language"`
	FieldsLabels map[string]string `json:"fieldsLabels"`
}

type payloadBuff struct {
	Type         string             `json:"tableType"`
	Method	     string             `json:"method"`
	Params	     map[string]string  `json:"params"`
	Lang	     []Translation      `json:"lang"`
	Fields	     []Field            `json:"fields"`
	Correlations []CorrelationField `json:"correlationFields"`
	IdField      string             `json:"idField"`

	// Data can only hold slices of any type. It can't be used for itteration
	Data	     interface{}        `json:"data"`
}

type Payload struct {
	Method	     string             `json:"method"`
	Params	     map[string]string  `json:"params"`
	Lang	     []Translation      `json:"lang"`
	Fields	     []Field            `json:"fields"`
	Correlations []CorrelationField `json:"correlationFields"`
	IdField      string             `json:"idField"`

	// Data can only hold slices of any type. It can't be used for itteration
	Data	     interface{}        `json:"data"`
}

// NewPayload returs a payload sceleton for provided table.
func NewPayload(r *http.Request, table string) Payload {
	var pload Payload

	pload.Method = r.Method + " " + r.URL.Path
	if table != "" {
		pload.Params = make(map[string]string, 0)
		pload.Lang = translations(table)
		pload.Fields = fields(table)
		pload.IdField = id(table)
		pload.Correlations = correlations(table)
	}
	return pload
}

// DeliverPayload encodes payload to w.
func DeliverPayload(w http.ResponseWriter, payload Payload) {
	json.NewEncoder(w).Encode(payload)
	payload.Data = nil
}

// translations returns a slice of translations for a payload/table of ptype type.
func translations(ptype string) []Translation {
	var translations []Translation

	for _, pload := range payloads {
		if pload.Type == ptype {
			for _, t := range pload.Lang {
				translations = append(translations, Translation{
					Language: t.Language,
					FieldsLabels: t.FieldsLabels,
				})
			}
		}
	}

	return translations
}

// fields returns a slice of fields for a payload/table of ptype type.
func fields(ptype string) []Field {
	var fields []Field

	for _, pload := range payloads {
		if pload.Type == ptype {
			for _, f := range pload.Fields {
				fields = append(fields, f)
			}
		}
	}

	return fields
}

// id returns the name of ID field of a payload/table of ptype type.
func id(ptype string) string {
	for _, pload := range payloads {
		if pload.Type == ptype {
			return pload.IdField
		}
	}
	return ""
}

// correlations returns a slice of correlation fields for a payload/table of ptype type.
func correlations(ptype string) []CorrelationField {
	var corr []CorrelationField

	for _, pload := range payloads {
		if pload.Type == ptype {
			for _, c := range pload.Correlations {
				corr = append(corr, c)
			}
		}
	}

	return corr
}

// InitTables loads all payloads in the payloads variable.
// Returns an error if it fails.
func InitTables(db *ora.Ses, project string) error {
	jsonbuf, _ := fetchJSON(db, EqualQuotes(project))
	mu.Lock()
	defer mu.Unlock()
	json.Unmarshal(jsonbuf, &payloads)
	if len(payloads) == 0 {
		return errors.New("tables config is corrupt")
	}
	return nil
}

// fetchJSON returns a byte slice of JSON configuration file from TABLES_CONFIG table.
// Returns an error if it fails.
func fetchJSON(db *ora.Ses, project string) ([]byte, error) {
	stmt, err := db.Prep(`SELECT
		JSON_CLOB
		FROM TABLES_CONFIG
		WHERE PROJEKAT` + project, ora.S)
	defer stmt.Close()

	if err != nil {
		return nil, err
	}

	rset, err := stmt.Qry()
	if err != nil {
		return nil, err
	}

	bytes := make([]byte, 0)
	if rset.Next() {
		lob := rset.Row[0].(io.Reader)
		if lob != nil {
			bytes, err = ioutil.ReadAll(lob)
			if err != nil {
				// TODO: Find a fix for this.
				// Some weird streaming read/write LOB error.
				// Ignore for now.
				//return nil, err
			}
		} else {
			return nil, errors.New("json config is null")
		}
	}

	return bytes, nil
}

// DecodeJSON decodes JSON data from r to v.
// Returns an error if it fails.
func DecodeJSON(r io.Reader, v interface{}) error {
	return json.NewDecoder(r).Decode(v)
}