From c1d27b53c204bf0628de088d52b6c6a09486f40b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kozak?= Date: Thu, 9 Apr 2026 20:47:43 +0200 Subject: [PATCH 1/2] feat(core): validate operationId uniqueness and warn on duplicates (#40) SpecValidator now detects duplicate operationId values across all operations and emits a warning listing the conflicting path+method pairs. Code generation still succeeds via NameRegistry deduplication. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../justworks/core/parser/SpecValidator.kt | 14 +++++ .../justworks/core/parser/SpecParserTest.kt | 54 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/core/src/main/kotlin/com/avsystem/justworks/core/parser/SpecValidator.kt b/core/src/main/kotlin/com/avsystem/justworks/core/parser/SpecValidator.kt index ac24363..ecf9030 100644 --- a/core/src/main/kotlin/com/avsystem/justworks/core/parser/SpecValidator.kt +++ b/core/src/main/kotlin/com/avsystem/justworks/core/parser/SpecValidator.kt @@ -38,5 +38,19 @@ object SpecValidator { Issue.Warning("Links are not supported in v1 and will be ignored") } } + + val operations = openApi.paths.orEmpty().flatMap { (path, pathItem) -> + pathItem.readOperationsMap().mapNotNull { (method, op) -> + op.operationId?.let { Triple(it, method.name, path) } + } + } + val operationIds = operations.groupBy { it.first } + + for ((opId, occurrences) in operationIds) { + ensureOrAccumulate(occurrences.size == 1) { + val locations = occurrences.joinToString { "${it.second} ${it.third}" } + Issue.Warning("Duplicate operationId '$opId' found at: $locations") + } + } } } diff --git a/core/src/test/kotlin/com/avsystem/justworks/core/parser/SpecParserTest.kt b/core/src/test/kotlin/com/avsystem/justworks/core/parser/SpecParserTest.kt index e531d94..bb72ee5 100644 --- a/core/src/test/kotlin/com/avsystem/justworks/core/parser/SpecParserTest.kt +++ b/core/src/test/kotlin/com/avsystem/justworks/core/parser/SpecParserTest.kt @@ -243,6 +243,60 @@ class SpecParserTest : SpecParserTestBase() { } } + // -- SPEC-03b: Duplicate operationId warning -- + + @Test + fun `parse spec with duplicate operationId emits warning`() { + val result = SpecParser.parse( + """ + openapi: 3.0.0 + info: + title: Test + version: 1.0.0 + paths: + /pets: + get: + operationId: listPets + tags: [Pets] + responses: + '200': + description: OK + /animals: + get: + operationId: listPets + tags: [Animals] + responses: + '200': + description: OK + """.trimIndent().toTempFile(), + ) + assertIs(result) + val dupWarnings = result.warnings.filter { + it.message.contains("Duplicate operationId") + } + assertTrue( + dupWarnings.isNotEmpty(), + "Expected warning about duplicate operationId, got: ${result.warnings}", + ) + assertTrue( + dupWarnings.first().message.contains("listPets"), + "Warning should mention the duplicate operationId", + ) + } + + @Test + fun `parse spec with unique operationIds has no duplicate warnings`() { + val result = SpecParser.parse(loadResource("petstore.yaml")) + assertIs(result) + val dupWarnings = result.warnings.filter { + it.message.contains("Duplicate operationId") + } + assertTrue( + dupWarnings.isEmpty(), + "Petstore should have no duplicate operationId warnings", + ) + } + // -- SPEC-04: Swagger 2.0 auto-conversion -- @Test From b1a1eaf984b4e5e59f9e1de15dd9b40762bbc583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kozak?= Date: Fri, 10 Apr 2026 09:47:28 +0200 Subject: [PATCH 2/2] fix(core): add override modifier to HttpError.message property Generated HttpError extends RuntimeException but its `message` property was missing the `override` modifier, causing compilation failures in downstream projects. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../avsystem/justworks/core/gen/shared/ApiResponseGenerator.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/kotlin/com/avsystem/justworks/core/gen/shared/ApiResponseGenerator.kt b/core/src/main/kotlin/com/avsystem/justworks/core/gen/shared/ApiResponseGenerator.kt index 202ea8c..02e5543 100644 --- a/core/src/main/kotlin/com/avsystem/justworks/core/gen/shared/ApiResponseGenerator.kt +++ b/core/src/main/kotlin/com/avsystem/justworks/core/gen/shared/ApiResponseGenerator.kt @@ -57,6 +57,7 @@ internal object ApiResponseGenerator { ).addProperty( PropertySpec .builder(MESSAGE, STRING) + .addModifiers(KModifier.OVERRIDE) .initializer(MESSAGE) .build(), ).addProperty(