Browse Source

Updated.

develop
Mingcai SHEN 6 years ago
parent
commit
0538254374
10 changed files with 1566 additions and 51 deletions
  1. +3
    -0
      .gitignore
  2. +26
    -0
      restlet/authenticator.go
  3. +95
    -0
      restlet/consts.go
  4. +64
    -35
      restlet/context.go
  5. +672
    -0
      restlet/curd.go
  6. +42
    -0
      restlet/jwt-auth.go
  7. +53
    -0
      restlet/jwt.go
  8. +339
    -3
      restlet/restlet.go
  9. +112
    -0
      restlet/type_param.go
  10. +160
    -13
      restlet/types.go

+ 3
- 0
.gitignore View File

@ -0,0 +1,3 @@
glide.lock
.idea/
vendor/

+ 26
- 0
restlet/authenticator.go View File

@ -0,0 +1,26 @@
package restlet
import (
"net/http"
"fmt"
"context"
)
type AuthFunc func(r *http.Request) (interface{}, error)
type Authenticator struct {
next http.Handler
authenticate AuthFunc
}
func (a *Authenticator) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var ctx context.Context
if a.authenticate != nil {
if u, e := a.authenticate(r); nil != e {
http.Error(w, fmt.Sprint(e), http.StatusUnauthorized)
} else {
ctx = context.WithValue(r.Context(),"sessUserInfo",u)
}
}
a.next.ServeHTTP(w, r.WithContext(ctx))
}

+ 95
- 0
restlet/consts.go View File

@ -0,0 +1,95 @@
package restlet
const CONTENT_HEADER_SIZE = 32
const (
SUCCESS_OK = 20000
SUCCESS_CREATED = 20100
SUCCESS_DELETED = 20400
)
const (
ERROR_BAD_REQUEST = 40000 + iota
ERROR_INVALID_REQUEST
ERROR_INVALID_PATH
ERROR_INVALID_PARAMS
ERROR_INVALID_DATA
)
const (
ERROR_UNAUTHORIZED = 40100 + iota
ERROR_LOGGIN_REQUIRED
ERROR_SECURET_REQUIRED
ERROR_SESSION_EXPIRED
ERROR_CLIENT_LOCKED
ERROR_CLIENT_UNREGISTERED
)
const (
ERROR_PAYMENT_REQUIRED = 40200 + iota
ERROR_PAYMENT_NOTFOUND
ERROR_PAYMENT_EXPIRED
)
const (
ERROR_FORBIDDEN = 40300 + iota
ERROR_PERMISSION_DENNIED
ERROR_RESOURCE_LOCKED
)
const (
ERROR_NOT_FOUND = 40400 + iota
ERROR_OBJECT_NOT_FOUND
ERROR_ASSETS_NOT_FOUND
)
const (
ERROR_METHOD_NOT_ALLOWED = 40500 + iota
)
const (
ERROR_NOT_ACCEPTABLE = 40600 + iota
)
const (
ERROR_REQUEST_TIMEOUT = 40800 + iota
)
const (
ERROR_CONFLICT = 40900 + iota
ERROR_RESOURCE_EXISTS
ERROR_RESOURCE_NOT_COMPATIBLE
)
const (
ERROR_GONE = 41000 + iota
)
const (
FATAL_INTERNAL_SERVER_ERROR = 50000 + iota
FATAL_DB_READ_FAILED
FATAL_DB_WRITE_FAILED
FATAL_KV_READ_FAILED
FATAL_KV_WRITE_FAILED
FATAL_DATA_ENCODE_FAILED
FATAL_DATA_DECODE_FAILED
)
const (
FATAL_NOT_IMPLEMENTED = 50100 + iota
)
const (
FATAL_BAD_GATEWAY = 50200 + iota
FATAL_BAD_CONFIG
FATAL_CANNOT_CONNECT
)
const (
FATAL_SERVICE_UNAVAILABLE = 50300
)
const (
FATAL_GATEWAY_TIMEOUT = 50400 + iota
FATAL_CONNECT_TIMEOUT
FATAL_RESPOSE_TIMEOUT
)
const (
QCTRL_OFFSET = "__offset"
QCTRL_LIMIT = "__limit"
QCTRL_INCLUDES = "__includes"
QCTRL_EXCLUDES = "__excludes"
QCTRL_ORDERBY = "__order_by"
QCTRL_GROUPBY = "__group_by"
QCTRL_DEBUG = "__debug"
QCTRL_OFFSET_DEFAULT int64 = 0
QCTRL_LIMIT_DEFAULT int64 = 50
)

+ 64
- 35
restlet/context.go View File

