Skip to content

Commit 523cf67

Browse files
committed
#121 Use URI Instead of String
This has theadvantage that URLs are validated in place and we can normalize theresulting URL to remove double slashes from path. Signed-off-by: Sven Strittmatter <[email protected]>
1 parent 574815b commit 523cf67

File tree

3 files changed

+77
-17
lines changed

3 files changed

+77
-17
lines changed

src/main/java/io/securecodebox/persistence/defectdojo/config/ClientConfig.java

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
@ToString
1818
@EqualsAndHashCode
1919
public final class ClientConfig {
20+
/**
21+
* Path prefix for the Defectdojo REST API
22+
*/
23+
public static final String API_PREFIX = "/api/v2/";
2024
/**
2125
* Default for {@link #maxPageCountForGets}
2226
*/

src/main/java/io/securecodebox/persistence/defectdojo/service/GenericDefectDojoService.java

+17-17
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import com.fasterxml.jackson.databind.cfg.CoercionAction;
1111
import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
1212
import io.securecodebox.persistence.defectdojo.config.ClientConfig;
13-
import io.securecodebox.persistence.defectdojo.exception.PersistenceException;
1413
import io.securecodebox.persistence.defectdojo.exception.TooManyResponsesException;
1514
import io.securecodebox.persistence.defectdojo.http.AuthHeaderFactory;
1615
import io.securecodebox.persistence.defectdojo.http.Foo;
@@ -34,7 +33,6 @@
3433
import org.springframework.web.util.UriComponentsBuilder;
3534

3635
import java.net.URI;
37-
import java.net.URISyntaxException;
3836
import java.util.*;
3937

4038
/**
@@ -44,7 +42,6 @@
4442
*/
4543
@Slf4j
4644
abstract class GenericDefectDojoService<T extends Model> implements DefectDojoService<T> {
47-
private static final String API_PREFIX = "/api/v2/";
4845
private static final long DEFECT_DOJO_OBJET_LIMIT = 100L;
4946
protected ClientConfig clientConfig;
5047

@@ -81,7 +78,7 @@ public final T get(long id) {
8178
final var restTemplate = this.getRestTemplate();
8279
final HttpEntity<String> payload = new HttpEntity<>(getDefectDojoAuthorizationHeaders());
8380

84-
final var url = createBaseUrl() + id;
81+
final var url = createBaseUrl().resolve(String.valueOf(id));
8582
log.debug("Requesting URL: {}", url);
8683
final ResponseEntity<T> response = restTemplate.exchange(
8784
url,
@@ -151,20 +148,22 @@ public final void delete(long id) {
151148
final var restTemplate = this.getRestTemplate();
152149
final HttpEntity<String> payload = new HttpEntity<>(getDefectDojoAuthorizationHeaders());
153150

154-
restTemplate.exchange(createBaseUrl() + id + "/", HttpMethod.DELETE, payload, String.class);
151+
final var url = createBaseUrl().resolve(id + "/");
152+
restTemplate.exchange(url, HttpMethod.DELETE, payload, String.class);
155153
}
156154

157155
@Override
158156
public final T update(@NonNull T object, long id) {
159157
final var restTemplate = this.getRestTemplate();
160158
final HttpEntity<T> payload = new HttpEntity<>(object, getDefectDojoAuthorizationHeaders());
161-
final ResponseEntity<T> response = restTemplate.exchange(createBaseUrl() + id + "/", HttpMethod.PUT, payload, getModelClass());
159+
final var url = createBaseUrl().resolve(id + "/");
160+
final ResponseEntity<T> response = restTemplate.exchange(url, HttpMethod.PUT, payload, getModelClass());
162161

163162
return response.getBody();
164163
}
165-
164+
166165
/**
167-
* Get the URL path for the REST endpoint relative to {@link #API_PREFIX}
166+
* Get the URL path for the REST endpoint relative to {@link ClientConfig#API_PREFIX}
168167
*
169168
* @return not {@code null}, not empty
170169
*/
@@ -185,8 +184,13 @@ public final T update(@NonNull T object, long id) {
185184
*/
186185
protected abstract PaginatedResult<T> deserializeList(@NonNull String response);
187186

188-
private String createBaseUrl() {
189-
return this.clientConfig.getUrl() + API_PREFIX + getUrlPath() + "/";
187+
final URI createBaseUrl() {
188+
final var buffer = clientConfig.getUrl() +
189+
ClientConfig.API_PREFIX +
190+
getUrlPath() +
191+
'/';
192+
193+
return URI.create(buffer).normalize();
190194
}
191195

192196
/**
@@ -226,13 +230,9 @@ protected PaginatedResult<T> internalSearch(Map<String, Object> queryParams, lon
226230

227231
final var url = createBaseUrl();
228232
final UriComponentsBuilder builder;
229-
try {
230-
builder = UriComponentsBuilder
231-
.fromUri(new URI(url))
232-
.queryParams(multiValueMap);
233-
} catch (URISyntaxException e) {
234-
throw new PersistenceException("Bad URL given: " + url, e);
235-
}
233+
builder = UriComponentsBuilder
234+
.fromUri(url)
235+
.queryParams(multiValueMap);
236236

237237
final ResponseEntity<String> responseString = restTemplate.exchange(
238238
builder.build(mutableQueryParams),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package io.securecodebox.persistence.defectdojo.service;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.core.type.TypeReference;
5+
import io.securecodebox.persistence.defectdojo.exception.PersistenceException;
6+
import io.securecodebox.persistence.defectdojo.model.Model;
7+
import io.securecodebox.persistence.defectdojo.model.PaginatedResult;
8+
import lombok.NonNull;
9+
import org.junit.jupiter.api.Test;
10+
11+
import java.net.URI;
12+
import java.util.Map;
13+
14+
import static org.junit.jupiter.api.Assertions.*;
15+
import static org.hamcrest.MatcherAssert.assertThat;
16+
import static org.hamcrest.Matchers.*;
17+
18+
/**
19+
* Tests for {@link GenericDefectDojoService}
20+
*/
21+
final class GenericDefectDojoServiceTest extends WireMockBaseTestCase {
22+
private static final class TestModel implements Model {
23+
@Override
24+
public boolean equalsQueryString(@NonNull Map<String, Object> queryParams) {
25+
// Stub this to false because we do not test this method here.
26+
return false;
27+
}
28+
}
29+
private final GenericDefectDojoService<TestModel> sut =new GenericDefectDojoService<>(conf()) {
30+
31+
@Override
32+
protected String getUrlPath() {
33+
return "snafu";
34+
}
35+
36+
@Override
37+
protected Class<TestModel> getModelClass() {
38+
return TestModel.class;
39+
}
40+
41+
@Override
42+
protected PaginatedResult<TestModel> deserializeList(@NonNull String response) {
43+
try {
44+
return this.objectMapper.readValue(response, new TypeReference<>() {
45+
});
46+
} catch (JsonProcessingException e) {
47+
throw new PersistenceException("Can't process JSON response!", e);
48+
}
49+
}
50+
};
51+
52+
@Test
53+
void createBaseUrl() {
54+
assertThat(sut.createBaseUrl(), is(URI.create("http://localhost:8888/api/v2/snafu/")));
55+
}
56+
}

0 commit comments

Comments
 (0)