Skip to content

Commit 6176fd2

Browse files
authored
[Bugfix] Exclude violation operation.notAllowed with 405 (#43)
1 parent 2f21230 commit 6176fd2

File tree

6 files changed

+94
-32
lines changed

6 files changed

+94
-32
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.getyourguide.openapi.validation.api;
2+
3+
public class Rules {
4+
public static class Request {
5+
public static final String PATH_MISSING = "validation.request.path.missing";
6+
public static final String OPERATION_NOT_ALLOWED = "validation.request.operation.notAllowed";
7+
public static final String BODY_SCHEMA_ONE_OF = "validation.request.body.schema.oneOf";
8+
}
9+
10+
public static class Response {
11+
public static final String BODY_SCHEMA_ONE_OF = "validation.response.body.schema.oneOf";
12+
}
13+
}

openapi-validation-api/src/main/java/com/getyourguide/openapi/validation/api/model/OpenApiViolation.java

+30-6
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,36 @@ public class OpenApiViolation {
1313
private final RequestMetaData requestMetaData;
1414
private final String body;
1515
private final String rule;
16-
private final Optional<String> operationId;
17-
private final Optional<String> normalizedPath;
18-
private final Optional<String> instance;
19-
private final Optional<String> parameter;
20-
private final Optional<String> schema;
21-
private final Optional<Integer> responseStatus;
16+
private final String operationId;
17+
private final String normalizedPath;
18+
private final String instance;
19+
private final String parameter;
20+
private final String schema;
21+
private final Integer responseStatus;
2222
private final String message;
2323
private final String logMessage;
24+
25+
public Optional<String> getOperationId() {
26+
return Optional.ofNullable(operationId);
27+
}
28+
29+
public Optional<String> getNormalizedPath() {
30+
return Optional.ofNullable(normalizedPath);
31+
}
32+
33+
public Optional<String> getInstance() {
34+
return Optional.ofNullable(instance);
35+
}
36+
37+
public Optional<String> getParameter() {
38+
return Optional.ofNullable(parameter);
39+
}
40+
41+
public Optional<String> getSchema() {
42+
return Optional.ofNullable(schema);
43+
}
44+
45+
public Optional<Integer> getResponseStatus() {
46+
return Optional.ofNullable(responseStatus);
47+
}
2448
}

openapi-validation-core/src/main/java/com/getyourguide/openapi/validation/core/ValidationReportHandler.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,12 @@ private OpenApiViolation buildOpenApiViolation(
7878
.requestMetaData(request)
7979
.body(body)
8080
.rule(message.getKey())
81-
.operationId(getOperationId(message))
82-
.normalizedPath(getNormalizedPath(message))
83-
.instance(pointersInstance)
84-
.parameter(parameterName)
85-
.schema(getPointersSchema(message))
86-
.responseStatus(getResponseStatus(response, message))
81+
.operationId(getOperationId(message).orElse(null))
82+
.normalizedPath(getNormalizedPath(message).orElse(null))
83+
.instance(pointersInstance.orElse(null))
84+
.parameter(parameterName.orElse(null))
85+
.schema(getPointersSchema(message).orElse(null))
86+
.responseStatus(getResponseStatus(response, message).orElse(null))
8787
.logMessage(logMessage)
8888
.message(message.getMessage())
8989
.build();
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
11
package com.getyourguide.openapi.validation.core.exclusions;
22

3+
import com.getyourguide.openapi.validation.api.Rules;
34
import com.getyourguide.openapi.validation.api.exclusions.ViolationExclusions;
45
import com.getyourguide.openapi.validation.api.model.Direction;
56
import com.getyourguide.openapi.validation.api.model.OpenApiViolation;
67
import lombok.AllArgsConstructor;
78

89
@AllArgsConstructor
910
public class InternalViolationExclusions {
11+
1012
private final ViolationExclusions customViolationExclusions;
1113

1214
public boolean isExcluded(OpenApiViolation violation) {
1315
return falsePositive404(violation)
1416
|| falsePositive400(violation)
17+
|| falsePositive405(violation)
1518
|| customViolationExclusions.isExcluded(violation)
1619
|| oneOfMatchesMoreThanOneSchema(violation);
1720
}
1821

1922
private static boolean oneOfMatchesMoreThanOneSchema(OpenApiViolation violation) {
2023
return (
21-
"validation.response.body.schema.oneOf".equals(violation.getRule())
22-
|| "validation.request.body.schema.oneOf".equals(violation.getRule())
24+
Rules.Response.BODY_SCHEMA_ONE_OF.equals(violation.getRule())
25+
|| Rules.Request.BODY_SCHEMA_ONE_OF.equals(violation.getRule())
2326
)
2427
&& violation.getMessage()
2528
.matches(".*Instance failed to match exactly one schema \\(matched [1-9][0-9]* out of \\d+\\).*");
2629
}
2730

2831
private boolean falsePositive404(OpenApiViolation violation) {
29-
return "validation.request.path.missing".equals(violation.getRule())
32+
return Rules.Request.PATH_MISSING.equals(violation.getRule())
3033
&& (
3134
violation.getDirection() == Direction.REQUEST
3235
|| (violation.getDirection() == Direction.RESPONSE && violation.getResponseStatus().orElse(0) == 404)
@@ -36,4 +39,9 @@ private boolean falsePositive404(OpenApiViolation violation) {
3639
private boolean falsePositive400(OpenApiViolation violation) {
3740
return violation.getDirection() == Direction.REQUEST && violation.getResponseStatus().orElse(0) == 400;
3841
}
42+
43+
private boolean falsePositive405(OpenApiViolation violation) {
44+
return violation.getResponseStatus().orElse(0) == 405
45+
&& Rules.Request.OPERATION_NOT_ALLOWED.equals(violation.getRule());
46+
}
3947
}

openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/exclusions/InternalViolationExclusionsTest.java

+30-12
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import com.getyourguide.openapi.validation.api.exclusions.ViolationExclusions;
1010
import com.getyourguide.openapi.validation.api.model.Direction;
1111
import com.getyourguide.openapi.validation.api.model.OpenApiViolation;
12-
import java.util.Optional;
1312
import org.junit.jupiter.api.BeforeEach;
1413
import org.junit.jupiter.api.Test;
1514

@@ -24,7 +23,7 @@ public void setup() {
2423
}
2524

2625
@Test
27-
public void testWhenViolationThenViolationNotExcluded() {
26+
public void whenViolationThenViolationNotExcluded() {
2827
when(customViolationExclusions.isExcluded(any())).thenReturn(false);
2928

3029
checkViolationNotExcluded(buildSimpleViolation(Direction.RESPONSE, 404));
@@ -38,20 +37,20 @@ private static OpenApiViolation buildSimpleViolation(Direction direction, Intege
3837
return OpenApiViolation.builder()
3938
.direction(direction)
4039
.rule("validation." + (direction == Direction.REQUEST ? "request" : "response") + ".something")
41-
.responseStatus(responseStatus != null ? Optional.of(responseStatus) : Optional.empty())
40+
.responseStatus(responseStatus)
4241
.message("Some violation message")
4342
.build();
4443
}
4544

4645
@Test
47-
public void testWhenCustomViolationExclusionThenViolationExcluded() {
46+
public void whenCustomViolationExclusionThenViolationExcluded() {
4847
when(customViolationExclusions.isExcluded(any())).thenReturn(true);
4948

5049
checkViolationExcluded(OpenApiViolation.builder().build());
5150
}
5251

5352
@Test
54-
public void testWhenInstanceFailedToMatchExactlyOneThenViolationExcluded() {
53+
public void whenInstanceFailedToMatchExactlyOneThenViolationExcluded() {
5554
when(customViolationExclusions.isExcluded(any())).thenReturn(false);
5655

5756
checkViolationExcluded(OpenApiViolation.builder()
@@ -60,7 +59,7 @@ public void testWhenInstanceFailedToMatchExactlyOneThenViolationExcluded() {
6059
}
6160

6261
@Test
63-
public void testWhenInstanceFailedToMatchExactlyOneWithOneOf24ThenViolationExcluded() {
62+
public void whenInstanceFailedToMatchExactlyOneWithOneOf24ThenViolationExcluded() {
6463
when(customViolationExclusions.isExcluded(any())).thenReturn(false);
6564

6665
checkViolationExcluded(OpenApiViolation.builder()
@@ -70,36 +69,55 @@ public void testWhenInstanceFailedToMatchExactlyOneWithOneOf24ThenViolationExclu
7069
}
7170

7271
@Test
73-
public void testWhen404ResponseWithApiPathNotSpecifiedThenViolationExcluded() {
72+
public void when404ResponseWithApiPathNotSpecifiedThenViolationExcluded() {
7473
when(customViolationExclusions.isExcluded(any())).thenReturn(false);
7574

7675
checkViolationExcluded(OpenApiViolation.builder()
7776
.direction(Direction.RESPONSE)
7877
.rule("validation.request.path.missing")
79-
.responseStatus(Optional.of(404))
78+
.responseStatus(404)
8079
.message("No API path found that matches request '/nothing'")
8180
.build());
8281
}
8382

8483
@Test
85-
public void testWhenRequestWithApiPathNotSpecifiedThenViolationExcluded() {
84+
public void whenRequestWithApiPathNotSpecifiedThenViolationExcluded() {
8685
when(customViolationExclusions.isExcluded(any())).thenReturn(false);
8786

8887
checkViolationExcluded(OpenApiViolation.builder()
8988
.direction(Direction.REQUEST)
9089
.rule("validation.request.path.missing")
91-
.responseStatus(Optional.empty())
90+
.responseStatus(null)
9291
.message("No API path found that matches request '/nothing'")
9392
.build());
9493
}
9594

9695
@Test
97-
public void testWhenRequestViolationsAnd400ThenViolationExcluded() {
96+
public void whenRequestViolationsAnd400ThenViolationExcluded() {
9897
when(customViolationExclusions.isExcluded(any())).thenReturn(false);
9998

10099
checkViolationExcluded(OpenApiViolation.builder()
101100
.direction(Direction.REQUEST)
102-
.responseStatus(Optional.of(400))
101+
.responseStatus(400)
102+
.message("")
103+
.build());
104+
}
105+
106+
@Test
107+
public void when405ResponseCodeWithOperationNotAllowedViolationThenViolationExcluded() {
108+
when(customViolationExclusions.isExcluded(any())).thenReturn(false);
109+
110+
checkViolationExcluded(OpenApiViolation.builder()
111+
.direction(Direction.REQUEST)
112+
.rule("validation.request.operation.notAllowed")
113+
.responseStatus(405)
114+
.message("")
115+
.build());
116+
117+
checkViolationExcluded(OpenApiViolation.builder()
118+
.direction(Direction.RESPONSE)
119+
.rule("validation.request.operation.notAllowed")
120+
.responseStatus(405)
103121
.message("")
104122
.build());
105123
}

openapi-validation-core/src/test/java/com/getyourguide/openapi/validation/core/throttle/RequestBasedValidationReportThrottlerTest.java

+4-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import com.getyourguide.openapi.validation.api.model.RequestMetaData;
99
import java.net.URI;
1010
import java.util.Collections;
11-
import java.util.Optional;
1211
import org.junit.jupiter.api.BeforeEach;
1312
import org.junit.jupiter.api.Test;
1413

@@ -89,10 +88,10 @@ private OpenApiViolation buildViolation(Direction direction, Request.Method meth
8988
.requestMetaData(
9089
new RequestMetaData(method.toString(), URI.create("https://example.com" + path), Collections.emptyMap())
9190
)
92-
.responseStatus(Optional.of(status))
93-
.normalizedPath(Optional.of(path))
94-
.instance(Optional.of(instance))
95-
.schema(Optional.of(schema))
91+
.responseStatus(status)
92+
.normalizedPath(path)
93+
.instance(instance)
94+
.schema(schema)
9695
.build();
9796
}
9897
}

0 commit comments

Comments
 (0)