From 607b634b35cadeecef8bf6e53ce5d775e16ff0cb Mon Sep 17 00:00:00 2001 From: Thilo Schwarz Date: Fri, 23 Jan 2026 17:26:58 +0100 Subject: [PATCH] issue #11 - Refactor `CfDnsClient` with a simplified `CfDnsClientBuilder` for authentication and configuration. Update tests and README for new builder. Added authtification with apiToken --- README.md | 29 ++- .../codes/thischwa/cf/CfBasicHttpClient.java | 5 +- .../java/codes/thischwa/cf/CfDnsClient.java | 58 ++---- .../codes/thischwa/cf/CfDnsClientBuilder.java | 186 ++++++++++++++++++ .../codes/thischwa/cf/auth/ApiTokenAuth.java | 31 --- .../java/codes/thischwa/cf/auth/CfAuth.java | 19 -- .../codes/thischwa/cf/auth/CfAuthBuilder.java | 12 -- .../codes/thischwa/cf/auth/EmailKeyAuth.java | 38 ---- .../codes/thischwa/cf/auth/package-info.java | 5 - .../codes/thischwa/cf/CfClientPenTest.java | 5 +- .../java/codes/thischwa/cf/CfClientTest.java | 15 +- 11 files changed, 228 insertions(+), 175 deletions(-) create mode 100644 src/main/java/codes/thischwa/cf/CfDnsClientBuilder.java delete mode 100644 src/main/java/codes/thischwa/cf/auth/ApiTokenAuth.java delete mode 100644 src/main/java/codes/thischwa/cf/auth/CfAuth.java delete mode 100644 src/main/java/codes/thischwa/cf/auth/CfAuthBuilder.java delete mode 100644 src/main/java/codes/thischwa/cf/auth/EmailKeyAuth.java delete mode 100644 src/main/java/codes/thischwa/cf/auth/package-info.java 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

gitlab-cloudflare @@ -51,13 +51,13 @@ The dependency is: - 0.2.0: - de-lombok the source jar - - **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` - RecordEntity getter methods renamed for clarity: `getName()` → `getSld()` + - **New Fluent API**: Changed the initialization of the client(`new CfDnsClientBuilder().withApiTokenAuth("your-api-token").build()`) and added chainable method interface for more readable DNS operations ( + `client.zone().record()...`) - 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 @@ -89,10 +89,18 @@ the [javadoc of the CfDnsClient](https://cloudflaredns-java-f4ee3a.gitlab.io/api ### Instantiation of `CfDnsClient` +#### With API Token (recommended): ```java -CfDnsClient cfDnsClient = new CfDnsClient( - "email@example.com", "yourApiKey" - ); +CfDnsClient cfDnsClient = new CfDnsClientBuilder() + .withApiTokenAuth("your-api-token") + .build(); +``` + +#### With Email/Key (legacy): +```java +CfDnsClient cfDnsClient = new CfDnsClientBuilder() + .withEmailKeyAuth("email@example.com", "yourApiKey") + .build(); ``` ### `zoneList` @@ -390,7 +398,9 @@ client.zone("example.com") ### Complete Example ```java -CfDnsClient client = new CfDnsClient("email@example.com", "yourApiKey"); +CfDnsClient client = new CfDnsClientBuilder() + .withApiTokenAuth("your-api-token") + .build(); // Create a new record client.zone("example.com") @@ -425,7 +435,10 @@ The `CfDnsClient` provides internal error-handling mechanisms through exceptions To enable exception throwing for empty results: ```java -CfDnsClient client = new CfDnsClient(true, "email@example.com", "yourApiKey"); +CfDnsClient client = new CfDnsClientBuilder() + .withApiTokenAuth("your-api-token") + .withEmptyResultThrowsException(true) + .build(); ``` ## Example: diff --git a/src/main/java/codes/thischwa/cf/CfBasicHttpClient.java b/src/main/java/codes/thischwa/cf/CfBasicHttpClient.java index 875f7fa..c51f6f5 100644 --- a/src/main/java/codes/thischwa/cf/CfBasicHttpClient.java +++ b/src/main/java/codes/thischwa/cf/CfBasicHttpClient.java @@ -1,6 +1,5 @@ package codes.thischwa.cf; -import codes.thischwa.cf.auth.CfAuth; import codes.thischwa.cf.model.AbstractResponse; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -31,7 +30,7 @@ import org.jetbrains.annotations.NotNull; abstract class CfBasicHttpClient { private final String baseUrl; - private final CfAuth auth; + private final CfDnsClientBuilder.CfAuth auth; private final ObjectMapper objectMapper; /** @@ -40,7 +39,7 @@ abstract class CfBasicHttpClient { * @param baseUrl the base URL for the Cloudflare API * @param auth the authentication mechanism to use */ - CfBasicHttpClient(@NotNull String baseUrl, @NotNull CfAuth auth) { + CfBasicHttpClient(@NotNull String baseUrl, @NotNull CfDnsClientBuilder.CfAuth auth) { this.baseUrl = baseUrl; this.auth = auth; this.objectMapper = JsonConf.initObjectMapper(); diff --git a/src/main/java/codes/thischwa/cf/CfDnsClient.java b/src/main/java/codes/thischwa/cf/CfDnsClient.java index bdc6d5f..13b480c 100644 --- a/src/main/java/codes/thischwa/cf/CfDnsClient.java +++ b/src/main/java/codes/thischwa/cf/CfDnsClient.java @@ -1,6 +1,5 @@ package codes.thischwa.cf; -import codes.thischwa.cf.auth.CfAuth; import codes.thischwa.cf.fluent.ZoneOperations; import codes.thischwa.cf.fluent.ZoneOperationsImpl; import codes.thischwa.cf.model.AbstractResponse; @@ -30,7 +29,9 @@ import org.jetbrains.annotations.Nullable; *

Example with API token authentication (recommended): *


  * // 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;
  *
  * 

Example with email/key authentication (legacy): *


- * CfDnsClient cfDnsClient = new CfDnsClient(
- *     CfAuthBuilder.build("email@example.com", "your-api-key")
- * );
+ * CfDnsClient cfDnsClient = new CfDnsClientBuilder()
+ *     .withEmailKeyAuth("email@example.com", "your-api-key")
+ *     .build();
  * 
* *

Example with exception throwing enabled: *


  * // 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();
  * 
* *

Example with custom base URL: *


- * 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();
  * 
*/ @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`. + * + *

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,