ControllerUtil.java
package se.jobtechdev.personaldatagateway.api.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.lang.NonNull;
import se.jobtechdev.personaldatagateway.api.exception.ApiException;
import se.jobtechdev.personaldatagateway.api.exception.ValueAssignmentException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Predicate;
import static se.jobtechdev.personaldatagateway.api.util.ProblemDetailsFactory.createProblemDetails;
public class ControllerUtil {
private static final Logger logger = LoggerFactory.getLogger(ControllerUtil.class);
private ControllerUtil() {
}
public static <T> Predicate<T> notAmong(Set<T> set) {
return t -> !set.contains(t);
}
@NonNull
public static <T> T throwApiExceptionOnAbsentValue(
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") Optional<T> value,
HttpStatus httpStatus,
String logMessageOnFailure) {
if (Objects.isNull(value) || value.isEmpty()) {
final var apiException = new ApiException(createProblemDetails(httpStatus, logMessageOnFailure));
if (apiException.getProblemDetails().getStatus() >= 500) {
logger.error(logMessageOnFailure, apiException);
}
throw apiException;
}
return value.get();
}
public static <T> T earlyExit(
T value, Predicate<T> check, HttpStatus httpStatus, String messageOnExit) {
try {
if (Boolean.TRUE.equals(check.test(value))) {
final var errorResponse = (messageOnExit != null)
? createProblemDetails(httpStatus, messageOnExit)
: ProblemDetailsFactory.createProblemDetails(httpStatus);
throw new ApiException(errorResponse);
}
return value;
} catch (ApiException apiException) {
if (messageOnExit != null && apiException.getProblemDetails().getStatus() >= 500) {
logger.error(messageOnExit, apiException);
}
throw apiException;
} catch (RuntimeException exception) {
logger.error("Failure when applying earlyExit check condition", exception);
throw new ApiException(ProblemDetailsFactory.createProblemDetails(HttpStatus.INTERNAL_SERVER_ERROR));
}
}
protected static <T> void preventReassignment(T currentValue, T newValue, String valueName) {
if (newValue != null && currentValue != null && !currentValue.equals(newValue)) {
throw ApiExceptionFactory.createApiException(
ProblemDetailsFactory.createProblemDetails(
HttpStatus.CONFLICT,
valueName + " already exist and can not be overwritten with new value"));
}
}
/**
* This method assumes that all fields present in the "input" type has getters that follows camel
* casing naming style. It also assumes that the "target" type has a setter with the same naming
* style.
*
* <p>Example of two compatible types: class Input { String val; public String getVal() { return
* val; } } class Target { String val; public String getVal() { return val; } public void setVal()
* { this.val = val; } }
*
* <p>This method will only set fields in the "target" instance if the "input" field is non-null
* and different from the "target" field.
*/
public static <I, T> T assignNonNull(I input, T target) {
return assignNonNull(input, target, List.of());
}
public static <I, T> T assignNonNull(
I input, T target, List<String> preventReassignmentOfFields) {
final var fields = Arrays.stream(input.getClass().getDeclaredFields()).toList();
final var allFieldNames = fields.stream().map(Field::getName).toList();
return assignNonNull(input, target, allFieldNames, preventReassignmentOfFields);
}
public static <I, T> T assignNonNull(
I input, T target, List<String> whitelistedFields, List<String> preventReassignmentOfFields) {
final var fields = Arrays.stream(input.getClass().getDeclaredFields()).toList();
for (final var field : fields) {
try {
final var fieldName = field.getName();
final var fieldNamePascalCase =
fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
if (!whitelistedFields.contains(fieldName)) continue;
final var getterName = "get" + fieldNamePascalCase;
final var setterName = "set" + fieldNamePascalCase;
final var inputGetter = input.getClass().getMethod(getterName);
final var targetGetter = target.getClass().getMethod(getterName);
final var targetSetter = target.getClass().getMethod(setterName, field.getType());
final var inputValue = inputGetter.invoke(input);
final var targetValue = targetGetter.invoke(target);
if (preventReassignmentOfFields.contains(fieldName))
preventReassignment(targetValue, inputValue, fieldName);
if (inputValue != null && !inputValue.equals(targetValue)) {
targetSetter.invoke(target, inputValue);
}
} catch (Exception e) {
if (e instanceof ApiException apiException) {
throw apiException;
}
throw new ValueAssignmentException(
"Failed to assign non-null values to target instance!", e);
}
}
return target;
}
public static ClientAndRole loggedInClientAndRole() {
final var clientId = AuthUtil.getLoggedInClientId();
final var role = AuthUtil.getLoggedInRole();
return new ClientAndRole(clientId, role);
}
public record ClientAndRole(String clientId, String role) {
}
}