Razvan
10/16/2020, 8:01 AM@Test
fun index() {
val app = routes(
"/hello" bind Method.GET to {
Response(OK).body("<html><title>hello</title></html>")
}
)
val driver = Http4kWebDriver(app)
driver.navigate().to("<http://localhost:10000/hello>")
println(driver.title)
println(driver.currentUrl)
println(driver.findElement(By.tagName("title")))
}
and gets as result
<http://localhost:10000/>\hello
null
I run it on Linux all is OK
hello
<http://localhost:10000/hello>
JSoupWebElement(navigate=fun org.http4k.webdriver.Http4kWebDriver.navigateTo(org.http4k.core.Request): kotlin.Unit, getURL=() -> kotlin.String?, element=<title>hello</title>)
Any ideea where that comes from?Razvan
10/16/2020, 2:47 PMheaders
or queries
? Your examples only cover body and Path but couldn't from that figure it out for queries.Riku
10/19/2020, 6:55 AMreturning(Status.OK, courseLocationsLens to exampleResponse)
Cosmin Victor Celea
10/19/2020, 12:19 PMRazvan
10/21/2020, 4:42 PM@Test
fun `response body string for html`() {
val logAll = Filter { next: HttpHandler ->
{ request ->
println("REQ: ${request.bodyString()}")
val response = next(request)
println("REP: ${response.bodyString()}")
response
}
}
val server = logAll
.then(routes("/static" bind static(ResourceLoader.Classpath("static"))))
.asServer(Undertow(8080)).start()
val client = DebuggingFilters.PrintResponse()
.then(OkHttp())
val response = client(Request(GET, "<http://localhost:8080/static/index.html>"))
response.status shouldBe 200
}
static/index.html exists the filter prints it.
the result:
REQ:
REP: <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello http4k</title>
</head>
<body>
<h1>Hello http4k</h1>
</body>
</html>
***** RESPONSE 500 to GET: <http://localhost:8080/static/index.html> *****
HTTP/1.1 500 Internal Server Error
connection: keep-alive
content-length: 0
content-type: text/html
date: Wed, 21 Oct 2020 16:41:37 GMT
dwellman
10/23/2020, 12:49 PMdave
10/23/2020, 9:17 PMRazvan
10/31/2020, 9:26 AMRazvan
10/31/2020, 12:55 PMdave
11/02/2020, 5:59 PMdave
11/02/2020, 6:44 PMhttp4k
via Brew/SDKman!Razvan
11/04/2020, 3:13 PMinfo:
title: Test API
version: v1.0
description: null
tags: []
paths:
/contract/api/v1/echo:
post:
summary: echoes the name and message sent to it
description: null
tags:
- /contract/api/v1
parameters: []
requestBody:
content:
application/json:
example:
name: jim
message: hello!
schema:
$ref: '#/components/schemas/NameAndMessage'
description: null
example: null
required: true
responses:
'200':
description: OK
content:
application/json:
example:
name: jim
message: hello!
schema:
$ref: '#/components/schemas/NameAndMessage'
description: null
example: null
security:
- api_key: []
operationId: postContractApiV1Echo
deprecated: false
components:
schemas:
NameAndMessage:
properties:
name:
example: jim
description: null
type: string
message:
example: hello!
description: null
type: string
example:
name: jim
message: hello!
description: null
type: object
required:
- message
- name
securitySchemes:
api_key:
type: apiKey
in: query
name: api
openapi: 3.0.0
and the online editor
https://editor.swagger.io/Razvan
11/05/2020, 11:24 PMval btcPath = <http://Path.int|Path.int>().map(::BTC).of("btc")
val spec = "mine" / btcPath
it works if i call /mine/42
but if the param is not an int ex: /mine/foo
I get a 404 . I was thinking about la lensFailure so 400 or if not catched 500 but not 404. Is it designed that way or is something not right ?Razvan
11/06/2020, 12:16 AMdata class Test(val name: Int)
you declare in meta :
receiving(Body.auto<Test>().toLens() to Test(3))
you call in a curl with -d "{\"name\":5}"
it passes (it’s ok is just for reference of a correct call) but you call also with a bad keyname ex: -d "{\"ne\":5}"
the call is valid.
if name is a String this works as expected, fails if the key is spelled bad.
I don’t use that value so don’t know if the created object would be valid. I’ll try to make a full test tomorrow evening too late for that right now.Vojtěch Knyttl
11/08/2020, 8:38 PMorg.http4k.contract.openapi.v3.NoFieldFound: Could not find XXX in YYY
at org.http4k.contract.openapi.v3.FieldRetrieval$Companion$compose$1.invoke(FieldRetrieval.kt:17) ~[http4k-contract-3.249.0.jar:?]
at org.http4k.contract.openapi.v3.FieldRetrieval$Companion$compose$1.invoke(FieldRetrieval.kt:9) ~[http4k-contract-3.249.0.jar:?]
at org.http4k.contract.openapi.v3.AutoJsonToJsonSchema.toObjectSchema(AutoJsonToJsonSchema.kt:64) ~[http4k-contract-3.249.0.jar:?]
at org.http4k.contract.openapi.v3.AutoJsonToJsonSchema.toObjectOrMapSchema(AutoJsonToJsonSchema.kt:60) ~[http4k-contract-3.249.0.jar:?]
Getting this in runtime is a bit too late 🙂Razvan
11/10/2020, 10:06 AMRazvan
11/11/2020, 10:32 AMpreFlightExtraction
in parameter in the meta
tag which is quite great as it can disable the checks of lens on the contract level and lets you handle the LensFailure exception when you use the lens. So that saved my day.
Then while I was going through how the OpenApi render is done, I saw that you even thought about letting the errorReponseRenderer
as a parameter to the constructor. So it's even easier to providing my custom ErrorResponseRender that tries to make sens of Jackson exceptions, to provide the required parameters.
So thank you thinking about different use cases and providing so many setting to answer to different (sometimes dumb) requirements !andyg
11/12/2020, 9:32 PMaraknafobia
11/12/2020, 10:28 PMTypesafe RequestContexts
. In certain level, it makes extraction type safe however it does not guarantee that we won't forget to populate context with data. Isn't there a better approach to handle this situation?andyg
11/14/2020, 1:38 AMstatic
because there's some validation involved (or maybe file created dynamically). Based on an earlier Slack thread, I came up with this:
return if (<some validation code here>)
Response(Status.OK)
.header("Content-Type", "application/zip") // "application/octet-stream")
.header("Content-Disposition", "attachment; filename=${myFileToServe.file.substringAfterLast("/")}")
.header("Content-Length", myFileToServe.openConnection().contentLength.toString())
.body(myFileToServe.openStream())
else Response(Status.UNAUTHORIZED)
Razvan
11/17/2020, 7:07 PMOpenApi3ApiRenderer
should render or not null values giving that OpenAPI specification clearly states that
Ok you can disable null from Json but it's not the default, requires a custom config, but I think the OpenApi3Renderer should (at least by default) follow to the specifications of the format it claim to render. Don't you think ?is not supported as a type.null
Vojtěch Knyttl
11/19/2020, 7:18 AMPath.string().of("")
optional? When I have "/events" / Path.of("eventId")
, it says 404
if eventId
is not given. Also, what would be difference between .string()
and .nonEmptyString()
?Arnab Datta
11/19/2020, 12:08 PMimplementation group: "org.http4k", name: "http4k-graphql", version: "3.278.0"
to my build.gradle
file. Any ideas?
Could not find org.http4k:http4k-format-jackson:3.278.0.
Arnab Datta
11/19/2020, 3:31 PM{
search(params: {ids: [1,2]}){
id
name
}
s2:search(params: {ids: [1,2]}){
id
name
}
}
and using a small .also { println("running some expensive query") }
I was able to see that this was calling the UserQueries.search
method twice. Since the example specifically has a dataloader, I was wondering why? Isn’t the whole point of dataloaders that regardless of how the query is formed, one only calls the underlying service just once? Or is this specific to nested queries only (i.e. calls to fields of type User
say for instance a friends
field that invokes some kind of function towards the DB)? What am I misunderstanding here?Riku
11/19/2020, 6:19 PMVojtěch Knyttl
11/19/2020, 11:37 PMContractRoute
with Path.string()
parameters? Basically, I can do:
val response = myContractRoute(request)
but when accessing Path.string()
parameters I am getting:
Request was not routed, so no uri-template present
I tried then with what I found in https://www.http4k.org/cookbook/test_driven_apps/:
val root = routes(myContractRoute)
but it seems the routes()
method cannot work with ContractRoute.
is there a way?Razvan
11/23/2020, 5:40 PMsecurity
definition of a ContractRoute or have do be done apart ?janvladimirmostert
11/28/2020, 9:42 AMval app = { request: Request ->
}
val server = app.asServer(Netty(9000)).start()
is the inside of that lambda a single thread or using some kind of event loop or is that server dependent?
when load testing (with Netty(9000)
), I'm seeing nioEventLoopGroup-2-1 and nioEventLoopGroup-3-1..24, I'm assuming those 25 threads are from Netty?
how would one execute suspendable functions (or even blocking functions) inside that lambda?
val app = { request: Request ->
GlobalScope.launch {
suspendableFunctionThatReturnsResponse()
}
}
val server = app.asServer(Netty(9000)).start() // obviously doesn't compile since that app lambda doesn't return a Response
Nikky
11/30/2020, 6:50 PMRazvan
12/02/2020, 6:51 PMobject CustomJackson : ConfigurableJackson(KotlinModule()
.asConfigurable()
.withStandardMappings()
.done()
.deactivateDefaultTyping()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true)
.configure(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS, true)
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
)
val obj = Body.auto<MyObject>().toLens()
in the json response I have the null object....
Trying to dig around, i see that Body.auto
uses the mapper that is ConfigurableJackson class but can't figure out how to make sure it's the one from my CustomJackson that's used and not the default Jackson object ?
any clues ?Razvan
12/02/2020, 6:51 PMobject CustomJackson : ConfigurableJackson(KotlinModule()
.asConfigurable()
.withStandardMappings()
.done()
.deactivateDefaultTyping()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true)
.configure(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS, true)
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
)
val obj = Body.auto<MyObject>().toLens()
in the json response I have the null object....
Trying to dig around, i see that Body.auto
uses the mapper that is ConfigurableJackson class but can't figure out how to make sure it's the one from my CustomJackson that's used and not the default Jackson object ?
any clues ?