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  }