@ -1,50 +1,79 @@
package restlet
import (
"net/http"
"database/sql"
)
type Context interface {
// GetConfig: Return a param value from configurations.
GetConfig(string, ...interface{}) (interface{}, bool)
"fmt"
//
"cygnux.net/kepler/cache"
"cygnux.net/kepler/config"
"cygnux.net/kepler/database"
"cygnux.net/kepler/kv"
"cygnux.net/kepler/msq"
log "github.com/Sirupsen/logrus"
)
type dumyContext struct {
_dbi database.DBI
_kvi kv.KV
_cache cache.Cache
_pub msq.Publisher
_sub msq.Subscriber
_cfg config.Config
}
func GetViaContext(k string, v interface{}) error {
return nil
func (d dumyContext) Schema(name ...string) string {
return d._dbi.Schema()
}
func AttachSQL(next http.Handler, d *sql.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
})
func (d dumyContext) SQL(name ...string) *sql.DB {
return d._dbi.DB()
}
func AttachMongoDB(next http.Handler, d *sql.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
})
func (d dumyContext) DBI(name ...string) database.DBI {
return d._dbi
}
func (d dumyContext) Chan(string, ...interface{}) error {
func AttachCache(next http.Handler, d *sql.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
})
return fmt.Errorf("not implemented")
}
func AttachKV(next http.Handler, d *sql.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
})
func (d dumyContext) Config() config.Config {
return d._cfg
}
func (d dumyContext) Cache(name ...string) cache.Cache {
return d._cache
}
func (d dumyContext) KV(name ...string) kv.KV {
return d._kvi
}
func (d dumyContext) Publish(topic string, bss ...[]byte) error {
for _, bs := range bss {
if e := d._pub.Publish(topic, bs); nil != e {
return e
}
}
return nil
}
func AttachConfig(next http.Handler, d interface{}) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
})
}
func NewDummyContext(cfg config.Config) (RestletContext, error) {
d := dumyContext{_cfg: cfg}
var e error
if d._dbi, e = database.SetupDBI(cfg.Sub("database")); nil != e {
log.Errorln("Open database failed:> ", e)
return nil, e
}
if d._cache, e = cache.SetupCache(cfg.Sub("cache")); nil != e {
log.Errorln("Make Cache failed:> ", e)
return nil, e
}
if d._kvi, e = kv.SetupKV(cfg.Sub("kv")); nil != e {
log.Errorln("Make KVStore failed:> ", e)
return nil, e
}
if d._pub, e = msq.SetupPublisher(cfg.Sub("publish")); nil != e {
log.Errorln("Make MessagePub failed:> ", e)
return nil, e
}
if d._sub, e = msq.SetupSubscriber(cfg.Sub("subscribe")); nil != e {
log.Errorln("Make MessageSub failed:> ", e)
return nil, e
}
return d, nil
}

+ 672
- 0
restlet/curd.go View File

