Skip to content

Commit f32be93

Browse files
nsevNils Stenholmefonsell
authored
Fix webflux error handling (#438)
* Minor change to use reactive error handling * Fix import order * Update nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java Co-authored-by: Edvard Fonsell <edvard.fonsell@nitor.com> * Update nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java Co-authored-by: Edvard Fonsell <edvard.fonsell@nitor.com> * Change http status magic numbers to constants * Add fix to changelog * Add smoke test for checking 404 code * Refactor netty smoke test a bit and fix indentation * Fix code formatting Co-authored-by: Nils Stenholm <nils.stenholm@nitor.com> Co-authored-by: Edvard Fonsell <edvard.fonsell@nitor.com>
1 parent 2702820 commit f32be93

File tree

4 files changed

+42
-9
lines changed

4 files changed

+42
-9
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
**Details**
77
- `nflow-engine`
88
- Add `disabled` state (type `manual`) to `CronWorkflow` to support disabling the work. By default there is no state method for the `disabled` state, but it can added in your workflow definition that extends `CronWorkflow` to execute custom logic when the workflow enters the `disabled` state.
9+
- `nflow-rest-api-common` and `nflow-rest-api-spring-web`
10+
- Fix exception to HTTP status conversion issue when using Netty
911

1012
## 7.2.3 (2021-02-22)
1113

nflow-netty/src/test/java/io/nflow/netty/StartNflowTest.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package io.nflow.netty;
22

33
import static io.nflow.rest.v1.ResourcePaths.NFLOW_WORKFLOW_DEFINITION_PATH;
4+
import static io.nflow.rest.v1.ResourcePaths.NFLOW_WORKFLOW_INSTANCE_PATH;
45
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
import static org.junit.jupiter.api.Assertions.assertFalse;
57
import static org.junit.jupiter.api.Assertions.assertNotNull;
68
import static org.junit.jupiter.api.Assertions.assertTrue;
9+
import static org.springframework.http.HttpStatus.NOT_FOUND;
710
import static org.springframework.http.HttpStatus.OK;
811

912
import java.util.HashMap;
@@ -20,8 +23,12 @@
2023

2124
public class StartNflowTest {
2225

26+
private static final String DEFAULT_LOCALHOST_SERVER_PORT = "7500";
27+
private static final String DEFAULT_LOCALHOST_SERVER_ADDRESS = "http://localhost:" + DEFAULT_LOCALHOST_SERVER_PORT;
28+
2329
public class TestApplicationListener implements ApplicationListener<ApplicationContextEvent> {
2430
public ApplicationContextEvent applicationContextEvent;
31+
2532
@Override
2633
public void onApplicationEvent(ApplicationContextEvent event) {
2734
applicationContextEvent = event;
@@ -44,22 +51,35 @@ public void startNflowNetty() throws Exception {
4451
ApplicationContext ctx = startNflow.startNetty(7500, "local", "", properties);
4552

4653
assertNotNull(testListener.applicationContextEvent);
47-
assertEquals("7500", ctx.getEnvironment().getProperty("port"));
54+
assertEquals(DEFAULT_LOCALHOST_SERVER_PORT, ctx.getEnvironment().getProperty("port"));
4855
assertEquals("local", ctx.getEnvironment().getProperty("env"));
4956
assertEquals("externallyDefinedExecutorGroup", ctx.getEnvironment().getProperty("nflow.executor.group"));
5057
assertEquals("true", ctx.getEnvironment().getProperty("nflow.db.create_on_startup"));
5158
assertEquals("false", ctx.getEnvironment().getProperty("nflow.autostart"));
5259
assertEquals("true", ctx.getEnvironment().getProperty("nflow.autoinit"));
5360

5461
smokeTestRestApi(restApiPrefix);
62+
smokeTestRestApiErrorHandling(restApiPrefix);
5563
}
5664

5765
private void smokeTestRestApi(String restApiPrefix) {
58-
WebClient client = WebClient.builder().baseUrl("http://localhost:7500").build();
59-
ClientResponse response = client.get().uri(restApiPrefix + NFLOW_WORKFLOW_DEFINITION_PATH).exchange().block();
66+
ClientResponse response = getFromDefaultServer(restApiPrefix + NFLOW_WORKFLOW_DEFINITION_PATH);
6067
assertEquals(OK, response.statusCode());
6168
JsonNode responseBody = response.bodyToMono(JsonNode.class).block();
6269
assertTrue(responseBody.isArray());
6370
}
6471

72+
// Smoke test for io.nflow.rest.v1.springweb.SpringWebResource#handleExceptions
73+
private void smokeTestRestApiErrorHandling(String restApiPrefix) {
74+
ClientResponse response = getFromDefaultServer(restApiPrefix + NFLOW_WORKFLOW_INSTANCE_PATH + "/id/0213132");
75+
assertEquals(NOT_FOUND, response.statusCode());
76+
JsonNode responseBody = response.bodyToMono(JsonNode.class).block();
77+
assertNotNull(responseBody);
78+
assertFalse(responseBody.isEmpty());
79+
}
80+
81+
private ClientResponse getFromDefaultServer(String url) {
82+
WebClient client = WebClient.builder().baseUrl(DEFAULT_LOCALHOST_SERVER_ADDRESS).build();
83+
return client.get().uri(url).exchange().block();
84+
}
6585
}

nflow-rest-api-common/src/main/java/io/nflow/rest/v1/ResourceBase.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package io.nflow.rest.v1;
22

33
import static io.nflow.engine.workflow.instance.WorkflowInstanceAction.WorkflowActionType.externalChange;
4+
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
5+
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
6+
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
47
import static java.util.Collections.sort;
58
import static java.util.Collections.unmodifiableMap;
69
import static java.util.stream.Collectors.toCollection;
@@ -183,15 +186,21 @@ public ListWorkflowInstanceResponse fetchWorkflowInstance(final long id, final S
183186
return listWorkflowConverter.convert(instance, includes);
184187
}
185188

189+
protected int resolveExceptionHttpStatus(Throwable t) {
190+
if (t instanceof IllegalArgumentException) {
191+
return HTTP_BAD_REQUEST;
192+
} else if (t instanceof NflowNotFoundException) {
193+
return HTTP_NOT_FOUND;
194+
}
195+
return HTTP_INTERNAL_ERROR;
196+
}
197+
186198
protected <T> T handleExceptions(Supplier<T> response, BiFunction<Integer, ErrorResponse, T> error) {
187199
try {
188200
return response.get();
189-
} catch (IllegalArgumentException e) {
190-
return error.apply(400, new ErrorResponse(e.getMessage()));
191-
} catch (NflowNotFoundException e) {
192-
return error.apply(404, new ErrorResponse(e.getMessage()));
193201
} catch (Throwable t) {
194-
return error.apply(500, new ErrorResponse(t.getMessage()));
202+
int code = resolveExceptionHttpStatus(t);
203+
return error.apply(code, new ErrorResponse(t.getMessage()));
195204
}
196205
}
197206
}

nflow-rest-api-spring-web/src/main/java/io/nflow/rest/v1/springweb/SpringWebResource.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ protected Mono<ResponseEntity<?>> wrapBlocking(Callable<ResponseEntity<?>> calla
2626
}
2727

2828
protected Mono<ResponseEntity<?>> handleExceptions(Supplier<Mono<ResponseEntity<?>>> response) {
29-
return handleExceptions(response::get, this::toErrorResponse);
29+
return Mono.just(response)
30+
.flatMap(Supplier::get)
31+
.onErrorResume(ex -> toErrorResponse(resolveExceptionHttpStatus(ex), new ErrorResponse(ex.getMessage())));
3032
}
3133

3234
private Mono<ResponseEntity<?>> toErrorResponse(int statusCode, ErrorResponse body) {

0 commit comments

Comments
 (0)