Skip to content

Commit 5ee6c78

Browse files
committed
feat: support custom root directory for all schema formats and tasks
- Add `rootDir` property to `SchemaRegistryExtension` for global configuration. - Update `ProtobufSchemaParser` to correctly handle path resolution with `rootDir`. - Add support for `rootDir` in Avro and JSON schema parsing. - Update `DownloadTask`, `RegisterSchemasTask`, and `CompatibilityTask` to use `rootDir`. - Ensure metadata and ruleset files are resolved relative to `rootDir`. - Add unit and integration tests for all supported formats and tasks.
1 parent 349f38c commit 5ee6c78

File tree

13 files changed

+511
-11
lines changed

13 files changed

+511
-11
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ configurations["integrationImplementation"].extendsFrom(
7373

7474
dependencies {
7575
integrationImplementation("org.wiremock:wiremock-standalone:3.13.2")
76-
integrationImplementation("org.testcontainers:kafka:1.21.3")
76+
integrationImplementation("org.testcontainers:kafka:1.21.4")
7777
}
7878

7979
task<Test>("integrationTest") {

src/integration/kotlin/com/github/imflog/schema/registry/tasks/compatibility/CompatibilityTaskIT.kt

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import org.gradle.testkit.runner.GradleRunner
1515
import org.gradle.testkit.runner.TaskOutcome
1616
import org.junit.jupiter.api.AfterEach
1717
import org.junit.jupiter.api.BeforeEach
18+
import org.junit.jupiter.api.Test
1819
import org.junit.jupiter.api.extension.ExtensionContext
1920
import org.junit.jupiter.params.ParameterizedTest
2021
import org.junit.jupiter.params.provider.Arguments
@@ -279,6 +280,57 @@ class CompatibilityTaskIT : KafkaTestContainersUtils() {
279280
Assertions.assertThat(result?.task(":testSchemasTask")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
280281
}
281282

283+
@Test
284+
fun `CompatibilityTask should support custom root directory`() {
285+
folderRule.create()
286+
val customRoot = folderRule.newFolder("src", "main", "avro")
287+
288+
val userFile = File(customRoot, "User.avsc")
289+
userFile.writeText(
290+
"""
291+
{
292+
"type": "record",
293+
"name": "User",
294+
"fields": [
295+
{ "name": "name", "type": "string" }
296+
]
297+
}
298+
""".trimIndent()
299+
)
300+
301+
// Register the first version
302+
client.register("user", io.confluent.kafka.schemaregistry.avro.AvroSchema(userFile.readText()), false)
303+
304+
folderRule.newFile("settings.gradle")
305+
buildFile = folderRule.newFile("build.gradle")
306+
buildFile.writeText(
307+
"""
308+
plugins {
309+
id 'java'
310+
id 'com.github.imflog.kafka-schema-registry-gradle-plugin'
311+
}
312+
313+
schemaRegistry {
314+
url = '$schemaRegistryEndpoint'
315+
rootDir = 'src/main/avro'
316+
compatibility {
317+
subject('user', 'User.avsc', 'AVRO')
318+
}
319+
}
320+
"""
321+
)
322+
323+
val result: BuildResult? = GradleRunner.create()
324+
.withGradleVersion("8.6")
325+
.withProjectDir(folderRule.root)
326+
.withArguments(CompatibilityTask.TASK_NAME)
327+
.withPluginClasspath()
328+
.withDebug(true)
329+
.build()
330+
331+
Assertions.assertThat(result?.task(":testSchemasTask")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
332+
}
333+
282334
private class SchemaSuccessArgumentProvider : ArgumentsProvider {
283335
override fun provideArguments(parameters: ParameterDeclarations, context: ExtensionContext): Stream<out Arguments> =
284336
Stream.of(

src/integration/kotlin/com/github/imflog/schema/registry/tasks/download/DownloadTaskIT.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,46 @@ class DownloadTaskIT : KafkaTestContainersUtils() {
568568
}""".trimIndent().trim())
569569
}}
570570

571+
@Test
572+
fun `DownloadTask should support custom root directory`() {
573+
// Given
574+
val subjectName = "download-custom-root"
575+
client.register(subjectName, AvroSchema("""{"type":"record","name":"User","fields":[{"name":"name","type":"string"}]}"""))
576+
577+
folderRule.create()
578+
folderRule.newFile("settings.gradle")
579+
buildFile = folderRule.newFile("build.gradle")
580+
buildFile.writeText(
581+
"""
582+
plugins {
583+
id 'java'
584+
id 'com.github.imflog.kafka-schema-registry-gradle-plugin'
585+
}
586+
587+
schemaRegistry {
588+
url = '$schemaRegistryEndpoint'
589+
rootDir = 'downloaded-schemas'
590+
download {
591+
subject('$subjectName', 'test')
592+
}
593+
}
594+
"""
595+
)
596+
597+
// When
598+
val result: BuildResult? = GradleRunner.create()
599+
.withGradleVersion("8.6")
600+
.withProjectDir(folderRule.root)
601+
.withArguments(DownloadTask.TASK_NAME)
602+
.withPluginClasspath()
603+
.withDebug(true)
604+
.build()
605+
606+
// Then
607+
Assertions.assertThat(result?.task(":downloadSchemasTask")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
608+
Assertions.assertThat(File(folderRule.root, "downloaded-schemas/test/$subjectName.avsc")).exists()
609+
}
610+
571611
private class SchemaArgumentProvider : ArgumentsProvider {
572612
override fun provideArguments(parameters: ParameterDeclarations, context: ExtensionContext): Stream<out Arguments> =
573613
Stream.of(

src/integration/kotlin/com/github/imflog/schema/registry/tasks/register/RegisterTaskIT.kt

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.gradle.testkit.runner.GradleRunner
99
import org.gradle.testkit.runner.TaskOutcome
1010
import org.junit.jupiter.api.AfterEach
1111
import org.junit.jupiter.api.BeforeEach
12+
import org.junit.jupiter.api.Test
1213
import org.junit.jupiter.api.extension.ExtensionContext
1314
import org.junit.jupiter.params.ParameterizedTest
1415
import org.junit.jupiter.params.provider.Arguments
@@ -286,6 +287,259 @@ class RegisterTaskIT : KafkaTestContainersUtils() {
286287
Assertions.assertThat(result?.task(":registerSchemasTask")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
287288
}
288289

290+
@Test
291+
fun `RegisterSchemasTask should support custom root directory for avro`() {
292+
folderRule.create()
293+
val customRoot = folderRule.newFolder("src", "main", "avro")
294+
val resultFolder = folderRule.newFolder("results-avro")
295+
296+
val addressFile = File(customRoot, "Address.avsc")
297+
addressFile.writeText(
298+
"""
299+
{
300+
"type": "record",
301+
"name": "Address",
302+
"fields": [
303+
{ "name": "street", "type": "string" }
304+
]
305+
}
306+
""".trimIndent()
307+
)
308+
309+
val userFile = File(customRoot, "User.avsc")
310+
userFile.writeText(
311+
"""
312+
{
313+
"type": "record",
314+
"name": "User",
315+
"fields": [
316+
{ "name": "name", "type": "string" },
317+
{ "name": "address", "type": "Address" }
318+
]
319+
}
320+
""".trimIndent()
321+
)
322+
323+
folderRule.newFile("settings.gradle")
324+
buildFile = folderRule.newFile("build-avro.gradle")
325+
buildFile.writeText(
326+
"""
327+
plugins {
328+
id 'java'
329+
id 'com.github.imflog.kafka-schema-registry-gradle-plugin'
330+
}
331+
332+
schemaRegistry {
333+
url = '$schemaRegistryEndpoint'
334+
outputDirectory = '${resultFolder.absolutePath}'
335+
rootDir = 'src/main/avro'
336+
register {
337+
subject('user', 'User.avsc', 'AVRO')
338+
.addLocalReference('Address', 'Address.avsc')
339+
}
340+
}
341+
"""
342+
)
343+
344+
val result: BuildResult? = GradleRunner.create()
345+
.withGradleVersion("8.6")
346+
.withProjectDir(folderRule.root)
347+
.withArguments(RegisterSchemasTask.TASK_NAME, "-b", "build-avro.gradle")
348+
.withPluginClasspath()
349+
.withDebug(true)
350+
.build()
351+
352+
Assertions.assertThat(result?.task(":registerSchemasTask")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
353+
}
354+
355+
@Test
356+
fun `RegisterSchemasTask should support custom root directory for json`() {
357+
folderRule.create()
358+
val customRoot = folderRule.newFolder("src", "main", "json")
359+
val resultFolder = folderRule.newFolder("results-json")
360+
361+
val addressFile = File(customRoot, "Address.json")
362+
addressFile.writeText(
363+
"""
364+
{
365+
"${"$"}schema": "http://json-schema.org/draft-07/schema#",
366+
"${"$"}id": "Address",
367+
"type": "object",
368+
"properties": {
369+
"street": {"type": "string"}
370+
}
371+
}
372+
""".trimIndent()
373+
)
374+
375+
val userFile = File(customRoot, "User.json")
376+
userFile.writeText(
377+
"""
378+
{
379+
"${"$"}schema": "http://json-schema.org/draft-07/schema#",
380+
"${"$"}id": "User",
381+
"type": "object",
382+
"properties": {
383+
"name": {"type": "string"},
384+
"address": {"${"$"}ref": "Address"}
385+
}
386+
}
387+
""".trimIndent()
388+
)
389+
390+
folderRule.newFile("settings.gradle")
391+
buildFile = folderRule.newFile("build-json.gradle")
392+
buildFile.writeText(
393+
"""
394+
plugins {
395+
id 'java'
396+
id 'com.github.imflog.kafka-schema-registry-gradle-plugin'
397+
}
398+
399+
schemaRegistry {
400+
url = '$schemaRegistryEndpoint'
401+
outputDirectory = '${resultFolder.absolutePath}'
402+
rootDir = 'src/main/json'
403+
register {
404+
subject('user', 'User.json', 'JSON')
405+
.addLocalReference('Address', 'Address.json')
406+
}
407+
}
408+
"""
409+
)
410+
411+
val result: BuildResult? = GradleRunner.create()
412+
.withGradleVersion("8.6")
413+
.withProjectDir(folderRule.root)
414+
.withArguments(RegisterSchemasTask.TASK_NAME, "-b", "build-json.gradle")
415+
.withPluginClasspath()
416+
.withDebug(true)
417+
.build()
418+
419+
Assertions.assertThat(result?.task(":registerSchemasTask")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
420+
}
421+
422+
@Test
423+
fun `RegisterSchemasTask should support custom root directory for metadata and ruleset`() {
424+
folderRule.create()
425+
val customRoot = folderRule.newFolder("src", "main", "avro")
426+
val resultFolder = folderRule.newFolder("results-avro")
427+
428+
val schemaFile = File(customRoot, "User.avsc")
429+
schemaFile.writeText(
430+
"""
431+
{
432+
"type": "record",
433+
"name": "User",
434+
"fields": [
435+
{ "name": "name", "type": "string" }
436+
]
437+
}
438+
""".trimIndent()
439+
)
440+
441+
val metadataFile = File(customRoot, "metadata.json")
442+
metadataFile.writeText("""{"notes": "some notes"}""")
443+
444+
val ruleSetFile = File(customRoot, "ruleSet.json")
445+
ruleSetFile.writeText("""{"domainRules": []}""")
446+
447+
folderRule.newFile("settings.gradle")
448+
buildFile = folderRule.newFile("build.gradle")
449+
buildFile.writeText(
450+
"""
451+
plugins {
452+
id 'java'
453+
id 'com.github.imflog.kafka-schema-registry-gradle-plugin'
454+
}
455+
456+
schemaRegistry {
457+
url = '$schemaRegistryEndpoint'
458+
outputDirectory = '${resultFolder.absolutePath}'
459+
rootDir = 'src/main/avro'
460+
register {
461+
subject('user', 'User.avsc', 'AVRO')
462+
.setMetadata('metadata.json')
463+
.setRuleSet('ruleSet.json')
464+
}
465+
}
466+
"""
467+
)
468+
469+
val result: BuildResult? = GradleRunner.create()
470+
.withGradleVersion("8.6")
471+
.withProjectDir(folderRule.root)
472+
.withArguments(RegisterSchemasTask.TASK_NAME)
473+
.withPluginClasspath()
474+
.withDebug(true)
475+
.build()
476+
477+
Assertions.assertThat(result?.task(":registerSchemasTask")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
478+
}
479+
480+
@Test
481+
fun `RegisterSchemasTask should support custom root directory for protobuf`() {
482+
folderRule.create()
483+
val customRoot = folderRule.newFolder("src", "main", "proto")
484+
val resultFolder = folderRule.newFolder("results")
485+
486+
val addressFile = File(customRoot, "com/example/address.proto")
487+
addressFile.parentFile.mkdirs()
488+
addressFile.writeText(
489+
"""
490+
syntax = "proto3";
491+
package com.example;
492+
message Address {
493+
string street = 1;
494+
}
495+
""".trimIndent()
496+
)
497+
498+
val userFile = File(customRoot, "com/example/user.proto")
499+
userFile.writeText(
500+
"""
501+
syntax = "proto3";
502+
package com.example;
503+
import "com/example/address.proto";
504+
message User {
505+
string name = 1;
506+
Address address = 2;
507+
}
508+
""".trimIndent()
509+
)
510+
511+
folderRule.newFile("settings.gradle")
512+
buildFile = folderRule.newFile("build.gradle")
513+
buildFile.writeText(
514+
"""
515+
plugins {
516+
id 'java'
517+
id 'com.github.imflog.kafka-schema-registry-gradle-plugin'
518+
}
519+
520+
schemaRegistry {
521+
url = '$schemaRegistryEndpoint'
522+
outputDirectory = '${resultFolder.absolutePath}'
523+
rootDir = 'src/main/proto'
524+
register {
525+
subject('address', 'com/example/address.proto', 'PROTOBUF')
526+
subject('user', 'com/example/user.proto', 'PROTOBUF')
527+
.addLocalReference('com/example/address.proto', 'com/example/address.proto')
528+
}
529+
}
530+
"""
531+
)
532+
533+
val result: BuildResult? = GradleRunner.create()
534+
.withGradleVersion("8.6")
535+
.withProjectDir(folderRule.root)
536+
.withArguments(RegisterSchemasTask.TASK_NAME)
537+
.withPluginClasspath()
538+
.withDebug(true)
539+
.build()
540+
541+
Assertions.assertThat(result?.task(":registerSchemasTask")?.outcome).isEqualTo(TaskOutcome.SUCCESS)
542+
}
289543

290544
private class SchemaArgumentProvider : ArgumentsProvider {
291545
override fun provideArguments(parameters: ParameterDeclarations, context: ExtensionContext): Stream<out Arguments> =

src/main/kotlin/com/github/imflog/schema/registry/SchemaRegistryExtension.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ open class SchemaRegistryExtension(objects: ObjectFactory) {
2121

2222
val outputDirectory: Property<String> = objects.property(String::class.java)
2323

24+
val rootDir: Property<String> = objects.property(String::class.java)
25+
2426
val pretty: Property<Boolean> = objects.property(Boolean::class.java).apply {
2527
convention(false)
2628
}

0 commit comments

Comments
 (0)