@ -0,0 +1,672 @@
package restlet
import (
//"errors"
"reflect"
"strconv"
"time"
"github.com/archsh/go.xql"
_ "github.com/archsh/go.xql/dialects/postgres"
//"cygnux.net/kepler/utils"
"fmt"
log "github.com/Sirupsen/logrus"
//"strings"
"encoding/json"
"strings"
)
type CURDKind interface {
Entity() xql.TableIdentified
}
type CURDTable interface {
Table() *xql.Table
}
type CURDPreRead interface {
PreRead(ctx RequestContext, session *xql.Session, url_params Parameters, queries Parameters) (*QueryController, error)
}
type CURDPreCreate interface {
PreCreate(ctx RequestContext, session *xql.Session, url_params Parameters, queries Parameters, entity interface{}) error
}
type CURDPreUpdate interface {
PreUpdate(ctx RequestContext, session *xql.Session, url_params Parameters, queries Parameters, columns []xql.UpdateColumn) (*QueryController, []xql.UpdateColumn, error)
}
type CURDPreDelete interface {
PreDelete(ctx RequestContext, session *xql.Session, url_params Parameters, queries Parameters) (*QueryController, error)
}
type CURDPostRead interface {
PostRead(ctx RequestContext, session *xql.Session, qc *QueryController, entity interface{}) (interface{}, error)
}
type CURDPostCreate interface {
PostCreate(ctx RequestContext, session *xql.Session, entity interface{}) (interface{}, error)
}
type CURDPostUpdate interface {
PostUpdate(ctx RequestContext, session *xql.Session, qc *QueryController, columns []xql.UpdateColumn) ([]xql.UpdateColumn, error)
}
type CURDPostDelete interface {
PostDelete(ctx RequestContext, session *xql.Session, qc *QueryController) error
}
type DefaultCURDHandler struct {
dbiName string
table *xql.Table
entity xql.TableIdentified
pks []string
notAllowed []string
preRead CURDPreRead
preCreate CURDPreCreate
preUpdate CURDPreUpdate
preDelete CURDPreDelete
postRead CURDPostRead
postCreate CURDPostCreate
postUpdate CURDPostUpdate
postDelete CURDPostDelete
}
func (h DefaultCURDHandler) Handle(ctx RequestContext, url_params Parameters, queries Parameters, post_data []byte) (*RestletResult, error) {
for _, x := range h.notAllowed {
if strings.ToUpper(x) == ctx.Request().Method {
return Failure_Response(ERROR_METHOD_NOT_ALLOWED, "Method Not Allowed!")
}
}
switch ctx.Request().Method {
case "GET":
return h.read(ctx, url_params, queries, post_data)
case "PATCH":
return h.update(ctx, url_params, queries, post_data)
case "PUT":
return h.update(ctx, url_params, queries, post_data)
case "POST":
return h.create(ctx, url_params, queries, post_data)
case "DELETE":
return h.delete(ctx, url_params, queries, post_data)
case "OPTIONS":
return h.options(ctx, url_params, queries, post_data)
default:
return Failure_Response(ERROR_METHOD_NOT_ALLOWED, "Method Not Allowed!")
}
}
func (h DefaultCURDHandler) options(ctx RequestContext, url_params Parameters, queries Parameters, post_data []byte) (*RestletResult, error) {
result := &RestletResult{}
result.Model = reflect.TypeOf(h.entity).Name()
result.Data = h.entity
return result, nil
}
func (h DefaultCURDHandler) read(ctx RequestContext, url_params Parameters, queries Parameters, post_data []byte) (*RestletResult, error) {
session := xql.MakeSession(ctx.SQL(h.dbiName), "postgres", true)
defer session.Close()
result := &RestletResult{}
entityType := reflect.TypeOf(h.entity)
if val := reflect.ValueOf(h.entity); val.Kind() == reflect.Ptr {
entityType = val.Elem().Type() //reflect.TypeOf()
}
log.Debugln("DefaultCURDHandler.read> ", h.table.TableName, entityType)
var queryControl *QueryController
if h.preRead != nil {
qc, e := h.preRead.PreRead(ctx, session, url_params, queries)
if nil != e {
return Failure_Response(ERROR_BAD_REQUEST, fmt.Sprintf("%s", e))
}
queryControl = qc
} else if qc, err := Build_QueryControl(queries, h.table); nil != err {
log.Errorln("DefaultCURDHandler.read:> failure:", err)
return Failure_Response(ERROR_BAD_REQUEST, fmt.Sprintf("%s", err))
} else {
queryControl = qc
}
var pk_mapping = _build_params_map(h.table, url_params, h.pks...)
if nil != pk_mapping {
obj := reflect.New(entityType)
row := session.Query(h.table, queryControl.Includes...).Filter(pk_mapping).One()
err := row.Scan(obj.Elem().Addr().Interface())
if nil != err {
//if e, ok := err.(*pq.Error); ok {
// log.Errorln("DefaultCURDHandler.read:1> failure:", e)
//}
log.Errorln("_make_get_handle:2> failure:", reflect.TypeOf(err), err)
//return Failure_Response(FATAL_DB_READ_FAILED, fmt.Sprintf("%s", err))
return Failure_Response(ERROR_NOT_FOUND, "Record Not Found!")
}
result.Data = obj.Elem().Addr().Interface()
//return result, nil
} else {
for k, v := range url_params {
f := xql.QueryFilter{Field: k, Operator: "="}
f.Value = _build_column_query_value(h.table, k, f.Operator, v)
queryControl.Filters = append(queryControl.Filters, f)
}
qs := session.Query(h.table, queryControl.Includes...).Filter(queryControl.Filters...)
total, err := qs.Count()
if nil != err {
log.Errorln("DefaultCURDHandler.read:> failure:", err)
return Failure_Response(FATAL_DB_READ_FAILED, fmt.Sprintf("%s", err))
}
result.Control = &ControlResult{}
result.Control.Total = total
result.Control.Offset = queryControl.Offset
result.Control.Limit = queryControl.Limit
qs = qs.Offset(queryControl.Offset).Limit(queryControl.Limit)
qs = qs.OrderBy(queryControl.OrderBy...)
rows, err := qs.All()
if nil != err {
log.Errorln("DefaultCURDHandler.read:> failure:", err)
return Failure_Response(FATAL_DB_READ_FAILED, fmt.Sprintf("%s", err))
}
defer rows.Close()
objects := reflect.MakeSlice(reflect.SliceOf(reflect.PtrTo(entityType)), 0, 0)
for rows.Next() {
obj := reflect.New(entityType)
err = rows.Scan(obj.Elem().Addr().Interface())
if nil != err {
log.Errorln("DefaultCURDHandler.read:> failure:", err)
return Failure_Response(FATAL_DB_READ_FAILED, fmt.Sprintf("%s", err))
}
objects = reflect.Append(objects, obj)
result.Control.Count += 1
}
result.Model = reflect.TypeOf(h.entity).Name()
result.Data = objects.Interface()
}
if h.postRead != nil {
if ret, e := h.postRead.PostRead(ctx, session, queryControl, result.Data); nil != e {
log.Errorln("DefaultCURDHandler.read:> failure:", e)
return Failure_Response(FATAL_DB_READ_FAILED, fmt.Sprintf("%s", e))
} else {
result.Data = ret
}
}
return result, nil
}
func (h DefaultCURDHandler) create(ctx RequestContext, url_params Parameters, queries Parameters, post_data []byte) (*RestletResult, error) {
entityType := reflect.TypeOf(h.entity)
if val := reflect.ValueOf(h.entity); val.Kind() == reflect.Ptr {
entityType = val.Elem().Type() //reflect.TypeOf()
}
log.Debugln("DefaultCURDHandler.create> ", h.table.TableName, entityType)
if nil == post_data || len(post_data) < 1 {
return Failure_Response(ERROR_INVALID_DATA, "Empty Data!")
}
session := xql.MakeSession(ctx.SQL(h.dbiName), "postgres", true)
defer session.Close()
entityObjs := reflect.MakeSlice(reflect.SliceOf(entityType), 1, 2)
p := reflect.New(reflect.SliceOf(entityType))
reflect.Indirect(p).Set(entityObjs)
if err := json.Unmarshal(post_data, entityObjs.Index(0).Addr().Interface()); nil == err {
log.Debugln("DefaultCURDHandler.create>>>: Single Object")
} else if err = json.Unmarshal(post_data, p.Interface()); nil == err {
log.Debugln("DefaultCURDHandler.create>>>: Slice Objects")
} else {
log.Warnln("DefaultCURDHandler.create> Invalid Data:", err)
return Failure_Response(ERROR_INVALID_DATA, "Invalid Data!")
}
var pk_mapping = _build_params_map(h.table, url_params, h.pks...)
for k, v := range url_params {
if nil == pk_mapping {
pk_mapping = make(map[string]interface{})
pk_mapping[k] = _build_column_query_value(h.table, k, "=", v)
continue
}
if _, ok := pk_mapping[k]; ! ok {
pk_mapping[k] = _build_column_query_value(h.table, k, "=", v)
}
}
result := &RestletResult{}
err := session.Begin()
if nil != err {
log.Errorln("DefaultCURDHandler.create:> failure:", err)
return Failure_Response(FATAL_DB_WRITE_FAILED, fmt.Sprintf("%s", err))
}
//n := 0
for i := 0; i < entityObjs.Len(); i++ {
obj := entityObjs.Index(i)
if nil != pk_mapping {
_assign_entity_from_map(obj.Addr().Interface(), pk_mapping, false)
}
if h.preCreate != nil {
if e := h.preCreate.PreCreate(ctx, session, url_params, queries, obj.Addr().Interface()); nil != e {
log.Errorln("DefaultCURDHandler.create> preCreate failed:", e)
return Failure_Response(ERROR_INVALID_DATA, fmt.Sprintf("%s", e))
}
}
log.Debugln("DefaultCURDHandler.create:> Inserting :", obj.Addr().Interface())
_, err = session.Query(h.table).Insert(obj.Addr().Interface())
if nil != err {
log.Errorln("DefaultCURDHandler.create:> failure:", err)
session.Rollback()
return Failure_Response(FATAL_DB_WRITE_FAILED, fmt.Sprintf("%s", err))
}
}
result.Data = entityObjs.Interface()
if h.postCreate != nil {
if ret, e := h.postCreate.PostCreate(ctx, session, result.Data); nil != e {
log.Errorln("DefaultCURDHandler.create:> postCreate failed:", e)
return Failure_Response(FATAL_DB_WRITE_FAILED, fmt.Sprintf("%s", e))
} else {
result.Data = ret
}
}
err = session.Commit()
if nil != err {
log.Errorln("DefaultCURDHandler.create:> failure:", err)
session.Rollback()
return Failure_Response(FATAL_DB_WRITE_FAILED, fmt.Sprintf("%s", err))
}
result.Code = SUCCESS_CREATED
return result, nil
}
func (h DefaultCURDHandler) update(ctx RequestContext, url_params Parameters, queries Parameters, post_data []byte) (*RestletResult, error) {
entityType := reflect.TypeOf(h.entity)
if val := reflect.ValueOf(h.entity); val.Kind() == reflect.Ptr {
entityType = val.Elem().Type() //reflect.TypeOf()
}
log.Debugln("DefaultCURDHandler.update> ", h.table.TableName, entityType)
if nil == post_data || len(post_data) < 1 {
log.Warnln("DefaultCURDHandler.update:> Empty DATA.")
return Failure_Response(ERROR_INVALID_DATA, "Empty Data!")
}
entityObj := reflect.New(entityType)
var entityMap = make(map[string]interface{})
err := json.Unmarshal(post_data, entityObj.Elem().Addr().Interface())
e1 := json.Unmarshal(post_data, &entityMap)
if nil != err || nil != e1 {
log.Warnln("DefaultCURDHandler.update:> Invalid DATA:", err, e1)
return Failure_Response(ERROR_INVALID_DATA, "Invalid Data!")
}
for k, _ := range entityMap {
if _, ok := h.table.GetColumn(k); ok {
continue
} else {
log.Warnln("DefaultCURDHandler.update:> Invalid Field:", k)
return Failure_Response(ERROR_INVALID_DATA, "Invalid Field:"+k)
}
}
if len(entityMap) == 0 {
log.Warnln("DefaultCURDHandler.update:> Empty DATA:", err, e1)
return Failure_Response(ERROR_INVALID_DATA, "Empty Data!")
}
session := xql.MakeSession(ctx.SQL(h.dbiName), "postgres", true)
defer session.Close()
var updateCols []xql.UpdateColumn
for _, c := range h.table.GetColumns() {
if _, ok := entityMap[c.FieldName]; ok {
} else if _, ok := entityMap[c.Jtag]; ok {
} else if _, ok := entityMap[c.ElemName]; ok {
} else {
continue
}
if c.PrimaryKey {
return Failure_Response(ERROR_FORBIDDEN, "Not Allowed to Change Primary Key(s).")
}
uc := xql.UpdateColumn{Field: c.FieldName, Operator: "="}
val := entityObj.Elem().FieldByName(c.ElemName)
switch val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
uc.Value = val.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
uc.Value = val.Uint()
case reflect.String:
uc.Value = val.String()
case reflect.Bool:
uc.Value = val.Bool()
case reflect.Float32, reflect.Float64:
uc.Value = val.Float()
default:
uc.Value = val.Interface()
}
updateCols = append(updateCols, uc)
}
var queryControl *QueryController
if h.preUpdate != nil {
if qc, cols, e := h.preUpdate.PreUpdate(ctx, session, url_params, queries, updateCols); nil != e {
return Failure_Response(ERROR_BAD_REQUEST, fmt.Sprintf("%s", e))
} else {
queryControl = qc
updateCols = cols
}
} else {
if qc, err := Build_QueryControl(queries, h.table); nil != err {
log.Errorln("DefaultCURDHandler.update:> failure:", err)
return Failure_Response(ERROR_BAD_REQUEST, fmt.Sprintf("%s", err))
} else {
queryControl = qc
}
}
log.Debugln("DefaultCURDHandler.update> QueryControl:", queryControl)
var pk_mapping = _build_params_map(h.table, url_params, h.pks...)
if nil != pk_mapping {
queryControl.Filters = append(queryControl.Filters, pk_mapping)
}
n, e := session.Query(h.table).Filter(queryControl.Filters...).Update(updateCols)
if nil != e {
log.Errorln("DefaultCURDHandler.update:> failure:", e)
return Failure_Response(FATAL_DB_WRITE_FAILED, fmt.Sprintf("%s", e))
}
if h.postUpdate != nil {
updateCols, e = h.postUpdate.PostUpdate(ctx, session, queryControl, updateCols)
if nil != e {
log.Errorln("DefaultCURDHandler.update:> post update failure:", e)
return Failure_Response(FATAL_DB_WRITE_FAILED, fmt.Sprintf("%s", e))
}
}
var updates = make(map[string]interface{})
for _, x := range updateCols {
updates[x.Field] = x.Value
}
result := &RestletResult{
Code: SUCCESS_OK,
Model: entityType.Name(),
Data: map[string]interface{}{
"count": n,
"updates": updates,
},
}
return result, nil
}
func (h DefaultCURDHandler) delete(ctx RequestContext, url_params Parameters, queries Parameters, post_data []byte) (*RestletResult, error) {
session := xql.MakeSession(ctx.SQL(h.dbiName), "postgres", true)
defer session.Close()
result := &RestletResult{}
entityType := reflect.TypeOf(h.entity)
if val := reflect.ValueOf(h.entity); val.Kind() == reflect.Ptr {
entityType = val.Elem().Type() //reflect.TypeOf()
}
result.Model = reflect.TypeOf(h.entity).Name()
log.Debugln("DefaultCURDHandler.delete> ", h.table.TableName, entityType)
var queryControl *QueryController
if h.preDelete != nil {
if qc, e := h.preDelete.PreDelete(ctx, session, url_params, queries); nil != e {
log.Errorln("DefaultCURDHandler.delete:> pre delete failure:", e)
return Failure_Response(ERROR_BAD_REQUEST, fmt.Sprintf("%s", e))
} else {
queryControl = qc
}
} else {
if qc, err := Build_QueryControl(queries, h.table); nil != err {
log.Errorln("DefaultCURDHandler.delete:> failure:", err)
return Failure_Response(ERROR_BAD_REQUEST, fmt.Sprintf("%s", err))
} else {
queryControl = qc
}
}
var pk_mapping = _build_params_map(h.table, url_params, h.pks...)
if nil != pk_mapping {
n, err := session.Query(h.table).Filter(pk_mapping).Delete()
if nil != err {
return Failure_Response(FATAL_DB_WRITE_FAILED, fmt.Sprint(err))
}
result.Code = SUCCESS_DELETED
result.Data = map[string]interface{}{"Deleted": n}
return result, nil
}
for k, v := range url_params {
f := xql.QueryFilter{Field: k, Operator: "="}
f.Value = _build_column_query_value(h.table, k, f.Operator, v)
queryControl.Filters = append(queryControl.Filters, f)
}
if len(queryControl.Filters) < 1 {
return Failure_Response(ERROR_FORBIDDEN, "Not allowed to delete without conditions!")
}
n, err := session.Query(h.table).Filter(queryControl.Filters...).Delete()
if nil != err {
return Failure_Response(FATAL_DB_WRITE_FAILED, fmt.Sprint(err))
}
if h.postDelete != nil {
if e := h.postDelete.PostDelete(ctx, session, queryControl); nil != e {
return Failure_Response(FATAL_DB_WRITE_FAILED, fmt.Sprint(e))
}
}
result.Code = SUCCESS_OK
result.Data = map[string]interface{}{"deleted": n}
return result, nil
}
func NewCURDHandler(dbiname string, kind interface{}, notAllowedMethods ...string) RestletHandler {
var h = DefaultCURDHandler{dbiName: dbiname, notAllowed: notAllowedMethods}
if h.dbiName == "" {
h.dbiName = "default"
}
if k, b := kind.(xql.TableIdentified); b {
h.entity = k
} else if k, b := kind.(CURDKind); b {
h.entity = k.Entity()
} else {
panic("kind param should at least implement CURDKind or xql.TableIdentified interface!")
}
if k, b := kind.(CURDTable); b {
h.table = k.Table()
} else {
h.table = xql.DeclareTable(h.entity)
}
for _, x := range h.table.GetPrimaryKeys() {
h.pks = append(h.pks, x.FieldName)
}
if k, b := kind.(CURDPreRead); b {
h.preRead = k
}
if k, b := kind.(CURDPreCreate); b {
h.preCreate = k
}
if k, b := kind.(CURDPreUpdate); b {
h.preUpdate = k
}
if k, b := kind.(CURDPreDelete); b {
h.preDelete = k
}
if k, b := kind.(CURDPostRead); b {
h.postRead = k
}
if k, b := kind.(CURDPostCreate); b {
h.postCreate = k
}
if k, b := kind.(CURDPostUpdate); b {
h.postUpdate = k
}
if k, b := kind.(CURDPostDelete); b {
h.postDelete = k
}
return h
}
func _build_params_map(table *xql.Table, params Parameters, pks ...string) map[string]interface{} {
var pk_mapping map[string]interface{}
for i, pk := range pks {
if v, ok := params.GetString(pk); ok {
log.Debugln("_make_get_handle:> Pk", i, pk, v)
if nil == pk_mapping {
pk_mapping = make(map[string]interface{})
}
pk_mapping[table.GetPrimaryKeys()[i].FieldName] =
_build_column_query_value(table, table.GetPrimaryKeys()[i].FieldName, "=", v)
delete(params, pk)
}
}
if len(pks) > 0 {
return pk_mapping
}
for k, v := range params {
if nil == pk_mapping {
pk_mapping = make(map[string]interface{})
}
pk_mapping[k] = _build_column_query_value(table, k, "=", v)
}
return pk_mapping
}
func _assign_entity_from_map(entity interface{}, params map[string]interface{}, recursive bool) error {
log.Debugln("_assign_entity_from_map:>>>", entity, params)
if nil != entity {
et := reflect.TypeOf(entity)
ev := reflect.ValueOf(entity)
for i := 0; i < et.Elem().NumField(); i++ {
f := et.Elem().Field(i)
x_tag := strings.Split(f.Tag.Get("xql"), ",")[0]
if x_tag == "-" {
continue
}
if f.Anonymous && !recursive {
_assign_entity_from_map(ev.Elem().Field(i).Addr().Interface(), params, true)
continue
}
json_tag := strings.Split(f.Tag.Get("json"), ",")[0]
var val interface{}
if v, ok := params[x_tag]; ok {
log.Debugln("_assign_entity_from_map:>>>", 1, v)
val = v
} else if v, ok := params[json_tag]; ok {
log.Debugln("_assign_entity_from_map:>>>", 2, v)
val = v
} else if v, ok := params[f.Name]; ok {
log.Debugln("_assign_entity_from_map:>>>", 3, v)
val = v
} else if v, ok := params[xql.Camel2Underscore(f.Name)]; ok {
log.Debugln("_assign_entity_from_map:>>>", 4, v)
val = v
} else {
continue
}
fv := ev.Elem().Field(i)
if fv.IsValid() && fv.CanSet() {
fv.Set(reflect.ValueOf(val))
}
}
} else {
panic("Empty pointer of entity!")
}
return nil
}
func translate_datetime(s string) time.Time {
if t, e := time.Parse(time.RFC3339Nano, s); nil == e {
return t
} else if t, e := time.Parse(time.RFC3339, s); nil == e {
return t
} else if t, e := time.Parse("2006-01-02T15:04:05", s); nil == e {
return t
} else if t, e := time.Parse("2006-01-02T15:04", s); nil == e {
return t
} else if t, e := time.Parse("2006-01-02", s); nil == e {
return t
} else if t, e := time.Parse("2006", s); nil == e {
return t
} else {
return time.Time{}
}
}
func translate_single_value(k reflect.Kind, val string) interface{} {
switch k {
case reflect.Bool:
switch strings.ToLower(val) {
case "yes", "true", "y", "t", "ok":
return true
default:
return false
}
case reflect.Int:
n, _ := strconv.ParseInt(val, 10, 32)
return int(n)
case reflect.Int8:
n, _ := strconv.ParseInt(val, 10, 8)
return int8(n)
case reflect.Int16:
n, _ := strconv.ParseInt(val, 10, 16)
return int16(n)
case reflect.Int64:
n, _ := strconv.ParseInt(val, 10, 64)
return int64(n)
case reflect.Uint:
n, _ := strconv.ParseUint(val, 10, 32)
return uint(n)
case reflect.Uint8:
n, _ := strconv.ParseUint(val, 10, 8)
return uint8(n)
case reflect.Uint16:
n, _ := strconv.ParseUint(val, 10, 16)
return uint16(n)
case reflect.Uint64:
n, _ := strconv.ParseUint(val, 10, 64)
return uint64(n)
case reflect.String:
return val
case reflect.Float32:
f, _ := strconv.ParseFloat(val, 32)
return float32(f)
case reflect.Float64:
f, _ := strconv.ParseFloat(val, 64)
return float64(f)
case reflect.TypeOf(time.Time{}).Kind():
t := translate_datetime(val)
return t
case reflect.TypeOf(&time.Time{}).Kind():
t := translate_datetime(val)
return &t
default:
return val
}
return nil
}
func _build_column_query_value(table *xql.Table, field string, operator string, val string) interface{} {
if col, b := table.GetColumn(field); !b {
return val
} else {
if col.Type.Kind() == reflect.Slice {
et := col.Type.Elem()
vv := reflect.MakeSlice(col.Type, 0, 0)
for _, x := range strings.Split(val, ",") {
if x == "" {
continue
}
v := translate_single_value(et.Kind(), x)
//fmt.Println(">>>>", v,reflect.ValueOf(v))
vv = reflect.Append(vv, reflect.ValueOf(v))
//fmt.Println(">>>>",vv)
}
return vv.Interface()
} else if operator == "IN" {
vv := reflect.MakeSlice(reflect.SliceOf(col.Type), 0, 0)
for _, x := range strings.Split(val, ",") {
if x == "" {
continue
}
v := translate_single_value(col.Type.Kind(), x)
//fmt.Println(">>>>", v,reflect.ValueOf(v))
vv = reflect.Append(vv, reflect.ValueOf(v))
//fmt.Println(">>>>",vv)
}
return vv.Interface()
} else {
return translate_single_value(col.Type.Kind(), val)
}
}
return val
}

