Published on

Return CSV with Spring Boot, Spring Rest and Kotlin

Authors

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

Sources