github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/apiserver/restfilter.go (about) 1 // Copyright © 2021 Kaleido, Inc. 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package apiserver 18 19 import ( 20 "net/http" 21 "net/url" 22 "sort" 23 "strconv" 24 "strings" 25 26 "github.com/kaleido-io/firefly/internal/i18n" 27 "github.com/kaleido-io/firefly/internal/log" 28 "github.com/kaleido-io/firefly/pkg/database" 29 ) 30 31 var ( 32 // Defaults set with config 33 defaultFilterLimit uint64 34 maxFilterLimit uint64 35 maxFilterSkip uint64 36 ) 37 38 func getValues(values url.Values, key string) (results []string) { 39 for queryName, queryValues := range values { 40 // We choose to be case insensitive for our filters, so protocolID and protocolid can be used interchangeably 41 if strings.EqualFold(queryName, key) { 42 results = append(results, queryValues...) 43 } 44 } 45 return results 46 } 47 48 func buildFilter(req *http.Request, ff database.QueryFactory) (database.AndFilter, error) { 49 ctx := req.Context() 50 log.L(ctx).Debugf("Query: %s", req.URL.RawQuery) 51 fb := ff.NewFilterLimit(ctx, defaultFilterLimit) 52 possibleFields := fb.Fields() 53 sort.Strings(possibleFields) 54 filter := fb.And() 55 _ = req.ParseForm() 56 for _, field := range possibleFields { 57 values := getValues(req.Form, field) 58 if len(values) == 1 { 59 filter.Condition(getCondition(fb, field, values[0])) 60 } else if len(values) > 0 { 61 sort.Strings(values) 62 fs := make([]database.Filter, len(values)) 63 for i, value := range values { 64 fs[i] = getCondition(fb, field, value) 65 } 66 filter.Condition(fb.Or(fs...)) 67 } 68 } 69 skipVals := getValues(req.Form, "skip") 70 if len(skipVals) > 0 { 71 s, _ := strconv.ParseUint(skipVals[0], 10, 64) 72 if maxFilterSkip != 0 && s > maxFilterSkip { 73 return nil, i18n.NewError(req.Context(), i18n.MsgMaxFilterSkip, maxFilterSkip) 74 } 75 filter.Skip(s) 76 } 77 limitVals := getValues(req.Form, "limit") 78 if len(limitVals) > 0 { 79 l, _ := strconv.ParseUint(limitVals[0], 10, 64) 80 if maxFilterLimit != 0 && l > maxFilterLimit { 81 return nil, i18n.NewError(req.Context(), i18n.MsgMaxFilterLimit, maxFilterLimit) 82 } 83 filter.Limit(l) 84 } 85 sortVals := getValues(req.Form, "sort") 86 for _, sv := range sortVals { 87 subSortVals := strings.Split(sv, ",") 88 for _, ssv := range subSortVals { 89 ssv = strings.TrimSpace(ssv) 90 if ssv != "" { 91 filter.Sort(ssv) 92 } 93 } 94 } 95 descendingVals := getValues(req.Form, "descending") 96 if len(descendingVals) > 0 && (descendingVals[0] == "" || strings.EqualFold(descendingVals[0], "true")) { 97 filter.Descending() 98 } 99 return filter, nil 100 } 101 102 func getCondition(fb database.FilterBuilder, field, value string) database.Filter { 103 switch { 104 case strings.HasPrefix(value, ">="): 105 return fb.Gte(field, value[2:]) 106 case strings.HasPrefix(value, "<="): 107 return fb.Lte(field, value[2:]) 108 case strings.HasPrefix(value, ">"): 109 return fb.Gt(field, value[1:]) 110 case strings.HasPrefix(value, "<"): 111 return fb.Lt(field, value[1:]) 112 case strings.HasPrefix(value, "@"): 113 return fb.Contains(field, value[1:]) 114 case strings.HasPrefix(value, "^"): 115 return fb.IContains(field, value[1:]) 116 case strings.HasPrefix(value, "!@"): 117 return fb.NotContains(field, value[2:]) 118 case strings.HasPrefix(value, "!^"): 119 return fb.NotIContains(field, value[2:]) 120 case strings.HasPrefix(value, "!"): 121 return fb.Neq(field, value[1:]) 122 default: 123 return fb.Eq(field, value) 124 } 125 }