Conversation
Codecov Report
@@ Coverage Diff @@
## master #12 +/- ##
=========================================
Coverage ? 65.35%
=========================================
Files ? 6
Lines ? 482
Branches ? 0
=========================================
Hits ? 315
Misses ? 167
Partials ? 0
Continue to review full report at Codecov.
|
| - Note: The code accessing this property is expected to know the | ||
| concrete type of this object and downcast, handling any | ||
| failures appropriately. | ||
| */ |
There was a problem hiding this comment.
Where the client receives an error response and a RequestError is generated, how does the data off the wire get decoded into a Codable? The type is not known, so initially you have some Data. You may be able to add a function to which the user supplies the concrete type of Codable they expect to be able to decode from the error body (in lieu of making the RequestError generic).
| } | ||
|
|
||
| /// Creates an error respresenting the given base error, with a custom | ||
| /// response body given as Data and a BodyFormat |
There was a problem hiding this comment.
I need to add - throws to the doc here
854ef08 to
9947ddb
Compare
9947ddb to
60dc142
Compare
|
@tunniclm is this ready to merge? |
|
There's a few more requirements that have arisen from testing with KituraKit and talking with @EnriqueL8 / @seabaylea. I'd like to work on that a bit to work out the required changes. |
| return lhs.type == rhs.type | ||
| } | ||
|
|
||
| public static let json = BodyFormat("application/json") |
There was a problem hiding this comment.
Do we have a separate "registry" of MIME types somewhere that we could reuse?
There was a problem hiding this comment.
I added the type here as a convenience for a client (eg KituraKit) trying to tally the BodyFormat with a Content-Type header from a server response.
I did think about how to integrate it with a general approach for MIME/Content-types, but I don't think we have a really coherent story on that in Kitura (something I think we should fix). So I concluded that I shouldn't rathole on it in this feature/PR.
| * limitations under the License. | ||
| **/ | ||
|
|
||
| import Foundation |
There was a problem hiding this comment.
I don't think this file needs to import Foundation.
There was a problem hiding this comment.
IIRC Foundation is required for the Data type
| self.reason = reason | ||
| } | ||
|
|
||
| /// Creates an error respresenting the given base error, with a custom |
There was a problem hiding this comment.
Typo - respresenting.
| is not supported | ||
| */ | ||
| public func encodeBody(_ format: BodyFormat) throws -> Data? { | ||
| guard case .codable? = body else { return nil } |
There was a problem hiding this comment.
I'm confused. Doesn't this return nil if we're not .codable? Don't we want to return nil if we are .codable?
There was a problem hiding this comment.
As discussed in person, this function is for encoding the Codable body into Data, so the "source" of the body must be Codable rather than Data.
I will change the doc to "Returns the codable error body..." in the hope this clarifies the behaviour.
Side-note: I did consider a different API where the encode/decode was done transparently (ie you could just access the body as Codable or Data and it would work regardless of how it was initialized, but it didn't turn out well so I went for this less magic API instead).
| - throws a `DecodingError` if decoding fails | ||
| */ | ||
| public func decodeBody<Body: Codable>(_ type: Body.Type) throws -> Body? { | ||
| guard case let .data(bodyData, format)? = body else { return nil } |
There was a problem hiding this comment.
Same comment as before, isn't this the wrong way round?
There was a problem hiding this comment.
Similar response to the above, I will update the doc to "Returns the Data error body as..."
|
|
||
| /// Representation of the error body | ||
| /// May be a type-erased Codable object or a Data (in a particular format) | ||
| public enum ErrorBody { |
There was a problem hiding this comment.
Did you consider using the struct approach again, just in case we ever want a third option?
There was a problem hiding this comment.
I thought about it, but I'm not sure it's necessary here. Unlike the BodyFormat case, the function seems fairly tightly defined and fitted to these two cases.
Add the capability to provide a
Codableobject toRequestErrorthat will be encoded and sent by theRouteras the response body when passed to the completion handler of a codable route.Description
This PR is one part of a pair, this one is for
KituraContracts, the other is forKitura(Kitura/Kitura#1214).In this PR we add a new generic constructor to
RequestErrorthat can specify a "base" request error and a bodyCodable, for example:RequestError(.unavailable, body: health.status).This will assign the object to a new stored property
bodyas a (type-erased)Codable. The fact the type is erased allow theRequestErrorstruct to remain non-generic (changing it to be generic would be a breaking change to the API).However, this means that the real type of the
bodyis no longer know. In order for theRouterto encode thebodyintoDatain some format (currently we only support JSON, but in the future there may be other formats) we need to be able to access the real type to pass to the encoder.To achieve this we also store a closure in a second new stored property
bodyDataat the same time (from within the genericinitwhere we still have access to the real type of the body codable). This closure accepts an argument describing the format into which you want to encode the body and returns aDatacontaining the encoded form of the body.A new struct
BodyFormathas been added to enumerate the different supported format. Currently only JSON is supported. This was made as a struct rather than an enum to allow new formats to be added without breaking the API (enums are exhaustive and adding a case would break any switch statements relying on that.)The
bodyDataclosure performs a switch on theBodyFormatprovided and calls the appropriate encoder on the body to convert it toData. If theBodyFormatis not recognised, then anUnsupportedBodyFormatError(another new type added in this change) will be thrown. SinceBodyFormatonly has a private initializer, no other code should be able to create them, and provided the generic initializer ofRequestErrorandBodyFormatare kept in sync, thenUnsupportedBodyFormatErrorshould never be thrown.This PR also adds a few small tests for the new initializer and properties.
Jazzy API doc has been added for the new code.
In the
KituraPR theRouterhas been updated so that codable routes will check for the existence of abodyDatain anyRequestErrorreturned by a codable route handler, and if it is non-nil then will use it to get aDatato send as the body of the response.Motivation and Context
This change allows codable route handlers to customise the body of error responses. One example of where this feature is needed is the health route generated by the Swift Server Generator and kitura-cli.
This route should send
HTTP 200 OKwhen the application is healthy andHTTP 503 Service Unavailablewhen the application is not working. It should also provide a response body that is a JSON object describing the status of the application and each dependent service. This body should be sent both when the application is healthy and when it is not, and is arguably more important in the latter case as it will give information about which service is not available.Without a custom body for
RequestErrorit is not possible to send a response body if the status code is not 2xx (usually HTTP 200 OK, but it varies depending on the HTTP method, eg:deleteuses HTTP 204 No content).How Has This Been Tested?
New tests have been added, tested with
swift test.Checklist: