Skip to content

Commit

Permalink
test query parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
yvesago committed Nov 6, 2016
1 parent 2f6ba37 commit 0b08e55
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 113 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ Sample :
## TODO
Refactoring to test query parsing
Sample with config
## Licence
Expand Down
31 changes: 1 addition & 30 deletions agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"github.com/gin-gonic/gin"
"gopkg.in/gorp.v1"
//"log"
"regexp"
"strconv"
"time"
)
Expand Down Expand Up @@ -54,35 +53,7 @@ func GetAgents(c *gin.Context) {
// receive : map[_filters:[{"q":"wx"}] _sortField:[id] ...
q := c.Request.URL.Query()
//log.Println(q)
if q["_filters"] != nil {
re := regexp.MustCompile("{\"([a-zA-Z0-9_]+?)\":\"([a-zA-Z0-9_. ]+?)\"}")
r := re.FindStringSubmatch(q["_filters"][0])
// TODO: special col name for all fields via reflections
col := r[1]
search := r[2]
if col != "" && search != "" {
query = query + " WHERE " + col + " LIKE \"%" + search + "%\" "
}
}
if q["_sortField"] != nil && q["_sortDir"] != nil {
sortField := q["_sortField"][0]
// prevent SQLi
valid := regexp.MustCompile("^[A-Za-z0-9_]+$")
if !valid.MatchString(sortField) {
sortField = ""
}
if sortField == "created" || sortField == "updated" { // XXX trick for sqlite
sortField = "datetime(" + sortField + ")"
}
sortOrder := q["_sortDir"][0]
if sortOrder != "ASC" {
sortOrder = "DESC"
}
// _page, _perPage, _sortDir, _sortField
if sortField != "" {
query = query + " ORDER BY " + sortField + " " + sortOrder
}
}
query = query + ParseQuery(q)
//log.Println(" -- " + query)

var agents []Agent
Expand Down
40 changes: 24 additions & 16 deletions agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"log"
"net/http"
"net/http/httptest"
"net/url"
"testing"
)

