roby
04/06/2023, 10:15 PMkt
val contract = contract {
renderer = OpenApi3(ApiInfo("Tasks API", "v0.1.0"), Argo)
descriptionPath = "/docs"
routes += UserRouter(userService).routes // these dont work
routes += "/ping" bindContract Method.GET to { _ -> Response(Status.OK).body("pong") } // this works
}
val handler = routes("/api" bind contract)
handler.asServer(Jetty(port)).start() ..
//....
class UserRouter(private val services: UserService) {
val routes: List<ContractRoute> = listOf(
"/users/{id}" meta {
summary = "Get user details"
returning(
Status.BAD_REQUEST to "Invalid id",
Status.NOT_FOUND to "User not found"
)
} bindContract GET to ::getUser,
Andrew O'Hara
04/06/2023, 10:26 PMContractRoute
with a path parameter would typically look like
"/users" / idLens meta {
...
} bindContract GET to { id ->
{ req ->
...
}
}
I'm not entirely sure if the contract router is unable to route for non-contract path parameters, but fixing that would be the first thing to try.Andrew O'Hara
04/06/2023, 10:27 PMroby
04/06/2023, 10:38 PM"/users" / <http://Path.int|Path.int>().of("id") meta {
summary = "Get user details"
returning(
Status.BAD_REQUEST to "Invalid id",
Status.NOT_FOUND to "User not found"
)
} bindContract GET to { id ->
{
getUser(id)
}
},
// ...
private fun getUser(userId: Int): Response = errorHandler {
val res = services.getUser(userId)
Response(Status.OK)
.header("content-type", "application/json")
.body(Json.encodeToString(res))
}
roby
04/06/2023, 10:38 PMroby
04/06/2023, 10:39 PMgetUser(id, it)
roby
04/06/2023, 10:39 PM"/users" / <http://Path.int|Path.int>().of("id") meta {
summary = "Get user details"
returning(
Status.BAD_REQUEST to "Invalid id",
Status.NOT_FOUND to "User not found"
)
} bindContract GET to { id -> { req ->
getUser(req, id)
}
},
idk, this looks a bit awfulAndrew O'Hara
04/06/2023, 10:40 PMbindContract GET to { id ->
{ req ->
getUser(id)
}
}
roby
04/06/2023, 10:46 PMusers/{id}/boards
roby
04/06/2023, 10:47 PMroby
04/06/2023, 10:47 PM/ "boards"
after the path... id thing is throwing error at the whole coderoby
04/06/2023, 10:54 PMval test = "/users" / <http://Path.int|Path.int>().of("id") / "boards" meta {
summary = "Get the list with all user available boards"
returning(
Status.OK to "User boards",
Status.BAD_REQUEST to "Invalid id",
Status.NOT_FOUND to "User not found"
)
} bindContract GET to ::getUserBoards
roby
04/06/2023, 10:54 PMPair<ContractRouteSpec2<Int, String>.Binder, KFunction1<Request, Response>>
(?)roby
04/06/2023, 10:54 PMAndrew O'Hara
04/06/2023, 10:55 PMRequest
type parameter to req
"/foo" / idLens meta {
...
} bindContract GET to { req: Request ->
Response(OK)
}
The issue with the new route you've added is that you're not handling the value of the new boards
parameter (even though it's not a lens).
val test = "/users" / <http://Path.int|Path.int>().of("id") / "boards" meta {
...
} bindContract GET to { id, _ ->
{ req ->
Response(OK)
}
}
Notice the _
parameter added after id
. Since we don't care about its value, it can be named _
and ignoredAndrew O'Hara
04/06/2023, 10:58 PMPathLens
between routes, rather than redefine it each time you add a new route.roby
04/06/2023, 10:59 PMAndrew O'Hara
04/06/2023, 11:02 PMval idLens: BiDiPathLens<Int> = <http://Path.int|Path.int>().of("id")
A lens is a typesafe way to get data in and out of an http message (ie request or response). So to share it, you would so something like:
val getUser = "/users" / idLens bindContract GET
val putUser = "/users" / idLens bindContract PUT
The benefit is that you can reuse the lens in other contracts or in a client call
val response = getUser
.newRequest(hostname)
.with(idLens of 123)
.let(httpHandler)
Andrew O'Hara
04/06/2023, 11:04 PM