fix content-type declared by /events API#50953
Conversation
|
Now that the api is a separate module, we need to re-vendor changes made in the You also need to update the content type declared in the Swagger file: Lines 10351 to 10352 in 3278393 And add a note in the API changelog: https://github.com/moby/moby/blob/master/api/docs/CHANGELOG.md |
stupid me, this is the reason I've been looking into this, and eventually forgot to fix 😅 |
|
This probably also requires changes in the swagger definition to declare the content-types it can produce. I recall you had another PR related to delimited JSON responses, but that one used |
|
@thaJeztah application/json-seq isn't actually a newline delimited JSON stream. Media type used here is the one recommended by https://github.com/ndjson/ndjson-spec?tab=readme-ov-file#33-mediatype-and-file-extensions |
|
I just noticed we already rely on unofficial |
Yes, we debated at the time; I think before that we used (I think using
Oh! I missed that this is (finally!) getting some traction; I think technically we don't have to wait for that proposal to land; we already have some code in place (using the very old moby/daemon/server/router/container/copy.go Lines 46 to 48 in 5a69e91 Digging a bit deeper in the options we have; during the maintainers call, @corhere also contributed another option (use SSE as a response), which I'll add below.
|
|
I want to have a look at (until the Go proposal lands) adding basic content negotiation similar to moby/daemon/server/router/container/copy.go Lines 46 to 48 in 5a69e91 |
|
content negotiation is the best option AFAICT, as this would prevent need to guard this by API version and risk for a non-compliant client to see a sudden failure to parse output. |
|
Oh, yes, if you have time to play; that sounds great! ❤️ - if not, it felt like a fun thing to play with in the weekend Using
As
Yup, I think that one is a "nice to have", and may require slightly more changes, but it sounds like a good option to have; depending on what language/framework is used, perhaps one is more common than the other (and vice-versa), and given that the differences are "small", we can probably support both with minimal changes. |
30dc633 to
79bf1aa
Compare
thaJeztah
left a comment
There was a problem hiding this comment.
Just some quick comments from glancing over
api/types/types.go
Outdated
| MediaTypeMultiplexedStream = "application/vnd.docker.multiplexed-stream" | ||
|
|
||
| // MediaTypeJson is the MIME-Type for JSON objects | ||
| MediaTypeJson = "application/json" |
There was a problem hiding this comment.
nit (and to prevent linters from complaining);
| MediaTypeJson = "application/json" | |
| MediaTypeJSON = "application/json" |
|
build was failing because 2 const references weren't updated in your last push; I did a quick rebase and fix for those to have CI run. |
| produces: | ||
| - "application/json" | ||
| - "application/x-ndjson" | ||
| - "application/json-seq" |
There was a problem hiding this comment.
I think we should leverage RFC 8091 to leave room for negotiating the format of the event content by defining a dedicated media type for the event document with the structured syntax suffix for json-seq.
| - "application/json-seq" | |
| - "application/vnd.docker.system-events.v1+json-seq" |
There was a problem hiding this comment.
👎 if content-type is expected to describe the JSON message being transported by HTTP, then each and every API endpoint would have to declare such a custom mime type (see https://datatracker.ietf.org/doc/html/rfc6839#section-3.1)
IMHO, even I don't expect most to require application/json Content-Type, this would introduce a breaking change in the API for consumers. This also makes content-type management harder to read/parse.
other (standard) options we have:
- RFC 6906 profile
Content-Type: application/json; profile=<URI for payload schema> - RFC 8288 Link Header
Link: <URI for payload schema>; rel="describedby"
There was a problem hiding this comment.
Fair enough. We already have the Engine API versioning scheme to describe the semantics of the documents.
There was a problem hiding this comment.
Oh! Thought I commented, but yeah, I think adding additional mediaTypes to provide more granularity .. could be interesting, but is a bit out of scope for these initial steps. AFAICS, this PR doesn't prevent us from doing so in future if we want to, but the initial goal of this PR was to make sure the mediaTypes we return are aligning better with the implementation, and to use mediaTypes that are accepted by common framework / languages (not the default Go implementation we have).
Marking this thread as "unresolved" but only for discoverability / finding it back if we want to have that discussion in future.
| @@ -0,0 +1,97 @@ | |||
| package internal | |||
There was a problem hiding this comment.
This could probably be in some sub package, but I don't have a good suggestion for a name, and it's all internal so .. fine for later if we come up with a good name
| headers.Add("Accept", types.MediaTypeJSONSequence) | ||
| headers.Add("Accept", types.MediaTypeNDJSON) |
There was a problem hiding this comment.
For others; double checked that Add appends to existing values (which it does). These functions always confuse me 😂
| contentType := httputil.NegotiateContentType(r, []string{ | ||
| types.MediaTypeNDJSON, | ||
| types.MediaTypeJSONSequence, | ||
| }, types.MediaTypeJSON) // output isn't actually JSON but API used to this content-type | ||
| w.Header().Set("Content-Type", contentType) |
There was a problem hiding this comment.
For others; the third argument is the "fallback". Also checked that the order in which options are taken as priority if "no q / weight" is specified by the client, so ndjson is the preferred option, and json-sequence is used if the client "accepts" it.
We could consider making ndjson the default for new API versions and only fallback to application/json for old API versions, but we can still make that change in a follow-up.
| produces: | ||
| - "application/json" | ||
| - "application/x-ndjson" | ||
| - "application/json-seq" |
There was a problem hiding this comment.
Oh! Thought I commented, but yeah, I think adding additional mediaTypes to provide more granularity .. could be interesting, but is a bit out of scope for these initial steps. AFAICS, this PR doesn't prevent us from doing so in future if we want to, but the initial goal of this PR was to make sure the mediaTypes we return are aligning better with the implementation, and to use mediaTypes that are accepted by common framework / languages (not the default Go implementation we have).
Marking this thread as "unresolved" but only for discoverability / finding it back if we want to have that discussion in future.
| reader io.Reader | ||
| buffer []byte |
There was a problem hiding this comment.
Would bufio.Reader simplify the implementation?
There was a problem hiding this comment.
Is buffering even necessary? An io.Reader is not required to fill the destination slice. This passes the unit test:
func (r *RSFilterReader) Read(p []byte) (n int, err error) {
if len(p) == 0 {
return 0, nil
}
n, err = r.reader.Read(p)
filtered := slices.DeleteFunc(p[:n], func(b byte) bool { return b == rs })
return len(filtered), err
}There was a problem hiding this comment.
indeed, not sure why I came to this unnecessary complexity
a58f243 to
8dc7215
Compare
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
|
did a rebase and re-vendor to make CI happy |
|
Flaky test (should be unrelated) |
thaJeztah
left a comment
There was a problem hiding this comment.
all green now 🎉
LGTM, thanks for updating!
- What I did
fixed
/eventsAPI to report content-type asapplication/x-ndjsonusing
application/jsonin this API is wrong, I assume client always ignore Content-Type set here otherwise they would only unmarshal the first object in the stream and miss all others.There's unfortunately no official mime type for line-delimited json object streams
- How I did it
- How to verify it
- Human readable description for the release notes
- A picture of a cute animal (not mandatory but encouraged)
