Compare commits

..

40 Commits

Author SHA1 Message Date
6ccce206b9 [FEAT] deploy on github
[DEV] deploy on gitea neofarm
2024-06-02 21:35:01 +02:00
e4c56a4da5 [RELEASE] create version v0.11.0 2024-06-02 21:30:58 +02:00
22614aee98 [DEPENDENCY] update dependency 2024-06-02 21:30:36 +02:00
5bbcf63c42 [FIX] close some statements 2024-06-02 21:28:29 +02:00
1c02d333a3 [FIX] remove a warnning of spotbug 2024-06-02 21:28:12 +02:00
1c41ea4273 [FEAT] add a simple tool to generate random string (usefull in test) 2024-06-02 21:27:53 +02:00
274767d89b [FEAT] add some Logs when fail 2024-06-02 21:27:20 +02:00
4236dc38bd [FIX] correct many data-access element that does not close elements 2024-06-02 21:23:57 +02:00
7d4b246d4a [FEAT] add dependency of spotbug to have better code 2024-06-02 21:23:26 +02:00
f6f256c006 [FIX] Correct Exception types in the DataJson Add-on 2024-06-02 13:11:25 +02:00
d46b84c741 [FEAT] support DELETE return Data 2024-06-02 13:10:38 +02:00
3e6b9bf77c [FIX] Correct RESTApi.gets retreive data list in a correct way 2024-06-02 13:10:18 +02:00
007003394a [FEAT] add FailException adding exception context to permit better display of error in the future 2024-06-02 13:09:30 +02:00
54d4c420f9 [FEAT] DataAccess reduce log levels 2024-06-02 13:08:48 +02:00
0a307f3f6e [FIX] correct annotation tool throws 2024-06-02 13:08:21 +02:00
d968a2c48f [DOC] add spotbug documentation 2024-06-02 13:07:51 +02:00
c43e283b57 [FIX] socme checks 2024-06-01 19:49:36 +02:00
6af6f91166 [FIX] SpotBug finding some errors 2024-06-01 16:48:59 +02:00
c94f488747 [FIX] better compatibility with @OneToMany 2024-06-01 13:53:48 +02:00
c44b726cc1 [FEAT] add test for @OneToMany that fail 2024-06-01 13:10:28 +02:00
c412daa1ca [TEST] simplify test model 2024-06-01 13:09:53 +02:00
aa700f9dc5 [FEAT] add a throw in an impossible class call 2024-06-01 13:09:20 +02:00
a1791cf61d [TEST] LOG in TRACE only the library 2024-06-01 13:08:45 +02:00
9c9da21bdb [STYLE] error commit 2024-06-01 13:08:15 +02:00
dc6eeac008 [FIX] fix @ManyToOne (add UUID test) 2024-06-01 13:07:58 +02:00
f3a9ebf5e1 [STYLE] set all the typescript generation in a sub-folder 2024-06-01 13:01:32 +02:00
1fe3cc3523 [FIX] Correct the fail retur API to transmit the error from backend when compatible and wrat it when error occured 2024-05-31 21:50:50 +02:00
91849094cd [DEV] Correct all catcher to be named normalized 2024-05-31 19:51:24 +02:00
7b72b08fc0 [FEAT] permit a better support of @OneToMany 2024-05-29 20:16:48 +02:00
ebf1b4b76a [RELEASE] create version v0.10.3 2024-05-29 10:40:06 +02:00
f6aff28488 [FIX] ser cover nullable 2024-05-29 00:01:28 +02:00
6ebb9eb395 [FIX] remove accept type when void return 2024-05-29 00:01:04 +02:00
17664f1c2f [FIX] missing delete in links 2024-05-29 00:00:08 +02:00
1373760498 [FIX] correct the declaration of Object 2024-05-28 22:22:24 +02:00
8ffa392b2a [FEAT] create sub-directories 2024-05-28 22:21:05 +02:00
6104b68a02 [FIX] correct input order. 2024-05-28 22:07:41 +02:00
3c85af5af9 [FEAT] add a contructor to simplify a checker API 2024-05-28 18:04:54 +02:00
38d1fa9241 [DEV] simple rework the Check JPA 2024-05-28 18:04:31 +02:00
0e498c6a26 [RELEASE] create version v0.10.2 2024-05-27 17:00:52 +02:00
187ffba188 [FEAT] manage @nullable contrainst on list and Map too. 2024-05-27 17:00:31 +02:00
64 changed files with 1556 additions and 826 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"> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>

View File

@@ -15,10 +15,16 @@
<arguments> <arguments>
</arguments> </arguments>
</buildCommand> </buildCommand>
<buildCommand>
<name>edu.umd.cs.findbugs.plugin.eclipse.findbugsBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec> </buildSpec>
<natures> <natures>
<nature>org.eclipse.jdt.core.javanature</nature> <nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature> <nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>edu.umd.cs.findbugs.plugin.eclipse.findbugsNature</nature>
</natures> </natures>
<filteredResources> <filteredResources>
<filter> <filter>

View File

@@ -93,7 +93,12 @@ Enable the pre-commit checker
> **_Note_**: You can change the code in `.git/hooks/pre-commit` by replacing `formatter:verify` with `formatter:format` to auto format the code @ every commit > **_Note_**: You can change the code in `.git/hooks/pre-commit` by replacing `formatter:verify` with `formatter:format` to auto format the code @ every commit
Run Spot-bug:
------------
```bash
mvn spotbugs:check
```
Add Gitea in the dependency for the registry: Add Gitea in the dependency for the registry:
============================================= =============================================

66
pom.xml
View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>kangaroo-and-rabbit</groupId> <groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId> <artifactId>archidata</artifactId>
<version>0.10.1</version> <version>0.11.0</version>
<properties> <properties>
<java.version>21</java.version> <java.version>21</java.version>
<maven.compiler.version>3.1</maven.compiler.version> <maven.compiler.version>3.1</maven.compiler.version>
@@ -14,13 +14,36 @@
<jaxb.version>2.3.1</jaxb.version> <jaxb.version>2.3.1</jaxb.version>
<istack.version>4.1.1</istack.version> <istack.version>4.1.1</istack.version>
</properties> </properties>
<!--
<repositories> <repositories>
<repository> <repository>
<id>gitea</id> <id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url> <url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
</repository> </repository>
</repositories> </repositories>
-->
<repositories>
<repository>
<id>gitea-neofarm</id>
<url>https://gitea.neo.farm/api/packages/kangaroo-and-rabbit/maven</url>
</repository>
</repositories>
<distributionManagement> <distributionManagement>
<repository>
<id>gitea-neofarm</id>
<url>https://gitea.neo.farm/api/packages/kangaroo-and-rabbit/maven</url>
</repository>
<snapshotRepository>
<id>gitea-neofarm</id>
<url>https://gitea.neo.farm/api/packages/kangaroo-and-rabbit/maven</url>
</snapshotRepository>
<!--
<repository>
<id>gitea-neofarm</id>
<url>https://gitea.neo.farm/api/packages/kangaroo-and-rabbit/maven</url>
</repository>
-->
<!--
<repository> <repository>
<id>gitea</id> <id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url> <url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
@@ -29,6 +52,15 @@
<id>gitea</id> <id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url> <url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
</snapshotRepository> </snapshotRepository>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/kangaroo-and-rabbit/archidata</url>
</repository>
<snapshotRepository>
<id>github</id>
<url>https://maven.pkg.github.com/kangaroo-and-rabbit/archidata</url>
</snapshotRepository>
-->
</distributionManagement> </distributionManagement>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
@@ -120,7 +152,6 @@
<artifactId>jackson-datatype-jsr310</artifactId> <artifactId>jackson-datatype-jsr310</artifactId>
<version>2.17.1</version> <version>2.17.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>jakarta.servlet</groupId> <groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId> <artifactId>jakarta.servlet-api</artifactId>
@@ -136,13 +167,13 @@
<dependency> <dependency>
<groupId>org.xerial</groupId> <groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId> <artifactId>sqlite-jdbc</artifactId>
<version>3.45.3.0</version> <version>3.46.0.0</version>
</dependency> </dependency>
<!-- Interface for JWT token --> <!-- Interface for JWT token -->
<dependency> <dependency>
<groupId>com.nimbusds</groupId> <groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId> <artifactId>nimbus-jose-jwt</artifactId>
<version>9.39.1</version> <version>9.39.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>jakarta.persistence</groupId> <groupId>jakarta.persistence</groupId>
@@ -155,6 +186,13 @@
<artifactId>swagger-jaxrs2-jakarta</artifactId> <artifactId>swagger-jaxrs2-jakarta</artifactId>
<version>2.2.22</version> <version>2.2.22</version>
</dependency> </dependency>
<!-- spotbug tooling -->
<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId>
<version>4.8.5</version>
<scope>compile</scope>
</dependency>
<!-- <!--
************************************************************ ************************************************************
** TEST dependency ** ** TEST dependency **
@@ -175,7 +213,7 @@
<dependency> <dependency>
<groupId>net.revelc.code.formatter</groupId> <groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId> <artifactId>formatter-maven-plugin</artifactId>
<version>2.23.0</version> <version>2.24.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
@@ -183,7 +221,6 @@
<version>3.3.1</version> <version>3.3.1</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<sourceDirectory>src</sourceDirectory> <sourceDirectory>src</sourceDirectory>
<resources> <resources>
@@ -305,6 +342,23 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.5.0</version>
<configuration>
<includeFilterFile>spotbugs-security-include.xml</includeFilterFile>
<excludeFilterFile>spotbugs-security-exclude.xml</excludeFilterFile>
<!--<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.12.0</version>
</plugin>
</plugins>
-->
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
<!-- Generate Java-docs As Part Of Project Reports --> <!-- Generate Java-docs As Part Of Project Reports -->

View File

@@ -0,0 +1,5 @@
<FindBugsFilter>
<Match>
<Bug category="SECURITY"/>
</Match>
</FindBugsFilter>

View File

@@ -0,0 +1,2 @@
<FindBugsFilter>
</FindBugsFilter>

View File

