Exceptions not captured in opencsv parsing - spring-boot

I'm trying to parse a csv file and map it onto a data class. I've setup some validations for the columns and I'm testing it by sending incorrect values for those columns. opencsv throws a generic exception
Basic instantiation of the given bean type (and subordinate beans created through recursion, if applicable) was determined to be impossible.
Code details
Data class:
data class UserInfo(
#CsvBindByName(column = "Id", required = true) val id: Long,
#CsvBindByName(column = "FirstName", required = true) val firstName: String,
#CsvBindByName(column = "LastName", required = true) val lastName: String,
#CsvBindByName(column = "Email", required = true) val email: String,
#CsvBindByName(column = "PhoneNumber", required = true) val phoneNumber: String,
#PreAssignmentValidator(
validator = MustMatchRegexExpression::class, paramString = "^[0-9]{10}$")
#CsvBindByName(column = "Age", required = true)
val age: Int
)
csv parsing logic
fun uploadCsvFile(file: MultipartFile): List<UserInfo> {
throwIfFileEmpty(file)
var fileReader: BufferedReader? = null
try {
fileReader = BufferedReader(InputStreamReader(file.inputStream))
val csvToBean = createCSVToBean(fileReader)
val mappingStrategy: HeaderColumnNameMappingStrategy<Any> =
HeaderColumnNameMappingStrategy<Any>()
mappingStrategy.type = UserInfo::class.java
val userInfos = csvToBean.parse()
userInfos.stream().forEach { user -> println("Parsed data:$user") }
csvToBean.capturedExceptions.stream().forEach { ex -> println(ex.message) }
return userInfos
} catch (ex: Exception) {
throw CsvImportException("Error during csv import")
} finally {
closeFileReader(fileReader)
}
}
private fun createCSVToBean(fileReader: BufferedReader?): CsvToBean<UserInfo> =
CsvToBeanBuilder<UserInfo>(fileReader)
.withType(UserInfo::class.java)
.withThrowExceptions(false)
.withIgnoreLeadingWhiteSpace(true)
.build()
I'm looking for the proper error message for the validation / missing field so that I can communicate it to the error response.

Related

Kotlin MVVM, How to get the latest value from Entity in ViewModel?

