Skip to content

Commit fd6c672

Browse files
authored
Merge pull request #202 from ruromero/custom-validation
feat: delegate client errors to each provider
2 parents 976b898 + 5e6d1a1 commit fd6c672

File tree

10 files changed

+155
-155
lines changed

10 files changed

+155
-155
lines changed

src/main/java/com/redhat/exhort/integration/Constants.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,6 @@ private Constants() {}
7777
public static final String DEFAULT_ACCEPT_MEDIA_TYPE = MediaType.APPLICATION_JSON;
7878
public static final boolean DEFAULT_VERBOSE_MODE = false;
7979

80-
public static final List<String> PKG_MANAGERS =
81-
Collections.unmodifiableList(
82-
new ArrayList<>() {
83-
{
84-
add(MAVEN_PKG_MANAGER);
85-
add(NPM_PKG_MANAGER);
86-
add(PYPI_PKG_MANAGER);
87-
add(GOLANG_PKG_MANAGER);
88-
}
89-
});
90-
9180
public static final List<String> PROVIDERS =
9281
Collections.unmodifiableList(
9382
new ArrayList<>() {

src/main/java/com/redhat/exhort/integration/backend/ExhortIntegration.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public class ExhortIntegration extends EndpointRouteBuilder {
7070

7171
@Override
7272
public void configure() {
73+
7374
// fmt:off
7475
getContext().getRegistry().bind(MicrometerConstants.METRICS_REGISTRY_NAME, registry);
7576
getContext().addRoutePolicyFactory(new MicrometerRoutePolicyFactory());
@@ -78,7 +79,8 @@ public void configure() {
7879
.clientRequestValidation(true);
7980

8081
errorHandler(deadLetterChannel("direct:processInternalError"));
81-
82+
onException(Exception.class)
83+
.log("foo?level=ERROR");
8284
onException(IllegalArgumentException.class)
8385
.routeId("onExhortIllegalArgumentException")
8486
.useOriginalMessage()
@@ -182,8 +184,7 @@ public void configure() {
182184
.to(seda("processFailedRequests"))
183185
.setBody().simple("${exception.message}")
184186
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(Status.INTERNAL_SERVER_ERROR.getStatusCode()))
185-
.setHeader(Exchange.CONTENT_TYPE, constant(MediaType.TEXT_PLAIN))
186-
;
187+
.setHeader(Exchange.CONTENT_TYPE, constant(MediaType.TEXT_PLAIN));
187188
//fmt:on
188189
}
189190

src/main/java/com/redhat/exhort/integration/backend/sbom/SbomParser.java

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,41 +19,14 @@
1919
package com.redhat.exhort.integration.backend.sbom;
2020

2121
import java.io.InputStream;
22-
import java.util.HashSet;
23-
import java.util.Set;
24-
import java.util.function.Predicate;
2522

26-
import com.redhat.exhort.integration.Constants;
2723
import com.redhat.exhort.model.DependencyTree;
2824

2925
public abstract class SbomParser {
3026

3127
public DependencyTree parse(InputStream input) {
32-
var tree = buildTree(input);
33-
validate(tree);
34-
return tree;
28+
return buildTree(input);
3529
}
3630

3731
protected abstract DependencyTree buildTree(InputStream input);
38-
39-
protected void validate(DependencyTree tree) {
40-
Set<String> types = new HashSet<>();
41-
tree.dependencies()
42-
.values()
43-
.forEach(
44-
d -> {
45-
types.add(d.ref().purl().getType());
46-
d.transitive().forEach(t -> types.add(t.purl().getType()));
47-
});
48-
49-
var invalidTypes =
50-
types.stream().filter(Predicate.not(Constants.PKG_MANAGERS::contains)).toList();
51-
if (!invalidTypes.isEmpty()) {
52-
throw new IllegalArgumentException("Unsupported package types received: " + invalidTypes);
53-
}
54-
if (types.size() > 1) {
55-
throw new IllegalArgumentException(
56-
"It is not supported to submit mixed Package Manager types. Found: " + types);
57-
}
58-
}
5932
}

src/main/java/com/redhat/exhort/integration/providers/ProviderResponseHandler.java

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.apache.camel.Body;
3232
import org.apache.camel.Exchange;
3333
import org.apache.camel.ExchangeProperty;
34+
import org.apache.camel.RuntimeCamelException;
3435
import org.apache.camel.http.base.HttpOperationFailedException;
3536
import org.slf4j.Logger;
3637
import org.slf4j.LoggerFactory;
@@ -89,23 +90,26 @@ public void processResponseError(Exchange exchange) {
8990
ProviderStatus status = new ProviderStatus().ok(false).name(getProviderName());
9091
Exception exception = (Exception) exchange.getProperty(Exchange.EXCEPTION_CAUGHT);
9192
Throwable cause = exception.getCause();
92-
if (cause != null) {
93-
if (cause instanceof HttpOperationFailedException) {
94-
HttpOperationFailedException httpException = (HttpOperationFailedException) cause;
95-
String message = prettifyHttpError(httpException);
96-
status.message(message).code(httpException.getStatusCode());
97-
LOGGER.warn("Unable to process request: {}", message, cause);
98-
} else {
99-
status
100-
.message(cause.getMessage())
101-
.code(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
102-
LOGGER.warn("Unable to process request to: {}", getProviderName(), cause);
103-
}
93+
94+
while (cause instanceof RuntimeCamelException && cause != null) {
95+
cause = cause.getCause();
96+
}
97+
if (cause == null) {
98+
cause = exception;
99+
}
100+
if (cause instanceof HttpOperationFailedException) {
101+
HttpOperationFailedException httpException = (HttpOperationFailedException) cause;
102+
String message = prettifyHttpError(httpException);
103+
status.message(message).code(httpException.getStatusCode());
104+
LOGGER.warn("Unable to process request: {}", message, cause);
105+
} else if (cause instanceof IllegalArgumentException) {
106+
status.message(cause.getMessage()).code(422);
107+
LOGGER.warn("Unable to process request to: {}", getProviderName(), exception);
104108
} else {
105109
status
106-
.message(exception.getMessage())
110+
.message(cause.getMessage())
107111
.code(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
108-
LOGGER.warn("Unable to process request to: {}", getProviderName(), exception);
112+
LOGGER.warn("Unable to process request to: {}", getProviderName(), cause);
109113
}
110114
ProviderReport report = new ProviderReport().status(status).sources(Collections.emptyMap());
111115
monitoringProcessor.processProviderError(exchange, exception, getProviderName());

src/main/java/com/redhat/exhort/integration/providers/ossindex/OssIndexIntegration.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.apache.camel.Exchange;
2626
import org.apache.camel.builder.AggregationStrategies;
2727
import org.apache.camel.builder.endpoint.EndpointRouteBuilder;
28+
import org.apache.camel.http.base.HttpOperationFailedException;
2829
import org.eclipse.microprofile.config.inject.ConfigProperty;
2930

3031
import com.redhat.exhort.integration.Constants;
@@ -59,15 +60,16 @@ public void configure() {
5960
.choice()
6061
.when(method(OssIndexRequestBuilder.class, "isEmpty"))
6162
.setBody(method(OssIndexResponseHandler.class, "emptyResponse"))
63+
.transform().method(OssIndexResponseHandler.class, "buildReport")
6264
.endChoice()
6365
.otherwise()
64-
.to(direct("ossSplitReq"))
65-
.end()
66-
.transform().method(OssIndexResponseHandler.class, "buildReport");
66+
.to(direct("ossSplitReq"));
6767

6868
from(direct("ossSplitReq"))
6969
.routeId("ossSplitReq")
70+
.doTry()
7071
.split(body(), AggregationStrategies.bean(OssIndexResponseHandler.class, "aggregateSplit"))
72+
.stopOnException()
7173
.parallelProcessing()
7274
.transform().method(OssIndexRequestBuilder.class, "buildRequest")
7375
.process(this::processComponentRequest)
@@ -78,8 +80,11 @@ public void configure() {
7880
.end()
7981
.to(vertxHttp("{{api.ossindex.host}}"))
8082
.transform(method(OssIndexResponseHandler.class, "responseToIssues"))
81-
.onFallback()
82-
.process(responseHandler::processResponseError);
83+
.end()
84+
.transform().method(OssIndexResponseHandler.class, "buildReport")
85+
.endDoTry()
86+
.doCatch(HttpOperationFailedException.class)
87+
.process(responseHandler::processResponseError);
8388

8489
from(direct("ossValidateCredentials"))
8590
.routeId("ossValidateCredentials")

src/main/java/com/redhat/exhort/integration/providers/snyk/SnykIntegration.java

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -52,37 +52,38 @@ public void configure() {
5252

5353
// fmt:off
5454
from(direct("snykDepGraph"))
55-
.routeId("snykDepGraph")
56-
.process(this::setAuthToken)
55+
.routeId("snykDepGraph")
56+
.process(this::setAuthToken)
57+
.circuitBreaker()
58+
.faultToleranceConfiguration()
59+
.timeoutEnabled(true)
60+
.timeoutDuration(timeout)
61+
.end()
62+
.process(SnykRequestBuilder::validate)
5763
.transform().method(SnykRequestBuilder.class, "fromDiGraph")
5864
.process(this::processDepGraphRequest)
59-
.circuitBreaker()
60-
.faultToleranceConfiguration()
61-
.timeoutEnabled(true)
62-
.timeoutDuration(timeout)
63-
.end()
6465
.to(direct("snykRequest"))
65-
.onFallback()
66-
.process(responseHandler::processResponseError);
66+
.onFallback()
67+
.process(responseHandler::processResponseError);
6768

6869
from(direct("snykRequest"))
69-
.routeId("snykRequest")
70-
.to(vertxHttp("{{api.snyk.host}}"))
71-
.transform().method(SnykResponseHandler.class, "responseToIssues")
72-
.transform().method(SnykResponseHandler.class, "buildReport");
70+
.routeId("snykRequest")
71+
.to(vertxHttp("{{api.snyk.host}}"))
72+
.transform().method(SnykResponseHandler.class, "responseToIssues")
73+
.transform().method(SnykResponseHandler.class, "buildReport");
7374

7475
from(direct("snykValidateToken"))
75-
.routeId("snykValidateToken")
76-
.process(this::processTokenRequest)
77-
.circuitBreaker()
78-
.faultToleranceConfiguration()
79-
.timeoutEnabled(true)
80-
.timeoutDuration(timeout)
81-
.end()
82-
.to(vertxHttp("{{api.snyk.host}}"))
83-
.setBody(constant("Token validated successfully"))
84-
.onFallback()
85-
.process(responseHandler::processTokenFallBack);
76+
.routeId("snykValidateToken")
77+
.process(this::processTokenRequest)
78+
.circuitBreaker()
79+
.faultToleranceConfiguration()
80+
.timeoutEnabled(true)
81+
.timeoutDuration(timeout)
82+
.end()
83+
.to(vertxHttp("{{api.snyk.host}}"))
84+
.setBody(constant("Token validated successfully"))
85+
.onFallback()
86+
.process(responseHandler::processTokenFallBack);
8687
// fmt:on
8788
}
8889

src/main/java/com/redhat/exhort/integration/providers/snyk/SnykRequestBuilder.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,14 @@
1818

1919
package com.redhat.exhort.integration.providers.snyk;
2020

21+
import java.util.ArrayList;
2122
import java.util.Collections;
23+
import java.util.HashSet;
24+
import java.util.List;
2225
import java.util.Set;
26+
import java.util.function.Predicate;
27+
28+
import org.apache.camel.Exchange;
2329

2430
import com.fasterxml.jackson.core.JsonProcessingException;
2531
import com.fasterxml.jackson.databind.JsonNode;
@@ -35,6 +41,17 @@
3541
@RegisterForReflection
3642
public class SnykRequestBuilder {
3743

44+
public static final List<String> SUPPORTED_PKG_MANAGERS =
45+
Collections.unmodifiableList(
46+
new ArrayList<>() {
47+
{
48+
add(Constants.MAVEN_PKG_MANAGER);
49+
add(Constants.NPM_PKG_MANAGER);
50+
add(Constants.PYPI_PKG_MANAGER);
51+
add(Constants.GOLANG_PKG_MANAGER);
52+
}
53+
});
54+
3855
private ObjectMapper mapper = ObjectMapperProducer.newInstance();
3956

4057
public String fromDiGraph(DependencyTree req) throws JsonProcessingException {
@@ -50,6 +67,28 @@ public String fromDiGraph(DependencyTree req) throws JsonProcessingException {
5067
return mapper.writeValueAsString(root);
5168
}
5269

70+
public static void validate(Exchange exchange) {
71+
var tree = exchange.getIn().getBody(DependencyTree.class);
72+
Set<String> types = new HashSet<>();
73+
tree.dependencies()
74+
.values()
75+
.forEach(
76+
d -> {
77+
types.add(d.ref().purl().getType());
78+
d.transitive().forEach(t -> types.add(t.purl().getType()));
79+
});
80+
81+
var invalidTypes =
82+
types.stream().filter(Predicate.not(SUPPORTED_PKG_MANAGERS::contains)).toList();
83+
if (!invalidTypes.isEmpty()) {
84+
throw new IllegalArgumentException("Unsupported package types received: " + invalidTypes);
85+
}
86+
if (types.size() > 1) {
87+
throw new IllegalArgumentException(
88+
"It is not supported to submit mixed Package Manager types. Found: " + types);
89+
}
90+
}
91+
5392
private JsonNode addPackages(ObjectNode depGraph, DependencyTree tree, PackageRef root) {
5493
var allDeps = tree.getAll();
5594
var rootNode = createNode(root, allDeps);

src/test/java/com/redhat/exhort/integration/AbstractAnalysisTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import com.google.common.base.Charsets;
4949
import com.redhat.exhort.extensions.InjectWireMock;
5050
import com.redhat.exhort.extensions.WiremockV3Extension;
51+
import com.redhat.exhort.integration.providers.snyk.SnykRequestBuilder;
5152

5253
import io.quarkus.test.common.QuarkusTestResource;
5354
import io.quarkus.test.junit.QuarkusTest;
@@ -271,7 +272,7 @@ protected void stubSnykRequests() {
271272
"{\"code\": 401, \"error\": \"Not authorised\""
272273
+ ", \"message\": \"Not authorised\"}")));
273274
// Other requests
274-
Constants.PKG_MANAGERS.forEach(this::stubSnykEmptyRequest);
275+
SnykRequestBuilder.SUPPORTED_PKG_MANAGERS.forEach(this::stubSnykEmptyRequest);
275276
// Dependency request
276277
server.stubFor(
277278
post(Constants.SNYK_DEP_GRAPH_API_PATH)

0 commit comments

Comments
 (0)