Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ private class InMemorySchemaSource(

private data class DependencySchemaSource(
val file: File,
val entryName: String,
val entryIndex: Int,
val schema: String,
)

Expand Down Expand Up @@ -282,20 +282,18 @@ class CodeGen private constructor(
}
}

jarFile.entries().asSequence().forEach { entry ->
jarFile.entries().asSequence().forEachIndexed { index, entry ->
if (!entry.isDirectory && entry.name.hasGraphQLSchemaExtension()) {
logger.info("Generating schema from {}: {}", file.name, entry.name)
val schema = jarFile.getInputStream(entry).bufferedReader().use { it.readText() }
dependencySchemaSources += DependencySchemaSource(file, entry.name, schema)
dependencySchemaSources += DependencySchemaSource(file, index, schema)
}
}
}
}

// Jars are ordered by path, as in 8.7.0. Entries within a jar are ordered by name, not by position in the
// archive: Gradle's classpath fingerprint ignores entry order, so the zip order must not affect the output.
dependencySchemaSources
.sortedWith(compareBy<DependencySchemaSource> { it.file }.thenBy { it.entryName })
.sortedWith(compareBy<DependencySchemaSource> { it.file }.thenBy { it.entryIndex })
.mapTo(schemaSources) { InMemorySchemaSource(it.schema, "codegen") }
return schemaSources
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class SchemaMergingTest {
}

@Test
fun `dependency jar schemas are ordered by entry name instead of zip order`(
fun `dependency jar schemas retain zip entry order`(
@TempDir tempDir: Path,
) {
val schemaJar =
Expand All @@ -98,10 +98,30 @@ class SchemaMergingTest {
),
)

listOf(schemaJar, reorderedJar).forEach {
listOf(
schemaJar to listOf("first", "third", "second"),
reorderedJar to listOf("first", "second", "third"),
).forEach { (jar, expectedFields) ->
val fooType =
CodeGen(CodeGenConfig(schemaJarFilesFromDependencies = listOf(jar), packageName = "com.example"))
.generate()
.javaDataTypes
.single { type -> type.typeSpec().name() == "Foo" }
.typeSpec()
Assertions
.assertThat(fooType.fieldSpecs().map { field -> field.name() })
.containsExactlyElementsOf(expectedFields)
Assertions
.assertThat(fooFields(CodeGenConfig(schemaJarFilesFromDependencies = listOf(it), packageName = "com.example")))
.containsExactly("first", "second", "third")
.assertThat(
fooType
.methodSpecs()
.single { constructor ->
constructor.isConstructor && constructor.parameters().isNotEmpty()
}.parameters()
.map { parameter ->
parameter.name()
},
).containsExactlyElementsOf(expectedFields)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import org.gradle.api.tasks.*
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper
import java.io.File
import java.util.*
import java.util.jar.JarFile
import javax.inject.Inject

@CacheableTask
Expand Down Expand Up @@ -264,6 +265,26 @@ open class GenerateJavaTask
val sortedDgsCodegenJars: ConfigurableFileCollection =
objectFactory.fileCollection().from(providerFactory.provider { dgsCodegenClasspath.files.sorted() })

/**
* `CodeGen` reads schema entries in ZIP order, which Gradle's classpath fingerprint ignores.
* Record that order separately so jars with the same entries in a different order cannot share cached output.
*/
@get:Input
val dependencySchemaEntryOrder: List<String>
get() =
dgsCodegenClasspath.files.sorted().flatMapIndexed { jarIndex, file ->
JarFile(file).use { jar ->
jar
.entries()
.asSequence()
.filter { !it.isDirectory && it.name.isDependencySchemaEntryName() }
.map { "$jarIndex:${it.name}" }
.toList()
}
}

private fun String.isDependencySchemaEntryName(): Boolean = endsWith(".graphqls") || endsWith(".graphql") || endsWith(".gqls")

@Input
val jacksonVersionOverride: ListProperty<String> =
objectFactory.listProperty(String::class.java).convention(emptyList())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,24 +96,28 @@ class GenerateJavaTaskBuildCacheTest {
}

@Test
fun dependencyJarEntryOrderDoesNotChangeTheOutput(
fun dependencyJarEntryOrderChangesOutputWithoutReusingStaleCache(
@TempDir tempDir: File,
) {
val cacheDir = File(tempDir, "shared-build-cache")
val projectDir = createJarProject(tempDir, "jar-entry-order", cacheDir)
writeSchemaJar(File(projectDir, "schemas.jar"), listOf("c-extension.graphqls", "a-base.graphqls", "b-extension.graphqls"))

assertThat(run(projectDir, "--build-cache", "generateJava").task(":generateJava")?.outcome).isEqualTo(SUCCESS)
assertThat(fooFields(projectDir)).containsExactly("base", "fb", "fc")
assertThat(fooFields(projectDir)).containsExactly("base", "fc", "fb")

writeSchemaJar(File(projectDir, "schemas.jar"), listOf("a-base.graphqls", "b-extension.graphqls", "c-extension.graphqls"))
run(projectDir, "--build-cache", "generateJava")
assertThat(run(projectDir, "--build-cache", "generateJava").task(":generateJava")?.outcome).isEqualTo(SUCCESS)
assertThat(fooFields(projectDir)).containsExactly("base", "fb", "fc")

val freshProject = createJarProject(tempDir, "jar-entry-order-fresh", File(tempDir, "unused-cache"))
writeSchemaJar(File(freshProject, "schemas.jar"), listOf("a-base.graphqls", "b-extension.graphqls", "c-extension.graphqls"))
assertThat(run(freshProject, "--no-build-cache", "generateJava").task(":generateJava")?.outcome).isEqualTo(SUCCESS)
assertThat(outputFingerprints(generatedSources(projectDir))).isEqualTo(outputFingerprints(generatedSources(freshProject)))

writeSchemaJar(File(projectDir, "schemas.jar"), listOf("c-extension.graphqls", "a-base.graphqls", "b-extension.graphqls"))
assertThat(run(projectDir, "--build-cache", "generateJava").task(":generateJava")?.outcome).isEqualTo(FROM_CACHE)
assertThat(fooFields(projectDir)).containsExactly("base", "fc", "fb")
}

@Test
Expand Down
Loading