Tell me how to work with ServerRequest. If I need to get N parameters.
I find simple example with 1 parameter.
Reactive Spring Query Parameters
request
.getQueryParam("type")
.map(type -> service.getAddressByType(type))
.orElseGet(() -> service.getAllAddresses());
You can use getQueryParams to get N parameters as a map.
getQueryParams() returns MultiValueMap, so you can handle query params as a map.
Let me make small example like your code block.
val queryParamsMap = request.queryParams()
queryParamsMap["type"]?.let { type -> service.getAddressByType(type) } ?: let { service.getAllAddresses() }
Related
I'm writing some tests using rest-assured and its Kotlin extensions to test some simple Spring MVC endpoints. I'm trying to understand how to extract values.
One endpoint returns a BookDetailsView POJO, the other returns a Page<BookDetailsView> (where Page is an interface provided by Spring for doing paging).
BookDetailsView is a really simple Kotlin data class with a single field:
data class BookDetailsView(val id: UUID)
For the single object endpoint, I have:
#Test
fun `single object`() {
val details = BookDetailsView(UUID.randomUUID())
whenever(bookDetailsService.getBookDetails(details.id)).thenReturn(details)
val result: BookDetailsView = Given {
mockMvc(mockMvc)
} When {
get("/book_details/${details.id}")
} Then {
statusCode(HttpStatus.SC_OK)
} Extract {
`as`(BookDetailsView::class.java)
}
assertEquals(details.id, result.id)
}
This works as expected, but trying to apply the same technique for the Page<BookDetailsView> runs afoul of all sorts of parsing challenges since Page is an interface, and even trying to use PageImpl isn't entirely straightforward. In the end, I don't even really care about the Page object, I just care about the nested list of POJOs inside it.
I've tried various permutations like the code below to just grab the bit I care about:
#Test
fun `extract nested`() {
val page = PageImpl(listOf(
BookDetailsView(UUID.randomUUID())
))
whenever(bookDetailsService.getBookDetailsPaged(any())).thenReturn(page)
val response = Given {
mockMvc(mockMvc)
} When {
get("/book_details")
} Then {
statusCode(HttpStatus.SC_OK)
body("content.size()", `is`(1))
body("content[0].id", equalTo(page.first().id.toString()))
} Extract {
path<List<BookDetailsView>>("content")
}
println(response[0].javaClass)
}
The final println spits out class java.util.LinkedHashMap. If instead I try to actually use the object, I get class java.util.LinkedHashMap cannot be cast to class BookDetailsView. There are lots of questions and answers related to this, and I understand it's ultimately an issue of the underlying JSON parser not knowing what to do, but I'm not clear on:
Why does the "simple" case parse without issue?
Shouldn't the type param passed to the path() function tell it what type to use?
What needs configuring to make the second case work, OR
Is there some other approach for grabbing a nested object that would make more sense?
Digging a bit into the code, it appears that the two cases may actually be using different json parsers/configurations (the former seems to stick to rest-assured JSON parsing, while the latter ends up in JsonPath's?)
I don't know kotlin but here is the thing:
path() doesn't know the Element in your List, so it'll be LinkedHashMap by default instead of BookDetailsView.class
to overcome it, you can provide TypeReference for this.
java example
List<BookDetailsView> response = ....then()
.extract().jsonPath()
.getObject("content", new TypeRef<List<BookDetailsView>>() {});
kotlin example
#Test
fun `extract nested`() {
var response = RestAssured.given().get("http://localhost:8000/req1")
.then()
.extract()
.jsonPath()
.getObject("content", object : TypeRef<List<BookDetailsView?>?>() {});
println(response)
//[{id=1}, {id=2}]
}
I am sure this has been already asked but I can't really describe it and search for it. I hope it gets clear what I try to accomplish from the following code:
userRepository.findAll()
.flatMap(u -> commentRepository.findByUser(u.getId))
.flatMap( // here i get of course the comment object but i also need the previous user object )
You can chain the second flatMap internally with the commentRepository.findByUser(u.getId) and then perform the desired operation inside that internal flatMap
This will look like :
userRepository.findAll()
.flatMap(u -> commentRepository.findByUser(u.getId).flatMap(comment -> {
// operation on comment
// operation on u
// return result
}));
You may also want to return the final result of the whole chain above, in order to maintain the chaining.
You need to use zip, so in next flatMap you will get tupple with item from findAll and then findUser.
userRepository.findAll()
.flatMap(u -> Mono.zip(Mono.just(u), commentRepository.findByUser(u.getId)))
.flatMap(tuple-> tuple.getT1() || tuple.getT2() )
Did you try this?
userRepository.findAll()
.flatMap(u -> {
List<Comment> commentsByUserId = commentRepository.findByUser(u.getId);
commentsByUserId.flatMap(// rest of the code)
// code
})
I'm trying to build a request with HttpRequestBuilder in ktor. I don't understand how I pass in the url. I would logically imagine we would pass in .url("https://url.com") as one of the items in the builder along with a few other things then call .build(), but the API docs say that the url function is meant to be passed in as fun url(block: URLBuilder.(URLBuilder) -> Unit): Unit.
Can someone help me understand how to pass in URLBuilder.(URLBuilder) -> Unit with an example please? I don't quite understand what it's trying to ask me to pass in.
The url() function you're referring to requires you to pass in an extension function of URLBuilder. For example:
val builder = HttpRequestBuilder()
builder.url {
protocol = URLProtocol.HTTPS
host = "stackoverflow.com"
encodedPath = "/somePath"
}
val request = builder.build()
I'd like to be able to use Micronaut's declarative client to hit an a different endpoint based on whether I'm in a local development environment vs a production environment.
I'm setting my client's base uri in application.dev.yml:
myserviceclient:
baseUri: http://localhost:1080/endpoint
Reading the docs from Micronaut, they have the developer jumping through quite a few hoops to get a dynamic value piped into the actual client. They're actually quite confusing. So I've created a configuration like this:
#ConfigurationProperties(PREFIX)
class MyServiceClientConfig {
companion object {
const val PREFIX = "myserviceclient"
const val BASE_URL = "http://localhost:1080/endpoint"
}
var baseUri: String? = null
fun toMap(): MutableMap<String, Any> {
val m = HashMap<String, Any>()
if (baseUri != null) {
m["baseUri"] = baseUri!!
}
return m
}
}
But as you can see, that's not actually reading any values from application.yml, it's simply setting a const value as a static on the class. I'd like that BASE_URL value to be dynamic based on which environment I'm in.
To use this class, I've created a declarative client like this:
#Client(MyServiceClientConfig.BASE_URL)
interface MyServiceClient {
#Post("/user/kfc")
#Produces("application/json")
fun sendUserKfc(transactionDto: TransactionDto)
}
The docs show an example where they're interpolating values from the config map that's built like this:
#Get("/api/\${bintray.apiversion}/repos/\${bintray.organization}/\${bintray.repository}/packages")
But how would I make this work in the #Client() annotation?
Nowhere in that example do they show how bintray is getting defined/injected/etc. This appears to be the same syntax that's used with the #Value() annotation. I've tried using that as well, but every value I try to use ends up being null.
This is very frustrating, but I'm sure I'm missing a key piece that will make this all work.
I'm setting my client's base uri in application.dev.yml
You probably want application-dev.yml.
But how would I make this work in the #Client() annotation?
You can put a config key in the #Client value using something like #Client("${myserviceclient.baseUri}").
If you want the url somewhere in your code use this:
#Value("${micronaut.http.services.occupancy.urls}")
private String occupancyUrl;
I want to unit test the return value of some code which looks similar to this:
Groovy Service code to test:
def findByThisAndThat(something) {
:
def items = []
sql.eachRow(query, criteriaList, {
def item = new Item(name:it.NAME)
items.add(item)
})
[items: items, whatever:whatevervalue]
}
Unit test code plan:
void testFindByThisAndThatReturnsAMapContainingItems(){
Sql.metaClass.eachRow = { String query, List criteria, Closure c ->
// call closure to get passed in items list
// add one new Item(name:"test item") to list
}
def result = service.findByThisAndThat("", "")
assert result.items
assertEquals('test item', result.items[0].name)
}
How can I do that?
Thanks!
A unit test like you're proposing really just tests that given proper data from the database, you assemble it correctly into Item instances. I'd switch instead to an integration test where you have access to a real database and test the whole method with test data in the db.
Unit testing database access typically is more of a test of the mocking code than of your code, so it's often of little use.
Call the closure by using it like a method. Alternatively, you can use Closure.call() as well. Pass in the value for it as the first parameter.
Sql.metaClass.eachRow = { String query, List criteria, Closure c ->
def mockItems = ["test item"]
mockItems.each { item ->
c(item)
// c.call(item) works too
}
}
Note that the Sql metaClass won't get reset at the end of the test. I'd recommend clearing it out after the test:
Sql.metaClass = null