Skip to content

Commit 0055dd4

Browse files
authored
bugfix: on exception log 500 status code instead of 200 status code (#65)
1 parent 91b5852 commit 0055dd4

File tree

38 files changed

+1838
-479
lines changed

38 files changed

+1838
-479
lines changed

build.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ allprojects {
2424
}
2525

2626
subprojects {
27-
if(it.parent.name == 'examples' || it.parent.name == 'test') {
27+
if(it.parent.name == 'examples') {
2828
apply plugin: 'java'
2929
} else {
3030
apply plugin: 'java-library'
@@ -70,6 +70,9 @@ subprojects {
7070
toolVersion = libs.versions.checkstyle.get()
7171
configDirectory.set(file("$rootProject.projectDir/config"))
7272
checkstyleMain.source = "src/main/java"
73+
checkstyleMain.exclude('**/build/generated/**')
74+
checkstyleTest.source = "src/main/java"
75+
checkstyleTest.exclude('**/build/generated/**')
7376
}
7477

7578
pmd {

openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/validator/MultipleSpecOpenApiInteractionValidatorWrapperTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ private static MockValidatorResult mockValidator() {
6161
var catchAllValidationReport = mock(ValidationReport.class);
6262
when(catchAllValidator.validateRequest(any())).thenReturn(catchAllValidationReport);
6363
when(catchAllValidator.validateResponse(any(), any(), any())).thenReturn(catchAllValidationReport);
64-
MockValidatorResult result = new MockValidatorResult(catchAllValidator, catchAllValidationReport);
65-
return result;
64+
return new MockValidatorResult(catchAllValidator, catchAllValidationReport);
6665
}
6766

6867
private record MockValidatorResult(

settings.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ include(':metrics-reporter:metrics-reporter-datadog-spring-boot')
1313
include(':examples:examples-common')
1414
include(':examples:example-spring-boot-starter-web')
1515
include(':examples:example-spring-boot-starter-webflux')
16+
17+
include(':test:openapi-web')
18+
include(':test:openapi-webflux')
19+
include(':test:test-utils')

spring-boot-starter/spring-boot-starter-web/build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import org.springframework.boot.gradle.plugin.SpringBootPlugin
22

33
plugins {
44
alias(libs.plugins.spring.boot) apply false
5+
alias(libs.plugins.openapi.generator)
56
}
67

7-
apply from: "${rootDir}/gradle/publish-module.gradle"
8-
98
dependencies {
109
implementation platform(SpringBootPlugin.BOM_COORDINATES)
1110

@@ -20,7 +19,8 @@ dependencies {
2019
// TODO use spotbugs instead and also apply to all modules?
2120
implementation(libs.find.bugs)
2221

23-
testImplementation 'org.springframework.boot:spring-boot-starter-test'
22+
testImplementation project(':test:test-utils')
23+
testImplementation project(':test:openapi-web')
2424
testImplementation 'org.springframework:spring-web'
2525
testImplementation 'org.springframework:spring-webmvc'
2626
testImplementation 'org.apache.tomcat.embed:tomcat-embed-core' // For jakarta.servlet.ServletContext

spring-boot-starter/spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/autoconfigure/SpringWebLibraryAutoConfiguration.java

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
import com.getyourguide.openapi.validation.core.OpenApiRequestValidator;
77
import com.getyourguide.openapi.validation.factory.ContentCachingWrapperFactory;
88
import com.getyourguide.openapi.validation.factory.ServletMetaDataFactory;
9-
import com.getyourguide.openapi.validation.filter.OpenApiValidationHttpFilter;
9+
import com.getyourguide.openapi.validation.filter.OpenApiValidationFilter;
10+
import com.getyourguide.openapi.validation.filter.OpenApiValidationInterceptor;
1011
import lombok.AllArgsConstructor;
1112
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
1213
import org.springframework.context.annotation.Bean;
1314
import org.springframework.context.annotation.Configuration;
15+
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
16+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
1417

1518
@Configuration
1619
@AllArgsConstructor
@@ -30,17 +33,39 @@ public ContentCachingWrapperFactory contentCachingWrapperFactory() {
3033

3134
@Bean
3235
@ConditionalOnWebApplication(type = Type.SERVLET)
33-
public OpenApiValidationHttpFilter openApiValidationHttpFilter(
36+
public OpenApiValidationFilter openApiValidationFilter(
3437
OpenApiRequestValidator validator,
3538
TrafficSelector trafficSelector,
3639
ServletMetaDataFactory metaDataFactory,
3740
ContentCachingWrapperFactory contentCachingWrapperFactory
3841
) {
39-
return new OpenApiValidationHttpFilter(
42+
return new OpenApiValidationFilter(
4043
validator,
4144
trafficSelector,
4245
metaDataFactory,
4346
contentCachingWrapperFactory
4447
);
4548
}
49+
50+
@Bean
51+
@ConditionalOnWebApplication(type = Type.SERVLET)
52+
public WebMvcConfigurer addOpenApiValidationInterceptor(
53+
OpenApiRequestValidator validator,
54+
TrafficSelector trafficSelector,
55+
ServletMetaDataFactory metaDataFactory,
56+
ContentCachingWrapperFactory contentCachingWrapperFactory
57+
) {
58+
var interceptor = new OpenApiValidationInterceptor(
59+
validator,
60+
trafficSelector,
61+
metaDataFactory,
62+
contentCachingWrapperFactory
63+
);
64+
return new WebMvcConfigurer() {
65+
@Override
66+
public void addInterceptors(final InterceptorRegistry registry) {
67+
registry.addInterceptor(interceptor);
68+
}
69+
};
70+
}
4671
}

spring-boot-starter/spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/factory/ContentCachingWrapperFactory.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,36 @@
22

33
import jakarta.servlet.http.HttpServletRequest;
44
import jakarta.servlet.http.HttpServletResponse;
5+
import javax.annotation.Nullable;
56
import org.springframework.web.util.ContentCachingRequestWrapper;
67
import org.springframework.web.util.ContentCachingResponseWrapper;
8+
import org.springframework.web.util.WebUtils;
79

810
public class ContentCachingWrapperFactory {
911
public ContentCachingRequestWrapper buildContentCachingRequestWrapper(HttpServletRequest request) {
12+
if (request instanceof ContentCachingRequestWrapper) {
13+
return (ContentCachingRequestWrapper) request;
14+
}
15+
1016
return new ContentCachingRequestWrapper(request);
1117
}
1218

1319
public ContentCachingResponseWrapper buildContentCachingResponseWrapper(HttpServletResponse response) {
20+
var cachingResponse = getCachingResponse(response);
21+
if (cachingResponse != null) {
22+
return cachingResponse;
23+
}
24+
1425
return new ContentCachingResponseWrapper(response);
1526
}
27+
28+
@Nullable
29+
public ContentCachingResponseWrapper getCachingResponse(final HttpServletResponse response) {
30+
return WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
31+
}
32+
33+
@Nullable
34+
public ContentCachingRequestWrapper getCachingRequest(HttpServletRequest request) {
35+
return request instanceof ContentCachingRequestWrapper ? (ContentCachingRequestWrapper) request : null;
36+
}
1637
}

spring-boot-starter/spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/factory/ServletMetaDataFactory.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ public RequestMetaData buildRequestMetaData(HttpServletRequest request) {
1717
}
1818

1919
public ResponseMetaData buildResponseMetaData(HttpServletResponse response) {
20-
return new ResponseMetaData(response.getStatus(), response.getContentType(), getHeaders(response));
20+
return buildResponseMetaData(response, null);
21+
}
22+
23+
public ResponseMetaData buildResponseMetaData(HttpServletResponse response, Exception exception) {
24+
var status = response.getStatus() == 200 && exception != null ? 500 : response.getStatus();
25+
return new ResponseMetaData(status, response.getContentType(), getHeaders(response));
2126
}
2227

2328
private static TreeMap<String, String> getHeaders(HttpServletRequest request) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.getyourguide.openapi.validation.filter;
2+
3+
import com.getyourguide.openapi.validation.api.selector.TrafficSelector;
4+
import com.getyourguide.openapi.validation.core.OpenApiRequestValidator;
5+
import com.getyourguide.openapi.validation.factory.ContentCachingWrapperFactory;
6+
import com.getyourguide.openapi.validation.factory.ServletMetaDataFactory;
7+
import jakarta.servlet.FilterChain;
8+
import jakarta.servlet.ServletException;
9+
import jakarta.servlet.http.HttpServletRequest;
10+
import jakarta.servlet.http.HttpServletResponse;
11+
import java.io.IOException;
12+
import lombok.AllArgsConstructor;
13+
import lombok.extern.slf4j.Slf4j;
14+
import org.springframework.web.filter.OncePerRequestFilter;
15+
16+
@Slf4j
17+
@AllArgsConstructor
18+
public class OpenApiValidationFilter extends OncePerRequestFilter {
19+
public static final String ATTRIBUTE_SKIP_VALIDATION = "gyg.openapi-validation.skipValidation";
20+
public static final String ATTRIBUTE_REQUEST_META_DATA = "gyg.openapi-validation.requestMetaData";
21+
22+
private final OpenApiRequestValidator validator;
23+
private final TrafficSelector trafficSelector;
24+
private final ServletMetaDataFactory metaDataFactory;
25+
private final ContentCachingWrapperFactory contentCachingWrapperFactory;
26+
27+
@Override
28+
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
29+
throws ServletException, IOException {
30+
var requestMetaData = metaDataFactory.buildRequestMetaData(request);
31+
request.setAttribute(ATTRIBUTE_REQUEST_META_DATA, requestMetaData);
32+
if (!validator.isReady() || !trafficSelector.shouldRequestBeValidated(requestMetaData)) {
33+
request.setAttribute(ATTRIBUTE_SKIP_VALIDATION, true);
34+
request.setAttribute(ATTRIBUTE_SKIP_VALIDATION, true);
35+
filterChain.doFilter(request, response);
36+
return;
37+
}
38+
39+
var requestToUse = contentCachingWrapperFactory.buildContentCachingRequestWrapper(request);
40+
var responseToUse = contentCachingWrapperFactory.buildContentCachingResponseWrapper(response);
41+
filterChain.doFilter(requestToUse, responseToUse);
42+
43+
// in case the response was cached it has to be written to the original response
44+
if (!isAsyncStarted(requestToUse)) {
45+
var cachingResponse = contentCachingWrapperFactory.getCachingResponse(responseToUse);
46+
if (cachingResponse != null) {
47+
cachingResponse.copyBodyToResponse();
48+
}
49+
}
50+
}
51+
52+
@Override
53+
protected boolean shouldNotFilterAsyncDispatch() {
54+
return false;
55+
}
56+
}

spring-boot-starter/spring-boot-starter-web/src/main/java/com/getyourguide/openapi/validation/filter/OpenApiValidationHttpFilter.java

Lines changed: 0 additions & 158 deletions
This file was deleted.

0 commit comments

Comments
 (0)