Return different Dto using same repository method with Spring Boot repositories - spring-boot

I have two DTOs
One for a get method like "/pessoas/{id}"
And another one for "/pessoas/{id}/detalhes" where I am going to see more attributes of Pessoa.
My codes are in Kotlin.
My simple DTO:
interface PessoaDTO {
val idInstitucional: UUID?
val nome: String?
}
data class PessoaDTOImpl(override val idInstitucional: UUID?, override val nome: String?): PessoaDTO
My DTO with details:
interface PessoaDetalhesDTO {
val idInstitucional: UUID?
val nome: String?
val email: String?
val telefone: String?
val cpf: Long?
}
data class PessoaDetalhesDTOImpl(override val idInstitucional: UUID?, override val nome: String?, override val email: String?, override val telefone: String?, override val cpf: Long?): PessoaDetalhesDTO
I have a repository that will be accessed by my PessoaController. I was thinking about having two methods in my Repository, each one for a different DTO.
That's my Repository:
internal interface PessoaRepository : CrudRepository<Pessoa, Long>, JpaSpecificationExecutorWithProjection<Pessoa> {
fun findByIdInstitucional(idInstitucional: UUID): PessoaDTO?
fun findByIdInstitucional(idInstitucional: UUID): PessoaDetalhesDTO?
}
However, I can't have two functions with the same name in the Repository for different returned data types.
How can I deal with that without having to create another Repository for the detailed information of Pessoa?

Related

How to use #value annotation in kotlin data class

I have an application.properties file like:
person-one-name=John
This is my data class, I have used #Value annotation outside dataclass.
#Value("\${person-one-name}")
lateinit var personOne: String
data class Person(val name: String, val age: Int) {
constructor(age: Int) : this(personOne, age)
}
I want to use the var personOne in my data class.
It gives an error lateinit property personOne has not been initialized
Following on from my comment under the Question:
data class Person(val name: String, val age: Int)
#Service
class PersonFactory(
#Value("\${person-one-name}")
private val personOne: String,
) {
fun createPerson(name: String? = null, age: Int) =
if (name != null) Person(name, age)
else Person(personOne, age)
}
Another gotcha, is that the PersonFactory service needs to be in a package at the same level or within the class that starts the App.
More info: https://springhow.com/a-guide-to-value-in-spring-boot/

How do I set and read properties in a SpringBoot application using Kotlin?

