[DEV] upgrade library start to be really cool

This commit is contained in:
Edouard DUPIN 2023-12-22 23:33:09 +01:00
parent 3b0c73bd55
commit 1c82fb1a86
19 changed files with 394 additions and 97 deletions

View File

@ -11,7 +11,7 @@
<maven.dependency.version>3.1.1</maven.dependency.version>
<jersey.version>3.1.1</jersey.version>
<jersey.version>3.1.5</jersey.version>
<jaxb.version>2.3.1</jaxb.version>
<istack.version>4.1.1</istack.version>
</properties>

View File

@ -14,7 +14,10 @@ import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
@ -70,6 +73,50 @@ public class AnnotationTools {
return ((DataDefault) annotation[0]).value();
}
public static ManyToOne getManyToOne(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(ManyToOne.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @ManyToOne on " + element.getClass().getCanonicalName());
}
return (ManyToOne) annotation[0];
}
public static DataJson getDataJson(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataJson.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @ManyToOne on " + element.getClass().getCanonicalName());
}
return (DataJson) annotation[0];
}
public static Long getConstraintsMax(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Max.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
}
return ((Max) annotation[0]).value();
}
public static Long getConstraintsMin(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Min.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
}
return ((Min) annotation[0]).value();
}
public static Integer getLimitSize(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) {
@ -289,7 +336,7 @@ public class AnnotationTools {
}
public static boolean isGenericField(final Field elem) throws Exception {
return AnnotationTools.isPrimaryKey(elem) || AnnotationTools.isCreatedAtField(elem) || AnnotationTools.isUpdateAtField(elem);
return AnnotationTools.isPrimaryKey(elem) || AnnotationTools.isCreatedAtField(elem) || AnnotationTools.isUpdateAtField(elem) || AnnotationTools.isDeletedField(elem);
}
public static Field getFieldOfId(final Class<?> clazz) throws Exception {

View File

@ -5,6 +5,11 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.kar.archidata.dataAccess.options.CheckFunctionInterface;
import org.kar.archidata.dataAccess.options.CheckFunctionVoid;
@Target({ ElementType.TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DataJson {}
public @interface DataJson {
Class<? extends CheckFunctionInterface> checker() default CheckFunctionVoid.class;
}

View File

@ -24,6 +24,7 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.security.PermitTokenInURI;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.filter.GenericContext;
import org.kar.archidata.model.Data;
import org.kar.archidata.tools.ConfigBaseVariable;
@ -94,7 +95,7 @@ public class DataResource {
public static Data getWithSha512(final String sha512) {
LOGGER.info("find sha512 = {}", sha512);
try {
return DataAccess.getWhere(Data.class, new QueryCondition("sha512", "=", sha512));
return DataAccess.getWhere(Data.class, new Condition(new QueryCondition("sha512", "=", sha512)));
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();

View File

@ -460,7 +460,7 @@ public class DataAccess {
return null;
}
public static <T> List<T> insertMultiple(final List<T> data, final QueryOptions options) throws Exception {
public static <T> List<T> insertMultiple(final List<T> data, final QueryOption... options) throws Exception {
final List<T> out = new ArrayList<>();
for (final T elem : data) {
final T tmp = insert(elem, options);
@ -469,20 +469,15 @@ public class DataAccess {
return out;
}
public static <T> T insert(final T data) throws Exception {
return insert(data, null);
}
public static <T> T insert(final T data, final QueryOptions options) throws Exception {
public static <T> T insert(final T data, final QueryOption... option) throws Exception {
final Class<?> clazz = data.getClass();
QueryOptions options = new QueryOptions(option);
// External checker of data:
if (options != null) {
final CheckFunction check = options.get(CheckFunction.class);
if (check != null) {
check.getChecker().check(data, AnnotationTools.getFieldsNames(clazz));
}
}
DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
// real add in the BDD:
@ -643,7 +638,7 @@ public class DataAccess {
}
// check the compatibility of the id and the declared ID
final Class<?> typeClass = idField.getType();
if (idKey == typeClass) {
if (idKey.getClass() == typeClass) {
throw new DataAccessException("Request update with the wrong type ...");
}
return new QueryCondition(AnnotationTools.getFieldName(idField), "=", idKey);
@ -658,11 +653,22 @@ public class DataAccess {
* @param jsonData Json data (partial) values to update
* @return the number of object updated
* @throws Exception */
public static <T, ID_TYPE> int updateWithJson(final Class<T> clazz, final ID_TYPE id, final String jsonData, final QueryOptions options) throws Exception {
return updateWhereWithJson(clazz, getTableIdCondition(clazz, id), jsonData, options);
public static <T, ID_TYPE> int updateWithJson(final Class<T> clazz, final ID_TYPE id, final String jsonData, final QueryOption... option) throws Exception {
QueryOptions options = new QueryOptions(option);
Condition condition = options.get(Condition.class);
if (condition != null) {
throw new DataAccessException("request a updateWithJson with a condition");
}
options.add(new Condition(getTableIdCondition(clazz, id)));
return updateWhereWithJson(clazz, jsonData, options.getAllArray());
}
public static <T> int updateWhereWithJson(final Class<T> clazz, final QueryItem condition, final String jsonData, final QueryOptions options) throws Exception {
public static <T> int updateWhereWithJson(final Class<T> clazz, final String jsonData, final QueryOption... option) throws Exception {
QueryOptions options = new QueryOptions(option);
Condition condition = options.get(Condition.class);
if (condition == null) {
throw new DataAccessException("request a updateWhereWithJson without any condition");
}
final ObjectMapper mapper = new ObjectMapper();
// parse the object to be sure the data are valid:
final T data = mapper.readValue(jsonData, clazz);
@ -671,16 +677,12 @@ public class DataAccess {
final List<String> keys = new ArrayList<>();
final var iterator = root.fieldNames();
iterator.forEachRemaining(e -> keys.add(e));
// TODO: set the filter in the Options...
return updateWhere(data, condition, options, keys);
options.add(new FilterValue(keys));
return updateWhere(data, options.getAllArray());
}
public static <T, ID_TYPE> int update(final T data, final ID_TYPE id) throws Exception {
return update(data, id, null);
}
public static <T> int updateWhere(final T data, final QueryOptions options) throws Exception {
return updateWhere(data, options, null);
return update(data, id, AnnotationTools.getFieldsNames(data.getClass()));
}
/** @param <T>
@ -689,8 +691,9 @@ public class DataAccess {
* @param filterValue
* @return the affected rows.
* @throws Exception */
public static <T, ID_TYPE> int update(final T data, final ID_TYPE id, final List<String> filterValue) throws Exception {
return updateWhere(data, new Condition(getTableIdCondition(data.getClass(), id)), filterValue);
public static <T, ID_TYPE> int update(final T data, final ID_TYPE id, final List<String> updateColomn) throws Exception {
QueryOptions options = new QueryOptions(new Condition(getTableIdCondition(data.getClass(), id)), new FilterValue(updateColomn));
return updateWhere(data, options.getAllArray());
}
// il y avait: final List<String> filterValue
@ -712,7 +715,7 @@ public class DataAccess {
if (options != null) {
final CheckFunction check = options.get(CheckFunction.class);
if (check != null) {
check.getChecker().check(data, filterValue);
check.getChecker().check(data, filter.getValues());
}
}
@ -733,10 +736,8 @@ public class DataAccess {
continue;
}
final String name = AnnotationTools.getFieldName(field);
if (filterValue != null) {
if (!filterValue.contains(name)) {
if (!filter.getValues().contains(name)) {
continue;
}
} else if (AnnotationTools.isGenericField(field)) {
continue;
}
@ -778,10 +779,8 @@ public class DataAccess {
continue;
}
final String name = AnnotationTools.getFieldName(field);
if (filterValue != null) {
if (!filterValue.contains(name)) {
if (!filter.getValues().contains(name)) {
continue;
}
} else if (AnnotationTools.isGenericField(field)) {
continue;
}
@ -815,30 +814,43 @@ public class DataAccess {
static void addElement(final PreparedStatement ps, final Object value, final CountInOut iii) throws Exception {
if (value instanceof final Long tmp) {
LOGGER.debug("Inject Long => {}", tmp);
ps.setLong(iii.value, tmp);
} else if (value instanceof final Integer tmp) {
LOGGER.debug("Inject Integer => {}", tmp);
ps.setInt(iii.value, tmp);
} else if (value instanceof final String tmp) {
LOGGER.debug("Inject String => {}", tmp);
ps.setString(iii.value, tmp);
} else if (value instanceof final Short tmp) {
LOGGER.debug("Inject Short => {}", tmp);
ps.setShort(iii.value, tmp);
} else if (value instanceof final Byte tmp) {
LOGGER.debug("Inject Byte => {}", tmp);
ps.setByte(iii.value, tmp);
} else if (value instanceof final Float tmp) {
LOGGER.debug("Inject Float => {}", tmp);
ps.setFloat(iii.value, tmp);
} else if (value instanceof final Double tmp) {
LOGGER.debug("Inject Double => {}", tmp);
ps.setDouble(iii.value, tmp);
} else if (value instanceof final Boolean tmp) {
LOGGER.debug("Inject Boolean => {}", tmp);
ps.setBoolean(iii.value, tmp);
} else if (value instanceof final Timestamp tmp) {
LOGGER.debug("Inject Timestamp => {}", tmp);
ps.setTimestamp(iii.value, tmp);
} else if (value instanceof final Date tmp) {
LOGGER.debug("Inject Date => {}", tmp);
ps.setTimestamp(iii.value, java.sql.Timestamp.from((tmp).toInstant()));
} else if (value instanceof final LocalDate tmp) {
LOGGER.debug("Inject LocalDate => {}", tmp);
ps.setDate(iii.value, java.sql.Date.valueOf(tmp));
} else if (value instanceof final LocalTime tmp) {
LOGGER.debug("Inject LocalTime => {}", tmp);
ps.setTime(iii.value, java.sql.Time.valueOf(tmp));
} else if (value.getClass().isEnum()) {
LOGGER.debug("Inject ENUM => {}", value.toString());
ps.setString(iii.value, value.toString());
} else {
throw new DataAccessException("Not manage type ==> need to add it ...");
@ -921,9 +933,6 @@ public class DataAccess {
@SuppressWarnings("unchecked")
public static <T> List<T> getsWhere(final Class<T> clazz, final QueryOptions options) throws Exception {
Condition condition = options.get(Condition.class);
if (condition == null) {
throw new DataAccessException("request a gets without any condition");
}
final List<LazyGetter> lazyCall = new ArrayList<>();
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
@ -942,7 +951,9 @@ public class DataAccess {
generateSelectField(querySelect, query, clazz, options, count);
querySelect.append(query.toString());
query = querySelect;
if (condition != null) {
condition.whereAppendQuery(query, tableName, options, deletedFieldName);
}
final OrderBy orders = options.get(OrderBy.class);
if (orders != null) {
orders.generateQuerry(query, tableName);
@ -955,7 +966,12 @@ public class DataAccess {
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
final CountInOut iii = new CountInOut(1);
if (condition != null) {
condition.injectQuerry(ps, iii);
}
if (limit != null) {
limit.injectQuerry(ps, iii);
}
// execute the request
final ResultSet rs = ps.executeQuery();
while (rs.next()) {
@ -1008,8 +1024,55 @@ public class DataAccess {
return data;
}
public static <T, ID_TYPE> T get(final Class<T> clazz, final ID_TYPE id) throws Exception {
return get(clazz, id, null);
public static <ID_TYPE> long count(final Class<?> clazz, final ID_TYPE id) throws Exception {
return DataAccess.countWhere(clazz, new Condition(getTableIdCondition(clazz, id)));
}
public static long countWhere(final Class<?> clazz, final QueryOption... option) throws Exception {
QueryOptions options = new QueryOptions(option);
Condition condition = options.get(Condition.class);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
long count = 0;
// real add in the BDD:
try {
StringBuilder query = new StringBuilder();
final String tableName = AnnotationTools.getTableName(clazz, options);
query.append("SELECT COUNT(*) FROM `");
query.append(tableName);
query.append("` ");
if (condition != null) {
condition.whereAppendQuery(query, tableName, options, deletedFieldName);
}
final Limit limit = options.get(Limit.class);
if (limit != null) {
limit.generateQuerry(query, tableName);
}
LOGGER.warn("generate the query: '{}'", query.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
final CountInOut iii = new CountInOut(1);
if (condition != null) {
condition.injectQuerry(ps, iii);
}
if (limit != null) {
limit.injectQuerry(ps, iii);
}
// execute the request
final ResultSet rs = ps.executeQuery();
if (rs.next()) {
count = rs.getLong("count");
}
} catch (final SQLException ex) {
ex.printStackTrace();
throw ex;
} catch (final Exception ex) {
ex.printStackTrace();
} finally {
entry.close();
entry = null;
}
return count;
}
public static <T, ID_TYPE> T get(final Class<T> clazz, final ID_TYPE id, final QueryOption... option) throws Exception {

View File

@ -18,12 +18,15 @@ public class QueryOptions {
private final List<QueryOption> options = new ArrayList<>();
public QueryOptions() {}
public QueryOptions(final QueryOption... elems) {
if (elems == null || elems.length == 0) {
return;
}
Collections.addAll(this.options, elems);
}
public QueryOptions() {}
public void add(final QueryOption option) {
this.options.add(option);
}

View File

@ -186,8 +186,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
final String tableName = AnnotationTools.getTableName(clazz);
final String linkTableName = generateLinkTableName(tableName, column);
final LinkTable insertElement = new LinkTable(localKey, remoteKey);
final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName));
DataAccess.insert(insertElement, options);
DataAccess.insert(insertElement, new OverrideTableName(linkTableName));
}
@ -195,8 +194,8 @@ public class AddOnManyToMany implements DataAccessAddOn {
final String tableName = AnnotationTools.getTableName(clazz);
final String linkTableName = generateLinkTableName(tableName, column);
final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName));
final QueryAnd condition = new QueryAnd(new QueryCondition("object1Id", "=", localKey), new QueryCondition("object2Id", "=", remoteKey));
return DataAccess.deleteWhere(LinkTable.class, condition, options);
options.add(new Condition(new QueryAnd(new QueryCondition("object1Id", "=", localKey), new QueryCondition("object2Id", "=", remoteKey))));
return DataAccess.deleteWhere(LinkTable.class, options.getAllArray());
}
@Override

View File

@ -2,12 +2,19 @@ package org.kar.archidata.dataAccess.options;
import java.util.List;
import org.kar.archidata.annotation.AnnotationTools;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public interface CheckFunctionInterface {
/** This function implementation is design to check if the updated class is valid of not for insertion
* @param baseName NAme of the object to be precise with the use of what fail.
* @param data The object that might be injected.
* @param filterValue List of fields that might be check. If null, then all column must be checked.
* @throws Exception Exception is generate if the data are incorrect. */
void check(Object data, List<String> filterValue) throws Exception;
void check(final String baseName, Object data, List<String> filterValue) throws Exception;
default void checkAll(final String baseName, final Object data) throws Exception {
check(baseName, data, AnnotationTools.getAllFieldsNames(data.getClass()));
}
}

View File

@ -0,0 +1,12 @@
package org.kar.archidata.dataAccess.options;
import java.util.List;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public class CheckFunctionVoid implements CheckFunctionInterface {
@Override
public void check(final String baseName, Object data, List<String> filterValue) {
}
}

View File

@ -12,6 +12,7 @@ import java.util.Map;
import java.util.regex.Pattern;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.exception.DataAccessException;
@ -21,6 +22,7 @@ import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonValue;
import jakarta.persistence.ManyToOne;
import jakarta.validation.constraints.Size;
public class CheckJPA<T> implements CheckFunctionInterface {
@ -34,7 +36,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
* @param data The object that might be injected.
* @param filterValue List of fields that might be check. If null, then all column must be checked.
* @throws Exception Exception is generate if the data are incorrect. */
void check(K data) throws Exception;
void check(final String baseName, final K data) throws Exception;
}
private Map<String, List<CheckInterface<T>>> checking = null;
@ -63,34 +65,155 @@ public class CheckJPA<T> implements CheckFunctionInterface {
for (final Field field : this.clazz.getFields()) {
final String fieldName = AnnotationTools.getFieldName(field);
if (AnnotationTools.isPrimaryKey(field)) {
add(fieldName, (final T data) -> {
throw new InputException(fieldName, "This is a '@Id' (primaryKey) ==> can not be change");
add(fieldName, (final String baseName, final T data) -> {
throw new InputException(baseName + fieldName, "This is a '@Id' (primaryKey) ==> can not be change");
});
}
if (AnnotationTools.getConstraintsNotNull(field)) {
add(fieldName, (final T data) -> {
add(fieldName, (final String baseName, final T data) -> {
if (field.get(data) == null) {
throw new InputException(fieldName, "Can not be null");
throw new InputException(baseName + fieldName, "Can not be null");
}
});
}
if (AnnotationTools.isCreatedAtField(field) || AnnotationTools.isUpdateAtField(field)) {
add(fieldName, (final T data) -> {
throw new InputException(fieldName, "It is forbidden to change this field");
add(fieldName, (final String baseName, final T data) -> {
throw new InputException(baseName + fieldName, "It is forbidden to change this field");
});
}
final Class<?> type = field.getType();
if (type == Long.class || type == long.class) {
final Long maxValue = AnnotationTools.getConstraintsMax(field);
if (maxValue != null) {
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, "Value too height max: " + maxValue);
}
});
}
final Long minValue = AnnotationTools.getConstraintsMax(field);
if (minValue != null) {
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, "Value too Low min: " + minValue);
}
});
}
final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field);
if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) {
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
long count = DataAccess.count(annotationManyToOne.targetEntity(), elem);
if (count == 0) {
throw new InputException(baseName + fieldName, "Foreign element does not exist in the DB:" + elem);
}
});
}
} else if (type == Integer.class || type == int.class) {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
int maxValue = maxValueRoot.intValue();
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, "Value too height max: " + maxValue);
}
});
}
final Long minValueRoot = AnnotationTools.getConstraintsMax(field);
if (minValueRoot != null) {
int minValue = minValueRoot.intValue();
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, "Value too Low min: " + minValue);
}
});
}
} else if (type == Boolean.class || type == boolean.class) {
} else if (type == Float.class || type == float.class) {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
float maxValue = maxValueRoot.floatValue();
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, "Value too height max: " + maxValue);
}
});
}
final Long minValueRoot = AnnotationTools.getConstraintsMax(field);
if (minValueRoot != null) {
float minValue = minValueRoot.floatValue();
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, "Value too Low min: " + minValue);
}
});
}
} else if (type == Double.class || type == double.class) {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
double maxValue = maxValueRoot.doubleValue();
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, "Value too height max: " + maxValue);
}
});
}
final Long minValueRoot = AnnotationTools.getConstraintsMax(field);
if (minValueRoot != null) {
double minValue = minValueRoot.doubleValue();
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, "Value too Low min: " + minValue);
}
});
}
} else if (type == Date.class || type == Timestamp.class) {
} else if (type == LocalDate.class) {
@ -100,59 +223,66 @@ public class CheckJPA<T> implements CheckFunctionInterface {
} else if (type == String.class) {
final int maxSizeString = AnnotationTools.getLimitSize(field);
if (maxSizeString > 0) {
add(fieldName, (final T data) -> {
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final String elemTyped = (String) elem;
if (elemTyped.length() > maxSizeString) {
throw new InputException(fieldName, "Too long size must be <= " + maxSizeString);
throw new InputException(baseName + fieldName, "Too long size must be <= " + maxSizeString);
}
});
}
final Size limitSize = AnnotationTools.getConstraintsSize(field);
if (limitSize != null) {
add(fieldName, (final T data) -> {
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final String elemTyped = (String) elem;
if (elemTyped.length() > limitSize.max()) {
throw new InputException(fieldName, "Too long size (constraints) must be <= " + limitSize.max());
throw new InputException(baseName + fieldName, "Too long size (constraints) must be <= " + limitSize.max());
}
if (elemTyped.length() < limitSize.min()) {
throw new InputException(fieldName, "Too small size (constraints) must be >= " + limitSize.max());
throw new InputException(baseName + fieldName, "Too small size (constraints) must be >= " + limitSize.max());
}
});
}
final String patternString = AnnotationTools.getConstraintsPattern(field);
if (patternString != null) {
final Pattern pattern = Pattern.compile(patternString);
add(fieldName, (final T data) -> {
add(fieldName, (final String baseName, final T data) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final String elemTyped = (String) elem;
if (!pattern.matcher(elemTyped).find()) {
throw new InputException(fieldName, "does not match the required pattern (constraints) must be '" + patternString + "'");
throw new InputException(baseName + fieldName, "does not match the required pattern (constraints) must be '" + patternString + "'");
}
});
}
} else if (type == JsonValue.class) {
final DataJson jsonAnnotation = AnnotationTools.getDataJson(field);
if (jsonAnnotation != null && jsonAnnotation.checker() != CheckFunctionVoid.class) {
// Here if we have an error it crash at start and no new instance after creation...
CheckFunctionInterface instance = jsonAnnotation.checker().getDeclaredConstructor().newInstance();
add(fieldName, (final String baseName, final T data) -> {
instance.checkAll(baseName + fieldName + ".", field.get(data));
});
}
} else if (type.isEnum()) {
// nothing to do.
}
// keep this is last ==> take more time...
if (AnnotationTools.isUnique(field)) {
// Create the request ...
add(fieldName, (final T data) -> {
final Object other = DataAccess.getWhere(this.clazz, new QueryCondition(fieldName, "==", field.get(data)));
add(fieldName, (final String baseName, final T data) -> {
final Object other = DataAccess.getWhere(this.clazz, new Condition(new QueryCondition(fieldName, "==", field.get(data))));
if (other != null) {
throw new InputException(fieldName, "Name already exist in the DB");
throw new InputException(baseName + fieldName, "Name already exist in the DB");
}
});
}
@ -165,7 +295,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
}
@Override
public void check(final Object data, final List<String> filterValue) throws Exception {
public void check(final String baseName, final Object data, final List<String> filterValue) throws Exception {
initialize();
if (!(this.clazz.isAssignableFrom(data.getClass()))) {
throw new DataAccessException("Incompatatyble type of Object" + data.getClass().getCanonicalName());
@ -178,7 +308,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
continue;
}
for (final CheckInterface<T> action : actions) {
action.check(dataCasted);
action.check(baseName, dataCasted);
}
}
checkTyped(dataCasted, filterValue);

View File

@ -15,6 +15,10 @@ public class Condition extends QueryOption {
this.condition = items;
}
public Condition() {
this.condition = null;
}
public void generateQuerry(final StringBuilder query, final String tableName) {
if (this.condition != null) {
this.condition.generateQuerry(query, tableName);

View File

@ -18,6 +18,6 @@ public class CORSFilter implements ContainerResponseFilter {
response.getHeaders().add("Access-Control-Allow-Headers", "*");
// "Origin, content-type, Content-type, Accept, authorization, mime-type, filename");
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD");
}
}

View File

@ -57,7 +57,7 @@ public class MigrationEngine {
try {
List<Migration> data = null;
try {
data = DataAccess.gets(Migration.class, new QueryOptions(QueryOptions.READ_ALL_COLOMN));
data = DataAccess.gets(Migration.class, QueryOptions.READ_ALL_COLOMN);
} catch (final Exception e) {
// Previous version does not have the same timeCode...
data = DataAccess.gets(Migration.class);

View File

@ -13,6 +13,7 @@ import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
import jakarta.validation.constraints.NotNull;
public class GenericData {
@Id
@ -25,11 +26,13 @@ public class GenericData {
@Column(nullable = false)
@Temporal(TemporalType.TIMESTAMP)
@DataComment("Create time of the object")
@NotNull
public Date createdAt = null;
@DataNotRead
@UpdateTimestamp
@Column(nullable = false)
@Temporal(TemporalType.TIMESTAMP)
@DataComment("When update the object")
@NotNull
public Date updatedAt = null;
}

View File

@ -6,6 +6,7 @@ import org.kar.archidata.annotation.DataDeleted;
import org.kar.archidata.annotation.DataNotRead;
import jakarta.persistence.Column;
import jakarta.validation.constraints.NotNull;
public class GenericDataSoftDelete extends GenericData {
@DataNotRead
@ -13,5 +14,6 @@ public class GenericDataSoftDelete extends GenericData {
@DataDefault("'0'")
@DataDeleted
@DataComment("When delete, they are not removed, they are just set in a deleted state")
@NotNull
public Boolean deleted = null;
}

View File

@ -18,6 +18,7 @@ import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryAnd;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.model.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -75,7 +76,7 @@ public class DataTools {
public static Data getWithSha512(final String sha512) {
try {
return DataAccess.getWhere(Data.class, new QueryCondition("sha512", "=", sha512));
return DataAccess.getWhere(Data.class, new Condition(new QueryCondition("sha512", "=", sha512)));
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
@ -85,7 +86,7 @@ public class DataTools {
public static Data getWithId(final long id) {
try {
return DataAccess.getWhere(Data.class, new QueryAnd(List.of(new QueryCondition("deleted", "=", false), new QueryCondition("id", "=", id))));
return DataAccess.getWhere(Data.class, new Condition(new QueryAnd(List.of(new QueryCondition("deleted", "=", false), new QueryCondition("id", "=", id)))));
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
@ -109,15 +110,12 @@ public class DataTools {
final String tmpPath = getTmpFileInData(tmpUID);
final long fileSize = Files.size(Paths.get(tmpPath));
Data out = new Data();
;
try {
out.sha512 = sha512;
out.mimeType = mimeType;
out.size = fileSize;
out = DataAccess.insert(out);
} catch (final SQLException ex) {
ex.printStackTrace();
return null;
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();

View File

@ -118,20 +118,45 @@ public class RESTApi {
}
public <T, U> T put(final Class<T> clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException {
final String body = this.mapper.writeValueAsString(data);
return putJson(clazz, urlOffset, body);
return modelSend("PUT", clazz, urlOffset, data);
}
public <T, U> T putJson(final Class<T> clazz, final String urlOffset, final String body) throws RESTErrorResponseExeption, IOException, InterruptedException {
return modelSendJson("PUT", clazz, urlOffset, body);
}
public <T> T putMap(final Class<T> clazz, final String urlOffset, final Map<String, Object> data) throws RESTErrorResponseExeption, IOException, InterruptedException {
return modelSendMap("PUT", clazz, urlOffset, data);
}
public <T, U> T patch(final Class<T> clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException {
return modelSend("PATCH", clazz, urlOffset, data);
}
public <T, U> T patchJson(final Class<T> clazz, final String urlOffset, final String body) throws RESTErrorResponseExeption, IOException, InterruptedException {
return modelSendJson("PATCH", clazz, urlOffset, body);
}
public <T> T patchMap(final Class<T> clazz, final String urlOffset, final Map<String, Object> data) throws RESTErrorResponseExeption, IOException, InterruptedException {
return modelSendMap("PATCH", clazz, urlOffset, data);
}
protected <T, U> T modelSend(final String model, final Class<T> clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException {
final String body = this.mapper.writeValueAsString(data);
return modelSendJson(model, clazz, urlOffset, body);
}
protected <T, U> T modelSendJson(final String model, final Class<T> clazz, final String urlOffset, final String body) throws RESTErrorResponseExeption, IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient();
// client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1).uri(URI.create(this.baseUrl + urlOffset));
LOGGER.trace("call PUT: {}", URI.create(this.baseUrl + urlOffset));
LOGGER.trace("call {}: {}", model, URI.create(this.baseUrl + urlOffset));
LOGGER.trace("DATA: {}", body);
if (this.token != null) {
requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token);
}
requestBuilding = requestBuilding.header("Content-Type", "application/json");
final HttpRequest request = requestBuilding.PUT(BodyPublishers.ofString(body)).build();
final HttpRequest request = requestBuilding.method(model, BodyPublishers.ofString(body)).build();
final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
@ -143,7 +168,7 @@ public class RESTApi {
return this.mapper.readValue(httpResponse.body(), clazz);
}
public <T> T putMap(final Class<T> clazz, final String urlOffset, final Map<String, Object> data) throws RESTErrorResponseExeption, IOException, InterruptedException {
protected <T> T modelSendMap(final String model, final Class<T> clazz, final String urlOffset, final Map<String, Object> data) throws RESTErrorResponseExeption, IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient();
final String body = this.mapper.writeValueAsString(data);
Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1).uri(URI.create(this.baseUrl + urlOffset));
@ -151,7 +176,7 @@ public class RESTApi {
requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token);
}
requestBuilding = requestBuilding.header("Content-Type", "application/json");
final HttpRequest request = requestBuilding.PUT(BodyPublishers.ofString(body)).build();
final HttpRequest request = requestBuilding.method(model, BodyPublishers.ofString(body)).build();
final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);

View File

@ -91,7 +91,7 @@ public class TestSimpleTable {
@Test
public void testReadAllValuesUnreadable() throws Exception {
// check the full values
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, new QueryOptions(QueryOptions.READ_ALL_COLOMN));
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, QueryOptions.READ_ALL_COLOMN);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
@ -114,7 +114,7 @@ public class TestSimpleTable {
final SimpleTable test = new SimpleTable();
test.data = TestSimpleTable.DATA_INJECTED_2;
DataAccess.update(test, TestSimpleTable.idOfTheObject, List.of("data"));
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, new QueryOptions(QueryOptions.READ_ALL_COLOMN));
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, QueryOptions.READ_ALL_COLOMN);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
Assertions.assertEquals(TestSimpleTable.idOfTheObject, retrieve.id);
@ -139,7 +139,7 @@ public class TestSimpleTable {
public void testReadDeletedObject() throws Exception {
// check if we set get deleted element
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, new QueryOptions(QueryOptions.ACCESS_DELETED_ITEMS));
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, QueryOptions.ACCESS_DELETED_ITEMS);
Assertions.assertNull(retrieve);
}
@ -148,7 +148,7 @@ public class TestSimpleTable {
@Test
public void testReadAllValuesUnreadableOfDeletedObject() throws Exception {
// check if we set get deleted element with all data
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, new QueryOptions(QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN));
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN);
Assertions.assertNull(retrieve);
}

View File

@ -92,7 +92,7 @@ public class TestSimpleTableSoftDelete {
@Test
public void testReadAllValuesUnreadable() throws Exception {
// check the full values
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, new QueryOptions(QueryOptions.READ_ALL_COLOMN));
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, QueryOptions.READ_ALL_COLOMN);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
@ -118,8 +118,7 @@ public class TestSimpleTableSoftDelete {
final SimpleTableSoftDelete test = new SimpleTableSoftDelete();
test.data = TestSimpleTableSoftDelete.DATA_INJECTED_2;
DataAccess.update(test, TestSimpleTableSoftDelete.idOfTheObject, List.of("data"));
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject,
new QueryOptions(QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN));
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
Assertions.assertEquals(TestSimpleTableSoftDelete.idOfTheObject, retrieve.id);
@ -151,7 +150,7 @@ public class TestSimpleTableSoftDelete {
public void testReadDeletedObject() throws Exception {
// check if we set get deleted element
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, new QueryOptions(QueryOptions.ACCESS_DELETED_ITEMS));
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, QueryOptions.ACCESS_DELETED_ITEMS);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
Assertions.assertEquals(TestSimpleTableSoftDelete.idOfTheObject, retrieve.id);
@ -166,8 +165,7 @@ public class TestSimpleTableSoftDelete {
@Test
public void testReadAllValuesUnreadableOfDeletedObject() throws Exception {
// check if we set get deleted element with all data
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject,
new QueryOptions(QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN));
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
Assertions.assertEquals(TestSimpleTableSoftDelete.idOfTheObject, retrieve.id);