diff --git a/README.md b/README.md index b904663..cacece1 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ This guide comes without any warranty. Use at your own risk. The author is not r The project has its own maven repository. It can be added to the `pom.xml`: -```xml +```xml
Example with API token authentication (recommended):
* Example with email/key authentication (legacy):
* Example with exception throwing enabled:
* Example with custom base URL:
* This class serves as a builder for creating and configuring instances of a CfDnsClient. It provides
+ * a fluent API to set various optional configurations, such as API authentication methods and base
+ * URL, before constructing the client.
+ *
+ * By using this constructor, you can initiate the building process with default settings, which can
+ * later be overridden using the provided builder methods.
+ */
+ public CfDnsClientBuilder() {
+ }
+
+ /**
+ * Configures whether an exception should be thrown when an empty result is encountered
+ * during operations performed by the `CfDnsClient`.
+ *
+ * @param emptyResultThrowsException a boolean flag indicating if an exception should be thrown
+ * when an empty result is returned. If set to `true`, operations
+ * that result in an empty response will throw an exception;
+ * otherwise, they will not.
+ * @return the current instance of {@code CfDnsClientBuilder}, allowing for method chaining
+ * to further configure the builder.
+ */
+ public CfDnsClientBuilder withEmptyResultThrowsException(boolean emptyResultThrowsException) {
+ this.emptyResultThrowsException = emptyResultThrowsException;
+ return this;
+ }
+
+ /**
+ * Sets the base URL to be used by the {@code CfDnsClient}.
+ * This method allows configuring the base URL for API requests, overriding any default value.
+ *
+ * @param baseUrl the base URL to be used for API requests
+ * @return the current instance of {@code CfDnsClientBuilder}, enabling method chaining
+ */
+ public CfDnsClientBuilder withBaseUrl(String baseUrl) {
+ this.baseUrl = baseUrl;
+ return this;
+ }
+
+ /**
+ * Configures the authentication method for the {@code CfDnsClient} to use an API token.
+ * This is the recommended way to authenticate with the Cloudflare API, as it provides
+ * enhanced security and ease of use compared to other authentication mechanisms.
+ *
+ * @param apiToken the Cloudflare API token. This token is required for authenticating
+ * API requests and must not be null or blank.
+ * @return the current instance of {@code CfDnsClientBuilder}, allowing for method chaining
+ * to further configure the builder.
+ * @throws IllegalArgumentException if the {@code apiToken} is null or blank.
+ */
+ public CfDnsClientBuilder withApiTokenAuth(String apiToken) {
+ this.auth = new ApiTokenAuth(apiToken);
+ return this;
+ }
+
+ /**
+ * Configures the authentication method for the {@code CfDnsClient} to use an email and API key.
+ * This approach uses the legacy authentication mechanism provided by Cloudflare, where requests
+ * are authenticated with a combination of an account's email address and API key.
+ *
+ * @param authEmail the email address associated with the Cloudflare account. This must not be null or blank.
+ * @param authKey the API key of the Cloudflare account. This must not be null or blank.
+ * @return the current instance of {@code CfDnsClientBuilder}, allowing for method chaining
+ * to further configure the builder.
+ * @throws IllegalArgumentException if {@code authEmail} or {@code authKey} is null or blank.
+ */
+ public CfDnsClientBuilder withEmailKeyAuth(String authEmail, String authKey) {
+ this.auth = new EmailKeyAuth(authEmail, authKey);
+ return this;
+ }
+
+ /**
+ * Builds and returns a configured instance of {@code CfDnsClient}.
+ *
+ * The method constructs a new {@code CfDnsClient} object based on the
+ * options set in the {@code CfDnsClientBuilder}. If no base URL has been
+ * explicitly configured, a default base URL will be used.
+ *
+ * @return a new instance of {@code CfDnsClient} configured with the
+ * specified options such as base URL, authentication details,
+ * and the exception-handling policy for empty results.
+ */
+ public CfDnsClient build() {
+ String url = baseUrl == null ? DEFAULT_BASEURL : baseUrl;
+ return new CfDnsClient(emptyResultThrowsException, url, auth);
+ }
+
+ /**
+ * Interface for Cloudflare authentication mechanisms.
+ * Implementations of this interface provide different methods of authentication
+ * with the Cloudflare API (e.g., API token, email/key combination).
+ */
+ interface CfAuth {
+
+ /**
+ * Applies authentication headers to the given HTTP request.
+ *
+ * @param request the HTTP request to authenticate
+ */
+ void applyAuth(ClassicHttpRequest request);
+
+ }
+
+ /**
+ * Authentication mechanism using Cloudflare API token.
+ * This is the recommended authentication method for the Cloudflare API.
+ */
+ static class ApiTokenAuth implements CfAuth {
+
+ private final String apiToken;
+
+ /**
+ * Creates a new API token authentication object.
+ *
+ * @param apiToken the Cloudflare API token
+ * @throws IllegalArgumentException if the API token is null or blank
+ */
+ ApiTokenAuth(@NotNull String apiToken) {
+ if (apiToken.isBlank()) {
+ throw new IllegalArgumentException("API token must not be null or blank!");
+ }
+ this.apiToken = apiToken;
+ }
+
+ @Override
+ public void applyAuth(ClassicHttpRequest request) {
+ request.addHeader("Authorization", "Bearer " + apiToken);
+ }
+ }
+
+ /**
+ * Authentication mechanism using Cloudflare account email and API key.
+ * This is the legacy authentication method for the Cloudflare API.
+ */
+ static class EmailKeyAuth implements CfAuth {
+
+ private final String authEmail;
+ private final String authKey;
+
+ /**
+ * Creates a new email/key authentication object.
+ *
+ * @param authEmail the email address associated with the Cloudflare account
+ * @param authKey the API key of the Cloudflare account
+ * @throws IllegalArgumentException if email or key is null or blank
+ */
+ EmailKeyAuth(@NotNull String authEmail, @NotNull String authKey) {
+ if (authEmail.isBlank()) {
+ throw new IllegalArgumentException("Authentication email must not be null or blank!");
+ }
+ if (authKey.isBlank()) {
+ throw new IllegalArgumentException("Authentication key must not be null or blank!");
+ }
+ this.authEmail = authEmail;
+ this.authKey = authKey;
+ }
+
+ @Override
+ public void applyAuth(ClassicHttpRequest request) {
+ request.addHeader("X-Auth-Email", authEmail);
+ request.addHeader("X-Auth-Key", authKey);
+ }
+ }
+}
diff --git a/src/main/java/codes/thischwa/cf/auth/ApiTokenAuth.java b/src/main/java/codes/thischwa/cf/auth/ApiTokenAuth.java
deleted file mode 100644
index 8450df9..0000000
--- a/src/main/java/codes/thischwa/cf/auth/ApiTokenAuth.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package codes.thischwa.cf.auth;
-
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * Authentication mechanism using Cloudflare API token.
- * This is the recommended authentication method for the Cloudflare API.
- */
-public class ApiTokenAuth implements CfAuth {
-
- private final String apiToken;
-
- /**
- * Creates a new API token authentication object.
- *
- * @param apiToken the Cloudflare API token
- * @throws IllegalArgumentException if the API token is null or blank
- */
- public ApiTokenAuth(@NotNull String apiToken) {
- if (apiToken.isBlank()) {
- throw new IllegalArgumentException("API token must not be null or blank!");
- }
- this.apiToken = apiToken;
- }
-
- @Override
- public void applyAuth(ClassicHttpRequest request) {
- request.addHeader("Authorization", "Bearer " + apiToken);
- }
-}
diff --git a/src/main/java/codes/thischwa/cf/auth/CfAuth.java b/src/main/java/codes/thischwa/cf/auth/CfAuth.java
deleted file mode 100644
index 5df4ad6..0000000
--- a/src/main/java/codes/thischwa/cf/auth/CfAuth.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package codes.thischwa.cf.auth;
-
-import org.apache.hc.core5.http.ClassicHttpRequest;
-
-/**
- * Interface for Cloudflare authentication mechanisms.
- * Implementations of this interface provide different methods of authentication
- * with the Cloudflare API (e.g., API token, email/key combination).
- */
-public interface CfAuth {
-
- /**
- * Applies authentication headers to the given HTTP request.
- *
- * @param request the HTTP request to authenticate
- */
- void applyAuth(ClassicHttpRequest request);
-
-}
diff --git a/src/main/java/codes/thischwa/cf/auth/CfAuthBuilder.java b/src/main/java/codes/thischwa/cf/auth/CfAuthBuilder.java
deleted file mode 100644
index 05b3d46..0000000
--- a/src/main/java/codes/thischwa/cf/auth/CfAuthBuilder.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package codes.thischwa.cf.auth;
-
-public class CfAuthBuilder {
-
- public static ApiTokenAuth build(String apiToken) {
- return new ApiTokenAuth(apiToken);
- }
-
- public static EmailKeyAuth build(String authEmail, String authKey) {
- return new EmailKeyAuth(authEmail, authKey);
- }
-}
diff --git a/src/main/java/codes/thischwa/cf/auth/EmailKeyAuth.java b/src/main/java/codes/thischwa/cf/auth/EmailKeyAuth.java
deleted file mode 100644
index ab3e6ab..0000000
--- a/src/main/java/codes/thischwa/cf/auth/EmailKeyAuth.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package codes.thischwa.cf.auth;
-
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * Authentication mechanism using Cloudflare account email and API key.
- * This is the legacy authentication method for the Cloudflare API.
- */
-public class EmailKeyAuth implements CfAuth {
-
- private final String authEmail;
- private final String authKey;
-
- /**
- * Creates a new email/key authentication object.
- *
- * @param authEmail the email address associated with the Cloudflare account
- * @param authKey the API key of the Cloudflare account
- * @throws IllegalArgumentException if email or key is null or blank
- */
- public EmailKeyAuth(@NotNull String authEmail, @NotNull String authKey) {
- if (authEmail.isBlank()) {
- throw new IllegalArgumentException("Authentication email must not be null or blank!");
- }
- if (authKey.isBlank()) {
- throw new IllegalArgumentException("Authentication key must not be null or blank!");
- }
- this.authEmail = authEmail;
- this.authKey = authKey;
- }
-
- @Override
- public void applyAuth(ClassicHttpRequest request) {
- request.addHeader("X-Auth-Email", authEmail);
- request.addHeader("X-Auth-Key", authKey);
- }
-}
diff --git a/src/main/java/codes/thischwa/cf/auth/package-info.java b/src/main/java/codes/thischwa/cf/auth/package-info.java
deleted file mode 100644
index c57458f..0000000
--- a/src/main/java/codes/thischwa/cf/auth/package-info.java
+++ /dev/null
@@ -1,5 +0,0 @@
-/**
- * The authentication package of CloudflareDNS-java.
- */
-
-package codes.thischwa.cf.auth;
diff --git a/src/test/java/codes/thischwa/cf/CfClientPenTest.java b/src/test/java/codes/thischwa/cf/CfClientPenTest.java
index 7110c3e..82f0186 100644
--- a/src/test/java/codes/thischwa/cf/CfClientPenTest.java
+++ b/src/test/java/codes/thischwa/cf/CfClientPenTest.java
@@ -4,7 +4,6 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
-import codes.thischwa.cf.auth.CfAuthBuilder;
import codes.thischwa.cf.model.RecordType;
import codes.thischwa.cf.model.ZoneEntity;
import java.util.List;
@@ -31,14 +30,14 @@ public class CfClientPenTest {
}
private CfDnsClient newClient() {
- return new CfDnsClient(true, CfAuthBuilder.build(API_TOKEN));
+ return new CfDnsClientBuilder().withEmptyResultThrowsException(true).withApiTokenAuth(API_TOKEN).build();
}
@Test
@DisplayName("Invalid credentials should not authenticate and must throw CloudflareApiException")
void testInvalidCredentialsShouldFail() {
// Use syntactically valid but wrong credentials
- CfDnsClient badClient = new CfDnsClient(CfAuthBuilder.build("invalid@example.com", UUID.randomUUID().toString()));
+ CfDnsClient badClient = new CfDnsClientBuilder().withEmailKeyAuth("invalid@example.com", UUID.randomUUID().toString()).build();
assertThrows(CloudflareApiException.class, badClient::zoneList);
}
diff --git a/src/test/java/codes/thischwa/cf/CfClientTest.java b/src/test/java/codes/thischwa/cf/CfClientTest.java
index 632cd4d..c1c7305 100644
--- a/src/test/java/codes/thischwa/cf/CfClientTest.java
+++ b/src/test/java/codes/thischwa/cf/CfClientTest.java
@@ -8,9 +8,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
-import codes.thischwa.cf.auth.ApiTokenAuth;
-import codes.thischwa.cf.auth.CfAuthBuilder;
-import codes.thischwa.cf.auth.EmailKeyAuth;
import codes.thischwa.cf.model.BatchEntry;
import codes.thischwa.cf.model.RecordEntity;
import codes.thischwa.cf.model.RecordType;
@@ -33,7 +30,7 @@ public class CfClientTest {
private static final String API_TOKEN = System.getenv("API_TOKEN");
- private final CfDnsClient client = new CfDnsClient(true, CfAuthBuilder.build(API_TOKEN));
+ private final CfDnsClient client = new CfDnsClientBuilder().withEmptyResultThrowsException(true).withApiTokenAuth(API_TOKEN).build();
@BeforeAll
static void checkEnv() {
@@ -225,16 +222,6 @@ public class CfClientTest {
}
}
- @Test
- void testException() {
- // Test EmailKeyAuth validation
- assertThrows(IllegalArgumentException.class, () -> new EmailKeyAuth("email", ""));
- assertThrows(IllegalArgumentException.class, () -> new EmailKeyAuth("", "key"));
-
- // Test ApiTokenAuth validation;
- assertThrows(IllegalArgumentException.class, () -> new ApiTokenAuth(""));
- }
-
@Test
void testRecordEntityInvalidType() {
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
* // Create a new CfDnsClient instance with API token
- * CfDnsClient cfDnsClient = new CfDnsClient(CfAuthBuilder.build("your-api-token"));
+ * CfDnsClient cfDnsClient = new CfDnsClientBuilder()
+ * .withApiTokenAuth("your-api-token")
+ * .build();
*
* // Retrieve a zone
* ZoneEntity zone = cfDnsClient.zoneGet("example.com");
@@ -49,63 +50,35 @@ import org.jetbrains.annotations.Nullable;
*
*
*
*
- * CfDnsClient cfDnsClient = new CfDnsClient(
- * CfAuthBuilder.build("email@example.com", "your-api-key")
- * );
+ * CfDnsClient cfDnsClient = new CfDnsClientBuilder()
+ * .withEmailKeyAuth("email@example.com", "your-api-key")
+ * .build();
*
*
*
* // Throws exception when results are empty
- * CfDnsClient cfDnsClient = new CfDnsClient(true, CfAuthBuilder.build("your-api-token"));
+ * CfDnsClient cfDnsClient = new CfDnsClientBuilder()
+ * .withApiTokenAuth("your-api-token")
+ * .withEmptyResultThrowsException(true)
+ * .build();
*
*/
@Slf4j
public class CfDnsClient extends CfBasicHttpClient {
- public static final String DEFAULT_BASEURL = "https://api.cloudflare.com/client/v4";
private final ResponseValidator responseValidator;
private final boolean emptyResultThrowsException;
- /**
- * Constructs a new instance of {@code CfDnsClient} with default configuration.
- *
- * @param auth The authentication mechanism to use (ApiTokenAuth or EmailKeyAuth)
- */
- public CfDnsClient(CfAuth auth) {
- this(false, DEFAULT_BASEURL, auth);
- }
-
- /**
- * Constructs a new instance of {@code CfDnsClient}.
- *
- * @param baseUrl The base URL of the Cloudflare API to be used for requests.
- * @param auth The authentication mechanism to use (ApiTokenAuth or EmailKeyAuth)
- */
- public CfDnsClient(String baseUrl, CfAuth auth) {
- this(false, baseUrl, auth);
- }
-
- /**
- * Constructs a new instance of {@code CfDnsClient}.
- *
- * @param emptyResultThrowsException A boolean value indicating whether an exception should be
- * thrown when the result is empty. Applies to both single and
- * multiple result requests. Default is false.
- * @param auth The authentication mechanism to use (ApiTokenAuth or EmailKeyAuth)
- */
- public CfDnsClient(boolean emptyResultThrowsException, CfAuth auth) {
- this(emptyResultThrowsException, DEFAULT_BASEURL, auth);
- }
-
/**
* Constructs a new instance of {@code CfDnsClient}.
*
@@ -115,7 +88,7 @@ public class CfDnsClient extends CfBasicHttpClient {
* @param baseUrl The base URL for the Cloudflare API endpoint.
* @param auth The authentication mechanism to use (ApiTokenAuth or EmailKeyAuth)
*/
- public CfDnsClient(boolean emptyResultThrowsException, String baseUrl, CfAuth auth) {
+ CfDnsClient(boolean emptyResultThrowsException, String baseUrl, CfDnsClientBuilder.CfAuth auth) {
super(baseUrl, auth);
this.responseValidator = new ResponseValidator(emptyResultThrowsException);
this.emptyResultThrowsException = emptyResultThrowsException;
@@ -514,4 +487,5 @@ public class CfDnsClient extends CfBasicHttpClient {
throws CloudflareApiException {
responseValidator.validate(resp, singleResultExpected);
}
+
}
diff --git a/src/main/java/codes/thischwa/cf/CfDnsClientBuilder.java b/src/main/java/codes/thischwa/cf/CfDnsClientBuilder.java
new file mode 100644
index 0000000..592f35d
--- /dev/null
+++ b/src/main/java/codes/thischwa/cf/CfDnsClientBuilder.java
@@ -0,0 +1,186 @@
+package codes.thischwa.cf;
+
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Builder class for configuring and creating instances of {@link CfDnsClient}.
+ * This class provides a fluent API for customizing the client settings,
+ * such as the base URL and authentication mechanism.
+ */
+public class CfDnsClientBuilder {
+
+ public static final String DEFAULT_BASEURL = "https://api.cloudflare.com/client/v4";
+ private boolean emptyResultThrowsException;
+ private CfAuth auth;
+
+ @Nullable
+ private String baseUrl;
+
+ /**
+ * Constructs a new instance of `CfDnsClientBuilder`.
+ *
+ *
- * CfAuth auth = CfAuthBuilder.build("your-api-token");
- * auth.setBaseUrl("https://custom-api.example.com");
- * CfDnsClient cfDnsClient = new CfDnsClient(auth);
+ * CfDnsClient cfDnsClient = new CfDnsClientBuilder()
+ * .withApiTokenAuth("your-api-token")
+ * .withBaseUrl("https://custom-api.example.com")
+ * .build();
*