Repository: cjbooms/fabrikt
Pull request #149 (2022-09-26 16:22:42)

Add spring controller datetimeformat annotations
#148 

cjbooms: @averabaq any chance you have capacity to review? 

Patch:
diff --git a/src/main/kotlin/com/cjbooms/fabrikt/generators/controller/SpringControllerInterfaceGenerator.kt b/src/main/kotlin/com/cjbooms/fabrikt/generators/controller/SpringControllerInterfaceGenerator.kt
index c254ca6..7265f03 100644
--- a/src/main/kotlin/com/cjbooms/fabrikt/generators/controller/SpringControllerInterfaceGenerator.kt
+++ b/src/main/kotlin/com/cjbooms/fabrikt/generators/controller/SpringControllerInterfaceGenerator.kt
@@ -28,6 +28,9 @@ import com.squareup.kotlinpoet.KModifier
 import com.squareup.kotlinpoet.ParameterSpec
 import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
 import com.squareup.kotlinpoet.TypeSpec
+import com.squareup.kotlinpoet.asTypeName
+import java.time.LocalDate
+import java.time.OffsetDateTime
 
 class SpringControllerInterfaceGenerator(
     private val packages: Packages,
@@ -164,6 +167,11 @@ class SpringControllerInterfaceGenerator(
             if (parameter.defaultValue != null)
                 it.addMember("defaultValue = %S", parameter.defaultValue)
 
+            if ( parameter.type == LocalDate::class.asTypeName() )
+                this.addAnnotation( SpringAnnotations.dateTimeFormat( SpringImports.DateTimeFormat.ISO_DATE ) )
+            else if (parameter.type == OffsetDateTime::class.asTypeName())
+                this.addAnnotation( SpringAnnotations.dateTimeFormat( SpringImports.DateTimeFormat.ISO_DATE_TIME ) )
+
             this.addAnnotation(it.build())
         }
 
diff --git a/src/main/kotlin/com/cjbooms/fabrikt/generators/controller/metadata/Spring.kt b/src/main/kotlin/com/cjbooms/fabrikt/generators/controller/metadata/Spring.kt
index c9dfe74..ebf6b30 100644
--- a/src/main/kotlin/com/cjbooms/fabrikt/generators/controller/metadata/Spring.kt
+++ b/src/main/kotlin/com/cjbooms/fabrikt/generators/controller/metadata/Spring.kt
@@ -15,6 +15,8 @@ object SpringImports {
         const val WEB_BIND_ANNOTATION = "$SPRING_BASE.web.bind.annotation"
 
         const val VALIDATION_ANNOTATION = "$SPRING_BASE.validation.annotation"
+
+        const val DATE_TIME_FORMAT = "org.springframework.format.annotation"
     }
 
     val CONTROLLER = ClassName(Packages.STEREOTYPE, "Controller")
@@ -33,6 +35,14 @@ object SpringImports {
 
     val VALIDATED = ClassName(Packages.VALIDATION_ANNOTATION, "Validated")
 
+    val DATE_TIME_FORMAT = ClassName(Packages.DATE_TIME_FORMAT, "DateTimeFormat")
+
+    object DateTimeFormat {
+        val ISO_DATE = "${DATE_TIME_FORMAT.simpleName}.ISO.DATE"
+
+        val ISO_DATE_TIME = "${DATE_TIME_FORMAT.simpleName}.ISO.DATE_TIME"
+    }
+
     object Static {
         val RESPONSE_STATUS = Pair(Packages.HTTP, "HttpStatus")
 
@@ -51,6 +61,12 @@ object SpringAnnotations {
             .builder(SpringImports.VALIDATED)
             .build()
 
+    fun dateTimeFormat( iso: String ): AnnotationSpec =
+        AnnotationSpec
+            .builder(SpringImports.DATE_TIME_FORMAT)
+            .addMember( "iso = %L", iso )
+            .build()
+
     fun requestMappingBuilder(): AnnotationSpec.Builder =
         AnnotationSpec
             .builder(SpringImports.REQUEST_MAPPING)


Test patch:
diff --git a/src/test/kotlin/com/cjbooms/fabrikt/generators/SpringControllerGeneratorTest.kt b/src/test/kotlin/com/cjbooms/fabrikt/generators/SpringControllerGeneratorTest.kt
index 0e60e72..328702a 100644
--- a/src/test/kotlin/com/cjbooms/fabrikt/generators/SpringControllerGeneratorTest.kt
+++ b/src/test/kotlin/com/cjbooms/fabrikt/generators/SpringControllerGeneratorTest.kt
@@ -175,4 +175,13 @@ class SpringControllerGeneratorTest {
         }
         return Linter.lintString(singleFileBuilder.build().toString())
     }
+
+    @Test
+    fun `controller parameters should have spring DateTimeFormat annotations`() {
+        val api = SourceApi(readTextResource("/examples/springFormatDateAndDateTime/api.yaml"))
+        val controllers = SpringControllerInterfaceGenerator(Packages(basePackage), api).generate().toSingleFile()
+        val expectedControllers = readTextResource("/examples/springFormatDateAndDateTime/controllers/Controllers.kt")
+
+        assertThat(controllers.trim()).isEqualTo(expectedControllers.trim())
+    }
 }
diff --git a/src/test/resources/examples/springFormatDateAndDateTime/api.yaml b/src/test/resources/examples/springFormatDateAndDateTime/api.yaml
new file mode 100644
index 0000000..8f7f61c
--- /dev/null
+++ b/src/test/resources/examples/springFormatDateAndDateTime/api.yaml
@@ -0,0 +1,28 @@
+openapi: 3.0.0
+info:
+  version: '1'
+  title: queryLevelParameters
+paths:
+  /example:
+    get:
+      parameters:
+        - in: query
+          name: aDate
+          required: true
+          schema:
+            type: string
+            format: date
+        - in: query
+          name: bDateTime
+          required: true
+          schema:
+            type: string
+            format: date-time
+        - in: query
+          name: cInt
+          required: true
+          schema:
+            type: integer
+      responses:
+        '204':
+          description: example
\ No newline at end of file
diff --git a/src/test/resources/examples/springFormatDateAndDateTime/controllers/Controllers.kt b/src/test/resources/examples/springFormatDateAndDateTime/controllers/Controllers.kt
new file mode 100644
index 0000000..f0c5e81
--- /dev/null
+++ b/src/test/resources/examples/springFormatDateAndDateTime/controllers/Controllers.kt
@@ -0,0 +1,42 @@
+package ie.zalando.controllers
+
+import org.springframework.format.annotation.DateTimeFormat
+import org.springframework.http.ResponseEntity
+import org.springframework.stereotype.Controller
+import org.springframework.validation.annotation.Validated
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RequestMethod
+import org.springframework.web.bind.annotation.RequestParam
+import java.time.LocalDate
+import java.time.OffsetDateTime
+import kotlin.Int
+import kotlin.Unit
+
+@Controller
+@Validated
+@RequestMapping("")
+interface ExampleController {
+    /**
+     *
+     *
+     * @param aDate
+     * @param bDateTime
+     * @param cInt
+     */
+    @RequestMapping(
+        value = ["/example"],
+        produces = [],
+        method = [RequestMethod.GET]
+    )
+    fun get(
+        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) @RequestParam(value = "aDate", required = true)
+        aDate: LocalDate,
+        @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) @RequestParam(
+            value = "bDateTime",
+            required =
+            true
+        )
+        bDateTime: OffsetDateTime,
+        @RequestParam(value = "cInt", required = true) cInt: Int
+    ): ResponseEntity<Unit>
+}
\ No newline at end of file