Conversation
…ura into typeSafeMiddleware
Codecov Report
@@ Coverage Diff @@
## master #1274 +/- ##
==========================================
- Coverage 88.97% 82.62% -6.35%
==========================================
Files 39 40 +1
Lines 2412 2872 +460
==========================================
+ Hits 2146 2373 +227
- Misses 266 499 +233
Continue to review full report at Codecov.
|
|
Why is one limited to three middleware? Why isn't the list of middleware an array or a variadic parameter? |
ianpartridge
left a comment
There was a problem hiding this comment.
A few initial comments.
| @@ -0,0 +1,1020 @@ | |||
| /* | |||
There was a problem hiding this comment.
Can this file be called CodableRouter+TypeSafeMiddleware.swift?
| extension Router { | ||
|
|
||
| // Used by PUT and PATCH with identifier | ||
| public typealias MiddlewareIdentifierCodableClosure<T: TypeSafeMiddleware, Id: Identifier, I: Codable, O: Codable> = (T, Id, I, @escaping CodableResultClosure<O>) -> Void |
There was a problem hiding this comment.
Is there a reason why these typealiases are inside extension Router {}? I would have thought they should be top-level.
| next() | ||
| return | ||
| } | ||
| self.handleMiddleware(T1.self, T2.self, request: request, response: response) { TypeSafeMiddleware1, TypeSafeMiddleware2 in |
There was a problem hiding this comment.
Please name variables with lower-case names, e.g. typeSafeMiddleware1 not TypeSafeMiddleware1 to distinguish them from type names. This comment applies across the whole PR.
|
@shmuelk I think the feature we really want is variadic generics but they aren't here yet unfortunately. Andrew and Dave did explore using an array, I'll let them comment on why they decided against that approach. |
| * limitations under the License. | ||
| */ | ||
|
|
||
| import KituraContracts |
There was a problem hiding this comment.
I think we can revert all the changes to this file now?
| @@ -0,0 +1,39 @@ | |||
| /* | |||
| * Copyright IBM Corporation 2016 | |||
| /// attempt to process the request, respectively. | ||
| static func handle(request: RouterRequest, response: RouterResponse, completion: @escaping (Self?, RequestError?) -> Void) -> Void | ||
|
|
||
| /// Decribe the type-safe middleware |
There was a problem hiding this comment.
Describe not Decribe. Also, please give an example of the kind of description that would be appropriate.
| @@ -0,0 +1,890 @@ | |||
| /* | |||
| * Copyright IBM Corporation 2017 | |||
| ) { | ||
| T.handle(request: request, response: response) { (typeSafeMiddleware: T?, error: RequestError?) in | ||
| guard let typeSafeMiddleware = typeSafeMiddleware else { | ||
| print("failed typeSafeMiddleware. Error: \(String(describing: error))") |
| scheme: basic | ||
| ``` | ||
| */ | ||
| static func describe() -> String |
There was a problem hiding this comment.
My only question is whether we should take this out for now.
| /// Defines the protocol which all Kitura type-safe middleware must implement. | ||
| /// | ||
| /// TypeSafeMiddleware are classes or structs that process an incoming request | ||
| /// and on success create an instance of self, which is passed to the users route handler. |
There was a problem hiding this comment.
users route handler -> user's route handler
|
|
||
| extension Router { | ||
|
|
||
| // MARK: Codable Routing With Type-Safe Middleware |
There was a problem hiding this comment.
At least the With should be lowercase in the // MARK here as it looks strange with a capital 'W' underneath the previous MARK'd title of Parameter handling (which has a lowercase 'h').
| ``` | ||
| - Parameter route: A String specifying the URL path that will invoke the handler. | ||
| - Parameter handler: A closure that receives a TypeSafeMiddleware instance and a QueryParams instance, and returns an array of Codable objects or a RequestError. | ||
| */ public func get<T: TypeSafeMiddleware, Q: QueryParams, O: Codable>( |
There was a problem hiding this comment.
This */ is being displayed in the API doc. public func... needs to move onto the next line to fix this.
| The handler contains the developer's logic, which determines the server's response. | ||
| ### Usage Example: ### | ||
| In this example, MySession is a struct that conforms to the TypeSafeMiddleware protocol. | ||
| session is an instance of MySession, that contains [Int: User] dictionary. |
There was a problem hiding this comment.
You need to add the word 'an' before [Int: User] dictionary. Same on line 978.
| id: Int, respondWith: (User?, RequestError?) -> Void) in | ||
| ``` | ||
| - Parameter route: A String specifying the URL path that will invoke the handler. | ||
| - Parameter handler: A closure that receives a TypeSafeMiddleware instance a Codable object and an identifier, and returns a Codable object or a RequestError. |
There was a problem hiding this comment.
Needs a comma after the word instance. The same is true in the usage example below.... I haven't checked others.
| The handler contains the developer's logic, which determines the server's response. | ||
| ### Usage Example: ### | ||
| In this example, MySession is a struct that conforms to the TypeSafeMiddleware protocol. | ||
| session is an instance of MySession, that contains optional Users. |
There was a problem hiding this comment.
Anywhere where we are starting a sentence with a variable name which is lowercase it looks poor in the jazzy API doc. Try the following instead:
In this example, MySession is a struct that conforms to the TypeSafeMiddleware protocol and specifies an optional User, where User conforms to Codable.
|
Tests for all routes and documentation has now been added. |
ianpartridge
left a comment
There was a problem hiding this comment.
Awesome work guys.
|
@shmuelk To answer your earlier question on the 'up to 3' TypeSafeMiddleware parameters: Ian is correct, variadic generics is what we'd need to implement this in a nice way. The reason we can't use an array is that the array would lose the type information of the specific instance types the user is expecting: the router would only know that the user's handler was expecting an array of Here is an example of how we could do it, but it's pretty hairy (and not very type-safe): func get(_ route: String, middlewareTypes array: [MyProto.Type], handler: ([MyProto]) -> Void) {
var result: [MyProto] = []
for elem in array {
var constructedType = elem.init()
result.append(constructedType)
}
handler(result)
}
get("/foo", middlewareTypes: [ImplA.self, ImplB.self]) {
(impls: [MyProto]) in
guard let implA = impls[0] as? ImplA, let implB = impls[1] as? ImplB else {
fatalError("Unexpected types passed")
}
}The biggest problem with this approach is requiring the user to restore the lost type information as a series of attempted downcasts, exactly matching the types in the array they passed to the router. |
Adds the new TypeSafeMiddleware protocol and routes for up to 3 TypeSafeMiddleware in Codable routing.
Description
TypeSafeMiddleware is a protocol for middleware where they return an instance of self instead of altering the request and response. This allows them to be used in Codable routing.
The routes have been added in a new file for Codable routing allowing a user to have up to three type safe middleware on a route by declaring them in there route signature.
Motivation and Context
type safe middleware means application gains compile-time safety and developers benefit from enhanced separation of concerns between the middleware(s) and route handlers.
It also allows middleware such as Auth and sessions them to be used in Codable routes.
How Has This Been Tested?
Has been tested manually using Sessions as a typed middleware.
Automated tests still need to be added.
Appropriate Docs also need to be added describing the new routes.