Skip to content

Commit

Permalink
re-organized the code and added resp server
Browse files Browse the repository at this point in the history
  • Loading branch information
alash3al committed Jan 9, 2019
1 parent 79a88ce commit dfc5344
Show file tree
Hide file tree
Showing 10 changed files with 318 additions and 161 deletions.
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -26,6 +26,7 @@ Configuration Overview
// create a macro/endpoint called "_boot",
// this macro is private "used within other macros"
// because it starts with "_".
// this rule only used within `RESTful` context.
_boot {
// the query we want to execute
exec = <<SQL
Expand All @@ -45,6 +46,7 @@ _boot {
adduser {
// what request method will this macro be called
// default: ["ANY"]
// this only used within `RESTful` context.
methods = ["POST"]
// authorizers,
Expand All @@ -53,6 +55,7 @@ adduser {
// each endpoint MUST return `200 OK` so sqler can continue, other wise,
// sqler will break the request and return back the client with the error occured.
// each authorizer has a method and a url.
// this only used within `RESTful` context.
// authorizers = ["GET http://web.hook/api/authorize", "GET http://web.hook/api/allowed?roles=admin,root,super_admin"]
// the validation rules
Expand Down
1 change: 1 addition & 0 deletions context.go
Expand Up @@ -72,6 +72,7 @@ func (c Context) Uniqid() string {
return snow.Generate().String()
}

// BindVar - bind a named var for prepared statements
func (c *Context) BindVar(name string, value interface{}) string {
c.SQLArgs[name] = value
return ""
Expand Down
3 changes: 3 additions & 0 deletions init.go
Expand Up @@ -7,6 +7,7 @@ import (
"flag"
"fmt"
"os"
"runtime"

"github.com/bwmarrin/snowflake"

Expand All @@ -21,6 +22,8 @@ import (
func init() {
flag.Parse()

runtime.GOMAXPROCS(*flagWorkers)

if _, err := sqlx.Connect(*flagDBDriver, *flagDBDSN); err != nil {
fmt.Println(color.RedString("[%s] - connection error - (%s)", *flagDBDriver, err.Error()))
os.Exit(0)
Expand Down
138 changes: 138 additions & 0 deletions macro.go
@@ -0,0 +1,138 @@
package main

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"strings"
"text/template"

"github.com/jmoiron/sqlx"
)

// Macro - a macro configuration
type Macro struct {
Authorizers []string `json:"authorizers"`
Methods []string `json:"method"`
Rules map[string][]string `json:"rules"`
Exec string `json:"exec"`
name string
compiled *template.Template
}

// Call - executes the macro
func (m *Macro) Call(input map[string]interface{}) (interface{}, error) {
ctx := NewContext()
ctx.SQLArgs = make(map[string]interface{})
ctx.Input = input

errs := Validate(input, m.Rules)
if len(errs) > 0 {
return errs, errors.New("validation errors")
}

src, err := m.compileMacro(ctx)
if err != nil {
return nil, err
}

return m.execSQLQuery(strings.Split(src, ";"), ctx.SQLArgs)
}

// compileMacro - compile the specified macro and pass the specified ctx
func (m *Macro) compileMacro(ctx *Context) (string, error) {
if m.compiled.Lookup(m.name) == nil {
return "resource not found", errors.New("resource not found")
}

var buf bytes.Buffer

rw := io.ReadWriter(&buf)
if err := m.compiled.ExecuteTemplate(rw, m.name, ctx); err != nil {
return "", err
}

src, err := ioutil.ReadAll(rw)
if err != nil {
return "", err
}

if len(src) < 1 {
return "", errors.New("empty resource")
}

return strings.Trim(strings.TrimSpace(string(src)), ";"), nil
}

// execSQLQuery - execute the specified sql query
func (m *Macro) execSQLQuery(sqls []string, args map[string]interface{}) (interface{}, error) {
conn, err := sqlx.Open(*flagDBDriver, *flagDBDSN)
if err != nil {
return nil, err
}
defer conn.Close()

for _, sql := range sqls[0 : len(sqls)-1] {
sql = strings.TrimSpace(sql)
if "" == sql {
continue
}
if _, err := conn.NamedExec(sql, args); err != nil {
fmt.Println("....")
return nil, err
}
}

rows, err := conn.NamedQuery(sqls[len(sqls)-1], args)
if err != nil {
return nil, err
}
defer rows.Close()

ret := []map[string]interface{}{}

for rows.Next() {
row, err := m.scanSQLRow(rows)
if err != nil {
continue
}
ret = append(ret, row)
}

return interface{}(ret), nil
}

// scanSQLRow - scan a row from the specified rows
func (m *Macro) scanSQLRow(rows *sqlx.Rows) (map[string]interface{}, error) {
row := make(map[string]interface{})
if err := rows.MapScan(row); err != nil {
return nil, err
}

for k, v := range row {
if nil == v {
continue
}

switch v.(type) {
case []uint8:
v = []byte(v.([]uint8))
default:
v, _ = json.Marshal(v)
}

var d interface{}
if nil == json.Unmarshal(v.([]byte), &d) {
row[k] = d
} else {
row[k] = string(v.([]byte))
}
}

return row, nil
}

// func (m *Macro) execJS()
31 changes: 17 additions & 14 deletions main.go
Expand Up @@ -5,27 +5,30 @@ package main

import (
"fmt"
"strconv"

"github.com/alash3al/go-color"

"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
)

func main() {
e := echo.New()
e.HideBanner = true
fmt.Println(color.MagentaString(sqlerBrand))
fmt.Printf("⇨ sqler server version: %s \n", color.GreenString(sqlerVersion))
fmt.Printf("⇨ sqler used dsn is %s \n", color.GreenString(*flagDBDSN))
fmt.Printf("⇨ sqler workers count: %s \n", color.GreenString(strconv.Itoa(*flagWorkers)))
fmt.Printf("⇨ sqler resp server available at: %s \n", color.GreenString(*flagRESPListenAddr))
fmt.Printf("⇨ sqler rest server available at: %s \n", color.GreenString(*flagRESTListenAddr))

e.Pre(middleware.RemoveTrailingSlash())
e.Use(middleware.CORS())
e.Use(middleware.GzipWithConfig(middleware.GzipConfig{Level: 9}))
e.Use(middleware.Recover())
err := make(chan error)

e.GET("/", routeIndex)
e.Any("/:macro", routeExecMacro, middlewareAuthorize)
go (func() {
err <- initRESPServer()
})()

fmt.Println(color.MagentaString(sqlerBrand))
fmt.Printf("⇨ used dsn is %s \n", color.GreenString(*flagDBDSN))
go (func() {
err <- initRESTServer()
})()

color.Red(e.Start(*flagListenAddr).Error())
if err := <-err; err != nil {
color.Red(err.Error())
}
}

0 comments on commit dfc5344

Please sign in to comment.