From 01bad71efcdb0671b5b852e72be31993be694e1e Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 15 May 2023 11:36:06 +0200 Subject: [PATCH 01/10] Refactoring: Log level configuration --- .../configuration/ExampleConfiguration.java | 3 +- .../configuration/ExampleConfiguration.java | 3 +- .../configuration/ExampleConfiguration.java | 3 +- .../configuration/ExampleConfiguration.java | 3 +- .../api/model/ValidatorConfiguration.java | 3 +- .../model/ValidatorConfigurationBuilder.java | 37 +++++++++++++++++++ .../LibraryAutoConfiguration.java | 10 +---- .../OpenApiValidationConfiguration.java | 18 +++++++++ 8 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfigurationBuilder.java create mode 100644 spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/configuration/OpenApiValidationConfiguration.java diff --git a/examples/example-spring-boot-starter-web-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java b/examples/example-spring-boot-starter-web-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java index 1d685167..e8b30917 100644 --- a/examples/example-spring-boot-starter-web-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java +++ b/examples/example-spring-boot-starter-web-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java @@ -2,13 +2,14 @@ import com.getyourguide.openapi.validation.api.log.LoggerExtension; import com.getyourguide.openapi.validation.api.metrics.MetricsReporter; +import com.getyourguide.openapi.validation.configuration.OpenApiValidationConfiguration; import com.getyourguide.openapi.validation.example.logging.ExampleLoggerExtension; import com.getyourguide.openapi.validation.metrics.LoggingMetricsReporter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration -public class ExampleConfiguration { +public class ExampleConfiguration extends OpenApiValidationConfiguration { @Bean public MetricsReporter metricsReporter() { return new LoggingMetricsReporter(); diff --git a/examples/example-spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java b/examples/example-spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java index 1d685167..e8b30917 100644 --- a/examples/example-spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java +++ b/examples/example-spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java @@ -2,13 +2,14 @@ import com.getyourguide.openapi.validation.api.log.LoggerExtension; import com.getyourguide.openapi.validation.api.metrics.MetricsReporter; +import com.getyourguide.openapi.validation.configuration.OpenApiValidationConfiguration; import com.getyourguide.openapi.validation.example.logging.ExampleLoggerExtension; import com.getyourguide.openapi.validation.metrics.LoggingMetricsReporter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration -public class ExampleConfiguration { +public class ExampleConfiguration extends OpenApiValidationConfiguration { @Bean public MetricsReporter metricsReporter() { return new LoggingMetricsReporter(); diff --git a/examples/example-spring-boot-starter-webflux-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java b/examples/example-spring-boot-starter-webflux-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java index f873cc2d..b3bc22b1 100644 --- a/examples/example-spring-boot-starter-webflux-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java +++ b/examples/example-spring-boot-starter-webflux-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java @@ -1,12 +1,13 @@ package com.getyourguide.openapi.validation.example.configuration; import com.getyourguide.openapi.validation.api.log.LoggerExtension; +import com.getyourguide.openapi.validation.configuration.OpenApiValidationConfiguration; import com.getyourguide.openapi.validation.example.logging.ExampleLoggerExtension; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration -public class ExampleConfiguration { +public class ExampleConfiguration extends OpenApiValidationConfiguration { @Bean public LoggerExtension loggerExtension() { diff --git a/examples/example-spring-boot-starter-webflux/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java b/examples/example-spring-boot-starter-webflux/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java index f873cc2d..b3bc22b1 100644 --- a/examples/example-spring-boot-starter-webflux/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java +++ b/examples/example-spring-boot-starter-webflux/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java @@ -1,12 +1,13 @@ package com.getyourguide.openapi.validation.example.configuration; import com.getyourguide.openapi.validation.api.log.LoggerExtension; +import com.getyourguide.openapi.validation.configuration.OpenApiValidationConfiguration; import com.getyourguide.openapi.validation.example.logging.ExampleLoggerExtension; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration -public class ExampleConfiguration { +public class ExampleConfiguration extends OpenApiValidationConfiguration { @Bean public LoggerExtension loggerExtension() { diff --git a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfiguration.java b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfiguration.java index 60fa52d7..29ebb5e8 100644 --- a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfiguration.java +++ b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfiguration.java @@ -2,10 +2,11 @@ import com.getyourguide.openapi.validation.api.log.LogLevel; import java.util.Map; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; -@Builder +@AllArgsConstructor @Getter public class ValidatorConfiguration { private final LogLevel levelResolverDefaultLevel; diff --git a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfigurationBuilder.java b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfigurationBuilder.java new file mode 100644 index 00000000..bb0d60c9 --- /dev/null +++ b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfigurationBuilder.java @@ -0,0 +1,37 @@ +package com.getyourguide.openapi.validation.api.model; + +import com.getyourguide.openapi.validation.api.log.LogLevel; +import java.util.HashMap; +import java.util.Map; + +public class ValidatorConfigurationBuilder { + private LogLevel levelResolverDefaultLevel; + private Map levelResolverLevels; + + public ValidatorConfigurationBuilder levelResolverDefaultLevel(LogLevel levelResolverDefaultLevel) { + this.levelResolverDefaultLevel = levelResolverDefaultLevel; + return this; + } + + public ValidatorConfigurationBuilder levelResolverLevel(String messageKey, LogLevel level) { + if (this.levelResolverLevels == null) { + this.levelResolverLevels = new HashMap<>(); + } + this.levelResolverLevels.put(messageKey, level); + return this; + } + + public ValidatorConfiguration build() { + return new ValidatorConfiguration( + levelResolverDefaultLevel, + levelResolverLevels + ); + } + + public String toString() { + return "ValidatorConfigurationBuilder(" + + "levelResolverDefaultLevel=" + this.levelResolverDefaultLevel + ", " + + "levelResolverLevels=" + this.levelResolverLevels + + ")"; + } +} diff --git a/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/autoconfigure/LibraryAutoConfiguration.java b/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/autoconfigure/LibraryAutoConfiguration.java index 334c0dab..6aa6e560 100644 --- a/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/autoconfigure/LibraryAutoConfiguration.java +++ b/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/autoconfigure/LibraryAutoConfiguration.java @@ -1,13 +1,13 @@ package com.getyourguide.openapi.validation.autoconfigure; import com.getyourguide.openapi.validation.OpenApiValidationApplicationProperties; -import com.getyourguide.openapi.validation.api.log.LogLevel; import com.getyourguide.openapi.validation.api.log.LoggerExtension; import com.getyourguide.openapi.validation.api.log.NoOpLoggerExtension; import com.getyourguide.openapi.validation.api.log.ViolationLogger; import com.getyourguide.openapi.validation.api.metrics.MetricsReporter; import com.getyourguide.openapi.validation.api.metrics.NoOpMetricsReporter; import com.getyourguide.openapi.validation.api.model.ValidatorConfiguration; +import com.getyourguide.openapi.validation.configuration.OpenApiValidationConfiguration; import com.getyourguide.openapi.validation.core.DefaultViolationLogger; import com.getyourguide.openapi.validation.core.OpenApiRequestValidator; import com.getyourguide.openapi.validation.core.ValidationReportHandler; @@ -69,13 +69,7 @@ public ValidationReportHandler validationReportHandler( @Bean @ConditionalOnMissingBean public ValidatorConfiguration validatorConfiguration() { - return ValidatorConfiguration.builder() - // Example: .levelResolverLevels(levels) - // levels = mapOf( - // "validation.request.body.schema.additionalProperties" => LogLevel.IGNORE - // ) - .levelResolverDefaultLevel(LogLevel.INFO) - .build(); + return (new OpenApiValidationConfiguration()).buildValidatorConfiguration(); } @Bean diff --git a/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/configuration/OpenApiValidationConfiguration.java b/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/configuration/OpenApiValidationConfiguration.java new file mode 100644 index 00000000..e0c3d471 --- /dev/null +++ b/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/configuration/OpenApiValidationConfiguration.java @@ -0,0 +1,18 @@ +package com.getyourguide.openapi.validation.configuration; + +import com.getyourguide.openapi.validation.api.log.LogLevel; +import com.getyourguide.openapi.validation.api.model.ValidatorConfiguration; +import com.getyourguide.openapi.validation.api.model.ValidatorConfigurationBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class OpenApiValidationConfiguration { + @Bean + public ValidatorConfiguration buildValidatorConfiguration() { + return new ValidatorConfigurationBuilder() + // .levelResolverLevel("validation.request.body.schema.additionalProperties", LogLevel.IGNORE) + .levelResolverDefaultLevel(LogLevel.INFO) + .build(); + } +} From f5f13a2c0a06c8280bd9617872904259bbd88f9d Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 15 May 2023 11:44:47 +0200 Subject: [PATCH 02/10] Formatting --- .../validation/api/model/ValidatorConfigurationBuilder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfigurationBuilder.java b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfigurationBuilder.java index bb0d60c9..00d77964 100644 --- a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfigurationBuilder.java +++ b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfigurationBuilder.java @@ -29,9 +29,9 @@ public ValidatorConfiguration build() { } public String toString() { - return "ValidatorConfigurationBuilder(" + - "levelResolverDefaultLevel=" + this.levelResolverDefaultLevel + ", " + - "levelResolverLevels=" + this.levelResolverLevels + return "ValidatorConfigurationBuilder(" + + "levelResolverDefaultLevel=" + this.levelResolverDefaultLevel + ", " + + "levelResolverLevels=" + this.levelResolverLevels + ")"; } } From 81ee4328c806bc3ffdee299e4016c817d7ed4d36 Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 15 May 2023 11:50:38 +0200 Subject: [PATCH 03/10] Remove unused import --- .../openapi/validation/api/model/ValidatorConfiguration.java | 1 - 1 file changed, 1 deletion(-) diff --git a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfiguration.java b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfiguration.java index 29ebb5e8..ff089f00 100644 --- a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfiguration.java +++ b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfiguration.java @@ -3,7 +3,6 @@ import com.getyourguide.openapi.validation.api.log.LogLevel; import java.util.Map; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Getter; @AllArgsConstructor From 47ed9d521e3a0746ed82930ce07e63656a6ecd12 Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 15 May 2023 12:07:43 +0200 Subject: [PATCH 04/10] Refactoring: Wrap OpenApiInteractionValidator usage within OpenApiInteractionValidatorWrapper --- .../OpenApiInteractionValidatorFactory.java | 7 ++++-- .../core/OpenApiRequestValidator.java | 4 ++-- .../OpenApiInteractionValidatorWrapper.java | 12 ++++++++++ ...pecOpenApiInteractionValidatorWrapper.java | 23 +++++++++++++++++++ 4 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/OpenApiInteractionValidatorWrapper.java create mode 100644 openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/SingleSpecOpenApiInteractionValidatorWrapper.java diff --git a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiInteractionValidatorFactory.java b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiInteractionValidatorFactory.java index 282051f3..ea18e2df 100644 --- a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiInteractionValidatorFactory.java +++ b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiInteractionValidatorFactory.java @@ -5,6 +5,8 @@ import com.atlassian.oai.validator.report.ValidationReport; import com.getyourguide.openapi.validation.api.log.LogLevel; import com.getyourguide.openapi.validation.api.model.ValidatorConfiguration; +import com.getyourguide.openapi.validation.core.validator.OpenApiInteractionValidatorWrapper; +import com.getyourguide.openapi.validation.core.validator.SingleSpecOpenApiInteractionValidatorWrapper; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -21,7 +23,7 @@ @Slf4j public class OpenApiInteractionValidatorFactory { @Nullable - public OpenApiInteractionValidator build(String specificationFilePath, ValidatorConfiguration configuration) { + public OpenApiInteractionValidatorWrapper build(String specificationFilePath, ValidatorConfiguration configuration) { var specOptional = loadOpenAPISpec(specificationFilePath); if (specOptional.isEmpty()) { log.info("OpenAPI spec file could not be found [validation disabled]"); @@ -30,12 +32,13 @@ public OpenApiInteractionValidator build(String specificationFilePath, Validator var spec = specOptional.get(); try { - return OpenApiInteractionValidator + var validator = OpenApiInteractionValidator .createForInlineApiSpecification(spec) .withResolveRefs(true) .withResolveCombinators(true) // Inline to avoid problems with allOf .withLevelResolver(buildLevelResolver(configuration)) .build(); + return new SingleSpecOpenApiInteractionValidatorWrapper(validator); } catch (Throwable e) { log.error("Could not initialize OpenApiInteractionValidator [validation disabled]", e); return null; diff --git a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidator.java b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidator.java index 42e87e1c..4ec73fa7 100644 --- a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidator.java +++ b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiRequestValidator.java @@ -1,6 +1,5 @@ package com.getyourguide.openapi.validation.core; -import com.atlassian.oai.validator.OpenApiInteractionValidator; import com.atlassian.oai.validator.model.Request; import com.atlassian.oai.validator.model.SimpleRequest; import com.atlassian.oai.validator.model.SimpleResponse; @@ -8,6 +7,7 @@ import com.getyourguide.openapi.validation.api.model.RequestMetaData; import com.getyourguide.openapi.validation.api.model.ResponseMetaData; import com.getyourguide.openapi.validation.api.model.ValidatorConfiguration; +import com.getyourguide.openapi.validation.core.validator.OpenApiInteractionValidatorWrapper; import java.nio.charset.StandardCharsets; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -19,7 +19,7 @@ public class OpenApiRequestValidator { private final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 2, 1000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(10)); - private final OpenApiInteractionValidator validator; + private final OpenApiInteractionValidatorWrapper validator; private final ValidationReportHandler validationReportHandler; public OpenApiRequestValidator(ValidationReportHandler validationReportHandler, String specificationFilePath, ValidatorConfiguration configuration) { diff --git a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/OpenApiInteractionValidatorWrapper.java b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/OpenApiInteractionValidatorWrapper.java new file mode 100644 index 00000000..ea273b68 --- /dev/null +++ b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/OpenApiInteractionValidatorWrapper.java @@ -0,0 +1,12 @@ +package com.getyourguide.openapi.validation.core.validator; + +import com.atlassian.oai.validator.model.Request; +import com.atlassian.oai.validator.model.SimpleRequest; +import com.atlassian.oai.validator.model.SimpleResponse; +import com.atlassian.oai.validator.report.ValidationReport; + +public interface OpenApiInteractionValidatorWrapper { + ValidationReport validateRequest(SimpleRequest request); + + ValidationReport validateResponse(String path, Request.Method method, SimpleResponse response); +} diff --git a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/SingleSpecOpenApiInteractionValidatorWrapper.java b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/SingleSpecOpenApiInteractionValidatorWrapper.java new file mode 100644 index 00000000..4ba34aa5 --- /dev/null +++ b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/SingleSpecOpenApiInteractionValidatorWrapper.java @@ -0,0 +1,23 @@ +package com.getyourguide.openapi.validation.core.validator; + +import com.atlassian.oai.validator.OpenApiInteractionValidator; +import com.atlassian.oai.validator.model.Request; +import com.atlassian.oai.validator.model.SimpleRequest; +import com.atlassian.oai.validator.model.SimpleResponse; +import com.atlassian.oai.validator.report.ValidationReport; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class SingleSpecOpenApiInteractionValidatorWrapper implements OpenApiInteractionValidatorWrapper { + private final OpenApiInteractionValidator validator; + + @Override + public ValidationReport validateRequest(SimpleRequest request) { + return validator.validateRequest(request); + } + + @Override + public ValidationReport validateResponse(String path, Request.Method method, SimpleResponse response) { + return validator.validateResponse(path, method, response); + } +} From 23a03c6f6baace2e29f9de0a05779fb63cbcc20c Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 15 May 2023 16:25:24 +0200 Subject: [PATCH 05/10] Add MultipleSpecOpenApiInteractionValidatorWrapper --- ...pecOpenApiInteractionValidatorWrapper.java | 118 ++++++++++++++++++ ...penApiInteractionValidatorWrapperTest.java | 84 +++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapper.java create mode 100644 openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapperTest.java diff --git a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapper.java b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapper.java new file mode 100644 index 00000000..cff53b99 --- /dev/null +++ b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapper.java @@ -0,0 +1,118 @@ +package com.getyourguide.openapi.validation.core.validator; + +import com.atlassian.oai.validator.model.Request; +import com.atlassian.oai.validator.model.SimpleRequest; +import com.atlassian.oai.validator.model.SimpleResponse; +import com.atlassian.oai.validator.report.ValidationReport; +import java.util.List; +import java.util.Optional; +import java.util.regex.Pattern; +import javax.annotation.Nonnull; +import lombok.AllArgsConstructor; +import org.apache.commons.lang3.tuple.Pair; + +public class MultipleSpecOpenApiInteractionValidatorWrapper implements OpenApiInteractionValidatorWrapper { + public static final String MESSAGE_KEY_VALIDATOR_FOUND = "zopenapi-validator-java.noValidatorFound"; + private final List> validators; + + public MultipleSpecOpenApiInteractionValidatorWrapper( + List> validators + ) { + assert validators != null && validators.size() > 0; + + this.validators = validators; + } + + @Override + public ValidationReport validateRequest(SimpleRequest request) { + return getValidatorForPath(request.getPath()) + .map(validator -> validator.validateRequest(request)) + .orElse(new SimpleValidationReport(List.of(buildNoValidatorFoundMessage(request.getPath())))); + } + + @Override + public ValidationReport validateResponse(String path, Request.Method method, SimpleResponse response) { + return getValidatorForPath(path) + .map(validator -> validator.validateResponse(path, method, response)) + .orElse(new SimpleValidationReport(List.of(buildNoValidatorFoundMessage(path)))); + } + + private Optional getValidatorForPath(String path) { + for (var validator : validators) { + if (validator.getLeft().matcher(path).matches()) { + return Optional.of(validator.getRight()); + } + } + + return Optional.empty(); + } + + private static SimpleMessage buildNoValidatorFoundMessage(String path) { + return new SimpleMessage( + MESSAGE_KEY_VALIDATOR_FOUND, + "No validator found for path: " + path + ); + } + + @AllArgsConstructor + private static class SimpleValidationReport implements ValidationReport { + private final List messages; + + @Nonnull + @Override + public List getMessages() { + return messages; + } + + @Override + public ValidationReport withAdditionalContext(MessageContext context) { + return this; + } + } + + @AllArgsConstructor + private static class SimpleMessage implements ValidationReport.Message { + private final String key; + private final String message; + + @Override + public String getKey() { + return key; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public ValidationReport.Level getLevel() { + return ValidationReport.Level.WARN; + } + + @Override + public List getAdditionalInfo() { + return List.of(); + } + + @Override + public Optional getContext() { + return Optional.empty(); + } + + @Override + public ValidationReport.Message withLevel(ValidationReport.Level level) { + return this; + } + + @Override + public ValidationReport.Message withAdditionalInfo(String info) { + return this; + } + + @Override + public ValidationReport.Message withAdditionalContext(ValidationReport.MessageContext context) { + return this; + } + } +} diff --git a/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapperTest.java b/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapperTest.java new file mode 100644 index 00000000..01fca450 --- /dev/null +++ b/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapperTest.java @@ -0,0 +1,84 @@ +package com.getyourguide.openapi.validation.core.validator; + +import static com.getyourguide.openapi.validation.core.validator.MultipleSpecOpenApiInteractionValidatorWrapper.MESSAGE_KEY_VALIDATOR_FOUND; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.atlassian.oai.validator.model.Request; +import com.atlassian.oai.validator.model.SimpleRequest; +import com.atlassian.oai.validator.report.ValidationReport; +import java.util.List; +import java.util.regex.Pattern; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.jupiter.api.Test; + +public class MultipleSpecOpenApiInteractionValidatorWrapperTest { + private MultipleSpecOpenApiInteractionValidatorWrapper validator; + + @Test + public void testCallsCorrectValidator() { + var specificValidator = mockValidator(); + var catchAllValidator = mockValidator(); + + validator = new MultipleSpecOpenApiInteractionValidatorWrapper( + List.of( + Pair.of(Pattern.compile("/test/.*"), specificValidator.validator), + Pair.of(Pattern.compile(".*"), catchAllValidator.validator) + ) + ); + + + assertRequestPathHitsCorrectValidator(specificValidator.validationReport, "/test/123"); + assertRequestPathHitsCorrectValidator(catchAllValidator.validationReport, "/123"); + + assertResponsePathHitsCorrectValidator(specificValidator.validationReport, "/test/123"); + assertResponsePathHitsCorrectValidator(catchAllValidator.validationReport, "/123"); + } + + @Test + public void testReturnsViolationWhenNoMatchingValidatorFound() { + var specificValidator = mockValidator(); + + validator = new MultipleSpecOpenApiInteractionValidatorWrapper( + List.of( + Pair.of(Pattern.compile("/test/.*"), specificValidator.validator()) + ) + ); + + var path = "/123"; + var report = validator.validateRequest(new SimpleRequest.Builder("GET", path).build()); + + var messages = report.getMessages(); + assertEquals(1, messages.size()); + var message = messages.get(0); + assertEquals(MESSAGE_KEY_VALIDATOR_FOUND, message.getKey()); + assertEquals("No validator found for path: /123", message.getMessage()); + } + + private static MockValidatorResult mockValidator() { + var catchAllValidator = mock(OpenApiInteractionValidatorWrapper.class); + var catchAllValidationReport = mock(ValidationReport.class); + when(catchAllValidator.validateRequest(any())).thenReturn(catchAllValidationReport); + when(catchAllValidator.validateResponse(any(), any(), any())).thenReturn(catchAllValidationReport); + MockValidatorResult result = new MockValidatorResult(catchAllValidator, catchAllValidationReport); + return result; + } + + private record MockValidatorResult( + OpenApiInteractionValidatorWrapper validator, + ValidationReport validationReport + ) { + } + + private void assertRequestPathHitsCorrectValidator(ValidationReport validationReport, String path) { + var report = validator.validateRequest(new SimpleRequest.Builder("GET", path).build()); + assertEquals(validationReport, report); + } + + private void assertResponsePathHitsCorrectValidator(ValidationReport validationReport, String path) { + var report = validator.validateResponse(path, Request.Method.GET, mock()); + assertEquals(validationReport, report); + } +} From 992954eadc0d024d0080455268e866bd28afa868 Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 15 May 2023 17:36:13 +0200 Subject: [PATCH 06/10] Use MultipleSpecOpenApiInteractionValidatorWrapper based on configuration --- .../api/model/ValidatorConfiguration.java | 7 +++ .../model/ValidatorConfigurationBuilder.java | 15 ++++- .../OpenApiInteractionValidatorFactory.java | 58 ++++++++++++++++--- ...pecOpenApiInteractionValidatorWrapper.java | 5 ++ 4 files changed, 76 insertions(+), 9 deletions(-) diff --git a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfiguration.java b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfiguration.java index ff089f00..1b0d0ea9 100644 --- a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfiguration.java +++ b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfiguration.java @@ -1,7 +1,9 @@ package com.getyourguide.openapi.validation.api.model; import com.getyourguide.openapi.validation.api.log.LogLevel; +import java.util.List; import java.util.Map; +import java.util.regex.Pattern; import lombok.AllArgsConstructor; import lombok.Getter; @@ -10,4 +12,9 @@ public class ValidatorConfiguration { private final LogLevel levelResolverDefaultLevel; private final Map levelResolverLevels; + + private final List specificationPaths; + + public record PathPatternSpec(Pattern pathPattern, String specificationFilePath) { + } } diff --git a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfigurationBuilder.java b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfigurationBuilder.java index 00d77964..ba5ef91d 100644 --- a/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfigurationBuilder.java +++ b/openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/ValidatorConfigurationBuilder.java @@ -1,12 +1,16 @@ package com.getyourguide.openapi.validation.api.model; import com.getyourguide.openapi.validation.api.log.LogLevel; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.regex.Pattern; public class ValidatorConfigurationBuilder { private LogLevel levelResolverDefaultLevel; private Map levelResolverLevels; + private List specificationPaths; public ValidatorConfigurationBuilder levelResolverDefaultLevel(LogLevel levelResolverDefaultLevel) { this.levelResolverDefaultLevel = levelResolverDefaultLevel; @@ -21,10 +25,19 @@ public ValidatorConfigurationBuilder levelResolverLevel(String messageKey, LogLe return this; } + public ValidatorConfigurationBuilder specificationPath(Pattern pathPattern, String specPath) { + if (this.specificationPaths == null) { + this.specificationPaths = new ArrayList<>(); + } + this.specificationPaths.add(new ValidatorConfiguration.PathPatternSpec(pathPattern, specPath)); + return this; + } + public ValidatorConfiguration build() { return new ValidatorConfiguration( levelResolverDefaultLevel, - levelResolverLevels + levelResolverLevels, + specificationPaths ); } diff --git a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiInteractionValidatorFactory.java b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiInteractionValidatorFactory.java index ea18e2df..7da3cea5 100644 --- a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiInteractionValidatorFactory.java +++ b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/OpenApiInteractionValidatorFactory.java @@ -5,6 +5,7 @@ import com.atlassian.oai.validator.report.ValidationReport; import com.getyourguide.openapi.validation.api.log.LogLevel; import com.getyourguide.openapi.validation.api.model.ValidatorConfiguration; +import com.getyourguide.openapi.validation.core.validator.MultipleSpecOpenApiInteractionValidatorWrapper; import com.getyourguide.openapi.validation.core.validator.OpenApiInteractionValidatorWrapper; import com.getyourguide.openapi.validation.core.validator.SingleSpecOpenApiInteractionValidatorWrapper; import java.io.BufferedReader; @@ -13,30 +14,66 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import javax.annotation.Nullable; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.tuple.Pair; @Slf4j public class OpenApiInteractionValidatorFactory { @Nullable - public OpenApiInteractionValidatorWrapper build(String specificationFilePath, ValidatorConfiguration configuration) { + public OpenApiInteractionValidatorWrapper build( + String specificationFilePath, + ValidatorConfiguration configuration + ) { + if (configuration.getSpecificationPaths() != null && !configuration.getSpecificationPaths().isEmpty()) { + return buildMultipleSpecOpenApiInteractionValidatorWrapper(configuration); + } + var specOptional = loadOpenAPISpec(specificationFilePath); if (specOptional.isEmpty()) { log.info("OpenAPI spec file could not be found [validation disabled]"); return null; } - var spec = specOptional.get(); + return buildSingleSpecOpenApiInteractionValidatorWrapper(specOptional.get(), + configuration.getLevelResolverLevels(), configuration.getLevelResolverDefaultLevel()); + } + + private MultipleSpecOpenApiInteractionValidatorWrapper buildMultipleSpecOpenApiInteractionValidatorWrapper( + ValidatorConfiguration configuration) { + var validators = configuration.getSpecificationPaths().stream() + .map(entry -> { + var path = entry.specificationFilePath(); + var specOptional = loadSpecFromPath(path).or(() -> loadSpecFromResources(path)); + if (specOptional.isEmpty()) { + log.error("OpenAPI spec file {} could not be found", path); + return null; + } + var validator = buildSingleSpecOpenApiInteractionValidatorWrapper(specOptional.get(), + configuration.getLevelResolverLevels(), configuration.getLevelResolverDefaultLevel()); + return Pair.of(entry.pathPattern(), (OpenApiInteractionValidatorWrapper) validator); + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + return new MultipleSpecOpenApiInteractionValidatorWrapper(validators); + } + + private SingleSpecOpenApiInteractionValidatorWrapper buildSingleSpecOpenApiInteractionValidatorWrapper( + String spec, + Map levelResolverLevels, + LogLevel levelResolverDefaultLevel + ) { try { var validator = OpenApiInteractionValidator .createForInlineApiSpecification(spec) .withResolveRefs(true) .withResolveCombinators(true) // Inline to avoid problems with allOf - .withLevelResolver(buildLevelResolver(configuration)) + .withLevelResolver(buildLevelResolver(levelResolverLevels, levelResolverDefaultLevel)) .build(); return new SingleSpecOpenApiInteractionValidatorWrapper(validator); } catch (Throwable e) { @@ -98,17 +135,22 @@ private Optional loadSpecFromResources(String resourceFileLocation) { } } - private LevelResolver buildLevelResolver(ValidatorConfiguration configuration) { + private LevelResolver buildLevelResolver( + Map levelResolverLevels, + LogLevel levelResolverDefaultLevel + ) { var builder = LevelResolver.create(); - if (configuration.getLevelResolverLevels() != null && !configuration.getLevelResolverLevels().isEmpty()) { + if (levelResolverLevels != null && !levelResolverLevels.isEmpty()) { builder.withLevels( - configuration.getLevelResolverLevels().entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> mapLevel(entry.getValue()).orElse(ValidationReport.Level.INFO))) + levelResolverLevels.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, entry -> + mapLevel(entry.getValue()).orElse(ValidationReport.Level.INFO)) + ) ); } return builder // this will cause all messages to be warn by default - .withDefaultLevel(mapLevel(configuration.getLevelResolverDefaultLevel()).orElse(ValidationReport.Level.INFO)) + .withDefaultLevel(mapLevel(levelResolverDefaultLevel).orElse(ValidationReport.Level.INFO)) .build(); } diff --git a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapper.java b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapper.java index cff53b99..cf314a34 100644 --- a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapper.java +++ b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapper.java @@ -114,5 +114,10 @@ public ValidationReport.Message withAdditionalInfo(String info) { public ValidationReport.Message withAdditionalContext(ValidationReport.MessageContext context) { return this; } + + @Override + public String toString() { + return message; + } } } From ae16248d042a6e039bd5d6801e51750356d21a5a Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 15 May 2023 17:38:57 +0200 Subject: [PATCH 07/10] Remove OpenApiValidationConfiguration (with `@AutoConfiguration`) --- .../configuration/ExampleConfiguration.java | 3 +-- .../configuration/ExampleConfiguration.java | 7 +++++-- .../configuration/ExampleConfiguration.java | 3 +-- .../configuration/ExampleConfiguration.java | 3 +-- .../LibraryAutoConfiguration.java | 8 ++++++-- .../OpenApiValidationConfiguration.java | 18 ------------------ 6 files changed, 14 insertions(+), 28 deletions(-) delete mode 100644 spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/configuration/OpenApiValidationConfiguration.java diff --git a/examples/example-spring-boot-starter-web-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java b/examples/example-spring-boot-starter-web-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java index e8b30917..1d685167 100644 --- a/examples/example-spring-boot-starter-web-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java +++ b/examples/example-spring-boot-starter-web-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java @@ -2,14 +2,13 @@ import com.getyourguide.openapi.validation.api.log.LoggerExtension; import com.getyourguide.openapi.validation.api.metrics.MetricsReporter; -import com.getyourguide.openapi.validation.configuration.OpenApiValidationConfiguration; import com.getyourguide.openapi.validation.example.logging.ExampleLoggerExtension; import com.getyourguide.openapi.validation.metrics.LoggingMetricsReporter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration -public class ExampleConfiguration extends OpenApiValidationConfiguration { +public class ExampleConfiguration { @Bean public MetricsReporter metricsReporter() { return new LoggingMetricsReporter(); diff --git a/examples/example-spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java b/examples/example-spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java index e8b30917..c4937dd0 100644 --- a/examples/example-spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java +++ b/examples/example-spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java @@ -1,15 +1,18 @@ package com.getyourguide.openapi.validation.example.configuration; +import com.getyourguide.openapi.validation.api.log.LogLevel; import com.getyourguide.openapi.validation.api.log.LoggerExtension; import com.getyourguide.openapi.validation.api.metrics.MetricsReporter; -import com.getyourguide.openapi.validation.configuration.OpenApiValidationConfiguration; +import com.getyourguide.openapi.validation.api.model.ValidatorConfiguration; +import com.getyourguide.openapi.validation.api.model.ValidatorConfigurationBuilder; import com.getyourguide.openapi.validation.example.logging.ExampleLoggerExtension; import com.getyourguide.openapi.validation.metrics.LoggingMetricsReporter; +import java.util.regex.Pattern; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration -public class ExampleConfiguration extends OpenApiValidationConfiguration { +public class ExampleConfiguration { @Bean public MetricsReporter metricsReporter() { return new LoggingMetricsReporter(); diff --git a/examples/example-spring-boot-starter-webflux-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java b/examples/example-spring-boot-starter-webflux-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java index b3bc22b1..f873cc2d 100644 --- a/examples/example-spring-boot-starter-webflux-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java +++ b/examples/example-spring-boot-starter-webflux-spring2.7/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java @@ -1,13 +1,12 @@ package com.getyourguide.openapi.validation.example.configuration; import com.getyourguide.openapi.validation.api.log.LoggerExtension; -import com.getyourguide.openapi.validation.configuration.OpenApiValidationConfiguration; import com.getyourguide.openapi.validation.example.logging.ExampleLoggerExtension; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration -public class ExampleConfiguration extends OpenApiValidationConfiguration { +public class ExampleConfiguration { @Bean public LoggerExtension loggerExtension() { diff --git a/examples/example-spring-boot-starter-webflux/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java b/examples/example-spring-boot-starter-webflux/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java index b3bc22b1..f873cc2d 100644 --- a/examples/example-spring-boot-starter-webflux/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java +++ b/examples/example-spring-boot-starter-webflux/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java @@ -1,13 +1,12 @@ package com.getyourguide.openapi.validation.example.configuration; import com.getyourguide.openapi.validation.api.log.LoggerExtension; -import com.getyourguide.openapi.validation.configuration.OpenApiValidationConfiguration; import com.getyourguide.openapi.validation.example.logging.ExampleLoggerExtension; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration -public class ExampleConfiguration extends OpenApiValidationConfiguration { +public class ExampleConfiguration { @Bean public LoggerExtension loggerExtension() { diff --git a/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/autoconfigure/LibraryAutoConfiguration.java b/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/autoconfigure/LibraryAutoConfiguration.java index 6aa6e560..dbe0e724 100644 --- a/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/autoconfigure/LibraryAutoConfiguration.java +++ b/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/autoconfigure/LibraryAutoConfiguration.java @@ -1,13 +1,14 @@ package com.getyourguide.openapi.validation.autoconfigure; import com.getyourguide.openapi.validation.OpenApiValidationApplicationProperties; +import com.getyourguide.openapi.validation.api.log.LogLevel; import com.getyourguide.openapi.validation.api.log.LoggerExtension; import com.getyourguide.openapi.validation.api.log.NoOpLoggerExtension; import com.getyourguide.openapi.validation.api.log.ViolationLogger; import com.getyourguide.openapi.validation.api.metrics.MetricsReporter; import com.getyourguide.openapi.validation.api.metrics.NoOpMetricsReporter; import com.getyourguide.openapi.validation.api.model.ValidatorConfiguration; -import com.getyourguide.openapi.validation.configuration.OpenApiValidationConfiguration; +import com.getyourguide.openapi.validation.api.model.ValidatorConfigurationBuilder; import com.getyourguide.openapi.validation.core.DefaultViolationLogger; import com.getyourguide.openapi.validation.core.OpenApiRequestValidator; import com.getyourguide.openapi.validation.core.ValidationReportHandler; @@ -69,7 +70,10 @@ public ValidationReportHandler validationReportHandler( @Bean @ConditionalOnMissingBean public ValidatorConfiguration validatorConfiguration() { - return (new OpenApiValidationConfiguration()).buildValidatorConfiguration(); + return new ValidatorConfigurationBuilder() + // .levelResolverLevel("validation.request.body.schema.additionalProperties", LogLevel.IGNORE) + .levelResolverDefaultLevel(LogLevel.INFO) + .build(); } @Bean diff --git a/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/configuration/OpenApiValidationConfiguration.java b/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/configuration/OpenApiValidationConfiguration.java deleted file mode 100644 index e0c3d471..00000000 --- a/spring-boot-starter/spring-boot-starter-core/src/main/java/com/getyourguide/openapi/validation/configuration/OpenApiValidationConfiguration.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.getyourguide.openapi.validation.configuration; - -import com.getyourguide.openapi.validation.api.log.LogLevel; -import com.getyourguide.openapi.validation.api.model.ValidatorConfiguration; -import com.getyourguide.openapi.validation.api.model.ValidatorConfigurationBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class OpenApiValidationConfiguration { - @Bean - public ValidatorConfiguration buildValidatorConfiguration() { - return new ValidatorConfigurationBuilder() - // .levelResolverLevel("validation.request.body.schema.additionalProperties", LogLevel.IGNORE) - .levelResolverDefaultLevel(LogLevel.INFO) - .build(); - } -} From 9403443c547d8114f9b8b20fd9853739e8bc362b Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Mon, 15 May 2023 17:39:42 +0200 Subject: [PATCH 08/10] Remove unused imports --- .../example/configuration/ExampleConfiguration.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/example-spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java b/examples/example-spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java index c4937dd0..1d685167 100644 --- a/examples/example-spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java +++ b/examples/example-spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/example/configuration/ExampleConfiguration.java @@ -1,13 +1,9 @@ package com.getyourguide.openapi.validation.example.configuration; -import com.getyourguide.openapi.validation.api.log.LogLevel; import com.getyourguide.openapi.validation.api.log.LoggerExtension; import com.getyourguide.openapi.validation.api.metrics.MetricsReporter; -import com.getyourguide.openapi.validation.api.model.ValidatorConfiguration; -import com.getyourguide.openapi.validation.api.model.ValidatorConfigurationBuilder; import com.getyourguide.openapi.validation.example.logging.ExampleLoggerExtension; import com.getyourguide.openapi.validation.metrics.LoggingMetricsReporter; -import java.util.regex.Pattern; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; From c741e6ee1a57eba63fd3a67c54a7b3f42649d25d Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Tue, 16 May 2023 12:14:09 +0200 Subject: [PATCH 09/10] Update README.md with instructions --- README.md | 38 +++++++++++++++++++ ...pecOpenApiInteractionValidatorWrapper.java | 6 ++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b455eb1d..200d465b 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,44 @@ public class SampleRateTrafficSelector implements TrafficSelector { } ``` +### Custom log levels +One can customize log levels as per the following example. The default log level is `info`. + +The key to be used here is also printed in the violation log message. + +```java +@Configuration +public class ValidatorConfiguration { + @Bean + public ValidatorConfiguration buildValidatorConfiguration() { + return new ValidatorConfigurationBuilder() + .levelResolverLevel("validation.request.body.schema.additionalProperties", LogLevel.ERROR) + .levelResolverDefaultLevel(LogLevel.INFO) + .build(); + } +} +``` + +### Multiple spec files +It is possible to use multiple spec files for different paths. This can be achieved as demonstrated in the following +code snipped. + +It is best practice to use a catch-all spec file. If a request is not matching any of the paths defined here it will +result in a violation error with log level `warn`. + +```java +@Configuration +public class ValidatorConfiguration { + @Bean + public ValidatorConfiguration buildValidatorConfiguration() { + return new ValidatorConfigurationBuilder() + .specificationPath(Pattern.compile("/v1/.*"), "openapi-v1.yaml") + .specificationPath(Pattern.compile("/.*"), "openapi.yaml") + .build(); + } +} +``` + ## Examples Run examples with `./gradlew :examples:example-spring-boot-starter-web:bootRun` or `./gradlew :examples:example-spring-boot-starter-webflux:bootRun`. diff --git a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapper.java b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapper.java index cf314a34..d5ef5049 100644 --- a/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapper.java +++ b/openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapper.java @@ -50,7 +50,8 @@ private Optional getValidatorForPath(String private static SimpleMessage buildNoValidatorFoundMessage(String path) { return new SimpleMessage( MESSAGE_KEY_VALIDATOR_FOUND, - "No validator found for path: " + path + "No validator found for path: " + path, + ValidationReport.Level.WARN ); } @@ -74,6 +75,7 @@ public ValidationReport withAdditionalContext(MessageContext context) { private static class SimpleMessage implements ValidationReport.Message { private final String key; private final String message; + private final ValidationReport.Level level; @Override public String getKey() { @@ -87,7 +89,7 @@ public String getMessage() { @Override public ValidationReport.Level getLevel() { - return ValidationReport.Level.WARN; + return level; } @Override From 55177a4fb5b662ce1ea45bfe6e9d165ac5a134f6 Mon Sep 17 00:00:00 2001 From: Patrick Boos Date: Tue, 16 May 2023 12:15:29 +0200 Subject: [PATCH 10/10] Remove unnecessary empty line --- .../MultipleSpecOpenApiInteractionValidatorWrapperTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapperTest.java b/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapperTest.java index 01fca450..53eb5b49 100644 --- a/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapperTest.java +++ b/openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapperTest.java @@ -29,7 +29,6 @@ public void testCallsCorrectValidator() { ) ); - assertRequestPathHitsCorrectValidator(specificValidator.validationReport, "/test/123"); assertRequestPathHitsCorrectValidator(catchAllValidator.validationReport, "/123");