Skip to content

Commit 9f0f10e

Browse files
authored
Show parameter name in violation log (#19)
1 parent 834a63e commit 9f0f10e

File tree

4 files changed

+110
-3
lines changed

4 files changed

+110
-3
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class OpenApiViolation {
1616
private final Optional<String> operationId;
1717
private final Optional<String> normalizedPath;
1818
private final Optional<String> instance;
19+
private final Optional<String> parameter;
1920
private final Optional<String> schema;
2021
private final Optional<Integer> responseStatus;
2122
private final String message;

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

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ private Map<String, String> buildLoggingContext(OpenApiViolation violation) {
3737
violation.getNormalizedPath().ifPresent(normalizedPath -> context.put("validation.api.path", normalizedPath));
3838
violation.getOperationId().ifPresent(operationId -> context.put("validation.api.operation_id", operationId));
3939
violation.getInstance().ifPresent(instance -> context.put("validation.instance", instance));
40+
violation.getParameter().ifPresent(instance -> context.put("validation.parameter", instance));
4041
return context;
4142
}
4243
}

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

+16-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.getyourguide.openapi.validation.api.model.OpenApiViolation;
1010
import com.getyourguide.openapi.validation.api.model.RequestMetaData;
1111
import com.getyourguide.openapi.validation.core.throttle.ValidationReportThrottler;
12+
import io.swagger.v3.oas.models.parameters.Parameter;
1213
import java.util.Optional;
1314
import lombok.AllArgsConstructor;
1415

@@ -48,13 +49,18 @@ private OpenApiViolation buildOpenApiViolation(
4849
) {
4950
var requestUri = request.getUri().toString();
5051
var requestString = String.format("%s %s", request.getMethod(), requestUri);
51-
var instance = getPointersInstance(message).map(i -> String.format("Instance: %s\n", i)).orElse("");
52+
var pointersInstance = getPointersInstance(message);
53+
var instance = pointersInstance.map(i -> String.format("Instance: %s\n", i)).orElse("");
54+
var parameterName = getParameterName(message);
55+
var parameter = parameterName.map(i -> String.format("Parameter: %s\n", i)).orElse("");
56+
5257
var logMessage = String.format(
53-
"OpenAPI spec validation error [%s]\n%s\nUser Agent: %s\n%s\n%s",
58+
"OpenAPI spec validation error [%s]\n%s\nUser Agent: %s\n%s%s\n%s",
5459
message.getKey(),
5560
requestString,
5661
request.getHeaders().get("User-Agent"),
5762
instance,
63+
parameter,
5864
message
5965
);
6066

@@ -66,7 +72,8 @@ private OpenApiViolation buildOpenApiViolation(
6672
.rule(message.getKey())
6773
.operationId(getOperationId(message))
6874
.normalizedPath(getNormalizedPath(message))
69-
.instance(getPointersInstance(message))
75+
.instance(pointersInstance)
76+
.parameter(parameterName)
7077
.schema(getPointersSchema(message))
7178
.responseStatus(getResponseStatus(message))
7279
.logMessage(logMessage)
@@ -94,6 +101,12 @@ private static Optional<String> getPointersSchema(ValidationReport.Message messa
94101
.map(ValidationReport.MessageContext.Pointers::getSchema);
95102
}
96103

104+
private static Optional<String> getParameterName(ValidationReport.Message message) {
105+
return message.getContext()
106+
.flatMap(ValidationReport.MessageContext::getParameter)
107+
.map(Parameter::getName);
108+
}
109+
97110
private static Optional<String> getOperationId(ValidationReport.Message message) {
98111
return message.getContext()
99112
.flatMap(ValidationReport.MessageContext::getApiOperation)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package com.getyourguide.openapi.validation.core;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.mockito.ArgumentMatchers.any;
5+
import static org.mockito.Mockito.doAnswer;
6+
import static org.mockito.Mockito.mock;
7+
import static org.mockito.Mockito.verify;
8+
import static org.mockito.Mockito.when;
9+
10+
import com.atlassian.oai.validator.report.ValidationReport;
11+
import com.getyourguide.openapi.validation.api.exclusions.ViolationExclusions;
12+
import com.getyourguide.openapi.validation.api.log.ViolationLogger;
13+
import com.getyourguide.openapi.validation.api.metrics.MetricsReporter;
14+
import com.getyourguide.openapi.validation.api.model.Direction;
15+
import com.getyourguide.openapi.validation.api.model.OpenApiViolation;
16+
import com.getyourguide.openapi.validation.api.model.RequestMetaData;
17+
import com.getyourguide.openapi.validation.core.throttle.ValidationReportThrottler;
18+
import io.swagger.v3.oas.models.parameters.Parameter;
19+
import java.net.URI;
20+
import java.util.HashMap;
21+
import java.util.List;
22+
import java.util.Optional;
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.Test;
25+
import org.mockito.ArgumentCaptor;
26+
27+
class ValidationReportHandlerTest {
28+
private ValidationReportThrottler throttleHelper;
29+
private ViolationLogger logger;
30+
31+
private ValidationReportHandler validationReportHandler;
32+
33+
@BeforeEach
34+
public void setUp() {
35+
throttleHelper = mock();
36+
logger = mock();
37+
MetricsReporter metrics = mock();
38+
ViolationExclusions violationExclusions = mock();
39+
40+
validationReportHandler = new ValidationReportHandler(throttleHelper, logger, metrics, violationExclusions);
41+
}
42+
43+
@Test
44+
public void testWhenParameterNameIsPresentThenItShouldAddItToTheMessage() {
45+
mockNoThrottling();
46+
var request = mockRequestMetaData();
47+
var validationReport = mockValidationReport("parameterName");
48+
49+
validationReportHandler.handleValidationReport(request, Direction.REQUEST, null, validationReport);
50+
51+
var argumentCaptor = ArgumentCaptor.forClass(OpenApiViolation.class);
52+
verify(logger).log(argumentCaptor.capture());
53+
var openApiViolation = argumentCaptor.getValue();
54+
assertEquals(Optional.of("parameterName"), openApiViolation.getParameter());
55+
assertEquals(
56+
String.join("\n",
57+
"OpenAPI spec validation error [key]",
58+
"GET https://api.example.com/index",
59+
"User Agent: null",
60+
"Parameter: parameterName",
61+
"",
62+
"Violation message (toString)"),
63+
openApiViolation.getLogMessage());
64+
}
65+
66+
private static RequestMetaData mockRequestMetaData() {
67+
var request = new RequestMetaData("GET", URI.create("https://api.example.com/index"), new HashMap<>());
68+
return request;
69+
}
70+
71+
private static ValidationReport mockValidationReport(String parameterName) {
72+
var validationReport = mock(ValidationReport.class);
73+
var message = mock(ValidationReport.Message.class);
74+
when(message.getKey()).thenReturn("key");
75+
when(message.getMessage()).thenReturn("Violation message");
76+
when(message.toString()).thenReturn("Violation message (toString)");
77+
var context = mock(ValidationReport.MessageContext.class);
78+
var parameter = mock(Parameter.class);
79+
when(parameter.getName()).thenReturn(parameterName);
80+
when(context.getParameter()).thenReturn(Optional.of(parameter));
81+
when(message.getContext()).thenReturn(Optional.of(context));
82+
when(validationReport.getMessages()).thenReturn(List.of(message));
83+
return validationReport;
84+
}
85+
86+
private void mockNoThrottling() {
87+
doAnswer(invocation -> {
88+
((Runnable) invocation.getArguments()[1]).run();
89+
return null;
90+
}).when(throttleHelper).throttle(any(), any());
91+
}
92+
}

0 commit comments

Comments
 (0)