- Published on
Return CSV with Spring Boot, Spring Rest and Kotlin
- Authors
- Name
- Yair Mark
- @yairmark
Today I was busy putting together some automated endpoint tests against a REST API we integrate with.
Initially, I was not 100% sure how I was going to expose these results. The automated tests execute roughly 66 calls to the endpoint. I settled on gathering these results and outputting them as a CSV.
My idea was that I could expose a REST endpoint to trigger these tests. After a bit of Googling, I came upon a solution that builds the results up and responds to a Spring REST endpoint in Kotlin.
First, to make it easier to work with CSVs in Kotlin I added this library to my build.gradle
:
plugins {
id 'org.springframework.boot' version '2.2.6.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
id 'org.jetbrains.kotlin.jvm' version '1.3.72'
id 'org.jetbrains.kotlin.plugin.spring' version '1.3.72'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web'
implementation group: 'com.cloudbees.thirdparty', name: 'zendesk-java-client', version: '0.12.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin"
implementation "com.github.doyaaaaaken:kotlin-csv-jvm:0.10.0"
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
My REST endpoint was coded out in Kotlin and looked similar to the below:
package com.example.middleware.endpoint
import com.github.doyaaaaaken.kotlincsv.dsl.csvWriter
import org.springframework.core.io.FileSystemResource
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.util.concurrent.Callable
@RestController
@RequestMapping("/csv-example")
class ExampleController {
@GetMapping("/employees/all", produces = ["text/csv"])
fun getCSV(): Callable<ResponseEntity<FileSystemResource?>> {
val rows = buildCSV()
val fullCSV = listOf(listOf("Name", "Surname", "Age"), *rows.toTypedArray())
// create a temp file to store the CSV results in
// the below `createTempFile` is a Kotlin standard lib function
val tempFile = createTempFile(prefix = "test", suffix = ".csv")
csvWriter().writeAll(fullCSV, tempFile, append = true)
return Callable {
ResponseEntity.ok()
.header("Content-Disposition", "attachment; filename=all-employees.csv")
.contentLength(tempFile.length())
.contentType(MediaType.parseMediaType("text/csv"))
.body(FileSystemResource(tempFile))
}
}
fun buildCSV(): List<List<String>> {
// your business logic here
// convert these results to list of String lists holding each cell
}
}