Add option to include null results when filtering
continuous-integration/drone/pr Build is failing Details

This commit is contained in:
kolaente 2020-06-27 18:10:34 +02:00
parent e4bb42b46c
commit cc688f0594
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
3 changed files with 88 additions and 21 deletions

View File

@ -45,6 +45,8 @@ type TaskCollection struct {
FilterComparatorArr []string `query:"filter_comparator[]"`
// The way all filter conditions are concatenated together, can be either "and" or "or".,
FilterConcat string `query:"filter_concat"`
// If set to true, the result will also include null values
FilterIncludeNulls bool `quiery:"filter_include_nulls"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
@ -93,6 +95,7 @@ func validateTaskField(fieldName string) error {
// @Param filter_value query string false "The value to filter for."
// @Param filter_comparator query string false "The comparator to use for a filter. Available values are `equals`, `greater`, `greater_equals`, `less` and `less_equals`. Defaults to `equals`"
// @Param filter_concat query string false "The concatinator to use for filters. Available values are `and` or `or`. Defaults to `or`."
// @Param filter_include_nulls query string false "If set to true the result will include filtered fields whose value is set to `null`. Available values are `true` or `false`. Defaults to `false`."
// @Security JWTKeyAuth
// @Success 200 {array} models.Task "The tasks"
// @Failure 500 {object} models.Message "Internal error"
@ -127,11 +130,12 @@ func (tf *TaskCollection) ReadAll(a web.Auth, search string, page int, perPage i
}
taskopts := &taskOptions{
search: search,
page: page,
perPage: perPage,
sortby: sort,
filterConcat: taskFilterConcatinator(tf.FilterConcat),
search: search,
page: page,
perPage: perPage,
sortby: sort,
filterConcat: taskFilterConcatinator(tf.FilterConcat),
filterIncludeNulls: tf.FilterIncludeNulls,
}
taskopts.filters, err = getTaskFiltersByCollections(tf)

View File

@ -559,9 +559,10 @@ func TestTaskCollection_ReadAll(t *testing.T) {
SortBy []string // Is a string, since this is the place where a query string comes from the user
OrderBy []string
FilterBy []string
FilterValue []string
FilterComparator []string
FilterBy []string
FilterValue []string
FilterComparator []string
FilterIncludeNulls bool
CRUDable web.CRUDable
Rights web.Rights
@ -791,6 +792,50 @@ func TestTaskCollection_ReadAll(t *testing.T) {
},
wantErr: false,
},
{
name: "range with nulls",
fields: fields{
FilterBy: []string{"start_date", "end_date"},
FilterValue: []string{"2018-12-11T03:46:40+00:00", "2018-12-13T11:20:01+00:00"},
FilterComparator: []string{"greater", "less"},
FilterIncludeNulls: true,
},
args: defaultArgs,
want: []*Task{
task1, // has nil dates
task2, // has nil dates
task3, // has nil dates
task4, // has nil dates
task5, // has nil dates
task6, // has nil dates
task7,
task8,
task9,
task10, // has nil dates
task11, // has nil dates
task12, // has nil dates
task15, // has nil dates
task16, // has nil dates
task17, // has nil dates
task18, // has nil dates
task19, // has nil dates
task20, // has nil dates
task21, // has nil dates
task22, // has nil dates
task23, // has nil dates
task24, // has nil dates
task25, // has nil dates
task26, // has nil dates
task27, // has nil dates
task28, // has nil dates
task29, // has nil dates
task30, // has nil dates
task31, // has nil dates
task32, // has nil dates
task33, // has nil dates
},
wantErr: false,
},
}
for _, tt := range tests {
@ -802,9 +847,10 @@ func TestTaskCollection_ReadAll(t *testing.T) {
SortBy: tt.fields.SortBy,
OrderBy: tt.fields.OrderBy,
FilterBy: tt.fields.FilterBy,
FilterValue: tt.fields.FilterValue,
FilterComparator: tt.fields.FilterComparator,
FilterBy: tt.fields.FilterBy,
FilterValue: tt.fields.FilterValue,
FilterComparator: tt.fields.FilterComparator,
FilterIncludeNulls: tt.fields.FilterIncludeNulls,
CRUDable: tt.fields.CRUDable,
Rights: tt.fields.Rights,

View File

@ -133,12 +133,13 @@ const (
)
type taskOptions struct {
search string
page int
perPage int
sortby []*sortParam
filters []*taskFilter
filterConcat taskFilterConcatinator
search string
page int
perPage int
sortby []*sortParam
filters []*taskFilter
filterConcat taskFilterConcatinator
filterIncludeNulls bool
}
// ReadAll is a dummy function to still have that endpoint documented
@ -227,13 +228,29 @@ func getRawTasksForLists(lists []*List, opts *taskOptions) (tasks []*Task, resul
case taskFilterComparatorNotEquals:
filters = append(filters, &builder.Neq{f.field: f.value})
case taskFilterComparatorGreater:
filters = append(filters, builder.Or(&builder.Gt{f.field: f.value}, &builder.IsNull{f.field}))
if opts.filterIncludeNulls {
filters = append(filters, builder.Or(&builder.Gt{f.field: f.value}, &builder.IsNull{f.field}))
} else {
filters = append(filters, &builder.Gt{f.field: f.value})
}
case taskFilterComparatorGreateEquals:
filters = append(filters, builder.Or(&builder.Gte{f.field: f.value}, &builder.IsNull{f.field}))
if opts.filterIncludeNulls {
filters = append(filters, builder.Or(&builder.Gte{f.field: f.value}, &builder.IsNull{f.field}))
} else {
filters = append(filters, &builder.Gte{f.field: f.value})
}
case taskFilterComparatorLess:
filters = append(filters, builder.Or(&builder.Lt{f.field: f.value}, &builder.IsNull{f.field}))
if opts.filterIncludeNulls {
filters = append(filters, builder.Or(&builder.Lt{f.field: f.value}, &builder.IsNull{f.field}))
} else {
filters = append(filters, &builder.Lt{f.field: f.value})
}
case taskFilterComparatorLessEquals:
filters = append(filters, builder.Or(&builder.Lte{f.field: f.value}, &builder.IsNull{f.field}))
if opts.filterIncludeNulls {
filters = append(filters, builder.Or(&builder.Lte{f.field: f.value}, &builder.IsNull{f.field}))
} else {
filters = append(filters, &builder.Lte{f.field: f.value})
}
}
}