Compare commits
	
		
			10 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a82c3e725d | |||
| 08124f212a | |||
| 631999e135 | |||
| 7674d9a299 | |||
| 9cd71ab601 | |||
| 4cb8c84312 | |||
| 5acdbfb0c7 | |||
| d35c83fcbf | |||
| 0fe769a203 | |||
| dbe1b469f6 | 
							
								
								
									
										2
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -3,7 +3,7 @@ | ||||
| 	<modelVersion>4.0.0</modelVersion> | ||||
| 	<groupId>kangaroo-and-rabbit</groupId> | ||||
| 	<artifactId>archidata</artifactId> | ||||
| 	<version>0.7.0</version> | ||||
| 	<version>0.7.3</version> | ||||
| 	<properties> | ||||
| 		<maven.compiler.version>3.1</maven.compiler.version> | ||||
| 		<maven.compiler.source>21</maven.compiler.source> | ||||
|   | ||||
| @@ -6,7 +6,7 @@ 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}) | ||||
| @Target({ ElementType.PARAMETER, ElementType.METHOD }) | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| public @interface AsyncType { | ||||
| 	Class<?> value(); | ||||
|   | ||||
							
								
								
									
										12
									
								
								src/org/kar/archidata/annotation/TypeScriptProgress.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/org/kar/archidata/annotation/TypeScriptProgress.java
									
									
									
									
									
										Normal 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 { | ||||
| } | ||||
| @@ -93,6 +93,7 @@ public class DataResource { | ||||
| 		} | ||||
| 		return filePath; | ||||
| 	} | ||||
|  | ||||
| 	public static String getFileData(final UUID uuid) { | ||||
| 		final String stringUUID = uuid.toString(); | ||||
| 		final String part1 = stringUUID.substring(0, 2); | ||||
| @@ -108,6 +109,7 @@ public class DataResource { | ||||
| 		filePath += part3; | ||||
| 		return filePath; | ||||
| 	} | ||||
|  | ||||
| 	public static String getFileMetaData(final UUID uuid) { | ||||
| 		return getFileData(uuid) + ".json"; | ||||
| 	} | ||||
| @@ -353,13 +355,12 @@ public class DataResource { | ||||
| 		return buildStream(filePathName, range, value.mimeType); | ||||
| 	} | ||||
|  | ||||
| 	// @Secured | ||||
| 	@GET | ||||
| 	@Path("{id}/{name}") | ||||
| 	@PermitTokenInURI | ||||
| 	@RolesAllowed("USER") | ||||
| 	@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, | ||||
| 			@PathParam("id") final UUID id, @PathParam("name") final String name) throws Exception { | ||||
| 		final GenericContext gc = (GenericContext) sc.getUserPrincipal(); | ||||
|   | ||||
| @@ -3,19 +3,21 @@ package org.kar.archidata.catcher; | ||||
| import java.time.Instant; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.tools.UuidUtils; | ||||
|  | ||||
| import jakarta.ws.rs.core.Response; | ||||
|  | ||||
| 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 error; | ||||
| 	public String message; | ||||
| 	final public int status; | ||||
| 	final public String statusMessage; | ||||
|  | ||||
| 	public RestErrorResponse(final Response.Status status, final String time, final String error, final String message) { | ||||
| 		this.time = time; | ||||
| 		this.error = error; | ||||
| 		this.name = error; | ||||
| 		this.message = message; | ||||
| 		this.status = status.getStatusCode(); | ||||
| 		this.statusMessage = status.getReasonPhrase(); | ||||
| @@ -23,13 +25,15 @@ public class RestErrorResponse { | ||||
|  | ||||
| 	public RestErrorResponse(final Response.Status status, final String error, final String message) { | ||||
| 		this.time = Instant.now().toString(); | ||||
| 		this.error = error; | ||||
| 		this.name = error; | ||||
| 		this.message = message; | ||||
| 		this.status = status.getStatusCode(); | ||||
| 		this.statusMessage = status.getReasonPhrase(); | ||||
| 	} | ||||
|  | ||||
| 	public RestErrorResponse(final Response.Status status) { | ||||
| 		this.name = "generic"; | ||||
| 		this.message = ""; | ||||
| 		this.time = Instant.now().toString(); | ||||
| 		this.status = status.getStatusCode(); | ||||
| 		this.statusMessage = status.getReasonPhrase(); | ||||
|   | ||||
| @@ -835,7 +835,8 @@ public class DataAccess { | ||||
| 			UUID uuid = null; | ||||
| 			if (generateUUID) { | ||||
| 				firstField = false; | ||||
| 				uuid = UUID.randomUUID(); | ||||
| 				// uuid = UUID.randomUUID(); | ||||
| 				uuid = UuidUtils.nextUUID(); | ||||
| 				addElement(ps, uuid, iii); | ||||
| 				iii.inc(); | ||||
| 			} | ||||
| @@ -889,13 +890,8 @@ public class DataAccess { | ||||
| 					if (generatedKeys.next()) { | ||||
| 						if (primaryKeyField.getType() == 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 generatedUUID = (UUID) generatedKeys.getObject(1); | ||||
| 							System.out.println("UUID généré: " + generatedUUID); | ||||
| 							 */ | ||||
| 							/* final Object obj = generatedKeys.getObject(1); 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 byte[] tmpid = generatedKeys.getBytes(1); | ||||
| 							uniqueSQLUUID = UuidUtils.asUuid(tmpid); | ||||
| @@ -925,7 +921,9 @@ public class DataAccess { | ||||
| 			} | ||||
| 			// ps.execute(); | ||||
| 		} catch (final SQLException ex) { | ||||
| 			LOGGER.error("Fail SQL request: {}", ex.getMessage()); | ||||
| 			ex.printStackTrace(); | ||||
| 			throw new DataAccessException("Fail to Insert data in DB : " + ex.getMessage()); | ||||
| 		} finally { | ||||
| 			entry.close(); | ||||
| 		} | ||||
|   | ||||
| @@ -20,6 +20,7 @@ import java.util.Set; | ||||
|  | ||||
| import org.glassfish.jersey.media.multipart.FormDataParam; | ||||
| import org.kar.archidata.annotation.AsyncType; | ||||
| import org.kar.archidata.annotation.TypeScriptProgress; | ||||
| import org.kar.archidata.catcher.RestErrorResponse; | ||||
| import org.kar.archidata.dataAccess.DataFactoryZod.ClassElement; | ||||
| import org.kar.archidata.dataAccess.DataFactoryZod.GeneratedTypes; | ||||
| @@ -56,7 +57,7 @@ public class DataFactoryTsApi { | ||||
| 				/** | ||||
| 				 * 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 { """; | ||||
|  | ||||
| 		for (final Class<?> clazz : classs) { | ||||
| @@ -125,6 +126,14 @@ public class DataFactoryTsApi { | ||||
| 		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 { | ||||
| 		final List<String> data = apiAnnotationProduces(method); | ||||
| 		if (data != null) { | ||||
| @@ -199,6 +208,7 @@ public class DataFactoryTsApi { | ||||
| 		} | ||||
| 		return ((AsyncType) annotation[0]).value(); | ||||
| 	} | ||||
|  | ||||
| 	public static Class<?> apiAnnotationGetAsyncType(final Method element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(AsyncType.class); | ||||
| 		if (annotation.length == 0) { | ||||
| @@ -260,12 +270,13 @@ public class DataFactoryTsApi { | ||||
| 			if (methodDescription != null) { | ||||
| 				LOGGER.trace("         description: {}", methodDescription); | ||||
| 			} | ||||
| 			final boolean needGenerateProgress = apiAnnotationTypeScriptProgress(method); | ||||
| 			Class<?> returnTypeModel = apiAnnotationGetAsyncType(method); | ||||
| 			if (returnTypeModel == null) { | ||||
| 				returnTypeModel = method.getReturnType(); | ||||
| 			} | ||||
| 			boolean isUnmanagedReturnType = false; | ||||
| 			if (returnTypeModel == Response.class ) { | ||||
| 			if (returnTypeModel == Response.class) { | ||||
| 				isUnmanagedReturnType = true; | ||||
| 				returnTypeModel = Void.class; | ||||
| 			} | ||||
| @@ -386,6 +397,9 @@ public class DataFactoryTsApi { | ||||
| 			} else if (formDataParams.size() != 0) { | ||||
| 				builder.append(" data,"); | ||||
| 			} | ||||
| 			if (needGenerateProgress) { | ||||
| 				builder.append(" callback,"); | ||||
| 			} | ||||
| 			builder.append(" }: {"); | ||||
| 			builder.append("\n\t\trestConfig: RESTConfig,"); | ||||
| 			if (!queryParams.isEmpty()) { | ||||
| @@ -453,6 +467,9 @@ public class DataFactoryTsApi { | ||||
| 				} | ||||
| 				builder.append(","); | ||||
| 			} | ||||
| 			if (needGenerateProgress) { | ||||
| 				builder.append("\n\t\tcallback?: RESTCallbacks,"); | ||||
| 			} | ||||
| 			builder.append("\n\t}): Promise<"); | ||||
| 			builder.append(tmpReturn.tsTypeName); | ||||
| 			if (returnModelIsArray) { | ||||
| @@ -518,6 +535,9 @@ public class DataFactoryTsApi { | ||||
| 			} else if (formDataParams.size() != 0) { | ||||
| 				builder.append("\n\t\t\tdata,"); | ||||
| 			} | ||||
| 			if (needGenerateProgress) { | ||||
| 				builder.append("\n\t\t\tcallback,"); | ||||
| 			} | ||||
| 			builder.append("\n\t\t}"); | ||||
| 			if (tmpReturn.tsCheckType != null) { | ||||
| 				builder.append(", "); | ||||
| @@ -551,16 +571,14 @@ public class DataFactoryTsApi { | ||||
| 		myWriter = new FileWriter(pathPackage + File.separator + "index.ts"); | ||||
| 		myWriter.write(index.toString()); | ||||
| 		myWriter.close(); | ||||
| 		final InputStream ioStream = DataFactoryTsApi.class | ||||
| 				.getClassLoader() | ||||
| 				.getResourceAsStream("rest-tools.ts"); | ||||
| 		final InputStream ioStream = DataFactoryTsApi.class.getClassLoader().getResourceAsStream("rest-tools.ts"); | ||||
| 		if (ioStream == null) { | ||||
| 			throw new IllegalArgumentException("rest-tools.ts is not found"); | ||||
| 		} | ||||
| 		final BufferedReader buffer = new BufferedReader(new InputStreamReader(ioStream)); | ||||
| 		myWriter = new FileWriter(pathPackage + File.separator + "rest-tools.ts"); | ||||
| 		String line; | ||||
| 		while( (line = buffer.readLine()) != null) { | ||||
| 		while ((line = buffer.readLine()) != null) { | ||||
| 			myWriter.write(line); | ||||
| 			myWriter.write("\n"); | ||||
| 		} | ||||
|   | ||||
| @@ -97,7 +97,7 @@ public class DataFactoryZod { | ||||
| 			out.append("zod.enum(["); | ||||
| 			for (final Object elem : arr) { | ||||
| 				if (!first) { | ||||
| 					out.append(", \n\t"); | ||||
| 					out.append(",\n\t"); | ||||
| 				} else { | ||||
| 					out.append("\n\t"); | ||||
| 					first = false; | ||||
| @@ -106,14 +106,18 @@ public class DataFactoryZod { | ||||
| 				out.append(elem.toString()); | ||||
| 				out.append("'"); | ||||
| 			} | ||||
| 			if (first) { | ||||
| 				out.append("]}"); | ||||
| 			} else { | ||||
| 				out.append("\n\t])"); | ||||
| 			} | ||||
| 		} else { | ||||
| 			element.isEnum = true; | ||||
| 			boolean first = true; | ||||
| 			out.append("{"); | ||||
| 			for (final Object elem : arr) { | ||||
| 				if (!first) { | ||||
| 					out.append(", \n\t"); | ||||
| 					out.append(",\n\t"); | ||||
| 				} else { | ||||
| 					out.append("\n\t"); | ||||
| 					first = false; | ||||
| @@ -123,7 +127,11 @@ public class DataFactoryZod { | ||||
| 				out.append(elem.toString()); | ||||
| 				out.append("'"); | ||||
| 			} | ||||
| 			if (first) { | ||||
| 				out.append("}"); | ||||
| 			} else { | ||||
| 				out.append(",\n\t}"); | ||||
| 			} | ||||
| 		} | ||||
| 		element.declaration = out.toString(); | ||||
| 		previous.addOrder(element); | ||||
|   | ||||
| @@ -145,6 +145,7 @@ public class AddOnDataJson implements DataAccessAddOn { | ||||
| 			final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception { | ||||
| 		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 { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversLongLong data = DataAccess.get(TableCoversLongLong.class, id, new OverrideTableName(tableName)); | ||||
| @@ -174,6 +175,7 @@ public class AddOnDataJson implements DataAccessAddOn { | ||||
| 		data.covers.add(remoteKey); | ||||
| 		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 { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, id, new OverrideTableName(tableName)); | ||||
| @@ -188,6 +190,7 @@ public class AddOnDataJson implements DataAccessAddOn { | ||||
| 		data.covers.add(remoteKey); | ||||
| 		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 { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, id, new OverrideTableName(tableName)); | ||||
| @@ -202,6 +205,7 @@ public class AddOnDataJson implements DataAccessAddOn { | ||||
| 		data.covers.add(remoteKey); | ||||
| 		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 { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, id, new OverrideTableName(tableName)); | ||||
| @@ -218,6 +222,7 @@ public class AddOnDataJson implements DataAccessAddOn { | ||||
| 		data.covers = newList; | ||||
| 		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 { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, id, new OverrideTableName(tableName)); | ||||
| @@ -234,6 +239,7 @@ public class AddOnDataJson implements DataAccessAddOn { | ||||
| 		data.covers = newList; | ||||
| 		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 { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversLongLong data = DataAccess.get(TableCoversLongLong.class, id, new OverrideTableName(tableName)); | ||||
| @@ -250,6 +256,7 @@ public class AddOnDataJson implements DataAccessAddOn { | ||||
| 		data.covers = newList; | ||||
| 		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 { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversLongUUID data = DataAccess.get(TableCoversLongUUID.class, id, new OverrideTableName(tableName)); | ||||
| @@ -266,22 +273,9 @@ public class AddOnDataJson implements DataAccessAddOn { | ||||
| 		data.covers = newList; | ||||
| 		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 = 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 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)); | ||||
|  | ||||
| 	}*/ | ||||
| 	/* public static <TYPE> void addLink(final Class<TYPE> clazz, final Object localKey, final String column, final Object remoteKey) throws Exception { final String tableName = | ||||
| 	 * 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 | ||||
| 	 * 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)); } */ | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import java.sql.PreparedStatement; | ||||
| import java.sql.ResultSet; | ||||
| import java.sql.Types; | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.annotation.AnnotationTools; | ||||
| 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.QueryOptions; | ||||
| import org.kar.archidata.exception.DataAccessException; | ||||
| import org.kar.archidata.tools.UuidUtils; | ||||
| import org.slf4j.Logger; | ||||
| 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 { | ||||
| 		final Object data = field.get(rootObject); | ||||
| 		if (data == null) { | ||||
| 			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) { | ||||
| 			final Long dataLong = (Long) data; | ||||
| 			ps.setLong(iii.value, dataLong); | ||||
| 			final Long dataTyped = (Long) data; | ||||
| 			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 { | ||||
| 			final Field idField = AnnotationTools.getFieldOfId(field.getType()); | ||||
| 			final Object uid = idField.get(data); | ||||
| @@ -71,7 +96,11 @@ public class AddOnManyToOne implements DataAccessAddOn { | ||||
|  | ||||
| 	@Override | ||||
| 	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; | ||||
| 		} | ||||
| 		final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); | ||||
| @@ -88,7 +117,11 @@ public class AddOnManyToOne implements DataAccessAddOn { | ||||
|  | ||||
| 	@Override | ||||
| 	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; | ||||
| 		} | ||||
| 		final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); | ||||
| @@ -101,7 +134,11 @@ public class AddOnManyToOne implements DataAccessAddOn { | ||||
| 	@Override | ||||
| 	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 { | ||||
| 		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(tableName); | ||||
| 			querrySelect.append("."); | ||||
| @@ -150,6 +187,39 @@ public class AddOnManyToOne implements DataAccessAddOn { | ||||
| 			} | ||||
| 			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 ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); | ||||
| 		if (decorators == null) { | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import java.util.UUID; | ||||
| public class RESTErrorResponseExeption extends Exception { | ||||
| 	public UUID uuid; | ||||
| 	public String time; | ||||
| 	public String error; | ||||
| 	public String name; | ||||
| 	public String message; | ||||
| 	public int status; | ||||
| 	public String statusMessage; | ||||
| @@ -13,16 +13,16 @@ public class RESTErrorResponseExeption extends Exception { | ||||
| 	public RESTErrorResponseExeption() { | ||||
| 		this.uuid = null; | ||||
| 		this.time = null; | ||||
| 		this.error = null; | ||||
| 		this.name = null; | ||||
| 		this.message = null; | ||||
| 		this.status = 0; | ||||
| 		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.time = time; | ||||
| 		this.error = error; | ||||
| 		this.name = name; | ||||
| 		this.message = message; | ||||
| 		this.status = status; | ||||
| 		this.statusMessage = statusMessage; | ||||
| @@ -30,7 +30,7 @@ public class RESTErrorResponseExeption extends Exception { | ||||
|  | ||||
| 	@Override | ||||
| 	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 + "]"; | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -211,7 +211,8 @@ public class DataTools { | ||||
| 		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 { | ||||
| 			// correct input string stream : | ||||
| 			fileName = multipartCorrection(fileName); | ||||
|   | ||||
| @@ -47,7 +47,7 @@ public class RESTApi { | ||||
| 		if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { | ||||
| 			try { | ||||
| 				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) { | ||||
| 				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) { | ||||
| 			try { | ||||
| 				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) { | ||||
| 				throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body()); | ||||
| 			} catch (final JsonParseException ex) { | ||||
| @@ -156,7 +156,7 @@ public class RESTApi { | ||||
| 		if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { | ||||
| 			try { | ||||
| 				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) { | ||||
| 				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) { | ||||
| 			try { | ||||
| 				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) { | ||||
| 				throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body()); | ||||
| 			} | ||||
|   | ||||
| @@ -2,6 +2,10 @@ package org.kar.archidata.tools; | ||||
|  | ||||
| import java.math.BigInteger; | ||||
| 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; | ||||
|  | ||||
| public class UuidUtils { | ||||
| @@ -25,4 +29,41 @@ public class UuidUtils { | ||||
| 		bb.putLong(uuid.getLeastSignificantBits()); | ||||
| 		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(); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -41,7 +41,7 @@ export interface RESTModel { | ||||
|     accept?: HTTPMimeType; | ||||
|     // Content of the local data. | ||||
|     contentType?: HTTPMimeType; | ||||
|     // Mode of the TOKEN in urk or Header | ||||
|     // Mode of the TOKEN in URL or Header (?token:${tokenInUrl}) | ||||
|     tokenInUrl?: boolean; | ||||
| } | ||||
|  | ||||
| @@ -67,22 +67,48 @@ export function isArrayOf<TYPE>( | ||||
|     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, | ||||
|     restConfig: RESTConfig, | ||||
|     data?: any, | ||||
|     params?: object, | ||||
|     queries?: object, | ||||
|     callback?: RESTCallbacks, | ||||
| }; | ||||
|  | ||||
| function removeTrailingSlashes(input: string): string { | ||||
|     if (isNullOrUndefined(input)) { | ||||
|         return "undefined"; | ||||
|     } | ||||
|     return input.replace(/\/+$/, ''); | ||||
| } | ||||
| function removeLeadingSlashes(input: string): string { | ||||
|     if (isNullOrUndefined(input)) { | ||||
|         return ""; | ||||
|     } | ||||
|     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: | ||||
|     let generateUrl = `${removeTrailingSlashes(restConfig.server)}/${removeLeadingSlashes(restModel.endPoint)}`; | ||||
|     if (params !== undefined) { | ||||
| @@ -112,7 +138,92 @@ export function RESTUrl({ restModel, restConfig, data, params, queries }: RESTRe | ||||
|     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: | ||||
|     let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries }); | ||||
|     let headers: any = {}; | ||||
| @@ -139,13 +250,27 @@ export function RESTRequest({ restModel, restConfig, data, params, queries }: RE | ||||
|         } | ||||
|         body = formData | ||||
|     } | ||||
|     console.log(`Call ${generateUrl}`) | ||||
|     return new Promise((resolve, reject) => { | ||||
|         fetch(generateUrl, { | ||||
|         let action: undefined | Promise<Response> = undefined; | ||||
|         if (isNullOrUndefined(callback) | ||||
|             || (isNullOrUndefined(callback.progressDownload) | ||||
|                 && isNullOrUndefined(callback.progressUpload) | ||||
|                 && isNullOrUndefined(callback.abortHandle))) { | ||||
|             // No information needed: call the generic fetch interface | ||||
|             action = fetch(generateUrl, { | ||||
|                 method: restModel.requestType, | ||||
|                 headers, | ||||
|                 body, | ||||
|         }).then((response: Response) => { | ||||
|             }); | ||||
|         } 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) { | ||||
|                 const contentType = response.headers.get('Content-Type'); | ||||
|                 if (restModel.accept !== contentType) { | ||||
| @@ -190,12 +315,14 @@ export function RESTRequest({ restModel, restConfig, data, params, queries }: RE | ||||
|                 status: 999, | ||||
|                 error: 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> { | ||||
|     return new Promise((resolve, reject) => { | ||||
|         RESTRequest(request).then((value: ModelResponseHttp) => { | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| 0.7.0 | ||||
| 0.7.3 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user