/
select_criteria.go
128 lines (108 loc) · 3.82 KB
/
select_criteria.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Copyright 2016 IBM Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package amalgam8
import (
"fmt"
"reflect"
"strings"
"github.com/ant0ine/go-json-rest/rest"
"github.com/amalgam8/amalgam8/registry/store"
"github.com/amalgam8/amalgam8/registry/utils/reflection"
)
type selectCriteria struct {
criteria map[string]interface{}
filterStatus string
}
// Parses the request's query params and returns the wanted fields and their expected values
// This method is used to apply a selection filter on instances
func newSelectCriteria(r *rest.Request) (*selectCriteria, error) {
if len(r.URL.Query()) == 0 {
return &selectCriteria{}, nil
}
c := make(map[string]interface{})
var filter string
for param := range r.URL.Query() {
// fields is used for projection-type filtering
if param == "fields" {
continue
}
// check whether the param name is a valid field
if _, ok := instanceQueryValuesToFieldNames[param]; !ok {
return nil, fmt.Errorf("Field %s is not a valid field", param)
}
// put the expected value of param to the selection criteria
requestedValue := r.URL.Query().Get(param)
// convert param field's name to its actual value in the struct definition
fieldName := instanceQueryValuesToFieldNames[param]
// The status is made upper case on registration so ignore the query param case
if fieldName == "Status" {
// If the status is user defined, leave the case alone
if strings.EqualFold(requestedValue, store.Up) ||
strings.EqualFold(requestedValue, store.Starting) ||
strings.EqualFold(requestedValue, store.OutOfService) ||
strings.EqualFold(requestedValue, store.All) {
requestedValue = strings.ToUpper(requestedValue)
}
filter = requestedValue
}
// if it's a string array, split by commas
tmpServiceInstance := ServiceInstance{}
fieldType := reflect.Indirect(reflect.ValueOf(tmpServiceInstance)).FieldByName(fieldName).Type().String()
// ensure field type is string or []string
if fieldType != "[]string" && fieldType != "string" {
return nil, fmt.Errorf("Field %s is not a string or a []string but is of type %s", fieldName, fieldType)
}
if fieldType == "[]string" {
c[fieldName] = strings.Split(requestedValue, ",")
} else {
c[fieldName] = requestedValue
}
}
return &selectCriteria{criteria: c, filterStatus: filter}, nil
}
func (sc *selectCriteria) instanceFilter(si *store.ServiceInstance) bool {
var otherkey bool
if sc.criteria != nil {
// iterate over selection criteria
for key, val := range sc.criteria {
// If the query string param is Status need to support ALL or filter appropriately
if key == "Status" {
if sc.filterStatus == store.All {
continue
}
} else {
// If we're filtering on another key, include any statuses
otherkey = true
}
fits, err := reflection.StructFieldMatchesValue(si, key, val)
if err != nil || !fits {
return false
}
}
}
// Filter out those instances that are not UP if the status query string param was not set
// unless there is another param specified
if sc.filterStatus == "" && !otherkey {
// For now, handle the case where there are user defined statuses as we want to treat them as UP
switch si.Status {
case store.Starting:
return false
case store.OutOfService:
return false
case store.Up:
default:
}
}
return true
}