-
Notifications
You must be signed in to change notification settings - Fork 112
Add Jackson 3 support #945
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| /* | ||
| * | ||
| * Copyright 2020 Netflix, Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| * | ||
| */ | ||
|
|
||
| package com.netflix.graphql.dgs.codegen | ||
|
|
||
| import org.assertj.core.api.Assertions.assertThat | ||
| import org.junit.jupiter.api.Test | ||
|
|
||
| class JacksonVersionDetectionTest { | ||
| private val schema = | ||
| """ | ||
| type Query { | ||
| movies: [Movie] | ||
| } | ||
|
|
||
| type Movie { | ||
| title: String | ||
| director: String | ||
| } | ||
| """.trimIndent() | ||
|
|
||
| @Test | ||
| fun `generates only Jackson 2 JsonDeserialize and JsonPOJOBuilder annotations when Jackson 2 is configured`() { | ||
| val result = | ||
| CodeGen( | ||
| CodeGenConfig( | ||
| schemas = setOf(schema), | ||
| packageName = "com.test", | ||
| language = Language.KOTLIN, | ||
| generateKotlinNullableClasses = true, | ||
| jacksonVersions = setOf(JacksonVersion.JACKSON_2), | ||
| ), | ||
| ).generate() | ||
|
|
||
| val movieType = result.kotlinDataTypes.first { it.name == "Movie" } | ||
| val fileContent = movieType.toString() | ||
|
|
||
| assertThat(fileContent).contains("com.fasterxml.jackson.databind.`annotation`.JsonDeserialize") | ||
| assertThat(fileContent).doesNotContain("tools.jackson.databind.`annotation`.JsonDeserialize") | ||
|
|
||
| assertThat(fileContent).contains("com.fasterxml.jackson.databind.`annotation`.JsonPOJOBuilder") | ||
| assertThat(fileContent).doesNotContain("tools.jackson.databind.`annotation`.JsonPOJOBuilder") | ||
| } | ||
|
|
||
| @Test | ||
| fun `generates only Jackson 3 JsonDeserialize and JsonPOJOBuilder annotations when Jackson 3 is configured`() { | ||
| val result = | ||
| CodeGen( | ||
| CodeGenConfig( | ||
| schemas = setOf(schema), | ||
| packageName = "com.test", | ||
| language = Language.KOTLIN, | ||
| generateKotlinNullableClasses = true, | ||
| jacksonVersions = setOf(JacksonVersion.JACKSON_3), | ||
| ), | ||
| ).generate() | ||
|
|
||
| val movieType = result.kotlinDataTypes.first { it.name == "Movie" } | ||
| val fileContent = movieType.toString() | ||
|
|
||
| assertThat(fileContent).contains("tools.jackson.databind.`annotation`.JsonDeserialize") | ||
| assertThat(fileContent).doesNotContain("com.fasterxml.jackson.databind.`annotation`.JsonDeserialize") | ||
|
|
||
| assertThat(fileContent).contains("tools.jackson.databind.`annotation`.JsonPOJOBuilder") | ||
| assertThat(fileContent).doesNotContain("com.fasterxml.jackson.databind.`annotation`.JsonPOJOBuilder") | ||
| } | ||
|
|
||
| @Test | ||
| fun `generates both Jackson 2 and 3 JsonDeserialize and JsonPOJOBuilder annotations when both are configured`() { | ||
| val result = | ||
| CodeGen( | ||
| CodeGenConfig( | ||
| schemas = setOf(schema), | ||
| packageName = "com.test", | ||
| language = Language.KOTLIN, | ||
| generateKotlinNullableClasses = true, | ||
| jacksonVersions = setOf(JacksonVersion.JACKSON_2, JacksonVersion.JACKSON_3), | ||
| ), | ||
| ).generate() | ||
|
|
||
| val movieType = result.kotlinDataTypes.first { it.name == "Movie" } | ||
| val fileContent = movieType.toString() | ||
|
|
||
| assertThat(fileContent).contains("com.fasterxml.jackson.databind.`annotation`.JsonDeserialize") | ||
| assertThat(fileContent).contains("tools.jackson.databind.`annotation`.JsonDeserialize") | ||
|
|
||
| assertThat(fileContent).contains("tools.jackson.databind.`annotation`.JsonPOJOBuilder") | ||
| assertThat(fileContent).contains("com.fasterxml.jackson.databind.`annotation`.JsonPOJOBuilder") | ||
|
|
||
| assertThat(fileContent).contains("@ToolsJacksonDatabindAnnotationJsonPOJOBuilder") | ||
| assertThat(fileContent).contains("@FasterxmlJacksonDatabindAnnotationJsonPOJOBuilder") | ||
|
|
||
| assertThat(fileContent).contains("@ToolsJacksonDatabindAnnotationJsonDeserialize") | ||
| assertThat(fileContent).contains("@FasterxmlJacksonDatabindAnnotationJsonDeserialize") | ||
| } | ||
|
|
||
| @Test | ||
| fun `defaults to Jackson 2 when no configuration is provided`() { | ||
| val result = | ||
| CodeGen( | ||
| CodeGenConfig( | ||
| schemas = setOf(schema), | ||
| packageName = "com.test", | ||
| language = Language.KOTLIN, | ||
| generateKotlinNullableClasses = true, | ||
| ), | ||
| ).generate() | ||
|
|
||
| val movieType = result.kotlinDataTypes.first { it.name == "Movie" } | ||
| val fileContent = movieType.toString() | ||
|
|
||
| assertThat(fileContent).contains("com.fasterxml.jackson.databind.`annotation`.JsonDeserialize") | ||
| assertThat(fileContent).doesNotContain("tools.jackson.databind.`annotation`.JsonDeserialize") | ||
|
|
||
| assertThat(fileContent).contains("com.fasterxml.jackson.databind.`annotation`.JsonPOJOBuilder") | ||
| assertThat(fileContent).doesNotContain("tools.jackson.databind.`annotation`.JsonPOJOBuilder") | ||
| } | ||
|
|
||
| @Test | ||
| fun `empty configuration defaults to Jackson 2`() { | ||
| val result = | ||
| CodeGen( | ||
| CodeGenConfig( | ||
| schemas = setOf(schema), | ||
| packageName = "com.test", | ||
| language = Language.KOTLIN, | ||
| generateKotlinNullableClasses = true, | ||
| jacksonVersions = emptySet(), | ||
| ), | ||
| ).generate() | ||
|
|
||
| val movieType = result.kotlinDataTypes.first { it.name == "Movie" } | ||
| val fileContent = movieType.toString() | ||
|
|
||
| // Should default to Jackson 2 (backwards compatibility) | ||
| assertThat(fileContent).contains("com.fasterxml.jackson.databind.`annotation`.JsonDeserialize") | ||
| assertThat(fileContent).doesNotContain("tools.jackson.databind.`annotation`.JsonDeserialize") | ||
|
|
||
| assertThat(fileContent).contains("com.fasterxml.jackson.databind.`annotation`.JsonPOJOBuilder") | ||
| assertThat(fileContent).doesNotContain("tools.jackson.databind.`annotation`.JsonPOJOBuilder") | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,10 +20,13 @@ package com.netflix.graphql.dgs.codegen.gradle | |
|
|
||
| import com.netflix.graphql.dgs.codegen.CodeGen | ||
| import com.netflix.graphql.dgs.codegen.CodeGenConfig | ||
| import com.netflix.graphql.dgs.codegen.JacksonVersion | ||
| import com.netflix.graphql.dgs.codegen.Language | ||
| import org.gradle.api.DefaultTask | ||
| import org.gradle.api.file.ConfigurableFileCollection | ||
| import org.gradle.api.model.ObjectFactory | ||
| import org.gradle.api.plugins.JavaPlugin | ||
| import org.gradle.api.provider.SetProperty | ||
| import org.gradle.api.tasks.* | ||
| import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper | ||
| import java.io.File | ||
|
|
@@ -174,6 +177,15 @@ open class GenerateJavaTask | |
| project.configurations.findByName("dgsCodegen"), | ||
| ) | ||
|
|
||
| @Input | ||
| val jacksonVersions: SetProperty<JacksonVersion> = | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wdyt of providing a customer-facing property that Gradle plugin consumers can override to specify the Jackson version to use if we detect it incorrectly (so they are not blocked)? The con is that it's yet another prop that we need to maintain |
||
| objectFactory.setProperty(JacksonVersion::class.java).convention( | ||
| project.configurations | ||
| .named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we consider runtime dependencies as well? Thinking of a (weird) setup where Jackson is only present on the runtime classpath via transitive dependencies |
||
| .map { it.incoming.resolutionResult.allComponents } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line uses an eager getter that will trigger eager resolution at configuration time.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you switch to the |
||
| .map { JacksonVersionDetector.detect(it) }, | ||
| ) | ||
|
|
||
| @TaskAction | ||
| fun generate() { | ||
| val schemaJarFilesFromDependencies = dgsCodegenClasspath.files.toList() | ||
|
|
@@ -229,6 +241,7 @@ open class GenerateJavaTask | |
| javaGenerateAllConstructor = javaGenerateAllConstructor, | ||
| trackInputFieldSet = trackInputFieldSet, | ||
| generateJSpecifyAnnotations = generateJSpecifyAnnotations, | ||
| jacksonVersions = jacksonVersions.get(), | ||
| ) | ||
|
|
||
| logger.info("Codegen config: {}", config) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| /* | ||
| * | ||
| * Copyright 2020 Netflix, Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| * | ||
| */ | ||
|
|
||
| package com.netflix.graphql.dgs.codegen.gradle | ||
|
|
||
| import com.netflix.graphql.dgs.codegen.JacksonVersion | ||
| import org.gradle.api.artifacts.result.ResolvedComponentResult | ||
|
|
||
| object JacksonVersionDetector { | ||
| private const val JACKSON_2_GROUP = "com.fasterxml.jackson.core" | ||
| private const val JACKSON_3_GROUP = "tools.jackson.core" | ||
| private const val JACKSON_DATABIND_MODULE = "jackson-databind" | ||
|
|
||
| /** | ||
| * Which Jackson major versions are present among the resolved [components] | ||
| * (pass `resolutionResult.allComponents`). Reads graph metadata only — no artifact download. | ||
| */ | ||
| fun detect(components: Set<ResolvedComponentResult>): Set<JacksonVersion> = | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: maybe add a couple of tests to cover various project setups (specifically thinking of muti-module project setups with various combinations of how and where plugin is declared vs. applied) |
||
| buildSet { | ||
| for (component in components) { | ||
| val moduleVersion = component.moduleVersion ?: continue | ||
| if (moduleVersion.name != JACKSON_DATABIND_MODULE) continue | ||
| when (moduleVersion.group) { | ||
| JACKSON_2_GROUP -> add(JacksonVersion.JACKSON_2) | ||
| JACKSON_3_GROUP -> add(JacksonVersion.JACKSON_3) | ||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are there any integration tests that explicitly reference Jackson3? idk if having two sets (Jackson 2 and the same for Jackson 3 would be an overkill)