client: add new Filters type to replace api/types/filters package#51115
client: add new Filters type to replace api/types/filters package#51115thaJeztah merged 2 commits intomoby:masterfrom
Filters type to replace api/types/filters package#51115Conversation
Filters type to replace api/types/filters package
c4d49a6 to
a0fcde8
Compare
robmry
left a comment
There was a problem hiding this comment.
LGTM, with a nit/suggestion ...
c4fd461 to
ebaab30
Compare
| // Like all other map types in Go, the zero value is empty and read-only. | ||
| type Filters map[string]map[string]bool |
There was a problem hiding this comment.
I like this PR overall; the only thing I went looking for is .. if it's somehow possible to make the zero value "work", i.e., something like this will panic;
type Something struct{
Filters Filters
}
foo := Something{}
foo.Filters.Add("hello", "world")Because in general, I'd like to get rid of the bare filters Filters as arguments, and always use "some option structure" for these arguments.
Changing the methods to be on a pointer-receiver ... would allow fixing that, but not great either.
And I guess making it back into a struct with the map as a field would bring us back to the type not working to be included in a struct (for JSON requests)
Happy to hear if there's options to ... somehow fix that though!
There was a problem hiding this comment.
I had initially written it to make the zero value writable, but ultimately backed out. It would break with Go convention for map types to have methods that initialize the map as a side effect. And I ran into some weirdness with method chaining when I tried that, IIRC.
What if we put convenience methods on the options struct?
type Something struct {
Filters Filters
}
func (s *Something) AddFilter(key string, values string...) *Something {
if s.Filters == nil {
s.Filters = Filters{}
}
s.Filters.Add(key, values...)
return s
}| // Like all other map types in Go, the zero value is empty and read-only. | ||
| type Filters map[string]map[string]bool | ||
|
|
||
| // Add appends values to the value-set of term. |
There was a problem hiding this comment.
Related to the above; do we need to make a mention of it panicking if Filter is nil? The issue with nice methods (like Add) makes it easier to assume "it just works" (whereas dealing with a raw map makes it more obvious "this must be initialised before use!"
There was a problem hiding this comment.
I called that out in the documentation for the type.
// Like all other map types in Go, the zero value is empty and read-only.The Go authors did not feel it necessary to document that ("net/url".Values).Add() panics on a nil receiver. It's the expected convention that nil-valued map types are not writable.
There was a problem hiding this comment.
That one definitely tripped me up a few times though 😂
| f[term][v] = true | ||
| } | ||
| return f | ||
| } |
There was a problem hiding this comment.
Add not only adds, but replaces what was already there; or at least for the term key, it's, it would keep it as-is, but for values, it replaces what was there.
Wondering if Set would be a more appropriate name? Alternatively AddValues I guess could be an option.
There was a problem hiding this comment.
No, Add appends, same as (url.Values).Add. I'll cover that in the unit test.
There was a problem hiding this comment.
DOH! You're so right! It's not a key/value pair; the boolean is just because it needed a value. I should think before I post; was even thinking "this could've been a struct{}{}" (although those are awkward for the API).
I've consider a few times that it probably should've kept the "old" format; just a slice, and handle de-duplicating (if needed) when creating and on the daemon-side, instead of that optimisation making its way to the API format, well 🤷
Most of the code in the filters package relates to the unmarshaling, validation and application of filters from client requests. None of this is necessary or particularly useful for Go SDK users. Move the full-fat filters package into daemon/internal and switch all the daemon code to import that package so we are free to iterate upon the code without worrying about source-code interface compatibility. Signed-off-by: Cory Snider <csnider@mirantis.com>
Add a new type to use for building filter predicates for API requests, replacing "./api/types/filters".Args in the client. Remove the now unused api/types/filters package. Signed-off-by: Cory Snider <csnider@mirantis.com>
ebaab30 to
7ea066c
Compare
api/typesdo not relate to the Engine API #50740Filters are only ever used in the Engine API as request query parameters; never in a request or response body. API clients therefore do not need to unmarshal, validate, or evaluate a filter predicate.
The filters.Args type in the api module has very low cohesion with the rest of the api module. No struct field in the whole api module is of a filter type. Furthermore, the whole api/types/filters package can be deleted without any impact to any of the other packages in the api module!
On the other hand, the client module is tightly coupled to the filters.Args type. It is extensively used in client struct fields and function parameters. And the client is responsible for marshalling and appending the filters to the request query.
- What I did
- How I did it
Filterstype to the client module which does everything that clients need for filtering requests, and no moreclient.Filtersin place offilters.Args- How to verify it
CI.
- Human readable description for the release notes
- A picture of a cute animal (not mandatory but encouraged)