Skip to content

Commit

Permalink
Add an option to add spring-response-entity for feign client return p…
Browse files Browse the repository at this point in the history
…aram (#302)
  • Loading branch information
y-marion authored Jul 20, 2024
1 parent 8caa7c4 commit e81c5fe
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ This section documents the available CLI parameters for controlling what gets ge
| | CHOOSE ANY OF: |
| | `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). |
| `--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 Down
5 changes: 3 additions & 2 deletions src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ 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)");
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).");

override fun toString() = "`${super.toString()}` - $description"
}
Expand Down Expand Up @@ -83,4 +84,4 @@ enum class ExternalReferencesResolutionMode(val description: String) {
AGGRESSIVE("Referencing any schema in an external API file triggers generation of every external schema in that file.");

override fun toString() = "`${super.toString()}` - $description"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.cjbooms.fabrikt.generators.client.ClientGeneratorUtils.deriveClientPa
import com.cjbooms.fabrikt.generators.client.ClientGeneratorUtils.getReturnType
import com.cjbooms.fabrikt.generators.client.ClientGeneratorUtils.simpleClientName
import com.cjbooms.fabrikt.generators.client.metadata.OpenFeignAnnotations
import com.cjbooms.fabrikt.generators.controller.metadata.SpringImports.RESPONSE_ENTITY
import com.cjbooms.fabrikt.model.ClientType
import com.cjbooms.fabrikt.model.Clients
import com.cjbooms.fabrikt.model.GeneratedFile
Expand All @@ -31,9 +32,12 @@ import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.asTypeName
import com.squareup.kotlinpoet.buildCodeBlock

import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy

class OpenFeignInterfaceGenerator(
private val packages: Packages,
private val api: SourceApi,
Expand Down Expand Up @@ -91,7 +95,11 @@ class OpenFeignInterfaceGenerator(
.defaultValue("emptyMap()")
.build(),
)
.returns(operation.getReturnType(packages))
.returns(
operation
.getReturnType(packages)
.optionallyParameterizeWithResponseEntity(options)
)
.build()
}

Expand Down Expand Up @@ -265,4 +273,14 @@ class OpenFeignInterfaceGenerator(
}
return this
}

/**
* Adds a ResponseEntity around the returned object so that we can get headers and statuscodes
*/
private fun TypeName.optionallyParameterizeWithResponseEntity(options: Set<ClientCodeGenOptionType>): TypeName {
if (options.contains(ClientCodeGenOptionType.SPRING_RESPONSE_ENTITY_WRAPPER)) {
return RESPONSE_ENTITY.parameterizedBy(this)
}
return this
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ class OpenFeignClientGeneratorTest {
)
}

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

private fun runTestCase(
testCaseName: String,
clientFileName: String = "OpenFeignClient.kt",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package examples.openFeignClient.client

import examples.openFeignClient.models.Content
import examples.openFeignClient.models.FirstModel
import examples.openFeignClient.models.QueryResult
import feign.HeaderMap
import feign.Headers
import feign.Param
import feign.RequestLine
import org.springframework.http.ResponseEntity
import kotlin.Boolean
import kotlin.Int
import kotlin.String
import kotlin.Suppress
import kotlin.Unit
import kotlin.collections.List
import kotlin.collections.Map

@Suppress("unused")
public interface ExamplePath1Client {
/**
* GET example path 1
*
* @param explodeListQueryParam
* @param queryParam2
* @param headerParam1
* @param headerParam2
*/
@RequestLine("GET /example-path-1?explode_list_query_param={explodeListQueryParam}&query_param2={queryParam2}")
@Headers(
"header_param1: {headerParam1}",
"header_param2: {headerParam2}",
"Accept: application/vnd.custom.media+json",
)
public fun getExamplePath1(
@Param("explodeListQueryParam") explodeListQueryParam: List<String>? = null,
@Param("queryParam2") queryParam2: Int? = null,
@Param("headerParam1") headerParam1: String? = null,
@Param("headerParam2") headerParam2: String? = null,
@HeaderMap additionalHeaders: Map<String, String> = emptyMap(),
): ResponseEntity<QueryResult>

/**
* POST example path 1
*
* @param content
* @param explodeListQueryParam
*/
@RequestLine("POST /example-path-1?explode_list_query_param={explodeListQueryParam}")
public fun postExamplePath1(
content: Content,
@Param("explodeListQueryParam") explodeListQueryParam: List<String>? = null,
@HeaderMap additionalHeaders: Map<String, String> = emptyMap(),
): ResponseEntity<Unit>
}

@Suppress("unused")
public interface ExamplePath2Client {
/**
* GET example path 2
*
* @param pathParam The resource id
* @param limit
* @param queryParam2
* @param ifNoneMatch The RFC7232 If-None-Match header field
*/
@RequestLine("GET /example-path-2/{pathParam}?limit={limit}&query_param2={queryParam2}")
@Headers(
"If-None-Match: {ifNoneMatch}",
"Accept: application/json",
)
public fun getExamplePath2PathParam(
@Param("pathParam") pathParam: String,
@Param("limit") limit: Int = 500,
@Param("queryParam2") queryParam2: Int? = null,
@Param("ifNoneMatch") ifNoneMatch: String? = null,
@HeaderMap additionalHeaders: Map<String, String> = emptyMap(),
): ResponseEntity<Content>

/**
* HEAD example path 2
*
* @param pathParam The resource id
* @param queryParam3
* @param ifNoneMatch The RFC7232 If-None-Match header field
*/
@RequestLine("HEAD /example-path-2/{pathParam}?query_param3={queryParam3}")
@Headers("If-None-Match: {ifNoneMatch}")
public fun headOperationIdExample(
@Param("pathParam") pathParam: String,
@Param("queryParam3") queryParam3: Boolean? = null,
@Param("ifNoneMatch") ifNoneMatch: String? = null,
@HeaderMap additionalHeaders: Map<String, String> = emptyMap(),
): ResponseEntity<Unit>

/**
* PUT example path 2
*
* @param firstModel
* @param pathParam The resource id
* @param ifMatch The RFC7232 If-Match header field
*/
@RequestLine("PUT /example-path-2/{pathParam}")
@Headers("If-Match: {ifMatch}")
public fun putExamplePath2PathParam(
firstModel: FirstModel,
@Param("pathParam") pathParam: String,
@Param("ifMatch") ifMatch: String,
@HeaderMap additionalHeaders: Map<String, String> = emptyMap(),
): ResponseEntity<Unit>
}

@Suppress("unused")
public interface ExamplePath3SubresourceClient {
/**
* PUT example path 3
*
* @param firstModel
* @param pathParam The resource id
* @param ifMatch The RFC7232 If-Match header field
* @param csvListQueryParam
*/
@RequestLine("PUT /example-path-3/{pathParam}/subresource?csv_list_query_param={csvListQueryParam}")
@Headers("If-Match: {ifMatch}")
public fun putExamplePath3PathParamSubresource(
firstModel: FirstModel,
@Param("pathParam") pathParam: String,
@Param("ifMatch") ifMatch: String,
@Param("csvListQueryParam") csvListQueryParam: List<String>? = null,
@HeaderMap additionalHeaders: Map<String, String> = emptyMap(),
): ResponseEntity<Unit>
}

0 comments on commit e81c5fe

Please sign in to comment.