Kepler core
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

347 lines
13 KiB

package restlet
import (
"crypto/sha1"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"reflect"
"strings"
"time"
log "github.com/Sirupsen/logrus"
"github.com/archsh/go.xql"
//"cygnux.net/kepler/utils"
"github.com/gorilla/mux"
)
var _debugInfo bool = false
func Build_QueryControl(queries Parameters, table *xql.Table, skips ...bool) (*QueryController, error) {
log.Debugln("_process_request_ctrl>>", table.TableName, queries)
qc := &QueryController{}
var include_fields = make(map[string]bool)
qc.Offset, _ = queries.GetInt64(QCTRL_OFFSET, QCTRL_OFFSET_DEFAULT)
delete(queries, QCTRL_OFFSET)
qc.Limit, _ = queries.GetInt64(QCTRL_LIMIT, QCTRL_LIMIT_DEFAULT)
delete(queries, QCTRL_LIMIT)
if ss, ok := queries.GetString(QCTRL_ORDERBY); ok {
kss := strings.Split(ss, ",")
for _, x := range kss {
qc.OrderBy = append(qc.OrderBy, x)
}
delete(queries, QCTRL_ORDERBY)
}
if ss, ok := queries.GetString(QCTRL_GROUPBY); ok {
kss := strings.Split(ss, ",")
for _, x := range kss {
qc.GroupBy = append(qc.GroupBy, x)
}
delete(queries, QCTRL_GROUPBY)
}
if ss, ok := queries.GetString(QCTRL_DEBUG); ok {
switch strings.ToLower(ss) {
case "true", "yes", "ok", "1", "t", "y":
qc.Debug = true
}
delete(queries, QCTRL_DEBUG)
}
if ss, ok := queries.GetString(QCTRL_INCLUDES); ok {
for _, x := range strings.Split(ss, ",") {
include_fields[x] = true
}
delete(queries, QCTRL_INCLUDES)
} else {
for _, c := range table.GetColumns() {
include_fields[c.FieldName] = true
}
}
if ss, ok := queries.GetString(QCTRL_EXCLUDES); ok {
for _, x := range strings.Split(ss, ",") {
include_fields[x] = false
}
delete(queries, QCTRL_EXCLUDES)
}
for k, v := range include_fields {
if ! v {
continue
}
if c, ok := table.GetColumn(k); ! ok {
return nil, errors.New("Invalid column:" + k)
} else {
qc.Includes = append(qc.Includes, c.FieldName)
}
}
if len(skips) > 0 && skips[0] == true {
return qc, nil
}
for k, v := range queries {
keys := strings.Split(k, "__")
if len(keys) > 3 {
return nil, fmt.Errorf("invalid expression: %s", k)
}
c, ok := table.GetColumn(keys[0])
if ! ok {
return nil, fmt.Errorf("unknow column: %s", keys[0])
}
f := xql.QueryFilter{Field: c.FieldName}
if len(keys) == 1 {
f.Operator = "="
f.Value = _build_column_query_value(table, c.FieldName, f.Operator, v)
} else if len(keys) == 2 {
switch strings.ToUpper(keys[1]) {
case "=":
f.Operator = "="
f.Value = _build_column_query_value(table, c.FieldName, f.Operator, v)
case "IN":
f.Operator = "IN"
f.Value = _build_column_query_value(table, c.FieldName, f.Operator, v)
//fmt.Println(">IN>>>>>>>>>>>",f.Value)
if reflect.TypeOf(f.Value).Kind() == reflect.Slice {
if reflect.ValueOf(f.Value).Len() < 1 {
continue
}
}
case "HAS":
f.Operator = "@>"
f.Value = _build_column_query_value(table, c.FieldName, f.Operator, v)
//fmt.Println(">HAS>>>>>>>>>>>",f.Value)
if reflect.TypeOf(f.Value).Kind() == reflect.Slice {
if reflect.ValueOf(f.Value).Len() < 1 {
continue
}
}
case "STARTSWITH":
f.Operator = "LIKE"
f.Value = v + "%"
case "ENDSWITH":
f.Operator = "LIKE"
f.Value = "%" + v
case "CONTAINS":
f.Operator = "LIKE"
f.Value = "%" + v + "%"
case "GT":
f.Operator = ">"
f.Value = _build_column_query_value(table, c.FieldName, f.Operator, v)
case "GTE":
f.Operator = ">="
f.Value = _build_column_query_value(table, c.FieldName, f.Operator, v)
case "LT":
f.Operator = "<"
f.Value = _build_column_query_value(table, c.FieldName, f.Operator, v)
case "LTE":
f.Operator = "<="
f.Value = _build_column_query_value(table, c.FieldName, f.Operator, v)
case "NOT":
f.Operator = "<>"
f.Value = _build_column_query_value(table, c.FieldName, f.Operator, v)
default:
return nil, fmt.Errorf("unknow operator: %s", keys[1])
}
}
if len(keys) == 3 {
switch strings.ToUpper(keys[2]) {
case "AND":
f.Condition = xql.CONDITION_AND
case "OR":
f.Condition = xql.CONDITION_OR
default:
return nil, fmt.Errorf("unknow condition: %s", keys[2])
}
}
//f.Value = _build_column_query_value(table, c.FieldName, f.Operator, v)
qc.Filters = append(qc.Filters, f)
}
return qc, nil
}
func MatchMethod(m string, methods []string) bool {
if len(methods) == 0 || methods[0] == "*" {
return true
}
for _, s := range methods {
if s == m {
return true
}
}
return false
}
func HashKey(s ...string) string {
h := sha1.New()
ns := strings.Join(s, "")
h.Write([]byte(ns))
bs := h.Sum(nil)
return fmt.Sprintf("%x", bs)
}
func SetDebug(d bool) {
_debugInfo = d
}
func Failure_Response(code uint, message string) (*RestletResult, error) {
return &RestletResult{
Code: code,
Message: message,
}, nil //errors.New(message)
}
func MakeRestletHandler(h RestletHandler, predictor RequestPredictor, ctx_provider RequestContextProvider, methods []string, cache *CacheController) http.Handler {
var f = func(response http.ResponseWriter, request *http.Request) {
var t1 int64
if _debugInfo {
t1 = time.Now().Unix()
}
log.Infoln(">", request.Method, request.URL.String())
if msg, ret := predictor.Predicate(request); !ret {
response.Header().Set("Content-Type", "text/plain")
response.WriteHeader(403)
response.Write([]byte(msg))
return
}
if ! MatchMethod(request.Method, methods) {
response.Header().Set("Content-Type", "text/plain")
response.WriteHeader(405)
response.Write([]byte(fmt.Sprintf("Method '%s' not allowed!", request.Method)))
return
}
//var ctx *RequestContext
var url_params Parameters
var queries Parameters = Parameters(make(map[string]string))
var post_data []byte
var c_key string
ctx := ctx_provider.NewContext(request)
url_params = Parameters(mux.Vars(request))
log.Debugln("makeRestletHandler:>>>", request.URL.Query())
for k, v := range request.URL.Query() {
log.Debugln("makeRestletHandler:>>>", k, v)
queries[k] = v[0]
}
if nil != cache && MatchMethod(request.Method, cache.CacheMethods) {
c_key = cache.KeyPrefix + HashKey(request.Method, request.URL.String())
if d, t := ctx.Cache().Get(c_key); t {
log.Debugln("Restlet:> Hit cache for :> ", c_key, request.Method, request.URL.String())
content_type := strings.TrimRightFunc(string(d[:CONTENT_HEADER_SIZE]), func(i rune) bool {
return i == 0
}) // http.DetectContentType(d)
log.Debugln("Restlet:> detect content-type:", content_type)
response.Header().Set("Content-Type", content_type)
response.WriteHeader(200)
response.Write(d[CONTENT_HEADER_SIZE:])
if _debugInfo {
t2 := time.Now().Unix()
log.Debugln("Request Time:", t1, t2, t2-t1, request.URL.String())
}
return
}
}
switch request.Method {
case "POST", "PUT", "PATCH":
if buf, e := ioutil.ReadAll(request.Body); e == nil {
post_data = buf
} else {
log.Errorln("Restlet:> Read Body error:>", e)
response.Header().Set("Content-Type", "text/plain")
response.WriteHeader(FATAL_INTERNAL_SERVER_ERROR)
response.Write([]byte(fmt.Sprintf("Read Body error:> %s", e)))
return
}
}
if r, e := h.Handle(ctx, url_params, queries, post_data); nil != e {
if nil == r {
response.Header().Set("Content-Type", "text/plain")
response.WriteHeader(FATAL_INTERNAL_SERVER_ERROR)
response.Write([]byte(fmt.Sprintf("Failed:> %s", e)))
} else {
response.Header().Set("Content-Type", "text/plain")
response.WriteHeader(int(r.Code / 100))
response.Write([]byte(r.Message))
}
} else {
r.ETag = fmt.Sprintf("%016x", time.Now().Unix())
if _debugInfo {
t2 := time.Now().Unix()
r.Debug = &DebugResult{Start: t1, Finish: t2}
}
var output interface{}
var err error
var pbytes []byte
var ok bool
if r.DataOnly {
output = r.Data
} else {
output = r
}
if r.RawBytes {
err = nil
pbytes, ok = output.([]byte)
if !ok {
panic("Restlet:> Data not row bytes!")
}
} else {
r.ContentType = "application/json"
pbytes, err = json.Marshal(output)
}
if nil != err {
response.Header().Set("Content-Type", "text/plain")
response.WriteHeader(FATAL_INTERNAL_SERVER_ERROR / 100)
response.Write([]byte(fmt.Sprintf("Failed:> %s", e)))
} else {
statusCode := int(r.Code / 100)
if statusCode/100 < 1 || statusCode/100 > 5 {
statusCode = 200
}
for _, x := range r.Cookies {
//x.Domain = ctx.Config().GetString("service.cookie_domain", "*.cygnux-tv.top")
http.SetCookie(response, x)
}
response.Header().Set("Content-Type", r.ContentType)
response.WriteHeader(statusCode)
response.Write(pbytes)
if nil != cache && MatchMethod(request.Method, cache.CacheMethods) /* && r.Cacheable */ {
var ct_bytes [CONTENT_HEADER_SIZE]byte
copy(ct_bytes[:], []byte(r.ContentType)[:CONTENT_HEADER_SIZE])
ctx.Cache().Set(c_key, append(ct_bytes[:], pbytes...), cache.Expires)
log.Debugln("Restlet:> Cached Content for:> ", c_key, request.Method, request.URL.String())
}
}
if _debugInfo {
t2 := time.Now().Unix()
log.Debugln("Request Time:", t1, t2, t2-t1, request.URL.String())
}
}
}
return http.HandlerFunc(f)
}
func MakeRawletHandler(h RawletHandler, predictor RequestPredictor, ctx_provider RequestContextProvider, methods []string) http.Handler {
var f = func(response http.ResponseWriter, request *http.Request) {
log.Infoln(">", request.Method, request.URL.String())
if msg, ret := predictor.Predicate(request); !ret {
response.Header().Set("Content-Type", "text/plain")
response.WriteHeader(403)
response.Write([]byte(msg))
return
}
if ! MatchMethod(request.Method, methods) {
response.Header().Set("Content-Type", "text/plain")
response.WriteHeader(405)
response.Write([]byte(fmt.Sprintf("Method '%s' not allowed!", request.Method)))
return
}
var params Parameters
ctx := ctx_provider.NewContext(request)
params = Parameters(mux.Vars(request))
log.Debugln("MakeRawletHandler:>>>", request.URL.Query())
h.Handle(ctx, params, response, request)
}
return http.HandlerFunc(f)
}