@@ -4,7 +4,7 @@ import org.kar.archidata.db.DBConfig;
import org.kar.archidata.tools.ConfigBaseVariable; import org.kar.archidata.tools.ConfigBaseVariable;
public class GlobalConfiguration { public class GlobalConfiguration {
public static DBConfig dbConfig = null; public static final DBConfig dbConfig;
static { static {
dbConfig = new DBConfig(ConfigBaseVariable.getDBType(), ConfigBaseVariable.getDBHost(), dbConfig = new DBConfig(ConfigBaseVariable.getDBType(), ConfigBaseVariable.getDBHost(),

View File

@@ -7,10 +7,12 @@ import java.util.List;
import org.kar.archidata.dataAccess.QueryOptions; import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.options.OverrideTableName; import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.exception.DataAccessException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
@@ -27,7 +29,7 @@ import jakarta.ws.rs.DefaultValue;
public class AnnotationTools { public class AnnotationTools {
static final Logger LOGGER = LoggerFactory.getLogger(AnnotationTools.class); static final Logger LOGGER = LoggerFactory.getLogger(AnnotationTools.class);
public static String getTableName(final Class<?> clazz, final QueryOptions options) throws Exception { public static String getTableName(final Class<?> clazz, final QueryOptions options) throws DataAccessException {
if (options != null) { if (options != null) {
final List<OverrideTableName> data = options.get(OverrideTableName.class); final List<OverrideTableName> data = options.get(OverrideTableName.class);
if (data.size() == 1) { if (data.size() == 1) {
@@ -37,14 +39,15 @@ public class AnnotationTools {
return AnnotationTools.getTableName(clazz); return AnnotationTools.getTableName(clazz);
} }
public static String getTableName(final Class<?> element) throws Exception { public static String getTableName(final Class<?> element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Table.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Table.class);
if (annotation.length == 0) { if (annotation.length == 0) {
// when no annotation is detected, then the table name is the class name // when no annotation is detected, then the table name is the class name
return element.getSimpleName(); return element.getSimpleName();
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Table on " + element.getClass().getCanonicalName()); throw new DataAccessException(
"Must not have more than 1 element @Table on " + element.getClass().getCanonicalName());
} }
final String tmp = ((Table) annotation[0]).name(); final String tmp = ((Table) annotation[0]).name();
if (tmp == null) { if (tmp == null) {
@@ -53,155 +56,158 @@ public class AnnotationTools {
return tmp; return tmp;
} }
public static boolean getSchemaReadOnly(final Field element) throws Exception { public static boolean getSchemaReadOnly(final Field 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) {
return false; return false;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
} }
return ((Schema) annotation[0]).readOnly(); return ((Schema) annotation[0]).readOnly();
} }
public static String getSchemaExample(final Class<?> element) throws Exception { public static String getSchemaExample(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) {
return null; return null;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
} }
return ((Schema) annotation[0]).example(); return ((Schema) annotation[0]).example();
} }
public static String getSchemaDescription(final Class<?> element) throws Exception { 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) {
return null; return null;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
} }
return ((Schema) annotation[0]).description(); return ((Schema) annotation[0]).description();
} }
public static String getSchemaDescription(final Field element) throws Exception { public static String getSchemaDescription(final Field 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) {
return null; return null;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
} }
return ((Schema) annotation[0]).description(); return ((Schema) annotation[0]).description();
} }
public static String getComment(final Field element) throws Exception { public static String getComment(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataComment.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataComment.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return getSchemaDescription(element); return getSchemaDescription(element);
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @DataComment on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @DataComment on " + element.getClass().getCanonicalName());
} }
return ((DataComment) annotation[0]).value(); return ((DataComment) annotation[0]).value();
} }
public static String getDefault(final Field element) throws Exception { public static String getDefault(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DefaultValue.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(DefaultValue.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return null; return null;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @DataDefault on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @DataDefault on " + element.getClass().getCanonicalName());
} }
return ((DefaultValue) annotation[0]).value(); return ((DefaultValue) annotation[0]).value();
} }
public static ManyToOne getManyToOne(final Field element) throws Exception { public static ManyToOne getManyToOne(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(ManyToOne.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(ManyToOne.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return null; return null;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @ManyToOne on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @ManyToOne on " + element.getClass().getCanonicalName());
} }
return (ManyToOne) annotation[0]; return (ManyToOne) annotation[0];
} }
public static DataJson getDataJson(final Field element) throws Exception { public static DataJson getDataJson(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataJson.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataJson.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return null; return null;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @ManyToOne on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @ManyToOne on " + element.getClass().getCanonicalName());
} }
return (DataJson) annotation[0]; return (DataJson) annotation[0];
} }
public static Long getConstraintsMax(final Field element) throws Exception { public static Long getConstraintsMax(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Max.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Max.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return null; return null;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Size on " + element.getClass().getCanonicalName()); throw new DataAccessException(
"Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
} }
return ((Max) annotation[0]).value(); return ((Max) annotation[0]).value();
} }
public static Long getConstraintsMin(final Field element) throws Exception { public static Long getConstraintsMin(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Min.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Min.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return null; return null;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Size on " + element.getClass().getCanonicalName()); throw new DataAccessException(
"Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
} }
return ((Min) annotation[0]).value(); return ((Min) annotation[0]).value();
} }
public static int getLimitSize(final Field element) throws Exception { public static int getLimitSize(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return 255; return 255;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
} }
final int length = ((Column) annotation[0]).length(); final int length = ((Column) annotation[0]).length();
return length <= 0 ? 0 : length; return length <= 0 ? 0 : length;
} }
public static Size getConstraintsSize(final Field element) throws Exception { public static Size getConstraintsSize(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Size.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Size.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return null; return null;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Size on " + element.getClass().getCanonicalName()); throw new DataAccessException(
"Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
} }
return (Size) annotation[0]; return (Size) annotation[0];
} }
public static String getConstraintsPattern(final Field element) throws Exception { public static String getConstraintsPattern(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Pattern.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Pattern.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return null; return null;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @Pattern on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @Pattern on " + element.getClass().getCanonicalName());
} }
return ((Pattern) annotation[0]).regexp(); return ((Pattern) annotation[0]).regexp();
@@ -230,13 +236,13 @@ public class AnnotationTools {
return false; return false;
} }
public static String getFieldName(final Field element) throws Exception { public static String getFieldName(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return element.getName(); return element.getName();
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
} }
final String name = ((Column) annotation[0]).name(); final String name = ((Column) annotation[0]).name();
@@ -246,31 +252,39 @@ public class AnnotationTools {
return name; return name;
} }
public static boolean getColumnNotNull(final Field element) throws Exception { public static boolean getColumnNotNull(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return false; return false;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
} }
return !((Column) annotation[0]).nullable(); return !((Column) annotation[0]).nullable();
} }
public static boolean getConstraintsNotNull(final Field element) throws Exception { public static boolean getNullable(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Nullable.class);
if (annotation.length == 0) {
return false;
}
return true;
}
public static boolean getConstraintsNotNull(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(NotNull.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(NotNull.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return false; return false;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @NotNull on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @NotNull on " + element.getClass().getCanonicalName());
} }
return true; return true;
} }
public static Field getPrimaryKeyField(final Class<?> clazz) throws Exception { public static Field getPrimaryKeyField(final Class<?> clazz) throws DataAccessException {
for (final Field field : clazz.getFields()) { for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it .. // static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) { if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
@@ -283,7 +297,7 @@ public class AnnotationTools {
return null; return null;
} }
public static boolean isPrimaryKey(final Field element) throws Exception { public static boolean isPrimaryKey(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Id.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Id.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return false; return false;
@@ -291,51 +305,51 @@ public class AnnotationTools {
return true; return true;
} }
public static boolean isUnique(final Field element) throws Exception { public static boolean isUnique(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return false; return false;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
} }
return ((Column) annotation[0]).unique(); return ((Column) annotation[0]).unique();
} }
public static GenerationType getStrategy(final Field element) throws Exception { public static GenerationType getStrategy(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(GeneratedValue.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(GeneratedValue.class);
if (annotation.length == 0) { if (annotation.length == 0) {
return null; return null;
} }
if (annotation.length > 1) { if (annotation.length > 1) {
throw new Exception( throw new DataAccessException(
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName()); "Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
} }
return ((GeneratedValue) annotation[0]).strategy(); return ((GeneratedValue) annotation[0]).strategy();
} }
public static boolean isDeletedField(final Field element) throws Exception { public static boolean isDeletedField(final Field element) throws DataAccessException {
return element.getDeclaredAnnotationsByType(DataDeleted.class).length != 0; return element.getDeclaredAnnotationsByType(DataDeleted.class).length != 0;
} }
public static boolean isCreatedAtField(final Field element) throws Exception { public static boolean isCreatedAtField(final Field element) throws DataAccessException {
return element.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0; return element.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
} }
public static boolean isUpdateAtField(final Field element) throws Exception { public static boolean isUpdateAtField(final Field element) throws DataAccessException {
return element.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0; return element.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
} }
public static boolean isdefaultNotRead(final Field element) throws Exception { public static boolean isdefaultNotRead(final Field element) throws DataAccessException {
return element.getDeclaredAnnotationsByType(DataNotRead.class).length != 0; return element.getDeclaredAnnotationsByType(DataNotRead.class).length != 0;
} }
public static boolean isIdField(final Field element) throws Exception { public static boolean isIdField(final Field element) throws DataAccessException {
return element.getDeclaredAnnotationsByType(Id.class).length != 0; return element.getDeclaredAnnotationsByType(Id.class).length != 0;
} }
public static String getDeletedFieldName(final Class<?> clazz) throws Exception { public static String getDeletedFieldName(final Class<?> clazz) throws DataAccessException {
try { try {
for (final Field elem : clazz.getFields()) { for (final Field elem : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it .. // static field is only for internal global declaration ==> remove it ..
@@ -352,7 +366,7 @@ public class AnnotationTools {
return null; return null;
} }
public static String getUpdatedFieldName(final Class<?> clazz) throws Exception { public static String getUpdatedFieldName(final Class<?> clazz) throws DataAccessException {
try { try {
for (final Field elem : clazz.getFields()) { for (final Field elem : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it .. // static field is only for internal global declaration ==> remove it ..
@@ -386,15 +400,16 @@ public class AnnotationTools {
return null; return null;
} }
public static List<String> getFieldsNames(final Class<?> clazz) throws Exception { public static List<String> getFieldsNames(final Class<?> clazz) throws DataAccessException {
return getFieldsNamesFilter(clazz, false); return getFieldsNamesFilter(clazz, false);
} }
public static List<String> getAllFieldsNames(final Class<?> clazz) throws Exception { public static List<String> getAllFieldsNames(final Class<?> clazz) throws DataAccessException {
return getFieldsNamesFilter(clazz, true); return getFieldsNamesFilter(clazz, true);
} }
private static List<String> getFieldsNamesFilter(final Class<?> clazz, final boolean full) throws Exception { private static List<String> getFieldsNamesFilter(final Class<?> clazz, final boolean full)
throws DataAccessException {
final List<String> out = new ArrayList<>(); final List<String> out = new ArrayList<>();
for (final Field field : clazz.getFields()) { for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it .. // static field is only for internal global declaration ==> remove it ..
@@ -409,12 +424,12 @@ public class AnnotationTools {
return out; return out;
} }
public static boolean isGenericField(final Field elem) throws Exception { public static boolean isGenericField(final Field elem) throws DataAccessException {
return AnnotationTools.isPrimaryKey(elem) || AnnotationTools.isCreatedAtField(elem) return AnnotationTools.isPrimaryKey(elem) || AnnotationTools.isCreatedAtField(elem)
|| AnnotationTools.isUpdateAtField(elem) || AnnotationTools.isDeletedField(elem); || AnnotationTools.isUpdateAtField(elem) || AnnotationTools.isDeletedField(elem);
} }
public static Field getFieldOfId(final Class<?> clazz) throws Exception { public static Field getFieldOfId(final Class<?> clazz) throws DataAccessException {
for (final Field field : clazz.getFields()) { for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it .. // static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) { if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
@@ -427,7 +442,7 @@ public class AnnotationTools {
return null; return null;
} }
public static Field getFieldNamed(final Class<?> clazz, final String name) throws Exception { public static Field getFieldNamed(final Class<?> clazz, final String name) throws DataAccessException {
for (final Field field : clazz.getFields()) { for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it .. // static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) { if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {

View File

@@ -5,6 +5,7 @@ import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -26,6 +27,7 @@ import org.kar.archidata.annotation.security.PermitTokenInURI;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition; import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.options.Condition; import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.exception.FailException;
import org.kar.archidata.filter.GenericContext; import org.kar.archidata.filter.GenericContext;
import org.kar.archidata.model.Data; import org.kar.archidata.model.Data;
import org.kar.archidata.tools.ConfigBaseVariable; import org.kar.archidata.tools.ConfigBaseVariable;
@@ -191,7 +193,8 @@ public class DataResource {
LOGGER.info("Move done"); LOGGER.info("Move done");
} }
public static String saveTemporaryFile(final InputStream uploadedInputStream, final long idData) { public static String saveTemporaryFile(final InputStream uploadedInputStream, final long idData)
throws FailException {
return saveFile(uploadedInputStream, DataResource.getTmpFileInData(idData)); return saveFile(uploadedInputStream, DataResource.getTmpFileInData(idData));
} }
@@ -208,35 +211,35 @@ public class DataResource {
} }
// save uploaded file to a defined location on the server // save uploaded file to a defined location on the server
static String saveFile(final InputStream uploadedInputStream, final String serverLocation) { static String saveFile(final InputStream uploadedInputStream, final String serverLocation) throws FailException {
String out = ""; String out = "";
try { MessageDigest md = null;
OutputStream outpuStream = new FileOutputStream(new File(serverLocation)); try (OutputStream outpuStream = new FileOutputStream(new File(serverLocation))) {
md = MessageDigest.getInstance("SHA-512");
} catch (final IOException ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Can not write in temporary file", ex);
} catch (final NoSuchAlgorithmException ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Can not find sha512 algorithms", ex);
}
if (md != null) {
try (OutputStream outpuStream = new FileOutputStream(new File(serverLocation))) {
int read = 0; int read = 0;
final byte[] bytes = new byte[CHUNK_SIZE_IN]; final byte[] bytes = new byte[CHUNK_SIZE_IN];
final MessageDigest md = MessageDigest.getInstance("SHA-512");
outpuStream = new FileOutputStream(new File(serverLocation));
while ((read = uploadedInputStream.read(bytes)) != -1) { while ((read = uploadedInputStream.read(bytes)) != -1) {
// logger.info("write {}", read); // logger.info("write {}", read);
md.update(bytes, 0, read); md.update(bytes, 0, read);
outpuStream.write(bytes, 0, read); outpuStream.write(bytes, 0, read);
} }
LOGGER.info("Flush input stream ... {}", serverLocation); LOGGER.info("Flush input stream ... {}", serverLocation);
System.out.flush();
outpuStream.flush(); outpuStream.flush();
outpuStream.close();
// create the end of sha512 // create the end of sha512
final byte[] sha512Digest = md.digest(); final byte[] sha512Digest = md.digest();
// convert in hexadecimal // convert in hexadecimal
out = bytesToHex(sha512Digest); out = bytesToHex(sha512Digest);
uploadedInputStream.close(); uploadedInputStream.close();
} catch (final IOException ex) { } catch (final IOException ex) {
LOGGER.info("Can not write in temporary file ... "); throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Can not write in temporary file", ex);
ex.printStackTrace(); }
} catch (final NoSuchAlgorithmException ex) {
LOGGER.info("Can not find sha512 algorithms");
ex.printStackTrace();
} }
return out; return out;
} }
@@ -267,7 +270,7 @@ public class DataResource {
public void uploadFile( public void uploadFile(
@Context final SecurityContext sc, @Context final SecurityContext sc,
@FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) { @FormDataParam("file") final FormDataContentDisposition fileMetaData) throws FailException {
final GenericContext gc = (GenericContext) sc.getUserPrincipal(); final GenericContext gc = (GenericContext) sc.getUserPrincipal();
LOGGER.info("==================================================="); LOGGER.info("===================================================");
LOGGER.info("== DATA uploadFile {}", (gc == null ? "null" : gc.userByToken)); LOGGER.info("== DATA uploadFile {}", (gc == null ? "null" : gc.userByToken));
@@ -277,8 +280,9 @@ public class DataResource {
final String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId++; final String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId++;
try { try {
createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator); createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator);
} catch (final IOException e) { } catch (final IOException ex) {
e.printStackTrace(); throw new FailException(Response.Status.INTERNAL_SERVER_ERROR,
"Impossible to create the folder in the server", ex);
} }
saveFile(fileInputStream, filePath); saveFile(fileInputStream, filePath);
} }
@@ -293,16 +297,21 @@ public class DataResource {
@Context final SecurityContext sc, @Context final SecurityContext sc,
@QueryParam(HttpHeaders.AUTHORIZATION) final String token, @QueryParam(HttpHeaders.AUTHORIZATION) final String token,
@HeaderParam("Range") final String range, @HeaderParam("Range") final String range,
@PathParam("uuid") final UUID uuid) throws Exception { @PathParam("uuid") final UUID uuid) throws FailException {
final GenericContext gc = (GenericContext) sc.getUserPrincipal(); final GenericContext gc = (GenericContext) sc.getUserPrincipal();
// logger.info("==================================================="); // logger.info("===================================================");
LOGGER.info("== DATA retrieveDataId ? id={} user={}", uuid, (gc == null ? "null" : gc.userByToken)); LOGGER.info("== DATA retrieveDataId ? id={} user={}", uuid, (gc == null ? "null" : gc.userByToken));
// logger.info("==================================================="); // logger.info("===================================================");
final Data value = getSmall(uuid); final Data value = getSmall(uuid);
if (value == null) { if (value == null) {
Response.status(404).entity("media NOT FOUND: " + uuid).type("text/plain").build(); return Response.status(404).entity("media NOT FOUND: " + uuid).type("text/plain").build();
}
try {
return buildStream(getFileData(uuid), range,
value.mimeType == null ? "application/octet-stream" : value.mimeType);
} catch (final Exception ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to build output stream", ex);
} }
return buildStream(getFileData(uuid), range, value.mimeType);
} }
@GET @GET
@@ -316,7 +325,7 @@ public class DataResource {
@Context final SecurityContext sc, @Context final SecurityContext sc,
@QueryParam(HttpHeaders.AUTHORIZATION) final String token, @QueryParam(HttpHeaders.AUTHORIZATION) final String token,
@HeaderParam("Range") final String range, @HeaderParam("Range") final String range,
@PathParam("uuid") final UUID uuid) throws Exception { @PathParam("uuid") final UUID uuid) throws FailException {
// GenericContext gc = (GenericContext) sc.getUserPrincipal(); // GenericContext gc = (GenericContext) sc.getUserPrincipal();
// logger.info("==================================================="); // logger.info("===================================================");
// logger.info("== DATA retrieveDataThumbnailId ? {}", (gc==null?"null":gc.user)); // logger.info("== DATA retrieveDataThumbnailId ? {}", (gc==null?"null":gc.user));
@@ -335,7 +344,12 @@ public class DataResource {
// || value.mimeType.contentEquals("image/webp") // || value.mimeType.contentEquals("image/webp")
) { ) {
// reads input image // reads input image
final BufferedImage inputImage = ImageIO.read(inputFile); BufferedImage inputImage;
try {
inputImage = ImageIO.read(inputFile);
} catch (final IOException ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to READ the image", ex);
}
final int scaledWidth = 250; final int scaledWidth = 250;
final int scaledHeight = (int) ((float) inputImage.getHeight() / (float) inputImage.getWidth() final int scaledHeight = (int) ((float) inputImage.getHeight() / (float) inputImage.getWidth()
* scaledWidth); * scaledWidth);
@@ -368,7 +382,11 @@ public class DataResource {
out.cacheControl(cc); out.cacheControl(cc);
return out.build(); return out.build();
} }
try {
return buildStream(filePathName, range, value.mimeType); return buildStream(filePathName, range, value.mimeType);
} catch (final Exception ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to build output stream", ex);
}
} }
@GET @GET
@@ -389,17 +407,20 @@ public class DataResource {
// logger.info("==================================================="); // logger.info("===================================================");
final Data value = getSmall(uuid); final Data value = getSmall(uuid);
if (value == null) { if (value == null) {
Response.status(404).entity("media NOT FOUND: " + uuid).type("text/plain").build(); return Response.status(404).entity("media NOT FOUND: " + uuid).type("text/plain").build();
} }
return buildStream(getFileData(uuid), range, value.mimeType); return buildStream(getFileData(uuid), range,
value.mimeType == null ? "application/octet-stream" : value.mimeType);
} }
/** Adapted from http://stackoverflow.com/questions/12768812/video-streaming-to-ipad-does-not-work-with-tapestry5/12829541#12829541 /** Adapted from http://stackoverflow.com/questions/12768812/video-streaming-to-ipad-does-not-work-with-tapestry5/12829541#12829541
* *
* @param range range header * @param range range header
* @return Streaming output * @return Streaming output
* @throws FileNotFoundException
* @throws Exception IOException if an error occurs in streaming. */ * @throws Exception IOException if an error occurs in streaming. */
private Response buildStream(final String filename, final String range, final String mimeType) throws Exception { private Response buildStream(final String filename, final String range, final String mimeType)
throws FailException {
final File file = new File(filename); final File file = new File(filename);
// logger.info("request range : {}", range); // logger.info("request range : {}", range);
// range not requested : Firefox does not send range headers // range not requested : Firefox does not send range headers
@@ -446,7 +467,7 @@ public class DataResource {
} }
final String responseRange = String.format("bytes %d-%d/%d", from, to, file.length()); final String responseRange = String.format("bytes %d-%d/%d", from, to, file.length());
// logger.info("responseRange: {}", responseRange); // logger.info("responseRange: {}", responseRange);
final RandomAccessFile raf = new RandomAccessFile(file, "r"); try (final RandomAccessFile raf = new RandomAccessFile(file, "r")) {
raf.seek(from); raf.seek(from);
final long len = to - from + 1; final long len = to - from + 1;
@@ -459,6 +480,11 @@ public class DataResource {
out.type(mimeType); out.type(mimeType);
} }
return out.build(); return out.build();
} catch (final FileNotFoundException ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to find the required file.", ex);
} catch (final IOException ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to access to the required file.", ex);
}
} }
public static void undelete(final Long id) throws Exception { public static void undelete(final Long id) throws Exception {

View File

@@ -21,8 +21,8 @@ public class ExceptionCatcher implements ExceptionMapper<Exception> {
} }
private RestErrorResponse build(final Exception exception) { private RestErrorResponse build(final Exception exception) {
return new RestErrorResponse(Response.Status.INTERNAL_SERVER_ERROR, "Catch Unknown Exception", return new RestErrorResponse(Response.Status.INTERNAL_SERVER_ERROR,
exception.getMessage()); "Catch Unknown Exception: " + exception.getClass().getCanonicalName(), exception.getMessage());
} }
} }

View File

@@ -13,11 +13,12 @@ public class FailExceptionCatcher implements ExceptionMapper<FailException> {
@Override @Override
public Response toResponse(final FailException exception) { public Response toResponse(final FailException exception) {
LOGGER.warn("Catch FailException:"); LOGGER.warn("Catch FailException: {}", exception.getLocalizedMessage());
final RestErrorResponse ret = build(exception); final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={}", ret.uuid); LOGGER.error("Error UUID={}", ret.uuid);
// Not display backtrace ==> this may be a normal case ... if (exception.exception != null) {
// exception.printStackTrace(); exception.exception.printStackTrace();
}
return Response.status(exception.status).entity(ret).type(MediaType.APPLICATION_JSON).build(); return Response.status(exception.status).entity(ret).type(MediaType.APPLICATION_JSON).build();
} }

View File

@@ -0,0 +1,24 @@
package org.kar.archidata.catcher;
import org.glassfish.jersey.server.ResourceConfig;
public class GenericCatcher {
/**
* Add All the the generic catcher to standardize returns.
* @param rc Resource exception model.
*/
public static void addAll(final ResourceConfig rc) {
// Generic Json parsing error
rc.register(JacksonExceptionCatcher.class);
// Catch jakarta generic errors
rc.register(WebApplicationExceptionCatcher.class);
// Archidata exceptions
rc.register(InputExceptionCatcher.class);
rc.register(SystemExceptionCatcher.class);
rc.register(FailExceptionCatcher.class);
// generic Exception catcher
rc.register(ExceptionCatcher.class);
}
}

View File

@@ -3,17 +3,17 @@ package org.kar.archidata.catcher;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JacksonException;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper; import jakarta.ws.rs.ext.ExceptionMapper;
public class JacksonCatcher implements ExceptionMapper<JsonProcessingException> { public class JacksonExceptionCatcher implements ExceptionMapper<JacksonException> {
private static final Logger LOGGER = LoggerFactory.getLogger(JacksonCatcher.class); private static final Logger LOGGER = LoggerFactory.getLogger(JacksonExceptionCatcher.class);
@Override @Override
public Response toResponse(final JsonProcessingException exception) { public Response toResponse(final JacksonException exception) {
LOGGER.warn("Catch exception Input data parsing:"); LOGGER.warn("Catch exception Input data parsing:");
final RestErrorResponse ret = build(exception); final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={}", ret.uuid); LOGGER.error("Error UUID={}", ret.uuid);

View File

@@ -3,23 +3,23 @@ package org.kar.archidata.catcher;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jakarta.ws.rs.ClientErrorException; import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper; import jakarta.ws.rs.ext.ExceptionMapper;
public class FailException404API implements ExceptionMapper<ClientErrorException> { public class WebApplicationExceptionCatcher implements ExceptionMapper<WebApplicationException> {
private static final Logger LOGGER = LoggerFactory.getLogger(FailException404API.class); private static final Logger LOGGER = LoggerFactory.getLogger(WebApplicationExceptionCatcher.class);
@Override @Override
public Response toResponse(final ClientErrorException exception) { public Response toResponse(final WebApplicationException exception) {
final RestErrorResponse ret = build(exception); final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={}", ret.uuid); LOGGER.error("Error UUID={}", ret.uuid);
return Response.status(exception.getResponse().getStatusInfo().toEnum()).entity(ret) return Response.status(exception.getResponse().getStatusInfo().toEnum()).entity(ret)
.type(MediaType.APPLICATION_JSON).build(); .type(MediaType.APPLICATION_JSON).build();
} }
private RestErrorResponse build(final ClientErrorException exception) { private RestErrorResponse build(final WebApplicationException exception) {
return new RestErrorResponse(exception.getResponse().getStatusInfo().toEnum(), "Catch system exception", return new RestErrorResponse(exception.getResponse().getStatusInfo().toEnum(), "Catch system exception",
exception.getMessage()); exception.getMessage());
} }

View File

@@ -25,7 +25,7 @@ import org.kar.archidata.annotation.UpdateTimestamp;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson; import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany; import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.addOn.AddOnManyToOne; import org.kar.archidata.dataAccess.addOn.AddOnManyToOne;
import org.kar.archidata.dataAccess.addOn.AddOnSQLTableExternalForeinKeyAsList; import org.kar.archidata.dataAccess.addOn.AddOnOneToMany;
import org.kar.archidata.dataAccess.options.CheckFunction; import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.dataAccess.options.Condition; import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.DBInterfaceOption; import org.kar.archidata.dataAccess.options.DBInterfaceOption;
@@ -47,6 +47,7 @@ import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.InternalServerErrorException; import jakarta.ws.rs.InternalServerErrorException;
@@ -64,7 +65,7 @@ public class DataAccess {
static { static {
addOn.add(new AddOnManyToMany()); addOn.add(new AddOnManyToMany());
addOn.add(new AddOnManyToOne()); addOn.add(new AddOnManyToOne());
addOn.add(new AddOnSQLTableExternalForeinKeyAsList()); addOn.add(new AddOnOneToMany());
addOn.add(new AddOnDataJson()); addOn.add(new AddOnDataJson());
} }
@@ -250,6 +251,14 @@ public class DataAccess {
return out; return out;
} }
public static UUID getListOfRawUUID(final ResultSet rs, final int iii) throws SQLException, DataAccessException {
final byte[] elem = rs.getBytes(iii);
if (rs.wasNull()) {
return null;
}
return UuidUtils.asUuid(elem);
}
protected static <T> void setValuedb( protected static <T> void setValuedb(
final Class<?> type, final Class<?> type,
final T data, final T data,
@@ -789,6 +798,7 @@ public class DataAccess {
return out; return out;
} }
@SuppressFBWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING")
public static <T> T insert(final T data, final QueryOption... option) throws Exception { public static <T> T insert(final T data, final QueryOption... option) throws Exception {
final Class<?> clazz = data.getClass(); final Class<?> clazz = data.getClass();
final QueryOptions options = new QueryOptions(option); final QueryOptions options = new QueryOptions(option);
@@ -895,7 +905,7 @@ public class DataAccess {
for (final OrderBy order : orders) { for (final OrderBy order : orders) {
order.generateQuery(query, tableName); order.generateQuery(query, tableName);
} }
LOGGER.warn("generate the query: '{}'", query.toString()); LOGGER.debug("generate the query: '{}'", query.toString());
// prepare the request: // prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), final PreparedStatement ps = entry.connection.prepareStatement(query.toString(),
Statement.RETURN_GENERATED_KEYS); Statement.RETURN_GENERATED_KEYS);
@@ -961,13 +971,12 @@ public class DataAccess {
// uniqueSQLUUID = generatedKeys.getObject(1, UUID.class); // uniqueSQLUUID = generatedKeys.getObject(1, UUID.class);
/* final Object obj = generatedKeys.getObject(1); final BigInteger bigint = (BigInteger) generatedKeys.getObject(1); uniqueSQLUUID = UuidUtils.asUuid(bigint); final UUID /* final Object obj = generatedKeys.getObject(1); final BigInteger bigint = (BigInteger) generatedKeys.getObject(1); uniqueSQLUUID = UuidUtils.asUuid(bigint); final UUID
* generatedUUID = (UUID) generatedKeys.getObject(1); System.out.println("UUID généré: " + generatedUUID); */ * generatedUUID = (UUID) generatedKeys.getObject(1); System.out.println("UUID généré: " + generatedUUID); */
final Object obj = generatedKeys.getObject(1); //final Object obj = generatedKeys.getObject(1);
final byte[] tmpid = generatedKeys.getBytes(1); final byte[] tmpid = generatedKeys.getBytes(1);
uniqueSQLUUID = UuidUtils.asUuid(tmpid); uniqueSQLUUID = UuidUtils.asUuid(tmpid);
} else { } else {
uniqueSQLID = generatedKeys.getLong(1); uniqueSQLID = generatedKeys.getLong(1);
} }
} else { } else {
throw new SQLException("Creating node failed, no ID obtained (1)."); throw new SQLException("Creating node failed, no ID obtained (1).");
} }
@@ -977,6 +986,7 @@ public class DataAccess {
throw new SQLException("Creating node failed, no ID obtained (2)."); throw new SQLException("Creating node failed, no ID obtained (2).");
} }
} }
ps.close();
if (primaryKeyField != null) { if (primaryKeyField != null) {
if (primaryKeyField.getType() == Long.class) { if (primaryKeyField.getType() == Long.class) {
primaryKeyField.set(data, uniqueSQLID); primaryKeyField.set(data, uniqueSQLID);
@@ -1020,7 +1030,7 @@ public class DataAccess {
} }
public static <ID_TYPE> QueryCondition getTableIdCondition(final Class<?> clazz, final ID_TYPE idKey) public static <ID_TYPE> QueryCondition getTableIdCondition(final Class<?> clazz, final ID_TYPE idKey)
throws Exception { throws DataAccessException {
// Find the ID field type .... // Find the ID field type ....
final Field idField = AnnotationTools.getIdField(clazz); final Field idField = AnnotationTools.getIdField(clazz);
if (idField == null) { if (idField == null) {
@@ -1107,10 +1117,14 @@ public class DataAccess {
return updateWhere(data, options); return updateWhere(data, options);
} }
public static <T> int updateWhere(final T data, final QueryOptions options) throws Exception { @SuppressFBWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING")
public static <T> int updateWhere(final T data, QueryOptions options) throws Exception {
final Class<?> clazz = data.getClass(); final Class<?> clazz = data.getClass();
if (options == null) {
options = new QueryOptions();
}
final Condition condition = conditionFusionOrEmpty(options, true); final Condition condition = conditionFusionOrEmpty(options, true);
final List<FilterValue> filters = options.get(FilterValue.class); final List<FilterValue> filters = options != null ? options.get(FilterValue.class) : new ArrayList<>();
if (filters.size() != 1) { if (filters.size() != 1) {
throw new DataAccessException("request a gets without/or with more 1 filter of values"); throw new DataAccessException("request a gets without/or with more 1 filter of values");
} }
@@ -1185,8 +1199,8 @@ public class DataAccess {
if (!firstField) { if (!firstField) {
LOGGER.debug("generate update query: '{}'", query.toString()); LOGGER.debug("generate update query: '{}'", query.toString());
// prepare the request: // prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), try (final PreparedStatement ps = entry.connection.prepareStatement(query.toString(),
Statement.RETURN_GENERATED_KEYS); Statement.RETURN_GENERATED_KEYS)) {
final CountInOut iii = new CountInOut(1); final CountInOut iii = new CountInOut(1);
for (final Field field : clazz.getFields()) { for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it .. // static field is only for internal global declaration ==> remove it ..
@@ -1217,7 +1231,9 @@ public class DataAccess {
} }
} }
condition.injectQuery(ps, iii); condition.injectQuery(ps, iii);
return ps.executeUpdate(); final int out = ps.executeUpdate();
return out;
}
} }
} catch (final SQLException ex) { } catch (final SQLException ex) {
ex.printStackTrace(); ex.printStackTrace();
@@ -1281,17 +1297,19 @@ public class DataAccess {
throws SQLException, IOException { throws SQLException, IOException {
final QueryOptions options = new QueryOptions(option); final QueryOptions options = new QueryOptions(option);
final DBEntry entry = DBInterfaceOption.getAutoEntry(options); final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
final Statement stmt = entry.connection.createStatement(); try (final Statement stmt = entry.connection.createStatement()) {
return stmt.executeUpdate(query); return stmt.executeUpdate(query);
} }
}
public static boolean executeQuery(final String query, final QueryOption... option) public static boolean executeQuery(final String query, final QueryOption... option)
throws SQLException, IOException { throws SQLException, IOException {
final QueryOptions options = new QueryOptions(option); final QueryOptions options = new QueryOptions(option);
final DBEntry entry = DBInterfaceOption.getAutoEntry(options); final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
final Statement stmt = entry.connection.createStatement(); try (final Statement stmt = entry.connection.createStatement()) {
return stmt.execute(query); return stmt.execute(query);
} }
}
public static <T> T getWhere(final Class<T> clazz, final QueryOptions options) throws Exception { public static <T> T getWhere(final Class<T> clazz, final QueryOptions options) throws Exception {
options.add(new Limit(1)); options.add(new Limit(1));
@@ -1357,6 +1375,9 @@ public class DataAccess {
public static Condition conditionFusionOrEmpty(final QueryOptions options, final boolean throwIfEmpty) public static Condition conditionFusionOrEmpty(final QueryOptions options, final boolean throwIfEmpty)
throws DataAccessException { throws DataAccessException {
if (options == null) {
return new Condition();
}
final List<Condition> conditions = options.get(Condition.class); final List<Condition> conditions = options.get(Condition.class);
if (conditions.size() == 0) { if (conditions.size() == 0) {
if (throwIfEmpty) { if (throwIfEmpty) {
@@ -1379,14 +1400,13 @@ public class DataAccess {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> List<T> getsWhere(final Class<T> clazz, final QueryOptions options) throws Exception { public static <T> List<T> getsWhere(final Class<T> clazz, final QueryOptions options)
throws DataAccessException, IOException {
final Condition condition = conditionFusionOrEmpty(options, false); final Condition condition = conditionFusionOrEmpty(options, false);
final List<LazyGetter> lazyCall = new ArrayList<>(); final List<LazyGetter> lazyCall = new ArrayList<>();
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz); final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
final DBEntry entry = DBInterfaceOption.getAutoEntry(options);
final List<T> outs = new ArrayList<>(); final List<T> outs = new ArrayList<>();
// real add in the BDD: try (final DBEntry entry = DBInterfaceOption.getAutoEntry(options)) {
try {
final CountInOut count = new CountInOut(); final CountInOut count = new CountInOut();
final StringBuilder querySelect = new StringBuilder(); final StringBuilder querySelect = new StringBuilder();
StringBuilder query = new StringBuilder(); StringBuilder query = new StringBuilder();
@@ -1414,10 +1434,10 @@ public class DataAccess {
} else if (limits.size() > 1) { } else if (limits.size() > 1) {
throw new DataAccessException("Request with multiple 'limit'..."); throw new DataAccessException("Request with multiple 'limit'...");
} }
LOGGER.warn("generate the query: '{}'", query.toString()); LOGGER.debug("generate the query: '{}'", query.toString());
// prepare the request: // prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), try (final PreparedStatement ps = entry.connection.prepareStatement(query.toString(),
Statement.RETURN_GENERATED_KEYS); Statement.RETURN_GENERATED_KEYS)) {
final CountInOut iii = new CountInOut(1); final CountInOut iii = new CountInOut(1);
condition.injectQuery(ps, iii); condition.injectQuery(ps, iii);
if (limits.size() == 1) { if (limits.size() == 1) {
@@ -1436,13 +1456,13 @@ public class DataAccess {
for (final LazyGetter elem : lazyCall) { for (final LazyGetter elem : lazyCall) {
elem.doRequest(); elem.doRequest();
} }
}
} catch (final SQLException ex) { } catch (final SQLException ex) {
ex.printStackTrace(); ex.printStackTrace();
throw ex; throw new DataAccessException("Catch a SQL Exception: " + ex.getMessage());
} catch (final Exception ex) { } catch (final Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
} finally { throw new DataAccessException("Catch an Exception: " + ex.getMessage());
entry.close();
} }
return outs; return outs;
} }
@@ -1519,7 +1539,7 @@ public class DataAccess {
} else if (limits.size() > 1) { } else if (limits.size() > 1) {
throw new DataAccessException("Request with multiple 'limit'..."); throw new DataAccessException("Request with multiple 'limit'...");
} }
LOGGER.warn("generate the query: '{}'", query.toString()); LOGGER.debug("generate the query: '{}'", query.toString());
// prepare the request: // prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), final PreparedStatement ps = entry.connection.prepareStatement(query.toString(),
Statement.RETURN_GENERATED_KEYS); Statement.RETURN_GENERATED_KEYS);
@@ -1560,10 +1580,6 @@ public class DataAccess {
return getsWhere(clazz, option); return getsWhere(clazz, option);
} }
public static <ID_TYPE> int delete(final Class<?> clazz, final ID_TYPE id) throws Exception {
return delete(clazz, id, null);
}
/** Delete items with the specific Id (cf @Id) and some options. If the Entity is manage as a softDeleted model, then it is flag as removed (if not already done before). /** Delete items with the specific Id (cf @Id) and some options. If the Entity is manage as a softDeleted model, then it is flag as removed (if not already done before).
* @param <ID_TYPE> Type of the reference @Id * @param <ID_TYPE> Type of the reference @Id
* @param clazz Data model that might remove element * @param clazz Data model that might remove element
@@ -1660,18 +1676,18 @@ public class DataAccess {
} }
} }
public static <ID_TYPE> int unsetDelete(final Class<?> clazz, final ID_TYPE id) throws Exception { public static <ID_TYPE> int unsetDelete(final Class<?> clazz, final ID_TYPE id) throws DataAccessException {
return unsetDeleteWhere(clazz, new Condition(getTableIdCondition(clazz, id))); return unsetDeleteWhere(clazz, new Condition(getTableIdCondition(clazz, id)));
} }
public static <ID_TYPE> int unsetDelete(final Class<?> clazz, final ID_TYPE id, final QueryOption... option) public static <ID_TYPE> int unsetDelete(final Class<?> clazz, final ID_TYPE id, final QueryOption... option)
throws Exception { throws DataAccessException {
final QueryOptions options = new QueryOptions(option); final QueryOptions options = new QueryOptions(option);
options.add(new Condition(getTableIdCondition(clazz, id))); options.add(new Condition(getTableIdCondition(clazz, id)));
return unsetDeleteWhere(clazz, options.getAllArray()); return unsetDeleteWhere(clazz, options.getAllArray());
} }
public static int unsetDeleteWhere(final Class<?> clazz, final QueryOption... option) throws Exception { public static int unsetDeleteWhere(final Class<?> clazz, final QueryOption... option) throws DataAccessException {
final QueryOptions options = new QueryOptions(option); final QueryOptions options = new QueryOptions(option);
final Condition condition = conditionFusionOrEmpty(options, true); final Condition condition = conditionFusionOrEmpty(options, true);
final String tableName = AnnotationTools.getTableName(clazz, options); final String tableName = AnnotationTools.getTableName(clazz, options);
@@ -1679,7 +1695,12 @@ public class DataAccess {
if (deletedFieldName == null) { if (deletedFieldName == null) {
throw new DataAccessException("The class " + clazz.getCanonicalName() + " has no deleted field"); throw new DataAccessException("The class " + clazz.getCanonicalName() + " has no deleted field");
} }
final DBEntry entry = DBInterfaceOption.getAutoEntry(options); DBEntry entry;
try {
entry = DBInterfaceOption.getAutoEntry(options);
} catch (final IOException ex) {
throw new DataAccessException("Fail to connect the DB: " + ex.getMessage());
}
final StringBuilder query = new StringBuilder(); final StringBuilder query = new StringBuilder();
query.append("UPDATE `"); query.append("UPDATE `");
query.append(tableName); query.append(tableName);
@@ -1689,13 +1710,14 @@ public class DataAccess {
// need to disable the deleted false because the model must be unselected to be updated. // need to disable the deleted false because the model must be unselected to be updated.
options.add(QueryOptions.ACCESS_DELETED_ITEMS); options.add(QueryOptions.ACCESS_DELETED_ITEMS);
condition.whereAppendQuery(query, tableName, options, deletedFieldName); condition.whereAppendQuery(query, tableName, options, deletedFieldName);
try { try (final PreparedStatement ps = entry.connection.prepareStatement(query.toString())) {
final PreparedStatement ps = entry.connection.prepareStatement(query.toString());
final CountInOut iii = new CountInOut(1); final CountInOut iii = new CountInOut(1);
condition.injectQuery(ps, iii); condition.injectQuery(ps, iii);
return ps.executeUpdate(); return ps.executeUpdate();
} finally { } catch (final SQLException ex) {
entry.close(); throw new DataAccessException("Catch SQL error:" + ex.getMessage());
} catch (final Exception ex) {
throw new DataAccessException("Fail to excute the SQL query:" + ex.getMessage());
} }
} }
@@ -1812,7 +1834,7 @@ public class DataAccess {
} else if (limits.size() > 1) { } else if (limits.size() > 1) {
throw new DataAccessException("Request with multiple 'limit'..."); throw new DataAccessException("Request with multiple 'limit'...");
} }
LOGGER.warn("generate the query: '{}'", query.toString()); LOGGER.debug("generate the query: '{}'", query.toString());
// prepare the request: // prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), final PreparedStatement ps = entry.connection.prepareStatement(query.toString(),
Statement.RETURN_GENERATED_KEYS); Statement.RETURN_GENERATED_KEYS);

View File

@@ -4,6 +4,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -26,6 +27,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@@ -53,7 +55,7 @@ public class AddOnDataJson implements DataAccessAddOn {
@Override @Override
public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii) public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii)
throws Exception { throws IllegalArgumentException, IllegalAccessException, SQLException, JsonProcessingException {
final Object data = field.get(rootObject); final Object data = field.get(rootObject);
if (data == null) { if (data == null) {
ps.setNull(iii.value, Types.VARCHAR); ps.setNull(iii.value, Types.VARCHAR);
@@ -70,7 +72,7 @@ public class AddOnDataJson implements DataAccessAddOn {
} }
@Override @Override
public boolean isInsertAsync(final Field field) throws Exception { public boolean isInsertAsync(final Field field) {
return false; return false;
} }

View File

@@ -172,6 +172,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
} }
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0]; .getActualTypeArguments()[0];
// TODO: manage better the eager and lazy !!
if (objectClass == Long.class || objectClass == UUID.class) { if (objectClass == Long.class || objectClass == UUID.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options); generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options);
} }

View File

@@ -238,7 +238,11 @@ public class AddOnManyToOne implements DataAccessAddOn {
if (dataNew != null && countNotNull.value != 0) { if (dataNew != null && countNotNull.value != 0) {
field.set(data, dataNew); field.set(data, dataNew);
} }
} else { return;
}
final Field remotePrimaryKeyField = AnnotationTools.getFieldOfId(objectClass);
final Class<?> remotePrimaryKeyType = remotePrimaryKeyField.getType();
if (remotePrimaryKeyType == Long.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ... // here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final Long foreignKey = rs.getLong(count.value); final Long foreignKey = rs.getLong(count.value);
count.inc(); count.inc();
@@ -254,6 +258,22 @@ public class AddOnManyToOne implements DataAccessAddOn {
}; };
lazyCall.add(lambda); lazyCall.add(lambda);
} }
} else if (remotePrimaryKeyType == UUID.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final UUID foreignKey = DataAccess.getListOfRawUUID(rs, count.value);
count.inc();
if (foreignKey != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types ....
final Object foreignData = DataAccess.get(decorators.targetEntity(), foreignKey);
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} }
} }
} }

View File

@@ -1,28 +1,36 @@
package org.kar.archidata.dataAccess.addOn; package org.kar.archidata.dataAccess.addOn;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.kar.archidata.annotation.AnnotationTools; import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.dataAccess.CountInOut; import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataAccessAddOn; import org.kar.archidata.dataAccess.DataAccessAddOn;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter; import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryOptions; import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
public class AddOnOneToMany implements DataAccessAddOn { public class AddOnOneToMany implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class); static final Logger LOGGER = LoggerFactory.getLogger(AddOnOneToMany.class);
static final String SEPARATOR_LONG = "-";
/** Convert the list if external id in a string '-' separated /** Convert the list if external id in a string '-' separated
* @param ids List of value (null are removed) * @param ids List of value (null are removed)
@@ -77,15 +85,7 @@ public class AddOnOneToMany implements DataAccessAddOn {
@Override @Override
public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii) public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii)
throws SQLException, IllegalArgumentException, IllegalAccessException { throws SQLException, IllegalArgumentException, IllegalAccessException {
final Object data = field.get(rootObject); throw new IllegalAccessException("Can not generate an inset of @OneToMany");
iii.inc();
if (data == null) {
ps.setNull(iii.value, Types.BIGINT);
} else {
@SuppressWarnings("unchecked")
final String dataTmp = getStringOfIds((List<Long>) data);
ps.setString(iii.value, dataTmp);
}
} }
@Override @Override
@@ -95,13 +95,87 @@ public class AddOnOneToMany implements DataAccessAddOn {
@Override @Override
public boolean isInsertAsync(final Field field) throws Exception { public boolean isInsertAsync(final Field field) throws Exception {
// TODO: can be implemented later...
return false; return false;
} }
@Override @Override
public boolean canRetrieve(final Field field) { public boolean canRetrieve(final Field field) {
if (field.getType() != List.class) {
return false; return false;
} }
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class) {
return true;
}
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return false;
}
if (decorators.targetEntity() == objectClass) {
return true;
}
return false;
}
public void generateConcatQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options,
final Class<?> targetEntity,
final String mappedBy) throws Exception {
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final String remoteTableName = AnnotationTools.getTableName(targetEntity);
final String remoteTablePrimaryKeyName = AnnotationTools
.getFieldName(AnnotationTools.getPrimaryKeyField(targetEntity));
final String tmpRemoteVariable = "tmp_" + Integer.toString(count.value);
final String remoteDeletedFieldName = AnnotationTools.getDeletedFieldName(targetEntity);
querySelect.append(" (SELECT GROUP_CONCAT(");
querySelect.append(tmpRemoteVariable);
querySelect.append(".");
querySelect.append(remoteTablePrimaryKeyName);
querySelect.append(" ");
if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
querySelect.append(", ");
} else {
querySelect.append("SEPARATOR ");
}
querySelect.append("'");
if (objectClass == Long.class) {
querySelect.append(SEPARATOR_LONG);
}
querySelect.append("') FROM ");
querySelect.append(remoteTableName);
querySelect.append(" ");
querySelect.append(tmpRemoteVariable);
querySelect.append(" WHERE ");
if (remoteDeletedFieldName != null) {
querySelect.append(tmpRemoteVariable);
querySelect.append(".");
querySelect.append(remoteDeletedFieldName);
querySelect.append(" = false");
querySelect.append(" AND ");
}
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(primaryKey);
querySelect.append(" = ");
querySelect.append(tmpRemoteVariable);
querySelect.append(".");
querySelect.append(mappedBy);
querySelect.append(" ");
querySelect.append(") AS ");
querySelect.append(name);
querySelect.append(" ");
}
@Override @Override
public void generateQuery( public void generateQuery(
@@ -112,12 +186,35 @@ public class AddOnOneToMany implements DataAccessAddOn {
@NotNull final StringBuilder query, @NotNull final StringBuilder query,
@NotNull final String name, @NotNull final String name,
@NotNull final CountInOut count, @NotNull final CountInOut count,
final QueryOptions options) { final QueryOptions options) throws Exception {
if (field.getType() != List.class) {
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return;
}
// TODO: manage better the eager and lazy !!
if (objectClass == Long.class || objectClass == UUID.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options,
decorators.targetEntity(), decorators.mappedBy());
return;
}
if (objectClass == decorators.targetEntity()) {
if (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else {
// Force a copy of the primaryKey to permit the async retrieve of the data
querySelect.append(" "); querySelect.append(" ");
querySelect.append(tableName); querySelect.append(tableName);
querySelect.append("."); querySelect.append(".");
querySelect.append(name); querySelect.append(primaryKey);
count.inc(); querySelect.append(" AS tmp_");
querySelect.append(Integer.toString(count.value));
}
}
} }
@Override @Override
@@ -127,16 +224,86 @@ public class AddOnOneToMany implements DataAccessAddOn {
final Object data, final Object data,
final CountInOut count, final CountInOut count,
final QueryOptions options, final QueryOptions options,
final List<LazyGetter> lazyCall) throws SQLException, IllegalArgumentException, IllegalAccessException { final List<LazyGetter> lazyCall) throws Exception {
final Long foreignKey = rs.getLong(count.value); if (field.getType() != List.class) {
LOGGER.error("Can not OneToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return;
}
if (objectClass == Long.class) {
final List<Long> idList = DataAccess.getListOfIds(rs, count.value, SEPARATOR_LONG);
field.set(data, idList);
count.inc(); count.inc();
if (!rs.wasNull()) { return;
} else if (objectClass == UUID.class) {
final List<UUID> idList = DataAccess.getListOfRawUUIDs(rs, count.value);
field.set(data, idList);
count.inc();
return;
}
if (objectClass == decorators.targetEntity()) {
field.set(data, foreignKey); Long parentIdTmp = null;
UUID parendUuidTmp = null;
try {
final String modelData = rs.getString(count.value);
parentIdTmp = Long.valueOf(modelData);
count.inc();
} catch (final NumberFormatException ex) {
final List<UUID> idList = DataAccess.getListOfRawUUIDs(rs, count.value);
parendUuidTmp = idList.get(0);
count.inc();
}
final Long parentId = parentIdTmp;
final UUID parendUuid = parendUuidTmp;
final String mappingKey = decorators.mappedBy();
// We get the parent ID ... ==> need to request the list of elements
if (objectClass == Long.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
return;
} else if (objectClass == UUID.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
return;
}
if (objectClass == decorators.targetEntity()) {
if (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else if (parentId != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = DataAccess.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parentId)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
} else if (parendUuid != null) {
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = DataAccess.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parendUuid)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
}
} }
} }
// TODO : refacto this table to manage a generic table with dynamic name to be serializable with the default system // TODO : refacto this table to manage a generic table with dynamic name to be serialize with the default system
@Override @Override
public void createTables( public void createTables(
final String tableName, final String tableName,
@@ -148,7 +315,6 @@ public class AddOnOneToMany implements DataAccessAddOn {
final boolean createIfNotExist, final boolean createIfNotExist,
final boolean createDrop, final boolean createDrop,
final int fieldId) throws Exception { final int fieldId) throws Exception {
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList, // This is a remote field ==> nothing to generate (it is stored in the remote object
postActionList, createIfNotExist, createDrop, fieldId, Long.class);
} }
} }

View File

@@ -1,136 +0,0 @@
package org.kar.archidata.dataAccess.addOn;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.addOn.SQLTableExternalForeinKeyAsList;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataAccessAddOn;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.validation.constraints.NotNull;
// TODO: maybe deprecated ==> use DataJson instead...
@Deprecated
public class AddOnSQLTableExternalForeinKeyAsList implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class);
static final String SEPARATOR = "-";
/** Convert the list if external id in a string '-' separated
* @param ids List of value (null are removed)
* @return '-' string separated */
protected static String getStringOfIds(final List<Long> ids) {
final List<Long> tmp = new ArrayList<>(ids);
return tmp.stream().map(String::valueOf).collect(Collectors.joining(SEPARATOR));
}
@Override
public Class<?> getAnnotationClass() {
return SQLTableExternalForeinKeyAsList.class;
}
@Override
public String getSQLFieldType(final Field field) throws Exception {
final String fieldName = AnnotationTools.getFieldName(field);
try {
return DataFactory.convertTypeInSQL(String.class, fieldName);
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
public boolean isCompatibleField(final Field field) {
final SQLTableExternalForeinKeyAsList decorators = field
.getDeclaredAnnotation(SQLTableExternalForeinKeyAsList.class);
return decorators != null;
}
@Override
public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii)
throws SQLException, IllegalArgumentException, IllegalAccessException {
final Object data = field.get(rootObject);
iii.inc();
if (data == null) {
ps.setNull(iii.value, Types.BIGINT);
} else {
@SuppressWarnings("unchecked")
final String dataTmp = getStringOfIds((List<Long>) data);
ps.setString(iii.value, dataTmp);
}
}
@Override
public boolean canInsert(final Field field) {
return true;
}
@Override
public boolean isInsertAsync(final Field field) throws Exception {
return false;
}
@Override
public boolean canRetrieve(final Field field) {
return true;
}
@Override
public void generateQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) {
count.inc();
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
}
@Override
public void fillFromQuery(
final ResultSet rs,
final Field field,
final Object data,
final CountInOut count,
final QueryOptions options,
final List<LazyGetter> lazyCall) throws SQLException, IllegalArgumentException, IllegalAccessException {
final List<Long> idList = DataAccess.getListOfIds(rs, count.value, SEPARATOR);
field.set(data, idList);
count.inc();
}
@Override
public void createTables(
final String tableName,
final Field primaryField,
final Field field,
final StringBuilder mainTableBuilder,
final List<String> preActionList,
final List<String> postActionList,
final boolean createIfNotExist,
final boolean createDrop,
final int fieldId) throws Exception {
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, String.class);
}
}

View File

@@ -3,12 +3,11 @@ package org.kar.archidata.dataAccess.addOn.model;
import java.util.List; import java.util.List;
import org.kar.archidata.annotation.DataJson; import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.GenericData; import org.kar.archidata.model.GenericDataSoftDelete;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Id;
public class TableCoversLongLong extends GenericData { public class TableCoversLongLong extends GenericDataSoftDelete {
public TableCoversLongLong() { public TableCoversLongLong() {
// nothing to do... // nothing to do...
} }
@@ -18,9 +17,6 @@ public class TableCoversLongLong extends GenericData {
this.covers = covers; this.covers = covers;
} }
@Column(nullable = false)
@Id
public Long id;
@DataJson() @DataJson()
@Column(nullable = false) @Column(nullable = false)
public List<Long> covers; public List<Long> covers;

View File

@@ -4,12 +4,11 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.kar.archidata.annotation.DataJson; import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.GenericData; import org.kar.archidata.model.GenericDataSoftDelete;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Id;
public class TableCoversLongUUID extends GenericData { public class TableCoversLongUUID extends GenericDataSoftDelete {
public TableCoversLongUUID() { public TableCoversLongUUID() {
// nothing to do... // nothing to do...
} }
@@ -19,9 +18,6 @@ public class TableCoversLongUUID extends GenericData {
this.covers = covers; this.covers = covers;
} }
@Column(nullable = false)
@Id
public Long id;
@DataJson() @DataJson()
@Column(nullable = false) @Column(nullable = false)
public List<UUID> covers; public List<UUID> covers;

View File

@@ -4,12 +4,12 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.kar.archidata.annotation.DataJson; import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.GenericData; import org.kar.archidata.model.GenericDataSoftDelete;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Id; import jakarta.persistence.Id;
public class TableCoversUUIDLong extends GenericData { public class TableCoversUUIDLong extends GenericDataSoftDelete {
public TableCoversUUIDLong() { public TableCoversUUIDLong() {
// nothing to do... // nothing to do...
} }

View File

@@ -4,12 +4,12 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.kar.archidata.annotation.DataJson; import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.GenericData; import org.kar.archidata.model.GenericDataSoftDelete;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Id; import jakarta.persistence.Id;
public class TableCoversUUIDUUID extends GenericData { public class TableCoversUUIDUUID extends GenericDataSoftDelete {
public TableCoversUUIDUUID() { public TableCoversUUIDUUID() {
// nothing to do... // nothing to do...
} }

View File

@@ -121,13 +121,10 @@ public class CheckJPA<T> implements CheckFunctionInterface {
return; return;
} }
final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class); final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class);
long count = 0; final Condition conditionCheck = condCheckers.isEmpty() ? null
if (condCheckers.isEmpty()) { : condCheckers.get(0).toCondition();
count = DataAccess.count(annotationManyToOne.targetEntity(), elem); final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem,
} else { conditionCheck);
count = DataAccess.count(annotationManyToOne.targetEntity(), elem,
condCheckers.get(0).toCondition());
}
if (count == 0) { if (count == 0) {
throw new InputException(baseName + fieldName, throw new InputException(baseName + fieldName,
"Foreign element does not exist in the DB:" + elem); "Foreign element does not exist in the DB:" + elem);

View File

@@ -10,6 +10,10 @@ public class ConditionChecker extends QueryOption {
this.condition = items; this.condition = items;
} }
public ConditionChecker(final Condition cond) {
this.condition = cond.condition;
}
public ConditionChecker() { public ConditionChecker() {
this.condition = null; this.condition = null;
} }

View File

@@ -36,6 +36,9 @@ public class DBInterfaceOption extends QueryOption {
} }
public static DBEntry getAutoEntry(final QueryOptions options) throws IOException { public static DBEntry getAutoEntry(final QueryOptions options) throws IOException {
if (options == null) {
return DBEntry.createInterface(GlobalConfiguration.dbConfig, false);
}
final List<DBInterfaceOption> dbOption = options.get(DBInterfaceOption.class); final List<DBInterfaceOption> dbOption = options.get(DBInterfaceOption.class);
if (dbOption.size() == 0) { if (dbOption.size() == 0) {
final List<DBInterfaceRoot> isRoot = options.get(DBInterfaceRoot.class); final List<DBInterfaceRoot> isRoot = options.get(DBInterfaceRoot.class);

View File

@@ -1,19 +1,34 @@
package org.kar.archidata.exception; package org.kar.archidata.exception;
import org.kar.archidata.api.DataResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
public class FailException extends Exception { public class FailException extends Exception {
private static final Logger LOGGER = LoggerFactory.getLogger(DataResource.class);
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public final Response.Status status; public final Response.Status status;
public final Exception exception;
public FailException(final Response.Status status, final String message) { public FailException(final Response.Status status, final String message) {
super(message); super(message);
this.status = status; this.status = status;
this.exception = null;
}
public FailException(final Response.Status status, final String message, final Exception ex) {
super(message);
this.status = status;
this.exception = ex;
ex.printStackTrace();
LOGGER.error("Generate Fail exception with exceptionData: {}", ex.getMessage());
} }
public FailException(final String message) { public FailException(final String message) {
super(message); super(message);
this.status = Response.Status.BAD_REQUEST; this.status = Response.Status.BAD_REQUEST;
this.exception = null;
} }
} }

View File

@@ -20,12 +20,21 @@ import java.util.UUID;
import org.glassfish.jersey.media.multipart.ContentDisposition; import org.glassfish.jersey.media.multipart.ContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.kar.archidata.catcher.RestErrorResponse; import org.kar.archidata.catcher.RestErrorResponse;
import org.kar.archidata.externalRestApi.TsClassElement.DefinedPosition;
import org.kar.archidata.externalRestApi.model.ApiGroupModel; import org.kar.archidata.externalRestApi.model.ApiGroupModel;
import org.kar.archidata.externalRestApi.model.ClassModel; import org.kar.archidata.externalRestApi.model.ClassModel;
import org.kar.archidata.externalRestApi.typescript.TsApiGeneration;
import org.kar.archidata.externalRestApi.typescript.TsClassElement;
import org.kar.archidata.externalRestApi.typescript.TsClassElement.DefinedPosition;
import org.kar.archidata.externalRestApi.typescript.TsClassElementGroup;
public class TsGenerateApi { public class TsGenerateApi {
/**
* Generate a full API tree for Typescript in a specific folder.
* This generate a folder containing a full API with "model" filder and "api" folder.
* The generation depend of Zod and can be strict compile.
* @param api Data model to generate the api
* @param pathPackage Path to store the api.
*/
public static void generateApi(final AnalyzeApi api, final String pathPackage) throws Exception { public static void generateApi(final AnalyzeApi api, final String pathPackage) throws Exception {
final List<TsClassElement> localModel = generateApiModel(api); final List<TsClassElement> localModel = generateApiModel(api);
final TsClassElementGroup tsGroup = new TsClassElementGroup(localModel); final TsClassElementGroup tsGroup = new TsClassElementGroup(localModel);
@@ -118,7 +127,7 @@ public class TsGenerateApi {
models = api.getCompatibleModels(List.of(Object.class)); models = api.getCompatibleModels(List.of(Object.class));
if (models != null) { if (models != null) {
tsModels.add( tsModels.add(
new TsClassElement(models, "zod.object()", "object", null, "zod.object()", DefinedPosition.NATIVE)); new TsClassElement(models, "zod.any()", "object", null, "zod.object()", DefinedPosition.NATIVE));
} }
// Map is binded to any ==> can not determine this complex model for now // Map is binded to any ==> can not determine this complex model for now
models = api.getCompatibleModels(List.of(Map.class)); models = api.getCompatibleModels(List.of(Map.class));

View File

@@ -64,6 +64,9 @@ public class ApiModel {
this.returnTypes.add(previousModel.add(clazz)); this.returnTypes.add(previousModel.add(clazz));
} }
} }
if (this.returnTypes.size() == 0) {
this.produces.clear();
}
return; return;
} }
@@ -91,6 +94,9 @@ public class ApiModel {
for (final ClassModel elem : this.returnTypes) { for (final ClassModel elem : this.returnTypes) {
LOGGER.warn(" - {}", elem); LOGGER.warn(" - {}", elem);
} }
if (this.returnTypes.size() == 0) {
this.produces.clear();
}
} }
public ApiModel(final Class<?> clazz, final Method method, final String baseRestEndPoint, public ApiModel(final Class<?> clazz, final Method method, final String baseRestEndPoint,
@@ -166,6 +172,5 @@ public class ApiModel {
if (this.unnamedElement.size() > 1) { if (this.unnamedElement.size() > 1) {
throw new IOException("Can not parse the API, enmpty element is more than 1 in " + this.name); throw new IOException("Can not parse the API, enmpty element is more than 1 in " + this.name);
} }
} }
} }

View File

@@ -54,18 +54,20 @@ public class ClassObjectModel extends ClassModel {
ClassModel model, ClassModel model,
String comment, String comment,
int limitSize, int limitSize,
boolean readOnly, Boolean readOnly,
boolean notNull, Boolean notNull,
boolean nullable) { Boolean columnNotNull,
Boolean nullable) {
public FieldProperty(final String name, final ClassModel model, final String comment, final int limitSize, public FieldProperty(final String name, final ClassModel model, final String comment, final int limitSize,
final boolean readOnly, final boolean notNull, final boolean nullable) { final Boolean readOnly, final Boolean notNull, final Boolean columnNotNull, final Boolean nullable) {
this.name = name; this.name = name;
this.model = model; this.model = model;
this.comment = comment; this.comment = comment;
this.limitSize = limitSize; this.limitSize = limitSize;
this.readOnly = readOnly; this.readOnly = readOnly;
this.notNull = notNull; this.notNull = notNull;
this.columnNotNull = columnNotNull;
this.nullable = nullable; this.nullable = nullable;
} }
@@ -77,7 +79,8 @@ public class ClassObjectModel extends ClassModel {
AnnotationTools.getLimitSize(field), // AnnotationTools.getLimitSize(field), //
AnnotationTools.getSchemaReadOnly(field), // AnnotationTools.getSchemaReadOnly(field), //
AnnotationTools.getConstraintsNotNull(field), // AnnotationTools.getConstraintsNotNull(field), //
AnnotationTools.getColumnNotNull(field)); AnnotationTools.getColumnNotNull(field), //
AnnotationTools.getNullable(field));
} }
} }

View File

@@ -1,4 +1,4 @@
package org.kar.archidata.externalRestApi; package org.kar.archidata.externalRestApi.typescript;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
@@ -9,9 +9,9 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.TreeSet;
import org.kar.archidata.dataAccess.DataExport; import org.kar.archidata.dataAccess.DataExport;
import org.kar.archidata.externalRestApi.TsClassElement.DefinedPosition;
import org.kar.archidata.externalRestApi.model.ApiGroupModel; import org.kar.archidata.externalRestApi.model.ApiGroupModel;
import org.kar.archidata.externalRestApi.model.ApiModel; import org.kar.archidata.externalRestApi.model.ApiModel;
import org.kar.archidata.externalRestApi.model.ClassEnumModel; import org.kar.archidata.externalRestApi.model.ClassEnumModel;
@@ -20,6 +20,7 @@ import org.kar.archidata.externalRestApi.model.ClassMapModel;
import org.kar.archidata.externalRestApi.model.ClassModel; import org.kar.archidata.externalRestApi.model.ClassModel;
import org.kar.archidata.externalRestApi.model.ClassObjectModel; import org.kar.archidata.externalRestApi.model.ClassObjectModel;
import org.kar.archidata.externalRestApi.model.RestTypeRequest; import org.kar.archidata.externalRestApi.model.RestTypeRequest;
import org.kar.archidata.externalRestApi.typescript.TsClassElement.DefinedPosition;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -318,6 +319,9 @@ public class TsApiGeneration {
if (produces.size() > 1) { if (produces.size() > 1) {
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,
imports, false);
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)) {
data.append("\n\t\t\t\taccept: HTTPMimeType.JSON,"); data.append("\n\t\t\t\taccept: HTTPMimeType.JSON,");
@@ -327,6 +331,7 @@ public class TsApiGeneration {
} }
} }
} }
}
data.append("\n\t\t\t},"); data.append("\n\t\t\t},");
data.append("\n\t\t\trestConfig,"); data.append("\n\t\t\trestConfig,");
if (!interfaceElement.parameters.isEmpty()) { if (!interfaceElement.parameters.isEmpty()) {
@@ -379,14 +384,14 @@ public class TsApiGeneration {
out.append("import { z as zod } from \"zod\"\n"); out.append("import { z as zod } from \"zod\"\n");
} }
final List<String> finalImportList = new ArrayList<>(); final Set<String> finalImportSet = new TreeSet<>();
for (final ClassModel model : imports) { for (final ClassModel model : imports) {
final TsClassElement tsModel = tsGroup.find(model); final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType == DefinedPosition.NATIVE) { if (tsModel.nativeType == DefinedPosition.NATIVE) {
continue; continue;
} }
finalImportList.add(tsModel.tsTypeName); finalImportSet.add(tsModel.tsTypeName);
} }
for (final ClassModel model : isImports) { for (final ClassModel model : isImports) {
final TsClassElement tsModel = tsGroup.find(model); final TsClassElement tsModel = tsGroup.find(model);
@@ -394,7 +399,7 @@ public class TsApiGeneration {
continue; continue;
} }
if (tsModel.tsCheckType != null) { if (tsModel.tsCheckType != null) {
finalImportList.add(tsModel.tsCheckType); finalImportSet.add(tsModel.tsCheckType);
} }
} }
for (final ClassModel model : zodImports) { for (final ClassModel model : zodImports) {
@@ -402,20 +407,19 @@ public class TsApiGeneration {
if (tsModel.nativeType == DefinedPosition.NATIVE) { if (tsModel.nativeType == DefinedPosition.NATIVE) {
continue; continue;
} }
finalImportList.add("Zod" + tsModel.tsTypeName); finalImportSet.add("Zod" + tsModel.tsTypeName);
} }
for (final ClassModel model : writeImports) { for (final ClassModel model : writeImports) {
final TsClassElement tsModel = tsGroup.find(model); final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType == DefinedPosition.NATIVE) { if (tsModel.nativeType == DefinedPosition.NATIVE) {
continue; continue;
} }
finalImportList.add(tsModel.tsTypeName + "Write"); finalImportSet.add(tsModel.tsTypeName + "Write");
} }
Collections.sort(finalImportList);
if (finalImportList.size() != 0) { if (finalImportSet.size() != 0) {
out.append("import {"); out.append("import {");
for (final String elem : finalImportList) { for (final String elem : finalImportSet) {
out.append("\n\t"); out.append("\n\t");
out.append(elem); out.append(elem);
out.append(","); out.append(",");

View File

@@ -1,4 +1,4 @@
package org.kar.archidata.externalRestApi; package org.kar.archidata.externalRestApi.typescript;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
@@ -216,10 +216,18 @@ public class TsClassElement {
} }
public String optionalTypeZod(final FieldProperty field) { public String optionalTypeZod(final FieldProperty field) {
// Common checking element (apply to List, Map, ...)
if (field.nullable()) {
return ".optional()";
}
if (field.notNull()) {
return "";
}
// Other object:
if (field.model().getOriginClasses() == null || field.model().getOriginClasses().isPrimitive()) { if (field.model().getOriginClasses() == null || field.model().getOriginClasses().isPrimitive()) {
return ""; return "";
} }
if (field.notNull()) { if (field.columnNotNull()) {
return ""; return "";
} }
return ".optional()"; return ".optional()";
@@ -417,7 +425,7 @@ public class TsClassElement {
} }
final Path path = Paths.get(pathPackage + File.separator + "model"); final Path path = Paths.get(pathPackage + File.separator + "model");
if (Files.notExists(path)) { if (Files.notExists(path)) {
Files.createDirectory(path); Files.createDirectories(path);
} }
final FileWriter myWriter = new FileWriter( final FileWriter myWriter = new FileWriter(
pathPackage + File.separator + "model" + File.separator + this.fileName + ".ts"); pathPackage + File.separator + "model" + File.separator + this.fileName + ".ts");

View File

@@ -1,4 +1,4 @@
package org.kar.archidata.externalRestApi; package org.kar.archidata.externalRestApi.typescript;
import java.util.List; import java.util.List;

View File

@@ -14,6 +14,8 @@ import org.kar.archidata.migration.model.Migration;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
public class MigrationEngine { public class MigrationEngine {
final static Logger LOGGER = LoggerFactory.getLogger(MigrationEngine.class); final static Logger LOGGER = LoggerFactory.getLogger(MigrationEngine.class);
@@ -103,6 +105,8 @@ public class MigrationEngine {
/** Process the automatic migration of the system /** Process the automatic migration of the system
* @param config SQL connection for the migration * @param config SQL connection for the migration
* @throws IOException Error if access on the DB */ * @throws IOException Error if access on the DB */
@SuppressFBWarnings({ "SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING",
"SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE" })
public void migrateErrorThrow(final DBConfig config) throws MigrationException { public void migrateErrorThrow(final DBConfig config) throws MigrationException {
LOGGER.info("Execute migration ... [BEGIN]"); LOGGER.info("Execute migration ... [BEGIN]");
// check the integrity of the migrations: // check the integrity of the migrations:
@@ -318,12 +322,12 @@ public class MigrationEngine {
boolean find = false; boolean find = false;
for (int iii = this.datas.size() - 1; iii >= 0; iii--) { for (int iii = this.datas.size() - 1; iii >= 0; iii--) {
if (!find) { if (!find) {
if (this.datas.get(iii).getName() == currentVersion.name) { if (this.datas.get(iii).getName().equals(currentVersion.name)) {
find = true; find = true;
} }
continue; continue;
} }
if (this.datas.get(iii).getName() == currentVersion.name) { if (this.datas.get(iii).getName().equals(currentVersion.name)) {
break; break;
} }
toApply.add(this.datas.get(iii)); toApply.add(this.datas.get(iii));

View File

@@ -4,6 +4,7 @@ import org.kar.archidata.annotation.DataDeleted;
import org.kar.archidata.annotation.DataNotRead; import org.kar.archidata.annotation.DataNotRead;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.DefaultValue;
@@ -13,5 +14,6 @@ public class GenericDataSoftDelete extends GenericData {
@DefaultValue("'0'") @DefaultValue("'0'")
@DataDeleted @DataDeleted
@Schema(description = "Deleted state", hidden = true, required = false, readOnly = true) @Schema(description = "Deleted state", hidden = true, required = false, readOnly = true)
@Nullable
public Boolean deleted = null; public Boolean deleted = null;
} }

View File

@@ -9,6 +9,7 @@ import org.kar.archidata.annotation.UpdateTimestamp;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column; import jakarta.persistence.Column;
public class GenericTiming { public class GenericTiming {
@@ -17,6 +18,7 @@ public class GenericTiming {
@Column(nullable = false) @Column(nullable = false)
@Schema(description = "Create time of the object", required = false, example = "2000-01-23T01:23:45.678+01:00", readOnly = true) @Schema(description = "Create time of the object", required = false, example = "2000-01-23T01:23:45.678+01:00", readOnly = true)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
@Nullable
public Date createdAt = null; public Date createdAt = null;
@DataNotRead @DataNotRead
@UpdateTimestamp @UpdateTimestamp
@@ -24,5 +26,6 @@ public class GenericTiming {
@Schema(description = "When update the object", required = false, example = "2000-01-23T00:23:45.678Z", readOnly = true) @Schema(description = "When update the object", required = false, example = "2000-01-23T00:23:45.678Z", readOnly = true)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
// public Instant updatedAt = null; // public Instant updatedAt = null;
@Nullable
public Date updatedAt = null; public Date updatedAt = null;
} }

View File

@@ -4,6 +4,7 @@ import org.kar.archidata.annotation.DataDeleted;
import org.kar.archidata.annotation.DataNotRead; import org.kar.archidata.annotation.DataNotRead;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.DefaultValue;
@@ -13,5 +14,6 @@ public class UUIDGenericDataSoftDelete extends UUIDGenericData {
@DefaultValue("'0'") @DefaultValue("'0'")
@DataDeleted @DataDeleted
@Schema(description = "Deleted state", hidden = true, required = false, readOnly = true) @Schema(description = "Deleted state", hidden = true, required = false, readOnly = true)
@Nullable
public Boolean deleted = null; public Boolean deleted = null;
} }

View File

@@ -16,6 +16,7 @@ CREATE TABLE `user` (
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.List; import java.util.List;
import java.util.UUID;
import org.kar.archidata.annotation.DataIfNotExists; import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.DataJson; import org.kar.archidata.annotation.DataJson;
@@ -24,6 +25,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.DefaultValue;
@@ -49,7 +51,8 @@ public class User extends GenericDataSoftDelete {
@Schema(description = "List of Id of the specific covers") @Schema(description = "List of Id of the specific covers")
@DataJson(targetEntity = Data.class) @DataJson(targetEntity = Data.class)
public List<Long> covers; @Nullable
public List<UUID> covers;
@Override @Override
public String toString() { public String toString() {

View File

@@ -22,7 +22,7 @@ public class UserByToken {
final Object data = this.right.get(key); final Object data = this.right.get(key);
if (data instanceof final Boolean elem) { if (data instanceof final Boolean elem) {
if (value instanceof final Boolean castVal) { if (value instanceof final Boolean castVal) {
if (elem == castVal) { if (elem.equals(castVal)) {
return true; return true;
} }
} }
@@ -38,7 +38,7 @@ public class UserByToken {
} }
if (data instanceof final Long elem) { if (data instanceof final Long elem) {
if (value instanceof final Long castVal) { if (value instanceof final Long castVal) {
if (elem == castVal) { if (elem.equals(castVal)) {
return true; return true;
} }
} }
@@ -46,7 +46,7 @@ public class UserByToken {
} }
if (data instanceof final Double elem) { if (data instanceof final Double elem) {
if (value instanceof final Double castVal) { if (value instanceof final Double castVal) {
if (elem == castVal) { if (elem.equals(castVal)) {
return true; return true;
} }
} }

View File

@@ -5,6 +5,7 @@ import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.ParseException; import java.text.ParseException;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
@@ -219,6 +220,10 @@ public class JWTWrapper {
try { try {
// On the consumer side, parse the JWS and verify its RSA signature // On the consumer side, parse the JWS and verify its RSA signature
final SignedJWT signedJWT = SignedJWT.parse(signedToken); final SignedJWT signedJWT = SignedJWT.parse(signedToken);
if (signedJWT == null) {
LOGGER.error("FAIL to parse signing");
return null;
}
if (ConfigBaseVariable.getTestMode() && signedToken.endsWith(TestSigner.test_signature)) { if (ConfigBaseVariable.getTestMode() && signedToken.endsWith(TestSigner.test_signature)) {
LOGGER.warn("Someone use a test token: {}", signedToken); LOGGER.warn("Someone use a test token: {}", signedToken);
} else if (rsaPublicJWK == null) { } else if (rsaPublicJWK == null) {
@@ -226,7 +231,7 @@ public class JWTWrapper {
if (!ConfigBaseVariable.getTestMode()) { if (!ConfigBaseVariable.getTestMode()) {
return null; return null;
} }
final String rawSignature = signedJWT.getSigningInput().toString(); final String rawSignature = new String(signedJWT.getSigningInput(), StandardCharsets.UTF_8);
if (rawSignature.equals(TestSigner.test_signature)) { if (rawSignature.equals(TestSigner.test_signature)) {
// Test token : .application.. // Test token : .application..
} else { } else {

View File

@@ -16,7 +16,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException; import com.fasterxml.jackson.databind.exc.MismatchedInputException;
@@ -56,7 +55,9 @@ public class RESTApi {
"Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body()); "Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body());
} }
} }
return this.mapper.readValue(httpResponse.body(), new TypeReference<List<T>>() {}); //return this.mapper.readValue(httpResponse.body(), new TypeReference<List<T>>() {});
return this.mapper.readValue(httpResponse.body(),
this.mapper.getTypeFactory().constructCollectionType(List.class, clazz));
} }
public <T> T get(final Class<T> clazz, final String urlOffset) public <T> T get(final Class<T> clazz, final String urlOffset)
@@ -153,6 +154,9 @@ public class RESTApi {
"Fail to get the ERROR data [" + httpResponse.statusCode() + "] " + httpResponse.body()); "Fail to get the ERROR data [" + httpResponse.statusCode() + "] " + httpResponse.body());
} }
} }
if (clazz == Void.class || clazz == void.class) {
return null;
}
if (clazz.equals(String.class)) { if (clazz.equals(String.class)) {
return (T) httpResponse.body(); return (T) httpResponse.body();
} }
@@ -190,13 +194,31 @@ public class RESTApi {
"Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body()); "Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body());
} }
} }
if (clazz == Void.class || clazz == void.class) {
return null;
}
if (clazz.equals(String.class)) { if (clazz.equals(String.class)) {
return (T) httpResponse.body(); return (T) httpResponse.body();
} }
return this.mapper.readValue(httpResponse.body(), clazz); return this.mapper.readValue(httpResponse.body(), clazz);
} }
public <T, U> void delete(final Class<T> clazz, final String urlOffset) /**
* Call a DELETE on a REST API
* @param urlOffset Offset to call the API
*/
public void delete(final String urlOffset) throws RESTErrorResponseExeption, IOException, InterruptedException {
delete(Void.class, urlOffset);
}
/**
* Call a DELETE on a REST API with retrieving some data
* @param <T> Type of data that might be received.
* @param clazz Class model of the data that might be parsed.
* @param urlOffset Offset to call the API
* @return The parsed object received.
*/
public <T> T delete(final Class<T> clazz, final String urlOffset)
throws RESTErrorResponseExeption, IOException, InterruptedException { throws RESTErrorResponseExeption, IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient(); final HttpClient client = HttpClient.newHttpClient();
Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1) Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1)
@@ -216,5 +238,12 @@ public class RESTApi {
"Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body()); "Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body());
} }
} }
if (clazz == Void.class || clazz == void.class) {
return null;
}
if (clazz.equals(String.class)) {
return (T) httpResponse.body();
}
return this.mapper.readValue(httpResponse.body(), clazz);
} }
} }

View File

@@ -0,0 +1,14 @@
package org.kar.archidata.tools;
public class StringTools {
public static String RandGeneratedStr(final int length) {
final String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvxyz0123456789éàê_- '()";
final StringBuilder out = new StringBuilder(length);
for (int iii = 0; iii < length; iii++) {
final int chId = (int) (base.length() * Math.random());
out.append(base.charAt(chId));
}
return out.toString();
}
}

View File

@@ -47,7 +47,7 @@ public class UuidUtils {
this.base = startingUUID.until(Instant.now(), ChronoUnit.SECONDS); this.base = startingUUID.until(Instant.now(), ChronoUnit.SECONDS);
final String serveurBaseUUID = System.getenv("UUID_SERVER_ID"); final String serveurBaseUUID = System.getenv("UUID_SERVER_ID");
if (serveurBaseUUID != null) { if (serveurBaseUUID != null) {
long serverId = Long.valueOf(serveurBaseUUID); long serverId = Long.parseLong(serveurBaseUUID);
serverId %= 0xFFFF; serverId %= 0xFFFF;
this.base += (serverId << (64 - 16)); this.base += (serverId << (64 - 16));
} else { } else {

View File

@@ -4,7 +4,7 @@
* @license MPL-2 * @license MPL-2
*/ */
import { RestErrorResponse } from "./model"; import { RestErrorResponse, isRestErrorResponse } from "./model";
export enum HTTPRequestModel { export enum HTTPRequestModel {
DELETE = "DELETE", DELETE = "DELETE",
@@ -303,9 +303,9 @@ export function RESTRequest({
name: "Model accept type incompatible", name: "Model accept type incompatible",
time: Date().toString(), time: Date().toString(),
status: 901, status: 901,
error: `REST check wrong type: ${restModel.accept} != ${contentType}`, message: `REST Content type are not compatible: ${restModel.accept} != ${contentType}`,
statusMessage: "Fetch error", statusMessage: "Fetch error",
message: "rest-tools.ts Wrong type in the message return type", error: "rest-tools.ts Wrong type in the message return type",
} as RestErrorResponse); } as RestErrorResponse);
} else if (contentType === HTTPMimeType.JSON) { } else if (contentType === HTTPMimeType.JSON) {
response response
@@ -313,39 +313,92 @@ export function RESTRequest({
.then((value: any) => { .then((value: any) => {
resolve({ status: response.status, data: value }); resolve({ status: response.status, data: value });
}) })
.catch((reason: any) => { .catch((reason: Error) => {
reject({ reject({
name: "API serialization error", name: "API serialization error",
time: Date().toString(), time: Date().toString(),
status: 902, status: 902,
error: `REST parse json fail: ${reason}`, message: `REST parse json fail: ${reason}`,
statusMessage: "Fetch parse error", statusMessage: "Fetch parse error",
message: "rest-tools.ts Wrong message model to parse", error: "rest-tools.ts Wrong message model to parse",
} as RestErrorResponse); } as RestErrorResponse);
}); });
} else { } else {
resolve({ status: response.status, data: response.body }); resolve({ status: response.status, data: response.body });
} }
} else { } else {
// the answer is not correct not a 2XX
// clone the response to keep the raw data if case of error:
response
.clone()
.json()
.then((value: any) => {
if (isRestErrorResponse(value)) {
reject(value);
} else {
response
.text()
.then((dataError: string) => {
reject({ reject({
name: "REST return no OK status", name: "API serialization error",
time: Date().toString(),
status: 903,
message: `REST parse error json with wrong type fail. ${dataError}`,
statusMessage: "Fetch parse error",
error: "rest-tools.ts Wrong message model to parse",
} as RestErrorResponse);
})
.catch((reason: any) => {
reject({
name: "API serialization error",
time: Date().toString(), time: Date().toString(),
status: response.status, status: response.status,
error: `${response.body}`, message: `unmanaged error model: ??? with error: ${reason}`,
statusMessage: "Fetch code error", statusMessage: "Fetch ERROR parse error",
message: "rest-tools.ts Wrong return code", error: "rest-tools.ts Wrong message model to parse",
} as RestErrorResponse); } as RestErrorResponse);
});
} }
}) })
.catch((error: any) => { .catch((reason: Error) => {
response
.text()
.then((dataError: string) => {
reject({
name: "API serialization error",
time: Date().toString(),
status: response.status,
message: `unmanaged error model: ${dataError} with error: ${reason}`,
statusMessage: "Fetch ERROR TEXT parse error",
error: "rest-tools.ts Wrong message model to parse",
} as RestErrorResponse);
})
.catch((reason: any) => {
reject({
name: "API serialization error",
time: Date().toString(),
status: response.status,
message: `unmanaged error model: ??? with error: ${reason}`,
statusMessage: "Fetch ERROR TEXT FAIL",
error: "rest-tools.ts Wrong message model to parse",
} as RestErrorResponse);
});
});
}
})
.catch((error: Error) => {
if (isRestErrorResponse(error)) {
reject(error);
} else {
reject({ reject({
name: "Request fail", name: "Request fail",
time: Date(), time: Date(),
status: 999, status: 999,
error: error, message: error,
statusMessage: "Fetch catch error", statusMessage: "Fetch catch error",
message: "rest-tools.ts detect an error in the fetch request", error: "rest-tools.ts detect an error in the fetch request",
}); });
}
}); });
}); });
} }

View File

@@ -3,12 +3,12 @@
# Default logging detail level for all instances of SimpleLogger. # Default logging detail level for all instances of SimpleLogger.
# Must be one of ("trace", "debug", "info", "warn", or "error"). # Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, defaults to "info". # If not specified, defaults to "info".
org.slf4j.simpleLogger.defaultLogLevel=debug org.slf4j.simpleLogger.defaultLogLevel=INFO
# Logging detail level for a SimpleLogger instance named "xxxxx". # Logging detail level for a SimpleLogger instance named "xxxxx".
# Must be one of ("trace", "debug", "info", "warn", or "error"). # Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, the default logging detail level is used. # If not specified, the default logging detail level is used.
#org.slf4j.simpleLogger.log.xxxxx= org.slf4j.simpleLogger.log.org.kar.archidata=TRACE
# Set to true if you want the current date and time to be included in output messages. # Set to true if you want the current date and time to be included in output messages.
# Default is false, and will output the number of milliseconds elapsed since startup. # Default is false, and will output the number of milliseconds elapsed since startup.

View File

@@ -22,6 +22,9 @@ import org.slf4j.LoggerFactory;
import test.kar.archidata.model.TypeManyToOneRemote; import test.kar.archidata.model.TypeManyToOneRemote;
import test.kar.archidata.model.TypeManyToOneRoot; import test.kar.archidata.model.TypeManyToOneRoot;
import test.kar.archidata.model.TypeManyToOneRootExpand; import test.kar.archidata.model.TypeManyToOneRootExpand;
import test.kar.archidata.model.TypeManyToOneUUIDRemote;
import test.kar.archidata.model.TypeManyToOneUUIDRoot;
import test.kar.archidata.model.TypeManyToOneUUIDRootExpand;
@ExtendWith(StepwiseExtension.class) @ExtendWith(StepwiseExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@@ -52,8 +55,9 @@ public class TestManyToOne {
@Test @Test
public void testCreateTable() throws Exception { public void testCreateTable() throws Exception {
final List<String> sqlCommand = DataFactory.createTable(TypeManyToOneRemote.class); final List<String> sqlCommand = DataFactory.createTable(TypeManyToOneRemote.class);
final List<String> sqlCommand2 = DataFactory.createTable(TypeManyToOneRoot.class); sqlCommand.addAll(DataFactory.createTable(TypeManyToOneRoot.class));
sqlCommand.addAll(sqlCommand2); sqlCommand.addAll(DataFactory.createTable(TypeManyToOneUUIDRoot.class));
sqlCommand.addAll(DataFactory.createTable(TypeManyToOneUUIDRemote.class));
for (final String elem : sqlCommand) { for (final String elem : sqlCommand) {
LOGGER.debug("request: '{}'", elem); LOGGER.debug("request: '{}'", elem);
DataAccess.executeSimpleQuery(elem); DataAccess.executeSimpleQuery(elem);
@@ -62,7 +66,7 @@ public class TestManyToOne {
@Order(2) @Order(2)
@Test @Test
public void testAddAlements() throws Exception { public void testRemoteLong() throws Exception {
TypeManyToOneRemote remote = new TypeManyToOneRemote(); TypeManyToOneRemote remote = new TypeManyToOneRemote();
remote.data = "remote1"; remote.data = "remote1";
final TypeManyToOneRemote insertedRemote1 = DataAccess.insert(remote); final TypeManyToOneRemote insertedRemote1 = DataAccess.insert(remote);
@@ -119,4 +123,63 @@ public class TestManyToOne {
Assertions.assertEquals(insertedData.otherData, retrieve2.otherData); Assertions.assertEquals(insertedData.otherData, retrieve2.otherData);
Assertions.assertNull(retrieve2.remote); Assertions.assertNull(retrieve2.remote);
} }
@Order(3)
@Test
public void testRemoteUUID() throws Exception {
TypeManyToOneUUIDRemote remote = new TypeManyToOneUUIDRemote();
remote.data = "remote1";
final TypeManyToOneUUIDRemote insertedRemote1 = DataAccess.insert(remote);
Assertions.assertEquals(insertedRemote1.data, remote.data);
remote = new TypeManyToOneUUIDRemote();
remote.data = "remote2";
final TypeManyToOneUUIDRemote insertedRemote2 = DataAccess.insert(remote);
Assertions.assertEquals(insertedRemote2.data, remote.data);
final TypeManyToOneUUIDRoot test = new TypeManyToOneUUIDRoot();
test.otherData = "kjhlkjlkj";
test.remoteUuid = insertedRemote2.uuid;
final TypeManyToOneUUIDRoot insertedData = DataAccess.insert(test);
Assertions.assertNotNull(insertedData);
Assertions.assertNotNull(insertedData.uuid);
Assertions.assertEquals(test.otherData, insertedData.otherData);
Assertions.assertEquals(insertedRemote2.uuid, insertedData.remoteUuid);
TypeManyToOneUUIDRoot retrieve = DataAccess.get(TypeManyToOneUUIDRoot.class, insertedData.uuid);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.uuid);
Assertions.assertEquals(insertedData.uuid, retrieve.uuid);
Assertions.assertEquals(insertedData.otherData, retrieve.otherData);
Assertions.assertEquals(insertedRemote2.uuid, retrieve.remoteUuid);
TypeManyToOneUUIDRootExpand retrieve2 = DataAccess.get(TypeManyToOneUUIDRootExpand.class, insertedData.uuid);
Assertions.assertNotNull(retrieve2);
Assertions.assertNotNull(retrieve2.uuid);
Assertions.assertEquals(insertedData.uuid, retrieve2.uuid);
Assertions.assertEquals(insertedData.otherData, retrieve2.otherData);
Assertions.assertNotNull(retrieve2.remote);
Assertions.assertEquals(insertedRemote2.uuid, retrieve2.remote.uuid);
Assertions.assertEquals(insertedRemote2.data, retrieve2.remote.data);
// remove values:
final int count = DataAccess.delete(TypeManyToOneUUIDRemote.class, remote.uuid);
Assertions.assertEquals(1, count);
// check fail:
retrieve = DataAccess.get(TypeManyToOneUUIDRoot.class, insertedData.uuid);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.uuid);
Assertions.assertEquals(insertedData.uuid, retrieve.uuid);
Assertions.assertEquals(insertedData.otherData, retrieve.otherData);
Assertions.assertEquals(insertedRemote2.uuid, retrieve.remoteUuid);
retrieve2 = DataAccess.get(TypeManyToOneUUIDRootExpand.class, insertedData.uuid);
Assertions.assertNotNull(retrieve2);
Assertions.assertNotNull(retrieve2.uuid);
Assertions.assertEquals(insertedData.uuid, retrieve2.uuid);
Assertions.assertEquals(insertedData.otherData, retrieve2.otherData);
Assertions.assertNull(retrieve2.remote);
}
} }

View File

@@ -4,6 +4,7 @@ import java.io.IOException;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Order;
@@ -18,7 +19,12 @@ import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import test.kar.archidata.model.TypesTable; import test.kar.archidata.model.TypeOneToManyRemote;
import test.kar.archidata.model.TypeOneToManyRoot;
import test.kar.archidata.model.TypeOneToManyRootExpand;
import test.kar.archidata.model.TypeOneToManyUUIDRemote;
import test.kar.archidata.model.TypeOneToManyUUIDRoot;
import test.kar.archidata.model.TypeOneToManyUUIDRootExpand;
@ExtendWith(StepwiseExtension.class) @ExtendWith(StepwiseExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@@ -48,7 +54,10 @@ public class TestOneToMany {
@Order(1) @Order(1)
@Test @Test
public void testCreateTable() throws Exception { public void testCreateTable() throws Exception {
final List<String> sqlCommand = DataFactory.createTable(TypesTable.class); final List<String> sqlCommand = DataFactory.createTable(TypeOneToManyRemote.class);
sqlCommand.addAll(DataFactory.createTable(TypeOneToManyRoot.class));
sqlCommand.addAll(DataFactory.createTable(TypeOneToManyUUIDRemote.class));
sqlCommand.addAll(DataFactory.createTable(TypeOneToManyUUIDRoot.class));
for (final String elem : sqlCommand) { for (final String elem : sqlCommand) {
LOGGER.debug("request: '{}'", elem); LOGGER.debug("request: '{}'", elem);
DataAccess.executeSimpleQuery(elem); DataAccess.executeSimpleQuery(elem);
@@ -57,7 +66,161 @@ public class TestOneToMany {
@Order(2) @Order(2)
@Test @Test
public void testPlop() throws Exception { public void testParentLong() throws Exception {
// create parent:
final TypeOneToManyRoot root = new TypeOneToManyRoot();
root.otherData = "plouf";
final TypeOneToManyRoot insertedRoot = DataAccess.insert(root);
Assertions.assertEquals(insertedRoot.otherData, root.otherData);
Assertions.assertNull(insertedRoot.remoteIds);
final TypeOneToManyRoot root2 = new TypeOneToManyRoot();
root2.otherData = "plouf 2";
final TypeOneToManyRoot insertedRoot2 = DataAccess.insert(root2);
Assertions.assertEquals(insertedRoot2.otherData, root2.otherData);
Assertions.assertNull(insertedRoot2.remoteIds);
// Create Some Remotes
final TypeOneToManyRemote remote10 = new TypeOneToManyRemote();
remote10.data = "remote10";
remote10.rootId = insertedRoot.id;
final TypeOneToManyRemote insertedRemote10 = DataAccess.insert(remote10);
Assertions.assertEquals(insertedRemote10.data, remote10.data);
Assertions.assertEquals(insertedRemote10.rootId, remote10.rootId);
final TypeOneToManyRemote remote11 = new TypeOneToManyRemote();
remote11.data = "remote11";
remote11.rootId = insertedRoot.id;
final TypeOneToManyRemote insertedRemote11 = DataAccess.insert(remote11);
Assertions.assertEquals(insertedRemote11.data, remote11.data);
Assertions.assertEquals(insertedRemote11.rootId, remote11.rootId);
final TypeOneToManyRemote remote20 = new TypeOneToManyRemote();
remote20.data = "remote20";
remote20.rootId = insertedRoot2.id;
final TypeOneToManyRemote insertedRemote20 = DataAccess.insert(remote20);
Assertions.assertEquals(insertedRemote20.data, remote20.data);
Assertions.assertEquals(insertedRemote20.rootId, remote20.rootId);
// Check remote are inserted
final TypeOneToManyRoot retreiveRoot1 = DataAccess.get(TypeOneToManyRoot.class, insertedRoot.id);
Assertions.assertEquals(retreiveRoot1.otherData, insertedRoot.otherData);
Assertions.assertNotNull(retreiveRoot1.remoteIds);
Assertions.assertEquals(2, retreiveRoot1.remoteIds.size());
Assertions.assertEquals(insertedRemote10.id, retreiveRoot1.remoteIds.get(0));
Assertions.assertEquals(insertedRemote11.id, retreiveRoot1.remoteIds.get(1));
final TypeOneToManyRoot retreiveRoot2 = DataAccess.get(TypeOneToManyRoot.class, insertedRoot2.id);
Assertions.assertEquals(retreiveRoot2.otherData, insertedRoot2.otherData);
Assertions.assertNotNull(retreiveRoot2.remoteIds);
Assertions.assertEquals(1, retreiveRoot2.remoteIds.size());
Assertions.assertEquals(insertedRemote20.id, retreiveRoot2.remoteIds.get(0));
// Check remote are inserted and expandable
final TypeOneToManyRootExpand retreiveRootExpand1 = DataAccess.get(TypeOneToManyRootExpand.class,
insertedRoot.id);
Assertions.assertEquals(retreiveRootExpand1.otherData, insertedRoot.otherData);
Assertions.assertNotNull(retreiveRootExpand1.remotes);
Assertions.assertEquals(2, retreiveRootExpand1.remotes.size());
Assertions.assertEquals(insertedRemote10.id, retreiveRootExpand1.remotes.get(0).id);
Assertions.assertEquals(insertedRemote10.rootId, retreiveRootExpand1.remotes.get(0).rootId);
Assertions.assertEquals(insertedRemote10.data, retreiveRootExpand1.remotes.get(0).data);
Assertions.assertEquals(insertedRemote11.id, retreiveRootExpand1.remotes.get(1).id);
Assertions.assertEquals(insertedRemote11.rootId, retreiveRootExpand1.remotes.get(1).rootId);
Assertions.assertEquals(insertedRemote11.data, retreiveRootExpand1.remotes.get(1).data);
final TypeOneToManyRootExpand retreiveRootExpand2 = DataAccess.get(TypeOneToManyRootExpand.class,
insertedRoot2.id);
Assertions.assertEquals(retreiveRootExpand2.otherData, insertedRoot2.otherData);
Assertions.assertNotNull(retreiveRootExpand2.remotes);
Assertions.assertEquals(1, retreiveRootExpand2.remotes.size());
Assertions.assertEquals(insertedRemote20.id, retreiveRootExpand2.remotes.get(0).id);
Assertions.assertEquals(insertedRemote20.rootId, retreiveRootExpand2.remotes.get(0).rootId);
Assertions.assertEquals(insertedRemote20.data, retreiveRootExpand2.remotes.get(0).data);
}
@Order(2)
@Test
public void testParentUUID() throws Exception {
// create parent:
final TypeOneToManyUUIDRoot root = new TypeOneToManyUUIDRoot();
root.otherData = "plouf";
final TypeOneToManyUUIDRoot insertedRoot = DataAccess.insert(root);
Assertions.assertEquals(insertedRoot.otherData, root.otherData);
Assertions.assertNull(insertedRoot.remoteIds);
final TypeOneToManyUUIDRoot root2 = new TypeOneToManyUUIDRoot();
root2.otherData = "plouf 2";
final TypeOneToManyUUIDRoot insertedRoot2 = DataAccess.insert(root2);
Assertions.assertEquals(insertedRoot2.otherData, root2.otherData);
Assertions.assertNull(insertedRoot2.remoteIds);
// Create Some Remotes
final TypeOneToManyUUIDRemote remote10 = new TypeOneToManyUUIDRemote();
remote10.data = "remote10";
remote10.rootUuid = insertedRoot.uuid;
final TypeOneToManyUUIDRemote insertedRemote10 = DataAccess.insert(remote10);
Assertions.assertEquals(insertedRemote10.data, remote10.data);
Assertions.assertEquals(insertedRemote10.rootUuid, remote10.rootUuid);
final TypeOneToManyUUIDRemote remote11 = new TypeOneToManyUUIDRemote();
remote11.data = "remote11";
remote11.rootUuid = insertedRoot.uuid;
final TypeOneToManyUUIDRemote insertedRemote11 = DataAccess.insert(remote11);
Assertions.assertEquals(insertedRemote11.data, remote11.data);
Assertions.assertEquals(insertedRemote11.rootUuid, remote11.rootUuid);
final TypeOneToManyUUIDRemote remote20 = new TypeOneToManyUUIDRemote();
remote20.data = "remote20";
remote20.rootUuid = insertedRoot2.uuid;
final TypeOneToManyUUIDRemote insertedRemote20 = DataAccess.insert(remote20);
Assertions.assertEquals(insertedRemote20.data, remote20.data);
Assertions.assertEquals(insertedRemote20.rootUuid, remote20.rootUuid);
// Check remote are inserted
final TypeOneToManyUUIDRoot retreiveRoot1 = DataAccess.get(TypeOneToManyUUIDRoot.class, insertedRoot.uuid);
Assertions.assertEquals(retreiveRoot1.otherData, insertedRoot.otherData);
Assertions.assertNotNull(retreiveRoot1.remoteIds);
Assertions.assertEquals(2, retreiveRoot1.remoteIds.size());
Assertions.assertEquals(insertedRemote10.uuid, retreiveRoot1.remoteIds.get(0));
Assertions.assertEquals(insertedRemote11.uuid, retreiveRoot1.remoteIds.get(1));
final TypeOneToManyUUIDRoot retreiveRoot2 = DataAccess.get(TypeOneToManyUUIDRoot.class, insertedRoot2.uuid);
Assertions.assertEquals(retreiveRoot2.otherData, insertedRoot2.otherData);
Assertions.assertNotNull(retreiveRoot2.remoteIds);
Assertions.assertEquals(1, retreiveRoot2.remoteIds.size());
Assertions.assertEquals(insertedRemote20.uuid, retreiveRoot2.remoteIds.get(0));
// Check remote are inserted and expandable
final TypeOneToManyUUIDRootExpand retreiveRootExpand1 = DataAccess.get(TypeOneToManyUUIDRootExpand.class,
insertedRoot.uuid);
Assertions.assertEquals(retreiveRootExpand1.otherData, insertedRoot.otherData);
Assertions.assertNotNull(retreiveRootExpand1.remotes);
Assertions.assertEquals(2, retreiveRootExpand1.remotes.size());
Assertions.assertEquals(insertedRemote10.uuid, retreiveRootExpand1.remotes.get(0).uuid);
Assertions.assertEquals(insertedRemote10.rootUuid, retreiveRootExpand1.remotes.get(0).rootUuid);
Assertions.assertEquals(insertedRemote10.data, retreiveRootExpand1.remotes.get(0).data);
Assertions.assertEquals(insertedRemote11.uuid, retreiveRootExpand1.remotes.get(1).uuid);
Assertions.assertEquals(insertedRemote11.rootUuid, retreiveRootExpand1.remotes.get(1).rootUuid);
Assertions.assertEquals(insertedRemote11.data, retreiveRootExpand1.remotes.get(1).data);
final TypeOneToManyUUIDRootExpand retreiveRootExpand2 = DataAccess.get(TypeOneToManyUUIDRootExpand.class,
insertedRoot2.uuid);
Assertions.assertEquals(retreiveRootExpand2.otherData, insertedRoot2.otherData);
Assertions.assertNotNull(retreiveRootExpand2.remotes);
Assertions.assertEquals(1, retreiveRootExpand2.remotes.size());
Assertions.assertEquals(insertedRemote20.uuid, retreiveRootExpand2.remotes.get(0).uuid);
Assertions.assertEquals(insertedRemote20.rootUuid, retreiveRootExpand2.remotes.get(0).rootUuid);
Assertions.assertEquals(insertedRemote20.data, retreiveRootExpand2.remotes.get(0).data);
} }
} }

View File

@@ -1,15 +1,8 @@
package test.kar.archidata.model; package test.kar.archidata.model;
import jakarta.persistence.Column; import org.kar.archidata.model.GenericData;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
public class TypeManyToManyRemote { public class TypeManyToManyRemote extends GenericData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, unique = true)
public Long id = null;
public String data; public String data;
} }

View File

@@ -2,18 +2,12 @@ package test.kar.archidata.model;
import java.util.List; import java.util.List;
import jakarta.persistence.Column; import org.kar.archidata.model.GenericData;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToMany;
public class TypeManyToManyRoot { public class TypeManyToManyRoot extends GenericData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, unique = true)
public Long id = null;
public String otherData; public String otherData;

View File

@@ -2,20 +2,14 @@ package test.kar.archidata.model;
import java.util.List; import java.util.List;
import jakarta.persistence.Column; import org.kar.archidata.model.GenericData;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToMany;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@Table(name = "TypeManyToManyRoot") @Table(name = "TypeManyToManyRoot")
public class TypeManyToManyRootExpand { public class TypeManyToManyRootExpand extends GenericData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, unique = true)
public Long id = null;
public String otherData; public String otherData;

View File

@@ -1,16 +1,14 @@
package test.kar.archidata.model; package test.kar.archidata.model;
import jakarta.persistence.Column; import org.kar.archidata.model.GenericData;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
public class TypeManyToOneRemote { public class TypeManyToOneRemote extends GenericData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, unique = true)
public Long id = null;
public String data; public String data;
@Override
public String toString() {
return "TypeManyToOneRemote [data=" + this.data + ", id=" + this.id + "]";
}
} }

View File

@@ -1,20 +1,22 @@
package test.kar.archidata.model; package test.kar.archidata.model;
import org.kar.archidata.model.GenericData;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
public class TypeManyToOneRoot { public class TypeManyToOneRoot extends GenericData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, unique = true)
public Long id = null;
public String otherData; public String otherData;
@ManyToOne(targetEntity = TypeManyToOneRemote.class) @ManyToOne(targetEntity = TypeManyToOneRemote.class)
@Column(nullable = false) @Column(nullable = false)
public Long remoteId; public Long remoteId;
@Override
public String toString() {
return "TypeManyToOneRoot [otherData=" + this.otherData + ", remoteId=" + this.remoteId + ", id=" + this.id
+ "]";
}
} }

View File

@@ -1,23 +1,25 @@
package test.kar.archidata.model; package test.kar.archidata.model;
import org.kar.archidata.model.GenericData;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@Table(name = "TypeManyToOneRoot") @Table(name = "TypeManyToOneRoot")
public class TypeManyToOneRootExpand { public class TypeManyToOneRootExpand extends GenericData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, unique = true)
public Long id = null;
public String otherData; public String otherData;
@ManyToOne(fetch = FetchType.LAZY, targetEntity = TypeManyToOneRemote.class) @ManyToOne(fetch = FetchType.LAZY, targetEntity = TypeManyToOneRemote.class)
@Column(name = "remoteId", nullable = false) @Column(name = "remoteId", nullable = false)
public TypeManyToOneRemote remote; public TypeManyToOneRemote remote;
@Override
public String toString() {
return "TypeManyToOneRootExpand [otherData=" + this.otherData + ", remote=" + this.remote + ", id=" + this.id
+ "]";
}
} }

View File

@@ -0,0 +1,9 @@
package test.kar.archidata.model;
import org.kar.archidata.model.UUIDGenericData;
public class TypeManyToOneUUIDRemote extends UUIDGenericData {
public String data;
}

View File

@@ -0,0 +1,17 @@
package test.kar.archidata.model;
import java.util.UUID;
import org.kar.archidata.model.UUIDGenericData;
import jakarta.persistence.Column;
import jakarta.persistence.ManyToOne;
public class TypeManyToOneUUIDRoot extends UUIDGenericData {
public String otherData;
@ManyToOne(targetEntity = TypeManyToOneUUIDRemote.class)
@Column(nullable = false)
public UUID remoteUuid;
}

View File

@@ -0,0 +1,18 @@
package test.kar.archidata.model;
import org.kar.archidata.model.UUIDGenericData;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
@Table(name = "TypeManyToOneUUIDRoot")
public class TypeManyToOneUUIDRootExpand extends UUIDGenericData {
public String otherData;
@ManyToOne(fetch = FetchType.LAZY, targetEntity = TypeManyToOneUUIDRemote.class)
@Column(name = "remoteUuid", nullable = false)
public TypeManyToOneUUIDRemote remote;
}

View File

@@ -0,0 +1,15 @@
package test.kar.archidata.model;
import org.kar.archidata.model.GenericData;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToOne;
public class TypeOneToManyRemote extends GenericData {
@ManyToOne(fetch = FetchType.LAZY, targetEntity = TypeOneToManyRoot.class)
public Long rootId;
public String data;
}

View File

@@ -0,0 +1,15 @@
package test.kar.archidata.model;
import java.util.List;
import org.kar.archidata.model.GenericData;
import jakarta.persistence.OneToMany;
public class TypeOneToManyRoot extends GenericData {
public String otherData;
@OneToMany(targetEntity = TypeOneToManyRemote.class, mappedBy = "rootId")
public List<Long> remoteIds;
}

View File

@@ -0,0 +1,25 @@
package test.kar.archidata.model;
import java.util.List;
import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
@Table(name = "TypeOneToManyRoot")
public class TypeOneToManyRootExpand {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, unique = true)
public Long id = null;
public String otherData;
@OneToMany(targetEntity = TypeOneToManyRemote.class, mappedBy = "rootId")
@Column(nullable = false)
public List<TypeOneToManyRemote> remotes;
}

View File

@@ -0,0 +1,17 @@
package test.kar.archidata.model;
import java.util.UUID;
import org.kar.archidata.model.UUIDGenericData;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToOne;
public class TypeOneToManyUUIDRemote extends UUIDGenericData {
@ManyToOne(fetch = FetchType.LAZY, targetEntity = TypeOneToManyUUIDRoot.class)
public UUID rootUuid;
public String data;
}

View File

@@ -0,0 +1,18 @@
package test.kar.archidata.model;
import java.util.List;
import java.util.UUID;
import org.kar.archidata.model.UUIDGenericData;
import jakarta.persistence.Column;
import jakarta.persistence.OneToMany;
public class TypeOneToManyUUIDRoot extends UUIDGenericData {
public String otherData;
@OneToMany(targetEntity = TypeOneToManyUUIDRemote.class, mappedBy = "rootUuid")
@Column(nullable = false)
public List<UUID> remoteIds;
}

View File

@@ -0,0 +1,19 @@
package test.kar.archidata.model;
import java.util.List;
import org.kar.archidata.model.UUIDGenericData;
import jakarta.persistence.Column;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
@Table(name = "TypeOneToManyUUIDRoot")
public class TypeOneToManyUUIDRootExpand extends UUIDGenericData {
public String otherData;
@OneToMany(targetEntity = TypeOneToManyUUIDRemote.class, mappedBy = "rootUuid")
@Column(nullable = false)
public List<TypeOneToManyUUIDRemote> remotes;
}

View File

@@ -1 +1 @@
0.10.1 0.11.0