1
0
mirror of https://github.com/rancher/norman.git synced 2025-04-27 19:15:07 +00:00

Filter resources on default fields

This change gives norman the ability to filter api requests based on the
default value of fields.  Before, it would filter on the actual data in
the resource, and then apply default values to the resource before
returning.

Issue:
https://github.com/rancher/rancher/issues/13418
This commit is contained in:
Nathan Jenan 2018-05-15 15:24:07 -07:00 committed by Darren Shepherd
parent 3499440178
commit 7fed8b17a8
4 changed files with 37 additions and 28 deletions

View File

@ -7,12 +7,12 @@ import (
"github.com/rancher/norman/types/convert"
)
func QueryFilter(opts *types.QueryOptions, data []map[string]interface{}) []map[string]interface{} {
return ApplyQueryOptions(opts, data)
func QueryFilter(opts *types.QueryOptions, schema *types.Schema, data []map[string]interface{}) []map[string]interface{} {
return ApplyQueryOptions(opts, schema, data)
}
func ApplyQueryOptions(options *types.QueryOptions, data []map[string]interface{}) []map[string]interface{} {
data = ApplyQueryConditions(options.Conditions, data)
func ApplyQueryOptions(options *types.QueryOptions, schema *types.Schema, data []map[string]interface{}) []map[string]interface{} {
data = ApplyQueryConditions(options.Conditions, schema, data)
data = ApplySort(options.Sort, data)
return ApplyPagination(options.Pagination, data)
}
@ -35,13 +35,13 @@ func ApplySort(sortOpts types.Sort, data []map[string]interface{}) []map[string]
return data
}
func ApplyQueryConditions(conditions []*types.QueryCondition, data []map[string]interface{}) []map[string]interface{} {
func ApplyQueryConditions(conditions []*types.QueryCondition, schema *types.Schema, data []map[string]interface{}) []map[string]interface{} {
var result []map[string]interface{}
outer:
for _, item := range data {
for _, condition := range conditions {
if !condition.Valid(item) {
if !condition.Valid(schema, item) {
continue outer
}
}

View File

@ -28,7 +28,7 @@ func (s *StoreWrapper) ByID(apiContext *types.APIContext, schema *types.Schema,
return apiContext.FilterObject(&types.QueryOptions{
Conditions: apiContext.SubContextAttributeProvider.Query(apiContext, schema),
}, data), nil
}, schema, data), nil
}
func (s *StoreWrapper) List(apiContext *types.APIContext, schema *types.Schema, opts *types.QueryOptions) ([]map[string]interface{}, error) {
@ -38,7 +38,7 @@ func (s *StoreWrapper) List(apiContext *types.APIContext, schema *types.Schema,
return nil, err
}
return apiContext.FilterList(opts, data), nil
return apiContext.FilterList(opts, schema, data), nil
}
func (s *StoreWrapper) Watch(apiContext *types.APIContext, schema *types.Schema, opt *types.QueryOptions) (chan map[string]interface{}, error) {
@ -50,7 +50,7 @@ func (s *StoreWrapper) Watch(apiContext *types.APIContext, schema *types.Schema,
return convert.Chan(c, func(data map[string]interface{}) map[string]interface{} {
return apiContext.FilterObject(&types.QueryOptions{
Conditions: apiContext.SubContextAttributeProvider.Query(apiContext, schema),
}, data)
}, schema, data)
}), nil
}
@ -83,7 +83,7 @@ func (s *StoreWrapper) Update(apiContext *types.APIContext, schema *types.Schema
return apiContext.FilterObject(&types.QueryOptions{
Conditions: apiContext.SubContextAttributeProvider.Query(apiContext, schema),
}, data), nil
}, schema, data), nil
}
func (s *StoreWrapper) Delete(apiContext *types.APIContext, schema *types.Schema, id string) (map[string]interface{}, error) {
@ -107,7 +107,7 @@ func validateGet(apiContext *types.APIContext, schema *types.Schema, id string)
if apiContext.Filter(&types.QueryOptions{
Conditions: apiContext.SubContextAttributeProvider.Query(apiContext, schema),
}, existing) == nil {
}, schema, existing) == nil {
return httperror.NewAPIError(httperror.NotFound, "failed to find "+id)
}