Expand All @@ -19,21 +20,19 @@ func TestAgent(t *testing.T) {
router := gin.New()
router.Use(Database("_test.sqlite3"))

var url = "/api/v1/agents"
router.POST(url, PostAgent)
router.GET(url, GetAgents)
router.GET(url+"/:id", GetAgent)
router.DELETE(url+"/:id", DeleteAgent)
router.PUT(url+"/:id", UpdateAgent)
var urla = "/api/v1/agents"
router.POST(urla, PostAgent)
router.GET(urla, GetAgents)
router.GET(urla+"/:id", GetAgent)
router.DELETE(urla+"/:id", DeleteAgent)
router.PUT(urla+"/:id", UpdateAgent)

// Add
log.Println("= http POST Agent")
var a = Agent{Name: "Name test", IP: "Ip test"}
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(a)
//b, _ := json.Marshal(a)
//var jsonStr = []byte(`{"name":"Name test","ip":"Ip test"}`)
req, err := http.NewRequest("POST", url, b)
req, err := http.NewRequest("POST", urla, b)
req.Header.Set("Content-Type", "application/json")
if err != nil {
fmt.Println(err)
Expand All @@ -47,15 +46,15 @@ func TestAgent(t *testing.T) {
log.Println("= http POST more Agent")
var a2 = Agent{Name: "Name test2", IP: "Ip test2"}
json.NewEncoder(b).Encode(a2)
req, err = http.NewRequest("POST", url, b)
req, err = http.NewRequest("POST", urla, b)
req.Header.Set("Content-Type", "application/json")
resp = httptest.NewRecorder()
router.ServeHTTP(resp, req)
assert.Equal(t, 201, resp.Code, "http POST success")

// Get all
log.Println("= http GET all Agents")
req, err = http.NewRequest("GET", url, nil)
req, err = http.NewRequest("GET", urla, nil)
if err != nil {
fmt.Println(err)
}
Expand All @@ -68,10 +67,19 @@ func TestAgent(t *testing.T) {
//fmt.Println(len(as))
assert.Equal(t, 2, len(as), "2 results")

log.Println("= Test parsing query")
s := "http://127.0.0.1:8080/api?_filters={\"name\":\"t\"}&_sortDir=ASC&_sortField=created"
u, _ := url.Parse(s)
q, _ := url.ParseQuery(u.RawQuery)
//fmt.Println(q)
query := ParseQuery(q)
//fmt.Println(query)
assert.Equal(t, " WHERE name LIKE \"%t%\" ORDER BY datetime(created) ASC", query, "Parse query")

// Get one
log.Println("= http GET one Agent")
var a1 Agent
req, err = http.NewRequest("GET", url+"/1", nil)
req, err = http.NewRequest("GET", urla+"/1", nil)
if err != nil {
fmt.Println(err)
}
Expand All @@ -85,7 +93,7 @@ func TestAgent(t *testing.T) {

// Delete one
log.Println("= http DELETE one Agent")
req, err = http.NewRequest("DELETE", url+"/1", nil)
req, err = http.NewRequest("DELETE", urla+"/1", nil)
if err != nil {
fmt.Println(err)
}
Expand All @@ -94,7 +102,7 @@ func TestAgent(t *testing.T) {
assert.Equal(t, 200, resp.Code, "http DELETE success")
//fmt.Println(a1.Name)
//fmt.Println(resp.Body)
req, err = http.NewRequest("GET", url, nil)
req, err = http.NewRequest("GET", urla, nil)
if err != nil {
fmt.Println(err)
}
Expand All @@ -111,14 +119,14 @@ func TestAgent(t *testing.T) {
//var a4 = Agent{Name: "Name test2 updated", IP: "Ip test2"}
a2.Name = "Name test2 updated"
json.NewEncoder(b).Encode(a2)
req, err = http.NewRequest("PUT", url+"/2", b)
req, err = http.NewRequest("PUT", urla+"/2", b)
req.Header.Set("Content-Type", "application/json")
resp = httptest.NewRecorder()
router.ServeHTTP(resp, req)
assert.Equal(t, 200, resp.Code, "http PUT success")

var a3 Agent
req, err = http.NewRequest("GET", url+"/2", nil)
req, err = http.NewRequest("GET", urla+"/2", nil)
if err != nil {
fmt.Println(err)
}
Expand Down
41 changes: 38 additions & 3 deletions repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package models

import (
"database/sql"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
_ "github.com/mattn/go-sqlite3"
"gopkg.in/gorp.v1"
"log"
"regexp"
)

// gin Middlware to select database
Expand All @@ -18,12 +19,12 @@ func Database(connString string) gin.HandlerFunc {
}

func InitDb(dbName string) *gorp.DbMap {
// XXX fix database type
// XXX fix database type
db, err := sql.Open("sqlite3", dbName)
checkErr(err, "sql.Open failed")
//dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}
dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
// XXX fix tables names
// XXX fix tables names
dbmap.AddTableWithName(Agent{}, "Agent").SetKeys(true, "Id")
dbmap.AddTableWithName(User{}, "User").SetKeys(true, "Id")
err = dbmap.CreateTablesIfNotExists()
Expand All @@ -32,6 +33,40 @@ func InitDb(dbName string) *gorp.DbMap {
return dbmap
}

func ParseQuery(q map[string][]string) string {
query := " "
if q["_filters"] != nil {
re := regexp.MustCompile("{\"([a-zA-Z0-9_]+?)\":\"([a-zA-Z0-9_. ]+?)\"}")
r := re.FindStringSubmatch(q["_filters"][0])
// TODO: special col name for all fields via reflections
col := r[1]
search := r[2]
if col != "" && search != "" {
query = query + " WHERE " + col + " LIKE \"%" + search + "%\" "
}
}
if q["_sortField"] != nil && q["_sortDir"] != nil {
sortField := q["_sortField"][0]
// prevent SQLi
valid := regexp.MustCompile("^[A-Za-z0-9_]+$")
if !valid.MatchString(sortField) {
sortField = ""
}
if sortField == "created" || sortField == "updated" { // XXX trick for sqlite
sortField = "datetime(" + sortField + ")"
}
sortOrder := q["_sortDir"][0]
if sortOrder != "ASC" {
sortOrder = "DESC"
}
//TODO _page, _perPage, _sortDir, _sortField
if sortField != "" {
query = query + " ORDER BY " + sortField + " " + sortOrder
}
}
return query
}

func checkErr(err error, msg string) {
if err != nil {
log.Fatalln(msg, err)
Expand Down
62 changes: 16 additions & 46 deletions user.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"github.com/gin-gonic/gin"
"gopkg.in/gorp.v1"
//"log"
"regexp"
"strconv"
"time"
)
Expand All @@ -21,14 +20,14 @@ or remove sqlite tricks

// XXX custom struct name and fields
type User struct {
Id int64 `db:"id" json:"id"`
Name string `db:"name" json:"name"`
Email string `db:"email" json:"mail"`
Status string `db:"status" json:"status"`
Comment string `db:"name:comment, size:16384" json:"comment"`
Pass string `db:"pass" json:"pass"`
Created time.Time `db:"created" json:"created"` // or int64
Updated time.Time `db:"updated" json:"updated"`
Id int64 `db:"id" json:"id"`
Name string `db:"name" json:"name"`
Email string `db:"email" json:"mail"`
Status string `db:"status" json:"status"`
Comment string `db:"comment, size:16384" json:"comment"`
Pass string `db:"pass" json:"pass"`
Created time.Time `db:"created" json:"created"` // or int64
Updated time.Time `db:"updated" json:"updated"`
}

// Hooks : PreInsert and PreUpdate
Expand All @@ -51,38 +50,9 @@ func GetUsers(c *gin.Context) {
query := "SELECT * FROM user"

// Parse query string
// receive : map[_filters:[{"q":"wx"}] _sortField:[id] ...
q := c.Request.URL.Query()
//log.Println(q)
if q["_filters"] != nil {
re := regexp.MustCompile("{\"([a-zA-Z0-9_]+?)\":\"([a-zA-Z0-9_. ]+?)\"}")
r := re.FindStringSubmatch(q["_filters"][0])
// TODO: special col name for all fields via reflections
col := r[1]
search := r[2]
if col != "" && search != "" {
query = query + " WHERE " + col + " LIKE \"%" + search + "%\" "
}
}
if q["_sortField"] != nil && q["_sortDir"] != nil {
sortField := q["_sortField"][0]
// prevent SQLi
valid := regexp.MustCompile("^[A-Za-z0-9_]+$")
if !valid.MatchString(sortField) {
sortField = ""
}
if sortField == "created" || sortField == "updated" { // XXX trick for sqlite
sortField = "datetime(" + sortField + ")"
}
sortOrder := q["_sortDir"][0]
if sortOrder != "ASC" {
sortOrder = "DESC"
}
// _page, _perPage, _sortDir, _sortField
if sortField != "" {
query = query + " ORDER BY " + sortField + " " + sortOrder
}
}
query = query + ParseQuery(q)
//log.Println(" -- " + query)

var users []User
Expand Down Expand Up @@ -152,13 +122,13 @@ func UpdateUser(c *gin.Context) {
//TODO : find fields via reflections
//XXX custom fields mapping
user := User{
Id: user_id,
Pass: json.Pass,
Name: json.Name,
Email: json.Email,
Status: json.Status,
Comment: json.Comment,
Created: user.Created, //user read from previous select
Id: user_id,
Pass: json.Pass,
Name: json.Name,
Email: json.Email,
Status: json.Status,
Comment: json.Comment,
Created: user.Created, //user read from previous select
}

if user.Name != "" { // XXX Check mandatory fields
Expand Down
Loading

0 comments on commit 0b08e55

Please sign in to comment.