Skip to content

Commit

Permalink
Add option to add spring-cloud-openfeign-starter annotation (#365)
Browse files Browse the repository at this point in the history
* Add option to add spring-cloud-openfeign-starter annotation

* Fix @FeignClient parameter

* revert gradle foojay-resolve

* use cli parameters instead of x- extensions

* add generation test for spring-cloud-openfeign-starter

* Provide a default for client name in open feign annotation.
Move validation to unit tests and revert change to end2end tests.

* ensure client name can be changed

* merge tests

---------

Co-authored-by: Misato <[email protected]>
  • Loading branch information
cjbooms and mirror-kt authored Feb 10, 2025
1 parent 0bbe36e commit ac8b186
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 4 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ This section documents the available CLI parameters for controlling what gets ge
| | `RESILIENCE4J` - Generates a fault tolerance service for the client using the following library "io.github.resilience4j:resilience4j-all:+" (only for OkHttp clients) |
| | `SUSPEND_MODIFIER` - This option adds the suspend modifier to the generated client functions (only for OpenFeign clients) |
| | `SPRING_RESPONSE_ENTITY_WRAPPER` - This option adds the Spring-ResponseEntity generic around the response to be able to get response headers and status (only for OpenFeign clients). |
| | `SPRING_CLOUD_OPENFEIGN_STARTER_ANNOTATION` - This option adds the @FeignClient annotation to generated client interface |
| `--http-client-target` | Optionally select the target client that you want to be generated. Defaults to OK_HTTP |
| | CHOOSE ONE OF: |
| | `OK_HTTP` - Generate OkHttp client. |
Expand All @@ -220,6 +221,7 @@ This section documents the available CLI parameters for controlling what gets ge
| | `SEALED_INTERFACES_FOR_ONE_OF` - This option enables the generation of interfaces for discriminated oneOf types |
| | `NON_NULL_MAP_VALUES` - This option makes map values non-null. The default (since v15) and most spec compliant is make map values nullable |
| `--http-model-suffix` | Specify custom suffix for all generated model classes. Defaults to no suffix. |
| `--openfeign-client-name` | Specify openfeign client name for spring-cloud-starter-openfeign. Defaults to 'fabrikt-client'. |
| `--output-directory` | Allows the generation dir to be overridden. Defaults to current dir |
| `--resources-path` | Allows the path for generated resources to be overridden. Defaults to `src/main/resources` |
| `--serialization-library` | Specify which serialization library to use for annotations in generated model classes. Default: JACKSON |
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ object CodeGen {
codeGenArgs.modelSuffix,
codeGenArgs.clientOptions,
codeGenArgs.clientTarget,
codeGenArgs.openfeignClientName,
codeGenArgs.typeOverrides,
codeGenArgs.srcPath,
codeGenArgs.resourcesPath,
Expand All @@ -52,6 +53,7 @@ object CodeGen {
modelSuffix: String,
clientOptions: Set<ClientCodeGenOptionType>,
clientTarget: ClientCodeGenTargetType,
openfeignClientName: String,
typeOverrides: Set<CodeGenTypeOverride>,
srcPath: Path,
resourcesPath: Path,
Expand All @@ -67,6 +69,7 @@ object CodeGen {
modelSuffix,
clientOptions,
clientTarget,
openfeignClientName,
typeOverrides,
validationLibrary,
externalRefResolutionMode,
Expand Down
8 changes: 7 additions & 1 deletion src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenArgs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ class CodeGenArgs {
)
var clientTarget: ClientCodeGenTargetType = ClientCodeGenTargetType.OK_HTTP

@Parameter(
names = ["--openfeign-client-name"],
description = "Specify openfeign client name for spring-cloud-starter-openfeign. Defaults to 'fabrikt-client'.",
)
var openfeignClientName: String = ClientCodeGenOptionType.DEFAULT_OPEN_FEIGN_CLIENT_NAME

@Parameter(
names = ["--src-path"],
description = "Allows the path for generated source files to be overridden. Defaults to `src/main/kotlin`",
Expand Down Expand Up @@ -226,4 +232,4 @@ inline fun <reified T : Enum<T>> convertToEnumValue(value: String): T {
} catch (e: IllegalArgumentException) {
throw ParameterException("$value is not a valid option. Please choose from ${enumValues<T>().joinToString()}")
}
}
}
7 changes: 6 additions & 1 deletion src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,14 @@ enum class CodeGenerationType(val description: String) {
enum class ClientCodeGenOptionType(private val description: String) {
RESILIENCE4J("Generates a fault tolerance service for the client using the following library \"io.github.resilience4j:resilience4j-all:+\" (only for OkHttp clients)"),
SUSPEND_MODIFIER("This option adds the suspend modifier to the generated client functions (only for OpenFeign clients)"),
SPRING_RESPONSE_ENTITY_WRAPPER("This option adds the Spring-ResponseEntity generic around the response to be able to get response headers and status (only for OpenFeign clients).");
SPRING_RESPONSE_ENTITY_WRAPPER("This option adds the Spring-ResponseEntity generic around the response to be able to get response headers and status (only for OpenFeign clients)."),
SPRING_CLOUD_OPENFEIGN_STARTER_ANNOTATION("This option adds the @FeignClient annotation to generated client interface")
;

override fun toString() = "`${super.toString()}` - $description"
companion object {
const val DEFAULT_OPEN_FEIGN_CLIENT_NAME = "fabrikt-client"
}
}

enum class ClientCodeGenTargetType(val description: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ object MutableSettings {
private lateinit var modelSuffix: String
private lateinit var clientOptions: MutableSet<ClientCodeGenOptionType>
private lateinit var clientTarget: ClientCodeGenTargetType
private lateinit var openfeignClientName: String
private lateinit var typeOverrides: MutableSet<CodeGenTypeOverride>
private lateinit var validationLibrary: ValidationLibrary
private lateinit var externalRefResolutionMode: ExternalReferencesResolutionMode
Expand All @@ -23,6 +24,7 @@ object MutableSettings {
modelSuffix: String = "",
clientOptions: Set<ClientCodeGenOptionType> = emptySet(),
clientTarget: ClientCodeGenTargetType = ClientCodeGenTargetType.default,
openfeignClientName: String = ClientCodeGenOptionType.DEFAULT_OPEN_FEIGN_CLIENT_NAME,
typeOverrides: Set<CodeGenTypeOverride> = emptySet(),
validationLibrary: ValidationLibrary = ValidationLibrary.default,
externalRefResolutionMode: ExternalReferencesResolutionMode = ExternalReferencesResolutionMode.default,
Expand All @@ -35,6 +37,7 @@ object MutableSettings {
this.modelSuffix = modelSuffix
this.clientOptions = clientOptions.toMutableSet()
this.clientTarget = clientTarget
this.openfeignClientName = openfeignClientName
this.typeOverrides = typeOverrides.toMutableSet()
this.validationLibrary = validationLibrary
this.externalRefResolutionMode = externalRefResolutionMode
Expand All @@ -51,6 +54,7 @@ object MutableSettings {
fun modelSuffix() = this.modelSuffix
fun clientOptions() = this.clientOptions.toSet()
fun clientTarget() = this.clientTarget
fun openfeignClientName() = this.openfeignClientName
fun typeOverrides() = this.typeOverrides.toSet()
fun validationLibrary() = this.validationLibrary
fun externalRefResolutionMode() = this.externalRefResolutionMode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.cjbooms.fabrikt.configurations.Packages
import com.cjbooms.fabrikt.generators.GeneratorUtils.functionName
import com.cjbooms.fabrikt.generators.GeneratorUtils.getPrimaryContentMediaType
import com.cjbooms.fabrikt.generators.GeneratorUtils.toKdoc
import com.cjbooms.fabrikt.generators.MutableSettings
import com.cjbooms.fabrikt.generators.TypeFactory
import com.cjbooms.fabrikt.generators.client.ClientGeneratorUtils.ADDITIONAL_HEADERS_PARAMETER_NAME
import com.cjbooms.fabrikt.generators.client.ClientGeneratorUtils.ADDITIONAL_QUERY_PARAMETERS_PARAMETER_NAME
Expand Down Expand Up @@ -53,6 +54,19 @@ class OpenFeignInterfaceGenerator(

val clientType = TypeSpec.interfaceBuilder(simpleClientName(resourceName))
.addAnnotation(AnnotationSpec.builder(Suppress::class).addMember("%S", "unused").build())
.apply {
if (options.contains(ClientCodeGenOptionType.SPRING_CLOUD_OPENFEIGN_STARTER_ANNOTATION)) {
addAnnotation(
OpenFeignAnnotations.feignClientBuilder()
.addMember(
"name = %S",
MutableSettings.openfeignClientName()
)
.addMember("contextId = %S", resourceName)
.build()
)
}
}
.addFunctions(funcSpecs)
.build()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ object OpenFeignImports {

private object Packages {
const val FEIGN = "feign"
const val SPRING_STARTER = "org.springframework.cloud.openfeign"
}

val REQUEST_LINE = ClassName(Packages.FEIGN, "RequestLine")
Expand All @@ -18,6 +19,8 @@ object OpenFeignImports {
val QUERY_MAP = ClassName(Packages.FEIGN, "QueryMap")

val PARAM = ClassName(Packages.FEIGN, "Param")

val FEIGN_CLIENT = ClassName(Packages.SPRING_STARTER, "FeignClient")
}

object OpenFeignAnnotations {
Expand All @@ -42,4 +45,8 @@ object OpenFeignAnnotations {
fun paramBuilder(): AnnotationSpec.Builder =
AnnotationSpec
.builder(OpenFeignImports.PARAM)

fun feignClientBuilder(): AnnotationSpec.Builder =
AnnotationSpec
.builder(OpenFeignImports.FEIGN_CLIENT)
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class OpenFeignClientGeneratorTest {
genTypes = setOf(CodeGenerationType.CLIENT),
clientTarget = ClientCodeGenTargetType.OPEN_FEIGN,
modelOptions = setOf(ModelCodeGenOptionType.X_EXTENSIBLE_ENUMS),
openfeignClientName = "test-feign-client-name",
)
ModelNameRegistry.clear()
}
Expand All @@ -59,14 +60,18 @@ class OpenFeignClientGeneratorTest {
}

@Test
fun `correct Open Feign interfaces are generated with response entity wrapper`() {
fun `correct Open Feign interfaces are generated with response entity wrapper and spring annotation`() {
runTestCase(
testCaseName = "openFeignClient",
clientFileName = "OpenFeignClientWithResponseEntity.kt",
options = setOf(ClientCodeGenOptionType.SPRING_RESPONSE_ENTITY_WRAPPER),
options = setOf(
ClientCodeGenOptionType.SPRING_RESPONSE_ENTITY_WRAPPER,
ClientCodeGenOptionType.SPRING_CLOUD_OPENFEIGN_STARTER_ANNOTATION
)
)
}


private fun runTestCase(
testCaseName: String,
clientFileName: String = "OpenFeignClient.kt",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import feign.Headers
import feign.Param
import feign.QueryMap
import feign.RequestLine
import org.springframework.cloud.openfeign.FeignClient
import org.springframework.http.ResponseEntity
import kotlin.Boolean
import kotlin.Int
Expand All @@ -18,6 +19,10 @@ import kotlin.collections.List
import kotlin.collections.Map

@Suppress("unused")
@FeignClient(
name = "test-feign-client-name",
contextId = "ExamplePath1",
)
public interface ExamplePath1Client {
/**
* GET example path 1
Expand Down Expand Up @@ -58,6 +63,10 @@ public interface ExamplePath1Client {
}

@Suppress("unused")
@FeignClient(
name = "test-feign-client-name",
contextId = "ExamplePath2",
)
public interface ExamplePath2Client {
/**
* GET example path 2
Expand Down Expand Up @@ -117,6 +126,10 @@ public interface ExamplePath2Client {
}

@Suppress("unused")
@FeignClient(
name = "test-feign-client-name",
contextId = "ExamplePath3Subresource",
)
public interface ExamplePath3SubresourceClient {
/**
* PUT example path 3
Expand Down

0 comments on commit ac8b186

Please sign in to comment.