+ 42
- 0
restlet/jwt-auth.go View File

@ -0,0 +1,42 @@
package restlet
import (
"errors"
"github.com/dgrijalva/jwt-go"
"fmt"
)
const (
RESTLET_JWT_KEYNAME = "jwt_secret"
RESTLET_JWT_DEFAULT_KEY = "thisisanotherrun"
)
func ParseToken(ctx RequestContext, claims jwt.Claims, tkstring string) error {
if token, err := jwt.ParseWithClaims(tkstring, claims, func(tk *jwt.Token)(interface{}, error){
if _, ok := tk.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", tk.Header["alg"])
}
secret := ctx.Config().GetString(RESTLET_JWT_KEYNAME, RESTLET_JWT_DEFAULT_KEY)
return []byte(secret), nil
}); nil != err {
return err
}else if !token.Valid {
return errors.New("Invalid token!")
}else{
return nil
}
}
func SignToken(ctx RequestContext, claims jwt.Claims) (string, error) {
tk := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
secret := ctx.Config().GetString(RESTLET_JWT_KEYNAME, RESTLET_JWT_DEFAULT_KEY)
if s, e := tk.SignedString([]byte(secret));nil != e {
fmt.Println("Error:1>", e)
return "", e
}else {
return s, nil
}
}

