Handle byte streams for both success and error responses #35
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
When an OpenAPI spec reports that errors may be unstructured, generated clients need a way to express this. Currently they just use
()
which is obviously imprecise.There were three options I considered:
Error
variant alongsideErrorResponse(ResponseValue<E>)
--something likeUnstructuredErrorResponse(Response)
--which would have the raw responseError<T>
which currently takes any typeT
to take either aResponseValue<T>
or aResponse
ByteStream
to represent the unstructured response body; this could be used within aResponseValue<ByteStream>
Each of these has its drawbacks. 1 requires clients to consider two--essentially identical--error variants, only one of which would only be relevant for a given API call. 2 imposes the knowledge of the structure of
Error
types more onerously on mock clients; it requires exposing a trait to treatResponseValue<T>
andResponse
generically which can also lead to confusing error messages. 3 doesn't give access to rest of theResponse
structure, extracting only the streaming body viaResponse::bytes_stream()
.After prototyping all three and trying them out with omicron, I chose 3. 1 felt pretty janky with the two nominally conflicting Error variants; 2 felt easy to get wrong and seemed to expose more implementation detail than was desirable; 3 actually had some nice properties of unifying type and status code handling of success and error responses. With 3, we figure out the type of the response
T
--either a generated type, aByteStream
, or()
--and then the success case is aResponseValue<T>
and the error case is anError<T>
. This has a pleasant symmetry to it.In Dropshot, the OpenAPI description for an endpoint whose response is completely free-form uses the
default
response type and the*/*
content type: there's nothing specific Dropshot can say about the response codes, content type, schema, etc. In those cases, the return value for the corresponding generated client method will beResponse<ResponseValue<ByteStream>, Error<ByteStream>>
where the only thing distinguishing the success and error cases is the response code that the endpoint applied to its custom response.For oxidecomputer/dropshot#297, with this PR a method with a streaming response but error types would have a return type that looks like this:
Response<ResponseValue<ByteStream>, Error<types::Error>>
.Note that both @leftwo and @iliana have hit conditions applicable to this PR.