Change default behavior of `emptyResultThrowsException` to `false`, update `ResponseValidator` for improved handling of single and multiple results, and enhance test coverage and documentation.

Add Fluent API for DNS operations, update `README`, and refactor `CfDnsClient` for zone-level chaining.

Validate record type in `RecordEntity#build`, throw exception for invalid types, and add test coverage.

Refactor `RecordEntity#getSld` to handle `zoneName` cases, improve null safety, and enhance substring logic.

Refactor `RecordEntity` getter (`getName` → `getSld`) for clarity, improve string handling in batch processing, and enhance type safety in HTTP operations. Update tests and documentation accordingly.

Refactor `deleteRequest` and `patchRequest` to accept `responseType` parameter for improved flexibility. Extract methods for cleaning and processing batch DNS records.

Refactor API method names for consistency (`zoneListAll` → `zoneList`, `zoneInfo` → `zoneGet`, `sldListAll` → `recordList`, `sldInfo` → `recordGet`) and update related documentation and tests.
This commit is contained in:
2026-01-05 17:59:26 +01:00
parent 1bfea09aa9
commit a221de4792
14 changed files with 760 additions and 159 deletions
+263 -46
View File
@@ -50,6 +50,16 @@ The dependency is:
## Changelog
- 0.2.0-beta-SNAPSHOT:
- **New Fluent API**: Added chainable method interface for more readable DNS operations (
`client.zone().record()...`)
- **Breaking Change**: `emptyResultThrowsException` default changed from `true` to `false`. Now applies to both
single and multiple result requests. Empty results will be returned by default without throwing exceptions.
- API method names refactored for consistency: `zoneListAll``zoneList`, `zoneInfo``zoneGet`, `sldListAll`
`recordList`, `sldInfo``recordGet`
- RecordEntity getter methods renamed for clarity: `getName()``getSld()`
- Code quality improvements: eliminated duplication in batch operations, improved type safety in HTTP methods,
optimized string concatenation, removed mutable setters from CfDnsClient
- Enhanced type validation in `RecordEntity.build()` with better error messages
- CfClient#sldInfo must return multiple RecordEntries
- add a missing source jar
- ResponseResultInfo#Errors: wrong object structure
@@ -68,6 +78,11 @@ The methods can be categorized as follows:
- `Zone`: list, info
- `Record`: list, info, create, update, delete
The API provides two styles for working with DNS records:
1. **Traditional API**: Direct method calls with explicit parameters
2. **Fluent API**: Chainable method calls for more readable code
The following text focuses on the basic methods. For further information, take a look at
the [javadoc of the CfDnsClient](https://cloudflaredns-java-f4ee3a.gitlab.io/apidocs/codes/thischwa/cf/CfDnsClient.html).
@@ -79,20 +94,20 @@ CfDnsClient cfDnsClient = new CfDnsClient(
);
```
### `zoneListAll`
### `zoneList`
Retrieve all zones within the Cloudflare account.
- **Returns**: A list of `ZoneEntity` objects.
```java
List<ZoneEntity> zones = cfDnsClient.zoneListAll();
List<ZoneEntity> zones = cfDnsClient.zoneList();
zones.forEach(zone -> System.out.println("Zone: " + zone.getName()));
```
---
### `zoneInfo`
### `zoneGet`
Get detailed information about a specific zone by its name.
@@ -101,13 +116,13 @@ Get detailed information about a specific zone by its name.
- **Returns**: A `ZoneEntity` object.
```java
ZoneEntity zone = cfDnsClient.zoneInfo("example.com");
ZoneEntity zone = cfDnsClient.zoneGet("example.com");
System.out.println("Zone ID: " + zone.getId());
```
---
### `sldListAll`
### `recordList`
Retrieve all records for a specific second-level domain (SLD) under a given zone.
@@ -117,15 +132,17 @@ Retrieve all records for a specific second-level domain (SLD) under a given zone
- **Returns**: A list of `RecordEntity` objects.
```java
List<RecordEntity> records = cfDnsClient.sldListAll(zone, "sld");
records.forEach(record ->
List<RecordEntity> records = cfDnsClient.recordList(zone, "sld");
records.
forEach(record ->
System.out.println("Record Type: " + record.getType() + ", Value: " + record.getContent())
);
```
---
### `sldInfo`
### `recordGet`
Retrieve DNS record details for a specific SLD and zone, optionally filtered by record types.
@@ -138,28 +155,28 @@ Retrieve DNS record details for a specific SLD and zone, optionally filtered by
```java
// Get all records for a specific SLD
List<RecordEntity> allRecords = cfDnsClient.sldInfo(zone, "www");
List<RecordEntity> allRecords = cfDnsClient.recordGet(zone, "www");
allRecords.
forEach(record ->
System.out.
println("Type: "+record.getType() +", Content: "+record.
println("Type: "+record.getType()+", Content: "+record.
getContent())
);
getContent()));
// Get only A records
List<RecordEntity> aRecords = cfDnsClient.sldInfo(zone, "www", RecordType.A);
List<RecordEntity> aRecords = cfDnsClient.recordGet(zone, "www", RecordType.A);
System.out.
println("Found "+aRecords.size() +" A records");
// Get A and AAAA records
List<RecordEntity> ipRecords = cfDnsClient.sldInfo(zone, "www", RecordType.A, RecordType.AAAA);
List<RecordEntity> ipRecords = cfDnsClient.recordGet(zone, "www", RecordType.A, RecordType.AAAA);
ipRecords.
forEach(record ->System.out.
forEach(record ->
System.out.
println("IP Record: "+record.getContent()));
```
@@ -232,20 +249,25 @@ Process multiple DNS record operations (POST, PUT, PATCH, DELETE) in a single ba
- **Parameters**:
- `ZoneEntity zone` - The target zone.
- `List<RecordEntity> postRecords` - Records to create (nullable).
- `List<RecordEntity> putRecords` - Records to fully replace (nullable).
- `List<RecordEntity> patchRecords` - Records to partially update (nullable).
- `List<RecordEntity> deleteRecords` - Records to delete (nullable).
- `List<RecordEntity> postRecords` - Records to create (nullable). Can be built without IDs.
- `List<RecordEntity> putRecords` - Records to fully replace (nullable). **Requires record IDs**.
- `List<RecordEntity> patchRecords` - Records to partially update (nullable). **Requires record IDs**.
- `List<RecordEntity> deleteRecords` - Records to delete (nullable). **Requires record IDs**.
- **Returns**: A `BatchEntry` object containing the processed records.
**Important**: For UPDATE (PATCH), REPLACE (PUT), and DELETE operations, you must first retrieve the existing records to
obtain their IDs.
#### Batch Create (POST)
Create new records, IDs are not required:
```java
List<RecordEntity> newRecords = Arrays.asList(
RecordEntity.build("api." + zone.getName(), RecordType.A, 60, "192.168.1.10"),
RecordEntity.build("cdn." + zone.getName(), RecordType.A, 60, "192.168.1.11"),
RecordEntity.build("mail." + zone.getName(), RecordType.A, 60, "192.168.1.12")
);
BatchEntry result = cfDnsClient.recordBatch(zone, newRecords, null, null, null);
System.out.
@@ -256,18 +278,30 @@ size() +" records.");
#### Batch Update (PATCH)
Partially update existing records. **Record IDs are required** - fetch them first:
```java
// Fetch existing records and modify them
List<RecordEntity> recordsToUpdate = Arrays.asList(
cfDnsClient.sldInfo(zone, "api", RecordType.A),
cfDnsClient.sldInfo(zone, "cdn", RecordType.A)
);
// Step 1: Fetch existing records to get their IDs
List<RecordEntity> recordsToUpdate = new ArrayList<>();
recordsToUpdate.
add(cfDnsClient.recordGet(zone, "api",RecordType.A).
get(0));
recordsToUpdate.
add(cfDnsClient.recordGet(zone, "cdn",RecordType.A).
get(0));
// Step 2: Modify only the fields you want to update
recordsToUpdate.
forEach(record ->record.
setContent("192.168.2.10"));
// Step 3: Send batch update request
BatchEntry result = cfDnsClient.recordBatch(zone, null, null, recordsToUpdate, null);
System.out.
@@ -278,14 +312,25 @@ size() +" records.");
#### Batch Replace (PUT)
Fully replace existing records. **Record IDs are required** - fetch them first:
```java
// Fetch existing records and fully replace them
List<RecordEntity> recordsToReplace = Arrays.asList(
cfDnsClient.sldInfo(zone, "api", RecordType.A),
cfDnsClient.sldInfo(zone, "cdn", RecordType.A)
);
// Step 1: Fetch existing records to get their IDs
List<RecordEntity> recordsToReplace = new ArrayList<>();
recordsToReplace.
add(cfDnsClient.recordGet(zone, "mail",RecordType.A).
get(0));
recordsToReplace.
add(cfDnsClient.recordGet(zone, "cdn",RecordType.A).
get(0));
// Step 2: Modify all fields as needed (full replacement)
recordsToReplace.
get(0).
setContent("192.168.3.10");
@@ -294,7 +339,18 @@ recordsToReplace.
get(0).
setTtl(120);
recordsToReplace.
get(1).
setContent("192.168.3.11");
recordsToReplace.
get(1).
setTtl(120);
// Step 3: Send batch replace request
BatchEntry result = cfDnsClient.recordBatch(zone, null, recordsToReplace, null, null);
System.out.
@@ -305,28 +361,177 @@ size() +" records.");
#### Batch Delete
Delete existing records. **Record IDs are required** - fetch them first:
```java
List<RecordEntity> recordsToDelete = Arrays.asList(
cfDnsClient.sldInfo(zone, "api", RecordType.A),
cfDnsClient.sldInfo(zone, "mail", RecordType.A)
);
// Step 1: Fetch existing records to get their IDs
List<RecordEntity> recordsToDelete = new ArrayList<>();
recordsToDelete.
add(cfDnsClient.recordGet(zone, "cdn",RecordType.A).
get(0));
recordsToDelete.
add(cfDnsClient.recordGet(zone, "mail",RecordType.A).
get(0));
// Step 2: Send batch delete request
BatchEntry result = cfDnsClient.recordBatch(zone, null, null, null, recordsToDelete);
System.out.
println("Deleted records.");
println("Deleted "+recordsToDelete.size() +" records.");
```
#### Combined Batch Operations
Combine multiple operations in a single batch request:
```java
// You can combine multiple operations in a single batch request
BatchEntry result = cfDnsClient.recordBatch(
zone,
newRecords, // POST
putRecords, // PUT
patchRecords, // PATCH
deleteRecords // DELETE
// Create new records (no IDs needed)
List<RecordEntity> newRecords = Arrays.asList(
RecordEntity.build("new-api." + zone.getName(), RecordType.A, 60, "192.168.1.100")
);
// Fetch existing records for update/delete (IDs required)
List<RecordEntity> recordsToUpdate = Arrays.asList(
cfDnsClient.recordGet(zone, "existing-api", RecordType.A).get(0)
);
recordsToUpdate.
get(0).
setContent("192.168.1.200");
List<RecordEntity> recordsToDelete = Arrays.asList(
cfDnsClient.recordGet(zone, "old-api", RecordType.A).get(0)
);
// Execute all operations in a single batch request
BatchEntry result = cfDnsClient.recordBatch(
zone,
newRecords, // POST - create new records
null, // PUT - not used in this example
recordsToUpdate, // PATCH - update existing records
recordsToDelete // DELETE - remove records
);
System.out.
println("Batch completed:");
System.out.
println(" Created: "+result.getPosts().
size());
System.out.
println(" Updated: "+result.getPatches().
size());
System.out.
println(" Deleted: "+result.getDeletes().
size());
```
---
## Fluent API
The fluent API provides a chainable, readable interface for DNS operations. It's an alternative to the traditional API
that reduces verbosity and improves code readability.
### Basic Usage
```java
// Create a DNS record
client.zone("example.com")
.
record("api")
.
create(RecordType.A, "192.168.1.1",60);
// Get DNS records
List<RecordEntity> records = client.zone("example.com")
.record("www", RecordType.A)
.get();
// Update a DNS record
RecordEntity updated = client.zone("example.com")
.record("api", RecordType.A)
.update("192.168.1.2");
// Delete DNS records
client.
zone("example.com")
.
record("old-service")
.
delete(RecordType.A, RecordType.AAAA);
```
### Advantages of Fluent API
- **More Readable**: The chain of method calls reads like natural language
- **Less Verbose**: No need to pass zone objects between method calls
- **Type Safe**: Full compile-time type checking
- **IDE Friendly**: Excellent autocomplete support
### Complete Example
```java
CfDnsClient client = new CfDnsClient("email@example.com", "yourApiKey");
// Create a new record
client.
zone("example.com")
.
record("api")
.
create(RecordType.A, "192.168.100.1",60);
// Retrieve and verify
List<RecordEntity> records = client.zone("example.com")
.record("api", RecordType.A)
.get();
System.out.
println("IP: "+records.get(0).
getContent());
// Update the record
client.
zone("example.com")
.
record("api",RecordType.A)
.
update("192.168.100.2");
// Clean up
client.
zone("example.com")
.
record("api")
.
delete(RecordType.A);
```
---
@@ -335,21 +540,33 @@ BatchEntry result = cfDnsClient.recordBatch(
The `CfDnsClient` provides internal error-handling mechanisms through exceptions. For example:
- `CloudflareApiException` is thrown for errors during API communication or invalid responses.
- `CloudflareNotFoundException` is thrown when the requested single resource is not found, if enabled via the `emptyResultThrowsException` flag during initialization.
- `CloudflareNotFoundException` is thrown when the requested resource (single or multiple) is not found, if enabled via
the `emptyResultThrowsException` flag during initialization. **Default is `false`**, meaning empty results will be
returned without throwing an exception.
To enable exception throwing for empty results:
```java
CfDnsClient client = new CfDnsClient(true, "email@example.com", "yourApiKey");
```
#### Example:
```java
try {
RecordEntity record = cfDnsClient.sldInfo(zone, "www", RecordType.A);
System.out.println("Record IP: " + record.getContent());
List<RecordEntity> records = cfDnsClient.recordGet(zone, "www", RecordType.A);
System.out.
println("Record IP: "+records.get(0).
getContent());
} catch (CloudflareApiException e) {
if (e instanceof CloudflareNotFoundException) {
log.warn("Sld not found: www");
} else {
log.error("Error while getting sld info of www", e);
throw e;
}
}
}
```