+ 53
- 0
restlet/jwt.go View File

@ -0,0 +1,53 @@
package restlet
import (
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/dgrijalva/jwt-go"
"github.com/dgrijalva/jwt-go/request"
"net/http"
)
func GetSession(ctx RequestContext, secret, cookiename string, claims jwt.Claims) error {
if e := ExtractClaimsViaHeader(ctx.Request(), secret, claims); nil == e {
log.Debugln("> GetSession via Header:>", claims)
return nil
} else {
log.Debugln("> GetSession via Header failed:>", e)
}
if e := ExtractClaimsViaCookie(ctx.Request(), cookiename, secret, claims); nil == e {
log.Debugln("> GetSession via Cookie:>", claims)
return nil
} else {
log.Debugln("> GetSession via Cookie failed:>", e)
}
return fmt.Errorf("can not read session token")
}
func ExtractClaimsViaHeader(r *http.Request, secret string, o jwt.Claims) error {
if token, err := request.ParseFromRequestWithClaims(r, request.AuthorizationHeaderExtractor, o,
func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
}); nil != err {
return err
} else if ! token.Valid {
return fmt.Errorf("token invalid")
}
return nil
}
func ExtractClaimsViaCookie(r *http.Request, ckname string, secret string, o jwt.Claims) error {
for _, x := range r.Cookies() {
if x.Name == ckname {
if token, e := jwt.ParseWithClaims(x.Value, o, func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
}); nil != e {
return e
} else if token.Valid {
return nil
}
break
}
}
return fmt.Errorf("not found")
}