I have created an app where I try to insert a record with the latest order number increased by one.
The main function is triggered from Activity, however, the whole process is in my ViewModel.
Issue no 1, After I insert a new record the order by number is not updated.
Issue no 2, When I insert first record the order by number is null, for that reason I am checking for null and setting the value to 0.
My goal here is to get the latest order_by number from Entity in my ViewModel, increased by 1 and add that new number to my new record using fun addTestData(..).
Entity:
#Entity(tableName = "word_table")
data class Word(
#ColumnInfo(name = "id") val id: Int,
#ColumnInfo(name = "word") val word: String,
#ColumnInfo(name = "order_by") val orderBy: Int
Dao:
#Query("SELECT order_by FROM word_table ORDER BY order_by DESC LIMIT 1")
suspend fun getHighestOrderId(): Int
Repository:
#Suppress("RedundantSuspendModifier")
#WorkerThread
suspend fun getHighestOrderId(): Int {
return wordDao.getHighestOrderId()
}
ViewModel:
private var _highestOrderId = MutableLiveData<Int>()
val highestOrderId: LiveData<Int> = _highestOrderId
fun getHighestOrderId() = viewModelScope.launch {
val highestOrderId = repository.getHighestOrderId()
_highestOrderId.postValue(highestOrderId)
}
fun addTestData(text: String) {
for (i in 0..1500) {
getHighestOrderId()
var highestNo = 0
val highestOrderId = highestOrderId.value
if (highestOrderId == null) {
highestNo = 0
} else {
highestNo = highestOrderId
}
val addNumber = highestNo + 1
val word2 = Word(0, text + "_" + addNumber,addNumber)
insertWord(word2)
}
}
Activity:
wordViewModel.addTestData(text)

Filter inside MemberProperties return error : Expected type mismatch: inferred type is KProperty1<out Any!, Any?>? but KProperty1<T, *>? was expected

I'm trying to create a inline function that can return entity column name, but get error when i try to filter list of memberProperties from a instance class that i create
This is the error i get :
Expected type mismatch: inferred type is KProperty1<out Any!, Any?>? but KProperty1<T, *>? was expected
i try the tutorial from this Create a class instance using a string containing the package name and class name
Here is my entity:
#Entity
#Table(name = "employee", schema = "dbo")
data class Employee(
#Id
#Column(name = "id", nullable = false, length = 20)
var id: String,
#Column(name = "employee_name", nullable = true, length = 30)
var employeeName: String? = "",
#ManyToOne
#JoinColumn(name = "dept_code", referencedColumnName = "dept_code", nullable = true)
var dept: MasterDept? = null
)
#Entity
#Table(name = "ms_dept", schema = "dbo")
data class MasterDept(
#Id
#Column(name = "dept_code", nullable = false, length = 20)
var code: String,
#Column(name = "dept_desc", nullable = true, length = 100)
var desc: String? = "",
#ManyToOne
#JoinColumn(name = "div_code", referencedColumnName = "div_code", nullable = true)
var div: MasterDiv? = null
)
#Entity
#Table(name = "ms_div", schema = "dbo")
data class MasterDiv(
#Id
#Column(name = "div_code", nullable = false, length = 20)
var code: String,
#Column(name = "div_desc", nullable = true, length = 100)
var desc: String? = ""
)
i try to get column name from property desc of entity MasterDept or MasterDiv, depend of usage
here is my function :
inline fun <reified T : Any> getColumnName(filt:String):String {
var listFilter:List<String> = listOf();
if(filt.contains(".", true)){
listFilter = filt.split(".")
filt = listFilter.get(0)
}
var property = T::class.memberProperties.filter { prop -> prop.name == filt}.firstOrNull()//here is working
var colName : String = ""
var tableName:String = ""
if(listFilter.size > 1){
var idx:Int = 1
for(item : String in listFilter) {
if(idx < listFilter.size){
val cls = Class.forName(property!!.returnType.toString()).kotlin
var table = cls.annotations.find { it.annotationClass == Table::class } as? Table
tableName = table!!.name
property = cls.memberProperties.filter { prop -> prop.name == listFilter.get(idx)}.firstOrNull() //error in here
}
idx++
}
}
var field = property!!.javaField!!
var column = field.getAnnotation(Column::class.java)
return tableName + "." + column!!.name()
}
filt parameter sometimes is "employeeName", "dept.desc" or "dept.div.desc"
am i missing something??
I would be glad for any help.
Kotlin is a strongly typed language. You can't declare a variable with a type KProperty1<T, *>? and reassign it with a value of type KProperty1<out Any, *>? (because the latter is not a subtype of the former). Actually, you haven't declared these types explicitly; they were inferred by the compiler.
Not sure what's going on in this code (and if it is possible to avoid mutation of the property variable, which is preferrable), but you can fix this error by declaring property's type as KProperty1<*, *>?, which is a supertype of all other KProperty1 types; also its declaration could be simplified:
var property: KProperty1<*, *>? = T::class.memberProperties.firstOrNull { it.name == filt }

Spring - Springboot controller GET requests with default parameters

I have a Springboot controller with two GET endpoints:
#RestController
#RequestMapping("/foo")
class MyController {
fun getFoo(
#RequestParam("x", required = false) x: Int = 0
) = ...
fun getFoo(
#RequestParam("x", required = true) x: Int,
#RequestParam("y", required = true) y: Int
) = ...
The behaviour I want is that when called:
/foo calls the first endpoint with an optional x param.
/foo?x=123 calls the first endpoint with a supplied x param.
/foo?x=123&y=456' calls the second endpoint with the supplied xandy` params.
Currently I get an error:
{
"timestamp": "2020-07-20T13:11:24.732+0000",
"status": 400,
"error": "Bad Request",
"message": "Parameter conditions \"x\" OR \"x, y\" not met for actual request parameters: ",
"path": "/foo"
}
Any ideas how to determine a default endpoint when zero params are specified?
Set a params in #RequestMapping or its variants(#GetMapping, #PostMapping etc).
eg.
#GetMapping(params=arrayOf("!x", "!y"))
fun getFoo()
#GetMapping(params=arrayOf("x", "!y"))
fun getFoo(
#RequestParam("x", required = true) x: Int = 0
#GetMapping(params=arrayOf("x", "y"))
fun getFoo(
#RequestParam("x", required = true) x: Int,
#RequestParam("y", required = true) y: Int
Different params can be applied and identified on the same URI.
You can specify a defaultValue as a String in #RequestParam:
fun getFoo(
#RequestParam(name = "x", required = false, defaultValue = "0") x: Int,
#RequestParam(name = "y", required = false, defaultValue = "1") y: Int
) =
Spring will convert the String to whatever type you really want (Int in your case) using the same method as if you specified x as a parameter (same coercion, errors, etc).
If you want more then one mapping to the same url (lot of times it is a codesmell, but sometime it is necessary, you can do i twith filter params in #GetMapping or #RequestMapping annotations' params element.
#RestController
#RequestMapping("/foo")
class MyController {
#GetMapping(params = ["!y"])
fun getFoo(
#RequestParam("x", required = false) x: Int?
) = ...
#GetMapping(params = ["x", "y"])
fun getFoo(
#RequestParam("x", required = true) x: Int,
#RequestParam("y", required = true) y: Int
) = ...
}

Kotlin iterator to check if payload list have an id/projectId or not, returning false when there is no attributes?

I have an issue where i have a method where i am checking the payload has the attributes or not. When i am sending my payload i want to check that the user dont have inserted attributes which not allowed in the payload.
My entity class:
#Entity
data class ProjectAssociated(
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(columnDefinition = "BINARY(16)")
var id: UUID? = null,
#Column(columnDefinition = "BINARY(16)")
var projectId: UUID? = null,
#Column(columnDefinition = "BINARY(16)")
var associatedProjectId: UUID? = null
)
My Service class:
fun addAssociatedProjectByProjectId(
projectId: UUID,
projectAssociatedList: MutableList<ProjectAssociated>
): MutableList<ProjectAssociated> {
if (projectAssociatedList.isNotEmpty()) {
println(projectAssociatedList)
if (!projectAssociatedList.map { it.id }.isNullOrEmpty()) {
val errorMessage = "Not allowed to provide parameter 'id' in this request"
throw UserInputValidationException(errorMessage)
}
if (!projectAssociatedList.map { it.projectId }.isNullOrEmpty()) {
val errorMessage = "Not allowed to provide parameter 'projectId' in this request"
throw UserInputValidationException(errorMessage)
}
val checkIds = projectAssociatedList.map {
projectRepository.existsById(it.associatedProjectId)
}
if (checkIds.contains(false)) {
val errorMessage = "One or more ID 'associatedProjectId' not exists"
throw UserInputValidationException(errorMessage)
}
}
return projectAssociatedList.map {
projectAssociatedRepository.save(
ProjectAssociated(
null,
projectId,
it.associatedProjectId
)
)
}.toMutableList()
}
My Controller class:
#ApiOperation("Add associated Projects to a specific Project")
#PostMapping(path = ["/project-associated"], consumes = [MediaType.APPLICATION_JSON_VALUE])
fun createAssociatedProjectList(
#ApiParam("The id of the Project", required = true)
#RequestParam("id")
id: UUID,
#ApiParam("JSON object representing the ProjectAssociated")
#RequestBody projectAssociated: MutableList<ProjectAssociated>
): ResponseEntity<WrappedResponse<MutableList<ProjectAssociated>>> {
val createdProjectAssociatedList = projectService.addAssociatedProjectByProjectId(id, projectAssociated)
return ResponseEntity
.status(201)
.location(URI.create("$id/project-associated"))
.body(
ResponseDto(
code = 201,
data = PageDto(list = mutableListOf(createdProjectAssociatedList))
).validated()
)
}
But when i try to send this payload with the project id in #RequestParam:
[
{
"associatedProjectId": "7fe40f90-5178-11ea-9136-1b65a920a5d9"
},
{
"associatedProjectId": "7fe8aaaa-5178-11ea-9136-1b65a920a5d9"
}
]
I have a custom exception where i tell the user if projectId or the id is in the payload that is now allowed to have it in the payload. When i try to POST the payload example above it tells me that projectId or id is in the request? How can that be?
I also printed out the list before if checks:
[ProjectAssociated(id=null, projectId=null, associatedProjectId=7fe40f90-5178-11ea-9136-1b65a920a5d9), ProjectAssociated(id=null, projectId=null, associatedProjectId=7fe8aaaa-5178-11ea-9136-1b65a920a5d9)]
What am I doing wrong?
Thanks for the help!
In the block projectAssociatedList.map { it.id } you are mapping your list to something like [null, null] and it is not null or empty.
So, the complete condition !projectAssociatedList.map { it.id }.isNullOrEmpty() returns true.
If you want to continue using the same logic, you should use !projectAssociatedList.mapNotNull { it.id }.isNullOrEmpty() instead.
The mapNotNull function will filter the null values and output a list just with the not null values. If there is only null values, the list will be empty.
But, a simpler and expressive way to check if there is any not null attribute in a list of objects could be projectAssociatedList.any { it.id != null }

In Play Framework 2, how do I pass in an external value for form validators to use?

This can't be too uncommon. I want to get a "fullPathAndFileName" value, that's not in my form, into a validator.
In one of my controllers I have a file renaming action:
def renameAction(fullPathAndFileName: String) = Action { implicit request =>
val newRenameForm = renameForm.bindFromRequest()
newRenameForm.fold(
hasErrors = { form =>
Redirect(routes.TestApp.renderFormAction(fullPathAndFileName)).flashing("error" -> "New filename must be unused and cannot be empty.")
},
success = { newFileName =>
...
Here's my validator:
private val renameForm: Form[String] = Form("newFileName" -> nonEmptyText.verifying({txt => dupeNotFound(txt)}))
private def dupeNotFound(newFileName: String) = { !Asset.findAsset(replaceFileNameOfAsset(fullPathAndFileName, newFileName)) }
So this code won't compile, because fullPathAndFileName is not defined. How can I let the validator use that value?
Posting as answer instead of just commenting...anyway, this should work if I understand things correctly...
val newRenameForm = renameForm(fullPathAndFileName).bindFromRequest()
And the validator...
private val renameForm: (String) => Form[String] = (fullPathAndFileName: String) => Form("newFileName" -> nonEmptyText.verifying({txt => dupeNotFound(fullPathAndFileName,txt)}))
private def dupeNotFound(fullPathAndFileName: String, newFileName: String) = { !Asset.findAsset(replaceFileNameOfAsset(fullPathAndFileName, newFileName)) }

Resources