[DEV] refacto the external rest api generator

This commit is contained in:
Edouard DUPIN 2024-05-17 00:41:40 +02:00
parent 1fbc9e5874
commit 592243ddc6
17 changed files with 728 additions and 218 deletions

View File

@ -5,12 +5,11 @@ import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
@ -20,37 +19,23 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; 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.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;
import org.kar.archidata.externalRestApi.model.ApiTool;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
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.MediaType;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
public class DataFactoryTsApi { public class DataFactoryTsApi {
static final Logger LOGGER = LoggerFactory.getLogger(DataFactoryTsApi.class); static final Logger LOGGER = LoggerFactory.getLogger(DataFactoryTsApi.class);
record APIModel( record APIModel(
String data, String data,
String className) {} String className) {}
/** Request the generation of the TypeScript file for the "Zod" export model /** Request the generation of the TypeScript file for the "Zod" export model
* @param classs List of class used in the model * @param classs List of class used in the model
* @throws Exception */ * @throws Exception */
@ -74,7 +59,7 @@ public class DataFactoryTsApi {
RESTRequestVoid RESTRequestVoid
} from "./rest-tools" } from "./rest-tools"
import {"""; import {""";
for (final Class<?> clazz : classs) { for (final Class<?> clazz : classs) {
final Set<Class<?>> includeModel = new HashSet<>(); final Set<Class<?>> includeModel = new HashSet<>();
final Set<Class<?>> includeCheckerModel = new HashSet<>(); final Set<Class<?>> includeCheckerModel = new HashSet<>();
@ -112,7 +97,7 @@ public class DataFactoryTsApi {
} }
generatedData.append("\n} from \"./model\"\n"); generatedData.append("\n} from \"./model\"\n");
generatedData.append(api.data()); generatedData.append(api.data());
String fileName = api.className(); String fileName = api.className();
fileName = fileName.replaceAll("([A-Z])", "-$1").toLowerCase(); fileName = fileName.replaceAll("([A-Z])", "-$1").toLowerCase();
fileName = fileName.replaceAll("^\\-*", ""); fileName = fileName.replaceAll("^\\-*", "");
@ -123,179 +108,11 @@ public class DataFactoryTsApi {
} }
return apis; return apis;
} }
public static String apiAnnotationGetPath(final Class<?> element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Path.class);
if (annotation.length == 0) {
return null;
}
return ((Path) annotation[0]).value();
}
public static List<String> apiAnnotationProduces(final Class<?> element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Produces.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Produces) annotation[0]).value());
}
public static List<String> apiAnnotationProduces(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Produces.class);
if (annotation.length == 0) {
return null;
}
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) {
return data;
}
return apiAnnotationProduces(clazz);
}
public static String apiAnnotationGetOperationDescription(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Operation.class);
if (annotation.length == 0) {
return null;
}
return ((Operation) annotation[0]).description();
}
public static String apiAnnotationGetPath(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Path.class);
if (annotation.length == 0) {
return null;
}
return ((Path) annotation[0]).value();
}
public static String apiAnnotationGetTypeRequest(final Method element) throws Exception {
if (element.getDeclaredAnnotationsByType(GET.class).length == 1) {
return "GET";
}
if (element.getDeclaredAnnotationsByType(POST.class).length == 1) {
return "POST";
}
if (element.getDeclaredAnnotationsByType(PUT.class).length == 1) {
return "PUT";
}
if (element.getDeclaredAnnotationsByType(PATCH.class).length == 1) {
return "PATCH";
}
if (element.getDeclaredAnnotationsByType(DELETE.class).length == 1) {
return "DELETE";
}
return null;
}
public static String apiAnnotationGetPathParam(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(PathParam.class);
if (annotation.length == 0) {
return null;
}
return ((PathParam) annotation[0]).value();
}
public static String apiAnnotationGetQueryParam(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(QueryParam.class);
if (annotation.length == 0) {
return null;
}
return ((QueryParam) annotation[0]).value();
}
public static String apiAnnotationGetFormDataParam(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(FormDataParam.class);
if (annotation.length == 0) {
return null;
}
return ((FormDataParam) annotation[0]).value();
}
public static Class<?>[] apiAnnotationGetAsyncType(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(AsyncType.class);
if (annotation.length == 0) {
return null;
}
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<String> apiAnnotationGetConsumes(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Consumes.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Consumes) annotation[0]).value());
}
public static List<String> apiAnnotationGetConsumes(final Class<?> element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Consumes.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Consumes) annotation[0]).value());
}
public static List<String> apiAnnotationGetConsumes(final Class<?> clazz, final Method method) throws Exception {
final List<String> data = apiAnnotationGetConsumes(method);
if (data != null) {
return data;
}
return apiAnnotationGetConsumes(clazz);
}
public static boolean apiAnnotationIsContext(final Parameter element) throws Exception {
return element.getDeclaredAnnotationsByType(Context.class).length != 0;
}
public static String convertInTypeScriptType(final List<ClassElement> tmp, final boolean isList) {
String out = "";
for (final ClassElement elem : tmp) {
if (out.length() != 0) {
out += " | ";
}
out += elem.tsTypeName;
if (isList) {
out += "[]";
}
}
return out;
}
public static String convertInTypeScriptCheckType(final List<ClassElement> tmp) {
String out = "";
for (final ClassElement elem : tmp) {
if (out.length() != 0) {
out += " | ";
}
out += elem.tsCheckType;
}
return out;
}
record OrderedElement( record OrderedElement(
String methodName, String methodName,
Method method) {} Method method) {}
public static APIModel createSingleApi( public static APIModel createSingleApi(
final Class<?> clazz, final Class<?> clazz,
final Set<Class<?>> includeModel, final Set<Class<?>> includeModel,
@ -303,14 +120,14 @@ public class DataFactoryTsApi {
final GeneratedTypes previous) throws Exception { final GeneratedTypes previous) throws Exception {
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
// the basic path has no specific elements... // the basic path has no specific elements...
final String basicPath = apiAnnotationGetPath(clazz); final String basicPath = ApiTool.apiAnnotationGetPath(clazz);
final String classSimpleName = clazz.getSimpleName(); final String classSimpleName = clazz.getSimpleName();
builder.append("export namespace "); builder.append("export namespace ");
builder.append(classSimpleName); builder.append(classSimpleName);
builder.append(" {\n"); builder.append(" {\n");
LOGGER.info("Parse Class for path: {} => {}", classSimpleName, basicPath); LOGGER.info("Parse Class for path: {} => {}", classSimpleName, basicPath);
final List<OrderedElement> orderedElements = new ArrayList<>(); final List<OrderedElement> orderedElements = new ArrayList<>();
for (final Method method : clazz.getDeclaredMethods()) { for (final Method method : clazz.getDeclaredMethods()) {
final String methodName = method.getName(); final String methodName = method.getName();
@ -318,26 +135,26 @@ public class DataFactoryTsApi {
} }
final Comparator<OrderedElement> comparator = Comparator.comparing(OrderedElement::methodName); final Comparator<OrderedElement> comparator = Comparator.comparing(OrderedElement::methodName);
Collections.sort(orderedElements, comparator); Collections.sort(orderedElements, comparator);
for (final OrderedElement orderedElement : orderedElements) { for (final OrderedElement orderedElement : orderedElements) {
final Method method = orderedElement.method(); final Method method = orderedElement.method();
final String methodName = orderedElement.methodName(); final String methodName = orderedElement.methodName();
final String methodPath = apiAnnotationGetPath(method); final String methodPath = ApiTool.apiAnnotationGetPath(method);
final String methodType = apiAnnotationGetTypeRequest(method); final String methodType = ApiTool.apiAnnotationGetTypeRequest(method);
if (methodType == null) { if (methodType == null) {
LOGGER.error(" [{}] {} => {}/{} ==> No methode type @PATH, @GET ...", methodType, methodName, LOGGER.error(" [{}] {} => {}/{} ==> No methode type @PATH, @GET ...", methodType, methodName,
basicPath, methodPath); basicPath, methodPath);
continue; continue;
} }
final String methodDescription = apiAnnotationGetOperationDescription(method); final String methodDescription = ApiTool.apiAnnotationGetOperationDescription(method);
final List<String> consumes = apiAnnotationGetConsumes(clazz, method); final List<String> consumes = ApiTool.apiAnnotationGetConsumes(clazz, method);
List<String> produces = apiAnnotationProduces(clazz, method); List<String> produces = ApiTool.apiAnnotationProduces(clazz, method);
LOGGER.trace(" [{}] {} => {}/{}", methodType, methodName, basicPath, methodPath); LOGGER.trace(" [{}] {} => {}/{}", methodType, methodName, basicPath, methodPath);
if (methodDescription != null) { if (methodDescription != null) {
LOGGER.trace(" description: {}", methodDescription); LOGGER.trace(" description: {}", methodDescription);
} }
final boolean needGenerateProgress = apiAnnotationTypeScriptProgress(method); final boolean needGenerateProgress = ApiTool.apiAnnotationTypeScriptProgress(method);
Class<?>[] returnTypeModel = apiAnnotationGetAsyncType(method); Class<?>[] returnTypeModel = ApiTool.apiAnnotationGetAsyncType(method);
boolean isUnmanagedReturnType = false; boolean isUnmanagedReturnType = false;
boolean returnModelIsArray = false; boolean returnModelIsArray = false;
List<ClassElement> tmpReturn; List<ClassElement> tmpReturn;
@ -357,8 +174,22 @@ public class DataFactoryTsApi {
produces = null; produces = null;
} else if (returnTypeModelRaw == Map.class) { } else if (returnTypeModelRaw == Map.class) {
LOGGER.warn("Not manage the Map Model ... set any"); LOGGER.warn("Not manage the Map Model ... set any");
returnTypeModel = new Class<?>[] { Map.class }; final ParameterizedType listType = (ParameterizedType) method.getGenericReturnType();
tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous); final Type typeKey = listType.getActualTypeArguments()[0];
final Type typeValue = listType.getActualTypeArguments()[1];
if (typeKey == String.class) {
if (typeValue instanceof ParameterizedType) {
final Type typeSubKey = listType.getActualTypeArguments()[0];
final Type typeSubValue = listType.getActualTypeArguments()[1];
if (typeKey == String.class) {
}
}
} else {
LOGGER.warn("Not manage the Map Model ... set any");
returnTypeModel = new Class<?>[] { Map.class };
tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous);
}
} else if (returnTypeModelRaw == List.class) { } else if (returnTypeModelRaw == List.class) {
final ParameterizedType listType = (ParameterizedType) method.getGenericReturnType(); final ParameterizedType listType = (ParameterizedType) method.getGenericReturnType();
returnTypeModelRaw = (Class<?>) listType.getActualTypeArguments()[0]; returnTypeModelRaw = (Class<?>) listType.getActualTypeArguments()[0];
@ -399,12 +230,12 @@ public class DataFactoryTsApi {
// LOGGER.info(" Parameters:"); // LOGGER.info(" Parameters:");
for (final Parameter parameter : method.getParameters()) { for (final Parameter parameter : method.getParameters()) {
// Security context are internal parameter (not available from API) // Security context are internal parameter (not available from API)
if (apiAnnotationIsContext(parameter)) { if (ApiTool.apiAnnotationIsContext(parameter)) {
continue; continue;
} }
final Class<?> parameterType = parameter.getType(); final Class<?> parameterType = parameter.getType();
String parameterTypeString; String parameterTypeString;
final Class<?>[] asyncType = apiAnnotationGetAsyncType(parameter); final Class<?>[] asyncType = ApiTool.apiAnnotationGetAsyncType(parameter);
if (parameterType == List.class) { if (parameterType == List.class) {
if (asyncType == null) { if (asyncType == null) {
LOGGER.warn("Detect List param ==> not managed type ==> any[] !!!"); LOGGER.warn("Detect List param ==> not managed type ==> any[] !!!");
@ -414,7 +245,7 @@ public class DataFactoryTsApi {
for (final ClassElement elem : tmp) { for (final ClassElement elem : tmp) {
includeModel.add(elem.model[0]); includeModel.add(elem.model[0]);
} }
parameterTypeString = convertInTypeScriptType(tmp, true); parameterTypeString = ApiTool.convertInTypeScriptType(tmp, true);
} }
} else if (asyncType == null) { } else if (asyncType == null) {
final ClassElement tmp = DataFactoryZod.createTable(parameterType, previous); final ClassElement tmp = DataFactoryZod.createTable(parameterType, previous);
@ -425,11 +256,11 @@ public class DataFactoryTsApi {
for (final ClassElement elem : tmp) { for (final ClassElement elem : tmp) {
includeModel.add(elem.model[0]); includeModel.add(elem.model[0]);
} }
parameterTypeString = convertInTypeScriptType(tmp, true); parameterTypeString = ApiTool.convertInTypeScriptType(tmp, true);
} }
final String pathParam = apiAnnotationGetPathParam(parameter); final String pathParam = ApiTool.apiAnnotationGetPathParam(parameter);
final String queryParam = apiAnnotationGetQueryParam(parameter); final String queryParam = ApiTool.apiAnnotationGetQueryParam(parameter);
final String formDataParam = apiAnnotationGetFormDataParam(parameter); final String formDataParam = ApiTool.apiAnnotationGetFormDataParam(parameter);
if (queryParam != null) { if (queryParam != null) {
queryParams.put(queryParam, parameterTypeString); queryParams.put(queryParam, parameterTypeString);
} else if (pathParam != null) { } else if (pathParam != null) {
@ -480,7 +311,7 @@ public class DataFactoryTsApi {
LOGGER.trace(" data type: {}", emptyElement.get(0)); LOGGER.trace(" data type: {}", emptyElement.get(0));
} }
// ALL is good can generate the Elements // ALL is good can generate the Elements
if (methodDescription != null) { if (methodDescription != null) {
builder.append("\n\t/**\n\t * "); builder.append("\n\t/**\n\t * ");
builder.append(methodDescription); builder.append(methodDescription);
@ -552,7 +383,7 @@ public class DataFactoryTsApi {
String isFist = null; String isFist = null;
for (final String elem : produces) { for (final String elem : produces) {
String lastElement = null; String lastElement = null;
if (MediaType.APPLICATION_JSON.equals(elem)) { if (MediaType.APPLICATION_JSON.equals(elem)) {
lastElement = "HTTPMimeType.JSON"; lastElement = "HTTPMimeType.JSON";
} }
@ -584,7 +415,7 @@ public class DataFactoryTsApi {
|| tmpReturn.get(0).tsTypeName.equals("void")) { || tmpReturn.get(0).tsTypeName.equals("void")) {
builder.append("void"); builder.append("void");
} else { } else {
builder.append(convertInTypeScriptType(tmpReturn, returnModelIsArray)); builder.append(ApiTool.convertInTypeScriptType(tmpReturn, returnModelIsArray));
} }
builder.append("> {"); builder.append("> {");
if (tmpReturn.size() == 0 // if (tmpReturn.size() == 0 //
@ -656,7 +487,7 @@ public class DataFactoryTsApi {
&& !tmpReturn.get(0).tsTypeName.equals("void")) { && !tmpReturn.get(0).tsTypeName.equals("void")) {
builder.append(", "); builder.append(", ");
// TODO: correct this it is really bad ... // TODO: correct this it is really bad ...
builder.append(convertInTypeScriptCheckType(tmpReturn)); builder.append(ApiTool.convertInTypeScriptCheckType(tmpReturn));
} }
builder.append(");"); builder.append(");");
builder.append("\n\t};"); builder.append("\n\t};");
@ -664,7 +495,7 @@ public class DataFactoryTsApi {
builder.append("\n}\n"); builder.append("\n}\n");
return new APIModel(builder.toString(), classSimpleName); return new APIModel(builder.toString(), classSimpleName);
} }
public static void generatePackage( public static void generatePackage(
final List<Class<?>> classApi, final List<Class<?>> classApi,
final List<Class<?>> classModel, final List<Class<?>> classModel,
@ -676,7 +507,7 @@ public class DataFactoryTsApi {
FileWriter myWriter = new FileWriter(pathPackage + File.separator + "model.ts"); FileWriter myWriter = new FileWriter(pathPackage + File.separator + "model.ts");
myWriter.write(packageApi.toString()); myWriter.write(packageApi.toString());
myWriter.close(); myWriter.close();
final StringBuilder index = new StringBuilder(""" final StringBuilder index = new StringBuilder("""
/** /**
* Global import of the package * Global import of the package
@ -704,5 +535,5 @@ public class DataFactoryTsApi {
myWriter.close(); myWriter.close();
return; return;
} }
} }

View File

@ -0,0 +1,5 @@
package org.kar.archidata.externalRestApi;
public class AnalyzeApi {
}

View File

@ -0,0 +1,5 @@
package org.kar.archidata.externalRestApi;
public class AnalyzeModel {
}

View File

@ -0,0 +1,5 @@
package org.kar.archidata.externalRestApi;
public class GeneratePythonApi {
}

View File

@ -0,0 +1,5 @@
package org.kar.archidata.externalRestApi;
public class GeneratePythonModel {
}

View File

@ -0,0 +1,5 @@
package org.kar.archidata.externalRestApi;
public class GenerateTsApi {
}

View File

@ -0,0 +1,5 @@
package org.kar.archidata.externalRestApi;
public class GenerateTsModel {
}

View File

@ -0,0 +1,60 @@
package org.kar.archidata.externalRestApi.model;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// Temporary class that permit to order the list of API.
record OrderedElement(
String methodName,
Method method) {}
public class ApiGroupModel {
static final Logger LOGGER = LoggerFactory.getLogger(ApiGroupModel.class);
// Name of the REST end-point name
public String restEndPoint;
// Name of the Class
String name;
// Origin class reference
Class<?> originClass;
// List of all API
public List<ApiModel> interfaces;
public ApiGroupModel(final Class<?> clazz, final ModelGroup previousModel) throws Exception {
this.originClass = clazz;
// the basic path has no specific elements...
this.restEndPoint = ApiTool.apiAnnotationGetPath(clazz);
this.name = clazz.getSimpleName();
final List<String> consumes = ApiTool.apiAnnotationGetConsumes(clazz);
final List<String> produces = ApiTool.apiAnnotationProduces(clazz);
// Get all method to order it. This permit to stabilize the generation.
// JAVA has dynamic allocation of member order, then we need to order it?.
final List<OrderedElement> orderedElements = new ArrayList<>();
for (final Method method : clazz.getDeclaredMethods()) {
final String methodName = method.getName();
orderedElements.add(new OrderedElement(methodName, method));
}
final Comparator<OrderedElement> comparator = Comparator.comparing(OrderedElement::methodName);
Collections.sort(orderedElements, comparator);
for (final OrderedElement orderedElement : orderedElements) {
// Check if the path is available
final RestTypeRequest methodType = ApiTool.apiAnnotationGetTypeRequest2(orderedElement.method());
if (methodType == null) {
LOGGER.info(" [{}] {} ==> No methode type @PATH, @GET ...", methodType, orderedElement.methodName());
continue;
}
final ApiModel model = new ApiModel(clazz, orderedElement.method(), this.restEndPoint, consumes, produces,
previousModel);
this.interfaces.add(model);
}
}
}

View File

@ -0,0 +1,239 @@
package org.kar.archidata.externalRestApi.model;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.kar.archidata.dataAccess.DataFactoryZod;
import org.kar.archidata.dataAccess.DataFactoryZod.ClassElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.core.Response;
public class ApiModel {
static final Logger LOGGER = LoggerFactory.getLogger(ApiModel.class);
Class<?> originClass;
Method orignMethod;
// Name of the REST end-point name
public String restEndPoint;
// Type of the request:
public RestTypeRequest restTypeRequest;
// Description of the API
public String description;
// need to generate the progression of stream (if possible)
boolean needGenerateProgress;
// List of types returned by the API
public List<ClassModel> returnTypes = new ArrayList<>();;
// Name of the API (function name)
public String name;
// list of all parameters (/{key}/...
public Map<String, ClassModel> parameters = new HashMap<>();
// list of all query (?key...)
public Map<String, ClassModel> queries = new HashMap<>();
// Possible input type of the REST API
public List<String> consumes = new ArrayList<>();
// Possible output type of the REST API
public List<String> produces = new ArrayList<>();
private void updateReturnTypes(final Method method, final ModelGroup previousModel) {
// get return type from the user specification:
final Class<?>[] returnTypeModel = ApiTool.apiAnnotationGetAsyncType(method);
if (returnTypeModel != null) {
if (returnTypeModel.length == 0) {
throw new IOException("Create a @AsyncType with empty elements ...");
}
for (final Class<?> clazz : returnTypeModel) {
if (clazz == Void.class || clazz == void.class) {
this.returnTypes.add(previousModel.add(Void.class));
} else if (clazz == List.class) {
throw new IOException("Unmanaged List.class in @AsyncType.");
} else if (clazz == Map.class) {
throw new IOException("Unmanaged Map.class in @AsyncType.");
} else {
this.returnTypes.add(previousModel.add(clazz));
}
}
return;
}
final Class<?> returnTypeModelRaw = method.getReturnType();
LOGGER.info("Get type: {}", returnTypeModelRaw);
if (returnTypeModelRaw == Map.class) {
LOGGER.warn("Not manage the Map Model ... set any");
final ParameterizedType listType = (ParameterizedType) method.getGenericReturnType();
this.returnTypes.add(new ClassMapModel(listType, previousModel));
return;
}
if (returnTypeModelRaw == List.class) {
final ParameterizedType listType = (ParameterizedType) method.getGenericReturnType();
this.returnTypes.add(new ClassListModel(listType, previousModel));
return;
}
this.returnTypes.add(previousModel.add(returnTypeModelRaw));
}
public ApiModel(final Class<?> clazz, final Method method, final String baseRestEndPoint,
final List<String> consume, final List<String> produce, final ModelGroup previousModel) throws Exception {
this.originClass = clazz;
this.orignMethod = method;
final String methodPath = ApiTool.apiAnnotationGetPath(method);
final String methodType = ApiTool.apiAnnotationGetTypeRequest(method);
final String methodName = method.getName();
this.description = ApiTool.apiAnnotationGetOperationDescription(method);
this.consumes = ApiTool.apiAnnotationGetConsumes2(consume, method);
this.produces = ApiTool.apiAnnotationProduces2(produce, method);
LOGGER.trace(" [{}] {} => {}/{}", methodType, methodName, baseRestEndPoint, methodPath);
this.needGenerateProgress = ApiTool.apiAnnotationTypeScriptProgress(method);
Class<?>[] returnTypeModel = ApiTool.apiAnnotationGetAsyncType(method);
boolean isUnmanagedReturnType = false;
boolean returnModelIsArray = false;
List<ClassElement> tmpReturn;
if (returnTypeModel == null) {
Class<?> returnTypeModelRaw = method.getReturnType();
LOGGER.info("Get type: {}", returnTypeModelRaw);
if (returnTypeModelRaw == Response.class) {
LOGGER.info("Get type: {}", returnTypeModelRaw);
}
if (returnTypeModelRaw == Response.class || returnTypeModelRaw == void.class
|| returnTypeModelRaw == Void.class) {
if (returnTypeModelRaw == Response.class) {
isUnmanagedReturnType = true;
}
returnTypeModel = new Class<?>[] { Void.class };
tmpReturn = new ArrayList<>();
this.produces = null;
} else if (returnTypeModelRaw == Map.class) {
LOGGER.warn("Not manage the Map Model ... set any");
final ParameterizedType listType = (ParameterizedType) method.getGenericReturnType();
final Type typeKey = listType.getActualTypeArguments()[0];
final Type typeValue = listType.getActualTypeArguments()[1];
if (typeKey == String.class) {
if (typeValue instanceof ParameterizedType) {
final Type typeSubKey = listType.getActualTypeArguments()[0];
final Type typeSubValue = listType.getActualTypeArguments()[1];
if (typeKey == String.class) {
}
}
} else {
LOGGER.warn("Not manage the Map Model ... set any");
returnTypeModel = new Class<?>[] { Map.class };
tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous);
}
} else if (returnTypeModelRaw == List.class) {
final ParameterizedType listType = (ParameterizedType) method.getGenericReturnType();
returnTypeModelRaw = (Class<?>) listType.getActualTypeArguments()[0];
returnModelIsArray = true;
returnTypeModel = new Class<?>[] { returnTypeModelRaw };
tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous);
} else {
returnTypeModel = new Class<?>[] { returnTypeModelRaw };
tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous);
}
} else if (returnTypeModel.length >= 0 && (returnTypeModel[0] == Response.class
|| returnTypeModel[0] == Void.class || returnTypeModel[0] == void.class)) {
if (returnTypeModel[0] == Response.class) {
isUnmanagedReturnType = true;
}
returnTypeModel = new Class<?>[] { Void.class };
tmpReturn = new ArrayList<>();
this.produces = null;
} else if (returnTypeModel.length > 0 && returnTypeModel[0] == Map.class) {
LOGGER.warn("Not manage the Map Model ...");
returnTypeModel = new Class<?>[] { Map.class };
tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous);
} else {
tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous);
}
for (final ClassElement elem : tmpReturn) {
includeModel.add(elem.model[0]);
includeCheckerModel.add(elem.model[0]);
}
LOGGER.trace(" return: {}", tmpReturn.size());
for (final ClassElement elem : tmpReturn) {
LOGGER.trace(" - {}", elem.tsTypeName);
}
final Map<String, String> queryParams = new HashMap<>();
final Map<String, String> pathParams = new HashMap<>();
final Map<String, String> formDataParams = new HashMap<>();
final List<String> emptyElement = new ArrayList<>();
// LOGGER.info(" Parameters:");
for (final Parameter parameter : method.getParameters()) {
// Security context are internal parameter (not available from API)
if (ApiTool.apiAnnotationIsContext(parameter)) {
continue;
}
final Class<?> parameterType = parameter.getType();
String parameterTypeString;
final Class<?>[] asyncType = ApiTool.apiAnnotationGetAsyncType(parameter);
if (parameterType == List.class) {
if (asyncType == null) {
LOGGER.warn("Detect List param ==> not managed type ==> any[] !!!");
parameterTypeString = "any[]";
} else {
final List<ClassElement> tmp = DataFactoryZod.createTables(asyncType, previous);
for (final ClassElement elem : tmp) {
includeModel.add(elem.model[0]);
}
parameterTypeString = ApiTool.convertInTypeScriptType(tmp, true);
}
} else if (asyncType == null) {
final ClassElement tmp = DataFactoryZod.createTable(parameterType, previous);
includeModel.add(tmp.model[0]);
parameterTypeString = tmp.tsTypeName;
} else {
final List<ClassElement> tmp = DataFactoryZod.createTables(asyncType, previous);
for (final ClassElement elem : tmp) {
includeModel.add(elem.model[0]);
}
parameterTypeString = ApiTool.convertInTypeScriptType(tmp, true);
}
final String pathParam = ApiTool.apiAnnotationGetPathParam(parameter);
final String queryParam = ApiTool.apiAnnotationGetQueryParam(parameter);
final String formDataParam = ApiTool.apiAnnotationGetFormDataParam(parameter);
if (queryParam != null) {
queryParams.put(queryParam, parameterTypeString);
} else if (pathParam != null) {
pathParams.put(pathParam, parameterTypeString);
} else if (formDataParam != null) {
formDataParams.put(formDataParam, parameterTypeString);
} else if (asyncType != null) {
final List<ClassElement> tmp = DataFactoryZod.createTables(asyncType, previous);
parameterTypeString = "";
for (final ClassElement elem : tmp) {
includeModel.add(elem.model[0]);
if (parameterTypeString.length() != 0) {
parameterTypeString += " | ";
}
parameterTypeString += elem.tsTypeName;
}
emptyElement.add(parameterTypeString);
} else if (parameterType == List.class) {
parameterTypeString = "any[]";
final Class<?> plop = parameterType.arrayType();
LOGGER.info("ArrayType = {}", plop);
emptyElement.add(parameterTypeString);
} else {
final ClassElement tmp = DataFactoryZod.createTable(parameterType, previous);
includeModel.add(tmp.model[0]);
emptyElement.add(tmp.tsTypeName);
}
}
}
}

View File

@ -0,0 +1,233 @@
package org.kar.archidata.externalRestApi.model;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.TypeScriptProgress;
import org.kar.archidata.dataAccess.DataFactoryZod.ClassElement;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
public class ApiTool {
public static String apiAnnotationGetPath(final Class<?> element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Path.class);
if (annotation.length == 0) {
return null;
}
return ((Path) annotation[0]).value();
}
public static List<String> apiAnnotationProduces(final Class<?> element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Produces.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Produces) annotation[0]).value());
}
public static List<String> apiAnnotationProduces(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Produces.class);
if (annotation.length == 0) {
return null;
}
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) {
return data;
}
return apiAnnotationProduces(clazz);
}
public static List<String> apiAnnotationProduces2(final List<String> parentProduce, final Method method)
throws Exception {
final List<String> data = apiAnnotationProduces(method);
if (data != null) {
return data;
}
return parentProduce;
}
public static String apiAnnotationGetOperationDescription(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Operation.class);
if (annotation.length == 0) {
return null;
}
return ((Operation) annotation[0]).description();
}
public static String apiAnnotationGetPath(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Path.class);
if (annotation.length == 0) {
return null;
}
return ((Path) annotation[0]).value();
}
public static String apiAnnotationGetTypeRequest(final Method element) throws Exception {
if (element.getDeclaredAnnotationsByType(GET.class).length == 1) {
return "GET";
}
if (element.getDeclaredAnnotationsByType(POST.class).length == 1) {
return "POST";
}
if (element.getDeclaredAnnotationsByType(PUT.class).length == 1) {
return "PUT";
}
if (element.getDeclaredAnnotationsByType(PATCH.class).length == 1) {
return "PATCH";
}
if (element.getDeclaredAnnotationsByType(DELETE.class).length == 1) {
return "DELETE";
}
return null;
}
public static RestTypeRequest apiAnnotationGetTypeRequest2(final Method element) throws Exception {
if (element.getDeclaredAnnotationsByType(GET.class).length == 1) {
return RestTypeRequest.GET;
}
if (element.getDeclaredAnnotationsByType(POST.class).length == 1) {
return RestTypeRequest.POST;
}
if (element.getDeclaredAnnotationsByType(PUT.class).length == 1) {
return RestTypeRequest.PUT;
}
if (element.getDeclaredAnnotationsByType(PATCH.class).length == 1) {
return RestTypeRequest.PATCH;
}
if (element.getDeclaredAnnotationsByType(DELETE.class).length == 1) {
return RestTypeRequest.DELETE;
}
return null;
}
public static String apiAnnotationGetPathParam(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(PathParam.class);
if (annotation.length == 0) {
return null;
}
return ((PathParam) annotation[0]).value();
}
public static String apiAnnotationGetQueryParam(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(QueryParam.class);
if (annotation.length == 0) {
return null;
}
return ((QueryParam) annotation[0]).value();
}
public static String apiAnnotationGetFormDataParam(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(FormDataParam.class);
if (annotation.length == 0) {
return null;
}
return ((FormDataParam) annotation[0]).value();
}
public static Class<?>[] apiAnnotationGetAsyncType(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(AsyncType.class);
if (annotation.length == 0) {
return null;
}
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<String> apiAnnotationGetConsumes(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Consumes.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Consumes) annotation[0]).value());
}
public static List<String> apiAnnotationGetConsumes(final Class<?> element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Consumes.class);
if (annotation.length == 0) {
return null;
}
return Arrays.asList(((Consumes) annotation[0]).value());
}
public static List<String> apiAnnotationGetConsumes(final Class<?> clazz, final Method method) throws Exception {
final List<String> data = apiAnnotationGetConsumes(method);
if (data != null) {
return data;
}
return apiAnnotationGetConsumes(clazz);
}
public static List<String> apiAnnotationGetConsumes2(final List<String> parentConsume, final Method method)
throws Exception {
final List<String> data = apiAnnotationGetConsumes(method);
if (data != null) {
return data;
}
return parentConsume;
}
public static boolean apiAnnotationIsContext(final Parameter element) throws Exception {
return element.getDeclaredAnnotationsByType(Context.class).length != 0;
}
public static String convertInTypeScriptType(final List<ClassElement> tmp, final boolean isList) {
String out = "";
for (final ClassElement elem : tmp) {
if (out.length() != 0) {
out += " | ";
}
out += elem.tsTypeName;
if (isList) {
out += "[]";
}
}
return out;
}
public static String convertInTypeScriptCheckType(final List<ClassElement> tmp) {
String out = "";
for (final ClassElement elem : tmp) {
if (out.length() != 0) {
out += " | ";
}
out += elem.tsCheckType;
}
return out;
}
}

View File

@ -0,0 +1,8 @@
package org.kar.archidata.externalRestApi.model;
public class ClassEnumModel extends ClassModel {
protected ClassEnumModel(final Class<?> clazz) {
this.originClasses.add(clazz);
}
}

View File

@ -0,0 +1,19 @@
package org.kar.archidata.externalRestApi.model;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public class ClassListModel extends ClassModel {
public ClassModel valueModel;
public ClassListModel(final ClassModel valueModel) {
this.valueModel = valueModel;
}
public ClassListModel(final ParameterizedType listType, final ModelGroup previousModel) throws IOException {
final Type model = listType.getActualTypeArguments()[0];
this.valueModel = getModel(model, previousModel);
}
}

View File

@ -0,0 +1,20 @@
package org.kar.archidata.externalRestApi.model;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
public class ClassMapModel extends ClassModel {
public ClassModel keyModel;
public ClassModel valueModel;
public ClassMapModel(final ClassModel keyModel, final ClassModel valueModel) {
this.keyModel = keyModel;
this.valueModel = valueModel;
}
public ClassMapModel(final ParameterizedType listType, final ModelGroup previousModel) throws IOException {
this.keyModel = getModel(listType.getActualTypeArguments()[0], previousModel);
this.valueModel = getModel(listType.getActualTypeArguments()[1], previousModel);
}
}

View File

@ -0,0 +1,35 @@
package org.kar.archidata.externalRestApi.model;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public abstract class ClassModel {
public List<Class<?>> originClasses = new ArrayList<>();
protected boolean isCompatible(final Class<?> clazz) {
return this.originClasses.contains(clazz);
}
public ClassModel getModel(final Type type, final ModelGroup previousModel) throws IOException {
if (type == List.class) {
if (type instanceof final ParameterizedType parameterizedType) {
return new ClassListModel(parameterizedType, previousModel);
} else {
throw new IOException("Fail to manage parametrized type...");
}
}
if (type == Map.class) {
if (type instanceof final ParameterizedType parameterizedType) {
return new ClassMapModel(parameterizedType, previousModel);
} else {
throw new IOException("Fail to manage parametrized type...");
}
}
return previousModel.add((Class<?>) type);
}
}

View File

@ -0,0 +1,8 @@
package org.kar.archidata.externalRestApi.model;
public class ClassObjectModel extends ClassModel {
public ClassObjectModel(final Class<?> clazz) {
this.originClasses.add(clazz);
}
}

View File

@ -0,0 +1,22 @@
package org.kar.archidata.externalRestApi.model;
import java.util.ArrayList;
import java.util.List;
public class ModelGroup {
public final List<ClassModel> previousModel = new ArrayList<>();
public ClassModel add(final Class<?> clazz) {
for (final ClassModel value : this.previousModel) {
if (value.isCompatible(clazz)) {
return value;
}
}
if (clazz.isEnum()) {
final ClassModel elem = new ClassEnumModel(clazz);
}
// create new model:
return null;
}
}

View File

@ -0,0 +1,5 @@
package org.kar.archidata.externalRestApi.model;
public enum RestTypeRequest {
GET, POST, PUT, PATCH, DELETE
}