This drives me crazy, I have 2 http requests in a sample application:
open class RestController : Controller() {
val api = Rest()
init {
api.baseURI = "http://127.0.0.1:5059/"
}
}
class PendingCtlr : RestController() {
fun load(): ObservableList<PendingEntity> {
val txt = api.get("pendings").list()
val temp = txt.toModel<PendingEntity>()
return temp.observable()
}
}
class ConfirmedCtrl : RestController() {
fun load(id: Long): ObservableList<ConfirmedEntity> {
val li= api.get("confirmeds").list()
val temp = li.toModel<ConfirmedEntity>()
return temp.observable()
}
}
The first one works, the second one doesn't even hit the application level, it gets rejected with 400 BadRequest by my backend (Werkzeug).
I see absolutely no difference in both functions, and I can call both routes from my Swagger, as well as from python as well as from curl! Could someone please advise at least where to look for debug?
EDIT: The problem was on server side -_- Solved
Related
How to verify auth before handle body request?
I'm using vertx:
vertxVersion = '3.8.3'
implementation "io.vertx:vertx-core:$rootProject.vertxVersion"
implementation "io.vertx:vertx-web:$rootProject.vertxVersion"
implementation "io.vertx:vertx-lang-kotlin:$rootProject.vertxVersion"
implementation "io.vertx:vertx-lang-kotlin-coroutines:$rootProject.vertxVersion"
implementation "io.vertx:vertx-mongo-client:$rootProject.vertxVersion"
implementation "io.vertx:vertx-auth-mongo:$rootProject.vertxVersion"
implementation "io.vertx:vertx-auth-jwt:$rootProject.vertxVersion"
I want to verify auth before handle body request. But I got error java.lang.IllegalStateException: Request has already been read
Reproduce by use delay on suspend function:
router.handler { context ->
launch {
context.request().setExpectMultipart(true)
delay(100) //This line is sample for a verify auth process
context.next()
}
}
.handler {context ->
println("2")
context.request()
.handler {
b -> println("buff ${b.length()}")
}
.endHandler {
println("end handle")
context.success("ok")
}
}.baseHandle(
fn
).failureHandler {
println("fail: ${it.failure()}")
it.error()
}
When run delay(100) (this's sample for a verify process), I got the error above. If I comment delay(100), It's will be working fine.
This happens because by the time you auhenticated the request, the content has kept arriving and has been dropped.
You need to invoke context.request().pause() in you first handler and then context.request().resume() when you're ready.
In most cases though it's easier to let the BodyHandler manage payload for you.
Finally, I did solve it.
My router is working with the flows:
router.post("/api/upload/file")
.baseHandle { checkAuthorization(it) }
.handleFileUpload { updateFileOnItem(it) }
And Following step:
fun checkAuthorization(context: RoutingContext) {
val request = context.request()
val tkCookie = request.getCookie("user")
...do something to verify user permission
request.pause()
context.next()
context.request().resume()
}
Next:
fun updateFileOnItem(context: RoutingContext) {
val file = context.fileUploads()
...do something
}
It's working with me. Hope it can be help you. Thanks!
I want to create a long living service that can handle events.
It receives events via postEvent, stores it in repository (with underlying database) and send batch of them api when there are enough events.
Also I'd like to shut it down on demand.
Furthermore I would like to test this service.
This is what I came up so far. Currently I'm struggling with unit testing it.
Either database is shut down prematurely after events are sent to service via fixture.postEvent() or test itself gets in some sort of deadlock (was experimenting with various context + job configurations).
What am I doing wrong here?
class EventSenderService(
private val repository: EventRepository,
private val api: Api,
private val serializer: GsonSerializer,
private val requestBodyBuilder: EventRequestBodyBuilder,
) : EventSender, CoroutineScope {
private val eventBatchSize = 25
val job = Job()
private val channel = Channel<Unit>()
init {
job.start()
launch {
for (event in channel) {
val trackingEventCount = repository.getTrackingEventCount()
if (trackingEventCount < eventBatchSize) continue
readSendDelete()
}
}
}
override val coroutineContext: CoroutineContext
get() = Dispatchers.Default + job
override fun postEvent(event: Event) {
launch(Dispatchers.IO) {
writeEventToDatabase(event)
}
}
override fun close() {
channel.close()
job.cancel()
}
private fun readSendDelete() {
try {
val events = repository.getTrackingEvents(eventBatchSize)
val request = requestBodyBuilder.buildFor(events).blockingGet()
api.postEvents(request).blockingGet()
repository.deleteTrackingEvents(events)
} catch (throwable: Throwable) {
Log.e(throwable)
}
}
private suspend fun writeEventToDatabase(event: Event) {
try {
val trackingEvent = TrackingEvent(eventData = serializer.toJson(event))
repository.insert(trackingEvent)
channel.send(Unit)
} catch (throwable: Throwable) {
throwable.printStackTrace()
Log.e(throwable)
}
}
}
Test
#RunWith(RobolectricTestRunner::class)
class EventSenderServiceTest : CoroutineScope {
#Rule
#JvmField
val instantExecutorRule = InstantTaskExecutorRule()
private val api: Api = mock {
on { postEvents(any()) } doReturn Single.just(BaseResponse())
}
private val serializer: GsonSerializer = mock {
on { toJson<Any>(any()) } doReturn "event_data"
}
private val bodyBuilder: EventRequestBodyBuilder = mock {
on { buildFor(any()) } doReturn Single.just(TypedJsonString.buildRequestBody("[ { event } ]"))
}
val event = Event(EventName.OPEN_APP)
private val database by lazy {
Room.inMemoryDatabaseBuilder(
RuntimeEnvironment.systemContext,
Database::class.java
).allowMainThreadQueries().build()
}
private val repository by lazy { database.getRepo() }
val fixture by lazy {
EventSenderService(
repository = repository,
api = api,
serializer = serializer,
requestBodyBuilder = bodyBuilder,
)
}
override val coroutineContext: CoroutineContext
get() = Dispatchers.Default + fixture.job
#Test
fun eventBundling_success() = runBlocking {
(1..40).map { Event(EventName.OPEN_APP) }.forEach { fixture.postEvent(it) }
fixture.job.children.forEach { it.join() }
verify(api).postEvents(any())
assertEquals(15, eventDao.getTrackingEventCount())
}
}
After updating code as #Marko Topolnik suggested - adding fixture.job.children.forEach { it.join() } test never finishes.
One thing you're doing wrong is related to this:
override fun postEvent(event: Event) {
launch(Dispatchers.IO) {
writeEventToDatabase(event)
}
}
postEvent launches a fire-and-forget async job that will eventually write the event to the database. Your test creates 40 such jobs in rapid succession and, while they're queued, asserts the expected state. I can't work out, though, why you assert 15 events after posting 40.
To fix this issue you should use the line you already have:
fixture.job.join()
but change it to
fixture.job.children.forEach { it.join() }
and place it lower, after the loop that creates the events.
I failed to take into account the long-running consumer job you launch in the init block. This invalidates the advice I gave above to join all children of the master job.
Instead you'll have to make a bit more changes. Make postEvent return the job it launches and collect all these jobs in the test and join them. This is more selective and avoids joining the long-living job.
As a separate issue, your batching approach isn't ideal because it will always wait for a full batch before doing anything. Whenever there's a lull period with no events, the events will be sitting in the incomplete batch indefinitely.
The best approach is natural batching, where you keep eagerly draining the input queue. When there's a big flood of incoming events, the batch will naturally grow, and when they are trickling in, they'll still be served right away. You can see the basic idea here.
I've got pretty simple application that uses Jooby as web framework. Its class responsible for REST looks like this
class Sandbox : Kooby ({
path("/sandbox") {
get {
val environment = require(Config::class).getString("application.env")
"Current environment: $environment"
}
get ("/:name") {
val name = param("name")
"Auto response $name"
}
}
})
I want to write integration test for it. My test looks like this. I use spock and rest-assured. The thing is that I don't have the application running and want to run it using some kind of embedded server or whatever. How to do that?
My simple test looks like this
class SandboxTest extends Specification {
def "check current environment"() {
given:
def request = given()
when:
def response = request.when().get("/sandbox")
then:
response.then().statusCode(200) // for now 404
}
}
You need to look for before/after test (or class) hooks in Spock. In the before hook you start Jooby without blocking the thread:
app.start("server.join=false")
in the after hook:
app.stop();
Never used Spock but here is a small extension method for Spek:
fun SpecBody.jooby(app: Jooby, body: SpecBody.() -> Unit) {
beforeGroup {
app.start("server.join=false")
}
body()
afterGroup {
app.stop()
}
}
Finally from your test:
#RunWith(JUnitPlatform::class)
object AppTest : Spek({
jooby(App()) {
describe("Get with query parameter") {
given("queryParameter name=Kotlin") {
it("should return Hello Kotlin!") {
val name = "Kotlin"
given()
.queryParam("name", name)
.`when`()
.get("/")
.then()
.assertThat()
.statusCode(Status.OK.value())
.extract()
.asString()
.let {
assertEquals(it, "Hello $name!")
}
}
...
...
...
...
Maven Spek example
Gradle Spek example
Here is my simple routing application:
object Main extends App with SimpleRoutingApp {
implicit val system = ActorSystem("my-system")
startServer(interface = "0.0.0.0", port = System.getenv("PORT").toInt) {
import format.UsageJsonFormat._
import spray.httpx.SprayJsonSupport._
path("") {
get {
complete("OK")
}
} ~
path("meter" / JavaUUID) {
meterUUID => pathEnd {
post {
entity(as[Usage]) {
usage =>
// execute some logic asynchronously
// do not wait for the result
complete("OK")
}
}
}
}
}
}
What I want to achieve is to execute some logic asynchronously in my path directive, do not wait for the result and return immediately HTTP 200 OK.
I am quite new to Scala and spray and wondering if there is any spray way to solve this specific problem. Otherwise I would go into direction of creating Actor for every request and letting it to do the job. Please advice.
There's no special way of handling this in spray: simply fire your async action (a method returning a Future, a message sent to an actor, whatever) and call complete right after.
def doStuffAsync = Future {
// literally anything
}
path("meter" / JavaUUID) { meterUUID =>
pathEnd {
post {
entity(as[Usage]) { usage =>
doStuffAsync()
complete("OK")
}
}
}
}
Conversely, if you need to wait for an async action to complete before sending the response, you can use spray-specific directives for working with Futures or Actors.
Helo,
at the beginning i wold like to apologize for my english :)
akka=2.3.6
spray=1.3.2
scalatest=2.2.1
I encountered strange behavior of teting routes, which asks actors in handleWith directive,
I've route with handleWith directive
pathPrefix("firstPath") {
pathEnd {
get(complete("Hello from this api")) ~
post(handleWith { (data: Data) =>{ println("receiving data")
(dataCalculator ? data).collect {
case Success(_) =>
Right(Created -> "")
case throwable: MyInternalValidatationException =>
Left(BadRequest -> s"""{"${throwable.subject}" : "${throwable.cause}"}""")
}
}})
}
}
and simple actor wchich always responds when receive object Data and has own receive block wrapped in LoggingReceive, so I should see logs when message is receiving by actor
and i test it using (I think simple code)
class SampleStarngeTest extends WordSpec with ThisAppTestBase with OneInstancePerTest
with routeTestingSugar {
val url = "/firstPath/"
implicit val routeTestTimeout = RouteTestTimeout(5 seconds)
def postTest(data: String) = Post(url).withJson(data) ~> routes
"posting" should {
"pass" when {
"data is valid and comes from the identified user" in {
postTest(correctData.copy(createdAt = System.currentTimeMillis()).asJson) ~> check {
print(entity)
status shouldBe Created
}
}
"report is valid and comes from the anonymous" in {
postTest(correctData.copy(createdAt = System.currentTimeMillis(), adid = "anonymous").asJson) ~> check {
status shouldBe Created
}
}
}
}
}
and behavior:
When I run either all tests in package (using Intellij Idea 14 Ultimate) or sbt test I encounter the same results
one execution -> all tests pass
and next one -> not all pass, this which not pass I can see:
1. fail becouse Request was neither completed nor rejected within X seconds ( X up tp 60)
2. system console output from route from line post(handleWith { (data: Data) =>{ println("receiving data"), so code in handleWith was executed
3. ask timeout exception from route code, but not always (among failed tests)
4. no logs from actor LoggingReceive, so actor hasn't chance to respond
5. when I rerun teststhe results are even different from the previous
Is there problem with threading? or test modules, thread blocking inside libraries? or sth else? I've no idea why it isn't work :(