[DEV] add progress callback ==> does not work

This commit is contained in:
Edouard DUPIN 2024-04-15 00:54:22 +02:00
parent 4cb8c84312
commit 9cd71ab601
5 changed files with 159 additions and 15 deletions

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

@ -319,8 +319,8 @@ public class DataResource {
return Response.status(404).entity("{\"error\":\"media Does not exist: " + id + "\"}").type("application/json").build(); 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") if (value.mimeType.contentEquals("image/jpeg") || value.mimeType.contentEquals("image/png")
// || value.mimeType.contentEquals("image/webp") // || value.mimeType.contentEquals("image/webp")
) { ) {
// reads input image // reads input image
final BufferedImage inputImage = ImageIO.read(inputFile); final BufferedImage inputImage = ImageIO.read(inputFile);
final int scaledWidth = 250; final int scaledWidth = 250;
@ -355,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

@ -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, ProgressCallback, 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) {
@ -261,6 +270,7 @@ 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();
@ -387,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(" progress,");
}
builder.append(" }: {"); builder.append(" }: {");
builder.append("\n\t\trestConfig: RESTConfig,"); builder.append("\n\t\trestConfig: RESTConfig,");
if (!queryParams.isEmpty()) { if (!queryParams.isEmpty()) {
@ -454,6 +467,9 @@ public class DataFactoryTsApi {
} }
builder.append(","); builder.append(",");
} }
if (needGenerateProgress) {
builder.append("\n\t\tprogress?: ProgressCallback,");
}
builder.append("\n\t}): Promise<"); builder.append("\n\t}): Promise<");
builder.append(tmpReturn.tsTypeName); builder.append(tmpReturn.tsTypeName);
if (returnModelIsArray) { if (returnModelIsArray) {
@ -519,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\tprogress,");
}
builder.append("\n\t\t}"); builder.append("\n\t\t}");
if (tmpReturn.tsCheckType != null) { if (tmpReturn.tsCheckType != null) {
builder.append(", "); builder.append(", ");

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;
@ -88,7 +90,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 +107,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 +160,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

@ -67,22 +67,33 @@ export function isArrayOf<TYPE>(
return true; return true;
} }
function isNullOrUndefined(data: any): data is undefined | null {
return data === undefined || data === null;
}
export type RESTRequestType = { export type RESTRequestType = {
restModel: RESTModel, restModel: RESTModel,
restConfig: RESTConfig, restConfig: RESTConfig,
data?: any, data?: any,
params?: object, params?: object,
queries?: object, queries?: object,
progress?: ProgressCallback,
}; };
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 +102,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 +123,55 @@ 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 type ProgressCallback = (count: number, total: number) => void;
// input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
export function fetchProgress<TYPE>(generateUrl: string, { method, headers, body }: {
method: HTTPRequestModel,
headers: any,
body: any,
}, progress: ProgressCallback): Promise<Response> {
//async function fetchForm(form, options = {}) {
const action = generateUrl;
const data = body;
const xhr = new XMLHttpRequest();
return new Promise((resolve, reject) => {
xhr.responseType = 'blob';
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) {
// done
return;
}
const response = new Response(xhr.response, {
status: xhr.status,
statusText: xhr.statusText
});
resolve(response);
}
// If fail:
xhr.addEventListener('error', () => {
reject(new TypeError('Failed to fetch'))
});
// Link the progression callback
if (progress) {
xhr.addEventListener('progress', (dataEvent) => {
progress(dataEvent.loaded, dataEvent.total);
});
}
// open the socket
xhr.open(method, action, true);
// configure the header
if (!isNullOrUndefined(headers)) {
for (const [key, value] of Object.entries(headers)) {
xhr.setRequestHeader(key, value as string);
}
}
xhr.send(data);
});
}
export function RESTRequest({ restModel, restConfig, data, params, queries, progress }: 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 = {};
@ -141,11 +200,21 @@ export function RESTRequest({ restModel, restConfig, data, params, queries }: RE
} }
console.log(`Call ${generateUrl}`) console.log(`Call ${generateUrl}`)
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fetch(generateUrl, { let action: Promise<Response> = undefined;
method: restModel.requestType, if (isNullOrUndefined(progress)) {
headers, action = fetch(generateUrl, {
body, method: restModel.requestType,
}).then((response: Response) => { headers,
body,
});
} else {
action = fetchProgress(generateUrl, {
method: restModel.requestType,
headers,
body,
}, progress);
}
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) {
@ -196,6 +265,8 @@ export function RESTRequest({ restModel, restConfig, data, params, queries }: RE
}); });
} }
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) => {