Skip to content

Commit 93ec461

Browse files
committed
merge
2 parents fb79250 + 16253cd commit 93ec461

File tree

3 files changed

+104
-6
lines changed

3 files changed

+104
-6
lines changed

config.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,14 @@ type Config struct {
122122
// }
123123
//
124124
FieldSep string
125+
// NameFn is a function that translates the incoming filter query field name to the struct field name.
126+
// For example, given the following query fields and their column names:
127+
//
128+
// fullName => "full_name"
129+
// httpPort => "http_port"
130+
//
131+
// By default the field name is expected to match the column.
132+
NameFn func(string, string) string
125133
// ColumnFn is the function that translate the struct field string into a table column.
126134
// For example, given the following fields and their column names:
127135
//
@@ -174,6 +182,9 @@ func (c *Config) defaults() error {
174182
if c.ColumnFn == nil {
175183
c.ColumnFn = Column
176184
}
185+
if c.NameFn == nil {
186+
c.NameFn = NameFn
187+
}
177188
if c.GetDBOp == nil {
178189
c.GetDBOp = func(o Op, _ *FieldMeta) string {
179190
return opFormat[o]

rql.go

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ type Field struct {
119119
type FieldMeta struct {
120120
// Name of the column.
121121
Name string
122+
// name of the column.
123+
Column string
122124
// Has a "sort" option in the tag.
123125
Sortable bool
124126
// Has a "filter" option in the tag.
@@ -215,6 +217,7 @@ func (p *Parser) ParseQuery(q *Query) (pr *Params, err error) {
215217
// FullName => full_name
216218
// HTTPCode => http_code
217219
func Column(s string) string {
220+
s = strings.Replace(s, ".", "_", -1)
218221
var b strings.Builder
219222
for i := 0; i < len(s); i++ {
220223
r := rune(s[i])
@@ -339,6 +342,29 @@ func GetValidateFn(f *FieldMeta) Validator {
339342
}
340343
}
341344

345+
func NameFn(str string, sep string) string {
346+
var buf bytes.Buffer
347+
348+
for i, r := range str {
349+
if r == '.' {
350+
buf.WriteRune(r)
351+
continue
352+
}
353+
354+
if unicode.IsUpper(r) {
355+
if i > 0 && str[i-1] != '_' && str[i-1] != '.' && !unicode.IsUpper(rune(str[i-1])) {
356+
buf.WriteRune('_')
357+
} else if i > 0 && unicode.IsUpper(rune(str[i-1])) && i+1 < len(str) && unicode.IsUpper(r) && unicode.IsLower(rune(str[i+1])) {
358+
buf.WriteRune('_')
359+
}
360+
}
361+
362+
buf.WriteRune(unicode.ToLower(r))
363+
}
364+
365+
return buf.String()
366+
}
367+
342368
// init initializes the parser parsing state. it scans the fields
343369
// in a breath-first-search order and for each one of the field calls parseField.
344370
func (p *Parser) init() error {
@@ -378,7 +404,8 @@ func (p *Parser) init() error {
378404
func (p *Parser) parseField(sf reflect.StructField) error {
379405
f := &Field{
380406
FieldMeta: &FieldMeta{
381-
Name: p.ColumnFn(sf.Name),
407+
Name: p.NameFn(sf.Name, p.FieldSep),
408+
Column: p.ColumnFn(sf.Name),
382409
FilterOps: make(map[string]bool),
383410
},
384411
CovertFn: valueFn,
@@ -392,7 +419,9 @@ func (p *Parser) parseField(sf reflect.StructField) error {
392419
case s == "filter":
393420
f.Filterable = true
394421
case strings.HasPrefix(opt, "column"):
395-
f.Name = strings.TrimPrefix(opt, "column=")
422+
f.Column = strings.TrimPrefix(opt, "column=")
423+
case strings.HasPrefix(opt, "name"):
424+
f.Name = strings.TrimPrefix(opt, "name=")
396425
case strings.HasPrefix(opt, "layout"):
397426
layout = strings.TrimPrefix(opt, "layout=")
398427
// if it's one of the standard layouts, like: RFC822 or Kitchen.
@@ -415,7 +444,6 @@ func (p *Parser) parseField(sf reflect.StructField) error {
415444
f.Type = indirect(sf.Type)
416445
filterOps := p.Config.GetSupportedOps(f.FieldMeta)
417446
if len(filterOps) == 0 {
418-
return fmt.Errorf("rql: field type for %q is not supported", sf.Name)
419447
}
420448
f.CovertFn = p.Config.GetConverter(f.FieldMeta)
421449
f.ValidateFn = p.Config.GetValidator(f.FieldMeta)
@@ -465,9 +493,10 @@ func (p *Parser) sort(fields []string) string {
465493
field = field[1:]
466494
}
467495

468-
expect(p.fields[field] != nil, "unrecognized key %q for sorting", field)
469-
expect(p.fields[field].Sortable, "field %q is not sortable", field)
470-
colName := p.colName(field)
496+
f := p.fields[field]
497+
expect(f != nil, "unrecognized key %q for sorting", field)
498+
expect(f.Sortable, "field %q is not sortable", field)
499+
colName := f.Column
471500
if orderBy != "" {
472501
colName += " " + orderBy
473502
}
@@ -548,6 +577,7 @@ func (p *parseState) field(f *Field, v interface{}) {
548577
p.WriteString(p.fmtOp(f, op))
549578
arg := f.CovertFn(op, *f.FieldMeta, opVal)
550579
p.values = append(p.values, arg)
580+
551581
i++
552582
}
553583
if len(terms) > 1 {

rql_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,63 @@ func TestParse(t *testing.T) {
955955
Sort: "",
956956
},
957957
},
958+
{
959+
name: "support name struct opt",
960+
conf: Config{
961+
Model: struct {
962+
SomeName string `rql:"filter,name=someName"`
963+
}{},
964+
},
965+
input: []byte(`{
966+
"filter": {
967+
"someName": {
968+
"$eq": "someName"
969+
}
970+
}
971+
}`),
972+
wantOut: &Params{
973+
Limit: 25,
974+
FilterExp: "some_name = ?",
975+
FilterArgs: []interface{}{"someName"},
976+
},
977+
},
978+
{
979+
name: "backwards compatibility to error with mismatching keys and no namefn",
980+
conf: Config{
981+
Model: struct {
982+
SomeName string `rql:"filter"`
983+
}{},
984+
},
985+
input: []byte(`{
986+
"filter": {
987+
"someName": {
988+
"$eq": "someName"
989+
}
990+
}
991+
}`),
992+
wantErr: true,
993+
},
994+
{
995+
name: "test nameFn works",
996+
conf: Config{
997+
Model: struct {
998+
SomeName string `rql:"filter"`
999+
}{},
1000+
NameFn: NameFn,
1001+
},
1002+
input: []byte(`{
1003+
"filter": {
1004+
"someName": {
1005+
"$eq": "someName"
1006+
}
1007+
}
1008+
}`),
1009+
wantOut: &Params{
1010+
Limit: 25,
1011+
FilterExp: "some_name = ?",
1012+
FilterArgs: []interface{}{"someName"},
1013+
},
1014+
},
9581015
}
9591016
for _, tt := range tests {
9601017
t.Run(tt.name, func(t *testing.T) {

0 commit comments

Comments
 (0)