Compare commits

..

57 Commits

Author SHA1 Message Date
b9eb17e5c6 [RELEASE] Release v0.19.0 2024-12-12 09:16:17 +01:00
6d05b3444c [FEAT] update role management 2024-12-12 08:46:54 +01:00
dependabot[bot]
7b5e034ac2 [DEV-OPS] (dependabot) Bump actions/setup-java from 3 to 4
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-08 15:25:10 +01:00
dependabot[bot]
b4554a8bdb [DEV-OPS] (dependabot) Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-08 15:25:07 +01:00
dependabot[bot]
ae84d1c6c8 [DEV-OPS] (dependabot) Bump advanced-security/maven-dependency-submission-action
Bumps [advanced-security/maven-dependency-submission-action](https://github.com/advanced-security/maven-dependency-submission-action) from 2.0.0 to 4.1.1.
- [Release notes](https://github.com/advanced-security/maven-dependency-submission-action/releases)
- [Commits](571e99aab1...4f64ddab9d)

---
updated-dependencies:
- dependency-name: advanced-security/maven-dependency-submission-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-08 15:25:03 +01:00
239763cf48 [DEV] update dev tag version 2024-12-08 15:20:42 +01:00
754c422be0 [RELEASE] new version 0.18.0 2024-12-08 15:20:23 +01:00
091ac4babd [FEAT,API] (Token) upgrade tocken management to permit to control more conplex token 2024-12-07 16:52:44 +01:00
dccb6b80d5 [FIX] check null pointer in condition for SQL 2024-12-07 16:52:42 +01:00
5633604d13 [FIX] Json field parsing 2024-12-07 16:52:39 +01:00
96cb8a6e16 [FEAT] add control on User JPA 2024-12-07 16:52:36 +01:00
ebe88e4a8d [FEAT] (test-tool) set public some test API to be more permisive 2024-12-07 16:52:32 +01:00
c82ab9f27f [FEAT] add Email generic contraint check 2024-12-07 16:52:27 +01:00
f914462460 [FEAT] add workflow 2024-11-06 17:35:03 +01:00
9da5f589db [FEAT] auto add assignee 2024-11-06 16:01:36 +01:00
a0a35efeaf [VERSION] update dev tag version 2024-11-05 09:02:29 +01:00
abf1ddcf24 [RELEASE] Release v0.17.0 2024-11-05 09:02:25 +01:00
3bbbea87fa [FEAT] add List<String> modifiedValue in all JPA checker to permit to know wich field are mododified 2024-10-27 08:23:43 +01:00
25a163d4fa [VERSION] update dev tag version 2024-10-24 08:31:03 +02:00
c9b9d38efe [RELEASE] Release v0.16.0 2024-10-24 08:30:59 +02:00
cd3a6a1d8b [FIX] missing ARCHIVE & RESTORE in OPTION and correct the typescript request 2024-10-23 16:32:00 +02:00
Edouard DUPIN
5c1b7cd193 [FIX] add missing ARCHIVE and RESTORE in the rest-tool.ts 2024-10-23 10:59:12 +02:00
Edouard DUPIN
9ed09d4fed [FIX] name of the version 2024-10-23 10:50:17 +02:00
Edouard DUPIN
33665d47b8 [FIX] correction of the mis ordering in the many many request, force order by uuid 2024-10-23 10:47:08 +02:00
Edouard DUPIN
b907d2212a [FEAT] add ARCHIVE and RESTORE 2024-10-22 16:53:54 +02:00
a0f4680271 [DEV] update dev tag version 2024-09-20 19:47:44 +02:00
d9e118afaa [RELEASE] new version 0.15.0 2024-09-20 19:46:56 +02:00
9f43ebc782 [FIX] some fixes 2024-09-20 19:45:11 +02:00
8b831522dc [DEV] fix the get of the data 2024-09-20 19:02:22 +02:00
4f5d55bb01 [DEV] update dev tag version 2024-09-15 15:56:06 +02:00
9cbeee66c9 [RELEASE] new version 0.14.2 2024-09-15 15:55:00 +02:00
d25be53b77 [FIX] dot generation 2024-09-15 15:52:10 +02:00
d36f9c005a [DEV] update dev tag version 2024-09-15 15:46:37 +02:00
9a6d712d7a [RELEASE] new version 0.14.0 2024-09-15 15:45:57 +02:00
55275e4f26 [FEAT] update dependencies 2024-09-15 15:44:10 +02:00
f7de0e1db0 [FEAT] add optional for covers 2024-09-15 01:14:09 +02:00
032728f05d [FIX] correct Many to Many (normal way) anf first value field 2024-09-14 15:42:21 +02:00
bfe722f074 [FIX] corect the export if integer as an object 2024-09-14 10:20:43 +02:00
fddf41bea0 [FEAT] add some logs 2024-09-14 10:20:18 +02:00
3fa48fc839 [STYLE] fix style 2024-09-14 10:20:07 +02:00
de08bcfab5 [FEAT] someting is wrong 2024-09-12 00:24:37 +02:00
37f1362c3c [FEAT] start working on reverse @ManyToMany 2024-08-15 11:45:48 +02:00
e2ee68cc03 [FIX] export of LongWrite that does not exist 2024-08-15 11:45:17 +02:00
f05527ce01 [FIX] some annotation throws 2024-08-15 11:44:57 +02:00
38503fac8e [DEV] update development version 2024-06-25 15:48:53 +02:00
ab7259f726 [RELEASE] create version v0.13.0 2024-06-25 15:47:53 +02:00
cdb4581799 [FEAT] add link of List<UUID> that is decorate with @ManyToOne, or @ManyToMany or @OneToMany 2024-06-20 00:10:22 +02:00
7e81bfef28 [FEAT] add generation of dot files 2024-06-20 00:10:22 +02:00
84525fd7aa [FIX] cover tools 2024-06-13 08:27:33 +02:00
d4eb9c2a5f [FIX] correct the ID in uuid for uuid primary-key 2024-06-12 20:08:26 +02:00
15688f93e5 [FIX] version number 2024-06-12 00:55:51 +02:00
906216f237 [FIX] throw when uploading data 2024-06-12 00:54:42 +02:00
b479414bc2 [FIX] callbacks elements 2024-06-12 00:54:22 +02:00
2bc68321e3 [DEV] develoopment version 2024-06-12 00:53:29 +02:00
d77a5518ff [RELEASE] create version v0.12.1 2024-06-11 23:59:59 +02:00
5f9d13d315 [FEAT] remove in upload cover the filename 2024-06-11 23:48:43 +02:00
Yoann Fleury
0b57d8571c fix: plural for callbacks 2024-06-11 12:41:31 +02:00
59 changed files with 2543 additions and 591 deletions

View File

@@ -25,21 +25,24 @@
<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>
</classpathentry> </classpathentry>
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="target/generated-sources/annotations"> <classpathentry kind="src" path="target/generated-sources/annotations">
<attributes> <attributes>
<attribute name="optional" value="true"/> <attribute name="optional" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
<attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/> <classpathentry kind="output" path="target/classes"/>
</classpath> </classpath>

16
.github/workflows/assign-pr-author.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
---
name: "Assign PR Author as Assignee"
on:
pull_request:
types:
- opened
jobs:
assign-pr-author-as-assignee:
runs-on: ubuntu-latest
steps:
- name: "Assign Author as Assignee"
uses: itsOliverBott/assign-pr-author-as-assignee@latest
with:
token: ${{ secrets.GITHUB_TOKEN }}

33
.github/workflows/check-title.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
---
name: "Check PR title"
on:
pull_request:
types:
- opened
- edited
- synchronize
- ready_for_review
- reopened
jobs:
check-title:
runs-on: ubuntu-latest
steps:
- name: "Check title"
uses: Slashgear/action-check-pr-title@v4.3.0
with:
regexp: "\\[(API,)?(API|DEV-OPS|DOC|FEAT|FIX|FIX\\-CI|STYLE)\\]( \\([A-Za-z0-9.\\-]+\\))? [A-Za-z0-9 ,.'\\-!]+$"
helpMessage: |
Title of the PR MUST respect format: "[{TYPE}] clear description without typos in english" with {TYPE}:
* [API] Change API that permit to access on the application (un-compatibility only). This one can specifically added with [API,{TYPE}]
* [DEV-OPS] Update automatic build system, method to deliver application/packages, ...
* [DOC] Update or add some documentation.
* [FEAT] Develop a new feature
* [FIX] When fixing issue
* [FIX-CI] When the CI fail to build and we apply a correction to set it work again.
* [STYLE] Update of the style tools/checker, or add/remove rules.
Examples:
[FEAT] My beautiful feature
[API,FIX] Change API to fix typo
[FIX] (module) Correct part of ...

View File

@@ -10,19 +10,17 @@ name: Java CI with Maven
on: on:
push: push:
branches: [ "develop" ] branches:
- develop
pull_request: pull_request:
branches: [ "develop" ]
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@v3 uses: actions/setup-java@v4
with: with:
java-version: '21' java-version: '21'
distribution: 'temurin' distribution: 'temurin'
@@ -34,4 +32,4 @@ jobs:
# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
- name: Update dependency graph - name: Update dependency graph
uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 uses: advanced-security/maven-dependency-submission-action@4f64ddab9d742a4806eeb588d238e4c311a8397d

55
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.12.0</version> <version>0.19.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>
@@ -54,6 +54,30 @@
<version>2.1.0-alpha1</version> <version>2.1.0-alpha1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- Decode webP images -->
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-webp</artifactId>
<version>3.11.0</version>
</dependency>
<!-- Decode JPEG image -->
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.11.0</version>
</dependency>
<!-- Encode file in webp -->
<dependency>
<groupId>com.github.gotson</groupId>
<artifactId>webp-imageio</artifactId>
<version>0.2.2</version>
</dependency>
<!-- Detect type of a file with mime type -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>3.0.0-BETA2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-multipart --> <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-multipart -->
<dependency> <dependency>
<groupId>org.glassfish.jersey.media</groupId> <groupId>org.glassfish.jersey.media</groupId>
@@ -96,11 +120,13 @@
<artifactId>istack-commons-runtime</artifactId> <artifactId>istack-commons-runtime</artifactId>
<version>${istack.version}</version> <version>${istack.version}</version>
</dependency> </dependency>
<!-- continu to be needed ??? -->
<dependency> <dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId> <groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId> <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- Serialize and un-serialize request in JSON-->
<dependency> <dependency>
<groupId>org.glassfish.jersey.media</groupId> <groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId> <artifactId>jersey-media-json-jackson</artifactId>
@@ -108,40 +134,41 @@
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>2.17.1</version> <version>2.18.0-rc1</version>
</dependency> </dependency>
<!-- encode output in CSV -->
<dependency> <dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId> <groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId> <artifactId>jackson-dataformat-csv</artifactId>
<version>2.17.1</version> <version>2.18.0-rc1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.datatype</groupId> <groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId> <artifactId>jackson-datatype-jsr310</artifactId>
<version>2.17.1</version> <version>2.18.0-rc1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>jakarta.servlet</groupId> <groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId> <artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0-M2</version> <version>6.1.0</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- Interface for My-sql & sqlite DB --> <!-- Interface for My-sql & sqlite DB -->
<dependency> <dependency>
<groupId>com.mysql</groupId> <groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId> <artifactId>mysql-connector-j</artifactId>
<version>8.4.0</version> <version>9.0.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.xerial</groupId> <groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId> <artifactId>sqlite-jdbc</artifactId>
<version>3.46.0.0</version> <version>3.46.1.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.3</version> <version>9.41.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>jakarta.persistence</groupId> <groupId>jakarta.persistence</groupId>
@@ -152,13 +179,13 @@
<dependency> <dependency>
<groupId>io.swagger.core.v3</groupId> <groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-jakarta</artifactId> <artifactId>swagger-jaxrs2-jakarta</artifactId>
<version>2.2.22</version> <version>2.2.23</version>
</dependency> </dependency>
<!-- spotbug tooling --> <!-- spotbug tooling -->
<dependency> <dependency>
<groupId>com.github.spotbugs</groupId> <groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId> <artifactId>spotbugs-annotations</artifactId>
<version>4.8.5</version> <version>4.8.6</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<!-- <!--
@@ -169,24 +196,24 @@
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit-jupiter-api</artifactId>
<version>5.11.0-M2</version> <version>5.11.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId> <artifactId>junit-jupiter-engine</artifactId>
<version>5.11.0-M2</version> <version>5.11.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<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.24.0</version> <version>2.24.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId> <artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version> <version>3.5.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -0,0 +1,19 @@
package org.kar.archidata.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.ws.rs.HttpMethod;
/**
* Indicates that the annotated method responds to HTTP ARCHIVE requests.
*
* @author Edouard DUPIN
* @see HttpMethod
*/
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("ARCHIVE")
public @interface ARCHIVE {}

View File

@@ -17,8 +17,11 @@ import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
@@ -136,18 +139,30 @@ public class AnnotationTools {
return ((DefaultValue) annotation[0]).value(); return ((DefaultValue) annotation[0]).value();
} }
public static ManyToOne getManyToOne(final Field element) throws DataAccessException { public static ManyToOne getManyToOne(final Field element) {
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) {
throw new DataAccessException(
"Must not have more than 1 element @ManyToOne on " + element.getClass().getCanonicalName());
}
return (ManyToOne) annotation[0]; return (ManyToOne) annotation[0];
} }
public static ManyToMany getManyToMany(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(ManyToMany.class);
if (annotation.length == 0) {
return null;
}
return (ManyToMany) annotation[0];
}
public static OneToMany getOneToMany(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(OneToMany.class);
if (annotation.length == 0) {
return null;
}
return (OneToMany) annotation[0];
}
public static DataJson getDataJson(final Field element) throws DataAccessException { 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) {
@@ -221,6 +236,14 @@ public class AnnotationTools {
return ((Pattern) annotation[0]).regexp(); return ((Pattern) annotation[0]).regexp();
} }
public static boolean getConstraintsEmail(final Field element) throws DataAccessException {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Email.class);
if (annotation.length == 0) {
return false;
}
return true;
}
public static boolean isAnnotationGroup(final Field field, final Class<?> annotationType) { public static boolean isAnnotationGroup(final Field field, final Class<?> annotationType) {
try { try {
final Annotation[] anns = field.getAnnotations(); final Annotation[] anns = field.getAnnotations();
@@ -244,15 +267,11 @@ public class AnnotationTools {
return false; return false;
} }
public static String getFieldName(final Field element) throws DataAccessException { public static String getFieldName(final Field element) {
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) {
throw new DataAccessException(
"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();
if (name.isBlank()) { if (name.isBlank()) {
return element.getName(); return element.getName();

View File

@@ -0,0 +1,12 @@
package org.kar.archidata.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface FormDataOptional {
}

View File

@@ -0,0 +1,19 @@
package org.kar.archidata.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.ws.rs.HttpMethod;
/**
* Indicates that the annotated method responds to HTTP RESTORE requests.
*
* @author Edouard DUPIN
* @see HttpMethod
*/
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("RESTORE")
public @interface RESTORE {}

View File

@@ -304,9 +304,11 @@ public class DataResource {
// logger.info("==================================================="); // logger.info("===================================================");
final Data value = getSmall(uuid); final Data value = getSmall(uuid);
if (value == null) { if (value == null) {
LOGGER.warn("Request data that does not exist : {}", uuid);
return 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 { try {
LOGGER.warn("Generate stream : {}", uuid);
return buildStream(getFileData(uuid), range, return buildStream(getFileData(uuid), range,
value.mimeType == null ? "application/octet-stream" : value.mimeType); value.mimeType == null ? "application/octet-stream" : value.mimeType);
} catch (final Exception ex) { } catch (final Exception ex) {
@@ -326,10 +328,10 @@ public class DataResource {
@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 FailException { @PathParam("uuid") final UUID uuid) throws FailException {
// GenericContext gc = (GenericContext) sc.getUserPrincipal(); final 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.userByToken));
// logger.info("==================================================="); LOGGER.info("===================================================");
final Data value = getSmall(uuid); final Data value = getSmall(uuid);
if (value == null) { if (value == null) {
return 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();
@@ -350,31 +352,43 @@ public class DataResource {
} catch (final IOException ex) { } catch (final IOException ex) {
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to READ the image", ex); throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to READ the image", ex);
} }
LOGGER.info("input size image: {}x{} type={}", inputImage.getWidth(), inputImage.getHeight(),
inputImage.getType());
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);
// creates output image // creates output image
final BufferedImage outputImage = new BufferedImage(scaledWidth, scaledHeight, inputImage.getType()); final BufferedImage outputImage = new BufferedImage(scaledWidth, scaledHeight, inputImage.getType());
// scales the input image to the output image // scales the input image to the output image
final Graphics2D g2d = outputImage.createGraphics(); final Graphics2D g2d = outputImage.createGraphics();
LOGGER.info("output size image: {}x{}", scaledWidth, scaledHeight);
g2d.drawImage(inputImage, 0, 0, scaledWidth, scaledHeight, null); g2d.drawImage(inputImage, 0, 0, scaledWidth, scaledHeight, null);
g2d.dispose(); g2d.dispose();
for (final String data : ImageIO.getWriterFormatNames()) {
LOGGER.info("availlable format: {}", data);
}
// create the output stream: // create the output stream:
final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try { try {
// TODO: check how to remove buffer file !!! here, it is not needed at all... // TODO: check how to remove buffer file !!! here, it is not needed at all...
ImageIO.write(outputImage, "JPG", baos); //ImageIO.write(outputImage, "JPEG", baos);
//ImageIO.write(outputImage, "png", baos);
ImageIO.write(outputImage, "WebP", baos);
} catch (final IOException e) { } catch (final IOException e) {
e.printStackTrace(); e.printStackTrace();
return Response.status(500).entity("Internal Error: resize fail: " + e.getMessage()).type("text/plain") return Response.status(500).entity("Internal Error: resize fail: " + e.getMessage()).type("text/plain")
.build(); .build();
} }
final byte[] imageData = baos.toByteArray(); final byte[] imageData = baos.toByteArray();
LOGGER.info("output length {}", imageData.length);
// Response.ok(new ByteArrayInputStream(imageData)).build(); // Response.ok(new ByteArrayInputStream(imageData)).build();
final Response.ResponseBuilder out = Response.ok(imageData).header(HttpHeaders.CONTENT_LENGTH, final Response.ResponseBuilder out = Response.ok(imageData).header(HttpHeaders.CONTENT_LENGTH,
imageData.length); imageData.length);
out.type("image/jpeg"); //out.type("image/jpeg");
out.type("image/webp");
//out.type("image/png");
// TODO: move this in a decorator !!! // TODO: move this in a decorator !!!
final CacheControl cc = new CacheControl(); final CacheControl cc = new CacheControl();
cc.setMaxAge(3600); cc.setMaxAge(3600);
@@ -466,8 +480,9 @@ public class DataResource {
to = file.length() - 1; to = file.length() - 1;
} }
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);
try (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;

View File

@@ -58,5 +58,4 @@ public class MediaStreamer implements StreamingOutput {
public long getLenth() { public long getLenth() {
return this.length; return this.length;
} }
} }

View File

@@ -0,0 +1,43 @@
package org.kar.archidata.api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
@Path("/proxy")
//@Produces(MediaType.APPLICATION_JSON)
public class ProxyResource {
private static final Logger LOGGER = LoggerFactory.getLogger(ProxyResource.class);
@GET
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getImageFromUrl(@QueryParam("url") final String url) {
if (url == null || url.isEmpty()) {
return Response.status(Status.BAD_REQUEST).entity("URL manquante").build();
}
final Client client = ClientBuilder.newClient();
try {
final WebTarget target = client.target(url);
final Response response = target.request().get();
if (response.getStatus() != 200) {
return Response.status(Status.BAD_GATEWAY).entity("Can not get the image : " + response.getStatus())
.build();
}
return Response.ok(response.readEntity(byte[].class)).header("Access-Control-Allow-Origin", "*")
.header("Content-Type", response.getHeaderString("Content-Type")).build();
} catch (final Exception e) {
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("SERVER internal error : " + e.getMessage())
.build();
}
}
}

View File

@@ -20,6 +20,7 @@ public class WebApplicationExceptionCatcher implements ExceptionMapper<WebApplic
} }
private RestErrorResponse build(final WebApplicationException exception) { private RestErrorResponse build(final WebApplicationException exception) {
exception.printStackTrace();
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

@@ -1246,6 +1246,10 @@ public class DataAccess {
public static void addElement(final PreparedStatement ps, final Object value, final CountInOut iii) public static void addElement(final PreparedStatement ps, final Object value, final CountInOut iii)
throws Exception { throws Exception {
if (value == null) {
ps.setNull(iii.value, Types.INTEGER);
return;
}
if (value instanceof final UUID tmp) { if (value instanceof final UUID tmp) {
final byte[] dataByte = UuidUtils.asBytes(tmp); final byte[] dataByte = UuidUtils.asBytes(tmp);
ps.setBytes(iii.value, dataByte); ps.setBytes(iii.value, dataByte);
@@ -1297,6 +1301,7 @@ 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);
LOGGER.info("Query : '{}'", query);
try (final Statement stmt = entry.connection.createStatement()) { try (final Statement stmt = entry.connection.createStatement()) {
return stmt.executeUpdate(query); return stmt.executeUpdate(query);
} }

View File

@@ -27,7 +27,7 @@ import jakarta.persistence.GenerationType;
public class DataFactory { public class DataFactory {
static final Logger LOGGER = LoggerFactory.getLogger(DataFactory.class); static final Logger LOGGER = LoggerFactory.getLogger(DataFactory.class);
public static String convertTypeInSQL(final Class<?> type, final String fieldName) throws Exception { public static String convertTypeInSQL(final Class<?> type, final String fieldName) throws DataAccessException {
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
if (type == UUID.class) { if (type == UUID.class) {
return "binary(16)"; return "binary(16)";
@@ -165,7 +165,7 @@ public class DataFactory {
final String comment = AnnotationTools.getComment(elem); final String comment = AnnotationTools.getComment(elem);
final String defaultValue = AnnotationTools.getDefault(elem); final String defaultValue = AnnotationTools.getDefault(elem);
if (fieldId == 0) { if (mainTableBuilder.toString().length() == 0) {
mainTableBuilder.append("\n\t\t`"); mainTableBuilder.append("\n\t\t`");
} else { } else {
mainTableBuilder.append(",\n\t\t`"); mainTableBuilder.append(",\n\t\t`");
@@ -404,6 +404,7 @@ public class DataFactory {
} }
final boolean dataInThisObject = tmpOut.toString().length() > 0; final boolean dataInThisObject = tmpOut.toString().length() > 0;
if (dataInThisObject) { if (dataInThisObject) {
LOGGER.info("Previous Object : '{}'", reverseOut.toString());
final boolean dataInPreviousObject = reverseOut.toString().length() > 0; final boolean dataInPreviousObject = reverseOut.toString().length() > 0;
if (dataInPreviousObject) { if (dataInPreviousObject) {
tmpOut.append(", "); tmpOut.append(", ");

View File

@@ -23,13 +23,16 @@ import org.kar.archidata.dataAccess.addOn.model.TableCoversLongUUID;
import org.kar.archidata.dataAccess.addOn.model.TableCoversUUIDLong; import org.kar.archidata.dataAccess.addOn.model.TableCoversUUIDLong;
import org.kar.archidata.dataAccess.addOn.model.TableCoversUUIDUUID; import org.kar.archidata.dataAccess.addOn.model.TableCoversUUIDUUID;
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 com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.core.JsonProcessingException; 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.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
@@ -42,7 +45,7 @@ public class AddOnDataJson implements DataAccessAddOn {
} }
@Override @Override
public String getSQLFieldType(final Field elem) throws Exception { public String getSQLFieldType(final Field elem) throws DataAccessException {
final String fieldName = AnnotationTools.getFieldName(elem); final String fieldName = AnnotationTools.getFieldName(elem);
return DataFactory.convertTypeInSQL(String.class, fieldName); return DataFactory.convertTypeInSQL(String.class, fieldName);
} }
@@ -151,7 +154,10 @@ public class AddOnDataJson implements DataAccessAddOn {
} }
LOGGER.warn("Maybe fail to translate Model in datajson list: List<{}>", listClass.getCanonicalName()); LOGGER.warn("Maybe fail to translate Model in datajson list: List<{}>", listClass.getCanonicalName());
} }
final Object dataParsed = objectMapper.readValue(jsonData, field.getType()); final TypeFactory typeFactory = objectMapper.getTypeFactory();
final JavaType fieldType = typeFactory.constructType(field.getGenericType());
final Object dataParsed = objectMapper.readValue(jsonData, fieldType);
//final Object dataParsed = objectMapper.readValue(jsonData, field.getType());
field.set(data, dataParsed); field.set(data, dataParsed);
} }
} }
@@ -188,9 +194,21 @@ public class AddOnDataJson implements DataAccessAddOn {
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
} }
/**
* Adds a remoteKey to the covers list of a data entry identified by the given class type and ID.
* If the covers list is null, it initializes it. If the remoteKey already exists in the list,
* the method returns without making any changes.
*
* @param clazz The class type to retrieve the table name from.
* @param id The ID of the data object to fetch.
* @param column The name of the column (currently not used, but may be used for specifying a field name).
* @param remoteKey The UUID to add to the covers list.
* @throws Exception If an error occurs during data retrieval or update.
*/
public static void addLink(final Class<?> clazz, final Long id, final String column, final UUID remoteKey) public static void addLink(final Class<?> clazz, final Long id, final String column, final UUID remoteKey)
throws Exception { throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
// TODO: Get primary key name
final TableCoversLongUUID data = DataAccess.get(TableCoversLongUUID.class, id, final TableCoversLongUUID data = DataAccess.get(TableCoversLongUUID.class, id,
new OverrideTableName(tableName)); new OverrideTableName(tableName));
if (data.covers == null) { if (data.covers == null) {
@@ -205,10 +223,21 @@ public class AddOnDataJson implements DataAccessAddOn {
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));// TODO: ,new OverrideFieldName("covers", column)); DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));// TODO: ,new OverrideFieldName("covers", column));
} }
public static void addLink(final Class<?> clazz, final UUID id, final String column, final UUID remoteKey) /**
* Adds a remoteKey to the covers list of a data entry identified by the given class type and ID.
* If the covers list is null, it initializes it. If the remoteKey already exists in the list,
* the method returns without making any changes.
*
* @param clazz The class type to retrieve the table name from.
* @param id The ID of the data object to fetch.
* @param column The name of the column (currently not used, but may be used for specifying a field name).
* @param remoteKey The UUID to add to the covers list.
* @throws Exception If an error occurs during data retrieval or update.
*/
public static void addLink(final Class<?> clazz, final UUID uuid, final String column, final UUID remoteKey)
throws Exception { throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, id, final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, uuid,
new OverrideTableName(tableName)); new OverrideTableName(tableName));
if (data.covers == null) { if (data.covers == null) {
data.covers = new ArrayList<>(); data.covers = new ArrayList<>();
@@ -219,13 +248,13 @@ public class AddOnDataJson implements DataAccessAddOn {
} }
} }
data.covers.add(remoteKey); data.covers.add(remoteKey);
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); DataAccess.update(data, data.uuid, List.of("covers"), new OverrideTableName(tableName));
} }
public static void addLink(final Class<?> clazz, final UUID id, final String column, final Long remoteKey) public static void addLink(final Class<?> clazz, final UUID uuid, final String column, final Long remoteKey)
throws Exception { throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, id, final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, uuid,
new OverrideTableName(tableName)); new OverrideTableName(tableName));
if (data.covers == null) { if (data.covers == null) {
data.covers = new ArrayList<>(); data.covers = new ArrayList<>();
@@ -236,13 +265,13 @@ public class AddOnDataJson implements DataAccessAddOn {
} }
} }
data.covers.add(remoteKey); data.covers.add(remoteKey);
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); DataAccess.update(data, data.uuid, List.of("covers"), new OverrideTableName(tableName));
} }
public static void removeLink(final Class<?> clazz, final UUID id, final String column, final Long remoteKey) public static void removeLink(final Class<?> clazz, final UUID uuid, final String column, final Long remoteKey)
throws Exception { throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, id, final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, uuid,
new OverrideTableName(tableName)); new OverrideTableName(tableName));
if (data.covers == null) { if (data.covers == null) {
return; return;
@@ -255,13 +284,13 @@ public class AddOnDataJson implements DataAccessAddOn {
newList.add(elem); newList.add(elem);
} }
data.covers = newList; data.covers = newList;
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); DataAccess.update(data, data.uuid, List.of("covers"), new OverrideTableName(tableName));
} }
public static void removeLink(final Class<?> clazz, final UUID id, final String column, final UUID remoteKey) public static void removeLink(final Class<?> clazz, final UUID uuid, final String column, final UUID remoteKey)
throws Exception { throws Exception {
final String tableName = AnnotationTools.getTableName(clazz); final String tableName = AnnotationTools.getTableName(clazz);
final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, id, final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, uuid,
new OverrideTableName(tableName)); new OverrideTableName(tableName));
if (data.covers == null) { if (data.covers == null) {
return; return;
@@ -274,7 +303,7 @@ public class AddOnDataJson implements DataAccessAddOn {
newList.add(elem); newList.add(elem);
} }
data.covers = newList; data.covers = newList;
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); DataAccess.update(data, data.uuid, List.of("covers"), new OverrideTableName(tableName));
} }
public static void removeLink(final Class<?> clazz, final Long id, final String column, final Long remoteKey) public static void removeLink(final Class<?> clazz, final Long id, final String column, final Long remoteKey)
@@ -314,9 +343,4 @@ public class AddOnDataJson implements DataAccessAddOn {
data.covers = newList; data.covers = newList;
DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName));
} }
/* public static <TYPE> void addLink(final Class<TYPE> clazz, final Object localKey, final String column, final Object remoteKey) throws Exception { final String tableName =
* AnnotationTools.getTableName(clazz); final TYPE data = DataAccess.get(clazz, localKey); // TODO: add filter of the "column" // find the field column: // add the remoteKey in the list: // post
* new data in the DB final String linkTableName = generateLinkTableName(this.tableName, this.column); final LinkTable insertElement = new LinkTable(this.localKey, this.remoteKey);
* DataAccess.insert(insertElement, new OverrideTableName(linkTableName)); } */
} }

