Extracting Nested POJO Object with Rest-Assured - kotlin

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}]
}

Related

How to send a List<Map<String, String>> as parameter for a GET API

I have a requirement to pass List<Map<String, String>> as a parameter for REST GET API.
I need help to know how this can be passed from Postman or similar tool.
I tried to set it as a BODY for a GET API, it is giving me errors.
400. That’s an error.
Your client has issued a malformed or illegal request. That’s all we know.
Any help is appreciated.
You can very well !
I tried and this worked for me
Create a model class which has variable of type List<Map<String, String>> myList;
Define a controller similar to below
#PostMapping("/addList")
public ResponseEntity<List<Map<String, String>>> passList(#RequestBody ListModel listModel) {
System.out.println("List mapped " + listModel);
return new ResponseEntity<>(HttpStatus.CREATED);
}
Create a request from Postman or any tool like this
{
"myList": [
{
"one": "1",
"two": "2"
}
]
}
Response I got
List mapped ListModel [myList=[{one=1, two=2}]]
Make sure you map correct variable name ( for e.g. I have defined myList, so that must be passed so it gets properly mapped in Controller class ) also assuming toString, GetterSetters , and your familiarity with few basic REST annotations related to Spring/SpringBoot :)

Kotlin (Ktor) request class for multipart form data

I want to create a request class that collects all the parts (files and items) and validate it, something similar to the example I put (below) with the json requests.
REQUEST JSON SERIALIZABLE EXAMPLE CLASS
import kotlinx.serialization.Serializable
#Serializable
class CreateGroupRequest(
val name: String,
val description: String? = null,
val visibility: String? = "PUBLIC"
)
HANDLE JSON REQUEST EXAMPLE
route("create") {
post {
val request = call.receive<CreateGroupRequest>()
try {
//CODE
call.respond(HttpStatusCode.OK)
} catch (e: SharedDomainException) {
call.respond(HttpStatusCode(e.errorCode, e.errorMessage))
}
}
}
What I mean, for example, is that in this case I want to change it because the groups also has a profile photo that I want to upload or in other cases, posts domain has text, author and a multiple images.
I have read this stackOverflow post but I can't see how I can make a general class to read the multipart requests without having to duplicate code in each handler.
So, does anyone know how I can read the request multipart-form-data body in a shared class and validate it with kotlin/ktor?
In principle, you can use the ContentNegotiation and register a content converter for the multipart/form-data Content-type. In the convertForReceive method you can use CIOMultipartDataBase to parse multipart data and then deserialize it using kotlinx.serialization library. For deserialize method call you need to provide a decoder for the MultiPartData objects, which you need to implement.
The above approach will work but is very inefficient for parts with a large binary body because parts in an HTTP message go one after another so all of them will be eagerly read into memory.

(Problem solved) Set the value of a livedata variable of type <data class> to a list of strings?

How to populate the value of this variable:
private val _urlList = MutableLiveData<List<Url>>()
of type Url:
data class Url(
val imgSrcUrl: String
)
with the incoming list of url strings from a firebase call?
Here is where the magic happens:
private fun getData(){
viewModelScope.launch {
try {
getImagesUrl {
"Here where I need to set the value of the variable to a listOf(it) with it being strings
of urls retrieved from firebase storage"
}
}catch (e: Exception){
"Handling the error"
}
}
}
Edit
The map function #dominicoder provided solved my problem, answer accepted.
Thank you all for your help
Your question is unclear because you're showing a live data of a single Url object but asking to stuff it with a list of strings. So first, your live data object needs to change to a list of Urls:
private val _urlList = MutableLiveData<List<Url>>()
Then, assuming getImagesUrl yields a list of strings, if I understood you correctly, then you would map that to a list of Urls:
getImagesUrl { listOfImageUrlStrings ->
_urlList.value = listOfImageUrlStrings.map { imageUrlString -> Url(imageUrlString) }
}
If that does not answer your question, you really need to review it and clarify.
You can set values on the MutableLiveDataObject in two ways (depends on what you're doing).
Setting the value as normal from the UI thread can be done with:
myLiveData.value = myobject
If you're setting it from a background thread like you might in a coroutine with a suspended function or async task etc then use:
myLiveData.postValue(myObject)
It's not clear from your question whether the LiveData is meant to hold a list as you mention both lists and single values. But your LiveData holds a set the values as a collection like a list, set or map. It's can be treated as a whole object so adding a value later needs to have the whole collection set again like:
myLiveData.value = mutableListOf<Url>()
//Response received and object created
myLiveData.value = myLiveData.value.apply {
add(myObject)
}
Or if the value is mutable updating the existing value (preferred as it's cleaner):
myLiveData.value.add(myObject)
The problem with that approach is you're exposing the map as a mutable/writeable object. Allowing accessors to change the values which you might not want.

Micronaut declarative client with base url per environment

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;

Introspection query for EnumValues as a GraphQL fragment in react-component

I'm building a React Native application using GraphQL (Hosted on graph.cool) with a Relay Schema.
I have a QueryRenderer in the top-level component, fetching data for the presentational components using fragments, which is working fine.
My problem: I want to do an introspection query to fetch possible enum values as a list, for a specific field in my schema and fetch these alongside the fragments.
My current query with fragments:
query ReportingContainerQuery {
viewer {
...MainList_items
...
}
}
The MainList_items fragment:
fragment AnimalList_items on Viewer {
allAnimalCategories {
edges {
node{
id
...AnimalListRow_item
}
}
}
}
I got the following query working for fetching enumValues via introspection (using: https://www.graph.cool/forum/t/how-to-access-the-possible-values-of-an-enum-type-created-inside-the-console/23/2):
query {
__type(name: "JOURNAL_ENTRY_GENDER") {
enumValues {
name
}
}
}
But i can't seem to find a way to create a fragment that can be added to the top-level query.
I could just paste the introspection directly into the top-level query, but that would kind of work against the relay framework, as far as I understand it. Since doing it this way I would have to explicitly pass the result down as a props, instead of letting the presentational component specify what it needs and supplying that as a fragment to the QueryRenderer at the top-level and letting the relay framework implicitly pass the query result down to the component.
After some tinkering around i found a way to solve it - it leaves two places to maintain the fragments query, but it was the only way I found that solved it. :)
In my component i defined the following fragment:
fragment GenderTile_items on __Type {
enumValues{
name
}
}
Then in my main container, i expanded the query in the QueryRenderer with the following
query ReportingContainerQuery {
viewer {
...MainList_items
...
}
__type(name: "JOURNAL_ENTRY_GENDER"){
...GenderTile_items
}
}
The resulting enum data from the QueryRenderer is then available in the successblock by passing 'props.__type' down to the component with the corresponding fragment and from there accessing props.items.enumValues (As the prop for the data was defined as 'items' in the fragment (e.g GenderTile_items when following the naming convention 'FileName_propName'. (https://facebook.github.io/relay/docs/fragment-container.html#data-dependencies-with-graphql)).
I then ran into the problem where i wanted to fetch more than one type of enums and the query returned an error with duplicate __type assignments. This can be fixed this by using alias' like this:
query ReportingContainerQuery {
viewer {
...MainList_items
...
}
genderEnums: __type(name: "JOURNAL_ENTRY_GENDER"){
...GenderTile_items
}
otherEnums: __type(name: "JOURNAL_ENTRY_OTHER"){
...OtherComponent_items
}
}
The data is then available via props.[alias] (e.g. 'props.genderEnums' and 'props.otherEnums'), which you then pass to the component with the fragment and as above access it via props.items.enumValues.
Hope that made sense for anyone else running into the same problem as me. :D