From 8e913b54ad7f2cb4c51d35b8428cb52c9c3a517e Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Mon, 11 Mar 2024 22:51:04 +0100 Subject: [PATCH] [DEV] update capacity to upload file --- .../kar/archidata/annotation/AsyncType.java | 2 +- src/org/kar/archidata/api/DataResource.java | 8 +- .../dataAccess/DataFactoryTsApi.java | 65 ++++++++--- .../archidata/dataAccess/DataFactoryZod.java | 3 + src/org/kar/archidata/tools/DataTools.java | 104 ++++++++++++++---- 5 files changed, 140 insertions(+), 42 deletions(-) diff --git a/src/org/kar/archidata/annotation/AsyncType.java b/src/org/kar/archidata/annotation/AsyncType.java index c183a0f..f9407fc 100644 --- a/src/org/kar/archidata/annotation/AsyncType.java +++ b/src/org/kar/archidata/annotation/AsyncType.java @@ -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) +@Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface AsyncType { Class value(); diff --git a/src/org/kar/archidata/api/DataResource.java b/src/org/kar/archidata/api/DataResource.java index f6f231d..e2d86d1 100644 --- a/src/org/kar/archidata/api/DataResource.java +++ b/src/org/kar/archidata/api/DataResource.java @@ -258,7 +258,7 @@ public class DataResource { @Consumes({ MediaType.MULTIPART_FORM_DATA }) @RolesAllowed("ADMIN") @Operation(description = "Insert a new data in the data environment", tags = "SYSTEM") - public Response uploadFile(@Context final SecurityContext sc, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData) { + public void uploadFile(@Context final SecurityContext sc, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData) { final GenericContext gc = (GenericContext) sc.getUserPrincipal(); LOGGER.info("==================================================="); LOGGER.info("== DATA uploadFile {}", (gc == null ? "null" : gc.userByToken)); @@ -272,8 +272,6 @@ public class DataResource { e.printStackTrace(); } saveFile(fileInputStream, filePath); - return Response.ok("Data uploaded successfully !!").build(); - // return null; } @GET @@ -318,8 +316,8 @@ public class DataResource { return Response.status(404).entity("{\"error\":\"media Does not exist: " + id + "\"}").type("application/json").build(); } if (value.mimeType.contentEquals("image/jpeg") || value.mimeType.contentEquals("image/png") - // || value.mimeType.contentEquals("image/webp") - ) { + // || value.mimeType.contentEquals("image/webp") + ) { // reads input image final BufferedImage inputImage = ImageIO.read(inputFile); final int scaledWidth = 250; diff --git a/src/org/kar/archidata/dataAccess/DataFactoryTsApi.java b/src/org/kar/archidata/dataAccess/DataFactoryTsApi.java index 340845b..6b47a4b 100644 --- a/src/org/kar/archidata/dataAccess/DataFactoryTsApi.java +++ b/src/org/kar/archidata/dataAccess/DataFactoryTsApi.java @@ -36,6 +36,7 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; public class DataFactoryTsApi { static final Logger LOGGER = LoggerFactory.getLogger(DataFactoryTsApi.class); @@ -56,16 +57,31 @@ public class DataFactoryTsApi { import { """; for (final Class clazz : classs) { - final Set includeModel = new HashSet<>(); - includeModel.add("RestErrorResponse"); - final APIModel api = createSingleApi(clazz, includeModel, previous); + final Set> includeModel = new HashSet<>(); + final Set> includeCheckerModel = new HashSet<>(); + final APIModel api = createSingleApi(clazz, includeModel, includeCheckerModel, previous); final StringBuilder generatedData = new StringBuilder(); generatedData.append(globalheader); - for (final String elem : includeModel) { - if (elem == null || elem.equals("string") || elem.equals("String")) { + for (final Class elem : includeModel) { + if (elem == null) { continue; } - generatedData.append(elem); + final ClassElement classElement = DataFactoryZod.createTable(elem, previous); + if (classElement.nativeType) { + continue; + } + generatedData.append(classElement.tsTypeName); + generatedData.append(", "); + } + for (final Class elem : includeCheckerModel) { + if (elem == null) { + continue; + } + final ClassElement classElement = DataFactoryZod.createTable(elem, previous); + if (classElement.nativeType) { + continue; + } + generatedData.append(classElement.tsCheckType); generatedData.append(", "); } generatedData.append("} from \"./model\"\n"); @@ -180,6 +196,13 @@ 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) { + return null; + } + return ((AsyncType) annotation[0]).value(); + } public static List apiAnnotationGetConsumes(final Method element) throws Exception { final Annotation[] annotation = element.getDeclaredAnnotationsByType(Consumes.class); @@ -209,7 +232,7 @@ public class DataFactoryTsApi { return element.getDeclaredAnnotationsByType(Context.class).length != 0; } - public static APIModel createSingleApi(final Class clazz, final Set includeModel, final GeneratedTypes previous) throws Exception { + public static APIModel createSingleApi(final Class clazz, final Set> includeModel, final Set> includeCheckerModel, final GeneratedTypes previous) throws Exception { final StringBuilder builder = new StringBuilder(); // the basic path has no specific elements... final String basicPath = apiAnnotationGetPath(clazz); @@ -230,14 +253,19 @@ public class DataFactoryTsApi { final String methodDescription = apiAnnotationGetOperationDescription(method); final List consumes = apiAnnotationGetConsumes(clazz, method); final List produces = apiAnnotationProduces(clazz, method); - /* if (consumes != null && consumes.contains(MediaType.MULTIPART_FORM_DATA)) { LOGGER.error(" [{}] {} => {}/{} ==> Multipart is not managed ...", methodType, methodName, basicPath, - * methodPath); if (methodDescription != null) { builder.append("\n\t/**\n\t * "); builder.append(methodDescription); builder.append("\n\t * /"); } - * builder.append("\n\t// TODO: export function "); builder.append(methodName); builder.append("(...): ... {} Multipart not managed ..."); continue; } */ LOGGER.trace(" [{}] {} => {}/{}", methodType, methodName, basicPath, methodPath); if (methodDescription != null) { LOGGER.trace(" description: {}", methodDescription); } - Class returnTypeModel = method.getReturnType(); + Class returnTypeModel = apiAnnotationGetAsyncType(method); + if (returnTypeModel == null) { + returnTypeModel = method.getReturnType(); + } + boolean isUnmanagedReturnType = false; + if (returnTypeModel == Response.class ) { + isUnmanagedReturnType = true; + returnTypeModel = Void.class; + } boolean returnModelIsArray = false; ClassElement tmpReturn; if (returnTypeModel == List.class) { @@ -245,13 +273,13 @@ public class DataFactoryTsApi { returnTypeModel = (Class) listType.getActualTypeArguments()[0]; tmpReturn = DataFactoryZod.createTable(returnTypeModel, previous); returnModelIsArray = true; - includeModel.add(tmpReturn.tsTypeName); + includeModel.add(tmpReturn.model[0]); } else { tmpReturn = DataFactoryZod.createTable(returnTypeModel, previous); } - includeModel.add(tmpReturn.tsTypeName); - includeModel.add(tmpReturn.tsCheckType); + includeModel.add(tmpReturn.model[0]); + includeCheckerModel.add(tmpReturn.model[0]); LOGGER.trace(" return: {}", tmpReturn.tsTypeName); final Map queryParams = new HashMap<>(); final Map pathParams = new HashMap<>(); @@ -270,7 +298,7 @@ public class DataFactoryTsApi { parameterTypeString = "any[]"; } else { final ClassElement tmp = DataFactoryZod.createTable(parameterType, previous); - includeModel.add(tmp.tsTypeName); + includeModel.add(tmp.model[0]); parameterTypeString = tmp.tsTypeName; } final String pathParam = apiAnnotationGetPathParam(parameter); @@ -286,7 +314,7 @@ public class DataFactoryTsApi { final Class asyncType = apiAnnotationGetAsyncType(parameter); if (asyncType != null) { final ClassElement tmp = DataFactoryZod.createTable(asyncType, previous); - includeModel.add(tmp.tsTypeName); + includeModel.add(tmp.model[0]); emptyElement.add(tmp.tsTypeName); } else if (parameterType == List.class) { parameterTypeString = "any[]"; @@ -294,7 +322,7 @@ public class DataFactoryTsApi { LOGGER.info("ArrayType = {}", plop); } else { final ClassElement tmp = DataFactoryZod.createTable(parameterType, previous); - includeModel.add(tmp.tsTypeName); + includeModel.add(tmp.model[0]); emptyElement.add(tmp.tsTypeName); } } @@ -327,6 +355,9 @@ public class DataFactoryTsApi { builder.append(methodDescription); builder.append("\n\t */"); } + if (isUnmanagedReturnType) { + builder.append("\n\t// TODO: unmanaged \"Response\" type: please specify @AsyncType or considered as 'void'."); + } builder.append("\n\texport function "); builder.append(methodName); builder.append("({ restConfig,"); diff --git a/src/org/kar/archidata/dataAccess/DataFactoryZod.java b/src/org/kar/archidata/dataAccess/DataFactoryZod.java index 71701d9..7888008 100644 --- a/src/org/kar/archidata/dataAccess/DataFactoryZod.java +++ b/src/org/kar/archidata/dataAccess/DataFactoryZod.java @@ -2,6 +2,7 @@ package org.kar.archidata.dataAccess; import java.io.File; import java.io.FileWriter; +import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.sql.Timestamp; @@ -13,6 +14,7 @@ import java.util.Date; import java.util.List; import java.util.UUID; +import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.kar.archidata.annotation.AnnotationTools; import org.kar.archidata.exception.DataAccessException; import org.slf4j.Logger; @@ -209,6 +211,7 @@ public class DataFactoryZod { final GeneratedTypes previous = new GeneratedTypes(); previous.add(new ClassElement(new Class[] { Void.class, void.class }, "void", "void", null, null, true)); previous.add(new ClassElement(new Class[] { String.class }, "zod.string()", "string", null, "zod.string()", true)); + previous.add(new ClassElement(new Class[] { InputStream.class, FormDataContentDisposition.class }, "z.instanceof(File)", "File", null, "z.instanceof(File)", true)); previous.add(new ClassElement(new Class[] { Boolean.class, boolean.class }, "zod.boolean()", "boolean", null, "zod.boolean()", true)); previous.add(new ClassElement(new Class[] { UUID.class }, "ZodUUID", "UUID", "isUUID", "zod.string().uuid()", false), true); previous.add(new ClassElement(new Class[] { Long.class, long.class }, "ZodLong", "Long", "isLong", diff --git a/src/org/kar/archidata/tools/DataTools.java b/src/org/kar/archidata/tools/DataTools.java index 1611131..41b6155 100644 --- a/src/org/kar/archidata/tools/DataTools.java +++ b/src/org/kar/archidata/tools/DataTools.java @@ -7,6 +7,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.sql.SQLException; @@ -17,6 +18,7 @@ import org.glassfish.jersey.media.multipart.FormDataContentDisposition; 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.dataAccess.options.ReadAllColumn; import org.kar.archidata.model.Data; @@ -95,13 +97,41 @@ public class DataTools { } public static Data createNewData(final long tmpUID, final String originalFileName, final String sha512) throws IOException, SQLException { - /* // determine mime type: String mimeType = ""; final String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1); mimeType = switch (extension.toLowerCase()) { case - * "jpg", "jpeg" -> "image/jpeg"; case "png" -> "image/png"; case "webp" -> "image/webp"; case "mka" -> "audio/x-matroska"; case "mkv" -> "video/x-matroska"; case "webm" -> "video/webm"; - * default -> throw new IOException("Can not find the mime type of data input: '" + extension + "'"); }; 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 Exception e) { - * // TODO Auto-generated catch block e.printStackTrace(); return null; } final String mediaPath = getFileData(out.id); LOGGER.info("src = {}", tmpPath); LOGGER.info("dst = {}", mediaPath); - * Files.move(Paths.get(tmpPath), Paths.get(mediaPath), StandardCopyOption.ATOMIC_MOVE); LOGGER.info("Move done"); // all is done the file is correctly installed... return out; */ - return null; + // determine mime type: + String mimeType = ""; + final String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1); + mimeType = switch (extension.toLowerCase()) { + case "jpg", "jpeg" -> "image/jpeg"; + case "png" -> "image/png"; + case "webp" -> "image/webp"; + case "mka" -> "audio/x-matroska"; + case "mkv" -> "video/x-matroska"; + case "webm" -> "video/webm"; + default -> throw new IOException("Can not find the mime type of data input: '" + extension + "'"); + }; + 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 Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + + final String mediaPath = getFileData(out.id); + LOGGER.info("src = {}", tmpPath); + LOGGER.info("dst = {}", mediaPath); + Files.move(Paths.get(tmpPath), Paths.get(mediaPath), StandardCopyOption.ATOMIC_MOVE); + + LOGGER.info("Move done"); + // all is done the file is correctly installed... + return out; } public static void undelete(final UUID id) { @@ -190,17 +220,53 @@ public class DataTools { return data; } - public static Response uploadCover(final Class clazz, final Long id, final String fileName, final InputStream fileInputStream, final FormDataContentDisposition fileMetaData) { - /* try { // correct input string stream : fileName = multipartCorrection(fileName); // public NodeSmall uploadFile(final FormDataMultiPart form) { LOGGER.info("Upload media file: {}", - * fileMetaData); LOGGER.info(" - id: {}", id); LOGGER.info(" - file_name: ", fileName); LOGGER.info(" - fileInputStream: {}", fileInputStream); LOGGER.info(" - fileMetaData: {}", - * fileMetaData); final T media = DataAccess.get(clazz, id); if (media == null) { return Response.notModified("Media Id does not exist or removed...").build(); } final long tmpUID = - * getTmpDataId(); final String sha512 = saveTemporaryFile(fileInputStream, tmpUID); Data data = getWithSha512(sha512); if (data == null) { LOGGER.info("Need to add the data in the BDD ... "); - * try { data = createNewData(tmpUID, fileName, sha512); } catch (final IOException ex) { removeTemporaryFile(tmpUID); ex.printStackTrace(); return - * Response.notModified("can not create input media").build(); } catch (final SQLException ex) { ex.printStackTrace(); removeTemporaryFile(tmpUID); return - * Response.notModified("Error in SQL insertion ...").build(); } } else if (data.deleted) { LOGGER.error("Data already exist but deleted"); undelete(data.id); data.deleted = false; } else { - * LOGGER.error("Data already exist ... all good"); } // Fist step: retrieve all the Id of each parents:... LOGGER.info("Find typeNode"); AddOnManyToMany.addLink(clazz, id, "cover", data.id); - * return Response.ok(DataAccess.get(clazz, id)).build(); } catch (final Exception ex) { System.out.println("Cat ann unexpected error ... "); ex.printStackTrace(); } return - * Response.serverError().build(); */ - return null; + public static Response uploadCover(final Class clazz, final Long id, String fileName, final InputStream fileInputStream, final FormDataContentDisposition fileMetaData) { + try { + // correct input string stream : + fileName = multipartCorrection(fileName); + + // public NodeSmall uploadFile(final FormDataMultiPart form) { + LOGGER.info("Upload media file: {}", fileMetaData); + LOGGER.info(" - id: {}", id); + LOGGER.info(" - file_name: ", fileName); + LOGGER.info(" - fileInputStream: {}", fileInputStream); + LOGGER.info(" - fileMetaData: {}", fileMetaData); + final T media = DataAccess.get(clazz, id); + if (media == null) { + return Response.notModified("Media Id does not exist or removed...").build(); + } + + final long tmpUID = getTmpDataId(); + final String sha512 = saveTemporaryFile(fileInputStream, tmpUID); + Data data = getWithSha512(sha512); + if (data == null) { + LOGGER.info("Need to add the data in the BDD ... "); + try { + data = createNewData(tmpUID, fileName, sha512); + } catch (final IOException ex) { + removeTemporaryFile(tmpUID); + ex.printStackTrace(); + return Response.notModified("can not create input media").build(); + } catch (final SQLException ex) { + ex.printStackTrace(); + removeTemporaryFile(tmpUID); + return Response.notModified("Error in SQL insertion ...").build(); + } + } else if (data.deleted) { + LOGGER.error("Data already exist but deleted"); + undelete(data.id); + data.deleted = false; + } else { + LOGGER.error("Data already exist ... all good"); + } + // Fist step: retrieve all the Id of each parents:... + LOGGER.info("Find typeNode"); + AddOnManyToMany.addLink(clazz, id, "cover", data.id); + return Response.ok(DataAccess.get(clazz, id)).build(); + } catch (final Exception ex) { + System.out.println("Cat ann unexpected error ... "); + ex.printStackTrace(); + } + return Response.serverError().build(); } }