Conversation
|
Working through rebasing the code on master, there are some conflicts to resolve. |
2dd2a0b to
5a26833
Compare
|
Conflicts now resolved but this code requires the KituraContracts changes before it will compile. |
Codecov Report
@@ Coverage Diff @@
## master #1214 +/- ##
==========================================
+ Coverage 86.94% 87.93% +0.99%
==========================================
Files 38 38
Lines 2367 2404 +37
==========================================
+ Hits 2058 2114 +56
+ Misses 309 290 -19
Continue to review full report at Codecov.
|
djones6
left a comment
There was a problem hiding this comment.
The changes look good (in particular, the refactoring of the tests looks great), I made a few minor suggestions.
| }) | ||
| } | ||
| .request("post", path: "/error/users", data: user) | ||
| .hasStatus(.conflict) |
There was a problem hiding this comment.
Did you want to assert .hasNoData() here?
There was a problem hiding this comment.
This looks correct to me, the "/error/users" route responds with nil.
There was a problem hiding this comment.
It wasn't obvious from the diff, but Dave was pointing out that .hasNoData() wasn't being called here. Then I added it in, but his original comment remains and now it reads like he's questioning if the .hasNoData() should be there!
I think we all agree it should be. :)
| }) | ||
| } | ||
| .request("post", path: "/error/users", data: user) | ||
| .hasStatus(.conflict) |
| } | ||
| router.post("/bodyerror/users") { (user: User, respondWith: (User?, RequestError?) -> Void) in | ||
| print("POST on /bodyerror/users for user \(user)") | ||
| respondWith(user, RequestError(.conflict, body: Conflict(on: "id"))) |
There was a problem hiding this comment.
I think this shouldn't be trying to respond with a user as well as an error. (We should probably have separate tests that check that the error overrides the body, both when the error does / does not have its own body)
| return | ||
| } | ||
| .request("get", path: "/error/status") | ||
| .hasStatus(.serviceUnavailable) |
|
@djones6 I think I have addressed everything in the review on this PR, can you re-review for me? Thanks |
4760018 to
5b613d4
Compare
|
LGTM, thanks for adding those extra tests. |
Package.swift
Outdated
| .package(url: "https://github.com/IBM-Swift/Kitura-net.git", .upToNextMinor(from: "2.0.0")), | ||
| .package(url: "https://github.com/IBM-Swift/Kitura-TemplateEngine.git", .upToNextMinor(from: "1.7.0")), | ||
| .package(url: "https://github.com/IBM-Swift/KituraContracts.git", .upToNextMinor(from: "0.0.17")) | ||
| .package(url: "https://github.com/IBM-Swift/KituraContracts.git", .branch("add_requesterror_body")) |
There was a problem hiding this comment.
Remember to revert before this is merged :)
Sources/Kitura/CodableRouter.swift
Outdated
| response.status(status) | ||
| do { | ||
| if let bodyData = try err.encodeBody(.json) { | ||
| response.headers.setType("json") |
There was a problem hiding this comment.
Indentation is off here.
| respondWith(Status("GOOD"), nil) | ||
| } | ||
| router.get("/error/status") { (respondWith: (Status?, RequestError?) -> Void) in | ||
| print("GET on /status") |
There was a problem hiding this comment.
Should be print("GET on /error/status")
| respondWith(nil, .serviceUnavailable) | ||
| } | ||
| router.get("/bodyerror/status") { (respondWith: (Status?, RequestError?) -> Void) in | ||
| print("GET on /status") |
There was a problem hiding this comment.
print("GET on /bodyerror/status")
| }) | ||
| } | ||
| .request("post", path: "/error/users", data: user) | ||
| .hasStatus(.conflict) |
There was a problem hiding this comment.
This looks correct to me, the "/error/users" route responds with nil.
| buildServerTest(router, timeout: 30) | ||
| .request("get", path: "/status/1") | ||
| .hasStatus(.notFound) | ||
| .hasData("Cannot GET /status/1.") |
There was a problem hiding this comment.
Not sure if testing the precise error message is wise - 404 seems enough.
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
Kitura, the other is forKituraContracts(Kitura/KituraContracts#12).In the
KituraContractsPR we add a new generic constructor toRequestErrorthat 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.In this PR the
Routerhas 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.This PR also adds a number of tests, and to prevent excessive code duplication a new test builder API has been added to allow construction of concise tests in a declarative style.
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: