-
Notifications
You must be signed in to change notification settings - Fork 18
/
selector.go
229 lines (205 loc) · 6.3 KB
/
selector.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors.
//
// SPDX-License-Identifier: Apache-2.0
package selector
import (
"encoding/json"
"fmt"
"regexp"
"github.com/ghodss/yaml"
"github.com/xeipuuv/gojsonschema"
)
// Interface defines a selector interface that
// matches a map of string to string.
type Interface interface {
Match(obj map[string]string) (bool, error)
}
// SelectorFunc describes a function that can be used as selector interface.
type SelectorFunc func(obj map[string]string) (bool, error)
func (s SelectorFunc) Match(obj map[string]string) (bool, error) {
return s(obj)
}
// MatchSelectors checks whether all selectors matches the given obj.
func MatchSelectors(obj map[string]string, selectors ...Interface) (bool, error) {
for _, sel := range selectors {
ok, err := sel.Match(obj)
if err != nil {
return false, err
}
if !ok {
return false, nil
}
}
return true, nil
}
// DefaultSelector defines the selector for the identity of an object.
// The default selector is a map of identity key to selector
// All keys are validated against the given selector.
//
// Valid selectors are
// - raw string value: identity value is compared to the selector value
// - array of strings: or-operator identity value must be one of the defined strings in the array.
type DefaultSelector map[string]interface{}
var _ Interface = DefaultSelector{}
// ParseDefaultSelector creates a Identity selector from a
// - json encoded selector
// - map[string]Selector
//
// A selector can be
// - a string: the value is directly matched
// - a array of strings: one selector in the array must match.
func ParseDefaultSelector(value interface{}) (DefaultSelector, error) {
switch v := value.(type) {
case map[string]interface{}:
return v, nil
case string:
selector := DefaultSelector{}
if err := json.Unmarshal([]byte(v), &selector); err != nil {
return nil, err
}
return selector, nil
default:
return nil, fmt.Errorf("unknown type %T", value)
}
}
func (is DefaultSelector) Match(obj map[string]string) (bool, error) {
for key, selector := range is {
value, ok := obj[key]
if !ok {
return false, nil
}
ok, err := matchValue(selector, value)
if err != nil {
return false, fmt.Errorf("error while trying to match '%s': %w", key, err)
}
if !ok {
return false, nil
}
}
return true, nil
}
func matchValue(selector interface{}, val string) (bool, error) {
switch s := selector.(type) {
case string:
return s == val, nil
case []interface{}:
for _, orVal := range s {
v, ok := orVal.(string)
if !ok {
return false, fmt.Errorf("invalid selector type '%T'", val)
}
if val == v {
return true, nil
}
}
return false, nil
default:
return false, fmt.Errorf("unknown selector type '%T' only string or a list of strings is supported", val)
}
}
// RegexSelector defines the selector for the identity of an object.
// The regex selector is a map of identity key to selector
// All keys are validated against the given selector.
//
// Valid selectors are
// - raw string value: identity value is compared to the selector value
// - array of strings: or-operator identity value must be one of the defined strings in the array.
type RegexSelector map[string]interface{}
var _ Interface = RegexSelector{}
// ParseRegexSelector creates a Identity selector from a
// - json encoded selector
// - map[string]Selector
//
// A selector can be
// - a string: the value is directly matched
// - a array of strings: one selector in the array must match.
func ParseRegexSelector(value interface{}) (RegexSelector, error) {
switch v := value.(type) {
case map[string]interface{}:
return v, nil
case string:
selector := RegexSelector{}
if err := json.Unmarshal([]byte(v), &selector); err != nil {
return nil, err
}
return selector, nil
default:
return nil, fmt.Errorf("unknown type %T", value)
}
}
func (is RegexSelector) Match(obj map[string]string) (bool, error) {
for key, selector := range is {
value, ok := obj[key]
if !ok {
return false, nil
}
ok, err := matchValueByRegex(selector, value)
if err != nil {
return false, fmt.Errorf("error while trying to match '%s': %w", key, err)
}
if !ok {
return false, nil
}
}
return true, nil
}
func matchValueByRegex(selector interface{}, val string) (bool, error) {
switch s := selector.(type) {
case string:
return regexp.MatchString(s, val)
case []interface{}:
for _, orVal := range s {
v, ok := orVal.(string)
if !ok {
return false, fmt.Errorf("invalid selector type '%T'", val)
}
if val == v {
return true, nil
}
}
return false, nil
default:
return false, fmt.Errorf("unknown selector type '%T' only string or a list of strings is supported", val)
}
}
// JSONSchemaSelector uses a jsonschema to match a specific object.
type JSONSchemaSelector struct {
Scheme *gojsonschema.Schema
}
// NewJSONSchemaSelector creates a new jsonschema selector from a gojsonschema.
func NewJSONSchemaSelector(scheme *gojsonschema.Schema) JSONSchemaSelector {
return JSONSchemaSelector{Scheme: scheme}
}
// NewJSONSchemaSelectorFromBytes creates a new jsonschema selector from a gojsonschema.
func NewJSONSchemaSelectorFromBytes(src []byte) (JSONSchemaSelector, error) {
data, err := yaml.YAMLToJSON(src)
if err != nil {
return JSONSchemaSelector{}, err
}
scheme, err := gojsonschema.NewSchema(gojsonschema.NewBytesLoader(data))
if err != nil {
return JSONSchemaSelector{}, err
}
return JSONSchemaSelector{Scheme: scheme}, nil
}
// NewJSONSchemaSelectorFromString creates a new jsonschema selector from a gojsonschema.
func NewJSONSchemaSelectorFromString(src string) (JSONSchemaSelector, error) {
return NewJSONSchemaSelectorFromBytes([]byte(src))
}
// NewJSONSchemaSelectorFromString creates a new jsonschema selector from a gojsonschema.
func NewJSONSchemaSelectorFromGoStruct(src interface{}) (JSONSchemaSelector, error) {
scheme, err := gojsonschema.NewSchema(gojsonschema.NewGoLoader(src))
if err != nil {
return JSONSchemaSelector{}, err
}
return NewJSONSchemaSelector(scheme), nil
}
var _ Interface = JSONSchemaSelector{}
func (j JSONSchemaSelector) Match(obj map[string]string) (bool, error) {
documentLoader := gojsonschema.NewGoLoader(obj)
res, err := j.Scheme.Validate(documentLoader)
if err != nil {
return false, err
}
return res.Valid(), nil
}