Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,4 @@ public static String getFileAsString(String fileName) {
return "";
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@
public abstract class ObjectFactory {
protected static <T> T newInstance(Class<T> clazz, Object... args) throws InvocationTargetException, InstantiationException, IllegalAccessException, ConstructorNotFoundException {
Constructor<T> suitableConstructor = getSuitableConstructor(clazz, args);

if (suitableConstructor.isVarArgs() && args.length < suitableConstructor.getParameterCount()) {
return (T)suitableConstructor.newInstance(addNullVarArgsTo(args));
} else
return (T)suitableConstructor.newInstance(args);
try {
if (suitableConstructor.isVarArgs() && args.length < suitableConstructor.getParameterCount()) {
return (T)suitableConstructor.newInstance(addNullVarArgsTo(args));
} else
return (T)suitableConstructor.newInstance(args);
} catch (Exception e) {
var exception = (InvocationTargetException)e;
throw new InvocationTargetException(exception.getTargetException());
}
}

private static Object[] addNullVarArgsTo(Object... args) {
Expand Down Expand Up @@ -77,7 +81,7 @@ private static <T> Constructor<T> findVarArgsConstructor(Class clazz, Class[] ar
args[args.length - 1] = getVarArgsType(clazz, argumentTypes);

return clazz.getDeclaredConstructor(args);
} catch (NoSuchMethodException|NullPointerException ignored) {
} catch (NoSuchMethodException | NullPointerException ignored) {
}

throw new NoSuchMethodException("No matching constructor found for the provided argument types.");
Expand All @@ -101,7 +105,7 @@ private static boolean lengthMatches(Parameter[] parameters, Class[] argumentTyp
}

private static boolean parameterTypesMatch(Parameter[] parameters, Class[] argumentTypes) {
for (int i = 0; i < argumentTypes.length; i ++) {
for (int i = 0; i < argumentTypes.length; i++) {
if (!parameters[i].getType().equals(argumentTypes[i]))
return false;
}
Expand Down Expand Up @@ -138,4 +142,4 @@ public ConstructorNotFoundException(String message, Throwable cause, boolean ena
super(message, cause, enableSuppression, writableStackTrace);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ public static <T> T getInstance(Class<T> classOf, Object... initargs) {
private static <T> T tryGetInstance(Class<T> classOf, Object... initargs) {
try {
return newInstance(classOf, initargs);
} catch (InvocationTargetException | InstantiationException | IllegalAccessException |
} catch (InvocationTargetException e) {
Log.error(e.getTargetException().getMessage(), e);
return null;
} catch (InstantiationException | IllegalAccessException |
ConstructorNotFoundException e) {
Log.error("Failed to create instance of the class %s.\nException was:\n%s".formatted(classOf.getName(), e));
Log.error("Failed to create instance of the class %s.\nException was:\n%s".formatted(classOf.getName(), e.getMessage()));
return null;
}
}
Expand All @@ -62,8 +65,8 @@ public static boolean containsKey(Class<?> classOf) {
public static boolean containsValue(Object object) {
return mapHolder.get().containsValue(object);
}

public static void clear() {
mapHolder.get().clear();
}
}
}
48 changes: 48 additions & 0 deletions bellatrix.data/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>solutions.bellatrix</groupId>
<artifactId>bellatrix</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<artifactId>bellatrix.data</artifactId>

<properties>
<maven.compiler.source>19</maven.compiler.source>
<maven.compiler.target>19</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<rest.assured.version>5.5.5</rest.assured.version>
<gson.version>2.13.1</gson.version>
</properties>

<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>solutions.bellatrix</groupId>
<artifactId>bellatrix.core</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>${rest.assured.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package solutions.bellatrix.data.configuration;

import com.google.gson.annotations.SerializedName;
import lombok.Data;
import solutions.bellatrix.data.http.configuration.HttpSettings;

@Data
public class DataSettings {
@SerializedName("dataSourceType")
private String datasourceType;
@SerializedName("httpSettings")
private HttpSettings httpSettings;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package solutions.bellatrix.data.configuration;

import solutions.bellatrix.core.utilities.SingletonFactory;
import solutions.bellatrix.data.contracts.Repository;
import solutions.bellatrix.data.http.infrastructure.Entity;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

public enum RepositoryFactory {
INSTANCE;

private final Map<Class<? extends Entity>, Class<? extends Repository>> repositories = new ConcurrentHashMap<>();

public <T extends Entity> void registerRepository(Class<T> entityClass, Class<? extends Repository<T>> repositoryClass) {
repositories.put(entityClass, repositoryClass);
}

public <T extends Entity> Repository<T> getRepository(Class<T> entityClass) {
var repositoryClassType = repositories.get(entityClass);

if (Objects.isNull(repositoryClassType)) {
throw new IllegalArgumentException("No repository registered for entity class: " + entityClass.getName());
}

return (Repository<T>)SingletonFactory.getInstance(repositoryClassType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package solutions.bellatrix.data.contracts;

import solutions.bellatrix.data.http.infrastructure.Entity;

import java.util.List;

public interface Repository<T extends Entity> {
T getById(T entity);

List<T> getAll();

T create(T entity);

T update(T entity);

void delete(T entity);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package solutions.bellatrix.data.http.authentication;

import io.restassured.authentication.AuthenticationScheme;
import io.restassured.authentication.BasicAuthScheme;
import io.restassured.authentication.NoAuthScheme;
import io.restassured.authentication.PreemptiveOAuth2HeaderScheme;

public class AuthSchemaFactory {
public static AuthenticationScheme getAuthenticationScheme(Authentication authentication) {
var authType = authentication.getAuthenticationMethod();
var option = authentication.getAuthenticationOptions().stream().filter(x -> x.get("type").equals(authType.getMethod())).findFirst();
if (option.isEmpty()) {
throw new IllegalArgumentException("Authentication type not found: %s, Supported types : ".formatted(authType));
}

switch (authType) {
case BASIC -> {
var basicAuth = option.get();
String username = basicAuth.get("username").toString();
String password = basicAuth.get("password").toString();
var basicSchema = new BasicAuthScheme();
basicSchema.setUserName(username);
basicSchema.setPassword(password);
return basicSchema;
}
case BEARER -> {
var bearerAuth = option.get();
String token = bearerAuth.get("token").toString();
var bearerSchema = new PreemptiveOAuth2HeaderScheme();
bearerSchema.setAccessToken(token);
return bearerSchema;
}
case QUERY_PARAMETER -> {
return new NoAuthScheme();
}
default -> throw new IllegalArgumentException("Unsupported authentication type: %s".formatted(authType));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package solutions.bellatrix.data.http.authentication;

import com.google.gson.annotations.SerializedName;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;

import java.util.LinkedHashMap;
import java.util.LinkedList;

public class Authentication {
@SerializedName("method")
private String method;
@SerializedName("options")
@Getter private LinkedList<LinkedHashMap<String, Object>> authenticationOptions;

@Setter(AccessLevel.PRIVATE)
private transient AuthenticationMethod authenticationMethod;

public AuthenticationMethod getAuthenticationMethod() {
setAuthenticationMethod(AuthenticationMethod.parse(method));
return authenticationMethod;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package solutions.bellatrix.data.http.authentication;

import lombok.Getter;

@Getter
public enum AuthenticationMethod {
BEARER("Bearer"),
BASIC("Basic"),
QUERY_PARAMETER("QueryParameters");

private final String method;

AuthenticationMethod(String method) {
this.method = method;
}

public static AuthenticationMethod parse(String y) {
for (var state : values()) {
String enumDisplayValue = state.getMethod();
if (enumDisplayValue != null && enumDisplayValue.equalsIgnoreCase(y)) {
return state;
}
}

throw new IllegalArgumentException("No enum constant with value: %s".formatted(y));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package solutions.bellatrix.data.http.configuration;

import com.google.gson.annotations.SerializedName;
import lombok.Data;
import solutions.bellatrix.core.configuration.ConfigurationService;
import solutions.bellatrix.data.configuration.DataSettings;
import solutions.bellatrix.data.http.authentication.Authentication;
import solutions.bellatrix.data.http.authentication.AuthenticationMethod;

import java.util.function.Consumer;

@Data
public class HttpSettings {
@SerializedName("baseUrl")
private String baseUrl;
@SerializedName("basePath")
private String basePath;
@SerializedName("contentType")
private String contentType;
@SerializedName("authentication")
private Authentication authentication;
@SerializedName("urlEncoderEnabled")
private boolean urlEncoderEnabled;

public HttpSettings(HttpSettings httpSettings) {
setBaseUrl(httpSettings.getBaseUrl());
setBasePath(httpSettings.getBasePath());
setContentType(httpSettings.getContentType());
setUrlEncoderEnabled(httpSettings.isUrlEncoderEnabled());
setAuthentication(httpSettings.getAuthentication());
}

public static HttpSettings custom(Consumer<HttpSettings> httpSettings) {
var settings = ConfigurationService.get(DataSettings.class).getHttpSettings();
Copy link
Collaborator

@n1xan n1xan Jul 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handle the case when such settings are not present in the config - maybe Log warning or throw specific exception, but this will happen in all existing projects, so its better not to break the execution if the module is not used

if (settings == null) {
throw new IllegalStateException("Include the httpSettings section in your config file");

}
httpSettings.accept(settings);
return settings;
}

public AuthenticationMethod getAuthenticationMethod() {
return authentication.getAuthenticationMethod();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package solutions.bellatrix.data.http.contracts;

import java.util.List;

public interface ObjectConverter {
<T> String toString(T object);

<T> T fromString(String data, Class<T> type);

<T> List<T> fromStringToList(String json, Class<T> elementType);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package solutions.bellatrix.data.http.contracts;

import com.google.gson.annotations.SerializedName;
import solutions.bellatrix.data.http.infrastructure.QueryParameter;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Objects;

public interface Queryable {
default LinkedList<QueryParameter> toQueryParams() {
try {
var queryParameters = new LinkedList<QueryParameter>();
Class<?> clazz = this.getClass();

while (clazz!=null) {
Field[] fields = clazz.getDeclaredFields();
Arrays.stream(fields).forEach(x -> x.setAccessible(true));
for (Field field : fields) {
if (field.isAnnotationPresent(SerializedName.class)) {
var queryParameterName = field.getAnnotation(SerializedName.class).value();
var value = field.get(this);
if (Objects.nonNull(value)) {
queryParameters.add(new QueryParameter(queryParameterName, value));
}
}
}

clazz = clazz.getSuperclass();
}

return queryParameters;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Loading