Skip to content

Commit a00eb81

Browse files
author
Vincent Giraud
authored
feat(worker): Can accept text/plain response content type (#149)
* feat(worker): Can accept text/plain response mime type if not application/json
1 parent aef4a23 commit a00eb81

File tree

3 files changed

+104
-12
lines changed

3 files changed

+104
-12
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
.idea/
22
.vscode/
3+
.classpath
4+
.project
5+
.settings/
36
target/*
47
coverage*
58
dist/

src/main/java/io/zeebe/http/HttpJobHandler.java

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.net.http.HttpClient;
2424
import java.net.http.HttpRequest;
2525
import java.net.http.HttpResponse;
26+
import java.net.http.HttpHeaders;
2627
import java.time.Duration;
2728
import java.util.Map;
2829
import java.util.Optional;
@@ -51,6 +52,8 @@ public class HttpJobHandler implements JobHandler {
5152
private static final String PARAMETER_METHOD = "method";
5253
private static final String PARAMETER_BODY = "body";
5354
private static final String PARAMETER_AUTHORIZATION = "authorization";
55+
private static final String PARAMETER_CONTENT_TYPE = "contentType";
56+
private static final String PARAMETER_ACCEPT = "accept";
5457
private static final String PARAMETER_HTTP_STATUS_CODE_FAILURE = "statusCodeFailure";
5558
private static final String PARAMETER_HTTP_STATUS_CODE_COMPLETION = "statusCodeCompletion";
5659
private static final String PARAMETER_HTTP_ERROR_CODE_PATH = "errorCodePath";
@@ -75,7 +78,7 @@ public void handle(JobClient jobClient, ActivatedJob job) throws IOException, In
7578
if (hasFailingStatusCode(response, configurationMaps)) {
7679
processFailure(configurationMaps, jobClient, job, response);
7780
} else if (hasCompletingStatusCode(response, configurationMaps)) {
78-
final Map<String, Object> result = processResponse(job, response);
81+
final Map<String, Object> result = processResponse(job, response, request);
7982
jobClient.newCompleteCommand(job.getKey()).variables(result).send().join();
8083
} else {
8184
// do nothing
@@ -128,11 +131,17 @@ private HttpRequest buildRequest(ConfigurationMaps configurationMaps) {
128131
HttpRequest.newBuilder()
129132
.uri(URI.create(url))
130133
.timeout(CONNECTION_TIMEOUT)
131-
.header("Content-Type", "application/json")
132-
.header("Accept", "application/json")
133134
.method(method, bodyPublisher);
134135

135-
getAuthentication(configurationMaps).ifPresent(auth -> builder.header("Authorization", auth));
136+
getAuthorization(configurationMaps).ifPresent(auth -> builder.header("Authorization", auth));
137+
138+
// if no accept or content type header then default to json
139+
getContentType(configurationMaps)
140+
.ifPresentOrElse(contentType -> builder.header("Content-Type", contentType),
141+
() -> builder.header("Content-Type", "application/json"));
142+
getAccept(configurationMaps)
143+
.ifPresentOrElse(accept -> builder.header("Accept", accept),
144+
() -> builder.header("Accept", "application/json"));
136145

137146
return builder.build();
138147
}
@@ -144,12 +153,24 @@ private String getUrl(ConfigurationMaps configMaps) {
144153
.orElseThrow(() -> new RuntimeException("Missing required parameter: " + PARAMETER_URL));
145154
}
146155

147-
private Optional<String> getAuthentication(ConfigurationMaps configMaps) {
156+
private Optional<String> getAuthorization(ConfigurationMaps configMaps) {
148157
return configMaps
149158
.getString(PARAMETER_AUTHORIZATION)
150159
.map(auth -> placeholderProcessor.process(auth, configMaps.getConfig()));
151160
}
152161

162+
private Optional<String> getContentType(ConfigurationMaps configMaps) {
163+
return configMaps
164+
.getString(PARAMETER_CONTENT_TYPE)
165+
.map(contentType -> placeholderProcessor.process(contentType, configMaps.getConfig()));
166+
}
167+
168+
private Optional<String> getAccept(ConfigurationMaps configMaps) {
169+
return configMaps
170+
.getString(PARAMETER_ACCEPT)
171+
.map(accept -> placeholderProcessor.process(accept, configMaps.getConfig()));
172+
}
173+
153174
private String getMethod(ConfigurationMaps configMaps) {
154175
return configMaps
155176
.getString(PARAMETER_METHOD)
@@ -201,20 +222,38 @@ private boolean checkIfCodeMatches(String statusCode, String matchCodePattern) {
201222
|| (statusCode.startsWith("5") && matchCodePattern.contains("5xx"));
202223
}
203224

204-
private Map<String, Object> processResponse(ActivatedJob job, HttpResponse<String> response) {
225+
private Map<String, Object> processResponse(ActivatedJob job,
226+
HttpResponse<String> response, HttpRequest request) {
205227
final Map<String, Object> result = new java.util.HashMap<>();
206-
207228
int statusCode = response.statusCode();
208229
result.put("statusCode", statusCode);
209-
210-
Optional.ofNullable(response.body())
211-
.filter(body -> !body.isEmpty())
212-
.map(this::bodyToObject)
230+
Optional<String> respBody = Optional.ofNullable(response.body())
231+
.filter(body -> !body.isEmpty());
232+
String acceptValue = request.headers().firstValue("Accept").orElse(null);
233+
// If accepting plain text
234+
if (hasContentTypeHeader(response.headers(), "text/plain") &&
235+
("text/plain".equals(acceptValue))) {
236+
respBody.ifPresent(body -> result.put("body", body));
237+
} else {
238+
// Assuming json by default
239+
respBody.map(this::bodyToObject)
213240
.ifPresent(body -> result.put("body", body));
214-
241+
}
215242
return result;
216243
}
217244

245+
private boolean hasContentTypeHeader(HttpHeaders headers, String contentTypeHeader) {
246+
boolean hasContentTypeHeader = false;
247+
try {
248+
hasContentTypeHeader = Optional.ofNullable(headers.firstValue("Content-Type"))
249+
.filter(contentType -> contentType.get().contains(contentTypeHeader))
250+
.isPresent();
251+
}catch(Exception e) {
252+
System.out.println(e.toString());
253+
}
254+
return hasContentTypeHeader;
255+
}
256+
218257
private Object bodyToObject(String body) {
219258
try {
220259
return objectMapper.readValue(body, Object.class);

src/test/java/io/zeebe/http/ProcessIntegrationTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,56 @@ public void testGetRequest() {
103103
WIRE_MOCK_RULE.verify(getRequestedFor(urlEqualTo("/api")));
104104
}
105105

106+
@Test
107+
public void testGetAcceptPlainTextResponse() {
108+
109+
stubFor(
110+
get(urlEqualTo("/api"))
111+
.willReturn(
112+
aResponse().withHeader("Content-Type", "text/plain")
113+
.withBody("This is text")));
114+
115+
final var processInstance =
116+
createInstance(
117+
serviceTask ->
118+
serviceTask
119+
.zeebeTaskHeader("url", WIRE_MOCK_RULE.baseUrl() + "/api")
120+
.zeebeTaskHeader("method", "GET")
121+
.zeebeTaskHeader("accept", "text/plain"),
122+
Collections.emptyMap());
123+
124+
ZeebeTestRule.assertThat(processInstance)
125+
.isEnded()
126+
.hasVariable("statusCode", 200)
127+
.hasVariable("body", "This is text");
128+
129+
WIRE_MOCK_RULE.verify(getRequestedFor(urlEqualTo("/api"))
130+
.withHeader("Accept", equalTo("text/plain")));
131+
}
132+
133+
@Test
134+
public void failsIfDoesNotAcceptResponseType() {
135+
stubFor(
136+
post(urlEqualTo("/api"))
137+
.willReturn(aResponse().withHeader("Content-Type", "text/plain")
138+
.withBody("This is text")));
139+
140+
final var processInstance =
141+
createInstance(
142+
serviceTask ->
143+
serviceTask
144+
.zeebeTaskHeader("url", WIRE_MOCK_RULE.baseUrl() + "/api")
145+
.zeebeTaskHeader("method", "POST"));
146+
147+
final var recorderJob =
148+
RecordingExporter.jobRecords(JobIntent.FAILED)
149+
.withProcessInstanceKey(processInstance.getProcessInstanceKey())
150+
.getFirst();
151+
152+
assertThat(recorderJob.getValue().getErrorMessage()).isNotNull()
153+
.contains("Failed to deserialize response body from JSON");
154+
}
155+
106156
@Test
107157
public void testPostRequest() {
108158

0 commit comments

Comments
 (0)