I'm a Java developer new to Kotlin and I'm trying to access values that I set in an application.yml file.
application.yml
q:
client:
apiUrl: https://app.abc.com/api/integr/v1.0
apiToken: abc
apiSecret: abc
authEmail: abc#example.com
sourceName: abc
This is my configuration class, which follows a similar pattern to Java.
#Component
#FeignClient(name = "q", url = "\${q.client.api-url}")
interface QClient {
#PostMapping("/user/get")
fun findUser(#RequestBody request: QRequest?):
QResponse<List<QUser?>?>
#PostMapping("/user/delete")
fun deleteUser(#RequestBody request: QRequest?): QResponse<DeleteResponse?>?
#Configuration
class QConfig {
#Value("\${q.client.apiToken}")
private val apiToken: String? = null
#Value("\${q.client.apiSecret}")
private val apiSecret: String? = null
#Value("\${q.client.authEmail}")
private val authEmail: String? = null
#Value("\${q.client.sourceName}")
private val sourceName: String? = null
fun createAuthRequest(): QAuth {
return QAuth(apiToken, apiSecret, authEmail, sourceName)
}
}
I don't want to assign null as default values for the instance variables, but Kotlin wants me to declare them like this to avoid null references.
I need to create an auth request and I'm calling the config class from the main class.
private fun generateRequest(email: String): QRequest {
val config = QClient.QConfig()
val auth = config.createAuthRequest()
return QRequest(auth, email)
}
But when debugging it just returns null values.
So after googling, I changed my approach and set all the key values into parameters of QConfig class like this:
#Configuration
class QConfig(
#Value("\${q.client.apiToken}") private val apiToken: String,
#Value("\${q.client.apiSecret}") private val apiSecret: String,
#Value("\${q.client.authEmail}") private val authEmail: String,
#Value("\${q.client.sourceName}") private val sourceName: String
) {
fun createAuthRequest(): QAuth {
return QAuth(apiToken, apiSecret, authEmail, sourceName)
}
}
The problem I faced here was it acts as a constructor and expects me to pass arguments while creating an instance for the QConfig class on the main class, which I wont have in the main class.
How can I get the values from the application.yml and access them as from instance variables?
You can use #ConfigurationProperties (ref)
#ConfigurationProperties("q.client")
#ConstructorBinding
data class ClientConfig(
val apiUrl: String, // variable name should be same as the one defined in application.yaml
val apiToken: String,
...other properties
)
#SpringBootApplication
#ConfigurationPropertiesScan
class SpringStackoverflowApplication {
#Autowired
private lateinit var clientConfig: ClientConfig
#EventListener(ApplicationReadyEvent::class)
fun doSomething() {
println("FOOBAR: $clientConfig")
}
}
fun main(args: Array<String>) {
runApplication<SpringStackoverflowApplication>(*args)
}
I solved this with Joffrey's reply, I used this format of config file
#Component
#Configuration
class QConfig {
#Value("\${q.client.apiToken}")
private val apiToken: String? = null
#Value("\${q.client.apiSecret}")
private val apiSecret: String? = null
#Value("\${q.client.authEmail}")
private val authEmail: String? = null
#Value("\${q.client.sourceName}")
private val sourceName: String? = null
fun createAuthRequest(): QAuth {
return QAuth(apiToken, apiSecret, authEmail, sourceName)
}
}
Then created the instance of QConfig like this on main class
#Autowired
val config = QConfig()
My bad, tried creating reference of class manually instead of using AutoWire. When started it pulled all the env variables passed on .yml file into the local variables.

(Spring boot) How can I ignore some field from request body inside rest controller

Suppose I have signUp method inside rest controller class looks like this.
#PostMapping("/signup")
fun authenticateSignUp(#RequestBody command: RegisterUserCommand): CompletableFuture<String> {
return commandGateway.send<String>(command)
}
So it requires request body which is RegisterUserCommand.
data class RegisterUserCommand(
val userId: String,
val balance: BigDecimal,
val username: String,
private val email: String,
private val password: String
)
I want to ignore some fields like userId, balance so I can generate it later inside controller like this
#PostMapping("/signup")
fun authenticateSignUp(#RequestBody request: RegisterUserCommand): CompletableFuture<String> {
val command = request.copy(userId = ObjectId.get().toHexString(), balance = BigDecimal.ZERO)
return commandGateway.send<String>(command)
}
Are there any annotation to ignore this field so it won't return bad request even though I didn't put userId, balance within request body
As correctly pointed out by #flaxel, you can use ? from Kotlin and I believe add #JvmOverloads constructor to it as well.
But, in my opinion, using DTO is the best way to deal with that for sure.
The input of your Controller should not be the Command but just a DTO with the fields you are interested in. How you build your command with enhanced values should not be affected by it.
In this case, you would have something like this (also added #TargetAggregateIdentifier because probably you missed it):
data class RegisterUserCommand(
#TargetAggregateIdentifier
val userId: String,
val balance: BigDecimal,
val username: String,
private val email: String,
private val password: String
)
...
data class RegisterUserDto(
val username: String,
private val email: String,
private val password: String
)
...
#PostMapping("/signup")
fun authenticateSignUp(#RequestBody request: RegisterUserDto): CompletableFuture<String> {
val command = new RegisterUserCommand // build the command the way you want it
return commandGateway.send<String>(command)
}
I guess you can mark the variable as nullable with the ? operator. But I would suggest to use DTOs.
data class RegisterUserDto(
val username: String,
private val email: String,
private val password: String
)

spring validation in composition

I have the following data classes:
data class User (
#field:NotEmpty
val firstName: String?
#field:NotEmpty
val lastName: String?
)
data class Expert (
#field:NotEmpty
val name: String?
#field:NotNull
val contact: User?
)
And I would like to use my rest API endpoint to create an expert with spring validation:
#RestController
#RequestMapping("/api/experts")
class ExpertController(private val expertService: ExpertService) {
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
fun create(#Valid #RequestBody dto: Expert) = expertService.create(dto)
}
Validation on name and contact fields works fine. But validation on firstName and lastName fields (User class) doesn't work. Is it a normal behaviour? I can't use validation on composition? Why? Or am I missing something?
In order for the User to be validated if it is contained within an Expert, you will need to add the #Valid annotation to it, so Spring's Validator knows to keep checking, otherwise it will stop.
Try this (untested):
data class Expert (
#field:NotEmpty
val name: String?
#field:NotNull
#field:Valid
val contact: User?
)

Spring data mongodb and scala lazy val: do not serialise bitmap$0 field

I use spring boot data mongodb in my scala project. When saving a case class that contains a lazy val, an additional bitmap$0 field appears in the mongo document (even if lazy val field is annotated with org.springframework.data.annotation.Transient). For example:
case class User(val firstName: String, val lastName: String) {
#Transient lazy val fullName: String = firstName +' ' + lastName
}
trait UserMongoRepository extends MongoRepository[User, String]
#Service
class userService(userMongoRepository: UserMongoRepository){
def saveUser = {
val u = User("Bob", "Marley")
userMongoRepository.save(u)
}
}
This results in a document:
{"firstName": "Bob", "lastName": "Marley", "bitmap$0": false}
How do I save a class with lazy val without the bitmap$0 field?

Resources