Refactor code style and add tests for exception handling

Adjusted code style to align with consistent brace formatting and removed the null/blank check for `baseUrl` in the constructor. Added unit tests to verify exception handling in `CfDnsClient` for null arguments.
This commit is contained in:
2025-04-13 19:57:15 +02:00
parent f0bc7b5446
commit 97b7f8371a
2 changed files with 63 additions and 29 deletions
@@ -31,7 +31,8 @@ import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
* managing authentication, and handling JSON serialization. * managing authentication, and handling JSON serialization.
*/ */
@Slf4j @Slf4j
abstract class CfBasicHttpClient { abstract class CfBasicHttpClient
{
private final String baseUrl; private final String baseUrl;
private final String authEmail; private final String authEmail;
private final String authKey; private final String authKey;
@@ -40,10 +41,6 @@ abstract class CfBasicHttpClient {
CfBasicHttpClient(String baseUrl, String authEmail, String authKey) throws IllegalArgumentException CfBasicHttpClient(String baseUrl, String authEmail, String authKey) throws IllegalArgumentException
{ {
if (baseUrl == null || baseUrl.isBlank())
{
throw new IllegalArgumentException("Base URL must not be null or blank!");
}
if (authEmail == null || authEmail.isBlank()) if (authEmail == null || authEmail.isBlank())
{ {
throw new IllegalArgumentException("Authentication email must not be null or blank!"); throw new IllegalArgumentException("Authentication email must not be null or blank!");
@@ -58,7 +55,8 @@ abstract class CfBasicHttpClient {
this.objectMapper = initObjectMapper(); this.objectMapper = initObjectMapper();
} }
private ObjectMapper initObjectMapper() { private ObjectMapper initObjectMapper()
{
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule()); mapper.registerModule(new JavaTimeModule());
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
@@ -67,7 +65,8 @@ abstract class CfBasicHttpClient {
return mapper; return mapper;
} }
private CloseableHttpClient createHttpClient() { private CloseableHttpClient createHttpClient()
{
return HttpClients.custom() return HttpClients.custom()
.addRequestInterceptorFirst( .addRequestInterceptorFirst(
(request, context, execChain) -> { (request, context, execChain) -> {
@@ -83,9 +82,11 @@ abstract class CfBasicHttpClient {
} }
private <T extends AbstractResponse> T executeRequest( private <T extends AbstractResponse> T executeRequest(
ClassicHttpRequest request, Class<T> responseType) throws CloudflareApiException { ClassicHttpRequest request, Class<T> responseType) throws CloudflareApiException
{
String logUri = null; String logUri = null;
try (CloseableHttpClient client = createHttpClient()) { try (CloseableHttpClient client = createHttpClient())
{
ResultWrapper result = ResultWrapper result =
client.execute( client.execute(
request, request,
@@ -95,9 +96,11 @@ abstract class CfBasicHttpClient {
EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8))); EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8)));
logUri = request.getRequestUri(); logUri = request.getRequestUri();
if (result.statusCode >= 200 && result.statusCode < 300) { if (result.statusCode >= 200 && result.statusCode < 300)
{
return objectMapper.readValue(result.responseBody, responseType); return objectMapper.readValue(result.responseBody, responseType);
} else { } else
{
log.error( log.error(
"{} request failed for URL {}: Status {}", "{} request failed for URL {}: Status {}",
request.getMethod(), request.getMethod(),
@@ -106,68 +109,93 @@ abstract class CfBasicHttpClient {
throw new CloudflareApiException( throw new CloudflareApiException(
request.getMethod() + " request failed with status code: " + result.statusCode); request.getMethod() + " request failed with status code: " + result.statusCode);
} }
} catch (JsonProcessingException e) { } catch (JsonProcessingException e)
{
log.error("JSON parsing error for request to {}", logUri, e); log.error("JSON parsing error for request to {}", logUri, e);
throw new CloudflareApiException("Error processing JSON response", e); throw new CloudflareApiException("Error processing JSON response", e);
} catch (Exception e) { } catch (Exception e)
{
log.error("Error during request execution", e); log.error("Error during request execution", e);
throw new CloudflareApiException("Request failed", e); throw new CloudflareApiException("Request failed", e);
} }
} }
/** Sends a GET request to the given endpoint and maps the response. */ /**
* Sends a GET request to the given endpoint and maps the response.
*/
<T extends AbstractResponse> T getRequest(String endpoint, Class<T> responseType) <T extends AbstractResponse> T getRequest(String endpoint, Class<T> responseType)
throws CloudflareApiException { throws CloudflareApiException
{
HttpGet request = new HttpGet(buildUrl(endpoint)); HttpGet request = new HttpGet(buildUrl(endpoint));
return executeRequest(request, responseType); return executeRequest(request, responseType);
} }
/** Sends a DELETE request to the given endpoint and maps the response. */ /**
* Sends a DELETE request to the given endpoint and maps the response.
*/
<T extends AbstractResponse> T deleteRequest(String endpoint) <T extends AbstractResponse> T deleteRequest(String endpoint)
throws CloudflareApiException { throws CloudflareApiException
{
HttpDelete request = new HttpDelete(buildUrl(endpoint)); HttpDelete request = new HttpDelete(buildUrl(endpoint));
return executeRequest(request, (Class<T>) codes.thischwa.cf.model.RecordSingleResponse.class); return executeRequest(request, (Class<T>) codes.thischwa.cf.model.RecordSingleResponse.class);
} }
/** Sends a POST request with a payload to the given endpoint and maps the response. */ /**
* Sends a POST request with a payload to the given endpoint and maps the response.
*/
<T extends AbstractResponse, R extends AbstractEntity> T postRequest( <T extends AbstractResponse, R extends AbstractEntity> T postRequest(
String endpoint, R requestPayload) throws CloudflareApiException { String endpoint, R requestPayload) throws CloudflareApiException
{
HttpPost request = new HttpPost(buildUrl(endpoint)); HttpPost request = new HttpPost(buildUrl(endpoint));
setRequestPayload(request, requestPayload); setRequestPayload(request, requestPayload);
return executeRequest(request, (Class<T>) codes.thischwa.cf.model.RecordSingleResponse.class); return executeRequest(request, (Class<T>) codes.thischwa.cf.model.RecordSingleResponse.class);
} }
/** Sends a PUT request with a payload to the given endpoint and maps the response. */ /**
* Sends a PUT request with a payload to the given endpoint and maps the response.
*/
<T extends AbstractResponse, R extends AbstractEntity> T putRequest( <T extends AbstractResponse, R extends AbstractEntity> T putRequest(
String endpoint, R requestPayload, Class<T> responseType) throws CloudflareApiException { String endpoint, R requestPayload, Class<T> responseType) throws CloudflareApiException
{
HttpPut request = new HttpPut(buildUrl(endpoint)); HttpPut request = new HttpPut(buildUrl(endpoint));
setRequestPayload(request, requestPayload); setRequestPayload(request, requestPayload);
return executeRequest(request, responseType); return executeRequest(request, responseType);
} }
/** Sends a PATCH request with a payload to the given endpoint and maps the response. */ /**
* Sends a PATCH request with a payload to the given endpoint and maps the response.
*/
<T extends AbstractResponse, R extends AbstractEntity> T patchRequest( <T extends AbstractResponse, R extends AbstractEntity> T patchRequest(
String endpoint, R requestPayload) throws CloudflareApiException { String endpoint, R requestPayload) throws CloudflareApiException
{
HttpPatch request = new HttpPatch(buildUrl(endpoint)); HttpPatch request = new HttpPatch(buildUrl(endpoint));
setRequestPayload(request, requestPayload); setRequestPayload(request, requestPayload);
return executeRequest(request, (Class<T>) codes.thischwa.cf.model.RecordSingleResponse.class); return executeRequest(request, (Class<T>) codes.thischwa.cf.model.RecordSingleResponse.class);
} }
/** Sets the JSON payload for a request. */ /**
* Sets the JSON payload for a request.
*/
private <R extends AbstractEntity> void setRequestPayload( private <R extends AbstractEntity> void setRequestPayload(
BasicClassicHttpRequest request, R requestPayload) throws CloudflareApiException { BasicClassicHttpRequest request, R requestPayload) throws CloudflareApiException
try { {
try
{
request.setEntity( request.setEntity(
new StringEntity( new StringEntity(
objectMapper.writeValueAsString(requestPayload), ContentType.APPLICATION_JSON)); objectMapper.writeValueAsString(requestPayload), ContentType.APPLICATION_JSON));
} catch (JsonProcessingException e) { } catch (JsonProcessingException e)
{
throw new CloudflareApiException("Error serializing JSON payload", e); throw new CloudflareApiException("Error serializing JSON payload", e);
} }
} }
private String buildUrl(String endpoint) { private String buildUrl(String endpoint)
{
return baseUrl + endpoint; return baseUrl + endpoint;
} }
private record ResultWrapper(int statusCode, String responseBody) {} private record ResultWrapper(int statusCode, String responseBody)
{
}
} }
@@ -89,4 +89,10 @@ public class CfClientTest {
client.recordDeleteTypeIfExists(z, sldStr, RecordType.A); client.recordDeleteTypeIfExists(z, sldStr, RecordType.A);
assertThrows(CloudflareNotFoundException.class, () -> client.sldInfo(z, sldStr, RecordType.A)); assertThrows(CloudflareNotFoundException.class, () -> client.sldInfo(z, sldStr, RecordType.A));
} }
@Test
void testException() {
assertThrows(IllegalArgumentException.class, () -> new CfDnsClient(null, "key"));
assertThrows(IllegalArgumentException.class, () -> new CfDnsClient("email", null));
}
} }