View File

@@ -108,16 +108,28 @@ public class AddOnManyToMany implements DataAccessAddOn {
@NotNull final String name, @NotNull final String name,
@NotNull final CountInOut count, @NotNull final CountInOut count,
final QueryOptions options) throws Exception { final QueryOptions options) throws Exception {
final String linkTableName = generateLinkTableName(tableName, name); final ManyToMany manyToMany = AnnotationTools.getManyToMany(field);
String linkTableName = generateLinkTableName(tableName, name);
if (manyToMany.mappedBy() != null && manyToMany.mappedBy().length() != 0) {
// TODO: get the remote table name .....
final String remoteTableName = AnnotationTools.getTableName(manyToMany.targetEntity());
linkTableName = generateLinkTableName(remoteTableName, manyToMany.mappedBy());
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0]; .getActualTypeArguments()[0];
final String tmpVariable = "tmp_" + Integer.toString(count.value); final String tmpVariable = "tmp_" + Integer.toString(count.value);
querySelect.append(" (SELECT GROUP_CONCAT("); querySelect.append(" (SELECT GROUP_CONCAT(");
querySelect.append(tmpVariable); querySelect.append(tmpVariable);
querySelect.append(".object2Id "); final boolean mode = manyToMany.mappedBy() == null || manyToMany.mappedBy().length() == 0;
if (mode) {
querySelect.append(".object2Id ");
} else {
querySelect.append(".object1Id ");
}
if ("sqlite".equals(ConfigBaseVariable.getDBType())) { if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
querySelect.append(", "); querySelect.append(", ");
} else { } else {
querySelect.append("ORDER BY uuid ASC ");
querySelect.append("SEPARATOR "); querySelect.append("SEPARATOR ");
} }
querySelect.append("'"); querySelect.append("'");
@@ -143,11 +155,19 @@ public class AddOnManyToMany implements DataAccessAddOn {
querySelect.append(" = "); querySelect.append(" = ");
querySelect.append(tmpVariable); querySelect.append(tmpVariable);
querySelect.append("."); querySelect.append(".");
querySelect.append("object1Id "); if (mode) {
querySelect.append("object1Id ");
} else {
querySelect.append("object2Id ");
}
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
querySelect.append(" GROUP BY "); querySelect.append(" GROUP BY ");
querySelect.append(tmpVariable); querySelect.append(tmpVariable);
querySelect.append(".object1Id"); if (mode) {
querySelect.append(".object1Id");
} else {
querySelect.append(".object2Id");
}
} }
querySelect.append(") AS "); querySelect.append(") AS ");
querySelect.append(name); querySelect.append(name);
@@ -509,6 +529,12 @@ public class AddOnManyToMany implements DataAccessAddOn {
final boolean createIfNotExist, final boolean createIfNotExist,
final boolean createDrop, final boolean createDrop,
final int fieldId) throws Exception { final int fieldId) throws Exception {
final ManyToMany manyToMany = AnnotationTools.getManyToMany(field);
if (manyToMany.mappedBy() != null && manyToMany.mappedBy().length() != 0) {
// not the reference model to create base:
return;
}
final String linkTableName = generateLinkTableNameField(tableName, field); final String linkTableName = generateLinkTableNameField(tableName, field);
final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName)); final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName));
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()) final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
@@ -527,7 +553,6 @@ public class AddOnManyToMany implements DataAccessAddOn {
postActionList.addAll(sqlCommand); postActionList.addAll(sqlCommand);
} }
} else if (primaryType == UUID.class) { } else if (primaryType == UUID.class) {
if (objectClass == Long.class) { if (objectClass == Long.class) {
final List<String> sqlCommand = DataFactory.createTable(LinkTableUUIDLong.class, options); final List<String> sqlCommand = DataFactory.createTable(LinkTableUUIDLong.class, options);
postActionList.addAll(sqlCommand); postActionList.addAll(sqlCommand);

View File

@@ -3,11 +3,10 @@ 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.GenericDataSoftDelete;
import jakarta.persistence.Column; import jakarta.persistence.Id;
public class TableCoversLongLong extends GenericDataSoftDelete { public class TableCoversLongLong {
public TableCoversLongLong() { public TableCoversLongLong() {
// nothing to do... // nothing to do...
} }
@@ -17,8 +16,10 @@ public class TableCoversLongLong extends GenericDataSoftDelete {
this.covers = covers; this.covers = covers;
} }
@Id
public Long id;
@DataJson() @DataJson()
@Column(nullable = false)
public List<Long> covers; public List<Long> covers;
} }

View File

@@ -1,25 +1,27 @@
package org.kar.archidata.dataAccess.addOn.model; package org.kar.archidata.dataAccess.addOn.model;
import java.util.ArrayList;
import java.util.List; 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.GenericDataSoftDelete;
import jakarta.persistence.Column; import jakarta.persistence.Id;
public class TableCoversLongUUID extends GenericDataSoftDelete { public class TableCoversLongUUID {
public TableCoversLongUUID() { public TableCoversLongUUID() {
// nothing to do... // nothing to do...
} }
public TableCoversLongUUID(final Long id, final List<UUID> covers) { public TableCoversLongUUID(final Long id, final List<UUID> covers) {
this.id = id; this.id = id;
this.covers = covers; this.covers = new ArrayList<>(covers);
} }
@Id
public Long id;
@DataJson() @DataJson()
@Column(nullable = false)
public List<UUID> covers; public List<UUID> covers;
} }

View File

@@ -1,29 +1,26 @@
package org.kar.archidata.dataAccess.addOn.model; package org.kar.archidata.dataAccess.addOn.model;
import java.util.ArrayList;
import java.util.List; 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.GenericDataSoftDelete;
import jakarta.persistence.Column;
import jakarta.persistence.Id; import jakarta.persistence.Id;
public class TableCoversUUIDLong extends GenericDataSoftDelete { public class TableCoversUUIDLong {
public TableCoversUUIDLong() { public TableCoversUUIDLong() {
// nothing to do... // nothing to do...
} }
public TableCoversUUIDLong(final UUID id, final List<Long> covers) { public TableCoversUUIDLong(final UUID uuid, final List<Long> covers) {
this.id = id; this.uuid = uuid;
this.covers = covers; this.covers = new ArrayList<>(covers);
} }
@Column(nullable = false)
@Id @Id
public UUID id; public UUID uuid;
@DataJson() @DataJson()
@Column(nullable = false)
public List<Long> covers; public List<Long> covers;
} }

View File

@@ -4,26 +4,22 @@ 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.GenericDataSoftDelete;
import jakarta.persistence.Column;
import jakarta.persistence.Id; import jakarta.persistence.Id;
public class TableCoversUUIDUUID extends GenericDataSoftDelete { public class TableCoversUUIDUUID {
public TableCoversUUIDUUID() { public TableCoversUUIDUUID() {
// nothing to do... // nothing to do...
} }
public TableCoversUUIDUUID(final UUID id, final List<UUID> covers) { public TableCoversUUIDUUID(final UUID uuid, final List<UUID> covers) {
this.id = id; this.uuid = uuid;
this.covers = covers; this.covers = covers;
} }
@Column(nullable = false)
@Id @Id
public UUID id; public UUID uuid;
@DataJson() @DataJson()
@Column(nullable = false)
public List<UUID> covers; public List<UUID> covers;
} }

View File

@@ -10,9 +10,9 @@ public interface CheckFunctionInterface {
/** This function implementation is design to check if the updated class is valid of not for insertion /** This function implementation is design to check if the updated class is valid of not for insertion
* @param baseName NAme of the object to be precise with the use of what fail. * @param baseName NAme of the object to be precise with the use of what fail.
* @param data The object that might be injected. * @param data The object that might be injected.
* @param filterValue List of fields that might be check. If null, then all column must be checked. * @param modifiedValue List of fields that might be check. If null, then all column must be checked.
* @throws Exception Exception is generate if the data are incorrect. */ * @throws Exception Exception is generate if the data are incorrect. */
void check(final String baseName, Object data, List<String> filterValue, final QueryOptions options) void check(final String baseName, Object data, List<String> modifiedValue, final QueryOptions options)
throws Exception; throws Exception;
default void checkAll(final String baseName, final Object data, final QueryOptions options) throws Exception { default void checkAll(final String baseName, final Object data, final QueryOptions options) throws Exception {

View File

@@ -35,10 +35,13 @@ public class CheckJPA<T> implements CheckFunctionInterface {
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */ /** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public interface CheckInterface<K> { public interface CheckInterface<K> {
/** This function implementation is design to check if the updated class is valid of not for insertion /** This function implementation is design to check if the updated class is valid of not for insertion
* @param baseName Base of the name input that is displayed in exception generated.
* @param data The object that might be injected. * @param data The object that might be injected.
* @param filterValue List of fields that might be check. If null, then all column must be checked. * @param modifiedValue List of fields that modification is requested.
* @param options Some query option that the checker can need to generate basic check.
* @throws Exception Exception is generate if the data are incorrect. */ * @throws Exception Exception is generate if the data are incorrect. */
void check(final String baseName, final K data, final QueryOptions options) throws Exception; void check(final String baseName, final K data, List<String> modifiedValue, final QueryOptions options)
throws Exception;
} }
protected Map<String, List<CheckInterface<T>>> checking = null; protected Map<String, List<CheckInterface<T>>> checking = null;
@@ -67,128 +70,182 @@ public class CheckJPA<T> implements CheckFunctionInterface {
for (final Field field : this.clazz.getFields()) { for (final Field field : this.clazz.getFields()) {
final String fieldName = field.getName(); // AnnotationTools.getFieldName(field); final String fieldName = field.getName(); // AnnotationTools.getFieldName(field);
if (AnnotationTools.isPrimaryKey(field)) { if (AnnotationTools.isPrimaryKey(field)) {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
throw new InputException(baseName + fieldName, (
"This is a '@Id' (primaryKey) ==> can not be change"); final String baseName,
}); final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
throw new InputException(baseName + fieldName,
"This is a '@Id' (primaryKey) ==> can not be change");
});
} }
if (AnnotationTools.getConstraintsNotNull(field)) { if (AnnotationTools.getConstraintsNotNull(field)) {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
if (field.get(data) == null) { (
throw new InputException(baseName + fieldName, "Can not be null"); final String baseName,
} final T data,
}); final List<String> modifiedValue,
final QueryOptions options) -> {
if (field.get(data) == null) {
throw new InputException(baseName + fieldName, "Can not be null");
}
});
} }
if (AnnotationTools.isCreatedAtField(field) || AnnotationTools.isUpdateAtField(field)) { if (AnnotationTools.isCreatedAtField(field) || AnnotationTools.isUpdateAtField(field)) {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
throw new InputException(baseName + fieldName, "It is forbidden to change this field"); (
}); final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
throw new InputException(baseName + fieldName, "It is forbidden to change this field");
});
} }
final Class<?> type = field.getType(); final Class<?> type = field.getType();
if (type == Long.class || type == long.class) { if (type == Long.class || type == long.class) {
final Long maxValue = AnnotationTools.getConstraintsMax(field); final Long maxValue = AnnotationTools.getConstraintsMax(field);
if (maxValue != null) { if (maxValue != null) {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final Long elemTyped = (Long) elem; final QueryOptions options) -> {
if (elemTyped > maxValue) { final Object elem = field.get(data);
throw new InputException(baseName + fieldName, "Value too height max: " + maxValue); if (elem == null) {
} return;
}); }
final Long elemTyped = (Long) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName,
"Value too height max: " + maxValue);
}
});
} }
final Long minValue = AnnotationTools.getConstraintsMin(field); final Long minValue = AnnotationTools.getConstraintsMin(field);
if (minValue != null) { if (minValue != null) {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final Long elemTyped = (Long) elem; final QueryOptions options) -> {
if (elemTyped < minValue) { final Object elem = field.get(data);
throw new InputException(baseName + fieldName, "Value too Low min: " + minValue); if (elem == null) {
} return;
}); }
final Long elemTyped = (Long) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName,
"Value too Low min: " + minValue);
}
});
} }
final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field); final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field);
if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) { if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class); final QueryOptions options) -> {
final Condition conditionCheck = condCheckers.isEmpty() ? null final Object elem = field.get(data);
: condCheckers.get(0).toCondition(); if (elem == null) {
final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem, return;
conditionCheck); }
if (count == 0) { final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class);
throw new InputException(baseName + fieldName, final Condition conditionCheck = condCheckers.isEmpty() ? null
"Foreign element does not exist in the DB:" + elem); : condCheckers.get(0).toCondition();
} final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem,
}); conditionCheck);
if (count == 0) {
throw new InputException(baseName + fieldName,
"Foreign element does not exist in the DB:" + elem);
}
});
} }
} else if (type == Integer.class || type == int.class) { } else if (type == Integer.class || type == int.class) {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field); final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) { if (maxValueRoot != null) {
final int maxValue = maxValueRoot.intValue(); final int maxValue = maxValueRoot.intValue();
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final Integer elemTyped = (Integer) elem; final QueryOptions options) -> {
if (elemTyped > maxValue) { final Object elem = field.get(data);
throw new InputException(baseName + fieldName, "Value too height max: " + maxValue); if (elem == null) {
} return;
}); }
final Integer elemTyped = (Integer) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName,
"Value too height max: " + maxValue);
}
});
} }
final Long minValueRoot = AnnotationTools.getConstraintsMin(field); final Long minValueRoot = AnnotationTools.getConstraintsMin(field);
if (minValueRoot != null) { if (minValueRoot != null) {
final int minValue = minValueRoot.intValue(); final int minValue = minValueRoot.intValue();
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final Integer elemTyped = (Integer) elem; final QueryOptions options) -> {
if (elemTyped < minValue) { final Object elem = field.get(data);
throw new InputException(baseName + fieldName, "Value too Low min: " + minValue); if (elem == null) {
} return;
}); }
final Integer elemTyped = (Integer) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName,
"Value too Low min: " + minValue);
}
});
} }
final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field); final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field);
if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) { if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem); final QueryOptions options) -> {
if (count == 0) { final Object elem = field.get(data);
throw new InputException(baseName + fieldName, if (elem == null) {
"Foreign element does not exist in the DB:" + elem); return;
} }
}); final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem);
if (count == 0) {
throw new InputException(baseName + fieldName,
"Foreign element does not exist in the DB:" + elem);
}
});
} }
} else if (type == UUID.class) { } else if (type == UUID.class) {
final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field); final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field);
if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) { if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem); final QueryOptions options) -> {
if (count == 0) { final Object elem = field.get(data);
throw new InputException(baseName + fieldName, if (elem == null) {
"Foreign element does not exist in the DB:" + elem); return;
} }
}); final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem);
if (count == 0) {
throw new InputException(baseName + fieldName,
"Foreign element does not exist in the DB:" + elem);
}
});
} }
} else if (type == Boolean.class || type == boolean.class) { } else if (type == Boolean.class || type == boolean.class) {
@@ -196,59 +253,83 @@ public class CheckJPA<T> implements CheckFunctionInterface {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field); final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) { if (maxValueRoot != null) {
final float maxValue = maxValueRoot.floatValue(); final float maxValue = maxValueRoot.floatValue();
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final Float elemTyped = (Float) elem; final QueryOptions options) -> {
if (elemTyped > maxValue) { final Object elem = field.get(data);
throw new InputException(baseName + fieldName, "Value too height max: " + maxValue); if (elem == null) {
} return;
}); }
final Float elemTyped = (Float) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName,
"Value too height max: " + maxValue);
}
});
} }
final Long minValueRoot = AnnotationTools.getConstraintsMin(field); final Long minValueRoot = AnnotationTools.getConstraintsMin(field);
if (minValueRoot != null) { if (minValueRoot != null) {
final float minValue = minValueRoot.floatValue(); final float minValue = minValueRoot.floatValue();
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final Float elemTyped = (Float) elem; final QueryOptions options) -> {
if (elemTyped < minValue) { final Object elem = field.get(data);
throw new InputException(baseName + fieldName, "Value too Low min: " + minValue); if (elem == null) {
} return;
}); }
final Float elemTyped = (Float) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName,
"Value too Low min: " + minValue);
}
});
} }
} else if (type == Double.class || type == double.class) { } else if (type == Double.class || type == double.class) {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field); final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) { if (maxValueRoot != null) {
final double maxValue = maxValueRoot.doubleValue(); final double maxValue = maxValueRoot.doubleValue();
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final Double elemTyped = (Double) elem; final QueryOptions options) -> {
if (elemTyped > maxValue) { final Object elem = field.get(data);
throw new InputException(baseName + fieldName, "Value too height max: " + maxValue); if (elem == null) {
} return;
}); }
final Double elemTyped = (Double) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName,
"Value too height max: " + maxValue);
}
});
} }
final Long minValueRoot = AnnotationTools.getConstraintsMin(field); final Long minValueRoot = AnnotationTools.getConstraintsMin(field);
if (minValueRoot != null) { if (minValueRoot != null) {
final double minValue = minValueRoot.doubleValue(); final double minValue = minValueRoot.doubleValue();
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final Double elemTyped = (Double) elem; final QueryOptions options) -> {
if (elemTyped < minValue) { final Object elem = field.get(data);
throw new InputException(baseName + fieldName, "Value too Low min: " + minValue); if (elem == null) {
} return;
}); }
final Double elemTyped = (Double) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName,
"Value too Low min: " + minValue);
}
});
} }
} else if (type == Date.class || type == Timestamp.class) { } else if (type == Date.class || type == Timestamp.class) {
@@ -259,51 +340,87 @@ public class CheckJPA<T> implements CheckFunctionInterface {
} else if (type == String.class) { } else if (type == String.class) {
final int maxSizeString = AnnotationTools.getLimitSize(field); final int maxSizeString = AnnotationTools.getLimitSize(field);
if (maxSizeString > 0) { if (maxSizeString > 0) {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final String elemTyped = (String) elem; final QueryOptions options) -> {
if (elemTyped.length() > maxSizeString) { final Object elem = field.get(data);
throw new InputException(baseName + fieldName, if (elem == null) {
"Too long size must be <= " + maxSizeString); return;
} }
}); final String elemTyped = (String) elem;
if (elemTyped.length() > maxSizeString) {
throw new InputException(baseName + fieldName,
"Too long size must be <= " + maxSizeString);
}
});
} }
final Size limitSize = AnnotationTools.getConstraintsSize(field); final Size limitSize = AnnotationTools.getConstraintsSize(field);
if (limitSize != null) { if (limitSize != null) {
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final String elemTyped = (String) elem; final QueryOptions options) -> {
if (elemTyped.length() > limitSize.max()) { final Object elem = field.get(data);
throw new InputException(baseName + fieldName, if (elem == null) {
"Too long size (constraints) must be <= " + limitSize.max()); return;
} }
if (elemTyped.length() < limitSize.min()) { final String elemTyped = (String) elem;
throw new InputException(baseName + fieldName, if (elemTyped.length() > limitSize.max()) {
"Too small size (constraints) must be >= " + limitSize.min()); throw new InputException(baseName + fieldName,
} "Too long size (constraints) must be <= " + limitSize.max());
}); }
if (elemTyped.length() < limitSize.min()) {
throw new InputException(baseName + fieldName,
"Too small size (constraints) must be >= " + limitSize.min());
}
});
} }
final String patternString = AnnotationTools.getConstraintsPattern(field); final String patternString = AnnotationTools.getConstraintsPattern(field);
if (patternString != null) { if (patternString != null) {
final Pattern pattern = Pattern.compile(patternString); final Pattern pattern = Pattern.compile(patternString);
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final Object elem = field.get(data); (
if (elem == null) { final String baseName,
return; final T data,
} final List<String> modifiedValue,
final String elemTyped = (String) elem; final QueryOptions options) -> {
if (!pattern.matcher(elemTyped).find()) { final Object elem = field.get(data);
throw new InputException(baseName + fieldName, if (elem == null) {
"does not match the required pattern (constraints) must be '" + patternString return;
+ "'"); }
} final String elemTyped = (String) elem;
}); if (!pattern.matcher(elemTyped).find()) {
throw new InputException(baseName + fieldName,
"does not match the required pattern (constraints) must be '"
+ patternString + "'");
}
});
}
if (AnnotationTools.getConstraintsEmail(field)) {
final String emailPattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
final Pattern pattern = Pattern.compile(emailPattern);
add(fieldName,
(
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final String elemTyped = (String) elem;
if (!pattern.matcher(elemTyped).find()) {
throw new InputException(baseName + fieldName,
"does not match the required pattern[email] (constraints) must be '"
+ emailPattern + "'");
}
});
} }
} else if (type == JsonValue.class) { } else if (type == JsonValue.class) {
final DataJson jsonAnnotation = AnnotationTools.getDataJson(field); final DataJson jsonAnnotation = AnnotationTools.getDataJson(field);
@@ -311,9 +428,14 @@ public class CheckJPA<T> implements CheckFunctionInterface {
// Here if we have an error it crash at start and no new instance after creation... // Here if we have an error it crash at start and no new instance after creation...
final CheckFunctionInterface instance = jsonAnnotation.checker().getDeclaredConstructor() final CheckFunctionInterface instance = jsonAnnotation.checker().getDeclaredConstructor()
.newInstance(); .newInstance();
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
instance.checkAll(baseName + fieldName + ".", field.get(data), options); (
}); final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
instance.checkAll(baseName + fieldName + ".", field.get(data), options);
});
} }
} else if (type.isEnum()) { } else if (type.isEnum()) {
// nothing to do. // nothing to do.
@@ -321,21 +443,26 @@ public class CheckJPA<T> implements CheckFunctionInterface {
// keep this is last ==> take more time... // keep this is last ==> take more time...
if (AnnotationTools.isUnique(field)) { if (AnnotationTools.isUnique(field)) {
// Create the request ... // Create the request ...
add(fieldName, (final String baseName, final T data, final QueryOptions options) -> { add(fieldName,
final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class); (
Object other = null; final String baseName,
if (condCheckers.isEmpty()) { final T data,
other = DataAccess.getWhere(this.clazz, final List<String> modifiedValue,
new Condition(new QueryCondition(fieldName, "==", field.get(data)))); final QueryOptions options) -> {
} else { final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class);
other = DataAccess.getWhere(this.clazz, Object other = null;
new Condition(new QueryCondition(fieldName, "==", field.get(data))), if (condCheckers.isEmpty()) {
condCheckers.get(0).toCondition()); other = DataAccess.getWhere(this.clazz,
} new Condition(new QueryCondition(fieldName, "==", field.get(data))));
if (other != null) { } else {
throw new InputException(baseName + fieldName, "Name already exist in the DB"); other = DataAccess.getWhere(this.clazz,
} new Condition(new QueryCondition(fieldName, "==", field.get(data))),
}); condCheckers.get(0).toCondition());
}
if (other != null) {
throw new InputException(baseName + fieldName, "Name already exist in the DB");
}
});
} }
} }
@@ -349,7 +476,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
public void check( public void check(
final String baseName, final String baseName,
final Object data, final Object data,
final List<String> filterValue, final List<String> modifiedValue,
final QueryOptions options) throws Exception { final QueryOptions options) throws Exception {
if (this.checking == null) { if (this.checking == null) {
initialize(); initialize();
@@ -359,19 +486,20 @@ public class CheckJPA<T> implements CheckFunctionInterface {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final T dataCasted = (T) data; final T dataCasted = (T) data;
for (final String filter : filterValue) { for (final String filter : modifiedValue) {
final List<CheckInterface<T>> actions = this.checking.get(filter); final List<CheckInterface<T>> actions = this.checking.get(filter);
if (actions == null) { if (actions == null) {
continue; continue;
} }
for (final CheckInterface<T> action : actions) { for (final CheckInterface<T> action : actions) {
action.check(baseName, dataCasted, options); action.check(baseName, dataCasted, modifiedValue, options);
} }
} }
checkTyped(dataCasted, filterValue, options); checkTyped(dataCasted, modifiedValue, options);
} }
public void checkTyped(final T data, final List<String> filterValue, final QueryOptions options) throws Exception { public void checkTyped(final T data, final List<String> modifiedValue, final QueryOptions options)
throws Exception {
// nothing to do ... // nothing to do ...
} }
} }

View File

@@ -63,6 +63,7 @@ public class DBEntry implements Closeable {
this.connection = DriverManager.getConnection(this.config.getUrl(), this.config.getLogin(), this.connection = DriverManager.getConnection(this.config.getUrl(), this.config.getLogin(),
this.config.getPassword()); this.config.getPassword());
} catch (final SQLException ex) { } catch (final SQLException ex) {
LOGGER.error("Connection db fail: " + ex.getMessage() + " On URL: " + this.config.getUrl(true));
throw new IOException("Connection db fail: " + ex.getMessage() + " On URL: " + this.config.getUrl(true)); throw new IOException("Connection db fail: " + ex.getMessage() + " On URL: " + this.config.getUrl(true));
} }

