diff --git a/core/src/main/kotlin/de/codecentric/hikaku/Hikaku.kt b/core/src/main/kotlin/de/codecentric/hikaku/Hikaku.kt index b33f164e..85d905fd 100644 --- a/core/src/main/kotlin/de/codecentric/hikaku/Hikaku.kt +++ b/core/src/main/kotlin/de/codecentric/hikaku/Hikaku.kt @@ -85,6 +85,7 @@ class Hikaku( Feature.MatrixParameters -> matches && it.matrixParameters == otherEndpoint.matrixParameters Feature.Produces -> matches && it.produces == otherEndpoint.produces Feature.Consumes -> matches && it.consumes == otherEndpoint.consumes + Feature.Deprecation -> matches && it.deprecated == otherEndpoint.deprecated } } diff --git a/core/src/main/kotlin/de/codecentric/hikaku/SupportedFeatures.kt b/core/src/main/kotlin/de/codecentric/hikaku/SupportedFeatures.kt index a26ed54c..d9eba4dc 100644 --- a/core/src/main/kotlin/de/codecentric/hikaku/SupportedFeatures.kt +++ b/core/src/main/kotlin/de/codecentric/hikaku/SupportedFeatures.kt @@ -23,6 +23,8 @@ class SupportedFeatures( /** Checks supported media type of requests. */ Consumes, /** Checks the equality of matrix parameters. */ - MatrixParameters + MatrixParameters, + /** Checks the equality of deprecation. */ + Deprecation } } \ No newline at end of file diff --git a/core/src/main/kotlin/de/codecentric/hikaku/endpoints/Endpoint.kt b/core/src/main/kotlin/de/codecentric/hikaku/endpoints/Endpoint.kt index 6f2926f5..fda01152 100644 --- a/core/src/main/kotlin/de/codecentric/hikaku/endpoints/Endpoint.kt +++ b/core/src/main/kotlin/de/codecentric/hikaku/endpoints/Endpoint.kt @@ -17,5 +17,6 @@ data class Endpoint( val headerParameters: Set = emptySet(), val matrixParameters: Set = emptySet(), val produces: Set = emptySet(), - val consumes: Set = emptySet() + val consumes: Set = emptySet(), + val deprecated: Boolean = false ) \ No newline at end of file diff --git a/core/src/main/kotlin/de/codecentric/hikaku/reporters/CommandLineReporter.kt b/core/src/main/kotlin/de/codecentric/hikaku/reporters/CommandLineReporter.kt index 0184c705..b9af6c70 100644 --- a/core/src/main/kotlin/de/codecentric/hikaku/reporters/CommandLineReporter.kt +++ b/core/src/main/kotlin/de/codecentric/hikaku/reporters/CommandLineReporter.kt @@ -54,6 +54,7 @@ class CommandLineReporter : Reporter { Feature.MatrixParameters -> listMatrixParameter(endpoint.matrixParameters) Feature.Consumes -> listRequestMediaTypes(endpoint.consumes) Feature.Produces -> listResponseMediaTypes(endpoint.produces) + Feature.Deprecation -> if (endpoint.deprecated) " Deprecated" else "" } } diff --git a/docs/features.md b/docs/features.md index b4f96261..5455f1b0 100644 --- a/docs/features.md +++ b/docs/features.md @@ -11,4 +11,5 @@ There might be various ways to declare or use a feature, so check each converter | HeaderParameters | Name of a header parameter and whether the parameter is required or not. | ✅ _(1.1.0)_ | ✅ _(1.1.0)_ | ✅ _(1.1.0)_ | ✅ _(2.0.0)_ | ✅ _(2.1.0)_ | ✅ _(2.3.0)_ | | MatrixParameters | Name of a matrix parameter and whether the parameter is required or not. _Example:_ `/todos;param=value` | ❌ | ✅ _(2.1.0)_ | ✅ _(2.1.0)_ | ❌ | ✅ _(2.1.0)_ | ❌ | | Produces | Checks the supported media types of the response. | ✅ _(1.1.0)_ | ✅ _(1.1.0)_ | ✅ _(1.1.0)_ | ✅ _(2.0.0)_ | ✅ _(2.1.0)_ | ✅ _(2.3.0)_ | -| Consumes | Checks the supported media types of the request. | ✅ _(1.1.0)_ | ✅ _(1.1.0)_ | ✅ _(1.1.0)_ | ✅ _(2.0.0)_ | ✅ _(2.1.0)_ | ✅ _(2.3.0)_ | \ No newline at end of file +| Consumes | Checks the supported media types of the request. | ✅ _(1.1.0)_ | ✅ _(1.1.0)_ | ✅ _(1.1.0)_ | ✅ _(2.0.0)_ | ✅ _(2.1.0)_ | ✅ _(2.3.0)_ | +| Deprecation | Checks deprecated endpoints are properly marked. | ✅ _(2.3.0)_ | ✅ _(2.3.0)_ | ❌ | ✅ _(2.3.0)_ | ✅ _(2.3.0)_ | ✅ _(2.3.0)_ | \ No newline at end of file diff --git a/jax-rs/src/main/kotlin/de/codecentric/hikaku/converters/jaxrs/JaxRsConverter.kt b/jax-rs/src/main/kotlin/de/codecentric/hikaku/converters/jaxrs/JaxRsConverter.kt index c9178773..3c8a5a2a 100644 --- a/jax-rs/src/main/kotlin/de/codecentric/hikaku/converters/jaxrs/JaxRsConverter.kt +++ b/jax-rs/src/main/kotlin/de/codecentric/hikaku/converters/jaxrs/JaxRsConverter.kt @@ -18,7 +18,8 @@ class JaxRsConverter(private val packageName: String) : AbstractEndpointConverte Feature.HeaderParameters, Feature.MatrixParameters, Feature.Consumes, - Feature.Produces + Feature.Produces, + Feature.Deprecation ) override fun convert(): Set { @@ -59,7 +60,8 @@ class JaxRsConverter(private val packageName: String) : AbstractEndpointConverte headerParameters = extractHeaderParameters(method), matrixParameters = extractMatrixParameters(method), produces = extractProduces(resource, method), - consumes = extractConsumes(resource, method) + consumes = extractConsumes(resource, method), + deprecated = isEndpointDeprecated(method) ) private fun extractPath(resource: Class<*>, method: Method): String { @@ -173,4 +175,8 @@ class JaxRsConverter(private val packageName: String) : AbstractEndpointConverte .map { MatrixParameter(it) } .toSet() } + + private fun isEndpointDeprecated(method: Method) = + method.isAnnotationPresent(Deprecated::class.java) + || method.declaringClass.isAnnotationPresent(Deprecated::class.java) } \ No newline at end of file diff --git a/jax-rs/src/test/kotlin/de/codecentric/hikaku/converters/jaxrs/JaxRsConverterDeprecationTest.kt b/jax-rs/src/test/kotlin/de/codecentric/hikaku/converters/jaxrs/JaxRsConverterDeprecationTest.kt new file mode 100644 index 00000000..c4559c8b --- /dev/null +++ b/jax-rs/src/test/kotlin/de/codecentric/hikaku/converters/jaxrs/JaxRsConverterDeprecationTest.kt @@ -0,0 +1,63 @@ +package de.codecentric.hikaku.converters.jaxrs + +import de.codecentric.hikaku.endpoints.Endpoint +import de.codecentric.hikaku.endpoints.HttpMethod.GET +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class JaxRsConverterDeprecationTest { + + @Test + fun `no deprecation`() { + // given + val specification = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + deprecated = false + ) + ) + + //when + val result = JaxRsConverter("test.jaxrs.deprecation.none").conversionResult + + //then + assertThat(result).containsExactlyInAnyOrderElementsOf(specification) + } + + @Test + fun `deprecated class`() { + // given + val specification = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + deprecated = true + ) + ) + + //when + val result = JaxRsConverter("test.jaxrs.deprecation.onclass").conversionResult + + //then + assertThat(result).containsExactlyInAnyOrderElementsOf(specification) + } + + @Test + fun `deprecated function`() { + // given + val specification = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + deprecated = true + ) + ) + + //when + val result = JaxRsConverter("test.jaxrs.deprecation.onfunction").conversionResult + + //then + assertThat(result).containsExactlyInAnyOrderElementsOf(specification) + } +} \ No newline at end of file diff --git a/jax-rs/src/test/kotlin/test/jaxrs/deprecation/none/NoDeprecation.kt b/jax-rs/src/test/kotlin/test/jaxrs/deprecation/none/NoDeprecation.kt new file mode 100644 index 00000000..9e86b2e4 --- /dev/null +++ b/jax-rs/src/test/kotlin/test/jaxrs/deprecation/none/NoDeprecation.kt @@ -0,0 +1,11 @@ +package test.jaxrs.deprecation.none + +import javax.ws.rs.GET +import javax.ws.rs.Path + +@Path("/todos") +class NoDeprecation { + + @GET + fun todo() { } +} \ No newline at end of file diff --git a/jax-rs/src/test/kotlin/test/jaxrs/deprecation/onclass/DeprecationOnClass.kt b/jax-rs/src/test/kotlin/test/jaxrs/deprecation/onclass/DeprecationOnClass.kt new file mode 100644 index 00000000..1ae0bdc8 --- /dev/null +++ b/jax-rs/src/test/kotlin/test/jaxrs/deprecation/onclass/DeprecationOnClass.kt @@ -0,0 +1,12 @@ +package test.jaxrs.deprecation.onclass + +import javax.ws.rs.GET +import javax.ws.rs.Path + +@Path("/todos") +@Deprecated("Test") +class DeprecationOnClass { + + @GET + fun todo() { } +} \ No newline at end of file diff --git a/jax-rs/src/test/kotlin/test/jaxrs/deprecation/onfunction/DeprecationOnFunction.kt b/jax-rs/src/test/kotlin/test/jaxrs/deprecation/onfunction/DeprecationOnFunction.kt new file mode 100644 index 00000000..6c12a8d2 --- /dev/null +++ b/jax-rs/src/test/kotlin/test/jaxrs/deprecation/onfunction/DeprecationOnFunction.kt @@ -0,0 +1,12 @@ +package test.jaxrs.deprecation.onfunction + +import javax.ws.rs.GET +import javax.ws.rs.Path + +@Path("/todos") +class DeprecationOnFunction { + + @GET + @Deprecated("Test") + fun todo() { } +} \ No newline at end of file diff --git a/micronaut/src/main/kotlin/de/codecentric/hikaku/converters/micronaut/MicronautConverter.kt b/micronaut/src/main/kotlin/de/codecentric/hikaku/converters/micronaut/MicronautConverter.kt index 9db9290b..274ee60d 100644 --- a/micronaut/src/main/kotlin/de/codecentric/hikaku/converters/micronaut/MicronautConverter.kt +++ b/micronaut/src/main/kotlin/de/codecentric/hikaku/converters/micronaut/MicronautConverter.kt @@ -18,7 +18,8 @@ class MicronautConverter(private val packageName: String) : AbstractEndpointConv Feature.PathParameters, Feature.HeaderParameters, Feature.Produces, - Feature.Consumes + Feature.Consumes, + Feature.Deprecation ) override fun convert(): Set { @@ -61,7 +62,8 @@ class MicronautConverter(private val packageName: String) : AbstractEndpointConv pathParameters = extractPathParameters(path, method), headerParameters = extractHeaderParameters(method), consumes = extractConsumes(resource, method), - produces = extractProduces(resource, method) + produces = extractProduces(resource, method), + deprecated = isEndpointDeprecated(method) ) } @@ -248,4 +250,8 @@ class MicronautConverter(private val packageName: String) : AbstractEndpointConv .map { HeaderParameter(it.value, it.defaultValue.isBlank()) } .toSet() } + + private fun isEndpointDeprecated(method: Method) = + method.isAnnotationPresent(Deprecated::class.java) + || method.declaringClass.isAnnotationPresent(Deprecated::class.java) } \ No newline at end of file diff --git a/micronaut/src/test/kotlin/de/codecentric/hikaku/converters/micronaut/MicronautConverterDeprecationTest.kt b/micronaut/src/test/kotlin/de/codecentric/hikaku/converters/micronaut/MicronautConverterDeprecationTest.kt new file mode 100644 index 00000000..b4ac8e11 --- /dev/null +++ b/micronaut/src/test/kotlin/de/codecentric/hikaku/converters/micronaut/MicronautConverterDeprecationTest.kt @@ -0,0 +1,63 @@ +package de.codecentric.hikaku.converters.micronaut + +import de.codecentric.hikaku.endpoints.Endpoint +import de.codecentric.hikaku.endpoints.HttpMethod.GET +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class MicronautConverterDeprecationTest { + + @Test + fun `no deprecation`() { + // given + val specification = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + deprecated = false + ) + ) + + //when + val result = MicronautConverter("test.micronaut.deprecation.none").conversionResult + + //then + assertThat(result).containsExactlyInAnyOrderElementsOf(specification) + } + + @Test + fun `deprecated class`() { + // given + val specification = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + deprecated = true + ) + ) + + //when + val result = MicronautConverter("test.micronaut.deprecation.onclass").conversionResult + + //then + assertThat(result).containsExactlyInAnyOrderElementsOf(specification) + } + + @Test + fun `deprecated function`() { + // given + val specification = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + deprecated = true + ) + ) + + //when + val result = MicronautConverter("test.micronaut.deprecation.onfunction").conversionResult + + //then + assertThat(result).containsExactlyInAnyOrderElementsOf(specification) + } +} \ No newline at end of file diff --git a/micronaut/src/test/kotlin/test/micronaut/deprecation/none/NoDeprecation.kt b/micronaut/src/test/kotlin/test/micronaut/deprecation/none/NoDeprecation.kt new file mode 100644 index 00000000..39380b4b --- /dev/null +++ b/micronaut/src/test/kotlin/test/micronaut/deprecation/none/NoDeprecation.kt @@ -0,0 +1,11 @@ +package test.micronaut.deprecation.none + +import io.micronaut.http.annotation.Controller +import io.micronaut.http.annotation.Get + +@Controller("/todos") +class NoDeprecation { + + @Get + fun todo() { } +} \ No newline at end of file diff --git a/micronaut/src/test/kotlin/test/micronaut/deprecation/onclass/DeprecationOnClass.kt b/micronaut/src/test/kotlin/test/micronaut/deprecation/onclass/DeprecationOnClass.kt new file mode 100644 index 00000000..c83ff194 --- /dev/null +++ b/micronaut/src/test/kotlin/test/micronaut/deprecation/onclass/DeprecationOnClass.kt @@ -0,0 +1,12 @@ +package test.micronaut.deprecation.onclass + +import io.micronaut.http.annotation.Controller +import io.micronaut.http.annotation.Get + +@Controller("/todos") +@Deprecated("Test") +class DeprecationOnClass { + + @Get + fun todo() { } +} \ No newline at end of file diff --git a/micronaut/src/test/kotlin/test/micronaut/deprecation/onfunction/DeprecationOnFunction.kt b/micronaut/src/test/kotlin/test/micronaut/deprecation/onfunction/DeprecationOnFunction.kt new file mode 100644 index 00000000..363fcdcf --- /dev/null +++ b/micronaut/src/test/kotlin/test/micronaut/deprecation/onfunction/DeprecationOnFunction.kt @@ -0,0 +1,12 @@ +package test.micronaut.deprecation.onfunction + +import io.micronaut.http.annotation.Controller +import io.micronaut.http.annotation.Get + +@Controller("/todos") +class DeprecationOnFunction { + + @Get + @Deprecated("Test") + fun todo() { } +} \ No newline at end of file diff --git a/openapi/src/main/kotlin/de/codecentric/hikaku/converters/openapi/OpenApiConverter.kt b/openapi/src/main/kotlin/de/codecentric/hikaku/converters/openapi/OpenApiConverter.kt index 0a9dde43..084c90df 100644 --- a/openapi/src/main/kotlin/de/codecentric/hikaku/converters/openapi/OpenApiConverter.kt +++ b/openapi/src/main/kotlin/de/codecentric/hikaku/converters/openapi/OpenApiConverter.kt @@ -35,7 +35,8 @@ class OpenApiConverter private constructor(private val specificationContent: Str Feature.PathParameters, Feature.HeaderParameters, Feature.Produces, - Feature.Consumes + Feature.Consumes, + Feature.Deprecation ) override fun convert(): Set { @@ -66,7 +67,8 @@ class OpenApiConverter private constructor(private val specificationContent: Str pathParameters = extractPathParameters(operation), headerParameters = extractHeaderParameters(operation), consumes = extractConsumesMediaTypes(operation), - produces = extractProduceMediaTypes(operation) + produces = extractProduceMediaTypes(operation), + deprecated = operation?.deprecated ?: false ) } } diff --git a/openapi/src/test/kotlin/de/codecentric/hikaku/converters/openapi/OpenApiConverterDeprecationTest.kt b/openapi/src/test/kotlin/de/codecentric/hikaku/converters/openapi/OpenApiConverterDeprecationTest.kt new file mode 100644 index 00000000..ad0ccffb --- /dev/null +++ b/openapi/src/test/kotlin/de/codecentric/hikaku/converters/openapi/OpenApiConverterDeprecationTest.kt @@ -0,0 +1,50 @@ +package de.codecentric.hikaku.converters.openapi + +import de.codecentric.hikaku.endpoints.Endpoint +import de.codecentric.hikaku.endpoints.HttpMethod.GET +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.nio.file.Paths + +class OpenApiConverterDeprecationTest { + + @Test + fun `no deprecation`() { + //given + val file = Paths.get(this::class.java.classLoader.getResource("deprecation/deprecation_none.yaml").toURI()) + val implementation = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + produces = setOf("application/json"), + deprecated = false + ) + ) + + //when + val specification = OpenApiConverter(file) + + //then + assertThat(specification.conversionResult).containsExactlyInAnyOrderElementsOf(implementation) + } + + @Test + fun `deprecated operation`() { + //given + val file = Paths.get(this::class.java.classLoader.getResource("deprecation/deprecation_operation.yaml").toURI()) + val implementation = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + produces = setOf("application/json"), + deprecated = true + ) + ) + + //when + val specification = OpenApiConverter(file) + + //then + assertThat(specification.conversionResult).containsExactlyInAnyOrderElementsOf(implementation) + } +} \ No newline at end of file diff --git a/openapi/src/test/resources/deprecation/deprecation_none.yaml b/openapi/src/test/resources/deprecation/deprecation_none.yaml new file mode 100644 index 00000000..59d12222 --- /dev/null +++ b/openapi/src/test/resources/deprecation/deprecation_none.yaml @@ -0,0 +1,18 @@ +openapi: 3.0.2 +info: + version: 1.0.0 + title: Todo List +paths: + /todos: + get: + deprecated: false + description: '' + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + type: string \ No newline at end of file diff --git a/openapi/src/test/resources/deprecation/deprecation_operation.yaml b/openapi/src/test/resources/deprecation/deprecation_operation.yaml new file mode 100644 index 00000000..b2d2e70f --- /dev/null +++ b/openapi/src/test/resources/deprecation/deprecation_operation.yaml @@ -0,0 +1,18 @@ +openapi: 3.0.2 +info: + version: 1.0.0 + title: Todo List +paths: + /todos: + get: + deprecated: true + description: '' + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + type: string \ No newline at end of file diff --git a/raml/src/main/kotlin/de/codecentric/hikaku/converters/raml/RamlConverter.kt b/raml/src/main/kotlin/de/codecentric/hikaku/converters/raml/RamlConverter.kt index 5e8b7044..f6443e5d 100644 --- a/raml/src/main/kotlin/de/codecentric/hikaku/converters/raml/RamlConverter.kt +++ b/raml/src/main/kotlin/de/codecentric/hikaku/converters/raml/RamlConverter.kt @@ -1,7 +1,7 @@ package de.codecentric.hikaku.converters.raml import de.codecentric.hikaku.SupportedFeatures -import de.codecentric.hikaku.SupportedFeatures.* +import de.codecentric.hikaku.SupportedFeatures.Feature import de.codecentric.hikaku.converters.AbstractEndpointConverter import de.codecentric.hikaku.converters.EndpointConverterException import de.codecentric.hikaku.converters.raml.extensions.* @@ -22,7 +22,8 @@ class RamlConverter(private val ramlSpecification: File) : AbstractEndpointConve Feature.PathParameters, Feature.HeaderParameters, Feature.Produces, - Feature.Consumes + Feature.Consumes, + Feature.Deprecation ) override fun convert(): Set { @@ -68,7 +69,8 @@ class RamlConverter(private val ramlSpecification: File) : AbstractEndpointConve pathParameters = it.resource()?.hikakuPathParameters().orEmpty(), headerParameters = it?.hikakuHeaderParameters().orEmpty(), consumes = it.requestMediaTypes(), - produces = it.responseMediaTypes() + produces = it.responseMediaTypes(), + deprecated = it.isEndpointDeprecated() ) } } diff --git a/raml/src/main/kotlin/de/codecentric/hikaku/converters/raml/extensions/MethodExtensions.kt b/raml/src/main/kotlin/de/codecentric/hikaku/converters/raml/extensions/MethodExtensions.kt index b7eac3a8..fe4b965b 100644 --- a/raml/src/main/kotlin/de/codecentric/hikaku/converters/raml/extensions/MethodExtensions.kt +++ b/raml/src/main/kotlin/de/codecentric/hikaku/converters/raml/extensions/MethodExtensions.kt @@ -34,5 +34,9 @@ internal fun Method.responseMediaTypes(): Set { return this.responses().flatMap {response -> response.body().map { it.name() } } - .toSet() -} \ No newline at end of file + .toSet() +} + +internal fun Method.isEndpointDeprecated() = + this.annotations().any { i -> i.annotation().name() == "deprecated" } + || checkNotNull(this.resource()).annotations().any { i -> i.annotation().name() == "deprecated" } \ No newline at end of file diff --git a/raml/src/test/kotlin/de/codecentric/hikaku/converters/raml/RamlConverterDeprecationTest.kt b/raml/src/test/kotlin/de/codecentric/hikaku/converters/raml/RamlConverterDeprecationTest.kt new file mode 100644 index 00000000..489616df --- /dev/null +++ b/raml/src/test/kotlin/de/codecentric/hikaku/converters/raml/RamlConverterDeprecationTest.kt @@ -0,0 +1,73 @@ +package de.codecentric.hikaku.converters.raml + +import de.codecentric.hikaku.endpoints.Endpoint +import de.codecentric.hikaku.endpoints.HttpMethod.GET +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.nio.file.Paths + +class RamlConverterDeprecationTest { + + @Test + fun `no deprecations`() { + //given + val file = Paths.get(this::class.java.classLoader.getResource("deprecation/none.raml").toURI()) + + val specification = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + produces = setOf("text/plain"), + deprecated = false + ) + ) + + //when + val implementation = RamlConverter(file).conversionResult + + //then + assertThat(implementation).containsExactlyInAnyOrderElementsOf(specification) + } + + @Test + fun `deprecated resource`() { + //given + val file = Paths.get(this::class.java.classLoader.getResource("deprecation/on_resource.raml").toURI()) + + val specification = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + produces = setOf("text/plain"), + deprecated = true + ) + ) + + //when + val implementation = RamlConverter(file).conversionResult + + //then + assertThat(implementation).containsExactlyInAnyOrderElementsOf(specification) + } + + @Test + fun `deprecated method`() { + //given + val file = Paths.get(this::class.java.classLoader.getResource("deprecation/on_method.raml").toURI()) + + val specification = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + produces = setOf("text/plain"), + deprecated = true + ) + ) + + //when + val implementation = RamlConverter(file).conversionResult + + //then + assertThat(implementation).containsExactlyInAnyOrderElementsOf(specification) + } +} \ No newline at end of file diff --git a/raml/src/test/resources/deprecation/none.raml b/raml/src/test/resources/deprecation/none.raml new file mode 100644 index 00000000..e0d23166 --- /dev/null +++ b/raml/src/test/resources/deprecation/none.raml @@ -0,0 +1,14 @@ +#%RAML 1.0 +title: test api +version: v3 +annotationTypes: + deprecated: string +/todos: + displayName: Todos + get: + displayName: Get all todos + responses: + 200: + body: + text/plain: + type: string \ No newline at end of file diff --git a/raml/src/test/resources/deprecation/on_method.raml b/raml/src/test/resources/deprecation/on_method.raml new file mode 100644 index 00000000..7639992c --- /dev/null +++ b/raml/src/test/resources/deprecation/on_method.raml @@ -0,0 +1,15 @@ +#%RAML 1.0 +title: test api +version: v3 +annotationTypes: + deprecated: string +/todos: + displayName: Todos + get: + (deprecated): This method is deprecated + displayName: Get all todos + responses: + 200: + body: + text/plain: + type: string \ No newline at end of file diff --git a/raml/src/test/resources/deprecation/on_resource.raml b/raml/src/test/resources/deprecation/on_resource.raml new file mode 100644 index 00000000..8c404249 --- /dev/null +++ b/raml/src/test/resources/deprecation/on_resource.raml @@ -0,0 +1,15 @@ +#%RAML 1.0 +title: test api +version: v3 +annotationTypes: + deprecated: string +/todos: + (deprecated): This endpoint is deprecated + displayName: Todos + get: + displayName: Get all todos + responses: + 200: + body: + text/plain: + type: string \ No newline at end of file diff --git a/spring/src/main/kotlin/de/codecentric/hikaku/converters/spring/SpringConverter.kt b/spring/src/main/kotlin/de/codecentric/hikaku/converters/spring/SpringConverter.kt index 12cc3a18..0b27dedf 100644 --- a/spring/src/main/kotlin/de/codecentric/hikaku/converters/spring/SpringConverter.kt +++ b/spring/src/main/kotlin/de/codecentric/hikaku/converters/spring/SpringConverter.kt @@ -24,7 +24,8 @@ class SpringConverter(private val applicationContext: ApplicationContext) : Abst Feature.HeaderParameters, Feature.MatrixParameters, Feature.Produces, - Feature.Consumes + Feature.Consumes, + Feature.Deprecation ) override fun convert(): Set { @@ -51,17 +52,19 @@ class SpringConverter(private val applicationContext: ApplicationContext) : Abst headerParameters = mappingEntry.value.hikakuHeaderParameters(), matrixParameters = mappingEntry.value.hikakuMatrixParameters(), produces = mappingEntry.produces(), - consumes = mappingEntry.consumes() + consumes = mappingEntry.consumes(), + deprecated = mappingEntry.isEndpointDeprecated() ) } .toMutableSet() // Spring always adds an OPTIONS http method if it does not exist, but without query and path parameter - if(!httpMethods.contains(OPTIONS)) { + if (!httpMethods.contains(OPTIONS)) { endpoints.add( Endpoint( path = cleanedPath, - httpMethod = OPTIONS + httpMethod = OPTIONS, + deprecated = mappingEntry.isEndpointDeprecated() ) ) } diff --git a/spring/src/main/kotlin/de/codecentric/hikaku/converters/spring/extensions/DeprecationExtension.kt b/spring/src/main/kotlin/de/codecentric/hikaku/converters/spring/extensions/DeprecationExtension.kt new file mode 100644 index 00000000..fb277f9c --- /dev/null +++ b/spring/src/main/kotlin/de/codecentric/hikaku/converters/spring/extensions/DeprecationExtension.kt @@ -0,0 +1,8 @@ +package de.codecentric.hikaku.converters.spring.extensions + +import org.springframework.web.method.HandlerMethod +import org.springframework.web.servlet.mvc.method.RequestMappingInfo + +internal fun Map.Entry.isEndpointDeprecated() = + this.value.method.isAnnotationPresent(Deprecated::class.java) + || this.value.method.declaringClass.isAnnotationPresent(Deprecated::class.java) \ No newline at end of file diff --git a/spring/src/test/kotlin/de/codecentric/hikaku/converters/spring/deprecation/DeprecationTestController.kt b/spring/src/test/kotlin/de/codecentric/hikaku/converters/spring/deprecation/DeprecationTestController.kt new file mode 100644 index 00000000..04f6a039 --- /dev/null +++ b/spring/src/test/kotlin/de/codecentric/hikaku/converters/spring/deprecation/DeprecationTestController.kt @@ -0,0 +1,33 @@ +package de.codecentric.hikaku.converters.spring.deprecation + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.stereotype.Controller +import org.springframework.web.bind.annotation.* + +@SpringBootApplication +open class DummyApp + +data class Todo(val description: String) + +@Controller +open class NoDeprecationController { + + @GetMapping("/todos") + fun todos(@RequestBody todo: Todo) { } +} + +@Controller +@Deprecated("Test") +open class DeprecatedClassController { + + @GetMapping("/todos") + fun todos(@RequestBody todo: Todo) { } +} + +@Controller +open class DeprecatedFunctionController { + + @GetMapping("/todos") + @Deprecated("Test") + fun todos(@RequestBody todo: Todo) { } +} \ No newline at end of file diff --git a/spring/src/test/kotlin/de/codecentric/hikaku/converters/spring/deprecation/SpringConverterDeprecationTest.kt b/spring/src/test/kotlin/de/codecentric/hikaku/converters/spring/deprecation/SpringConverterDeprecationTest.kt new file mode 100644 index 00000000..b7b8d0a5 --- /dev/null +++ b/spring/src/test/kotlin/de/codecentric/hikaku/converters/spring/deprecation/SpringConverterDeprecationTest.kt @@ -0,0 +1,118 @@ +package de.codecentric.hikaku.converters.spring.deprecation + +import de.codecentric.hikaku.converters.spring.SpringConverter +import de.codecentric.hikaku.endpoints.Endpoint +import de.codecentric.hikaku.endpoints.HttpMethod.* +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest +import org.springframework.context.ConfigurableApplicationContext +import org.springframework.http.MediaType + +class SpringConverterDeprecationTest { + + @Nested + @WebMvcTest(NoDeprecationController::class, excludeAutoConfiguration = [ErrorMvcAutoConfiguration::class]) + inner class NoDeprecationTest { + + @Autowired + lateinit var context: ConfigurableApplicationContext + + @Test + fun `no deprecation`() { + //given + val specification: Set = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + consumes = setOf(MediaType.APPLICATION_JSON_UTF8_VALUE), + deprecated = false + ), + Endpoint( + path = "/todos", + httpMethod = HEAD, + consumes = setOf(MediaType.APPLICATION_JSON_UTF8_VALUE), + deprecated = false + ), + Endpoint("/todos", OPTIONS, deprecated = false) + ) + + //when + val implementation = SpringConverter(context) + + //then + assertThat(implementation.conversionResult).containsExactlyInAnyOrderElementsOf(specification) + } + } + + @Nested + @WebMvcTest(DeprecatedClassController::class, excludeAutoConfiguration = [ErrorMvcAutoConfiguration::class]) + inner class NoDeprecatedClassTest { + + @Autowired + lateinit var context: ConfigurableApplicationContext + + @Test + fun `deprecated class`() { + //given + val specification: Set = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + consumes = setOf(MediaType.APPLICATION_JSON_UTF8_VALUE), + deprecated = true + ), + Endpoint( + path = "/todos", + httpMethod = HEAD, + consumes = setOf(MediaType.APPLICATION_JSON_UTF8_VALUE), + deprecated = true + ), + Endpoint("/todos", OPTIONS, deprecated = true) + ) + + //when + val implementation = SpringConverter(context) + + //then + assertThat(implementation.conversionResult).containsExactlyInAnyOrderElementsOf(specification) + } + } + + @Nested + @WebMvcTest(DeprecatedFunctionController::class, excludeAutoConfiguration = [ErrorMvcAutoConfiguration::class]) + inner class NoDeprecatedFunctionTest { + + @Autowired + lateinit var context: ConfigurableApplicationContext + + @Test + fun `deprcated function`() { + //given + val specification: Set = setOf( + Endpoint( + path = "/todos", + httpMethod = GET, + consumes = setOf(MediaType.APPLICATION_JSON_UTF8_VALUE), + deprecated = true + ), + Endpoint( + path = "/todos", + httpMethod = HEAD, + consumes = setOf(MediaType.APPLICATION_JSON_UTF8_VALUE), + deprecated = true + ), + Endpoint("/todos", OPTIONS, deprecated = true) + ) + + //when + val implementation = SpringConverter(context) + + //then + assertThat(implementation.conversionResult).containsExactlyInAnyOrderElementsOf(specification) + } + } +} \ No newline at end of file