Swagger 2 UI How to show models that are not explicitly returned by RestController - spring-boot

I'm having following issue, on swagger under Models, i see just abstract Base class that is extended by 3 other classes. My current end point returns Base type of class, because i can have 3 different types returned on one end point.
So basically i have something like this
#MappedSuperclass
#ApiModel(description = "Base Details.")
abstract class BaseClass(
open var id: String? = null,
var prop1: String? = null,
var prop2: String? = null,
var prop3: String? = null,
var prop4: String? = null
)
#ApiModel(description = "Some Specific Details that contains all base properties.")
data class AnotherClass(
val prop4: String,
val prop5: String,
val prop6: Set<Amount>,
val prop7: Set<Amount>,
val prop8: String
) : BaseClass()
#ApiModel(description = "Some more Specific Details that contains all base properties.")
data class OneMoreClass(
val prop4: String,
val prop5: String
) : BaseClass()
And in RestController i have this
#GetMapping
#ApiOperation(value = "End point description", notes = "Notes notes notes.")
fun getSomethingFromDatabase(): List<BaseClass> {
return someService.getData();
}
So issue that i have is on swagger UI, under Models section i see just BaseClass and no other classes at all...
I tried this, because somewhere i seen this example:
#ApiModel(description = "Base Details.", subTypes = {AnotherClass.class})
BaseClass
but this way i have "kotlin" issue, that is saying "name is missing", also i can not do AnotherClass::class...

You will have to add those in the config as below:
return new Docket(DocumentationType.SWAGGER_2)
.additionalModels(typeResolver.resolve(AnotherClass.class), typeResolver.resolve(OneMoreClass.class))
.....
subTypes is still not completely supported in Swagger 2, still has an open ticket
For your Kotlin config, this is how it should look like:
subTypes = [AnotherClass::class, OneMoreClass::class]
I have just added a sample Kotlin controller for you to refer in my github project. Look for AnimalController.kt & SwaggerConfig for required setup.

Related

SonarCloud coverage stating that class property with JsonProperty annotation not covered by tests

I have a Kotlin project with Spring, and I created a class that looks like the following:
#JsonIgnoreProperties(ignoreUnknown = true)
data class Response(
val id: String,
#JsonProperty("quantity_of_days")
val quantityOfDays: Int,
)
And my SonarCloud reports state that the quantityOfDays line is not covered by tests:
This line is accessed multiple times inside my tests, and I even created one specifically to instantiate an object of that class. However, this line is still marked as not covered.
I wonder if it has something to do with the annotation, and if so, how do I ignore or force this line to be covered?
Ok so, it was necessary to write some very specific tests to cover that:
class ResponseTest {
#Test
fun `create response from json with underline attribute`() {
val id = "123"
val quantityOfDays = 1
val response = Response(id, quantityOfDays)
val value = Mapper.objectMapper.readValue(
"""
{
"id": "$id",
"quantity_of_days": $quantityOfDays
}
""".trimIndent(), Response::class.java)
assertThat(value).isEqualTo(response)
}
#Test
fun `create response from json with camel case attribute`() {
val response = ResponseBuilder().build()
val json = Mapper.objectMapper.writeValueAsString(response)
val value = Mapper.objectMapper.readValue(json, Response::class.java)
assertThat(value).isEqualTo(response)
}
}
I am not sure if that is the best solution, maybe there is a way to make the coverage ignore that in specific, but I could not find it. But it works.

Javax validation of generics in Springboot with Kotlin

I have a controller:
#PostMapping
fun create(
#RequestBody #Valid request: MyContainer<CreateRequest>,
): MyContainer<Dto> = service.create(request.objects)
with MyContainer and CreateRequest looking something like this:
class MyContainer<T>(
#field:Valid // also tried param
#field:NotEmpty(message = "The list of objects can not be null or empty")
var objects: List<#Valid T>? = listOf(),
)
class CreateRequest(
#field:NotNull(message = "Value can not be null")
var value: BigDecimal? = null,
)
In my tests, the "outer" validation works, that is I do get the expected error message if I send it { "objects": null } or { "objects": [] }. But I can not get it to validate the contents of the list. From what I understand in Java List<#Valid T> should work, but for whatever I can not get it to work in kotlin.
I figured I might need some kind of use-site target on #Valid in List<#Valid T>, but I can't find one that's applicable for this use case.
How can I get the validation to work for the list?
I managed to find a solution myself.
Apparently get: is the correct use-site target, not field: or param:. Furthermore the #Valid in List<#Valid T> was not necessary.
For reference, here's the working class (also changed it back to a data class as that doesn't seem to pose an issue).
class MyContainer<T>(
#get:Valid
#get:NotEmpty(message = "The list of objects can not be null or empty")
var objects: List<T>? = listOf(),
)
and the CreateRequest:
class CreateRequest(
#get:NotNull(message = "Value can not be null")
var value: BigDecimal? = null,
)
Changing to the get: use-site target was only necessary for #Valid, but I opted for using it everywhere for consistency and since it seems to be the one that works best.