+ 339
- 3
restlet/restlet.go View File

@ -1,11 +1,347 @@
package restlet
import (
"github.com/archsh/go.xql"
"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"
)
func MakeRestletHandler(entity xql.TableIdentified, schema string) http.Handler {
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)
}
return nil
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)
}

+ 112
- 0
restlet/type_param.go View File

@ -0,0 +1,112 @@
package restlet
import (
"strings"
"strconv"
"github.com/archsh/go.uuid"
)
func (h Parameters) HasKey(k string) bool {
if nil == h {
return false
}
_, ok := h[k]
return ok
}
func (h Parameters) GetInt(k string, defaults ... int) (int, bool) {
d := 0
if len(defaults) > 0 {
d = defaults[0]
}
if nil == h {
return d, false
}
if ns, ok := h[k]; ok {
if n, e := strconv.Atoi(ns); e == nil {
return n, true
}
}
return d, false
}
func (h Parameters) GetInt64(k string, defaults ... int64) (int64, bool) {
var d int64 = 0
if len(defaults) > 0 {
d = defaults[0]
}
if nil == h {
return d, false
}
if ns, ok := h[k]; ok {
if n, e := strconv.ParseInt(ns, 10, 64); e == nil {
return n, true
}
}
return d, false
}
func (h Parameters) GetUInt(k string, defaults ... uint) (uint, bool) {
var d uint = 0
if len(defaults) > 0 {
d = defaults[0]
}
if nil == h {
return d, false
}
if ns, ok := h[k]; ok {
if n, e := strconv.ParseUint(ns, 10, 32); e == nil {
return uint(n), true
}
}
return d, false
}
func (h Parameters) GetString(k string, defaults ... string) (string, bool) {
d := ""
if len(defaults) > 0 {
d = defaults[0]
}
if nil == h {
return d, false
}
if ns, ok := h[k]; ok {
return ns, true
}
return d, false
}
func (h Parameters) GetBool(k string, defaults ... bool) (bool, bool) {
d := false
if len(defaults) > 0 {
d = defaults[0]
}
if nil == h {
return d, false
}
if ns, ok := h[k]; ok {
switch strings.ToLower(ns) {
case "t", "true", "yes", "ok":
return true, true
case "f", "false", "no":
return false, true
}
}
return d, false
}
func (h Parameters) GetUUID(k string, defaults ... uuid.UUID) (uuid.UUID, bool) {
d := uuid.UUID{}
if len(defaults) > 0 {
d = defaults[0]
}
if nil == h {
return d, false
}
if ns, ok := h[k]; ok {
if uid, e := uuid.FromString(ns); nil == e {
return uid, true
}
}
return d, false
}

