Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
notes
*.todo
*.vscode
1 change: 1 addition & 0 deletions integration/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ FROM golang
RUN go get github.com/mailru/easyjson
RUN go get github.com/jinzhu/gorm
RUN go get github.com/go-sql-driver/mysql
RUN go get gopkg.in/mgo.v2/bson
1 change: 0 additions & 1 deletion integration/rql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"time"

"github.com/a8m/rql"
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
)

Expand Down
61 changes: 61 additions & 0 deletions rql.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ import (
"sync"
"time"
"unicode"

"gopkg.in/mgo.v2/bson"
)

//go:generate easyjson -omit_empty -disallow_unknown_fields -snake_case rql.go

// Query is the decoded result of the user input.
//easyjson:json
// TODO: Need to add Selector for Mongodb operations.
type Query struct {
// Limit must be greater than 0 and less than or equal to `LimitMaxValue`.
Limit int `json:"limit,omitempty"`
Expand Down Expand Up @@ -82,6 +85,25 @@ type Params struct {
FilterArgs []interface{}
}

// MgoParams contains details for mongodb query operations.
type MgoParams struct {
//Limit represents the number of documents to be retruned.
Limit int

//Skip represents the number of documents to be skiped.
Skip int

// Sort the documents.
// example: "name,-age".
Sort string

//Select the required keys and values for the document.
Select bson.M

//Find the documents based on the query.
Find bson.M
}

// ParseError is type of error returned when there is a parsing problem.
type ParseError struct {
msg string
Expand Down Expand Up @@ -150,17 +172,20 @@ func (p *Parser) Parse(b []byte) (pr *Params, err error) {
pr = nil
}
}()

q := new(Query)
must(q.UnmarshalJSON(b), "decoding buffer to Query")
pr = &Params{
Limit: p.DefaultLimit,
}

expect(q.Offset >= 0, "offset must be greater than or equal to 0")
pr.Offset = q.Offset
if q.Limit != 0 {
expect(q.Limit > 0 && q.Limit <= p.LimitMaxValue, "limit must be greater than 0 and less than or equal to %d", p.Model)
pr.Limit = q.Limit
}

ps := p.newParseState()
ps.and(q.Filter)
pr.FilterExp = ps.String()
Expand All @@ -170,6 +195,42 @@ func (p *Parser) Parse(b []byte) (pr *Params, err error) {
return
}

// ParseMgo parses the given buffer into a Param object. It returns an error
// if the JSON is invalid, or its values don't follow the schema of rql.
func (p *Parser) ParseMgo(b []byte) (pr *MgoParams, err error) {
defer func() {
if e := recover(); e != nil {
perr, ok := e.(ParseError)
if !ok {
panic(e)
}
err = perr
pr = nil
}
}()

q := new(Query)
must(q.UnmarshalJSON(b), "decoding buffer to Query")
pr = &MgoParams{
Limit: p.DefaultLimit,
}

expect(q.Offset >= 0, "offset must be greater than or equal to 0")
pr.Skip = q.Offset
if q.Limit != 0 {
expect(q.Limit > 0 && q.Limit <= p.LimitMaxValue, "limit must be greater than 0 and less than or equal to %d", p.Model)
pr.Limit = q.Limit
}

if q.Filter != nil {
expect(len(q.Filter) > 0, "filter shouldn't be empty")
}

// TODO: Need to perform type converstion.
pr.Find = q.Filter
return
}

// Column is the default function that converts field name into a database column.
// It used to convert the struct fields into their database names. For example:
//
Expand Down
89 changes: 89 additions & 0 deletions rql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package rql

import (
"database/sql"
"log"
"reflect"
"strings"
"testing"
"time"

"gopkg.in/mgo.v2/bson"
)

func TestInit(t *testing.T) {
Expand Down Expand Up @@ -788,6 +791,57 @@ func TestParse(t *testing.T) {
}
}

func TestParseMgo(t *testing.T) {
tests := []struct {
name string
conf Config
input []byte
wantErr bool
wantOut *MgoParams
}{
{
//BUG: need to perform type converison, want int but got float64.
name: "simple test",
conf: Config{
Model: new(struct {
Age int `rql:"filter"`
Name string `rql:"filter"`
}),
DefaultLimit: 25,
},
input: []byte(`{
"filter": {
"name": "foo",
"age": 12
}
}`),
wantOut: &MgoParams{
Limit: 25,
Find: bson.M{
"name": "foo",
"age": 12,
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p, err := NewParser(tt.conf)
if err != nil {
t.Fatalf("failed to build parser: %v", err)
}

_, err = p.ParseMgo(tt.input)
if tt.wantErr != (err != nil) {
t.Fatalf("want: %v\ngot:%v\nerr: %v", tt.wantErr, err != nil, err)
}

// assertMgoParams(t, out, tt.wantOut)
})
}
}

// AssertQueryEqual tests if two query input are equal.
// TODO: improve this in the future.
func assertParams(t *testing.T, got *Params, want *Params) {
Expand All @@ -811,6 +865,41 @@ func assertParams(t *testing.T, got *Params, want *Params) {
}
}

// assertMgoParasm tests if two query input are equal for Mongodb query output.
// TODO: need to add Select & Find.
func assertMgoParams(t *testing.T, got *MgoParams, want *MgoParams) {
if got == nil && want == nil {
return
}

if got.Limit != want.Limit {
t.Fatalf("limit: got: %v want %v", got.Limit, want.Limit)
}

if got.Skip != want.Skip {
t.Fatalf("skip: got: %v want %v", got.Limit, want.Limit)
}

if got.Sort != want.Sort {
t.Fatalf("sort: got: %q want %q", got.Sort, want.Sort)
}

for wantKey, wantVal := range want.Select {
if gotVal, ok := got.Select[wantKey]; !ok || gotVal != wantVal {
t.Fatalf("select: got: %v want %v", gotVal, wantVal)
}
}

for wantKey, wantVal := range want.Find {
if gotVal, ok := got.Find[wantKey]; !ok || gotVal != wantVal {
log.Print(reflect.ValueOf(gotVal).Type())
log.Print(reflect.ValueOf(wantVal).Type())
t.Fatalf("find: got: %v want %v", gotVal, wantVal)
}
}

}

func equalArgs(a, b []interface{}) bool {
seen := make([]bool, len(b))
for _, arg1 := range a {
Expand Down