issue #9 - Refactor CfClientTest, CfDnsClient, and CfBasicHttpClient: improve error handling, enable record filtering, refine empty result behavior, and update tests for clarity and robustness.

This commit is contained in:
2026-01-11 18:28:22 +01:00
parent b88f4f78ba
commit b241d8cf09
3 changed files with 89 additions and 55 deletions
@@ -71,8 +71,19 @@ abstract class CfBasicHttpClient {
T respObj = objectMapper.readValue(result.responseBody, responseType);
if (!respObj.getResponseResultInfo().isSuccess()) {
log.error("API error.");
respObj.getResponseResultInfo().getErrors().forEach(e -> log.error(" - {}", e.toString()));
throw new CloudflareApiException("API error.");
StringBuilder errorMessage = new StringBuilder("API error");
if (!respObj.getResponseResultInfo().getErrors().isEmpty()) {
errorMessage.append(": ");
respObj.getResponseResultInfo().getErrors().forEach(e -> {
log.error(" - {}", e.toString());
errorMessage.append(e).append("; ");
});
// Remove trailing "; "
if (errorMessage.toString().endsWith("; ")) {
errorMessage.setLength(errorMessage.length() - 2);
}
}
throw new CloudflareApiException(errorMessage.toString());
}
logUri = request.getRequestUri();
if (result.statusCode >= 200 && result.statusCode < 300) {
@@ -13,6 +13,7 @@ import codes.thischwa.cf.model.RecordType;
import codes.thischwa.cf.model.ZoneEntity;
import codes.thischwa.cf.model.ZoneMultipleResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -51,6 +52,8 @@ public class CfDnsClient extends CfBasicHttpClient {
private final ResponseValidator responseValidator;
private final boolean emptyResultThrowsException;
/**
* Constructs a new instance of {@code CfDnsClient}.
*
@@ -107,6 +110,7 @@ public class CfDnsClient extends CfBasicHttpClient {
String authKey) {
super(baseUrl, authEmail, authKey);
this.responseValidator = new ResponseValidator(emptyResultThrowsException);
this.emptyResultThrowsException = emptyResultThrowsException;
}
private static String buildFqdn(ZoneEntity zone, String sld) {
@@ -216,15 +220,12 @@ public class CfDnsClient extends CfBasicHttpClient {
*/
public List<RecordEntity> recordList(ZoneEntity zone, String sld, @Nullable RecordType... types)
throws CloudflareApiException {
String fqdn = buildFqdn(zone, sld);
String endpoint = buildEndpointWithTypeFilters(CfRequest.RECORD_INFO_NAME.buildPath(zone.getId(), fqdn), types);
RecordMultipleResponse resp = getRequest(endpoint, RecordMultipleResponse.class);
checkResponse(resp, false);
List<RecordEntity> recs = resp.getResult();
recs.forEach(rec -> rec.setZoneId(zone.getId()));
return recs;
PagingRequest pagingRequest = PagingRequest.defaultPaging();
List<RecordEntity> recs = recordList(zone, sld, pagingRequest);
return filterAndSetZoneRecords(zone, types, recs);
}
/**
* Retrieves all record entities for a specific second-level domain (SLD) within a given DNS
* zone using the provided paging request parameters.
@@ -256,23 +257,11 @@ public class CfDnsClient extends CfBasicHttpClient {
*/
public List<RecordEntity> recordList(ZoneEntity zone, RecordType... types)
throws CloudflareApiException {
String endpoint = buildEndpointWithTypeFilters(CfRequest.RECORD_LIST.buildPath(zone.getId()), types);
String endpoint = CfRequest.RECORD_LIST.buildPath(zone.getId());
RecordMultipleResponse resp = getRequest(endpoint, RecordMultipleResponse.class);
checkResponse(resp, false);
return resp.getResult();
}
private String buildEndpointWithTypeFilters(String baseEndpoint, @Nullable RecordType... types) {
if (types == null || types.length == 0) {
return baseEndpoint;
}
StringBuilder endpoint = new StringBuilder(baseEndpoint);
String separator = baseEndpoint.contains("?") ? "&" : "?";
for (RecordType type : types) {
endpoint.append(separator).append("type=").append(type);
separator = "&";
}
return endpoint.toString();
List<RecordEntity> recs = resp.getResult();
return filterAndSetZoneRecords(zone, types, recs);
}
/**
@@ -459,6 +448,24 @@ public class CfDnsClient extends CfBasicHttpClient {
return result;
}
private List<RecordEntity> filterAndSetZoneRecords(ZoneEntity zone, @Nullable RecordType[] types, List<RecordEntity> recs)
throws CloudflareNotFoundException {
List<RecordEntity> filtered;
if (types != null && types.length > 0) {
filtered = recs.stream().filter(rec -> Arrays.asList(types).contains(RecordType.valueOf(rec.getType()))).collect(Collectors.toList());
} else {
filtered = new ArrayList<>(recs);
}
filtered.forEach(rec -> rec.setZoneId(zone.getId()));
// special exception for an empty result, normally it's done in the RecordValidator
if (filtered.isEmpty() && emptyResultThrowsException) {
throw new CloudflareNotFoundException("No records exist after filtering zone: " + zone.getName());
}
return filtered;
}
private List<RecordEntity> cleanRecordsForPostOrPut(List<RecordEntity> records) {
List<RecordEntity> cleaned = new ArrayList<>();
records.forEach(