View File

@@ -71,4 +71,25 @@ public class AnalyzeApi {
} }
return out; return out;
} }
public void displayAllApi() {
LOGGER.info("List all API:");
for (final ApiGroupModel model : getAllApi()) {
LOGGER.info(" - {}: {}", model.name, model.getClass().getCanonicalName());
}
}
public void displayAllModel() {
LOGGER.info("List all Model:");
for (final ClassModel model : getAllModel()) {
final StringBuilder out = new StringBuilder();
for (final ClassModel classModel : model.getAlls()) {
out.append(classModel.getOriginClasses().getCanonicalName());
out.append(",");
}
LOGGER.info(" - {}", out.toString());
}
}
} }

View File

@@ -0,0 +1,232 @@
package org.kar.archidata.externalRestApi;
import java.io.FileWriter;
import java.io.InputStream;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.glassfish.jersey.media.multipart.ContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.kar.archidata.catcher.RestErrorResponse;
import org.kar.archidata.externalRestApi.dot.DotApiGeneration;
import org.kar.archidata.externalRestApi.dot.DotClassElement;
import org.kar.archidata.externalRestApi.dot.DotClassElement.DefinedPosition;
import org.kar.archidata.externalRestApi.dot.DotClassElementGroup;
import org.kar.archidata.externalRestApi.model.ApiGroupModel;
import org.kar.archidata.externalRestApi.model.ClassModel;
public class DotGenerateApi {
public static void generateApi(final AnalyzeApi api, final String pathDotFile) throws Exception {
final List<DotClassElement> localModel = generateApiModel(api);
final DotClassElementGroup dotGroup = new DotClassElementGroup(localModel);
try (final FileWriter myWriter = new FileWriter(pathDotFile)) {
myWriter.write("""
# Architecture auto-generated file
digraph UML_Class_diagram {
#rankdir=NS;
graph [
pad="0.5"
nodesep="1"
#ranksep="2"
label="Rest API server Model"
labelloc="t"
fontname="FreeMono,Sans-Mono,Helvetica,Arial,sans-serif"
]
node [
fontname="FreeMono,Sans-Mono,Helvetica,Arial,sans-serif"
shape=record
style=filled
fillcolor=gray95
]
edge [fontname="FreeMono,Sans-Mono,Helvetica,Arial,sans-serif"]
""");
/*
myWriter.write("""
subgraph REST_API {
style=filled;
color=lightgrey;
label="REST API";
rankdir=LR;
""");
*/
for (final ApiGroupModel element : api.apiModels) {
final String tmp = DotApiGeneration.generateApiFile(element, dotGroup);
myWriter.write(tmp);
myWriter.write("\n");
}
// create an invisible link to force all element to be link together:
if (false) {
String previous = null;
for (final ApiGroupModel element : api.apiModels) {
if (previous == null) {
previous = element.name;
continue;
}
myWriter.write("\t{ ");
myWriter.write(previous);
myWriter.write(":s -> ");
previous = element.name;
myWriter.write(previous);
myWriter.write(":n [style=invis]}\n");
}
}
/*
myWriter.write("""
}
""");
myWriter.write("""
subgraph Models {
style=filled;
color=lightgrey;
label="Models";
rankdir=NS;
""");
*/
// Generates all MODEL files
for (final DotClassElement element : localModel) {
final String tmp = element.generateFile(dotGroup);
myWriter.write(tmp);
myWriter.write("\n");
}
/*
myWriter.write("""
}
""");
*/
myWriter.write("""
}
""");
}
}
private static List<DotClassElement> generateApiModel(final AnalyzeApi api) throws Exception {
// First step is to add all specific basic elements the wrap correctly the model
final List<DotClassElement> dotModels = new ArrayList<>();
List<ClassModel> models = api.getCompatibleModels(List.of(Void.class, void.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "void", "void", null, null, DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(Object.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Object", "object", null, "Object", DefinedPosition.NATIVE));
}
// Map is binded to any ==> can not determine this complex model for now
models = api.getCompatibleModels(List.of(Map.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Object", "any", null, null, DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(String.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "String", "string", null, "String", DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(
List.of(InputStream.class, FormDataContentDisposition.class, ContentDisposition.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "File", "File", null, "File", DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(Boolean.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Boolean", "boolean", null, "Boolean", DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(boolean.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "boolean", "boolean", null, "boolean", DefinedPosition.NATIVE));
}
models = api.getCompatibleModels(List.of(UUID.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "UUID", "UUID", "isUUID", "UUID", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(long.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "long", "Long", "isLong", "long", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Long.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Long", "Long", "isLong", "Long", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(short.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "short", "Short", "isShort", "short", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Short.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Short", "Short", "isShort", "Short", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(int.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "int", "Integer", "isInteger", "int", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Integer.class));
if (models != null) {
dotModels.add(
new DotClassElement(models, "Integer", "Integer", "isInteger", "Integer", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(double.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Double", "Double", "isDouble", "double", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Double.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Double", "Double", "isDouble", "Double", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(float.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "float", "Float", "isFloat", "float", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Float.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Float", "Float", "isFloat", "Float", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Instant.class));
if (models != null) {
dotModels.add(
new DotClassElement(models, "Instant", "Instant", "isInstant", "Instant", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Date.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Date", "IsoDate", "isIsoDate", "Date", DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(Timestamp.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "Timestamp", "Timestamp", "isTimestamp", "Timestamp",
DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(LocalDate.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "LocalDate", "LocalDate", "isLocalDate", "LocalDate",
DefinedPosition.BASIC));
}
models = api.getCompatibleModels(List.of(LocalTime.class));
if (models != null) {
dotModels.add(new DotClassElement(models, "LocalTime", "LocalTime", "isLocalTime", "LocalTime",
DefinedPosition.BASIC));
}
// needed for Rest interface
api.addModel(RestErrorResponse.class);
for (final ClassModel model : api.getAllModel()) {
boolean alreadyExist = false;
for (final DotClassElement elem : dotModels) {
if (elem.isCompatible(model)) {
alreadyExist = true;
break;
}
}
if (alreadyExist) {
continue;
}
dotModels.add(new DotClassElement(model));
}
return dotModels;
}
}

View File

@@ -30,7 +30,7 @@ import org.kar.archidata.externalRestApi.typescript.TsClassElementGroup;
public class TsGenerateApi { public class TsGenerateApi {
/** /**
* Generate a full API tree for Typescript in a specific folder. * 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. * This generate a folder containing a full API with "model" folder and "api" folder.
* The generation depend of Zod and can be strict compile. * The generation depend of Zod and can be strict compile.
* @param api Data model to generate the api * @param api Data model to generate the api
* @param pathPackage Path to store the api. * @param pathPackage Path to store the api.
@@ -118,6 +118,8 @@ public class TsGenerateApi {
} }
private static List<TsClassElement> generateApiModel(final AnalyzeApi api) throws Exception { private static List<TsClassElement> generateApiModel(final AnalyzeApi api) throws Exception {
// needed for Rest interface
api.addModel(RestErrorResponse.class);
// First step is to add all specific basic elements the wrap correctly the model // First step is to add all specific basic elements the wrap correctly the model
final List<TsClassElement> tsModels = new ArrayList<>(); final List<TsClassElement> tsModels = new ArrayList<>();
List<ClassModel> models = api.getCompatibleModels(List.of(Void.class, void.class)); List<ClassModel> models = api.getCompatibleModels(List.of(Void.class, void.class));
@@ -205,8 +207,6 @@ public class TsGenerateApi {
tsModels.add(new TsClassElement(models, "ZodLocalTime", "LocalTime", "isLocalTime", "zod.string().time()", tsModels.add(new TsClassElement(models, "ZodLocalTime", "LocalTime", "isLocalTime", "zod.string().time()",
DefinedPosition.BASIC)); DefinedPosition.BASIC));
} }
// needed for Rest interface
api.addModel(RestErrorResponse.class);
for (final ClassModel model : api.getAllModel()) { for (final ClassModel model : api.getAllModel()) {
boolean alreadyExist = false; boolean alreadyExist = false;
for (final TsClassElement elem : tsModels) { for (final TsClassElement elem : tsModels) {

View File

@@ -0,0 +1,415 @@
package org.kar.archidata.externalRestApi.dot;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import org.kar.archidata.externalRestApi.dot.DotClassElement.DefinedPosition;
import org.kar.archidata.externalRestApi.model.ApiGroupModel;
import org.kar.archidata.externalRestApi.model.ApiModel;
import org.kar.archidata.externalRestApi.model.ApiModel.OptionalClassModel;
import org.kar.archidata.externalRestApi.model.ClassEnumModel;
import org.kar.archidata.externalRestApi.model.ClassListModel;
import org.kar.archidata.externalRestApi.model.ClassMapModel;
import org.kar.archidata.externalRestApi.model.ClassModel;
import org.kar.archidata.externalRestApi.model.ClassObjectModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DotApiGeneration {
static final Logger LOGGER = LoggerFactory.getLogger(DotApiGeneration.class);
public static String generateClassEnumModelTypescript(
final ClassEnumModel model,
final DotClassElementGroup dotGroup,
final Set<ClassModel> imports) throws IOException {
imports.add(model);
final DotClassElement dotModel = dotGroup.find(model);
return dotModel.dotTypeName;
}
public static String generateClassObjectModelTypescript(
final ClassObjectModel model,
final DotClassElementGroup dotGroup,
final Set<ClassModel> imports) throws IOException {
final DotClassElement dotModel = dotGroup.find(model);
if (dotModel.nativeType != DefinedPosition.NATIVE) {
imports.add(model);
}
if (dotModel.nativeType != DefinedPosition.NORMAL) {
return dotModel.dotTypeName;
}
return dotModel.dotTypeName;
}
public static String generateClassMapModelTypescript(
final ClassMapModel model,
final DotClassElementGroup dotGroup,
final Set<ClassModel> imports) throws IOException {
final StringBuilder out = new StringBuilder();
out.append("Map&lt;");
out.append(generateClassModelTypescript(model.keyModel, dotGroup, imports));
out.append(", ");
out.append(generateClassModelTypescript(model.valueModel, dotGroup, imports));
out.append("&gt;");
return out.toString();
}
public static String generateClassListModelTypescript(
final ClassListModel model,
final DotClassElementGroup dotGroup,
final Set<ClassModel> imports) throws IOException {
final StringBuilder out = new StringBuilder();
out.append("List&lt;");
out.append(generateClassModelTypescript(model.valueModel, dotGroup, imports));
out.append("&gt;");
return out.toString();
}
public static String generateClassModelTypescript(
final ClassModel model,
final DotClassElementGroup dotGroup,
final Set<ClassModel> imports) throws IOException {
if (model instanceof final ClassObjectModel objectModel) {
return generateClassObjectModelTypescript(objectModel, dotGroup, imports);
}
if (model instanceof final ClassListModel listModel) {
return generateClassListModelTypescript(listModel, dotGroup, imports);
}
if (model instanceof final ClassMapModel mapModel) {
return generateClassMapModelTypescript(mapModel, dotGroup, imports);
}
if (model instanceof final ClassEnumModel enumModel) {
return generateClassEnumModelTypescript(enumModel, dotGroup, imports);
}
throw new IOException("Impossible model:" + model);
}
public static String generateClassModelsTypescript(
final List<ClassModel> models,
final DotClassElementGroup dotGroup) throws IOException {
if (models.size() == 0) {
return "void";
}
final StringBuilder out = new StringBuilder();
if (models.size() > 1) {
out.append("Union&lt;");
}
boolean isFirst = true;
for (final ClassModel model : models) {
if (isFirst) {
isFirst = false;
} else {
out.append(", ");
}
final String data = DotClassElement.generateClassModelTypescript(model, dotGroup);
out.append(data);
}
if (models.size() > 1) {
out.append("&gt;");
}
return out.toString();
}
public static List<String> generateClassModelsLinks(
final List<ClassModel> models,
final DotClassElementGroup dotGroup) throws IOException {
if (models.size() == 0) {
return null;
}
final List<String> out = new ArrayList<>();
final boolean isFirst = true;
for (final ClassModel model : models) {
final String data = DotClassElement.generateClassModelTypescriptLink(model, dotGroup);
if (data != null) {
out.add(data);
}
}
return out;
}
public static String capitalizeFirstLetter(final String str) {
if (str == null || str.isEmpty()) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
public static String generateApiFile(final ApiGroupModel element, final DotClassElementGroup dotGroup)
throws IOException {
final StringBuilder data = new StringBuilder();
final StringBuilder outLinks = new StringBuilder();
data.append("""
%s [
shape=plain
label=<<table color="#FF3333" border="2" cellborder="1" cellspacing="0" cellpadding="4">
<tr>
<td><b>%s</b><br/>(REST)</td>
</tr>
<tr>
<td>
<table border="0" cellborder="0" cellspacing="0" >
""".formatted(element.name, element.name));
final Set<ClassModel> imports = new HashSet<>();
final Set<ClassModel> zodImports = new HashSet<>();
final Set<ClassModel> isImports = new HashSet<>();
final Set<ClassModel> writeImports = new HashSet<>();
final Set<String> toolImports = new HashSet<>();
for (final ApiModel interfaceElement : element.interfaces) {
final List<String> consumes = interfaceElement.consumes;
final List<String> produces = interfaceElement.produces;
final boolean needGenerateProgress = interfaceElement.needGenerateProgress;
/*
if (returnComplexModel != null) {
data.append(returnComplexModel.replaceAll("(?m)^", "\t"));
for (final ClassModel elem : interfaceElement.returnTypes) {
zodImports.addAll(elem.getDependencyGroupModels());
}
}
*/
data.append("\t\t\t\t\t<tr><td align=\"left\" port=\"");
data.append(interfaceElement.name);
data.append("\"><b> + ");
data.append(interfaceElement.name);
data.append("(");
boolean hasParam = false;
/*
if (!interfaceElement.queries.isEmpty()) {
data.append("\n\t\tqueries: {");
for (final Entry<String, List<ClassModel>> queryEntry : interfaceElement.queries.entrySet()) {
data.append("\n\t\t\t");
data.append(queryEntry.getKey());
data.append("?: ");
data.append(generateClassModelsTypescript(queryEntry.getValue(), dotGroup, imports, false));
data.append(",");
}
data.append("\n\t\t},");
}
*/
/* fonctionnel mais trop de donnée
if (!interfaceElement.parameters.isEmpty()) {
//data.append("params: {");
for (final Entry<String, List<ClassModel>> paramEntry : interfaceElement.parameters.entrySet()) {
data.append("");
data.append(paramEntry.getKey());
data.append(": ");
data.append(generateClassModelsTypescript(paramEntry.getValue(), dotGroup, imports, false));
data.append(",");
}
//data.append("},");
}
*/
if (interfaceElement.unnamedElement.size() == 1) {
if (hasParam) {
data.append(", ");
}
hasParam = true;
data.append("data: ");
data.append(
generateClassModelTypescript(interfaceElement.unnamedElement.get(0), dotGroup, writeImports));
} else if (interfaceElement.multiPartParameters.size() != 0) {
if (hasParam) {
data.append(", ");
}
hasParam = true;
boolean hasParam2 = false;
data.append("data: {");
for (final Entry<String, OptionalClassModel> pathEntry : interfaceElement.multiPartParameters
.entrySet()) {
if (hasParam2) {
data.append(", ");
}
hasParam2 = true;
data.append(pathEntry.getKey());
data.append(": ");
data.append(generateClassModelsTypescript(pathEntry.getValue().model(), dotGroup));
}
data.append("}");
}
data.append("): ");
/*
String tmp = DotClassElement.generateLocalModel(
final String ModelName,
final List<ClassModel> models,
final DotClassElementGroup dotGroup)
public static String generateClassModelsTypescript(
final List<ClassModel> models,
final DotClassElementGroup dotGroup,
final Set<ClassModel> imports) throws IOException {
*/
final String returnType = generateClassModelsTypescript(interfaceElement.returnTypes, dotGroup);
data.append(returnType);
final List<String> returnLinks = generateClassModelsLinks(interfaceElement.returnTypes, dotGroup);
for (final String link : returnLinks) {
outLinks.append("\t");
outLinks.append(element.name);
outLinks.append(":");
outLinks.append(interfaceElement.name);
outLinks.append(":e -> ");
outLinks.append(link);
outLinks.append(":NAME:w\n");
}
data.append("</b>");
//data.append("<br align=\"left\"/>&nbsp;&nbsp;&nbsp;&nbsp;");
data.append("</td></tr>\n\t\t\t\t\t\t\t<tr><td align=\"left\"> ");
/*
data.append("\n\t\t\trestModel: {");
data.append("\n\t\t\t\tendPoint: \"");
*/
data.append(interfaceElement.restEndPoint);
/*
data.append("\",");
data.append("\n\t\t\t\trequestType: HTTPRequestModel.");
toolImports.add("HTTPRequestModel");
data.append(interfaceElement.restTypeRequest);
data.append(",");
if (consumes != null) {
for (final String elem : consumes) {
if (MediaType.APPLICATION_JSON.equals(elem)) {
data.append("\n\t\t\t\tcontentType: HTTPMimeType.JSON,");
toolImports.add("HTTPMimeType");
break;
} else if (MediaType.MULTIPART_FORM_DATA.equals(elem)) {
data.append("\n\t\t\t\tcontentType: HTTPMimeType.MULTIPART,");
toolImports.add("HTTPMimeType");
break;
} else if (MediaType.TEXT_PLAIN.equals(elem)) {
data.append("\n\t\t\t\tcontentType: HTTPMimeType.TEXT_PLAIN,");
toolImports.add("HTTPMimeType");
break;
}
}
} else if (RestTypeRequest.DELETE.equals(interfaceElement.restTypeRequest)) {
data.append("\n\t\t\t\tcontentType: HTTPMimeType.TEXT_PLAIN,");
toolImports.add("HTTPMimeType");
}
if (produces != null) {
if (produces.size() > 1) {
data.append("\n\t\t\t\taccept: produce,");
} else {
final String returnType = generateClassModelsTypescript(interfaceElement.returnTypes, dotGroup,
imports, false);
if (!"void".equals(returnType)) {
for (final String elem : produces) {
if (MediaType.APPLICATION_JSON.equals(elem)) {
data.append("\n\t\t\t\taccept: HTTPMimeType.JSON,");
toolImports.add("HTTPMimeType");
break;
}
}
}
}
}
data.append("\n\t\t\t},");
data.append("\n\t\t\trestConfig,");
if (!interfaceElement.parameters.isEmpty()) {
data.append("\n\t\t\tparams,");
}
if (!interfaceElement.queries.isEmpty()) {
data.append("\n\t\t\tqueries,");
}
if (interfaceElement.unnamedElement.size() == 1) {
data.append("\n\t\t\tdata,");
} else if (interfaceElement.multiPartParameters.size() != 0) {
data.append("\n\t\t\tdata,");
}
if (needGenerateProgress) {
data.append("\n\t\t\tcallback,");
}
data.append("\n\t\t}");
if (returnComplexModel != null) {
data.append(", is");
data.append(returnModelNameIfComplex);
} else {
final DotClassElement retType = dotGroup.find(interfaceElement.returnTypes.get(0));
if (retType.dotCheckType != null) {
data.append(", ");
data.append(retType.dotCheckType);
imports.add(interfaceElement.returnTypes.get(0));
}
}
*/
data.append("</td></tr>\n");
}
/*
data.append("\n}\n");
final StringBuilder out = new StringBuilder();
final List<String> toolImportsList = new ArrayList<>(toolImports);
Collections.sort(toolImportsList);
if (toolImportsList.size() != 0) {
out.append("import {");
for (final String elem : toolImportsList) {
out.append("\n\t");
out.append(elem);
out.append(",");
}
out.append("\n} from \"../rest-tools\";\n\n");
}
if (zodImports.size() != 0) {
out.append("import { z as zod } from \"zod\"\n");
}
final Set<String> finalImportSet = new TreeSet<>();
for (final ClassModel model : imports) {
final DotClassElement dotModel = dotGroup.find(model);
if (dotModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
finalImportSet.add(dotModel.dotTypeName);
}
for (final ClassModel model : isImports) {
final DotClassElement dotModel = dotGroup.find(model);
if (dotModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
if (dotModel.dotCheckType != null) {
finalImportSet.add(dotModel.dotCheckType);
}
}
for (final ClassModel model : zodImports) {
final DotClassElement dotModel = dotGroup.find(model);
if (dotModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
finalImportSet.add("Zod" + dotModel.dotTypeName);
}
for (final ClassModel model : writeImports) {
final DotClassElement dotModel = dotGroup.find(model);
if (dotModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
finalImportSet.add(dotModel.dotTypeName + "Write");
}
if (finalImportSet.size() != 0) {
out.append("import {");
for (final String elem : finalImportSet) {
out.append("\n\t");
out.append(elem);
out.append(",");
}
out.append("\n} from \"../model\";\n\n");
}
out.append(data.toString());
*/
data.append("""
</table>
</td>
</tr>
</table>>
]
""");
data.append(outLinks.toString());
return data.toString();
}
}

View File

@@ -0,0 +1,485 @@
package org.kar.archidata.externalRestApi.dot;
import java.io.IOException;
import java.util.List;
import java.util.Map.Entry;
import org.kar.archidata.externalRestApi.model.ClassEnumModel;
import org.kar.archidata.externalRestApi.model.ClassListModel;
import org.kar.archidata.externalRestApi.model.ClassMapModel;
import org.kar.archidata.externalRestApi.model.ClassModel;
import org.kar.archidata.externalRestApi.model.ClassObjectModel;
import org.kar.archidata.externalRestApi.model.ClassObjectModel.FieldProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DotClassElement {
static final Logger LOGGER = LoggerFactory.getLogger(DotClassElement.class);
public enum DefinedPosition {
NATIVE, // Native element of dot language.
BASIC, // basic wrapping for JAVA type.
NORMAL // Normal Object to interpret.
}
public List<ClassModel> models;
public String zodName;
public String dotTypeName;
public String dotCheckType;
public String declaration;
public String fileName = null;
public String comment = null;
public DefinedPosition nativeType = DefinedPosition.NORMAL;
public static String determineFileName(final String className) {
return className.replaceAll("([a-z])([A-Z])", "$1-$2").replaceAll("([A-Z])([A-Z][a-z])", "$1-$2").toLowerCase();
}
public DotClassElement(final List<ClassModel> model, final String zodName, final String dotTypeName,
final String dotCheckType, final String declaration, final DefinedPosition nativeType) {
this.models = model;
this.zodName = zodName;
this.dotTypeName = dotTypeName;
this.declaration = declaration;
this.nativeType = nativeType;
}
public DotClassElement(final ClassModel model) {
this.models = List.of(model);
this.dotTypeName = model.getOriginClasses().getSimpleName();
this.declaration = null;
}
public boolean isCompatible(final ClassModel model) {
return this.models.contains(model);
}
public String generateEnum(final ClassEnumModel model, final DotClassElementGroup dotGroup) throws IOException {
final StringBuilder out = new StringBuilder();
out.append("""
%s [
shape=plain
label=<<table color="#33FF33" border="2" cellborder="1" cellspacing="0" cellpadding="4">
<tr>
<td port="NAME"><b>%s</b><br/>(ENUM)</td>
</tr>
<tr>
<td>
<table border="0" cellborder="0" cellspacing="0" >
""".formatted(this.dotTypeName, this.dotTypeName));
final boolean first = true;
for (final Entry<String, Object> elem : model.getListOfValues().entrySet()) {
out.append("\t\t\t\t\t\t<tr><td align=\"left\"><b> + ");
out.append(elem.getKey());
out.append("</b> = ");
if (elem.getValue() instanceof final Integer value) {
out.append(value);
} else {
out.append("'");
out.append(elem.getValue());
out.append("'");
}
out.append("</td></tr>\n");
}
out.append("""
</table>
</td>
</tr>
</table>>
]
""");
return out.toString();
}
public String generateImporDot(final List<ClassModel> depModels, final DotClassElementGroup dotGroup)
throws IOException {
final StringBuilder out = new StringBuilder();
for (final ClassModel depModel : depModels) {
final DotClassElement dotModel = dotGroup.find(depModel);
if (dotModel.nativeType != DefinedPosition.NATIVE) {
out.append("import {");
out.append(dotModel.zodName);
out.append("} from \"./");
out.append(dotModel.fileName);
out.append("\";\n");
}
}
return out.toString();
}
private Object generateComment(final ClassObjectModel model) {
final StringBuilder out = new StringBuilder();
if (model.getDescription() != null || model.getExample() != null) {
out.append("/**\n");
if (model.getDescription() != null) {
for (final String elem : model.getDescription().split("\n")) {
out.append(" * ");
out.append(elem);
out.append("\n");
}
}
if (model.getExample() != null) {
out.append(" * Example:\n");
out.append(" * ```\n");
for (final String elem : model.getExample().split("\n")) {
out.append(" * ");
out.append(elem);
out.append("\n");
}
out.append(" * ```\n");
}
out.append(" */\n");
}
return out.toString();
}
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()) {
return "";
}
if (field.columnNotNull()) {
return "";
}
return ".optional()";
}
public String maxSizeZod(final FieldProperty field) {
final StringBuilder builder = new StringBuilder();
final Class<?> clazz = field.model().getOriginClasses();
if (clazz == String.class) {
if (field.sizeMin() > 0) {
builder.append(".min(");
builder.append(field.sizeMin());
builder.append(")");
}
if (field.sizeMax() > 0) {
builder.append(".max(");
builder.append(field.sizeMax());
builder.append(")");
}
}
if (clazz == short.class || clazz == Short.class || clazz == int.class || clazz == Integer.class
|| clazz == long.class || clazz == Long.class || clazz == float.class || clazz == Float.class
|| clazz == double.class || clazz == Double.class) {
if (field.min() != null && field.min() > 0) {
builder.append(".min(");
builder.append(field.min());
builder.append(")");
}
if (field.max() != null && field.max() > 0) {
builder.append(".max(");
builder.append(field.max());
builder.append(")");
}
}
return builder.toString();
}
public String readOnlyZod(final FieldProperty field) {
if (field.readOnly()) {
return ".readonly()";
}
return "";
}
public String generateBaseObject() {
final StringBuilder out = new StringBuilder();
return out.toString();
}
public String convertHtml(final String data) {
return data.replace("<", "&lt;").replace(">", "&gt;");
}
public static String generateClassModelTypescript(final ClassModel model, final DotClassElementGroup dotGroup)
throws IOException {
if (model instanceof ClassEnumModel) {
final DotClassElement dotFieldModel = dotGroup.find(model);
return dotFieldModel.dotTypeName;
} else if (model instanceof ClassObjectModel) {
final DotClassElement dotFieldModel = dotGroup.find(model);
return dotFieldModel.dotTypeName;
} else if (model instanceof final ClassListModel fieldListModel) {
return generateDotList(fieldListModel, dotGroup);
} else if (model instanceof final ClassMapModel fieldMapModel) {
return generateDotMap(fieldMapModel, dotGroup);
}
throw new IOException("Impossible model:" + model);
}
public static String generateClassModelTypescriptLink(final ClassModel model, final DotClassElementGroup dotGroup)
throws IOException {
if (model instanceof ClassEnumModel) {
final DotClassElement dotFieldModel = dotGroup.find(model);
return dotFieldModel.dotTypeName;
} else if (model instanceof ClassObjectModel) {
final DotClassElement dotFieldModel = dotGroup.find(model);
if (dotFieldModel.nativeType == DefinedPosition.NORMAL) {
return dotFieldModel.dotTypeName;
}
} else if (model instanceof final ClassListModel fieldListModel) {
final String className = generateDotListClassName(fieldListModel, dotGroup);
if (className != null) {
return className;
}
} else if (model instanceof final ClassMapModel fieldMapModel) {
final String className = generateDotMapClassName(fieldMapModel, dotGroup);
if (className != null) {
return className;
}
}
return null;
}
public String generateObject(final ClassObjectModel model, final DotClassElementGroup dotGroup) throws IOException {
final StringBuilder out = new StringBuilder();
final StringBuilder outLinks = new StringBuilder();
out.append("""
%s [
shape=plain
ranksep="2"
label=<<table color="#000000" border="2" cellborder="1" cellspacing="0" cellpadding="4">
<tr>
<td port="NAME"><b>%s</b></td>
</tr>
<tr>
<td>
<table border="0" cellborder="0" cellspacing="0" >
""".formatted(this.dotTypeName, this.dotTypeName));
String inheritence = null;
if (model.getExtendsClass() != null) {
final ClassModel parentClass = model.getExtendsClass();
final DotClassElement dotParentModel = dotGroup.find(parentClass);
inheritence = dotParentModel.dotTypeName;
}
if (model.getFields().size() == 0) {
out.append("\t\t\t\t\t\t<tr><td> <i>(empty)</i> </td></tr>");
}
for (final FieldProperty field : model.getFields()) {
final ClassModel fieldModel = field.model();
if (field.comment() != null) {
out.append("\t\t\t\t\t\t<tr><td align=\"left\"><i> // ");
out.append(convertHtml(field.comment()));
out.append("</i></td></tr>\n");
}
out.append("\t\t\t\t\t\t<tr><td align=\"left\" port=\"");
out.append(field.name());
out.append("\"><b> + ");
out.append(field.name());
out.append("</b>: ");
out.append(generateClassModelTypescript(fieldModel, dotGroup));
final String remoteType = generateClassModelTypescriptLink(fieldModel, dotGroup);
if (remoteType != null) {
outLinks.append("\t");
outLinks.append(this.dotTypeName);
outLinks.append(":");
outLinks.append(field.name());
outLinks.append(":e -> ");
outLinks.append(remoteType);
outLinks.append(":NAME:w\n");
} else if (field.linkClass() != null) {
final String remoteLinkType = generateClassModelTypescriptLink(field.linkClass(), dotGroup);
if (remoteLinkType != null) {
outLinks.append("\t");
outLinks.append(this.dotTypeName);
outLinks.append(":");
outLinks.append(field.name());
outLinks.append(":e -> ");
outLinks.append(remoteLinkType);
outLinks.append(":NAME:w\n");
}
}
out.append("</td></tr>\n");
}
out.append("""
</table>
</td>
</tr>
</table>>
]
""");
if (inheritence != null) {
out.append("\tedge [dir=back arrowtail=empty arrowsize=2]\n");
out.append("\t");
out.append(inheritence);
// heritage stop link on the "s" South
out.append(":s -> ");
out.append(this.dotTypeName);
// heritage start link on the "n" North
out.append(":n\n");
}
if (!outLinks.isEmpty()) {
out.append("\tedge [dir=back arrowtail=diamond arrowsize=2]\n");
//out.append("\tedge [arrowhead=diamond arrowsize=2]\n");
out.append(outLinks.toString());
}
return out.toString();
}
private static String generateDotMap(final ClassMapModel model, final DotClassElementGroup dotGroup) {
final StringBuilder out = new StringBuilder();
out.append("Map&lt;");
if (model.keyModel instanceof final ClassListModel fieldListModel) {
final String tmp = generateDotList(fieldListModel, dotGroup);
out.append(tmp);
} else if (model.keyModel instanceof final ClassMapModel fieldMapModel) {
final String tmp = generateDotMap(fieldMapModel, dotGroup);
out.append(tmp);
} else if (model.keyModel instanceof final ClassObjectModel fieldObjectModel) {
final String tmp = generateDotObject(fieldObjectModel, dotGroup);
out.append(tmp);
} else if (model.keyModel instanceof final ClassEnumModel fieldEnumModel) {
final String tmp = generateDotEnum(fieldEnumModel, dotGroup);
out.append(tmp);
}
out.append(", ");
if (model.valueModel instanceof final ClassListModel fieldListModel) {
final String tmp = generateDotList(fieldListModel, dotGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassMapModel fieldMapModel) {
final String tmp = generateDotMap(fieldMapModel, dotGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassObjectModel fieldObjectModel) {
final String tmp = generateDotObject(fieldObjectModel, dotGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassEnumModel fieldEnumModel) {
final String tmp = generateDotEnum(fieldEnumModel, dotGroup);
out.append(tmp);
}
out.append("&gt;");
return out.toString();
}
private static String generateDotEnum(final ClassEnumModel model, final DotClassElementGroup dotGroup) {
final DotClassElement dotParentModel = dotGroup.find(model);
return dotParentModel.dotTypeName;
}
private static String generateDotObject(final ClassObjectModel model, final DotClassElementGroup dotGroup) {
final DotClassElement dotParentModel = dotGroup.find(model);
return dotParentModel.dotTypeName;
}
private static String generateDotObjectClassName(
final ClassObjectModel model,
final DotClassElementGroup dotGroup) {
final DotClassElement dotParentModel = dotGroup.find(model);
if (dotParentModel.nativeType == DefinedPosition.NORMAL) {
return dotParentModel.dotTypeName;
}
return null;
}
private static String generateDotListClassName(final ClassListModel model, final DotClassElementGroup dotGroup) {
if (model.valueModel instanceof final ClassListModel fieldListModel) {
return generateDotListClassName(fieldListModel, dotGroup);
} else if (model.valueModel instanceof final ClassMapModel fieldMapModel) {
return generateDotMapClassName(fieldMapModel, dotGroup);
} else if (model.valueModel instanceof final ClassObjectModel fieldObjectModel) {
return generateDotObjectClassName(fieldObjectModel, dotGroup);
}
return null;
}
private static String generateDotMapClassName(final ClassMapModel model, final DotClassElementGroup dotGroup) {
if (model.valueModel instanceof final ClassListModel fieldListModel) {
return generateDotListClassName(fieldListModel, dotGroup);
} else if (model.valueModel instanceof final ClassMapModel fieldMapModel) {
return generateDotMapClassName(fieldMapModel, dotGroup);
} else if (model.valueModel instanceof final ClassObjectModel fieldObjectModel) {
return generateDotObjectClassName(fieldObjectModel, dotGroup);
} else if (model.valueModel instanceof final ClassEnumModel fieldEnumModel) {
return generateDotEnum(fieldEnumModel, dotGroup);
}
return null;
}
private static String generateDotList(final ClassListModel model, final DotClassElementGroup dotGroup) {
final StringBuilder out = new StringBuilder();
out.append("List&lt;");
if (model.valueModel instanceof final ClassListModel fieldListModel) {
final String tmp = generateDotList(fieldListModel, dotGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassMapModel fieldMapModel) {
final String tmp = generateDotMap(fieldMapModel, dotGroup);
out.append(tmp);
} else if (model.valueModel instanceof final ClassObjectModel fieldObjectModel) {
final String tmp = generateDotObject(fieldObjectModel, dotGroup);
out.append(tmp);
}
out.append("&gt;");
return out.toString();
}
public String generateFile(final DotClassElementGroup dotGroup) throws IOException {
if (this.nativeType == DefinedPosition.NATIVE) {
return "";
}
final ClassModel model = this.models.get(0);
String data = "";
if (this.nativeType == DefinedPosition.BASIC && model instanceof ClassObjectModel) {
// nothing to do___ data = generateBaseObject();
} else if (model instanceof final ClassEnumModel modelEnum) {
data = generateEnum(modelEnum, dotGroup);
} else if (model instanceof final ClassObjectModel modelObject) {
data = generateObject(modelObject, dotGroup);
}
return data;
}
private static String generateLocalModelBase(final ClassModel model, final DotClassElementGroup dotGroup)
throws IOException {
if (model instanceof final ClassObjectModel objectModel) {
return generateDotObject(objectModel, dotGroup);
}
if (model instanceof final ClassEnumModel enumModel) {
return generateDotEnum(enumModel, dotGroup);
}
if (model instanceof final ClassListModel listModel) {
return generateDotList(listModel, dotGroup);
}
if (model instanceof final ClassMapModel mapModel) {
return generateDotMap(mapModel, dotGroup);
}
return "";
}
public static String generateLocalModel(
final String ModelName,
final List<ClassModel> models,
final DotClassElementGroup dotGroup) throws IOException {
if (models.size() == 1) {
if (models.get(0) instanceof ClassObjectModel) {
return null;
}
if (models.get(0) instanceof ClassEnumModel) {
return null;
}
}
final StringBuilder out = new StringBuilder();
if (models.size() == 1) {
out.append(generateLocalModelBase(models.get(0), dotGroup));
} else {
out.append("Union&lt;");
for (final ClassModel model : models) {
out.append("\t");
out.append(generateLocalModelBase(models.get(0), dotGroup));
out.append(",\n");
}
out.append("&gt;");
}
return out.toString();
}
}

View File

@@ -0,0 +1,27 @@
package org.kar.archidata.externalRestApi.dot;
import java.util.List;
import org.kar.archidata.externalRestApi.model.ClassModel;
public class DotClassElementGroup {
private final List<DotClassElement> dotElements;
public List<DotClassElement> getDotElements() {
return this.dotElements;
}
public DotClassElementGroup(final List<DotClassElement> tsElements) {
this.dotElements = tsElements;
}
public DotClassElement find(final ClassModel model) {
for (final DotClassElement elem : this.dotElements) {
if (elem.isCompatible(model)) {
return elem;
}
}
return null;
}
}

View File

@@ -15,6 +15,10 @@ import org.slf4j.LoggerFactory;
public class ApiModel { public class ApiModel {
static final Logger LOGGER = LoggerFactory.getLogger(ApiModel.class); static final Logger LOGGER = LoggerFactory.getLogger(ApiModel.class);
public record OptionalClassModel(
List<ClassModel> model,
boolean optional) {}
Class<?> originClass; Class<?> originClass;
Method orignMethod; Method orignMethod;
@@ -36,7 +40,7 @@ public class ApiModel {
// list of all query (?key...) // list of all query (?key...)
public final Map<String, List<ClassModel>> queries = new HashMap<>(); public final Map<String, List<ClassModel>> queries = new HashMap<>();
// when request multi-part, need to separate it. // when request multi-part, need to separate it.
public final Map<String, List<ClassModel>> multiPartParameters = new HashMap<>(); public final Map<String, OptionalClassModel> multiPartParameters = new HashMap<>();
// model of data available // model of data available
public final List<ClassModel> unnamedElement = new ArrayList<>(); public final List<ClassModel> unnamedElement = new ArrayList<>();
@@ -153,6 +157,7 @@ public class ApiModel {
final String pathParam = ApiTool.apiAnnotationGetPathParam(parameter); final String pathParam = ApiTool.apiAnnotationGetPathParam(parameter);
final String queryParam = ApiTool.apiAnnotationGetQueryParam(parameter); final String queryParam = ApiTool.apiAnnotationGetQueryParam(parameter);
final String formDataParam = ApiTool.apiAnnotationGetFormDataParam(parameter); final String formDataParam = ApiTool.apiAnnotationGetFormDataParam(parameter);
final boolean formDataParamOptional = ApiTool.apiAnnotationGetFormDataOptional(parameter);
if (queryParam != null) { if (queryParam != null) {
if (!this.queries.containsKey(queryParam)) { if (!this.queries.containsKey(queryParam)) {
this.queries.put(queryParam, parameterModel); this.queries.put(queryParam, parameterModel);
@@ -163,7 +168,8 @@ public class ApiModel {
} }
} else if (formDataParam != null) { } else if (formDataParam != null) {
if (!this.multiPartParameters.containsKey(formDataParam)) { if (!this.multiPartParameters.containsKey(formDataParam)) {
this.multiPartParameters.put(formDataParam, parameterModel); this.multiPartParameters.put(formDataParam,
new OptionalClassModel(parameterModel, formDataParamOptional));
} }
} else { } else {
this.unnamedElement.addAll(parameterModel); this.unnamedElement.addAll(parameterModel);

View File

@@ -7,7 +7,10 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import org.glassfish.jersey.media.multipart.FormDataParam; import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.ARCHIVE;
import org.kar.archidata.annotation.AsyncType; import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.FormDataOptional;
import org.kar.archidata.annotation.RESTORE;
import org.kar.archidata.annotation.TypeScriptProgress; import org.kar.archidata.annotation.TypeScriptProgress;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@@ -106,6 +109,12 @@ public class ApiTool {
if (element.getDeclaredAnnotationsByType(DELETE.class).length == 1) { if (element.getDeclaredAnnotationsByType(DELETE.class).length == 1) {
return "DELETE"; return "DELETE";
} }
if (element.getDeclaredAnnotationsByType(RESTORE.class).length == 1) {
return "RESTORE";
}
if (element.getDeclaredAnnotationsByType(ARCHIVE.class).length == 1) {
return "ARCHIVE";
}
return null; return null;
} }
@@ -125,6 +134,12 @@ public class ApiTool {
if (element.getDeclaredAnnotationsByType(DELETE.class).length == 1) { if (element.getDeclaredAnnotationsByType(DELETE.class).length == 1) {
return RestTypeRequest.DELETE; return RestTypeRequest.DELETE;
} }
if (element.getDeclaredAnnotationsByType(RESTORE.class).length == 1) {
return RestTypeRequest.RESTORE;
}
if (element.getDeclaredAnnotationsByType(ARCHIVE.class).length == 1) {
return RestTypeRequest.ARCHIVE;
}
return null; return null;
} }
@@ -144,6 +159,14 @@ public class ApiTool {
return ((QueryParam) annotation[0]).value(); return ((QueryParam) annotation[0]).value();
} }
public static boolean apiAnnotationGetFormDataOptional(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(FormDataOptional.class);
if (annotation.length == 0) {
return false;
}
return true;
}
public static String apiAnnotationGetFormDataParam(final Parameter element) throws Exception { public static String apiAnnotationGetFormDataParam(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(FormDataParam.class); final Annotation[] annotation = element.getDeclaredAnnotationsByType(FormDataParam.class);
if (annotation.length == 0) { if (annotation.length == 0) {

View File

@@ -15,6 +15,9 @@ import org.kar.archidata.exception.DataAccessException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
public class ClassObjectModel extends ClassModel { public class ClassObjectModel extends ClassModel {
@@ -56,6 +59,7 @@ public class ClassObjectModel extends ClassModel {
public record FieldProperty( public record FieldProperty(
String name, String name,
ClassModel model, ClassModel model,
ClassModel linkClass, // link class when use remote ID (ex: list<UUID>)
String comment, String comment,
int sizeMin, // String SizeMin int sizeMin, // String SizeMin
int sizeMax, // String SizeMax int sizeMax, // String SizeMax
@@ -66,11 +70,12 @@ public class ClassObjectModel extends ClassModel {
Boolean columnNotNull, Boolean columnNotNull,
Boolean nullable) { Boolean nullable) {
public FieldProperty(final String name, final ClassModel model, final String comment, final int sizeMin, public FieldProperty(final String name, final ClassModel model, final ClassModel linkClass,
final int sizeMax, final Long min, final Long max, final Boolean readOnly, final Boolean notNull, final String comment, final int sizeMin, final int sizeMax, final Long min, final Long max,
final Boolean columnNotNull, 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.linkClass = linkClass;
this.comment = comment; this.comment = comment;
this.sizeMin = sizeMin; this.sizeMin = sizeMin;
this.sizeMax = sizeMax; this.sizeMax = sizeMax;
@@ -85,7 +90,6 @@ public class ClassObjectModel extends ClassModel {
private static int getStringMinSize(final Field field) throws DataAccessException { private static int getStringMinSize(final Field field) throws DataAccessException {
final Size size = AnnotationTools.getConstraintsSize(field); final Size size = AnnotationTools.getConstraintsSize(field);
final int colomnLimitSize = AnnotationTools.getLimitSize(field);
return size != null ? size.min() : 0; return size != null ? size.min() : 0;
} }
@@ -95,9 +99,43 @@ public class ClassObjectModel extends ClassModel {
return size == null ? colomnLimitSize : colomnLimitSize < size.max() ? colomnLimitSize : size.max(); return size == null ? colomnLimitSize : colomnLimitSize < size.max() ? colomnLimitSize : size.max();
} }
private static Class<?> getSubModelIfExist2(final Field field) {
final ManyToOne manyToOne = AnnotationTools.getManyToOne(field);
if (manyToOne != null) {
if (manyToOne.targetEntity() != null && manyToOne.targetEntity() != void.class) {
return manyToOne.targetEntity();
}
return null;
}
final ManyToMany manyToMany = AnnotationTools.getManyToMany(field);
if (manyToMany != null) {
if (manyToMany.targetEntity() != null && manyToMany.targetEntity() != void.class) {
return manyToMany.targetEntity();
}
return null;
}
final OneToMany oneToMany = AnnotationTools.getOneToMany(field);
if (oneToMany != null) {
if (oneToMany.targetEntity() != null && oneToMany.targetEntity() != void.class) {
return oneToMany.targetEntity();
}
return null;
}
return null;
}
private static ClassModel getSubModelIfExist(final Field field, final ModelGroup previous) throws IOException {
final Class<?> tmp = getSubModelIfExist2(field);
if (tmp == null) {
return null;
}
return ClassModel.getModel(tmp, previous);
}
public FieldProperty(final Field field, final ModelGroup previous) throws DataAccessException, IOException { public FieldProperty(final Field field, final ModelGroup previous) throws DataAccessException, IOException {
this(field.getName(), // this(field.getName(), //
ClassModel.getModel(field.getGenericType(), previous), // ClassModel.getModel(field.getGenericType(), previous), //
getSubModelIfExist(field, previous), //
AnnotationTools.getComment(field), // AnnotationTools.getComment(field), //
getStringMinSize(field), // getStringMinSize(field), //
getStringMaxSize(field), // getStringMaxSize(field), //

View File

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

View File

@@ -3,6 +3,9 @@ package org.kar.archidata.externalRestApi.typescript;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@@ -14,6 +17,7 @@ import java.util.TreeSet;
import org.kar.archidata.dataAccess.DataExport; import org.kar.archidata.dataAccess.DataExport;
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.ApiModel.OptionalClassModel;
import org.kar.archidata.externalRestApi.model.ClassEnumModel; import org.kar.archidata.externalRestApi.model.ClassEnumModel;
import org.kar.archidata.externalRestApi.model.ClassListModel; import org.kar.archidata.externalRestApi.model.ClassListModel;
import org.kar.archidata.externalRestApi.model.ClassMapModel; import org.kar.archidata.externalRestApi.model.ClassMapModel;
@@ -192,7 +196,7 @@ public class TsApiGeneration {
data.append("\n\t\t\tdata,"); data.append("\n\t\t\tdata,");
} }
if (needGenerateProgress) { if (needGenerateProgress) {
data.append("\n\t\t\tcallback,"); data.append("\n\t\t\tcallbacks,");
} }
data.append("\n\t\t}: {"); data.append("\n\t\t}: {");
data.append("\n\t\trestConfig: RESTConfig,"); data.append("\n\t\trestConfig: RESTConfig,");
@@ -226,12 +230,16 @@ public class TsApiGeneration {
data.append(","); data.append(",");
} else if (interfaceElement.multiPartParameters.size() != 0) { } else if (interfaceElement.multiPartParameters.size() != 0) {
data.append("\n\t\tdata: {"); data.append("\n\t\tdata: {");
for (final Entry<String, List<ClassModel>> pathEntry : interfaceElement.multiPartParameters for (final Entry<String, OptionalClassModel> pathEntry : interfaceElement.multiPartParameters
.entrySet()) { .entrySet()) {
data.append("\n\t\t\t"); data.append("\n\t\t\t");
data.append(pathEntry.getKey()); data.append(pathEntry.getKey());
if (pathEntry.getValue().optional()) {
data.append("?");
}
data.append(": "); data.append(": ");
data.append(generateClassModelsTypescript(pathEntry.getValue(), tsGroup, imports, writeImports)); data.append(generateClassModelsTypescript(pathEntry.getValue().model(), tsGroup, imports,
writeImports));
data.append(","); data.append(",");
} }
data.append("\n\t\t},"); data.append("\n\t\t},");
@@ -268,7 +276,7 @@ public class TsApiGeneration {
data.append(","); data.append(",");
} }
if (needGenerateProgress) { if (needGenerateProgress) {
data.append("\n\t\tcallback?: RESTCallbacks,"); data.append("\n\t\tcallbacks?: RESTCallbacks,");
toolImports.add("RESTCallbacks"); toolImports.add("RESTCallbacks");
} }
data.append("\n\t}): Promise<"); data.append("\n\t}): Promise<");
@@ -350,7 +358,7 @@ public class TsApiGeneration {
data.append("\n\t\t\tdata,"); data.append("\n\t\t\tdata,");
} }
if (needGenerateProgress) { if (needGenerateProgress) {
data.append("\n\t\t\tcallback,"); data.append("\n\t\t\tcallbacks,");
} }
data.append("\n\t\t}"); data.append("\n\t\t}");
if (returnComplexModel != null) { if (returnComplexModel != null) {
@@ -415,7 +423,10 @@ public class TsApiGeneration {
} }
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.NORMAL) {
continue;
}
if (tsModel.models.get(0).isNoWriteSpecificMode()) {
continue; continue;
} }
finalImportSet.add(tsModel.tsTypeName + "Write"); finalImportSet.add(tsModel.tsTypeName + "Write");
@@ -433,6 +444,10 @@ public class TsApiGeneration {
out.append(data.toString()); out.append(data.toString());
final Path path = Paths.get(pathPackage + File.separator + "api");
if (Files.notExists(path)) {
Files.createDirectories(path);
}
final String fileName = TsClassElement.determineFileName(element.name); final String fileName = TsClassElement.determineFileName(element.name);
final FileWriter myWriter = new FileWriter( final FileWriter myWriter = new FileWriter(
pathPackage + File.separator + "api" + File.separator + fileName + ".ts"); pathPackage + File.separator + "api" + File.separator + fileName + ".ts");

View File

@@ -12,6 +12,7 @@ import java.util.Map.Entry;
import org.kar.archidata.annotation.security.PermitTokenInURI; import org.kar.archidata.annotation.security.PermitTokenInURI;
import org.kar.archidata.catcher.RestErrorResponse; import org.kar.archidata.catcher.RestErrorResponse;
import org.kar.archidata.exception.SystemException;
import org.kar.archidata.model.UserByToken; import org.kar.archidata.model.UserByToken;
import org.kar.archidata.tools.JWTWrapper; import org.kar.archidata.tools.JWTWrapper;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -23,6 +24,7 @@ import jakarta.annotation.Priority;
import jakarta.annotation.security.DenyAll; import jakarta.annotation.security.DenyAll;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Priorities; import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.container.ContainerRequestFilter;
@@ -42,18 +44,40 @@ public class AuthenticationFilter implements ContainerRequestFilter {
@Context @Context
private ResourceInfo resourceInfo; private ResourceInfo resourceInfo;
protected final String applicationName; protected final String applicationName;
protected final String issuer;
public static final String AUTHENTICATION_SCHEME = "Bearer"; public static final String AUTHENTICATION_SCHEME = "Bearer";
public static final String APIKEY = "ApiKey"; public static final String APIKEY = "ApiKey";
public AuthenticationFilter(final String applicationName) { public AuthenticationFilter(final String applicationName) {
this.applicationName = applicationName; this.applicationName = applicationName;
this.issuer = "KarAuth";
}
public AuthenticationFilter(final String applicationName, final String issuer) {
this.applicationName = applicationName;
this.issuer = issuer;
}
public String getRequestedPath(final ContainerRequestContext requestContext) {
final Class<?> resourceClass = this.resourceInfo.getResourceClass();
final Method resourceMethod = this.resourceInfo.getResourceMethod();
final String classPath = resourceClass.isAnnotationPresent(Path.class)
? resourceClass.getAnnotation(Path.class).value()
: "";
final String methodPath = resourceMethod.isAnnotationPresent(Path.class)
? resourceMethod.getAnnotation(Path.class).value()
: "";
final String fullPath = (classPath.startsWith("/") ? "" : "/") + classPath
+ (methodPath.startsWith("/") ? "" : "/") + methodPath;
return fullPath;
} }
@Override @Override
public void filter(final ContainerRequestContext requestContext) throws IOException { public void filter(final ContainerRequestContext requestContext) throws IOException {
/* logger.debug("-----------------------------------------------------"); logger.debug("---- Check if have authorization ----"); /* logger.debug("-----------------------------------------------------"); logger.debug("---- Check if have authorization ----");
* logger.debug("-----------------------------------------------------"); logger.debug(" for:{}", requestContext.getUriInfo().getPath()); */ * logger.debug("-----------------------------------------------------"); logger.debug(" for:{}", requestContext.getUriInfo().getPath()); */
final Method method = this.resourceInfo.getResourceMethod(); final Method method = this.resourceInfo.getResourceMethod();
// Access denied for all // Access denied for all
if (method.isAnnotationPresent(DenyAll.class)) { if (method.isAnnotationPresent(DenyAll.class)) {
@@ -140,12 +164,13 @@ public class AuthenticationFilter implements ContainerRequestFilter {
final List<String> roles = Arrays.asList(rolesAnnotation.value()); final List<String> roles = Arrays.asList(rolesAnnotation.value());
// check if the user have the right: // check if the user have the right:
boolean haveRight = false; boolean haveRight = false;
for (final String role : roles) { try {
if (userContext.isUserInRole(role)) { haveRight = checkRight(requestContext, userContext, roles);
haveRight = true; } catch (final SystemException e) {
break; // TODO Auto-generated catch block
} e.printStackTrace();
} }
// Is user valid? // Is user valid?
if (!haveRight) { if (!haveRight) {
LOGGER.error("REJECTED not enought right : {} require: {}", requestContext.getUriInfo().getPath(), roles); LOGGER.error("REJECTED not enought right : {} require: {}", requestContext.getUriInfo().getPath(), roles);
@@ -157,6 +182,18 @@ public class AuthenticationFilter implements ContainerRequestFilter {
// logger.debug("Get local user : {} / {}", user, userByToken); // logger.debug("Get local user : {} / {}", user, userByToken);
} }
protected boolean checkRight(
final ContainerRequestContext requestContext,
final MySecurityContext userContext,
final List<String> roles) throws SystemException {
for (final String role : roles) {
if (userContext.isUserInRole(this.applicationName + "/" + role)) {
return true;
}
}
return false;
}
private boolean isTokenBasedAuthentication(final String authorizationHeader) { private boolean isTokenBasedAuthentication(final String authorizationHeader) {
// Check if the Authorization header is valid // Check if the Authorization header is valid
// It must not be null and must be prefixed with "Bearer" plus a whitespace // It must not be null and must be prefixed with "Bearer" plus a whitespace
@@ -193,7 +230,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
// must be override to be good implementation // must be override to be good implementation
protected UserByToken validateJwtToken(final String authorization) throws Exception { protected UserByToken validateJwtToken(final String authorization) throws Exception {
// logger.debug(" validate token : " + authorization); // logger.debug(" validate token : " + authorization);
final JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth", null); final JWTClaimsSet ret = JWTWrapper.validateToken(authorization, this.issuer, null);
// check the token is valid !!! (signed and coherent issuer... // check the token is valid !!! (signed and coherent issuer...
if (ret == null) { if (ret == null) {
LOGGER.error("The token is not valid: '{}'", authorization); LOGGER.error("The token is not valid: '{}'", authorization);
@@ -208,13 +245,16 @@ public class AuthenticationFilter implements ContainerRequestFilter {
user.type = UserByToken.TYPE_USER; user.type = UserByToken.TYPE_USER;
final Object rowRight = ret.getClaim("right"); final Object rowRight = ret.getClaim("right");
if (rowRight != null) { if (rowRight != null) {
final Map<String, Map<String, Object>> rights = (Map<String, Map<String, Object>>) ret.getClaim("right"); LOGGER.info("Detect right in Authentication Filter: {}", rowRight);
user.right = (Map<String, Map<String, Object>>) ret.getClaim("right");
/*
if (rights.containsKey(this.applicationName)) { if (rights.containsKey(this.applicationName)) {
user.right = rights.get(this.applicationName); user.right = rights.get(this.applicationName);
} else { } else {
LOGGER.error("Connect with no right for this application='{}' full Right='{}'", this.applicationName, LOGGER.error("Connect with no right for this application='{}' full Right='{}'", this.applicationName,
rights); rights);
} }
*/
} }
// logger.debug("request user: '{}' right: '{}' row='{}'", userUID, user.right, rowRight); // logger.debug("request user: '{}' right: '{}' row='{}'", userUID, user.right, rowRight);
return user; return user;

View File

@@ -16,9 +16,12 @@ public class CORSFilter implements ContainerResponseFilter {
// System.err.println("filter cors ..." + request.toString()); // System.err.println("filter cors ..." + request.toString());
response.getHeaders().add("Access-Control-Allow-Origin", "*"); response.getHeaders().add("Access-Control-Allow-Origin", "*");
response.getHeaders().add("Access-Control-Allow-Range", "bytes");
response.getHeaders().add("access-control-expose-headers", "range");
response.getHeaders().add("Access-Control-Allow-Headers", response.getHeaders().add("Access-Control-Allow-Headers",
"Origin, content-type, Content-type, Accept, Authorization, mime-type, filename"); "Origin, content-type, Content-type, Accept, Authorization, mime-type, filename, Range");
response.getHeaders().add("Access-Control-Allow-Credentials", "true"); response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD"); response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, PATCH, DELETE, ARCHIVE, RESTORE, OPTIONS, HEAD");
} }
} }

View File

@@ -1,13 +1,17 @@
package org.kar.archidata.filter; package org.kar.archidata.filter;
import java.security.Principal; import java.security.Principal;
import java.util.Set;
import org.kar.archidata.model.UserByToken; import org.kar.archidata.model.UserByToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.core.SecurityContext; import jakarta.ws.rs.core.SecurityContext;
// https://simplapi.wordpress.com/2015/09/19/jersey-jax-rs-securitycontext-in-action/ // https://simplapi.wordpress.com/2015/09/19/jersey-jax-rs-securitycontext-in-action/
class MySecurityContext implements SecurityContext { public class MySecurityContext implements SecurityContext {
private static final Logger LOGGER = LoggerFactory.getLogger(MySecurityContext.class);
private final GenericContext contextPrincipale; private final GenericContext contextPrincipale;
private final String sheme; private final String sheme;
@@ -22,17 +26,128 @@ class MySecurityContext implements SecurityContext {
return this.contextPrincipale; return this.contextPrincipale;
} }
@Override public Object getRightOfRoleInGroup(final String group, final String role) {
public boolean isUserInRole(final String role) {
if (this.contextPrincipale.userByToken != null) { if (this.contextPrincipale.userByToken != null) {
final Object value = this.contextPrincipale.userByToken.right.get(role); return this.contextPrincipale.userByToken.getRight(group, role);
if (value instanceof final Boolean ret) { }
return ret; return null;
} }
public Set<String> getGroups() {
if (this.contextPrincipale.userByToken != null) {
return this.contextPrincipale.userByToken.getGroups();
}
return Set.of();
}
public boolean groupExist(final String group) {
if (this.contextPrincipale.userByToken != null) {
return this.contextPrincipale.userByToken.groupExist(group);
} }
return false; return false;
} }
// Not sure the Long type is definitive.
public Long getUserID() {
if (this.contextPrincipale.userByToken != null) {
return this.contextPrincipale.userByToken.id;
}
return null;
}
public boolean checkRightInGroup(
final String group,
final String role,
final boolean needRead,
final boolean needWrite) {
if ("USER".equals(role)) {
if (groupExist(group)) {
return true;
}
return false;
}
// get associated Roles:
final Object rightPart = getRightOfRoleInGroup(group, role);
LOGGER.info("detect : {}", rightPart);
long dataRight = 0;
if (rightPart instanceof final Long rightPartCasted) {
dataRight = rightPartCasted;
} else if (rightPart instanceof final Integer rightPartCasted) {
dataRight = rightPartCasted;
}
if (dataRight == PartRight.READ_WRITE.getValue()) {
return true;
}
if (!needRead && needWrite && dataRight == PartRight.WRITE.getValue()) {
return true;
}
if (needRead && !needWrite && dataRight == PartRight.READ.getValue()) {
return true;
}
return false;
}
@Override
public boolean isUserInRole(final String role) {
String roleEdit = role;
boolean needRead = false;
boolean needWrite = false;
// Check if the API overwrite the right needed for this API.
if (roleEdit.contains(":")) {
if (roleEdit.endsWith(":w")) {
try {
roleEdit = roleEdit.substring(0, roleEdit.length() - 2);
} catch (final IndexOutOfBoundsException ex) {
LOGGER.error("Fail to extract role of '{}'", role);
ex.printStackTrace();
return false;
}
needWrite = true;
} else if (roleEdit.endsWith(":r")) {
try {
roleEdit = roleEdit.substring(0, roleEdit.length() - 2);
} catch (final IndexOutOfBoundsException ex) {
LOGGER.error("Fail to extract role of '{}'", role);
ex.printStackTrace();
return false;
}
needRead = true;
} else if (roleEdit.endsWith(":rw")) {
try {
roleEdit = roleEdit.substring(0, roleEdit.length() - 3);
} catch (final IndexOutOfBoundsException ex) {
LOGGER.error("Fail to extract role of '{}'", role);
ex.printStackTrace();
return false;
}
needRead = true;
needWrite = true;
} else {
LOGGER.error("Request check right of an unknow right mode: {} (after ':')", roleEdit);
return false;
}
}
if (roleEdit.contains("/")) {
final String[] elements = roleEdit.split("/");
return checkRightInGroup(elements[0], elements[1], needRead, needWrite);
}
// Special case, if the token is valid, it is an USER ...
if ("USER".equals(roleEdit)) {
return true;
}
return checkRightInGroup("?system?", roleEdit, needRead, needWrite);
}
public Object getRole(final String role) {
LOGGER.info("contextPrincipale={}", this.contextPrincipale);
if (this.contextPrincipale.userByToken != null) {
LOGGER.info("contextPrincipale.userByToken={}", this.contextPrincipale.userByToken);
LOGGER.info("contextPrincipale.userByToken.right={}", this.contextPrincipale.userByToken.right);
return this.contextPrincipale.userByToken.right.get(role);
}
return null;
}
@Override @Override
public boolean isSecure() { public boolean isSecure() {
return "https".equalsIgnoreCase(this.sheme); return "https".equalsIgnoreCase(this.sheme);

View File

@@ -0,0 +1,30 @@
package org.kar.archidata.filter;
import com.fasterxml.jackson.annotation.JsonValue;
public enum PartRight {
NONE(0), //
READ(1), //
WRITE(2), //
READ_WRITE(3);
private final int value;
PartRight(final int value) {
this.value = value;
}
@JsonValue
public int getValue() {
return this.value;
}
public static PartRight fromValue(final int value) {
for (final PartRight element : values()) {
if (element.getValue() == value) {
return element;
}
}
throw new IllegalArgumentException("PartRight: Unknown value: " + value);
}
}

View File

@@ -28,26 +28,29 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable; import jakarta.annotation.Nullable;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.DefaultValue;
@Table(name = "user") @Table(name = "user")
@DataIfNotExists @DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
public class User extends GenericDataSoftDelete { public class User extends GenericDataSoftDelete {
@NotNull
@Column(length = 128) @Column(length = 128)
@Size(min = 3, max = 128)
@Pattern(regexp = "^[a-zA-Z0-9-_ \\.]+$")
public String login = null; public String login = null;
@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 Timestamp lastConnection = null; public Timestamp lastConnection = null;
@DefaultValue("'0'")
@Column(nullable = false)
public boolean admin = false;
@DefaultValue("'0'") @DefaultValue("'0'")
@Column(nullable = false) @Column(nullable = false)
public boolean blocked = false; public boolean blocked = false;
@DefaultValue("'0'") @Column(length = 512)
@Column(nullable = false) public String blockedReason;
public boolean removed = false;
@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)
@@ -56,7 +59,8 @@ public class User extends GenericDataSoftDelete {
@Override @Override
public String toString() { public String toString() {
return "User [login=" + this.login + ", last=" + this.lastConnection + ", admin=" + this.admin + "]"; return "User [login=" + this.login + ", last=" + this.lastConnection + ", blocked=" + this.blocked
+ ", blockedReason=" + this.blockedReason + "]";
} }
} }

View File

@@ -2,6 +2,7 @@ package org.kar.archidata.model;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
public class UserByToken { public class UserByToken {
public static final int TYPE_USER = -1; public static final int TYPE_USER = -1;
@@ -13,13 +14,35 @@ public class UserByToken {
public Long parentId = null; // FOr application, this is the id of the application, and of user token, this is the USERID public Long parentId = null; // FOr application, this is the id of the application, and of user token, this is the USERID
public String name = null; public String name = null;
// Right map // Right map
public Map<String, Object> right = new HashMap<>(); public Map<String, Map<String, Object>> right = new HashMap<>();
public boolean hasRight(final String key, final Object value) { public Set<String> getGroups() {
if (!this.right.containsKey(key)) { return this.right.keySet();
}
public boolean groupExist(final String group) {
if (!this.right.containsKey(group)) {
return false;
}
return this.right.containsKey(group);
}
public Object getRight(final String group, final String key) {
if (!this.right.containsKey(group)) {
return null;
}
final Map<String, Object> rightGroup = this.right.get(group);
if (!rightGroup.containsKey(key)) {
return null;
}
return rightGroup.get(key);
}
public boolean hasRight(final String group, final String key, final Object value) {
final Object data = getRight(group, key);
if (data == null) {
return false; return false;
} }
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.equals(castVal)) { if (elem.equals(castVal)) {

View File

@@ -11,9 +11,11 @@ import java.nio.file.StandardCopyOption;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.apache.tika.Tika;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.kar.archidata.api.DataResource; import org.kar.archidata.api.DataResource;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
@@ -22,10 +24,15 @@ import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.addOn.AddOnDataJson; import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.options.Condition; import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.ReadAllColumn; import org.kar.archidata.dataAccess.options.ReadAllColumn;
import org.kar.archidata.exception.FailException;
import org.kar.archidata.exception.InputException;
import org.kar.archidata.model.Data; import org.kar.archidata.model.Data;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
public class DataTools { public class DataTools {
@@ -35,6 +42,9 @@ public class DataTools {
public final static int CHUNK_SIZE_IN = 50 * 1024 * 1024; // 1MB chunks public final static int CHUNK_SIZE_IN = 50 * 1024 * 1024; // 1MB chunks
/** Upload some data */ /** Upload some data */
private static long tmpFolderId = 1; private static long tmpFolderId = 1;
public final static String[] SUPPORTED_IMAGE_MIME_TYPE = { "image/jpeg", "image/png", "image/webp" };
public final static String[] SUPPORTED_AUDIO_MIME_TYPE = { "audio/x-matroska" };
public final static String[] SUPPORTED_VIDEO_MIME_TYPE = { "video/x-matroska", "video/webm" };
public static void createFolder(final String path) throws IOException { public static void createFolder(final String path) throws IOException {
if (!Files.exists(java.nio.file.Path.of(path))) { if (!Files.exists(java.nio.file.Path.of(path))) {
@@ -89,20 +99,12 @@ public class DataTools {
return null; return null;
} }
public static Data createNewData(final long tmpUID, final String originalFileName, final String sha512) public static Data createNewData(
throws IOException, SQLException { final long tmpUID,
// determine mime type: final String originalFileName,
String mimeType = ""; final String sha512,
final String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1); final String mimeType) throws IOException, SQLException {
mimeType = switch (extension.toLowerCase()) {
case "jpg", "jpeg" -> "image/jpeg";
case "png" -> "image/png";
case "webp" -> "image/webp";
case "mka" -> "audio/x-matroska";
case "mkv" -> "video/x-matroska";
case "webm" -> "video/webm";
default -> throw new IOException("Can not find the mime type of data input: '" + extension + "'");
};
final String tmpPath = getTmpFileInData(tmpUID); final String tmpPath = getTmpFileInData(tmpUID);
final long fileSize = Files.size(Paths.get(tmpPath)); final long fileSize = Files.size(Paths.get(tmpPath));
Data out = new Data(); Data out = new Data();
@@ -128,6 +130,23 @@ public class DataTools {
return out; return out;
} }
public static Data createNewData(final long tmpUID, final String originalFileName, final String sha512)
throws IOException, SQLException {
// determine mime type:
String mimeType = "";
final String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1);
mimeType = switch (extension.toLowerCase()) {
case "jpg", "jpeg" -> "image/jpeg";
case "png" -> "image/png";
case "webp" -> "image/webp";
case "mka" -> "audio/x-matroska";
case "mkv" -> "video/x-matroska";
case "webm" -> "video/webm";
default -> throw new IOException("Can not find the mime type of data input: '" + extension + "'");
};
return createNewData(tmpUID, originalFileName, sha512, mimeType);
}
public static void undelete(final UUID id) { public static void undelete(final UUID id) {
try { try {
DataAccess.unsetDelete(Data.class, id); DataAccess.unsetDelete(Data.class, id);
@@ -141,6 +160,10 @@ public class DataTools {
return saveFile(uploadedInputStream, getTmpFileInData(idData)); return saveFile(uploadedInputStream, getTmpFileInData(idData));
} }
public static String saveTemporaryFile(final byte[] uploadedInputStream, final long idData) {
return saveFile(uploadedInputStream, getTmpFileInData(idData));
}
public static void removeTemporaryFile(final long idData) { public static void removeTemporaryFile(final long idData) {
final String filepath = getTmpFileInData(idData); final String filepath = getTmpFileInData(idData);
if (Files.exists(Paths.get(filepath))) { if (Files.exists(Paths.get(filepath))) {
@@ -186,6 +209,31 @@ public class DataTools {
return out; return out;
} }
public static String saveFile(final byte[] bytes, final String serverLocation) {
String out = "";
try {
final OutputStream outpuStream = new FileOutputStream(new File(serverLocation));
final MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(bytes, 0, bytes.length);
outpuStream.write(bytes, 0, bytes.length);
LOGGER.info("Flush input stream ... {}", serverLocation);
outpuStream.flush();
outpuStream.close();
// create the end of sha512
final byte[] sha512Digest = md.digest();
// convert in hexadecimal
out = bytesToHex(sha512Digest);
} catch (final IOException ex) {
LOGGER.error("Can not write in temporary file ... ");
ex.printStackTrace();
} catch (final NoSuchAlgorithmException ex) {
LOGGER.error("Can not find sha512 algorithms");
ex.printStackTrace();
}
return out;
}
// curl http://localhost:9993/api/users/3 // curl http://localhost:9993/api/users/3
// @Secured // @Secured
/* @GET /* @GET
@@ -211,67 +259,144 @@ public class DataTools {
if (data.contentEquals("null")) { if (data.contentEquals("null")) {
return null; return null;
} }
if (data.contentEquals("undefined")) {
return null;
}
return data; return data;
} }
public static <CLASS_TYPE, ID_TYPE> Response uploadCover( public static String getMimeType(final byte[] data) {
final Tika tika = new Tika();
final String mimeType = tika.detect(data);
return mimeType;
}
public static <CLASS_TYPE, ID_TYPE> void uploadCoverFromUri(
final Class<CLASS_TYPE> clazz, final Class<CLASS_TYPE> clazz,
final ID_TYPE id, final ID_TYPE id,
String fileName, final String url) throws Exception {
final InputStream fileInputStream,
final FormDataContentDisposition fileMetaData) {
try {
// correct input string stream :
fileName = multipartCorrection(fileName);
// public NodeSmall uploadFile(final FormDataMultiPart form) { LOGGER.info(" - id: {}", id);
LOGGER.info("Upload media file: {}", fileMetaData); LOGGER.info(" - url: {} ", url);
LOGGER.info(" - id: {}", id); final CLASS_TYPE media = DataAccess.get(clazz, id);
LOGGER.info(" - file_name: ", fileName); if (media == null) {
LOGGER.info(" - fileInputStream: {}", fileInputStream); throw new InputException(clazz.getCanonicalName(),
LOGGER.info(" - fileMetaData: {}", fileMetaData); "[" + id.toString() + "] Id does not exist or removed...");
final CLASS_TYPE media = DataAccess.get(clazz, id); }
if (media == null) { // Download data:
return Response.notModified("Media Id does not exist or removed...").build();
} final Client client = ClientBuilder.newClient();
byte[] dataResponse = null;
final long tmpUID = getTmpDataId(); try {
final String sha512 = saveTemporaryFile(fileInputStream, tmpUID); final WebTarget target = client.target(url);
Data data = getWithSha512(sha512); final Response response = target.request().get();
if (data == null) { if (response.getStatus() != 200) {
LOGGER.info("Need to add the data in the BDD ... "); throw new FailException(Response.Status.BAD_GATEWAY,
try { clazz.getCanonicalName() + "[" + id.toString() + "] Can not download the media");
data = createNewData(tmpUID, fileName, sha512); }
} catch (final IOException ex) { dataResponse = response.readEntity(byte[].class);
removeTemporaryFile(tmpUID); } catch (final Exception ex) {
ex.printStackTrace(); throw new FailException(Response.Status.INTERNAL_SERVER_ERROR,
return Response.notModified("can not create input media").build(); clazz.getCanonicalName() + "[" + id.toString() + "] can not create input media", ex);
} catch (final SQLException ex) { }
ex.printStackTrace(); if (dataResponse == null) {
removeTemporaryFile(tmpUID); throw new FailException(Response.Status.NOT_ACCEPTABLE,
return Response.notModified("Error in SQL insertion ...").build(); clazz.getCanonicalName() + "[" + id.toString() + "] Data does not exist");
} }
} else if (data.deleted) { if (dataResponse.length == 0 || dataResponse.length == 50 * 1024 * 1024) {
LOGGER.error("Data already exist but deleted"); throw new FailException(Response.Status.NOT_ACCEPTABLE, clazz.getCanonicalName() + "[" + id.toString()
undelete(data.uuid); + "] Data size is not correct " + dataResponse.length);
data.deleted = false; }
} else {
LOGGER.error("Data already exist ... all good"); final long tmpUID = getTmpDataId();
} final String sha512 = saveTemporaryFile(dataResponse, tmpUID);
// Fist step: retrieve all the Id of each parents:... Data data = getWithSha512(sha512);
LOGGER.info("Find typeNode"); final String mimeType = getMimeType(dataResponse);
if (id instanceof final Long idLong) { if (!Arrays.asList(SUPPORTED_IMAGE_MIME_TYPE).contains(mimeType)) {
AddOnDataJson.addLink(clazz, idLong, "covers", data.uuid); throw new FailException(Response.Status.NOT_ACCEPTABLE,
} else if (id instanceof final UUID idUUID) { clazz.getCanonicalName() + "[" + id.toString() + "] Data CoverType is not accesptable: " + mimeType
AddOnDataJson.addLink(clazz, idUUID, "covers", data.uuid); + "support only: " + String.join(", ", SUPPORTED_IMAGE_MIME_TYPE));
} else {
throw new IOException("Fail to add Cover can not detect type..."); }
} if (data == null) {
return Response.ok(DataAccess.get(clazz, id)).build(); LOGGER.info("Need to add the data in the BDD ... ");
} catch (final Exception ex) { try {
System.out.println("Cat ann unexpected error ... "); data = createNewData(tmpUID, url, sha512, mimeType);
ex.printStackTrace(); } catch (final IOException ex) {
removeTemporaryFile(tmpUID);
throw new FailException(Response.Status.NOT_MODIFIED,
clazz.getCanonicalName() + "[" + id.toString() + "] can not create input media", ex);
} catch (final SQLException ex) {
removeTemporaryFile(tmpUID);
throw new FailException(Response.Status.NOT_MODIFIED,
clazz.getCanonicalName() + "[" + id.toString() + "] Error in SQL insertion", ex);
}
} else if (data.deleted) {
LOGGER.error("Data already exist but deleted");
undelete(data.uuid);
data.deleted = false;
} else {
LOGGER.error("Data already exist ... all good");
}
// Fist step: retrieve all the Id of each parents:...
LOGGER.info("Find typeNode");
if (id instanceof final Long idLong) {
AddOnDataJson.addLink(clazz, idLong, "covers", data.uuid);
} else if (id instanceof final UUID idUUID) {
AddOnDataJson.addLink(clazz, idUUID, "covers", data.uuid);
} else {
throw new IOException("Fail to add Cover can not detect type...");
}
}
public static <CLASS_TYPE, ID_TYPE> void uploadCover(
final Class<CLASS_TYPE> clazz,
final ID_TYPE id,
final InputStream fileInputStream,
final FormDataContentDisposition fileMetaData) throws Exception {
// public NodeSmall uploadFile(final FormDataMultiPart form) {
LOGGER.info("Upload media file: {}", fileMetaData);
LOGGER.info(" - id: {}", id);
LOGGER.info(" - file_name: {} ", fileMetaData.getFileName());
LOGGER.info(" - fileInputStream: {}", fileInputStream);
LOGGER.info(" - fileMetaData: {}", fileMetaData);
final CLASS_TYPE media = DataAccess.get(clazz, id);
if (media == null) {
throw new InputException(clazz.getCanonicalName(),
"[" + id.toString() + "] Id does not exist or removed...");
}
final long tmpUID = getTmpDataId();
final String sha512 = saveTemporaryFile(fileInputStream, tmpUID);
Data data = getWithSha512(sha512);
if (data == null) {
LOGGER.info("Need to add the data in the BDD ... ");
try {
data = createNewData(tmpUID, fileMetaData.getFileName(), sha512);
} catch (final IOException ex) {
removeTemporaryFile(tmpUID);
throw new FailException(Response.Status.NOT_MODIFIED,
clazz.getCanonicalName() + "[" + id.toString() + "] can not create input media", ex);
} catch (final SQLException ex) {
removeTemporaryFile(tmpUID);
throw new FailException(Response.Status.NOT_MODIFIED,
clazz.getCanonicalName() + "[" + id.toString() + "] Error in SQL insertion", ex);
}
} else if (data.deleted) {
LOGGER.error("Data already exist but deleted");
undelete(data.uuid);
data.deleted = false;
} else {
LOGGER.error("Data already exist ... all good");
}
// Fist step: retrieve all the Id of each parents:...
LOGGER.info("Find typeNode");
if (id instanceof final Long idLong) {
AddOnDataJson.addLink(clazz, idLong, "covers", data.uuid);
} else if (id instanceof final UUID idUUID) {
AddOnDataJson.addLink(clazz, idUUID, "covers", data.uuid);
} else {
throw new IOException("Fail to add Cover can not detect type...");
} }
return Response.serverError().build();
} }
} }

View File

@@ -107,7 +107,7 @@ public class JWTWrapper {
} }
in.close(); in.close();
// print result // print result
// LOGGER.debug(response.toString()); LOGGER.debug(response.toString());
final ObjectMapper mapper = new ObjectMapper(); final ObjectMapper mapper = new ObjectMapper();
final PublicKey values = mapper.readValue(response.toString(), PublicKey.class); final PublicKey values = mapper.readValue(response.toString(), PublicKey.class);
rsaPublicJWK = RSAKey.parse(values.key); rsaPublicJWK = RSAKey.parse(values.key);

View File

@@ -121,7 +121,7 @@ public class RESTApi {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected <T, U> T modelSendJson(final String model, final Class<T> clazz, final String urlOffset, String body) public <T, U> T modelSendJson(final String model, final Class<T> clazz, final String urlOffset, String body)
throws RESTErrorResponseExeption, IOException, InterruptedException { throws RESTErrorResponseExeption, IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient(); final HttpClient client = HttpClient.newHttpClient();
// client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true); // client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
@@ -166,7 +166,7 @@ public class RESTApi {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected <T> T modelSendMap( public <T> T modelSendMap(
final String model, final String model,
final Class<T> clazz, final Class<T> clazz,
final String urlOffset, final String urlOffset,
@@ -222,13 +222,66 @@ public class RESTApi {
*/ */
public <T> T delete(final Class<T> clazz, final String urlOffset) public <T> T delete(final Class<T> clazz, final String urlOffset)
throws RESTErrorResponseExeption, IOException, InterruptedException { throws RESTErrorResponseExeption, IOException, InterruptedException {
return simpleRequest("DELETE", clazz, urlOffset);
}
/**
* Call an ARCHIVE on a REST API
* @param urlOffset Offset to call the API
*/
public void archive(final String urlOffset) throws RESTErrorResponseExeption, IOException, InterruptedException {
archive(Void.class, urlOffset);
}
/**
* Call a ARCHIVE 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 archive(final Class<T> clazz, final String urlOffset)
throws RESTErrorResponseExeption, IOException, InterruptedException {
return simpleRequest("ARCHIVE", clazz, urlOffset);
}
/**
* Call an RESTORE on a REST API
* @param urlOffset Offset to call the API
*/
public void restore(final String urlOffset) throws RESTErrorResponseExeption, IOException, InterruptedException {
restore(Void.class, urlOffset);
}
/**
* Call a RESTORE 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 restore(final Class<T> clazz, final String urlOffset)
throws RESTErrorResponseExeption, IOException, InterruptedException {
return simpleRequest("RESTORE", clazz, urlOffset);
}
/**
* Call a key on a REST API with retrieving some data
* @param <T> Type of data that might be received.
* @param model name of the key for the REST call
* @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 simpleRequest(final String model, final Class<T> clazz, final String urlOffset)
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)
.uri(URI.create(this.baseUrl + urlOffset)); .uri(URI.create(this.baseUrl + urlOffset));
if (this.token != null) { if (this.token != null) {
requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Bearer " + this.token); requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Bearer " + this.token);
} }
final HttpRequest request = requestBuilding.DELETE().build(); final HttpRequest request = requestBuilding.method(model, BodyPublishers.ofString("")).build();
final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
try { try {

View File

@@ -7,11 +7,15 @@
import { RestErrorResponse, isRestErrorResponse } from "./model"; import { RestErrorResponse, isRestErrorResponse } from "./model";
export enum HTTPRequestModel { export enum HTTPRequestModel {
ARCHIVE = "ARCHIVE",
DELETE = "DELETE", DELETE = "DELETE",
HEAD = "HEAD",
GET = "GET", GET = "GET",
OPTION = "OPTION",
PATCH = "PATCH", PATCH = "PATCH",
POST = "POST", POST = "POST",
PUT = "PUT", PUT = "PUT",
RESTORE = "RESTORE",
} }
export enum HTTPMimeType { export enum HTTPMimeType {
ALL = "*/*", ALL = "*/*",
@@ -74,7 +78,7 @@ export interface RESTRequestType {
data?: any; data?: any;
params?: object; params?: object;
queries?: object; queries?: object;
callback?: RESTCallbacks; callbacks?: RESTCallbacks;
} }
function replaceAll(input, searchValue, replaceValue) { function replaceAll(input, searchValue, replaceValue) {
@@ -237,7 +241,7 @@ export function RESTRequest({
data, data,
params, params,
queries, queries,
callback, callbacks,
}: RESTRequestType): Promise<ModelResponseHttp> { }: RESTRequestType): Promise<ModelResponseHttp> {
// Create the URL PATH: // Create the URL PATH:
let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries }); let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries });
@@ -248,9 +252,14 @@ export function RESTRequest({
if (restModel.accept !== undefined) { if (restModel.accept !== undefined) {
headers["Accept"] = restModel.accept; headers["Accept"] = restModel.accept;
} }
if (restModel.requestType !== HTTPRequestModel.GET) { if (restModel.requestType !== HTTPRequestModel.GET &&
restModel.requestType !== HTTPRequestModel.ARCHIVE &&
restModel.requestType !== HTTPRequestModel.RESTORE
) {
// if Get we have not a content type, the body is empty // if Get we have not a content type, the body is empty
if (restModel.contentType !== HTTPMimeType.MULTIPART) { if (restModel.contentType !== HTTPMimeType.MULTIPART &&
restModel.contentType !== undefined
) {
// special case of multi-part ==> no content type otherwise the browser does not set the ";bundary=--****" // special case of multi-part ==> no content type otherwise the browser does not set the ";bundary=--****"
headers["Content-Type"] = restModel.contentType; headers["Content-Type"] = restModel.contentType;
} }
@@ -268,10 +277,10 @@ export function RESTRequest({
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let action: undefined | Promise<Response> = undefined; let action: undefined | Promise<Response> = undefined;
if ( if (
isNullOrUndefined(callback) || isNullOrUndefined(callbacks) ||
(isNullOrUndefined(callback.progressDownload) && (isNullOrUndefined(callbacks.progressDownload) &&
isNullOrUndefined(callback.progressUpload) && isNullOrUndefined(callbacks.progressUpload) &&
isNullOrUndefined(callback.abortHandle)) isNullOrUndefined(callbacks.abortHandle))
) { ) {
// No information needed: call the generic fetch interface // No information needed: call the generic fetch interface
action = fetch(generateUrl, { action = fetch(generateUrl, {
@@ -288,7 +297,7 @@ export function RESTRequest({
headers, headers,
body, body,
}, },
callback callbacks
); );
} }
action action

View File

@@ -0,0 +1,40 @@
package test.kar.archidata;
import java.io.IOException;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ConfigureDb {
final static private Logger LOGGER = LoggerFactory.getLogger(ConfigureDb.class);
public static void configure() throws IOException {
if (true) {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) {
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
}
} else {
// Enable this if you want to access to a local MySQL base to test with an adminer
ConfigBaseVariable.bdDatabase = "test_db";
ConfigBaseVariable.dbPort = "3906";
ConfigBaseVariable.dbUser = "root";
//ConfigBaseVariable.dbPassword = "password";
}
// Connect the dataBase...
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
entry.connect();
}
public static void clear() throws IOException {
LOGGER.info("Remove the test db");
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
}
}

View File

@@ -11,11 +11,8 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -29,22 +26,12 @@ public class TestJson {
@BeforeAll @BeforeAll
public static void configureWebServer() throws Exception { public static void configureWebServer() throws Exception {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) { ConfigureDb.configure();
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
}
// Connect the dataBase...
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
entry.connect();
} }
@AfterAll @AfterAll
public static void removeDataBase() throws IOException { public static void removeDataBase() throws IOException {
LOGGER.info("Remove the test db"); ConfigureDb.clear();
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
} }
@Order(1) @Order(1)

View File

@@ -12,11 +12,8 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -29,22 +26,12 @@ public class TestListJson {
@BeforeAll @BeforeAll
public static void configureWebServer() throws Exception { public static void configureWebServer() throws Exception {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) { ConfigureDb.configure();
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
}
// Connect the dataBase...
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
entry.connect();
} }
@AfterAll @AfterAll
public static void removeDataBase() throws IOException { public static void removeDataBase() throws IOException {
LOGGER.info("Remove the test db"); ConfigureDb.clear();
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
} }
@Order(1) @Order(1)

View File

@@ -11,12 +11,9 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany; import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -31,22 +28,12 @@ public class TestManyToMany {
@BeforeAll @BeforeAll
public static void configureWebServer() throws Exception { public static void configureWebServer() throws Exception {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) { ConfigureDb.configure();
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
}
// Connect the dataBase...
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
entry.connect();
} }
@AfterAll @AfterAll
public static void removeDataBase() throws IOException { public static void removeDataBase() throws IOException {
LOGGER.info("Remove the test db"); ConfigureDb.clear();
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
} }
@Order(1) @Order(1)
@@ -129,7 +116,7 @@ public class TestManyToMany {
Assertions.assertNotNull(retrieve.otherData); Assertions.assertNotNull(retrieve.otherData);
Assertions.assertEquals(insertedData.otherData, retrieve.otherData); Assertions.assertEquals(insertedData.otherData, retrieve.otherData);
Assertions.assertNotNull(retrieve.remote); Assertions.assertNotNull(retrieve.remote);
Assertions.assertEquals(retrieve.remote.size(), 2); Assertions.assertEquals(2, retrieve.remote.size());
Assertions.assertEquals(retrieve.remote.get(0), insertedRemote1.id); Assertions.assertEquals(retrieve.remote.get(0), insertedRemote1.id);
Assertions.assertEquals(retrieve.remote.get(1), insertedRemote2.id); Assertions.assertEquals(retrieve.remote.get(1), insertedRemote2.id);
@@ -141,7 +128,7 @@ public class TestManyToMany {
Assertions.assertNotNull(retrieveExpand.otherData); Assertions.assertNotNull(retrieveExpand.otherData);
Assertions.assertEquals(insertedData.otherData, retrieveExpand.otherData); Assertions.assertEquals(insertedData.otherData, retrieveExpand.otherData);
Assertions.assertNotNull(retrieveExpand.remote); Assertions.assertNotNull(retrieveExpand.remote);
Assertions.assertEquals(retrieveExpand.remote.size(), 2); Assertions.assertEquals(2, retrieveExpand.remote.size());
Assertions.assertEquals(retrieveExpand.remote.get(0).id, insertedRemote1.id); Assertions.assertEquals(retrieveExpand.remote.get(0).id, insertedRemote1.id);
Assertions.assertEquals(retrieveExpand.remote.get(1).id, insertedRemote2.id); Assertions.assertEquals(retrieveExpand.remote.get(1).id, insertedRemote2.id);

View File

@@ -11,11 +11,8 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -33,22 +30,12 @@ public class TestManyToOne {
@BeforeAll @BeforeAll
public static void configureWebServer() throws Exception { public static void configureWebServer() throws Exception {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) { ConfigureDb.configure();
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
}
// Connect the dataBase...
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
entry.connect();
} }
@AfterAll @AfterAll
public static void removeDataBase() throws IOException { public static void removeDataBase() throws IOException {
LOGGER.info("Remove the test db"); ConfigureDb.clear();
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
} }
@Order(1) @Order(1)

View File

@@ -11,11 +11,8 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -33,22 +30,12 @@ public class TestOneToMany {
@BeforeAll @BeforeAll
public static void configureWebServer() throws Exception { public static void configureWebServer() throws Exception {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) { ConfigureDb.configure();
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
}
// Connect the dataBase...
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
entry.connect();
} }
@AfterAll @AfterAll
public static void removeDataBase() throws IOException { public static void removeDataBase() throws IOException {
LOGGER.info("Remove the test db"); ConfigureDb.clear();
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
} }
@Order(1) @Order(1)

View File

@@ -11,11 +11,8 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -28,22 +25,12 @@ public class TestRawQuery {
@BeforeAll @BeforeAll
public static void configureWebServer() throws Exception { public static void configureWebServer() throws Exception {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) { ConfigureDb.configure();
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
}
// Connect the dataBase...
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
entry.connect();
} }
@AfterAll @AfterAll
public static void removeDataBase() throws IOException { public static void removeDataBase() throws IOException {
LOGGER.info("Remove the test db"); ConfigureDb.clear();
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
} }
@Order(1) @Order(1)
@@ -83,13 +70,13 @@ public class TestRawQuery {
test.floatData = 7.0F; test.floatData = 7.0F;
DataAccess.insert(test); DataAccess.insert(test);
{ {
String query = """ final String query = """
SELECT * SELECT *
FROM TypesTable FROM TypesTable
WHERE `intData` = ? WHERE `intData` = ?
ORDER BY id DESC ORDER BY id DESC
"""; """;
List<Object> parameters = List.of(Integer.valueOf(99)); final List<Object> parameters = List.of(Integer.valueOf(99));
// Try to retrieve all the data: // Try to retrieve all the data:
final List<TypesTable> retrieve = DataAccess.query(TypesTable.class, query, parameters); final List<TypesTable> retrieve = DataAccess.query(TypesTable.class, query, parameters);
@@ -102,13 +89,13 @@ public class TestRawQuery {
} }
{ {
String query = """ final String query = """
SELECT DISTINCT intData SELECT DISTINCT intData
FROM TypesTable FROM TypesTable
WHERE `intData` = ? WHERE `intData` = ?
ORDER BY id DESC ORDER BY id DESC
"""; """;
List<Object> parameters = List.of(Integer.valueOf(99)); final List<Object> parameters = List.of(Integer.valueOf(99));
// Try to retrieve all the data: // Try to retrieve all the data:
final List<TypesTable> retrieve = DataAccess.query(TypesTable.class, query, parameters); final List<TypesTable> retrieve = DataAccess.query(TypesTable.class, query, parameters);

View File

@@ -14,12 +14,9 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.QueryOptions; import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -36,26 +33,15 @@ public class TestSimpleTable {
@BeforeAll @BeforeAll
public static void configureWebServer() throws Exception { public static void configureWebServer() throws Exception {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) {
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
}
// Clear the static test: // Clear the static test:
idOfTheObject = null; idOfTheObject = null;
startAction = null; startAction = null;
ConfigureDb.configure();
// Connect the dataBase...
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
entry.connect();
} }
@AfterAll @AfterAll
public static void removeDataBase() throws IOException { public static void removeDataBase() throws IOException {
LOGGER.info("Remove the test db"); ConfigureDb.clear();
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
} }
@Order(1) @Order(1)

View File

@@ -14,11 +14,9 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.QueryOptions; import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.tools.ConfigBaseVariable; import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -36,26 +34,16 @@ public class TestSimpleTableSoftDelete {
@BeforeAll @BeforeAll
public static void configureWebServer() throws Exception { public static void configureWebServer() throws Exception {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) {
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
}
// Clear the static test: // Clear the static test:
idOfTheObject = null; idOfTheObject = null;
startAction = null; startAction = null;
// Connect the dataBase... ConfigureDb.configure();
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
entry.connect();
} }
@AfterAll @AfterAll
public static void removeDataBase() throws IOException { public static void removeDataBase() throws IOException {
LOGGER.info("Remove the test db"); ConfigureDb.clear();
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
} }
@Order(1) @Order(1)

View File

@@ -11,11 +11,8 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -29,22 +26,12 @@ public class TestTypeEnum1 {
@BeforeAll @BeforeAll
public static void configureWebServer() throws Exception { public static void configureWebServer() throws Exception {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) { ConfigureDb.configure();
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
}
// Connect the dataBase...
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
entry.connect();
} }
@AfterAll @AfterAll
public static void removeDataBase() throws IOException { public static void removeDataBase() throws IOException {
LOGGER.info("Remove the test db"); ConfigureDb.clear();
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
} }
@Order(1) @Order(1)

View File

@@ -11,11 +11,8 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -29,22 +26,12 @@ public class TestTypeEnum2 {
@BeforeAll @BeforeAll
public static void configureWebServer() throws Exception { public static void configureWebServer() throws Exception {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) { ConfigureDb.configure();
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
}
// Connect the dataBase...
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
entry.connect();
} }
@AfterAll @AfterAll
public static void removeDataBase() throws IOException { public static void removeDataBase() throws IOException {
LOGGER.info("Remove the test db"); ConfigureDb.clear();
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
} }
@Order(1) @Order(1)

View File

@@ -16,11 +16,8 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataFactory; import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -33,22 +30,12 @@ public class TestTypes {
@BeforeAll @BeforeAll
public static void configureWebServer() throws Exception { public static void configureWebServer() throws Exception {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) { ConfigureDb.configure();
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
}
// Connect the dataBase...
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
entry.connect();
} }
@AfterAll @AfterAll
public static void removeDataBase() throws IOException { public static void removeDataBase() throws IOException {
LOGGER.info("Remove the test db"); ConfigureDb.clear();
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
} }
@Order(1) @Order(1)

View File

@@ -1,8 +1,15 @@
package test.kar.archidata.model; package test.kar.archidata.model;
import java.util.List;
import org.kar.archidata.model.GenericData; import org.kar.archidata.model.GenericData;
public class TypeManyToManyRemote extends GenericData { import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToMany;
public class TypeManyToManyRemote extends GenericData {
@ManyToMany(fetch = FetchType.LAZY, targetEntity = TypeManyToManyRoot.class, mappedBy = "remote")
public List<Long> remoteToParent;
public String data; public String data;
} }

View File

@@ -1 +1 @@
0.12.0 0.19.0