Skip to content

Commit

Permalink
feat: adds the ability to control SelectBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-poliushkin committed Dec 24, 2021
1 parent f382a96 commit 420a698
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 12 deletions.
33 changes: 22 additions & 11 deletions builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

type Builder interface {
// Build builds sql query
Build(ctx context.Context, query *qparser.Query) (Sqlizer, error)
Build(ctx context.Context, query *qparser.Query, sb ...*SelectBuilder) (*SelectBuilder, error)
}

// Extension is used in order to extend a builder
Expand Down Expand Up @@ -54,9 +54,20 @@ func NewResourceSelectBuilder(
}

// Build builds sql query which depends on the applied options
func (s *ResourceSelectBuilder) Build(ctx context.Context, query *qparser.Query) (Sqlizer, error) {
var selectFields []string
sb := new(SelectBuilder)
func (s *ResourceSelectBuilder) Build(
ctx context.Context,
query *qparser.Query,
sb ...*SelectBuilder,
) (*SelectBuilder, error) {
var (
selectFields []string
b *SelectBuilder
)
if len(sb) > 0 {
b = sb[0]
} else {
b = new(SelectBuilder)
}
if fields, ok := query.Fields.FieldsByResource(s.resourceName); ok {
fields, err := s.translator(fields)
if err != nil {
Expand All @@ -71,15 +82,15 @@ func (s *ResourceSelectBuilder) Build(ctx context.Context, query *qparser.Query)
return nil, fmt.Errorf("field %q not allowed for selection criteria", field)
}
}
sb.Select(selectFields).From(s.resourceName)
b.Select(selectFields).From(s.resourceName)
conditions, err := s.retrieveFilterConditions(query)
if err != nil {
return nil, err
}
if len(conditions) == 0 {
sb.Where(alwaysTrue)
if len(conditions) == 0 && len(b.WhereParts) == 0 {
b.Where(alwaysTrue)
} else {
sb.Where(conditions...)
b.Where(conditions...)
}
sortList := make([]qparser.Sort, len(query.Sort))
sortFields := make([]string, len(query.Sort))
Expand All @@ -105,14 +116,14 @@ func (s *ResourceSelectBuilder) Build(ctx context.Context, query *qparser.Query)
sortList[i].FieldName = sortFields[i]
}
if len(sortList) > 0 {
sb.OrderBy(OrderBy(sortList))
b.OrderBy(OrderBy(sortList))
}
for _, extension := range s.extensions {
if err = extension(ctx, query, sb); err != nil {
if err = extension(ctx, query, b); err != nil {
return nil, err
}
}
return sb, nil
return b, nil
}

func (s *ResourceSelectBuilder) retrieveFilterConditions(query *qparser.Query) ([]Sqlizer, error) {
Expand Down
17 changes: 16 additions & 1 deletion builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type resourceBuilderTest struct {
sql string
args []interface{}
expectedErr bool
sb func() *SelectBuilder
}

const (
Expand Down Expand Up @@ -85,6 +86,16 @@ var resourceBuilderTests = []resourceBuilderTest{
query: fmt.Sprintf("fields[%s]=updatedAt", resourceName),
expectedErr: true,
},
{
title: "With extra params",
query: fmt.Sprintf("fields[%s]=%s,%s", resourceName, resourceFieldID, resourceFieldTitle),
sql: fmt.Sprintf("SELECT %s, %s FROM %s WHERE id = ?", resourceFieldID, resourceFieldTitle, resourceName),
args: []interface{}{"1"},
sb: func() *SelectBuilder {
sb := new(SelectBuilder)
return sb.Where(&RawSqlWithArgs{"id = ?", []interface{}{"1"}})
},
},
}

func TestNewResourceSelectBuilder(t *testing.T) {
Expand Down Expand Up @@ -149,7 +160,11 @@ func TestNewResourceSelectBuilder(t *testing.T) {
if err != nil {
t.Fatalf("%sqparser.ParseQuery(%q) returned unexpected error: %s", meta, tt.query, err)
}
sqlizer, err := builder.Build(ctx, query)
var sb []*SelectBuilder
if tt.sb != nil {
sb = []*SelectBuilder{tt.sb()}
}
sqlizer, err := builder.Build(ctx, query, sb...)
if !tt.expectedErr && err != nil {
t.Errorf("%sunexpected error %s", meta, err)
continue
Expand Down
File renamed without changes.
66 changes: 66 additions & 0 deletions examples/custom_params/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package main

import (
"context"
"fmt"
"github.com/velmie/qparser"
"log"

"github.com/velmie/q2sql"
"github.com/velmie/q2sql/condition"
"github.com/velmie/q2sql/extension"
)

func main() {
translator := q2sql.MapTranslator(map[string]string{
"id": "id",
"title": "title",
"body": "body",
"author": "author",
"createdAt": "created_at",
})

allowedConditionsByField := q2sql.AllowedConditions{
"id": []string{condition.NameIn},
}
builder := q2sql.NewResourceSelectBuilder(
"articles",
translator,
q2sql.AllowSelectFields([]string{"id", "title", "author"}),
q2sql.AllowFiltering(
allowedConditionsByField,
condition.DefaultConditionMap,
q2sql.DefaultFilterExpressionParser,
),
q2sql.AllowSortingByFields([]string{"created_at", "title"}),
q2sql.Extend(extension.LimitOffsetPagination(extension.Unlimited, extension.Unlimited)),
)

qstr := "?fields[articles]=id,title,author&filter[id]=in:1,2,3,4,5&sort=-createdAt,title&page[limit]=100"
query, _ := qparser.ParseQuery(qstr)

// It's possible to pass select builder
sb := new(q2sql.SelectBuilder)
// and define custom conditions
sb.Where(&q2sql.Eq{"author", "Alan Turing"})

// not necessary to declare new variable since the "sb" already points to the select builder
_, err := builder.Build(context.Background(), query, sb)
if err != nil {
log.Fatal("failed to build query", err)
}
// or rewrite some parts, for example the line below clears sorting
sb.OrderByParts = nil

sqlStr, args, err := sb.ToSql()
if err != nil {
log.Fatal("failed to build SQL query", err)
}
fmt.Println(sqlStr)
fmt.Println(args)
// prints
/*
SELECT id, title, author FROM articles WHERE author = ? AND id IN (?,?,?,?,?) LIMIT 100
[Alan Turing 1 2 3 4 5]
*/
}
11 changes: 11 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,14 @@ The Extension accesses * q2sql.SelectBuilder and can use it to modify the result
[1 2 3 4 5]
*/
```

In order to apply custom conditions you may pass select builder to the resource select builder.

```go
sb := new(q2sql.SelectBuilder)
sb.Where(&q2sql.Eq{"author", "Alan Turing"})

_, err := builder.Build(context.Background(), query, sb)
...

```

0 comments on commit 420a698

Please sign in to comment.