View File

@ -39,35 +39,44 @@ type QueryCondition struct {
left, right *QueryCondition
}
func (q *QueryCondition) Valid(data map[string]interface{}) bool {
func (q *QueryCondition) Valid(schema *Schema, data map[string]interface{}) bool {
switch q.conditionType {
case CondAnd:
if q.left == nil || q.right == nil {
return false
}
return q.left.Valid(data) && q.right.Valid(data)
return q.left.Valid(schema, data) && q.right.Valid(schema, data)
case CondOr:
if q.left == nil || q.right == nil {
return false
}
return q.left.Valid(data) || q.right.Valid(data)
return q.left.Valid(schema, data) || q.right.Valid(schema, data)
case CondEQ:
return q.Value == convert.ToString(data[q.Field])
return q.Value == convert.ToString(valueOrDefault(schema, data, q))
case CondNE:
return q.Value != convert.ToString(data[q.Field])
return q.Value != convert.ToString(valueOrDefault(schema, data, q))
case CondIn:
return q.Values[convert.ToString(data[q.Field])]
return q.Values[convert.ToString(valueOrDefault(schema, data, q))]
case CondNotIn:
return !q.Values[convert.ToString(data[q.Field])]
return !q.Values[convert.ToString(valueOrDefault(schema, data, q))]
case CondNotNull:
return convert.ToString(data[q.Field]) != ""
return convert.ToString(valueOrDefault(schema, data, q)) != ""
case CondNull:
return convert.ToString(data[q.Field]) == ""
return convert.ToString(valueOrDefault(schema, data, q)) == ""
}
return false
}
func valueOrDefault(schema *Schema, data map[string]interface{}, q *QueryCondition) interface{} {
value := data[q.Field]
if value == nil {
value = schema.ResourceFields[q.Field].Default
}
return value
}
func (q *QueryCondition) ToCondition() Condition {
cond := Condition{
Modifier: q.conditionType.Name,

View File

@ -49,7 +49,7 @@ type ActionHandler func(actionName string, action *Action, request *APIContext)
type RequestHandler func(request *APIContext, next RequestHandler) error
type QueryFilter func(opts *QueryOptions, data []map[string]interface{}) []map[string]interface{}
type QueryFilter func(opts *QueryOptions, schema *Schema, data []map[string]interface{}) []map[string]interface{}
type Validator func(request *APIContext, schema *Schema, data map[string]interface{}) error
@ -127,25 +127,25 @@ func (r *APIContext) WriteResponse(code int, obj interface{}) {
r.ResponseWriter.Write(r, code, obj)
}
func (r *APIContext) FilterList(opts *QueryOptions, obj []map[string]interface{}) []map[string]interface{} {
return r.QueryFilter(opts, obj)
func (r *APIContext) FilterList(opts *QueryOptions, schema *Schema, obj []map[string]interface{}) []map[string]interface{} {
return r.QueryFilter(opts, schema, obj)
}
func (r *APIContext) FilterObject(opts *QueryOptions, obj map[string]interface{}) map[string]interface{} {
func (r *APIContext) FilterObject(opts *QueryOptions, schema *Schema, obj map[string]interface{}) map[string]interface{} {
opts.Pagination = nil
result := r.QueryFilter(opts, []map[string]interface{}{obj})
result := r.QueryFilter(opts, schema, []map[string]interface{}{obj})
if len(result) == 0 {
return nil
}
return result[0]
}
func (r *APIContext) Filter(opts *QueryOptions, obj interface{}) interface{} {
func (r *APIContext) Filter(opts *QueryOptions, schema *Schema, obj interface{}) interface{} {
switch v := obj.(type) {
case []map[string]interface{}:
return r.FilterList(opts, v)
return r.FilterList(opts, schema, v)
case map[string]interface{}:
return r.FilterObject(opts, v)
return r.FilterObject(opts, schema, v)
}
return nil