package webutility import ( "encoding/json" "errors" "fmt" "io" "net/http" "os" "sync" "time" "gopkg.in/rana/ora.v4" ) var ( mu = &sync.Mutex{} metadata map[string]Payload metadataDB *ora.Ses activeProject string inited bool ) 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 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 holds JSON payload. It can't be used for itteration. Data interface{} `json:"data"` } // LoadPayloadsdetaData loads all payloads' information into 'metadata' variable. func LoadPayloadsMetadata(db *ora.Ses, project string, hotloading bool, hlPeriod int) error { metadataDB = db activeProject = project mu.Lock() defer mu.Unlock() err := initMetadata(project) if err != nil { return err } if hotloading { go hotload(hlPeriod) } inited = true return nil } func UpdateMetadataModels(md map[string][]byte) (upd, add int, err error) { if !inited { return 0, 0, errors.New("webutil: metadata not initialized but update was tried.") } forUpdate := make([]string, 0) forCreate := make([]string, 0) for k, _ := range md { if _, exists := metadata[k]; exists { forUpdate = append(forUpdate, k) } else { forCreate = append(forCreate, k) } } for _, k := range forUpdate { fmt.Printf("for update: %s\n", k) _, err := metadataDB.PrepAndExe(`update entities set entity_model = :1 where entity_type = :2`, string(md[k]), k) if err != nil { fmt.Printf("webutility: update metadata: prep and exe: %v\n", err) continue } upd++ } for _, k := range forCreate { fmt.Printf("for add: %s\n", k) /* _, err := metadataDB.PrepAndExe(`insert into entities (projekat, metadata, entity_type, entity_model) values(:1, :2, :3, :4)`, activeProject, "", k, string(md[k])) if err != nil { fmt.Printf("webutility: add metadata: prep and exe: %v\n", err) continue } */ add++ } return upd, add, nil } func GetMetadataForAllEntities() map[string]Payload { return metadata } func GetMetadataForEntityType(t string) Payload { return metadata[t] } func UpdateMetadata(entityType string, p *Payload) error { md, err := json.Marshal(p) if err != nil { return err } fmt.Printf("md: %s %s\n", entityType, string(md)) mu.Lock() defer mu.Unlock() _, err = metadataDB.PrepAndExe(`update entities set metadata = :1 where projekat = :2 and entity_type = :3`, string(md), activeProject, entityType) if err != nil { return err } return 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) } // NewPayload returs a payload sceleton for entity described with etype. func NewPayload(r *http.Request, etype string) Payload { pload := metadata[etype] pload.Method = r.Method + " " + r.RequestURI return pload } func initMetadata(project string) error { metadataDB.SetCfg(metadataDB.Cfg().SetClob(ora.S)) stmt, err := metadataDB.Prep(`select entity_type, metadata from entities where projekat = `+fmt.Sprintf("'%s'", project), ora.S, ora.S) defer stmt.Close() if err != nil { return err } rset, err := stmt.Qry() if err != nil { return err } count := 0 success := 0 metadata = make(map[string]Payload) for rset.Next() { name := rset.Row[0].(string) load := []byte(rset.Row[1].(string)) p := Payload{} err := json.Unmarshal(load, &p) if err != nil { fmt.Printf("couldn't init: '%s' metadata\n", name) } else { success++ metadata[name] = p } count++ } fmt.Printf("webutility: successfully loaded %d/%d (%.1f%%) entities\n", success, count, float32(success)/float32(count)*100.0) return nil } func hotload(n int) { entityScan := make(map[string]int64) firstCheck := true for { time.Sleep(time.Duration(n) * time.Second) stmt, err := metadataDB.Prep(`select ora_rowscn, entity_type from entities where projekat = `+fmt.Sprintf("'%s'", activeProject), ora.I64, ora.S) if err != nil { fmt.Fprintf(os.Stderr, "hotload failed: %v\n", err) time.Sleep(time.Duration(n) * time.Second) continue } rset, err := stmt.Qry() if err != nil { fmt.Fprintf(os.Stderr, "hotload failed: %v\n", err) time.Sleep(time.Duration(n) * time.Second) continue } var toRefresh []string for rset.Next() { scanID := rset.Row[0].(int64) entity := rset.Row[1].(string) oldID, ok := entityScan[entity] if !ok || oldID != scanID { entityScan[entity] = scanID toRefresh = append(toRefresh, entity) } } stmt.Close() if rset.Err() != nil { fmt.Fprintf(os.Stderr, "hotload rset error: %v\n", rset.Err()) time.Sleep(time.Duration(n) * time.Second) continue } if len(toRefresh) > 0 && !firstCheck { mu.Lock() refreshMetadata(toRefresh) mu.Unlock() } if firstCheck { firstCheck = false } } } func refreshMetadata(entities []string) { for _, e := range entities { //fmt.Printf("refreshing %s\n", e) stmt, err := metadataDB.Prep(`select metadata from entities where projekat = `+fmt.Sprintf("'%s'", activeProject)+ ` and entity_type = `+fmt.Sprintf("'%s'", e), ora.S) if err != nil { fmt.Printf("webutility: refresh: prep: %v\n", err) stmt.Close() continue } rset, err := stmt.Qry() if err != nil { fmt.Printf("webutility: refresh: query: %v\n", err) stmt.Close() continue } for rset.Next() { load := []byte(rset.Row[0].(string)) p := Payload{} err := json.Unmarshal(load, &p) if err != nil { fmt.Printf("couldn't refresh: '%s' metadata\n", e) } else { metadata[e] = p //fmt.Printf("unmarshaled %s %v\n", e, metadata[e]) } } stmt.Close() } }