Enums in kotlin

I want to access the keys for an item in enum class
enum class Events {
REFER_AND_EARN {
val key: String = "Refer and Earn"
val source: String = "Source"
},
REFILL_PAST_MEDICINE_CLICK {
val key: String = "Refill Past Medicine Click"
val source: String = "Source"
val pointOfInitiation: String = "Point of initiation"
}
}
Like for the above enum class can I access source like this??
Events.REFER_AND_EARN.source
You can do what you want to achieve by writing this:
enum class Events(val key: String, val source: String, val pointOfInitiation: String? = null) {
REFER_AND_EARN(key = "Refer and Earn", source = "Source"),
REFILL_PAST_MEDICINE_CLICK(
key = "Refill Past Medicine Click",
source = "Source",
pointOfInitiation = "Point of initiation"
)
}
Enum constants do not declare new types themselves. This means that you can't simply access these properties: they are public, but there's no access to the type where they are declared.
You can implement an interface by enum and expose these properties by overriding ones from interface.
Or you can declare a sealed class instead of enum class and use object declarations instead of enum constants.
You need to use properties instead:
enum class Events(val key: String,
val source: String,
val pointOfInitiation: String) {
REFER_AND_EARN("Refer and Earn",
"Source",
"Unknown"),
REFILL_PAST_MEDICINE_CLICK(
"Refill Past Medicine Click",
"Source",
"Point of initiation"
);
}
Or you can use a sealed class as others mentioned.

Kotlin not nullable value can be null?

I have backend that return me some json.
I parse it to my class:
class SomeData(
#SerializedName("user_name") val name: String,
#SerializedName("user_city") val city: String,
var notNullableValue: String
)
Use gson converter factory:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ENDPOINT)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
And in my interface:
interface MyAPI {
#GET("get_data")
Observable<List<SomeData>> getSomeData();
}
Then I retrieve data from the server (with rxJava) without any error. But I expected an error because I thought I should do something like this (to prevent GSON converter error, because notNullableValue is not present in my JSON response):
class SomeData #JvmOverloads constructor(
#SerializedName("user_name") val name: String,
#SerializedName("user_city") val city: String,
var notNullableValue: String = ""
)
After the data is received from backend and parsed to my SomeData class with constructor without def value, the value of the notNullableValue == null.
As I understand not nullable value can be null in Kotlin?
Yes, that is because you're giving it a default value. Ofcourse it will never be null. That's the whole point of a default value.
Remove ="" from constructor and you will get an error.
Edit: Found the issue. GSON uses the magic sun.misc.Unsafe class which has an allocateInstance method which is obviously considered very unsafe because what it does is skip initialization (constructors/field initializers and the like) and security checks. So there is your answer why a Kotlin non-nullable field can be null. Offending code is in com/google/gson/internal/ConstructorConstructor.java:223
Some interesting details about the Unsafe class: http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/
Try to override constructor like this:
class SomeData(
#SerializedName("user_name") val name: String,
#SerializedName("user_city") val city: String,
var notNullableValue: String = "") {
constructor() : this("","","")
}
Now after server response you can check the notNullableValue is not null - its empty

SpEL not able to extract attribute value from Scala object

I have a simple Scala class called Case
case class Case(
#(Id#field) var id: String,
var state: CaseState = new OpenCaseState,
var notes: List[CaseNote] = new ArrayList(),
var assignedGroups:Set[String] = new HashSet(),
var aclTemplateIds: Set[String] = new HashSet()
) extends Serializable { }
I created an instance of this class called a_case, setting id as 123. I am trying to get the value of the id attribute. I tried this
var parser: ExpressionParser = new SpelExpressionParser
var context: EvaluationContext = new StandardEvaluationContext(a_case)
var extractedId = parser.parseExpression("'id'").getValue(context).asInstanceOf[String]
All I get is "id" in my extractedId variable. When I try to parse "id" without the single quotes, I get an exception saying the property id is not found in Case. Am I missing something here or is this a Scala issue?
SpEL can do that for you if your id has getter.
I'm not well with Scala, but:
BeanProperty
You can annotate vals and vars with the #BeanProperty annotation. This generates getters/setters that look like POJO getter/setter definitions. If you want the isFoo variant, use the BooleanBeanProperty annotation. The ugly foo$_eq becomes
setFoo("newfoo");
getFoo();
https://twitter.github.io/scala_school/java.html

Resources