Skip to content

client: add new Filters type to replace api/types/filters package#51115

Merged
thaJeztah merged 2 commits intomoby:masterfrom
corhere:api-filter-type
Oct 8, 2025
Merged

client: add new Filters type to replace api/types/filters package#51115
thaJeztah merged 2 commits intomoby:masterfrom
corhere:api-filter-type

Conversation

@corhere
Copy link
Contributor

@corhere corhere commented Oct 6, 2025

Filters 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

  • Removed extraneous (to clients) request-filter utilities from the Go SDK

- How I did it

  • added a new Filters type to the client module which does everything that clients need for filtering requests, and no more
  • refactored the client module to use client.Filters in place of filters.Args
  • moved the api/types/filters module under daemon/internal, verbatim, and updated the imports

- How to verify it
CI.

- Human readable description for the release notes

- Go SDK: the client now uses its own `client.Filters` type for filtering API requests, with a more ergonomic interface. Users of the `github.com/docker/docker/api/types/filters` package will need to refactor when they upgrade to the v29 client.

- A picture of a cute animal (not mandatory but encouraged)

@corhere corhere added this to the 29.0.0 milestone Oct 6, 2025
@corhere corhere added area/api API impact/changelog area/go-sdk impact/go-sdk Noteworthy (compatibility changes) in the Go SDK release-blocker PRs we want to block a release on labels Oct 6, 2025
@corhere corhere added the kind/refactor PR's that refactor, or clean-up code label Oct 6, 2025
@corhere corhere changed the title Api filter type client: add new Filters type to replace api/types/filters package Oct 6, 2025
Copy link
Contributor

@robmry robmry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, with a nit/suggestion ...

@corhere corhere force-pushed the api-filter-type branch 2 times, most recently from c4fd461 to ebaab30 Compare October 7, 2025 17:32
Comment on lines +15 to +16
// Like all other map types in Go, the zero value is empty and read-only.
type Filters map[string]map[string]bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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!"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That one definitely tripped me up a few times though 😂

f[term][v] = true
}
return f
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, Add appends, same as (url.Values).Add. I'll cover that in the unit test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@thaJeztah thaJeztah merged commit 6bbb92d into moby:master Oct 8, 2025
181 checks passed
@corhere corhere deleted the api-filter-type branch October 27, 2025 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api API area/go-sdk impact/changelog impact/go-sdk Noteworthy (compatibility changes) in the Go SDK kind/refactor PR's that refactor, or clean-up code module/client release-blocker PRs we want to block a release on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

api: split types/filters API struct from handling code

3 participants