+ 160
- 13
restlet/types.go View File

@ -1,15 +1,162 @@
package restlet
type RestletControl struct {
Offset int64 `json:"offset" xml:"offset,attr"`
Limit int64 `json:"limit" xml:"limit,attr"`
Count int64 `json:"count" xml:"count,attr"`
Total int64 `json:"total" xml:"total,attr"`
}
type RestletResponse struct {
Code int `json:"Code" xml:"Code"`
Message string `json:"message" xml:"Message"`
Control *RestletControl `json:"ctrl,omitempty" xml:"Control,omitempty"`
Data interface{} `json:"data" xml:"Data"`
}
import (
"database/sql"
"net/http"
"cygnux.net/kepler/cache"
"cygnux.net/kepler/config"
"cygnux.net/kepler/database"
"cygnux.net/kepler/kv"
)
type Parameters map[string]string
type ControlResult struct {
Total int64 `json:"total"`
Count int64 `json:"count"`
Offset int64 `json:"offset"`
Limit int64 `json:"limit"`
}
type DebugResult struct {
Start int64
Finish int64
}
type RestletResult struct {
Code uint `json:"code"`
Message string `json:"message,omitempty"`
ContentType string `json:"-"`
DataOnly bool `json:"-"`
RawBytes bool `json:"-"`
Cacheable bool
Headers http.Header `json:"-"`
Cookies []*http.Cookie `json:"-"`
ETag string `json:"__eTag,omitempty"`
Debug *DebugResult `json:"__debug,omitempty"`
Model string `json:"__model,omitempty"`
IsList bool `json:"__is_list,omitempty"`
Control *ControlResult `json:"__control,omitempty"`
Data interface{} `json:"data"`
}
type RawletResult struct {
Code uint
Message string
ContentType string
Data []byte
}
type QueryController struct {
Offset int64
Limit int64
Debug bool
Includes []interface{}
Excludes []interface{}
OrderBy []interface{}
GroupBy []interface{}
Filters []interface{}
Params map[string]interface{}
}
type RestletError struct {
msg string
code uint
}
func (e RestletError) Error() string {
return e.msg
}
func (e RestletError) Code() uint {
return e.code
}
func MakeError(code uint, msg string) *RestletError {
return &RestletError{
code: code,
msg: msg,
}
}
//type MessageHandleFunc func ([]byte) error
//
//type MessageQueue interface {
// Chan(channel string, body []byte) error
// Subscribe(channel string, queue string, handle MessageHandleFunc) error
//}
type RestletContext interface {
Schema(name ...string) string
SQL(name ...string) *sql.DB
DBI(name ...string) database.DBI
Chan(string, ...interface{}) error
Config() config.Config
Cache(name ...string) cache.Cache
KV(name ...string) kv.KV
Publish(string, ...[]byte) error
}
type RequestContext interface {
RestletContext
Request() *http.Request
}
type RequestContextProvider interface {
NewContext(*http.Request) RequestContext
}
type RequestPredictor interface {
Predicate(*http.Request) (string, bool)
}
type CacheController struct {
KeyPrefix string
CacheMethods []string
CacheStatus []int
Expires int64
}
type RestletHandler interface {
Handle(ctx RequestContext, url_params Parameters, queries Parameters, post_data []byte) (*RestletResult, error)
}
type RawletHandler interface {
Handle(ctx RequestContext, params Parameters, w http.ResponseWriter, r *http.Request)
}
type RawletHandleFunc func(ctx RequestContext, params Parameters, w http.ResponseWriter, r *http.Request)
func (f RawletHandleFunc) Handle(ctx RequestContext, params Parameters, w http.ResponseWriter, r *http.Request) {
f(ctx, params, w, r)
}
type TaskContext interface {
RestletContext
}
type TaskContextProvider interface {
NewContext() TaskContext
}
type TaskObject struct {
Queue string
Params []interface{}
}
type TaskletHandler interface {
Handle(TaskContext, ...interface{}) error
}
type RestletHandleFunc func(ctx RequestContext, url_params Parameters, queries Parameters, post_data []byte) (*RestletResult, error)
type TaskletHandlerFunc func(TaskContext, ...interface{}) error
func (f RestletHandleFunc) Handle(ctx RequestContext, url_params Parameters, queries Parameters, post_data []byte) (*RestletResult, error) {
return f(ctx, url_params, queries, post_data)
}
func (f TaskletHandlerFunc) Handle(ctx TaskContext, params ...interface{}) error {
return f(ctx, params...)
}

Loading…
Cancel
Save