Compare commits

..

4 Commits

Author SHA1 Message Date
a41e837f21 [FIX] import "Write" when no write availlable. 2024-06-08 11:55:51 +02:00
5496855698 [FEAT] add a remove warning 2024-06-08 11:43:29 +02:00
c9cb0d043a [API] remove @SQLWhere 2024-06-08 11:43:00 +02:00
09cfcfc578 [FEAT] generate a full Zod object for write mode.
- Add @NoWriteSpecificMode to permit to remove specific object write model
  - refactor Zod Write model
  - Add .nullable() in write Optional element

Residual bug element use in APi that is mark as no write
2024-06-08 11:42:38 +02:00
11 changed files with 159 additions and 53 deletions

View File

@ -25,7 +25,7 @@
<attribute name="optional" value="true"/> <attribute name="optional" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21"> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>

View File

@ -11,12 +11,12 @@
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand> <buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name> <name>edu.umd.cs.findbugs.plugin.eclipse.findbugsBuilder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand> <buildCommand>
<name>edu.umd.cs.findbugs.plugin.eclipse.findbugsBuilder</name> <name>org.eclipse.m2e.core.maven2Builder</name>
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>

View File

@ -80,6 +80,14 @@ public class AnnotationTools {
return ((Schema) annotation[0]).example(); return ((Schema) annotation[0]).example();
} }
public static boolean getNoWriteSpecificMode(final Class<?> element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(NoWriteSpecificMode.class);
if (annotation.length == 0) {
return false;
}
return true;
}
public static String getSchemaDescription(final Class<?> element) throws DataAccessException { public static String getSchemaDescription(final Class<?> element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
if (annotation.length == 0) { if (annotation.length == 0) {

View File

@ -5,8 +5,9 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** When we wend to have only One type for read and write mode (Wrapping API). */
@Target({ ElementType.TYPE }) @Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface SQLWhere { public @interface NoWriteSpecificMode {
String clause();
} }

View File

@ -3,12 +3,14 @@ package org.kar.archidata.catcher;
import java.time.Instant; import java.time.Instant;
import java.util.UUID; import java.util.UUID;
import org.kar.archidata.annotation.NoWriteSpecificMode;
import org.kar.archidata.tools.UuidUtils; import org.kar.archidata.tools.UuidUtils;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
@NoWriteSpecificMode
public class RestErrorResponse { public class RestErrorResponse {
public UUID uuid = UuidUtils.nextUUID(); public UUID uuid = UuidUtils.nextUUID();
@NotNull @NotNull

View File

@ -10,6 +10,7 @@ public class ClassEnumModel extends ClassModel {
protected ClassEnumModel(final Class<?> clazz) { protected ClassEnumModel(final Class<?> clazz) {
this.originClasses = clazz; this.originClasses = clazz;
this.noWriteSpecificMode = true;
} }
@Override @Override

View File

@ -11,12 +11,17 @@ import java.util.Set;
public abstract class ClassModel { public abstract class ClassModel {
protected boolean analyzeDone = false; protected boolean analyzeDone = false;
protected Class<?> originClasses = null; protected Class<?> originClasses = null;
protected boolean noWriteSpecificMode = false;
protected List<ClassModel> dependencyModels = new ArrayList<>(); protected List<ClassModel> dependencyModels = new ArrayList<>();
public Class<?> getOriginClasses() { public Class<?> getOriginClasses() {
return this.originClasses; return this.originClasses;
} }
public boolean isNoWriteSpecificMode() {
return this.noWriteSpecificMode;
}
protected boolean isCompatible(final Class<?> clazz) { protected boolean isCompatible(final Class<?> clazz) {
return this.originClasses == clazz; return this.originClasses == clazz;
} }

View File

@ -123,6 +123,7 @@ public class ClassObjectModel extends ClassModel {
} }
this.analyzeDone = true; this.analyzeDone = true;
final Class<?> clazz = this.originClasses; final Class<?> clazz = this.originClasses;
this.noWriteSpecificMode = AnnotationTools.getNoWriteSpecificMode(clazz);
this.isPrimitive = clazz.isPrimitive(); this.isPrimitive = clazz.isPrimitive();
if (this.isPrimitive) { if (this.isPrimitive) {
return; return;

View File

@ -41,7 +41,7 @@ public class TsApiGeneration {
final ClassEnumModel model, final ClassEnumModel model,
final TsClassElementGroup tsGroup, final TsClassElementGroup tsGroup,
final Set<ClassModel> imports, final Set<ClassModel> imports,
final boolean writeMode) throws IOException { final Set<ClassModel> importWrite) throws IOException {
imports.add(model); imports.add(model);
final TsClassElement tsModel = tsGroup.find(model); final TsClassElement tsModel = tsGroup.find(model);
return tsModel.tsTypeName; return tsModel.tsTypeName;
@ -51,15 +51,19 @@ public class TsApiGeneration {
final ClassObjectModel model, final ClassObjectModel model,
final TsClassElementGroup tsGroup, final TsClassElementGroup tsGroup,
final Set<ClassModel> imports, final Set<ClassModel> imports,
final boolean writeMode) throws IOException { final Set<ClassModel> importWrite) throws IOException {
final TsClassElement tsModel = tsGroup.find(model); final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType != DefinedPosition.NATIVE) { if (tsModel.nativeType != DefinedPosition.NATIVE) {
if (importWrite == null || tsModel.models.get(0).isNoWriteSpecificMode()) {
imports.add(model); imports.add(model);
} else {
importWrite.add(model);
}
} }
if (tsModel.nativeType != DefinedPosition.NORMAL) { if (tsModel.nativeType != DefinedPosition.NORMAL) {
return tsModel.tsTypeName; return tsModel.tsTypeName;
} }
if (writeMode) { if (importWrite != null && !tsModel.models.get(0).isNoWriteSpecificMode()) {
return tsModel.tsTypeName + "Write"; return tsModel.tsTypeName + "Write";
} }
return tsModel.tsTypeName; return tsModel.tsTypeName;
@ -69,12 +73,12 @@ public class TsApiGeneration {
final ClassMapModel model, final ClassMapModel model,
final TsClassElementGroup tsGroup, final TsClassElementGroup tsGroup,
final Set<ClassModel> imports, final Set<ClassModel> imports,
final boolean writeMode) throws IOException { final Set<ClassModel> importWrite) throws IOException {
final StringBuilder out = new StringBuilder(); final StringBuilder out = new StringBuilder();
out.append("{[key: "); out.append("{[key: ");
out.append(generateClassModelTypescript(model.keyModel, tsGroup, imports, writeMode)); out.append(generateClassModelTypescript(model.keyModel, tsGroup, imports, importWrite));
out.append("]: "); out.append("]: ");
out.append(generateClassModelTypescript(model.valueModel, tsGroup, imports, writeMode)); out.append(generateClassModelTypescript(model.valueModel, tsGroup, imports, importWrite));
out.append(";}"); out.append(";}");
return out.toString(); return out.toString();
} }
@ -83,9 +87,9 @@ public class TsApiGeneration {
final ClassListModel model, final ClassListModel model,
final TsClassElementGroup tsGroup, final TsClassElementGroup tsGroup,
final Set<ClassModel> imports, final Set<ClassModel> imports,
final boolean writeMode) throws IOException { final Set<ClassModel> importWrite) throws IOException {
final StringBuilder out = new StringBuilder(); final StringBuilder out = new StringBuilder();
out.append(generateClassModelTypescript(model.valueModel, tsGroup, imports, writeMode)); out.append(generateClassModelTypescript(model.valueModel, tsGroup, imports, importWrite));
out.append("[]"); out.append("[]");
return out.toString(); return out.toString();
} }
@ -94,18 +98,18 @@ public class TsApiGeneration {
final ClassModel model, final ClassModel model,
final TsClassElementGroup tsGroup, final TsClassElementGroup tsGroup,
final Set<ClassModel> imports, final Set<ClassModel> imports,
final boolean writeMode) throws IOException { final Set<ClassModel> importWrite) throws IOException {
if (model instanceof final ClassObjectModel objectModel) { if (model instanceof final ClassObjectModel objectModel) {
return generateClassObjectModelTypescript(objectModel, tsGroup, imports, writeMode); return generateClassObjectModelTypescript(objectModel, tsGroup, imports, importWrite);
} }
if (model instanceof final ClassListModel listModel) { if (model instanceof final ClassListModel listModel) {
return generateClassListModelTypescript(listModel, tsGroup, imports, writeMode); return generateClassListModelTypescript(listModel, tsGroup, imports, importWrite);
} }
if (model instanceof final ClassMapModel mapModel) { if (model instanceof final ClassMapModel mapModel) {
return generateClassMapModelTypescript(mapModel, tsGroup, imports, writeMode); return generateClassMapModelTypescript(mapModel, tsGroup, imports, importWrite);
} }
if (model instanceof final ClassEnumModel enumModel) { if (model instanceof final ClassEnumModel enumModel) {
return generateClassEnumModelTypescript(enumModel, tsGroup, imports, writeMode); return generateClassEnumModelTypescript(enumModel, tsGroup, imports, importWrite);
} }
throw new IOException("Impossible model:" + model); throw new IOException("Impossible model:" + model);
} }
@ -114,7 +118,7 @@ public class TsApiGeneration {
final List<ClassModel> models, final List<ClassModel> models,
final TsClassElementGroup tsGroup, final TsClassElementGroup tsGroup,
final Set<ClassModel> imports, final Set<ClassModel> imports,
final boolean writeMode) throws IOException { final Set<ClassModel> importWrite) throws IOException {
if (models.size() == 0) { if (models.size() == 0) {
return "void"; return "void";
} }
@ -126,7 +130,7 @@ public class TsApiGeneration {
} else { } else {
out.append(" | "); out.append(" | ");
} }
final String data = generateClassModelTypescript(model, tsGroup, imports, writeMode); final String data = generateClassModelTypescript(model, tsGroup, imports, importWrite);
out.append(data); out.append(data);
} }
return out.toString(); return out.toString();
@ -199,7 +203,7 @@ public class TsApiGeneration {
data.append("\n\t\t\t"); data.append("\n\t\t\t");
data.append(queryEntry.getKey()); data.append(queryEntry.getKey());
data.append("?: "); data.append("?: ");
data.append(generateClassModelsTypescript(queryEntry.getValue(), tsGroup, imports, false)); data.append(generateClassModelsTypescript(queryEntry.getValue(), tsGroup, imports, null));
data.append(","); data.append(",");
} }
data.append("\n\t\t},"); data.append("\n\t\t},");
@ -210,15 +214,15 @@ public class TsApiGeneration {
data.append("\n\t\t\t"); data.append("\n\t\t\t");
data.append(paramEntry.getKey()); data.append(paramEntry.getKey());
data.append(": "); data.append(": ");
data.append(generateClassModelsTypescript(paramEntry.getValue(), tsGroup, imports, false)); data.append(generateClassModelsTypescript(paramEntry.getValue(), tsGroup, imports, null));
data.append(","); data.append(",");
} }
data.append("\n\t\t},"); data.append("\n\t\t},");
} }
if (interfaceElement.unnamedElement.size() == 1) { if (interfaceElement.unnamedElement.size() == 1) {
data.append("\n\t\tdata: "); data.append("\n\t\tdata: ");
data.append(generateClassModelTypescript(interfaceElement.unnamedElement.get(0), tsGroup, writeImports, data.append(generateClassModelTypescript(interfaceElement.unnamedElement.get(0), tsGroup, imports,
true)); writeImports));
data.append(","); data.append(",");
} else if (interfaceElement.multiPartParameters.size() != 0) { } else if (interfaceElement.multiPartParameters.size() != 0) {
data.append("\n\t\tdata: {"); data.append("\n\t\tdata: {");
@ -227,7 +231,7 @@ public class TsApiGeneration {
data.append("\n\t\t\t"); data.append("\n\t\t\t");
data.append(pathEntry.getKey()); data.append(pathEntry.getKey());
data.append(": "); data.append(": ");
data.append(generateClassModelsTypescript(pathEntry.getValue(), tsGroup, writeImports, true)); data.append(generateClassModelsTypescript(pathEntry.getValue(), tsGroup, imports, writeImports));
data.append(","); data.append(",");
} }
data.append("\n\t\t},"); data.append("\n\t\t},");
@ -275,7 +279,7 @@ public class TsApiGeneration {
toolImports.add("RESTRequestJson"); toolImports.add("RESTRequestJson");
} else { } else {
final String returnType = generateClassModelsTypescript(interfaceElement.returnTypes, tsGroup, imports, final String returnType = generateClassModelsTypescript(interfaceElement.returnTypes, tsGroup, imports,
false); null);
data.append(returnType); data.append(returnType);
data.append("> {"); data.append("> {");
if ("void".equals(returnType)) { if ("void".equals(returnType)) {
@ -320,7 +324,7 @@ public class TsApiGeneration {
data.append("\n\t\t\t\taccept: produce,"); data.append("\n\t\t\t\taccept: produce,");
} else { } else {
final String returnType = generateClassModelsTypescript(interfaceElement.returnTypes, tsGroup, final String returnType = generateClassModelsTypescript(interfaceElement.returnTypes, tsGroup,
imports, false); imports, null);
if (!"void".equals(returnType)) { if (!"void".equals(returnType)) {
for (final String elem : produces) { for (final String elem : produces) {
if (MediaType.APPLICATION_JSON.equals(elem)) { if (MediaType.APPLICATION_JSON.equals(elem)) {

View File

@ -181,12 +181,18 @@ public class TsClassElement {
if (tsModel.nativeType != DefinedPosition.NATIVE) { if (tsModel.nativeType != DefinedPosition.NATIVE) {
out.append("import {"); out.append("import {");
out.append(tsModel.zodName); out.append(tsModel.zodName);
if (tsModel.nativeType == DefinedPosition.NORMAL && !(tsModel.models.get(0).isNoWriteSpecificMode())) {
out.append(", ");
out.append(tsModel.zodName);
out.append("Write ");
}
out.append("} from \"./"); out.append("} from \"./");
out.append(tsModel.fileName); out.append(tsModel.fileName);
out.append("\";\n"); out.append("\";\n");
} }
} }
return out.toString(); return out.toString();
} }
private Object generateComment(final ClassObjectModel model) { private Object generateComment(final ClassObjectModel model) {
@ -215,22 +221,38 @@ public class TsClassElement {
return out.toString(); return out.toString();
} }
public String optionalTypeZod(final FieldProperty field) { public boolean isOptionalTypeZod(final FieldProperty field) {
// Common checking element (apply to List, Map, ...) // Common checking element (apply to List, Map, ...)
if (field.nullable()) { if (field.nullable()) {
return ".optional()"; return true;
} }
if (field.notNull()) { if (field.notNull()) {
return ""; return false;
} }
// Other object: // Other object:
if (field.model().getOriginClasses() == null || field.model().getOriginClasses().isPrimitive()) { if (field.model().getOriginClasses() == null || field.model().getOriginClasses().isPrimitive()) {
return ""; return false;
} }
if (field.columnNotNull()) { if (field.columnNotNull()) {
return false;
}
return true;
}
public String optionalTypeZod(final FieldProperty field) {
// Common checking element (apply to List, Map, ...)
if (isOptionalTypeZod(field)) {
return ".optional()";
}
return ""; return "";
} }
return ".optional()";
public String optionalWriteTypeZod(final FieldProperty field) {
// Common checking element (apply to List, Map, ...)
if (isOptionalTypeZod(field)) {
return ".nullable()";
}
return "";
} }
public String maxSizeZod(final FieldProperty field) { public String maxSizeZod(final FieldProperty field) {
@ -270,7 +292,9 @@ public class TsClassElement {
out.append(getBaseHeader()); out.append(getBaseHeader());
out.append(generateImports(model.getDependencyModels(), tsGroup)); out.append(generateImports(model.getDependencyModels(), tsGroup));
out.append("\n"); out.append("\n");
// ------------------------------------------------------------------------
// -- Generate read mode
// ------------------------------------------------------------------------
out.append(generateComment(model)); out.append(generateComment(model));
out.append("export const "); out.append("export const ");
out.append(this.zodName); out.append(this.zodName);
@ -315,8 +339,69 @@ public class TsClassElement {
out.append("\n});\n"); out.append("\n});\n");
out.append(generateZodInfer(this.tsTypeName, this.zodName)); out.append(generateZodInfer(this.tsTypeName, this.zodName));
out.append(generateExportCheckFunctionWrite("")); out.append(generateExportCheckFunctionWrite(""));
// check if we need to generate write mode :
if (!model.isNoWriteSpecificMode()) {
// ------------------------------------------------------------------------
// -- Generate write mode
// ------------------------------------------------------------------------
//out.append(generateComment(model));
out.append("export const ");
out.append(this.zodName);
out.append("Write = ");
if (model.getExtendsClass() != null) {
final ClassModel parentClass = model.getExtendsClass();
final TsClassElement tsParentModel = tsGroup.find(parentClass);
out.append(tsParentModel.zodName);
out.append("Write");
out.append(".extend({");
} else {
out.append("zod.object({");
}
out.append("\n");
for (final FieldProperty field : model.getFields()) {
// remove all readOnly field
if (field.readOnly()) {
continue;
}
final ClassModel fieldModel = field.model();
if (field.comment() != null) {
out.append("\t/**\n");
out.append("\t * ");
out.append(field.comment());
out.append("\n\t */\n");
}
out.append("\t");
out.append(field.name());
out.append(": ");
if (fieldModel instanceof ClassEnumModel || fieldModel instanceof ClassObjectModel) {
final TsClassElement tsFieldModel = tsGroup.find(fieldModel);
out.append(tsFieldModel.zodName);
} else if (fieldModel instanceof final ClassListModel fieldListModel) {
final String data = generateTsList(fieldListModel, tsGroup);
out.append(data);
} else if (fieldModel instanceof final ClassMapModel fieldMapModel) {
final String data = generateTsMap(fieldMapModel, tsGroup);
out.append(data);
}
out.append(maxSizeZod(field));
out.append(optionalWriteTypeZod(field));
// all write field are optional
if (field.model() instanceof final ClassObjectModel plop) {
if (!plop.isPrimitive()) {
out.append(".optional()");
}
} else {
out.append(".optional()");
}
out.append(",\n");
}
out.append("\n});\n");
out.append(generateZodInfer(this.tsTypeName + "Write", this.zodName + "Write"));
// Check only the input value ==> no need of the output
out.append(generateExportCheckFunctionWrite("Write"));
// Generate the Write Type associated. // Generate the Write Type associated.
/*
out.append("\nexport const "); out.append("\nexport const ");
out.append(this.zodName); out.append(this.zodName);
out.append("Write = "); out.append("Write = ");
@ -331,11 +416,8 @@ public class TsClassElement {
out.append("\n})"); out.append("\n})");
} }
out.append(".partial();\n"); out.append(".partial();\n");
out.append(generateZodInfer(this.tsTypeName + "Write", this.zodName + "Write")); */
}
// Check only the input value ==> no need of the output
out.append(generateExportCheckFunctionWrite("Write"));
return out.toString(); return out.toString();
} }

View File

@ -120,6 +120,7 @@ public class RESTApi {
} }
} }
@SuppressWarnings("unchecked")
protected <T, U> T modelSendJson(final String model, final Class<T> clazz, final String urlOffset, String body) protected <T, U> T modelSendJson(final String model, final Class<T> clazz, final String urlOffset, String body)
throws RESTErrorResponseExeption, IOException, InterruptedException { throws RESTErrorResponseExeption, IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient(); final HttpClient client = HttpClient.newHttpClient();
@ -164,6 +165,7 @@ public class RESTApi {
return this.mapper.readValue(httpResponse.body(), clazz); return this.mapper.readValue(httpResponse.body(), clazz);
} }
@SuppressWarnings("unchecked")
protected <T> T modelSendMap( protected <T> T modelSendMap(
final String model, final String model,
final Class<T> clazz, final Class<T> clazz,