diff --git a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/configuration/HttpSettings.java b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/configuration/HttpSettings.java index fd338ebb..4e896770 100644 --- a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/configuration/HttpSettings.java +++ b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/configuration/HttpSettings.java @@ -6,7 +6,9 @@ import solutions.bellatrix.data.configuration.DataSettings; import solutions.bellatrix.data.http.authentication.Authentication; import solutions.bellatrix.data.http.authentication.AuthenticationMethod; +import solutions.bellatrix.data.http.infrastructure.HttpHeader; +import java.util.LinkedHashSet; import java.util.function.Consumer; @Data @@ -21,6 +23,8 @@ public class HttpSettings { private Authentication authentication; @SerializedName("urlEncoderEnabled") private boolean urlEncoderEnabled; + @SerializedName("headers") + private LinkedHashSet headers; public HttpSettings(HttpSettings httpSettings) { setBaseUrl(httpSettings.getBaseUrl()); @@ -28,6 +32,7 @@ public HttpSettings(HttpSettings httpSettings) { setContentType(httpSettings.getContentType()); setUrlEncoderEnabled(httpSettings.isUrlEncoderEnabled()); setAuthentication(httpSettings.getAuthentication()); + setHeaders(httpSettings.getHeaders()); } public static HttpSettings custom(Consumer httpSettings) { diff --git a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/httpContext/HttpContext.java b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/httpContext/HttpContext.java index 9ce76a6d..e885902b 100644 --- a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/httpContext/HttpContext.java +++ b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/httpContext/HttpContext.java @@ -9,6 +9,7 @@ import solutions.bellatrix.data.http.authentication.AuthenticationMethod; import solutions.bellatrix.data.http.configuration.HttpSettings; import solutions.bellatrix.data.http.infrastructure.HTTPMethod; +import solutions.bellatrix.data.http.infrastructure.HttpHeader; import solutions.bellatrix.data.http.infrastructure.QueryParameter; import java.util.*; @@ -18,16 +19,16 @@ public class HttpContext { private final HttpSettings httpSettings; private final LinkedList pathParameters; private final LinkedHashSet queryParameters; - @Setter - private RequestSpecBuilder specBuilder; - @Getter - private String requestBody; + private final LinkedHashSet httpHeaders; + @Setter private RequestSpecBuilder specBuilder; + @Getter private String requestBody; @Getter private HTTPMethod httpMethod; public HttpContext(HttpSettings settings) { this.httpSettings = settings; this.pathParameters = new LinkedList<>(); this.queryParameters = new LinkedHashSet<>(); + this.httpHeaders = httpSettings.getHeaders(); this.specBuilder = createInitialSpecBuilder(httpSettings); } @@ -36,6 +37,7 @@ public HttpContext(HttpContext httpContext) { this.queryParameters = httpContext.getQueryParameters(); this.pathParameters = httpContext.getPathParameters(); this.specBuilder = httpContext.getRequestBuilder(); + this.httpHeaders = httpContext.getHeaders(); } public HttpSettings getHttpSettings() { @@ -58,6 +60,10 @@ public LinkedList getPathParameters() { return new LinkedList<>(pathParameters); } + public LinkedHashSet getHeaders() { + return new LinkedHashSet<>(httpHeaders); + } + public LinkedHashSet getQueryParameters() { return new LinkedHashSet<>(queryParameters); } @@ -67,7 +73,7 @@ private RequestSpecBuilder getRequestBuilder() { } public RequestSpecification requestSpecification() { - if (requestBody != null) { + if (Objects.nonNull(requestBody)) { specBuilder.setBody(requestBody); } @@ -75,6 +81,8 @@ public RequestSpecification requestSpecification() { specBuilder.addQueryParams(getRequestQueryParameters()); } + specBuilder.addHeaders(getRequestHeaders()); + specBuilder.setBasePath(buildRequestPath()); return specBuilder.build(); @@ -98,10 +106,27 @@ public void addQueryParameters(Collection parameters) { parameters.forEach(this::addQueryParameter); } + public void addHeader(HttpHeader httpHeader) { + httpHeaders.add(httpHeader); + } + + public void addHeaders(Collection headers) { + headers.forEach(this::addHeader); + } + public void addQueryParameter(QueryParameter parameter) { queryParameters.add(parameter); } + private Map getRequestHeaders() { + LinkedHashMap requestHeaders = new LinkedHashMap<>(); + httpHeaders.forEach(x -> { + requestHeaders.put(x.getName(), x.getValue()); + }); + + return requestHeaders; + } + private Map getRequestQueryParameters() { //todo: this logic should be extracted because create tightly coupling with settings LinkedHashMap queryParams = new LinkedHashMap<>(); @@ -139,7 +164,6 @@ private Map getRequestQueryParameters() { return queryParams; } - public String buildRequestPath() { if (!pathParameters.isEmpty()) { String[] pathList = pathParameters.stream().filter(path -> !String.valueOf(path).isEmpty()).map(String::valueOf).toArray(String[]::new); @@ -151,9 +175,16 @@ public String buildRequestPath() { } private RequestSpecBuilder createInitialSpecBuilder(HttpSettings httpSettings) { + LinkedHashMap initialHeaders = new LinkedHashMap<>(); + httpHeaders.forEach(x -> { + initialHeaders.put(x.getName(), x.getValue()); + }); + return new RequestSpecBuilder() .setBaseUri(httpSettings.getBaseUrl()) - .setBasePath(httpSettings.getBasePath()).setContentType(httpSettings.getContentType()) + .addHeaders(initialHeaders) + .setBasePath(httpSettings.getBasePath()) + .setContentType(httpSettings.getContentType()) .setAuth(AuthSchemaFactory.getAuthenticationScheme(httpSettings.getAuthentication())) .setUrlEncodingEnabled(httpSettings.isUrlEncoderEnabled()) .log(LogDetail.ALL); diff --git a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/Entity.java b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/Entity.java index f16e45c1..2e991b6c 100644 --- a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/Entity.java +++ b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/Entity.java @@ -5,6 +5,7 @@ import solutions.bellatrix.data.contracts.Repository; @SuperBuilder +@SuppressWarnings("unchecked") public abstract class Entity { public TEntity get() { var repository = (Repository)RepositoryFactory.INSTANCE.getRepository(this.getClass()); diff --git a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpEntity.java b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpEntity.java index 3cbe16bd..f3b06964 100644 --- a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpEntity.java +++ b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpEntity.java @@ -1,6 +1,5 @@ package solutions.bellatrix.data.http.infrastructure; -import io.restassured.response.Response; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.SuperBuilder; @@ -12,7 +11,7 @@ @SuperBuilder @EqualsAndHashCode(callSuper = true) public abstract class HttpEntity extends Entity implements Queryable { - private transient Response response; + private transient HttpResponse response; public boolean hasInvalidIdentifier() { return Objects.isNull(this.getIdentifier()); diff --git a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpHeader.java b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpHeader.java new file mode 100644 index 00000000..0f5206cd --- /dev/null +++ b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpHeader.java @@ -0,0 +1,20 @@ +package solutions.bellatrix.data.http.infrastructure; + +import lombok.Getter; + +@Getter +public class HttpHeader { + private final String name; + private final String value; + + public HttpHeader(String name, String value) { + this.name = name; + this.value = value; + } + + @Override + public boolean equals(Object obj) { + HttpHeader ob = (HttpHeader)obj; + return this.value.equals(ob.getValue()); + } +} \ No newline at end of file diff --git a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpRepository.java b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpRepository.java index 2627c2c1..bf9d351d 100644 --- a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpRepository.java +++ b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpRepository.java @@ -16,6 +16,7 @@ import static solutions.bellatrix.data.http.infrastructure.HTTPMethod.*; +@SuppressWarnings("unchecked") public abstract class HttpRepository implements Repository { public static final EventListener SENDING_REQUEST = new EventListener<>(); public static final EventListener REQUEST_SENT = new EventListener<>(); @@ -161,11 +162,11 @@ private R deserializeInternal(HttpResponse response, DeserializationMode mod try { if (mode == DeserializationMode.LIST) { List entities = objectConverter.fromStringToList(response.getBody(), entityType); - entities.forEach(entity -> entity.setResponse(response.getResponse())); + entities.forEach(entity -> entity.setResponse(response)); return (R)entities; } else { THttpEntity entity = objectConverter.fromString(response.getBody(), entityType); - entity.setResponse(response.getResponse()); + entity.setResponse(response); return (R)entity; } } catch (Exception e) { diff --git a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpResponse.java b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpResponse.java index 4ebab26a..c1a69978 100644 --- a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpResponse.java +++ b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/HttpResponse.java @@ -2,14 +2,17 @@ import io.restassured.response.Response; import lombok.Getter; +import solutions.bellatrix.data.http.infrastructure.internal.HttpStatusCode; @Getter public class HttpResponse { private final String body; - private final Response response; + private final Response nativeResponse; + private final HttpStatusCode statusCode; public HttpResponse(String body, Response response) { this.body = body; - this.response = response; + this.nativeResponse = response; + statusCode = HttpStatusCode.parse(response.getStatusCode()); } } \ No newline at end of file diff --git a/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/internal/HttpStatusCode.java b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/internal/HttpStatusCode.java new file mode 100644 index 00000000..503939fa --- /dev/null +++ b/bellatrix.data/src/main/java/solutions/bellatrix/data/http/infrastructure/internal/HttpStatusCode.java @@ -0,0 +1,55 @@ +package solutions.bellatrix.data.http.infrastructure.internal; + +import lombok.Getter; + +@Getter +public enum HttpStatusCode { + // 1xx Informational responses + CONTINUE(100, "Continue"), + SWITCHING_PROTOCOLS(101, "Switching Protocols"), + PROCESSING(102, "Processing"), + EARLY_HINTS(103, "Early Hints"), + + // 2xx Success + OK(200, "OK"), + CREATED(201, "Created"), + ACCEPTED(202, "Accepted"), + NO_CONTENT(204, "No Content"), + // 3xx Redirection + MULTIPLE_CHOICES(300, "Multiple Choices"), + MOVED_PERMANENTLY(301, "Moved Permanently"), + FOUND(302, "Found"), + + // 4xx Client errors + BAD_REQUEST(400, "Bad Request"), + UNAUTHORIZED(401, "Unauthorized"), + FORBIDDEN(403, "Forbidden"), + NOT_FOUND(404, "Not Found"), + METHOD_NOT_ALLOWED(405, "Method Not Allowed"), + UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), + + // 5xx Server errors + INTERNAL_SERVER_ERROR(500, "Internal Server Error"), + NOT_IMPLEMENTED(501, "Not Implemented"), + BAD_GATEWAY(502, "Bad Gateway"), + SERVICE_UNAVAILABLE(503, "Service Unavailable"); + + private final int code; + private final String reasonPhrase; + + HttpStatusCode(int code, String reasonPhrase) { + this.code = code; + this.reasonPhrase = reasonPhrase; + } + + public static HttpStatusCode parse(int statusCode) { + for (var state : values()) { + int enumDisplayValue = state.getCode(); + if (enumDisplayValue == statusCode) { + return state; + } + } + + throw new IllegalArgumentException("Not found"); + } +} \ No newline at end of file diff --git a/framework-tests/bellatrix.data.tests/pom.xml b/framework-tests/bellatrix.data.tests/pom.xml new file mode 100644 index 00000000..e438f9e9 --- /dev/null +++ b/framework-tests/bellatrix.data.tests/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + solutions.bellatrix + bellatrix + 1.0-SNAPSHOT + ../../pom.xml + + + bellatrix.data.tests + + + 19 + 19 + UTF-8 + + + + + solutions.bellatrix + bellatrix.data + 1.0-SNAPSHOT + + + + \ No newline at end of file diff --git a/framework-tests/bellatrix.data.tests/src/main/java/infrastructure/artist/Artist.java b/framework-tests/bellatrix.data.tests/src/main/java/infrastructure/artist/Artist.java new file mode 100644 index 00000000..d132776c --- /dev/null +++ b/framework-tests/bellatrix.data.tests/src/main/java/infrastructure/artist/Artist.java @@ -0,0 +1,23 @@ +package infrastructure.artist; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.SuperBuilder; +import solutions.bellatrix.data.http.infrastructure.HttpEntity; + +@Data +@SuperBuilder +@EqualsAndHashCode(callSuper = true) +public class Artist extends HttpEntity { + @SerializedName("ArtistId") + private String id; + + @SerializedName("Name") + private String name; + + @Override + public String getIdentifier() { + return id; + } +} \ No newline at end of file diff --git a/framework-tests/bellatrix.data.tests/src/main/resources/testFrameworkSettings.dev.json b/framework-tests/bellatrix.data.tests/src/main/resources/testFrameworkSettings.dev.json new file mode 100644 index 00000000..e4b630cd --- /dev/null +++ b/framework-tests/bellatrix.data.tests/src/main/resources/testFrameworkSettings.dev.json @@ -0,0 +1,146 @@ +{ + "troubleshootingSettings": { + "debugInformationEnabled": "true" + }, + "dataSettings": { + "dataSourceType": "HTTP", + "httpSettings": { + "baseUrl": "http://localhost:3001/", + "basePath": "/api", + "urlEncoderEnabled": "false", + "contentType": "application/json", + "headers": [ + { + "name": "Accept", + "value": "application/json" + } + ], + "authentication": { + "method": "Basic", + "options": [ + { + "type": "Bearer", + "token": "{env_http_bearer_token}" + }, + { + "type": "Basic", + "username": "{env_http_username}", + "password": "{env_http_password}" + }, + { + "insertionOrder": "start", + "type": "QueryParameters", + "key": "{env_http_query_key}", + "token": "{env_http_query_token}" + } + ] + } + } + }, + "webSettings": { + "baseUrl": "https://ecommerce-playground.lambdatest.io", + "executionType": "regular", + "defaultBrowser": "chrome", + "defaultLifeCycle": "restart everytime time", + "artificialDelayBeforeAction": "0", + "automaticallyScrollToVisible": "false", + "waitUntilReadyOnElementFound": "false", + "waitForAngular": "false", + "shouldHighlightElements": "true", + "shouldCaptureHttpTraffic": "false", + "screenshotsOnFailEnabled": "false", + "screenshotsSaveLocation": "${user.home}/BELLATRIX/Screenshots", + "videosOnFailEnabled": "false", + "videosSaveLocation": "${user.home}/BELLATRIX/Videos", + "timeoutSettings": { + "elementWaitTimeout": "30", + "pageLoadTimeout": "30", + "scriptTimeout": "1", + "waitForAjaxTimeout": "30", + "sleepInterval": "1", + "waitUntilReadyTimeout": "30", + "waitForJavaScriptAnimationsTimeout": "30", + "waitForAngularTimeout": "30", + "waitForPartialUrl": "30", + "validationsTimeout": "30", + "elementToBeVisibleTimeout": "30", + "elementToExistTimeout": "30", + "elementToNotExistTimeout": "30", + "elementToBeClickableTimeout": "30", + "elementNotToBeVisibleTimeout": "30", + "elementToHaveContentTimeout": "15" + }, + "gridSettings": [ + { + "providerName": "saucelabs", + "url": "http://ondemand.saucelabs.com:80/wd/hub", + "arguments": [ + { + "screenResolution": "1280x800", + "recordVideo": "true", + "recordScreenshots": "true", + "username": "myUserName", + "accessKey": "myPass", + "name": "bellatrix_run" + } + ] + }, + { + "providerName": "browserstack", + "url": "http://hub-cloud.browserstack.com/wd/hub/", + "arguments": [ + { + "resolution": "1280x800", + "browserstack.video": "true", + "browserstack.networkLogs": "true", + "browserstack.debug": "true", + "browserstack.console": "errors", + "browserstack.user": "myUserName", + "browserstack.key": "myPass", + "build": "bellatrix_run" + } + ] + }, + { + "providerName": "crossbrowsertesting", + "url": "http://hub.crossbrowsertesting.com:80/wd/hub", + "arguments": [ + { + "screen_resolution": "1280x800", + "record_video": "true", + "record_network": "true", + "username": "myUserName", + "password": "myPass", + "name": "bellatrix_run" + } + ] + }, + { + "providerName": "selenoid", + "url": "http://127.0.0.1:4444/wd/hub", + "arguments": [ + { + "screenResolution": "1280x800", + "enableVNC": "true", + "enableVideo": "true", + "enableLog": "true", + "name": "bellatrix_run" + } + ] + }, + { + "providerName": "grid", + "url": "http://127.0.0.1:4444/wd/hub", + "arguments": [ + { + "name": "bellatrix_run" + } + ] + } + ] + }, + "urlSettings": { + "shopUrl": "http://demos.bellatrix.solutions/cart/", + "accountUrl": "http://demos.bellatrix.solutions/account/" + } +} \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/README.md b/getting-started/bellatrix.data.getting.started/README.md new file mode 100644 index 00000000..38a26876 --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/README.md @@ -0,0 +1,137 @@ +# Bellatrix Data Module + +## Overview + +The **Bellatrix Data Module** simplifies data management in your tests by providing a clean, type-safe interface for API interactions. + +## Who Is This Guide For? + +Test automation engineers looking to streamline data management in their automation projects. + +## Why Use the Data Module? + +### Simplicity + +> Eliminate boilerplate HTTP request code and focus on your test logic. + +**Traditional Approach:** +```java +HttpClient client = HttpClient.newHttpClient(); +HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:3001/api/artists")) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(gson.toJson(artist))) + .build(); + +HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); +Artist createdArtist = gson.fromJson(response.body(), Artist.class); +``` + +**Bellatrix Data Module Approach:** + +```java +RepositoryFactory.INSTANCE.registerRepository(Artist.class, ArtistRepository.class); +Artist artist = Artist.builder().name("Artist Name").build().create(); +``` + +### Speed +> [!NOTE]\ +> Start writing tests immediately—no need to learn complex HTTP client libraries. + +**Traditional Approach:** +Requires creating an HTTP client, building a request, and then executing it with the client: + +``` java +HttpClient client = HttpClient.newHttpClient(); +HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:3001/api/artists")) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(gson.toJson(artist))) + .build(); + +HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); +``` + +**With Bellatrix Approach:** +Just define your HTTP entity and create a repository instance - no boilerplate required. + +## Core Components + +### HttpEntity + +HttpEntity represents the data objects your API works with. Each entity corresponds to a resource (such as Artist, Album, etc.) and defines the structure and fields of that resource. + +**Key Features:** +- **Type Safety**: Generic type parameters ensure compile-time type checking +- **JSON Serialization**: Built-in support for JSON field mapping with `@SerializedName` +- **Builder Pattern**: Lombok's `@SuperBuilder` enables fluent object creation + +```java +@Data +@SuperBuilder +@EqualsAndHashCode(callSuper = true) +public class Artist extends HttpEntity { + @SerializedName("ArtistId") + private String id; + + @SerializedName("Name") + private String name; + + @Override + public String getIdentifier() { + return id; + } +} +``` + +### HttpRepository + +HttpRepository acts as the client for performing CRUD operations on your entities. It manages HTTP requests and responses, providing a clean interface for data interactions. + +**Key Features:** +- **Generic CRUD Operations**: Built-in methods for Create, Read, Update, Delete +- **Configurable Conversion**: Custom JSON converters for different API formats +- **HTTP Context Management**: Flexible configuration for different endpoints + +```java +public class ArtistRepository extends HttpRepository { + public ArtistRepository() { + super(Artist.class, new JsonConverter(builder -> { + builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE); + }), () -> { + var httpSettings = ConfigurationService.get(DataSettings.class).getHttpSettings(); + var httpContext = new HttpContext(httpSettings); + httpContext.addPathParameter("/artists"); + return httpContext; + }); + } +} +``` + +### Usage Example + +Once you've defined your entity and repository, you can start performing operations immediately: + +```java +// Create repository instance +ArtistRepository artistRepository = new ArtistRepository(); + +// Perform CRUD operations +List artists = artistRepository.getAll(); +Artist newArtist = Artist.builder().name("New Artist").build(); +Artist created = artistRepository.create(newArtist); +``` + +## Extensibility in mind + +The Bellatrix Data Module is designed with extensibility in mind. Explore these guides to learn more: + +### Core Concepts +- **[Create Repository](src/test/java/04_create_repository/ReadMe.md)** - Learn how to create custom repositories for your data entities +- **[HTTP Context Configuration](src/test/java/05_http_context/ReadMe.md)** - Configure HTTP settings and customize requests for your API endpoints +- **[Object Converter](src/test/java/06_object_converter/ReadMe.md)** - Implement custom converters for different data formats (JSON, XML, etc.) + +### Additional Resources +- **[Project Setup](src/test/java/01_project_setup/)** - Initial project configuration +- **[HTTP Settings](src/test/java/02_http_settings/)** - Configure HTTP client settings +- **[Create Entity](src/test/java/03_create_entity/)** - Define your data entities \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/pom.xml b/getting-started/bellatrix.data.getting.started/pom.xml new file mode 100644 index 00000000..89b73cc0 --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + solutions.bellatrix + bellatrix + 1.0-SNAPSHOT + ../../pom.xml + + + bellatrix.data.getting.started + + + 19 + 19 + UTF-8 + + + + + + solutions.bellatrix + bellatrix.data + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/main/java/Artist.java b/getting-started/bellatrix.data.getting.started/src/main/java/Artist.java new file mode 100644 index 00000000..bdd9d90e --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/main/java/Artist.java @@ -0,0 +1,20 @@ +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.SuperBuilder; +import solutions.bellatrix.data.http.infrastructure.HttpEntity; + +@Data +@SuperBuilder +@EqualsAndHashCode(callSuper = true) +public class Artist extends HttpEntity { + @SerializedName("ArtistId") + private String id; + @SerializedName("Name") + private String name; + + @Override + public String getIdentifier() { + return id; + } +} \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/main/java/ArtistRepository.java b/getting-started/bellatrix.data.getting.started/src/main/java/ArtistRepository.java new file mode 100644 index 00000000..09df64b0 --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/main/java/ArtistRepository.java @@ -0,0 +1,22 @@ +import com.google.gson.FieldNamingPolicy; +import solutions.bellatrix.core.configuration.ConfigurationService; +import solutions.bellatrix.data.configuration.DataSettings; +import solutions.bellatrix.data.http.httpContext.HttpContext; +import solutions.bellatrix.data.http.infrastructure.HttpRepository; +import solutions.bellatrix.data.http.infrastructure.JsonConverter; +import solutions.bellatrix.data.http.infrastructure.QueryParameter; + +public class ArtistRepository extends HttpRepository { + public ArtistRepository() { + super(Artist.class, new JsonConverter(builder -> { + builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE); + }), () -> { + var httpSettings = ConfigurationService.get(DataSettings.class).getHttpSettings(); + var httpContext = new HttpContext(httpSettings); + httpContext.addPathParameter("artists"); + httpContext.addQueryParameter(new QueryParameter("username", "testUser")); + return httpContext; + }); + } + +} \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/main/java/ArtistsConverter.java b/getting-started/bellatrix.data.getting.started/src/main/java/ArtistsConverter.java new file mode 100644 index 00000000..5d186f4e --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/main/java/ArtistsConverter.java @@ -0,0 +1,10 @@ +import com.google.gson.FieldNamingPolicy; +import solutions.bellatrix.data.http.infrastructure.JsonConverter; + +public class ArtistsConverter extends JsonConverter { + public ArtistsConverter() { + super(builder -> { + builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE); + }); + } +} \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/main/resources/application.properties b/getting-started/bellatrix.data.getting.started/src/main/resources/application.properties new file mode 100644 index 00000000..1f94aee3 --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/main/resources/application.properties @@ -0,0 +1,15 @@ +# +# Copyright 2022 Automate The Planet Ltd. +# Author: Anton Angelov +# Licensed under the Apache License, Version 2.0 (the "License"); +# You may not use this file except in compliance with the License. +# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +environment=test +buildName={randomNumber} \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/main/resources/testFrameworkSettings.test.json b/getting-started/bellatrix.data.getting.started/src/main/resources/testFrameworkSettings.test.json new file mode 100644 index 00000000..e4b630cd --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/main/resources/testFrameworkSettings.test.json @@ -0,0 +1,146 @@ +{ + "troubleshootingSettings": { + "debugInformationEnabled": "true" + }, + "dataSettings": { + "dataSourceType": "HTTP", + "httpSettings": { + "baseUrl": "http://localhost:3001/", + "basePath": "/api", + "urlEncoderEnabled": "false", + "contentType": "application/json", + "headers": [ + { + "name": "Accept", + "value": "application/json" + } + ], + "authentication": { + "method": "Basic", + "options": [ + { + "type": "Bearer", + "token": "{env_http_bearer_token}" + }, + { + "type": "Basic", + "username": "{env_http_username}", + "password": "{env_http_password}" + }, + { + "insertionOrder": "start", + "type": "QueryParameters", + "key": "{env_http_query_key}", + "token": "{env_http_query_token}" + } + ] + } + } + }, + "webSettings": { + "baseUrl": "https://ecommerce-playground.lambdatest.io", + "executionType": "regular", + "defaultBrowser": "chrome", + "defaultLifeCycle": "restart everytime time", + "artificialDelayBeforeAction": "0", + "automaticallyScrollToVisible": "false", + "waitUntilReadyOnElementFound": "false", + "waitForAngular": "false", + "shouldHighlightElements": "true", + "shouldCaptureHttpTraffic": "false", + "screenshotsOnFailEnabled": "false", + "screenshotsSaveLocation": "${user.home}/BELLATRIX/Screenshots", + "videosOnFailEnabled": "false", + "videosSaveLocation": "${user.home}/BELLATRIX/Videos", + "timeoutSettings": { + "elementWaitTimeout": "30", + "pageLoadTimeout": "30", + "scriptTimeout": "1", + "waitForAjaxTimeout": "30", + "sleepInterval": "1", + "waitUntilReadyTimeout": "30", + "waitForJavaScriptAnimationsTimeout": "30", + "waitForAngularTimeout": "30", + "waitForPartialUrl": "30", + "validationsTimeout": "30", + "elementToBeVisibleTimeout": "30", + "elementToExistTimeout": "30", + "elementToNotExistTimeout": "30", + "elementToBeClickableTimeout": "30", + "elementNotToBeVisibleTimeout": "30", + "elementToHaveContentTimeout": "15" + }, + "gridSettings": [ + { + "providerName": "saucelabs", + "url": "http://ondemand.saucelabs.com:80/wd/hub", + "arguments": [ + { + "screenResolution": "1280x800", + "recordVideo": "true", + "recordScreenshots": "true", + "username": "myUserName", + "accessKey": "myPass", + "name": "bellatrix_run" + } + ] + }, + { + "providerName": "browserstack", + "url": "http://hub-cloud.browserstack.com/wd/hub/", + "arguments": [ + { + "resolution": "1280x800", + "browserstack.video": "true", + "browserstack.networkLogs": "true", + "browserstack.debug": "true", + "browserstack.console": "errors", + "browserstack.user": "myUserName", + "browserstack.key": "myPass", + "build": "bellatrix_run" + } + ] + }, + { + "providerName": "crossbrowsertesting", + "url": "http://hub.crossbrowsertesting.com:80/wd/hub", + "arguments": [ + { + "screen_resolution": "1280x800", + "record_video": "true", + "record_network": "true", + "username": "myUserName", + "password": "myPass", + "name": "bellatrix_run" + } + ] + }, + { + "providerName": "selenoid", + "url": "http://127.0.0.1:4444/wd/hub", + "arguments": [ + { + "screenResolution": "1280x800", + "enableVNC": "true", + "enableVideo": "true", + "enableLog": "true", + "name": "bellatrix_run" + } + ] + }, + { + "providerName": "grid", + "url": "http://127.0.0.1:4444/wd/hub", + "arguments": [ + { + "name": "bellatrix_run" + } + ] + } + ] + }, + "urlSettings": { + "shopUrl": "http://demos.bellatrix.solutions/cart/", + "accountUrl": "http://demos.bellatrix.solutions/account/" + } +} \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/test/java/01_project_setup/ReadMe.md b/getting-started/bellatrix.data.getting.started/src/test/java/01_project_setup/ReadMe.md new file mode 100644 index 00000000..9bcb62fb --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/test/java/01_project_setup/ReadMe.md @@ -0,0 +1,145 @@ +# Getting Started with Bellatrix Data Module + +## Prerequisites + +### 1. Add Maven Dependency + +To start using the Data Module, add the following dependency to your project's `pom.xml`: + +```xml + + solutions.bellatrix + bellatrix.data + 1.0-SNAPSHOT + +``` + +### 2. Create Configuration File + +Create `testFrameworkSettings.{env}.json` inside the `src/main/resources` or `src/test/resources` folder. + +## Configuration Structure + +The configuration file contains several key sections: + +- **dataSettings**: HTTP data source configuration for API interactions +- **httpSettings**: Nested configuration for HTTP-specific settings + +### Example Configuration File + +```json +{ + "dataSettings": { + "dataSourceType": "HTTP", + "httpSettings": { + "baseUrl": "http://localhost:3001/", + "basePath": "/api", + "urlEncoderEnabled": "false", + "contentType": "application/json", + "headers": [ + { + "name": "Accept", + "value": "application/json" + } + ], + "authentication": { + "method": "Bearer", + "options": [ + { + "type": "Bearer", + "token": "{env_http_bearer_token}" + }, + { + "type": "Basic", + "username": "{env_http_username}", + "password": "{env_http_password}" + }, + { + "insertionOrder": "start", + "type": "QueryParameters", + "key": "{env_http_query_key}", + "token": "{env_http_query_token}" + } + ] + } + } + } +} +``` + +## Key Configuration Sections + +### Data Settings (Required for HTTP APIs) + +- **dataSourceType**: Set to "HTTP" for REST API interactions +- **baseUrl**: Your API's root URL +- **basePath**: Common path prefix for all endpoints +- **authentication**: Support for Bearer tokens, Basic auth, or query parameters +- **headers**: Default headers sent with every request + +### Environment Variables + +Use `{env_variable_name}` syntax for sensitive data: + +- `{env_http_bearer_token}` - Your API bearer token +- `{env_http_username}` - Basic auth username +- `{env_http_password}` - Basic auth password + +## Environment-Specific Configuration + +You can create multiple configuration files for different environments: + +- `testFrameworkSettings.dev.json` - Development environment +- `testFrameworkSettings.test.json` - Test environment +- `testFrameworkSettings.staging.json` - Staging environment +- `testFrameworkSettings.prod.json` - Production environment + +## Environment Configuration + +### 3. Create Application Properties File + +Add an `application.properties` file inside the `/src/main/resources` or `/src/test/resources` folder to specify which configuration environment to use. + +### Environment Mapping + +The `environment` property determines which configuration file the framework will load: + +| Environment Value | Configuration File Loaded | Use Case | +|------------------|---------------------------|----------| +| `dev` | `testFrameworkSettings.dev.json` | Development environment | +| `test` | `testFrameworkSettings.test.json` | Test environment | +| `staging` | `testFrameworkSettings.staging.json` | Staging environment | +| `prod` | `testFrameworkSettings.prod.json` | Production environment | + +### Example Application Properties + +```properties +# Set environment for configuration file selection +environment=test +``` + +> **Note**: Make sure your `testFrameworkSettings.{environment}.json` file exists and matches the environment value you specify. + +## Using Template Configuration + +For faster setup, you can use the provided template configuration file: + +### Setup Steps + +1. **Locate the template**: Find the example configuration file in the `template` folder of the project +2. **Copy the template**: Copy `testFrameworkSettings.template.json` +3. **Place in resources**: Paste the file into your `src/main/resources` or `src/test/resources` folder +4. **Rename appropriately**: Rename to match your environment (e.g., `testFrameworkSettings.dev.json`) +5. **Customize settings**: Update the configuration values to match your environment + +> **Tip**: This template includes all necessary sections with example values, making it easier to get started quickly. + +## Next Steps + +After completing the configuration: + +1. Create your data entities (models) +2. Set up repositories for API interactions +3. Write your first tests + +*Detailed implementation examples will be provided in the following sections.* \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/test/java/01_project_setup/template/application.properties b/getting-started/bellatrix.data.getting.started/src/test/java/01_project_setup/template/application.properties new file mode 100644 index 00000000..14f1a606 --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/test/java/01_project_setup/template/application.properties @@ -0,0 +1 @@ +environment=test \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/test/java/01_project_setup/template/testFrameworkSettings.test.json b/getting-started/bellatrix.data.getting.started/src/test/java/01_project_setup/template/testFrameworkSettings.test.json new file mode 100644 index 00000000..9c31302e --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/test/java/01_project_setup/template/testFrameworkSettings.test.json @@ -0,0 +1,37 @@ +{ + "dataSettings": { + "dataSourceType": "HTTP", + "httpSettings": { + "baseUrl": "http://localhost:3001/", + "basePath": "/api", + "urlEncoderEnabled": "false", + "contentType": "application/json", + "headers": [ + { + "name": "Accept", + "value": "application/json" + } + ], + "authentication": { + "method": "Bearer", + "options": [ + { + "type": "Bearer", + "token": "{env_http_bearer_token}" + }, + { + "type": "Basic", + "username": "{env_http_username}", + "password": "{env_http_password}" + }, + { + "insertionOrder": "start", + "type": "QueryParameters", + "key": "{env_http_query_key}", + "token": "{env_http_query_token}" + } + ] + } + } + } +} \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/test/java/02_http_settings/ReadMe.md b/getting-started/bellatrix.data.getting.started/src/test/java/02_http_settings/ReadMe.md new file mode 100644 index 00000000..e9b32041 --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/test/java/02_http_settings/ReadMe.md @@ -0,0 +1,97 @@ +# HTTP Settings + +## Overview + +This guide explains how to configure HTTP data sources in the Bellatrix framework. The configuration is typically placed in your `testFrameworksSettings.json` file, located in `src/main/resources`. + +## HttpSettings + +The `HttpSettings` class configures HTTP data source settings for your application. Add this configuration to your `testFrameworksSettings.json` file to define base URLs, headers, authentication methods, and other HTTP-specific settings that apply to all repository requests. + +### Configuration Example + +```json +{ + "dataSettings": { + "dataSourceType": "HTTP", + "httpSettings": { + "baseUrl": "http://localhost:3001/", + "basePath": "/api", + "urlEncoderEnabled": "false", + "contentType": "application/json", + "headers": [ + { + "name": "Accept", + "value": "application/json" + } + ], + "authentication": { + "method": "Bearer", + "options": [ + { + "type": "Bearer", + "token": "{env_http_bearer_token}" + }, + { + "type": "Basic", + "username": "{env_http_username}", + "password": "{env_http_password}" + }, + { + "insertionOrder": "start", + "type": "QueryParameters", + "key": "{env_http_query_key}", + "token": "{env_http_query_token}" + } + ] + } + } + } +} +``` + +> **Environment Variables**: Use `{env_variable_name}` syntax to reference environment variables for sensitive data like tokens and passwords. + +### Configuration Properties + +| Property | Description | Example | +|----------|-------------|---------| +| `dataSourceType` | Type of data source (currently supports "HTTP") | `"HTTP"` | +| `baseUrl` | Root URL of your API endpoint | `"http://localhost:3001/"` | +| `basePath` | Path appended to the base URL for all API requests | `"/api"` | +| `urlEncoderEnabled` | Whether to enable URL encoding for requests | `"false"` | +| `contentType` | Content type for requests | `"application/json"` | +| `authentication` | Authentication configuration with multiple method support | See authentication section below | +| `headers` | Default headers included in every request | Custom headers array | + +### Authentication Methods + +The framework supports multiple authentication methods that can be configured simultaneously: + +- **Bearer Token**: Uses authorization header with Bearer token +- **Basic Authentication**: Uses username/password with Basic auth header +- **Query Parameters**: Adds authentication parameters to the URL query string + +**Selecting Default Method:** + +To specify a default authentication method, set the **method** property in the **authentication** section to one of the supported types: "Bearer", "Basic", or "QueryParameters". The framework will apply this method to all requests in your project. + +**Example:** +To use Basic authentication, simply change the method property to "Basic": +```json + "authentication": { + "method": "Basic", + "options": [ + { + "type": "Basic", + "username": "{env_http_username}", + "password": "{env_http_password}" + } + ] + } +``` +>[!CAUTION] +>Ensure you have set the environment variables `http_username` and `http_password` on your machine. While you can hardcode these values directly in the configuration, using environment variables is the recommended approach for security reasons. + +> [!NOTE] +> The current version supports these three authentication methods. Future releases will include additional authentication types and the ability to create custom, project-specific authentication methods. \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/test/java/03_create_entity/ReadMe.md b/getting-started/bellatrix.data.getting.started/src/test/java/03_create_entity/ReadMe.md new file mode 100644 index 00000000..750dc5b1 --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/test/java/03_create_entity/ReadMe.md @@ -0,0 +1,95 @@ +# Entity Classes + +`HttpEntity` is the base class for all data entity classes in your application. + +**Key Features:** + +- Validates whether the entity identifier is properly set, ensuring entities are ready for operations like getById, update, or delete +- Maintains a reference to the HTTP Response object, enabling testing of request-specific properties such as status code, headers, and response body + +All your data entities should extend this class to inherit these capabilities. + +**Example Usage:** + +Let's create an entity class that represents an artist in our music shop application: + +```java +@Data +@SuperBuilder +@EqualsAndHashCode(callSuper = true) +public class Artist extends HttpEntity { + @SerializedName("ArtistId") + private String id; + + @SerializedName("Name") + private String name; + + @Override + public String getIdentifier() { + return id; + } +} +``` + +**Key Points:** + +- The class extends `HttpEntity` +- `@SerializedName` annotations map JSON fields to Java properties for proper serialization +- The `getIdentifier()` method returns the unique identifier used for CRUD operations + + +# Base Entity Class + +`HttpEntity` extends the `Entity` class, which provides the core interface for all data entities regardless of their data source. + +**Key Features:** + +- **Direct Entity Operations**: Perform database/API operations directly on entity instances without requiring separate repository calls +- **Consistent Behavior**: Maintain uniform operation patterns across all entity types in your application +- **Flexible Identifier Types**: Support different identifier types (UUID, String, Integer, etc.) based on your data model requirements +- **Reduced Boilerplate**: Minimize code duplication by embedding CRUD operations directly within entity classes + +This architecture streamlines data management by reducing the lines of code needed to create and manage entities. You can focus on defining your data model while the framework handles the underlying HTTP interactions and operations. + +## Usage Patterns + +There are three main approaches to working with entities and repositories: + +#### Option 1: Explicit Repository Usage (Verbose but Clear) + +Create a repository instance and use it explicitly for operations: + +```java +ArtistRepository artistRepository = new ArtistRepository(); +Artist artist = Artist.builder().name("Arthur Conan Doyle").build(); +Artist createdArtist = artistRepository.create(artist); +``` + +**Benefits:** Clear separation of concerns, explicit repository management + +#### Option 2: Inline Repository Usage (Concise) + +Combine repository creation and operation in a single statement: + +```java +Artist artist = new ArtistRepository().create(Artist.builder().name("J.K.Rowling").build()); +``` + +**Benefits:** Fewer lines of code, good for simple operations + +#### Option 3: Bellatrix Entity-Centric Approach + +Register repositories once and perform operations directly on entities: + +```java +Artist artist = Artist.builder().name("James Clavell").build().create(); + +// Direct operations on the entity +artist.setName("Updated Name"); +artist.update(); // Updates via HTTP call + +// Access response information +artist.getResponse().getNativeResponse(); +``` + +**Benefits:** Most concise, entity-focused design, automatic repository management \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/test/java/04_create_repository/ReadMe.md b/getting-started/bellatrix.data.getting.started/src/test/java/04_create_repository/ReadMe.md new file mode 100644 index 00000000..0dffd2b2 --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/test/java/04_create_repository/ReadMe.md @@ -0,0 +1,102 @@ +# HTTP Repositories + +## Overview + +The `HttpRepository` is the base class for all repositories that interact with your data source via HTTP calls. It provides methods to perform CRUD operations on your data entities. + +## Key Features + +- **Type Safety**: Generic `` allows you to define the specific entity type the repository handles, ensuring type safety and consistency across your data operations +- **Consistent Interface**: Provides a uniform interface for data operations across different repositories +- **HTTP Management**: Handles HTTP-specific details like request context and response handling automatically + +## How It Works + +The `HttpRepository` uses three core components to manage data operations: + +- **repositoryContext** (`HttpContext`): The base context for the repository, initialized in the constructor with settings specific to the data source, such as base path and resource-specific settings +- **requestContext** (`HttpContext`): The context for the current request, which can include headers, query parameters, and other request-specific settings +- **objectConverter** (`ObjectConverter`): An interface that defines methods for converting between strings and Java objects. There is an out-of-the-box implementation for JSON conversion, but you can implement your own converter for other formats (e.g., XML) + +## Request Flow + +1. When the repository is created, `repositoryContext` stores the base context +2. For each request, `requestContext` handles request-specific parameters like headers and query parameters +3. When a response with data is received, `objectConverter` converts the response data into Java objects +4. At the end of the request, the request context is reset to the repository context + +This design enables reusing the repository context as a foundation for all requests while allowing request-specific customization. This approach maintains a single repository instance for all operations, with no overlap between different requests, ensuring that each request can be tailored to its specific needs without affecting others. + +## Example: Creating an Artist Repository + +To demonstrate how to use the `HttpRepository`, let's create a complete example with an `Artist` entity and its corresponding repository. + +### Step 1: Create the Entity Class + +```java +@Data +@SuperBuilder +@EqualsAndHashCode(callSuper = true) +public class Artist extends HttpEntity { + @SerializedName("ArtistId") + private String id; + + @SerializedName("Name") + private String name; + + @Override + public String getIdentifier() { + return id; + } +} +``` + +### Step 2: Create the Repository Class + +Next, create a repository that extends `HttpRepository`: + +```java +public class ArtistRepository extends HttpRepository<¡Artist> { + public ArtistRepository() { + super(Artist.class, new JsonConverter(builder -> { + builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE); + }), () -> { + var httpSettings = ConfigurationService.get(DataSettings.class).getHttpSettings(); + var httpContext = new HttpContext(httpSettings); + httpContext.addPathParameter("/artists"); + return httpContext; + }); + } +} +``` + +**Constructor Parameters Explained:** + +- `Artist.class`: Specifies the entity type for type-safe operations +- `JsonConverter`: Adds a custom JSON converter that handles UpperCamelCase field naming +- `HttpContext`: Configures the HTTP endpoint by combining base settings with the `/artists` path + +### Step 3: Using the Repository + +Now you can perform CRUD operations using your repository: + +```java +ArtistRepository artistRepository = new ArtistRepository(); + +// Get all artists +List artists = artistRepository.getAll(); + +// Create a new artist +Artist newArtist = Artist.builder().name("The Beatles").build(); +Artist createdArtist = artistRepository.create(newArtist); + +// Get artist by ID +Artist artist = artistRepository.getById("123"); + +// Update an artist +artist.setName("The Rolling Stones"); +Artist updatedArtist = artistRepository.update(artist); + +// Delete an artist +artistRepository.delete(artist); +``` \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/test/java/05_http_context/ReadMe.md b/getting-started/bellatrix.data.getting.started/src/test/java/05_http_context/ReadMe.md new file mode 100644 index 00000000..30949979 --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/test/java/05_http_context/ReadMe.md @@ -0,0 +1,73 @@ +# Configuring HTTP Context + +## Overview + +This guide explains how to use `HttpContext` for dynamic configuration and customize requests for your specific API endpoints. + +## Prerequisites + +Before using HttpContext, ensure you have: +- Configured your `testFrameworksSettings.json` file +- Set up your HTTP data source settings +- Created your entity and repository classes + +## HttpContext Class + +The `HttpContext` class acts as a bridge between your base configuration and actual HTTP requests. It encapsulates HTTP operation settings and provides dynamic request configuration capabilities. + +### Key Capabilities + +- **URL Construction**: Combines base settings with dynamic path parameters +- **Request Customization**: Allows per-repository endpoint customization +- **Parameter Management**: Supports both path parameters and query parameters +- **Configuration Extension**: Builds upon base settings from `testFrameworksSettings.json` + +## Dynamic Configuration and URL Construction + +The `HttpContext` takes your base configuration from `testFrameworksSettings.json` and allows repository-specific customization. + +### URL Building Process + +The framework constructs URLs by combining multiple components: + +| Step | Component | Source | Example | +|------|-----------|--------|---------| +| 1 | Base URL | Configuration file | `http://localhost:3001` | +| 2 | Base Path | Configuration file | `/api` | +| 3 | Path Parameters | Dynamic via `addPathParameter()` | `/artists` | +| 4 | **Final URL** | **Combined result** | **`http://localhost:3001/api/artists`** | + +### Repository Implementation Example + +```java +public class ArtistRepository extends HttpRepository { + public ArtistRepository() { + super(Artist.class, new JsonConverter(), () -> { + // Get base HTTP settings from configuration + var httpSettings = ConfigurationService.get(DataSettings.class).getHttpSettings(); + + // Create HttpContext with base settings + var httpContext = new HttpContext(httpSettings); + + // Add repository-specific path parameter + httpContext.addPathParameter("/artists"); + + // The resulting URL will be: {baseUrl}{basePath}/artists + // Example: http://localhost:3001/api/artists + return httpContext; + }); + } +} +``` + +### Additional HttpContext Methods + +```java +// Add multiple path parameters +httpContext.addPathParameter("/artists"); +httpContext.addPathParameter("/123"); // Results in: /api/artists/123 + +// Add query parameters +httpContext.addQueryParameter(new QueryParameter("username", "testUser")); + // Results in: /api/artists?username=testUser +``` \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/test/java/06_object_converter/ReadMe.md b/getting-started/bellatrix.data.getting.started/src/test/java/06_object_converter/ReadMe.md new file mode 100644 index 00000000..27a7def5 --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/test/java/06_object_converter/ReadMe.md @@ -0,0 +1,84 @@ +# ObjectConverter + +The `ObjectConverter` is an interface that defines methods for converting between string and Java objects. The Bellatrix Data Module includes an out-of-the-box implementation for JSON conversion, but you can implement your own converter for other formats like XML. + +## JsonConverter Implementation + +The `JsonConverter` class provides a simple way to convert between Java objects and their JSON string representations using the Gson library. It offers flexible configuration through a `Consumer` parameter in its constructor. + +**Key Features:** + +- Converts objects to JSON strings with `toString()` methods +- Deserializes JSON strings back to Java objects with `fromString()` +- Supports list deserialization with `fromStringToList()` +- Includes error handling for null values and malformed JSON +- Provided default settings which can be seen in `getInstance()` method +- Mechanism for extending or overriding default settings via constructor + +**Configuration Process:** + +1. The constructor accepts a `Consumer` that allows you to customize serialization settings +2. The `getInstance` method creates a `Gson` instance with predefined default settings +3. Your custom consumer function then overrides these defaults with your specific configuration +4. The final configured `Gson` instance handles all serialization and deserialization operations + +### Implementing Custom Converters + +**Custom Configuration Example:** + +If your server returns JSON data in a different format than the default (e.g., using UpperCamelCase instead of lowercase_with_underscores), you can configure the `JsonConverter` to handle this: + +```json +{ + "ArtistId": 281, + "Name": "Artist Name" +} +``` + +### Option 1: Repository-specific Configuration + +For cases where you need to handle a unique format, configure the `JsonConverter` directly in your repository class: + +```java +protected ArtistRepository() { + super(Artist.class, new JsonConverter(builder -> { + builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE); + }), () -> { + var httpSettings = ConfigurationService.get(DataSettings.class).getHttpSettings(); + var httpContext = new HttpContext(httpSettings); + httpContext.addPathParameter("/artists"); + return httpContext; + }); +} +``` + +This configuration tells the `JsonConverter` to use UpperCamelCase field naming, allowing it to properly map JSON fields like "ArtistId" and "Name" to your Java object properties. + +### Option 2: Custom Converter Class + +Create your own custom converter by extending the `JsonConverter` class. This is useful when you have specific rules applicable to multiple entities of the same type: The benefit of this approach is that you can encapsulate the conversion logic in a single class, making it reusable across different repositories as well as providing multiple options at single place. + +```java +public class ArtistsConverter extends JsonConverter { + public ArtistsConverter() { + super(builder -> { + builder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE); + }); + } +} +``` + +Recommended for cases where you have multiple repositories that need to handle the same JSON format. This way, you can reuse the converter across different repositories without duplicating code. + +Then use it in your repository: + +```java +public ArtistRepository() { + super(Artist.class, new ArtistsConverter(), () -> { + var httpSettings = ConfigurationService.get(DataSettings.class).getHttpSettings(); + var httpContext = new HttpContext(httpSettings); + httpContext.addPathParameter("/artists"); + return httpContext; + }); +} +``` \ No newline at end of file diff --git a/getting-started/bellatrix.data.getting.started/src/test/java/BaseTest.java b/getting-started/bellatrix.data.getting.started/src/test/java/BaseTest.java new file mode 100644 index 00000000..188d88d1 --- /dev/null +++ b/getting-started/bellatrix.data.getting.started/src/test/java/BaseTest.java @@ -0,0 +1,14 @@ +import org.junit.jupiter.api.Test; +import solutions.bellatrix.data.configuration.RepositoryFactory; + +public class BaseTest { + + @Test + public void getAllResources_when_sendGetAlRequest() { + RepositoryFactory.INSTANCE.registerRepository(Artist.class, ArtistRepository.class); + var artistRepository = new ArtistRepository(); + + Artist artist = Artist.builder().name("James Clavell").build().create(); + artist.getResponse().getNativeResponse(); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index c7ef85be..c837c072 100644 --- a/pom.xml +++ b/pom.xml @@ -31,9 +31,11 @@ framework-tests/bellatrix.android.tests framework-tests/bellatrix.ios.tests framework-tests/bellatrix.playwright.tests + framework-tests/bellatrix.data.tests getting-started/bellatrix.web.getting.started getting-started/bellatrix.playwright.getting.started + getting-started/bellatrix.data.getting.started @@ -46,7 +48,7 @@ 2.22.2 3.0.0-M5 - 1.18.30 + 1.18.38 ${surefire.junit}