Compare commits

...

10 Commits

16 changed files with 347 additions and 73 deletions

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>kangaroo-and-rabbit</groupId> <groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId> <artifactId>archidata</artifactId>
<version>0.7.0</version> <version>0.7.3</version>
<properties> <properties>
<maven.compiler.version>3.1</maven.compiler.version> <maven.compiler.version>3.1</maven.compiler.version>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** In case of the update parameter with String input to detect null element. */ /** In case of the update parameter with String input to detect null element. */
@Target({ElementType.PARAMETER, ElementType.METHOD}) @Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface AsyncType { public @interface AsyncType {
Class<?> value(); Class<?> value();

View File

@@ -0,0 +1,12 @@
package org.kar.archidata.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** In case of the update parameter with String input to detect null element. */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeScriptProgress {
}

View File

@@ -93,6 +93,7 @@ public class DataResource {
} }
return filePath; return filePath;
} }
public static String getFileData(final UUID uuid) { public static String getFileData(final UUID uuid) {
final String stringUUID = uuid.toString(); final String stringUUID = uuid.toString();
final String part1 = stringUUID.substring(0, 2); final String part1 = stringUUID.substring(0, 2);
@@ -108,6 +109,7 @@ public class DataResource {
filePath += part3; filePath += part3;
return filePath; return filePath;
} }
public static String getFileMetaData(final UUID uuid) { public static String getFileMetaData(final UUID uuid) {
return getFileData(uuid) + ".json"; return getFileData(uuid) + ".json";
} }
@@ -353,13 +355,12 @@ public class DataResource {
return buildStream(filePathName, range, value.mimeType); return buildStream(filePathName, range, value.mimeType);
} }
// @Secured
@GET @GET
@Path("{id}/{name}") @Path("{id}/{name}")
@PermitTokenInURI @PermitTokenInURI
@RolesAllowed("USER") @RolesAllowed("USER")
@Produces(MediaType.APPLICATION_OCTET_STREAM) @Produces(MediaType.APPLICATION_OCTET_STREAM)
@Operation(description = "Get back some data from the data environment (with a beautifull name (permit download with basic name)", tags = "SYSTEM") @Operation(description = "Get back some data from the data environment (with a beautiful name (permit download with basic name)", tags = "SYSTEM")
public Response retrieveDataFull(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range, public Response retrieveDataFull(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range,
@PathParam("id") final UUID id, @PathParam("name") final String name) throws Exception { @PathParam("id") final UUID id, @PathParam("name") final String name) throws Exception {
final GenericContext gc = (GenericContext) sc.getUserPrincipal(); final GenericContext gc = (GenericContext) sc.getUserPrincipal();

View File

@@ -3,19 +3,21 @@ package org.kar.archidata.catcher;
import java.time.Instant; import java.time.Instant;
import java.util.UUID; import java.util.UUID;
import org.kar.archidata.tools.UuidUtils;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
public class RestErrorResponse { public class RestErrorResponse {
public UUID uuid = UUID.randomUUID(); public UUID uuid = UuidUtils.nextUUID();
public String name; // Mandatory for TS generic error
public String message; // Mandatory for TS generic error
public String time; public String time;
public String error;
public String message;
final public int status; final public int status;
final public String statusMessage; final public String statusMessage;
public RestErrorResponse(final Response.Status status, final String time, final String error, final String message) { public RestErrorResponse(final Response.Status status, final String time, final String error, final String message) {
this.time = time; this.time = time;
this.error = error; this.name = error;
this.message = message; this.message = message;
this.status = status.getStatusCode(); this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase(); this.statusMessage = status.getReasonPhrase();
@@ -23,13 +25,15 @@ public class RestErrorResponse {
public RestErrorResponse(final Response.Status status, final String error, final String message) { public RestErrorResponse(final Response.Status status, final String error, final String message) {
this.time = Instant.now().toString(); this.time = Instant.now().toString();
this.error = error; this.name = error;
this.message = message; this.message = message;
this.status = status.getStatusCode(); this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase(); this.statusMessage = status.getReasonPhrase();
} }
public RestErrorResponse(final Response.Status status) { public RestErrorResponse(final Response.Status status) {
this.name = "generic";
this.message = "";
this.time = Instant.now().toString(); this.time = Instant.now().toString();
this.status = status.getStatusCode(); this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase(); this.statusMessage = status.getReasonPhrase();

View File

@@ -835,7 +835,8 @@ public class DataAccess {
UUID uuid = null; UUID uuid = null;
if (generateUUID) { if (generateUUID) {
firstField = false; firstField = false;
uuid = UUID.randomUUID(); // uuid = UUID.randomUUID();
uuid = UuidUtils.nextUUID();
addElement(ps, uuid, iii); addElement(ps, uuid, iii);
iii.inc(); iii.inc();
} }
@@ -889,13 +890,8 @@ public class DataAccess {
if (generatedKeys.next()) { if (generatedKeys.next()) {
if (primaryKeyField.getType() == UUID.class) { if (primaryKeyField.getType() == UUID.class) {
// uniqueSQLUUID = generatedKeys.getObject(1, UUID.class); // uniqueSQLUUID = generatedKeys.getObject(1, UUID.class);
/* /* final Object obj = generatedKeys.getObject(1); final BigInteger bigint = (BigInteger) generatedKeys.getObject(1); uniqueSQLUUID = UuidUtils.asUuid(bigint); final UUID
final Object obj = generatedKeys.getObject(1); * generatedUUID = (UUID) generatedKeys.getObject(1); System.out.println("UUID généré: " + generatedUUID); */
final BigInteger bigint = (BigInteger) generatedKeys.getObject(1);
uniqueSQLUUID = UuidUtils.asUuid(bigint);
final UUID generatedUUID = (UUID) generatedKeys.getObject(1);
System.out.println("UUID généré: " + generatedUUID);
*/
final Object obj = generatedKeys.getObject(1); final Object obj = generatedKeys.getObject(1);
final byte[] tmpid = generatedKeys.getBytes(1); final byte[] tmpid = generatedKeys.getBytes(1);
uniqueSQLUUID = UuidUtils.asUuid(tmpid); uniqueSQLUUID = UuidUtils.asUuid(tmpid);
@@ -925,7 +921,9 @@ public class DataAccess {
} }
// ps.execute(); // ps.execute();
} catch (final SQLException ex) { } catch (final SQLException ex) {
LOGGER.error("Fail SQL request: {}", ex.getMessage());
ex.printStackTrace(); ex.printStackTrace();
throw new DataAccessException("Fail to Insert data in DB : " + ex.getMessage());
} finally { } finally {
entry.close(); entry.close();
} }

View File

@@ -20,6 +20,7 @@ import java.util.Set;
import org.glassfish.jersey.media.multipart.FormDataParam; import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.AsyncType; import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.catcher.RestErrorResponse; import org.kar.archidata.catcher.RestErrorResponse;
import org.kar.archidata.dataAccess.DataFactoryZod.ClassElement; import org.kar.archidata.dataAccess.DataFactoryZod.ClassElement;
import org.kar.archidata.dataAccess.DataFactoryZod.GeneratedTypes; import org.kar.archidata.dataAccess.DataFactoryZod.GeneratedTypes;
@@ -56,7 +57,7 @@ public class DataFactoryTsApi {
/** /**
* API of the server (auto-generated code) * API of the server (auto-generated code)
*/ */
import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools" import { HTTPMimeType, HTTPRequestModel, ModelResponseHttp, RESTConfig, RESTCallbacks, RESTRequestJson, RESTRequestJsonArray, RESTRequestVoid } from "./rest-tools"
import { """; import { """;
for (final Class<?> clazz : classs) { for (final Class<?> clazz : classs) {
@@ -125,6 +126,14 @@ public class DataFactoryTsApi {
return Arrays.asList(((Produces) annotation[0]).value()); return Arrays.asList(((Produces) annotation[0]).value());
} }
public static boolean apiAnnotationTypeScriptProgress(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(TypeScriptProgress.class);
if (annotation.length == 0) {
return false;
}
return true;
}
public static List<String> apiAnnotationProduces(final Class<?> clazz, final Method method) throws Exception { public static List<String> apiAnnotationProduces(final Class<?> clazz, final Method method) throws Exception {
final List<String> data = apiAnnotationProduces(method); final List<String> data = apiAnnotationProduces(method);
if (data != null) { if (data != null) {
@@ -199,6 +208,7 @@ public class DataFactoryTsApi {
} }
return ((AsyncType) annotation[0]).value(); return ((AsyncType) annotation[0]).value();
} }
public static Class<?> apiAnnotationGetAsyncType(final Method element) throws Exception { public static Class<?> apiAnnotationGetAsyncType(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(AsyncType.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(AsyncType.class);
if (annotation.length == 0) { if (annotation.length == 0) {
@@ -260,12 +270,13 @@ public class DataFactoryTsApi {
if (methodDescription != null) { if (methodDescription != null) {
LOGGER.trace(" description: {}", methodDescription); LOGGER.trace(" description: {}", methodDescription);
} }
final boolean needGenerateProgress = apiAnnotationTypeScriptProgress(method);
Class<?> returnTypeModel = apiAnnotationGetAsyncType(method); Class<?> returnTypeModel = apiAnnotationGetAsyncType(method);
if (returnTypeModel == null) { if (returnTypeModel == null) {
returnTypeModel = method.getReturnType(); returnTypeModel = method.getReturnType();
} }
boolean isUnmanagedReturnType = false; boolean isUnmanagedReturnType = false;
if (returnTypeModel == Response.class ) { if (returnTypeModel == Response.class) {
isUnmanagedReturnType = true; isUnmanagedReturnType = true;
returnTypeModel = Void.class; returnTypeModel = Void.class;
} }
@@ -386,6 +397,9 @@ public class DataFactoryTsApi {
} else if (formDataParams.size() != 0) { } else if (formDataParams.size() != 0) {
builder.append(" data,"); builder.append(" data,");
} }
if (needGenerateProgress) {
builder.append(" callback,");
}
builder.append(" }: {"); builder.append(" }: {");
builder.append("\n\t\trestConfig: RESTConfig,"); builder.append("\n\t\trestConfig: RESTConfig,");
if (!queryParams.isEmpty()) { if (!queryParams.isEmpty()) {
@@ -453,6 +467,9 @@ public class DataFactoryTsApi {
} }
builder.append(","); builder.append(",");
} }
if (needGenerateProgress) {
builder.append("\n\t\tcallback?: RESTCallbacks,");
}
builder.append("\n\t}): Promise<"); builder.append("\n\t}): Promise<");
builder.append(tmpReturn.tsTypeName); builder.append(tmpReturn.tsTypeName);
if (returnModelIsArray) { if (returnModelIsArray) {
@@ -518,6 +535,9 @@ public class DataFactoryTsApi {
} else if (formDataParams.size() != 0) { } else if (formDataParams.size() != 0) {
builder.append("\n\t\t\tdata,"); builder.append("\n\t\t\tdata,");
} }
if (needGenerateProgress) {
builder.append("\n\t\t\tcallback,");
}
builder.append("\n\t\t}"); builder.append("\n\t\t}");
if (tmpReturn.tsCheckType != null) { if (tmpReturn.tsCheckType != null) {
builder.append(", "); builder.append(", ");
@@ -551,16 +571,14 @@ public class DataFactoryTsApi {
myWriter = new FileWriter(pathPackage + File.separator + "index.ts"); myWriter = new FileWriter(pathPackage + File.separator + "index.ts");
myWriter.write(index.toString()); myWriter.write(index.toString());
myWriter.close(); myWriter.close();
final InputStream ioStream = DataFactoryTsApi.class final InputStream ioStream = DataFactoryTsApi.class.getClassLoader().getResourceAsStream("rest-tools.ts");
.getClassLoader()
.getResourceAsStream("rest-tools.ts");
if (ioStream == null) { if (ioStream == null) {
throw new IllegalArgumentException("rest-tools.ts is not found"); throw new IllegalArgumentException("rest-tools.ts is not found");
} }
final BufferedReader buffer = new BufferedReader(new InputStreamReader(ioStream)); final BufferedReader buffer = new BufferedReader(new InputStreamReader(ioStream));
myWriter = new FileWriter(pathPackage + File.separator + "rest-tools.ts"); myWriter = new FileWriter(pathPackage + File.separator + "rest-tools.ts");
String line; String line;
while( (line = buffer.readLine()) != null) { while ((line = buffer.readLine()) != null) {
myWriter.write(line); myWriter.write(line);
myWriter.write("\n"); myWriter.write("\n");
} }

View File

@@ -97,7 +97,7 @@ public class DataFactoryZod {
out.append("zod.enum(["); out.append("zod.enum([");
for (final Object elem : arr) { for (final Object elem : arr) {
if (!first) { if (!first) {
out.append(", \n\t"); out.append(",\n\t");
} else { } else {
out.append("\n\t"); out.append("\n\t");
first = false; first = false;
@@ -106,14 +106,18 @@ public class DataFactoryZod {
out.append(elem.toString()); out.append(elem.toString());
out.append("'"); out.append("'");
} }
out.append("\n\t])"); if (first) {
out.append("]}");
} else {
out.append("\n\t])");
}
} else { } else {
element.isEnum = true; element.isEnum = true;
boolean first = true; boolean first = true;
out.append("{"); out.append("{");
for (final Object elem : arr) { for (final Object elem : arr) {
if (!first) { if (!first) {
out.append(", \n\t"); out.append(",\n\t");
} else { } else {
out.append("\n\t"); out.append("\n\t");
first = false; first = false;
@@ -123,7 +127,11 @@ public class DataFactoryZod {
out.append(elem.toString()); out.append(elem.toString());
out.append("'"); out.append("'");
} }
out.append("}"); if (first) {
out.append("}");
} else {
out.append(",\n\t}");
}
} }
element.declaration = out.toString(); element.declaration = out.toString();
previous.addOrder(element); previous.addOrder(element);

View File

@@ -145,6 +145,7 @@ public class AddOnDataJson implements DataAccessAddOn {
final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception { final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception {
DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, JsonValue.class); DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, JsonValue.class);
} }
public static void addLink(final Class<?> clazz, final Long id, final String column, final Long remoteKey) throws Exception { public static void addLink(final Class<?> clazz, final Long id, final String column, final Long remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversLongLong data = DataAccess.get(TableCoversLongLong.class, id, new OverrideTableName(tableName)); final TableCoversLongLong data = DataAccess.get(TableCoversLongLong.class, id, new OverrideTableName(tableName));
@@ -174,6 +175,7 @@ public class AddOnDataJson implements DataAccessAddOn {
data.covers.add(remoteKey); data.covers.add(remoteKey);
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
} }
public static void addLink(final Class<?> clazz, final UUID id, final String column, final UUID remoteKey) throws Exception { public static void addLink(final Class<?> clazz, final UUID id, final String column, final UUID remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, id, new OverrideTableName(tableName)); final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, id, new OverrideTableName(tableName));
@@ -188,6 +190,7 @@ public class AddOnDataJson implements DataAccessAddOn {
data.covers.add(remoteKey); data.covers.add(remoteKey);
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
} }
public static void addLink(final Class<?> clazz, final UUID id, final String column, final Long remoteKey) throws Exception { public static void addLink(final Class<?> clazz, final UUID id, final String column, final Long remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, id, new OverrideTableName(tableName)); final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, id, new OverrideTableName(tableName));
@@ -202,6 +205,7 @@ public class AddOnDataJson implements DataAccessAddOn {
data.covers.add(remoteKey); data.covers.add(remoteKey);
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
} }
public static void removeLink(final Class<?> clazz, final UUID id, final String column, final Long remoteKey) throws Exception { public static void removeLink(final Class<?> clazz, final UUID id, final String column, final Long remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, id, new OverrideTableName(tableName)); final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, id, new OverrideTableName(tableName));
@@ -218,6 +222,7 @@ public class AddOnDataJson implements DataAccessAddOn {
data.covers = newList; data.covers = newList;
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
} }
public static void removeLink(final Class<?> clazz, final UUID id, final String column, final UUID remoteKey) throws Exception { public static void removeLink(final Class<?> clazz, final UUID id, final String column, final UUID remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, id, new OverrideTableName(tableName)); final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, id, new OverrideTableName(tableName));
@@ -234,6 +239,7 @@ public class AddOnDataJson implements DataAccessAddOn {
data.covers = newList; data.covers = newList;
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
} }
public static void removeLink(final Class<?> clazz, final Long id, final String column, final Long remoteKey) throws Exception { public static void removeLink(final Class<?> clazz, final Long id, final String column, final Long remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversLongLong data = DataAccess.get(TableCoversLongLong.class, id, new OverrideTableName(tableName)); final TableCoversLongLong data = DataAccess.get(TableCoversLongLong.class, id, new OverrideTableName(tableName));
@@ -250,6 +256,7 @@ public class AddOnDataJson implements DataAccessAddOn {
data.covers = newList; data.covers = newList;
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
} }
public static void removeLink(final Class<?> clazz, final Long id, final String column, final UUID remoteKey) throws Exception { public static void removeLink(final Class<?> clazz, final Long id, final String column, final UUID remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversLongUUID data = DataAccess.get(TableCoversLongUUID.class, id, new OverrideTableName(tableName)); final TableCoversLongUUID data = DataAccess.get(TableCoversLongUUID.class, id, new OverrideTableName(tableName));
@@ -266,22 +273,9 @@ public class AddOnDataJson implements DataAccessAddOn {
data.covers = newList; data.covers = newList;
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
} }
/* /* public static <TYPE> void addLink(final Class<TYPE> clazz, final Object localKey, final String column, final Object remoteKey) throws Exception { final String tableName =
public static <TYPE> void addLink(final Class<TYPE> clazz, final Object localKey, final String column, final Object remoteKey) throws Exception { * AnnotationTools.getTableName(clazz); final TYPE data = DataAccess.get(clazz, localKey); // TODO: add filter of the "column" // find the field column: // add the remoteKey in the list: // post
final String tableName = AnnotationTools.getTableName(clazz); * new data in the DB final String linkTableName = generateLinkTableName(this.tableName, this.column); final LinkTable insertElement = new LinkTable(this.localKey, this.remoteKey);
final TYPE data = DataAccess.get(clazz, localKey); // TODO: add filter of the "column" * DataAccess.insert(insertElement, new OverrideTableName(linkTableName)); } */
// find the field column:
// add the remoteKey in the list:
// post new data in the DB
final String linkTableName = generateLinkTableName(this.tableName, this.column);
final LinkTable insertElement = new LinkTable(this.localKey, this.remoteKey);
DataAccess.insert(insertElement, new OverrideTableName(linkTableName));
}*/
} }

View File

@@ -5,6 +5,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.Types; import java.sql.Types;
import java.util.List; import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.AnnotationTools; import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.dataAccess.CountInOut; import org.kar.archidata.dataAccess.CountInOut;
@@ -14,6 +15,7 @@ import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter; import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions; import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.exception.DataAccessException; import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.UuidUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -51,10 +53,33 @@ public class AddOnManyToOne implements DataAccessAddOn {
public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii) throws Exception { public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii) throws Exception {
final Object data = field.get(rootObject); final Object data = field.get(rootObject);
if (data == null) { if (data == null) {
ps.setNull(iii.value, Types.BIGINT); if (field.getType() == Long.class) {
ps.setNull(iii.value, Types.BIGINT);
} else if (field.getType() == Integer.class) {
ps.setNull(iii.value, Types.INTEGER);
} else if (field.getType() == Short.class) {
ps.setNull(iii.value, Types.INTEGER);
} else if (field.getType() == String.class) {
ps.setNull(iii.value, Types.VARCHAR);
} else if (field.getType() == UUID.class) {
ps.setNull(iii.value, Types.BINARY);
}
} else if (field.getType() == Long.class) { } else if (field.getType() == Long.class) {
final Long dataLong = (Long) data; final Long dataTyped = (Long) data;
ps.setLong(iii.value, dataLong); ps.setLong(iii.value, dataTyped);
} else if (field.getType() == Integer.class) {
final Integer dataTyped = (Integer) data;
ps.setInt(iii.value, dataTyped);
} else if (field.getType() == Short.class) {
final Short dataTyped = (Short) data;
ps.setShort(iii.value, dataTyped);
} else if (field.getType() == String.class) {
final String dataTyped = (String) data;
ps.setString(iii.value, dataTyped);
} else if (field.getType() == UUID.class) {
final UUID dataTyped = (UUID) data;
final byte[] dataByte = UuidUtils.asBytes(dataTyped);
ps.setBytes(iii.value, dataByte);
} else { } else {
final Field idField = AnnotationTools.getFieldOfId(field.getType()); final Field idField = AnnotationTools.getFieldOfId(field.getType());
final Object uid = idField.get(data); final Object uid = idField.get(data);
@@ -71,7 +96,11 @@ public class AddOnManyToOne implements DataAccessAddOn {
@Override @Override
public boolean canInsert(final Field field) { public boolean canInsert(final Field field) {
if (field.getType() == Long.class) { if (field.getType() == Long.class
|| field.getType() == Integer.class
|| field.getType() == Short.class
|| field.getType() == String.class
|| field.getType() == UUID.class) {
return true; return true;
} }
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@@ -88,7 +117,11 @@ public class AddOnManyToOne implements DataAccessAddOn {
@Override @Override
public boolean canRetrieve(final Field field) { public boolean canRetrieve(final Field field) {
if (field.getType() == Long.class) { if (field.getType() == Long.class
|| field.getType() == Integer.class
|| field.getType() == Short.class
|| field.getType() == String.class
|| field.getType() == UUID.class) {
return true; return true;
} }
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@@ -101,7 +134,11 @@ public class AddOnManyToOne implements DataAccessAddOn {
@Override @Override
public void generateQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, @NotNull final String name, public void generateQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, @NotNull final String name,
@NotNull final CountInOut elemCount, final QueryOptions options) throws Exception { @NotNull final CountInOut elemCount, final QueryOptions options) throws Exception {
if (field.getType() == Long.class) { if (field.getType() == Long.class
|| field.getType() == Integer.class
|| field.getType() == Short.class
|| field.getType() == String.class
|| field.getType() == UUID.class) {
querrySelect.append(" "); querrySelect.append(" ");
querrySelect.append(tableName); querrySelect.append(tableName);
querrySelect.append("."); querrySelect.append(".");
@@ -150,6 +187,39 @@ public class AddOnManyToOne implements DataAccessAddOn {
} }
return; return;
} }
if (field.getType() == Integer.class) {
final Integer foreignKey = rs.getInt(count.value);
count.inc();
if (!rs.wasNull()) {
field.set(data, foreignKey);
}
return;
}
if (field.getType() == Short.class) {
final Short foreignKey = rs.getShort(count.value);
count.inc();
if (!rs.wasNull()) {
field.set(data, foreignKey);
}
return;
}
if (field.getType() == String.class) {
final String foreignKey = rs.getString(count.value);
count.inc();
if (!rs.wasNull()) {
field.set(data, foreignKey);
}
return;
}
if (field.getType() == UUID.class) {
final byte[] tmp = rs.getBytes(count.value);
count.inc();
if (!rs.wasNull()) {
final UUID foreignKey = UuidUtils.asUuid(tmp);
field.set(data, foreignKey);
}
return;
}
final Class<?> objectClass = field.getType(); final Class<?> objectClass = field.getType();
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
if (decorators == null) { if (decorators == null) {

View File

@@ -5,7 +5,7 @@ import java.util.UUID;
public class RESTErrorResponseExeption extends Exception { public class RESTErrorResponseExeption extends Exception {
public UUID uuid; public UUID uuid;
public String time; public String time;
public String error; public String name;
public String message; public String message;
public int status; public int status;
public String statusMessage; public String statusMessage;
@@ -13,16 +13,16 @@ public class RESTErrorResponseExeption extends Exception {
public RESTErrorResponseExeption() { public RESTErrorResponseExeption() {
this.uuid = null; this.uuid = null;
this.time = null; this.time = null;
this.error = null; this.name = null;
this.message = null; this.message = null;
this.status = 0; this.status = 0;
this.statusMessage = null; this.statusMessage = null;
} }
public RESTErrorResponseExeption(final UUID uuid, final String time, final String error, final String message, final int status, final String statusMessage) { public RESTErrorResponseExeption(final UUID uuid, final String time, final String name, final String message, final int status, final String statusMessage) {
this.uuid = uuid; this.uuid = uuid;
this.time = time; this.time = time;
this.error = error; this.name = name;
this.message = message; this.message = message;
this.status = status; this.status = status;
this.statusMessage = statusMessage; this.statusMessage = statusMessage;
@@ -30,7 +30,7 @@ public class RESTErrorResponseExeption extends Exception {
@Override @Override
public String toString() { public String toString() {
return "RESTErrorResponseExeption [uuid=" + this.uuid + ", time=" + this.time + ", error=" + this.error + ", message=" + this.message + ", status=" + this.status + ", statusMessage=" return "RESTErrorResponseExeption [uuid=" + this.uuid + ", time=" + this.time + ", name=" + this.name + ", message=" + this.message + ", status=" + this.status + ", statusMessage="
+ this.statusMessage + "]"; + this.statusMessage + "]";
} }

View File

@@ -211,7 +211,8 @@ public class DataTools {
return data; return data;
} }
public static <CLASS_TYPE, ID_TYPE> Response uploadCover(final Class<CLASS_TYPE> clazz, final ID_TYPE id, String fileName, final InputStream fileInputStream, final FormDataContentDisposition fileMetaData) { public static <CLASS_TYPE, ID_TYPE> Response uploadCover(final Class<CLASS_TYPE> clazz, final ID_TYPE id, String fileName, final InputStream fileInputStream,
final FormDataContentDisposition fileMetaData) {
try { try {
// correct input string stream : // correct input string stream :
fileName = multipartCorrection(fileName); fileName = multipartCorrection(fileName);

View File

@@ -47,7 +47,7 @@ public class RESTApi {
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
try { try {
final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw new RESTErrorResponseExeption(out.uuid, out.time, out.error, out.message, out.status, out.statusMessage); throw out;
} catch (final MismatchedInputException ex) { } catch (final MismatchedInputException ex) {
throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body()); throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body());
} }
@@ -123,7 +123,7 @@ public class RESTApi {
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
try { try {
final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw new RESTErrorResponseExeption(out.uuid, out.time, out.error, out.message, out.status, out.statusMessage); throw out;
} catch (final MismatchedInputException ex) { } catch (final MismatchedInputException ex) {
throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body()); throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body());
} catch (final JsonParseException ex) { } catch (final JsonParseException ex) {
@@ -156,7 +156,7 @@ public class RESTApi {
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
try { try {
final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw new RESTErrorResponseExeption(out.uuid, out.time, out.error, out.message, out.status, out.statusMessage); throw out;
} catch (final MismatchedInputException ex) { } catch (final MismatchedInputException ex) {
throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body()); throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body());
} }
@@ -178,7 +178,7 @@ public class RESTApi {
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
try { try {
final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw new RESTErrorResponseExeption(out.uuid, out.time, out.error, out.message, out.status, out.statusMessage); throw out;
} catch (final MismatchedInputException ex) { } catch (final MismatchedInputException ex) {
throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body()); throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body());
} }

View File

@@ -2,6 +2,10 @@ package org.kar.archidata.tools;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.UUID; import java.util.UUID;
public class UuidUtils { public class UuidUtils {
@@ -25,4 +29,41 @@ public class UuidUtils {
bb.putLong(uuid.getLeastSignificantBits()); bb.putLong(uuid.getLeastSignificantBits());
return bb.array(); return bb.array();
} }
private static class Generator {
private long base;
private final long offset;
private long previous;
public Generator() {
this.offset = System.currentTimeMillis();
// The local method never generate new UUID in the past, then we use the creation function time to prevent 2038 error
final Instant startingUUID = LocalDate.of(2024, 03, 19).atStartOfDay(ZoneOffset.UTC).toInstant();
this.base = startingUUID.until(Instant.now(), ChronoUnit.SECONDS);
final String serveurBaseUUID = System.getenv("UUID_SERVER_ID");
if (serveurBaseUUID != null) {
long serverId = Long.valueOf(serveurBaseUUID);
serverId %= 0xFFFF;
this.base += (serverId << (64 - 16));
} else {
this.base += (1L << (64 - 16));
}
}
public synchronized UUID next() {
long tmp = System.currentTimeMillis();
if (this.previous >= tmp) {
tmp = this.previous + 1;
}
this.previous = tmp;
tmp -= this.offset;
return new UUID(Long.reverseBytes(tmp), this.base);
}
}
private static Generator generator = new Generator();
public static UUID nextUUID() {
return generator.next();
}
} }

View File

@@ -41,7 +41,7 @@ export interface RESTModel {
accept?: HTTPMimeType; accept?: HTTPMimeType;
// Content of the local data. // Content of the local data.
contentType?: HTTPMimeType; contentType?: HTTPMimeType;
// Mode of the TOKEN in urk or Header // Mode of the TOKEN in URL or Header (?token:${tokenInUrl})
tokenInUrl?: boolean; tokenInUrl?: boolean;
} }
@@ -67,22 +67,48 @@ export function isArrayOf<TYPE>(
return true; return true;
} }
export type RESTRequestType = { function isNullOrUndefined(data: any): data is undefined | null {
return data === undefined || data === null;
}
// generic progression callback
export type ProgressCallback = (count: number, total: number) => void;
export interface RESTAbort {
abort?: () => boolean
}
// Rest generic callback have a basic model to upload and download advancement.
export interface RESTCallbacks {
progressUpload?: ProgressCallback,
progressDownload?: ProgressCallback,
abortHandle?: RESTAbort,
};
export interface RESTRequestType {
restModel: RESTModel, restModel: RESTModel,
restConfig: RESTConfig, restConfig: RESTConfig,
data?: any, data?: any,
params?: object, params?: object,
queries?: object, queries?: object,
callback?: RESTCallbacks,
}; };
function removeTrailingSlashes(input: string): string { function removeTrailingSlashes(input: string): string {
if (isNullOrUndefined(input)) {
return "undefined";
}
return input.replace(/\/+$/, ''); return input.replace(/\/+$/, '');
} }
function removeLeadingSlashes(input: string): string { function removeLeadingSlashes(input: string): string {
if (isNullOrUndefined(input)) {
return "";
}
return input.replace(/^\/+/, ''); return input.replace(/^\/+/, '');
} }
export function RESTUrl({ restModel, restConfig, data, params, queries }: RESTRequestType): string { export function RESTUrl({ restModel, restConfig, params, queries }: RESTRequestType): string {
// Create the URL PATH: // Create the URL PATH:
let generateUrl = `${removeTrailingSlashes(restConfig.server)}/${removeLeadingSlashes(restModel.endPoint)}`; let generateUrl = `${removeTrailingSlashes(restConfig.server)}/${removeLeadingSlashes(restModel.endPoint)}`;
if (params !== undefined) { if (params !== undefined) {
@@ -91,7 +117,7 @@ export function RESTUrl({ restModel, restConfig, data, params, queries }: RESTRe
} }
} }
if (queries === undefined && (restConfig.token === undefined || restModel.tokenInUrl !== true)) { if (queries === undefined && (restConfig.token === undefined || restModel.tokenInUrl !== true)) {
return generateUrl; return generateUrl;
} }
const searchParams = new URLSearchParams(); const searchParams = new URLSearchParams();
if (queries !== undefined) { if (queries !== undefined) {
@@ -112,7 +138,92 @@ export function RESTUrl({ restModel, restConfig, data, params, queries }: RESTRe
return generateUrl + "?" + searchParams.toString(); return generateUrl + "?" + searchParams.toString();
} }
export function RESTRequest({ restModel, restConfig, data, params, queries }: RESTRequestType): Promise<ModelResponseHttp> {
export function fetchProgress(generateUrl: string, { method, headers, body }: {
method: HTTPRequestModel,
headers: any,
body: any,
}, { progressUpload, progressDownload, abortHandle }: RESTCallbacks): Promise<Response> {
const xhr = {
io: new XMLHttpRequest()
}
return new Promise((resolve, reject) => {
// Stream the upload progress
if (progressUpload) {
xhr.io.upload.addEventListener("progress", (dataEvent) => {
if (dataEvent.lengthComputable) {
//console.log(` ==> has a progress event: ${dataEvent.loaded} / ${dataEvent.total}`);
progressUpload(dataEvent.loaded, dataEvent.total);
}
});
}
// Stream the download progress
if (progressDownload) {
xhr.io.addEventListener("progress", (dataEvent) => {
if (dataEvent.lengthComputable) {
//console.log(` ==> download progress:: ${dataEvent.loaded} / ${dataEvent.total}`);
progressUpload(dataEvent.loaded, dataEvent.total);
}
});
}
if (abortHandle) {
abortHandle.abort = () => {
if (xhr.io) {
console.log(`Request abort on the XMLHttpRequest: ${generateUrl}`);
xhr.io.abort();
return true;
}
console.log(`Request abort (FAIL) on the XMLHttpRequest: ${generateUrl}`);
return false;
}
}
// Check if we have an internal Fail:
xhr.io.addEventListener('error', () => {
xhr.io = undefined;
reject(new TypeError('Failed to fetch'))
});
// Capture the end of the stream
xhr.io.addEventListener("loadend", () => {
if (xhr.io.readyState !== XMLHttpRequest.DONE) {
//console.log(` ==> READY state`);
return;
}
if (xhr.io.status === 0) {
//the stream has been aborted
reject(new TypeError('Fetch has been aborted'));
return;
}
// Stream is ended, transform in a generic response:
const response = new Response(xhr.io.response, {
status: xhr.io.status,
statusText: xhr.io.statusText
});
const headersArray = xhr.io.getAllResponseHeaders().trim().replaceAll("\r\n", "\n").split('\n');
headersArray.forEach(function (header) {
const firstColonIndex = header.indexOf(':');
if (firstColonIndex !== -1) {
var key = header.substring(0, firstColonIndex).trim();
var value = header.substring(firstColonIndex + 1).trim();
response.headers.set(key, value);
} else {
response.headers.set(header, "");
}
});
xhr.io = undefined;
resolve(response);
});
xhr.io.open(method, generateUrl, true);
if (!isNullOrUndefined(headers)) {
for (const [key, value] of Object.entries(headers)) {
xhr.io.setRequestHeader(key, value as string);
}
}
xhr.io.send(body);
});
}
export function RESTRequest({ restModel, restConfig, data, params, queries, callback }: RESTRequestType): Promise<ModelResponseHttp> {
// Create the URL PATH: // Create the URL PATH:
let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries }); let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries });
let headers: any = {}; let headers: any = {};
@@ -139,13 +250,27 @@ export function RESTRequest({ restModel, restConfig, data, params, queries }: RE
} }
body = formData body = formData
} }
console.log(`Call ${generateUrl}`)
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fetch(generateUrl, { let action: undefined | Promise<Response> = undefined;
method: restModel.requestType, if (isNullOrUndefined(callback)
headers, || (isNullOrUndefined(callback.progressDownload)
body, && isNullOrUndefined(callback.progressUpload)
}).then((response: Response) => { && isNullOrUndefined(callback.abortHandle))) {
// No information needed: call the generic fetch interface
action = fetch(generateUrl, {
method: restModel.requestType,
headers,
body,
});
} else {
// need progression information: call old fetch model (XMLHttpRequest) that permit to keep % upload and % download for HTTP1.x
action = fetchProgress(generateUrl, {
method: restModel.requestType ?? HTTPRequestModel.GET,
headers,
body,
}, callback);
}
action.then((response: Response) => {
if (response.status >= 200 && response.status <= 299) { if (response.status >= 200 && response.status <= 299) {
const contentType = response.headers.get('Content-Type'); const contentType = response.headers.get('Content-Type');
if (restModel.accept !== contentType) { if (restModel.accept !== contentType) {
@@ -190,12 +315,14 @@ export function RESTRequest({ restModel, restConfig, data, params, queries }: RE
status: 999, status: 999,
error: error, error: error,
statusMessage: "Fetch catch error", statusMessage: "Fetch catch error",
message: "http-wrapper.ts detect an error in the fetch request" message: "rest-tools.ts detect an error in the fetch request"
}); });
}); });
}); });
} }
export function RESTRequestJson<TYPE>(request: RESTRequestType, checker: (data: any) => data is TYPE): Promise<TYPE> { export function RESTRequestJson<TYPE>(request: RESTRequestType, checker: (data: any) => data is TYPE): Promise<TYPE> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
RESTRequest(request).then((value: ModelResponseHttp) => { RESTRequest(request).then((value: ModelResponseHttp) => {

View File

@@ -1 +1 @@
0.7.0 0.7.3