Compare commits

...

86 Commits

Author SHA1 Message Date
a15be78e10 [RELEASE] Release v0.25.6 2025-03-30 23:27:32 +02:00
abff1ada45 [FIX] (RestApi tool) null object are serialize as null (like browser) 2025-03-30 23:24:17 +02:00
1c769827cf [FIX] (RestApi tool) multipart file is not merged with nest parameter 2025-03-30 23:23:51 +02:00
e09de7cc7a [FEAT] remove dataJson plugin that never exist in NoSQL 2025-03-30 17:00:19 +02:00
a5b9b60294 [FEAT] rename parameter to be clear 2025-03-30 16:59:34 +02:00
288e1f8293 [FEAT] add capabilituy of rest API to manage multipart 2025-03-30 16:58:34 +02:00
d9a5f1ece2 [FEAT] configure deleted element to be missing or false in morphia 2025-03-29 20:23:42 +01:00
f77c6ce13e [FEAT] remove doucle of code 2025-03-29 20:23:04 +01:00
85754f20f8 [FIX] configuration of mongo interface 2025-03-29 20:22:44 +01:00
969bf78576 [FEAT] configure back compatibility with mongo 2025-03-29 20:21:07 +01:00
f0cf1acf8a [FIX] configure morphia that is compatible with mongo 2025-03-29 20:20:21 +01:00
7208db5bdf [FEAT] normalize Mongo and SQL 2025-03-29 20:19:55 +01:00
5b88401d48 [FIX] name of the primary key of the cover is not managed 2025-03-28 21:13:15 +01:00
d53a0719b5 [FEAT] fix 400 error in input error 2025-03-21 08:57:24 +01:00
3e7a1a5473 [VERSION] update dev tag version 2025-03-19 23:36:56 +01:00
c7338c7877 [RELEASE] Release v0.25.4 2025-03-19 23:35:39 +01:00
1007628713 [FEAT] update test 2025-03-19 23:34:24 +01:00
84a968a426 [Fix] correct @OneToMany on ObjectId 2025-03-19 19:15:24 +01:00
581c936bec [VERSION] update dev tag version 2025-03-18 23:07:41 +01:00
a6204032b5 [RELEASE] Release v0.25.2 2025-03-18 23:04:15 +01:00
e824feb8a2 [FIX] fix json serialize null in error (remove) 2025-03-18 15:27:42 +01:00
26ea70f80c [FIX] correct some generic code generation 2025-03-18 15:27:39 +01:00
6291466df0 [VERSION] update dev tag version 2025-03-17 21:59:02 +01:00
2bc7a2c5e3 [RELEASE] Release v0.25.0 2025-03-17 21:55:44 +01:00
dependabot[bot]
6c69bc63c6 [DEV-OPS] (dependabot) Bump all dependency 2025-03-17 21:13:14 +01:00
85ac72648f [FIX] not ready dependency 2025-03-16 11:48:33 +01:00
1281415c48 [DEV] integrate hybernate validator 2025-03-16 11:24:16 +01:00
dependabot[bot]
a4521853c3 [DEV-OPS] (dependabot) Bump com.twelvemonkeys.imageio:imageio-webp
Bumps com.twelvemonkeys.imageio:imageio-webp from 3.11.0 to 3.12.0.

---
updated-dependencies:
- dependency-name: com.twelvemonkeys.imageio:imageio-webp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:51:19 +01:00
dependabot[bot]
abe2dd9480 [DEV-OPS] (dependabot) Bump com.nimbusds:nimbus-jose-jwt
Bumps [com.nimbusds:nimbus-jose-jwt](https://bitbucket.org/connect2id/nimbus-jose-jwt) from 9.41.1 to 10.0.2.
- [Changelog](https://bitbucket.org/connect2id/nimbus-jose-jwt/src/master/CHANGELOG.txt)
- [Commits](https://bitbucket.org/connect2id/nimbus-jose-jwt/branches/compare/10.0.2..9.41.1)

---
updated-dependencies:
- dependency-name: com.nimbusds:nimbus-jose-jwt
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:36:46 +01:00
dependabot[bot]
b7af0b4575 [DEV-OPS] (dependabot) Bump org.hibernate.validator:hibernate-validator
Bumps [org.hibernate.validator:hibernate-validator](https://github.com/hibernate/hibernate-validator) from 8.0.1.Final to 8.0.2.Final.
- [Changelog](https://github.com/hibernate/hibernate-validator/blob/8.0.2.Final/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-validator/compare/8.0.1.Final...8.0.2.Final)

---
updated-dependencies:
- dependency-name: org.hibernate.validator:hibernate-validator
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:36:43 +01:00
dependabot[bot]
5412eadf2e [DEV-OPS] (dependabot) Bump org.apache.maven.plugins:maven-checkstyle-plugin
Bumps [org.apache.maven.plugins:maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.5.0 to 3.6.0.
- [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.5.0...maven-checkstyle-plugin-3.6.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:36:41 +01:00
dependabot[bot]
d5e2e0f5b3 [DEV-OPS] (dependabot) Bump org.glassfish.jersey:jersey-bom
Bumps org.glassfish.jersey:jersey-bom from 3.1.5 to 3.1.10.

---
updated-dependencies:
- dependency-name: org.glassfish.jersey:jersey-bom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-15 08:36:38 +01:00
28ab6c49d4 [FIX] update PR title 2025-03-15 08:30:14 +01:00
260a3abd13 [FEAT] update dependabot 2025-03-15 08:25:09 +01:00
895e8c2b37 [FEAT] update versions 2025-03-15 08:18:14 +01:00
1673f1680b [FEAT] add a spotbug nullable 2025-03-15 08:06:59 +01:00
04a82250d8 [FEAT] add capability to catch ConstraintViolationException 2025-03-15 08:06:50 +01:00
0326bde209 [FEAT] understable template typing 2025-03-10 07:27:51 +01:00
c1ccaf20ec [FEAT] rework the generation of code for typescript (#27)
This PR is not compatible with previous code...
Rename some Annotation to have a better experience

Update the generation of the Typescript object 
===================================

By default the generation of object are only the object requested

if you annotate the object with: `@ApiGenerationMode(create = true,
update = true)`

the generation will add 2 object in typescript:
  - `MyClassUpdate` that contain the object specific for `PUT` request
  - `MyClassCreate` that contain the object specific for `POST` request
  - the PATCH request are generate with `Partial<MyClassUpdate>`

If you do not generate the create or update the system will wrap the
bass by default: `MyClass`

Add support of hidding the element in the generated object
=============================================

When generate the object some field are not needed to transmit to the
client, then we add `@ApiAccessLimitation(creatable = false, updatable =
false)` that permit to remove the field when Update or Create object are
generated

It will be used to check the Input validity instead of
`@schema(readonly)` (error of implementation)


TODO:
=====

  - Some issue when request Update generation and parent does not exist
- The checker is not updated with the use of the `@ApiAccessLimitation`
decorator.


dependencies:
===========
  - Closes: https://github.com/kangaroo-and-rabbit/archidata/issues/22
2025-03-08 14:32:16 +01:00
2174d7689f [FEAT] add callback to detect and collect errors 2025-03-05 23:48:15 +01:00
15113807b3 [DOC] add some documentation 2025-03-05 23:45:08 +01:00
ffdc6c1249 [VERSION] update dev tag version 2025-02-24 12:34:45 +01:00
1b4e6ca239 [RELEASE] Release v0.24.0 2025-02-24 12:34:41 +01:00
be8a5c713a [FIX] ObjectId manyToOne and OneToMany 2025-02-17 00:16:11 +01:00
c3f03bc1e8 [VERSION] update dev tag version 2025-02-09 22:06:56 +01:00
c7eadc607d [RELEASE] Release v0.23.6 2025-02-09 22:06:53 +01:00
5f89ff7944 [DEV] add variable to permit not check if the DB exist 2025-02-09 22:05:35 +01:00
20d2d004cb [VERSION] update dev tag version 2025-02-06 07:47:20 +01:00
da3c467569 [RELEASE] Release v0.23.4 2025-02-06 07:47:17 +01:00
0c932d4e92 [FEAT] set the object enpty as identical as their parent.
this prevent react-hook-form resolver erreur, it does not support empty object
2025-02-04 21:15:10 +01:00
a400bb99b8 [FEAT] add Jwt token description to be serialize in front 2025-02-02 19:34:23 +01:00
bdc9a4ac4d [VERSION] update dev tag version 2025-01-30 21:45:29 +01:00
b0bf103195 [RELEASE] Release v0.23.2 2025-01-30 21:45:26 +01:00
d36c366ab6 [FIX] zod number gte/lte 2025-01-30 21:25:15 +01:00
cddb4dd7fe [DEV] update dev tag version 2025-01-30 10:01:58 +01:00
218fa3be2e [RELEASE] new version 0.23.0 2025-01-29 23:30:01 +01:00
015c38ff5b [FEAT] add @CheckForeignKey but not tested 2025-01-29 23:30:01 +01:00
24590b2a1e [FIX] correct min and mas size of string 2025-01-29 23:10:53 +01:00
c04660c01a [FEAT] simplify annotation tool 2025-01-29 23:09:49 +01:00
3e81673d38 [FIX] normalize comment when check size 2025-01-29 22:43:22 +01:00
69880df4aa [FIX] missing string size checker 2025-01-29 22:41:08 +01:00
510553550f [FIX] remove unneeded comment 2025-01-29 22:40:34 +01:00
e071d3dbf7 [FEAT] correct the output generation of the typescript ==> missing decimal min and decimal max ==> need to test pattern and email 2025-01-29 00:34:49 +01:00
249e6ad2c8 set JPAChecker agnostic of @ManyToOne primary key type 2025-01-28 23:48:40 +01:00
e156e528c1 [FIX] use instance of DB instead of create a new one 2025-01-28 23:44:36 +01:00
89ab0f3b6a [DEV] update dev tag version 2025-01-28 23:27:30 +01:00
3d5a024084 [RELEASE] new version 0.22.0 2025-01-28 23:27:05 +01:00
ba6478182d [API] move the cheker in the correct folder 2025-01-28 23:25:32 +01:00
cc639243fc [FEAT] add support of @DecimalMin and @DecimalMax 2025-01-28 23:17:34 +01:00
24c226e92c [FEAT] add @Size @Min @Max @parttern @email unit test for JPAChecker 2025-01-28 23:16:18 +01:00
b5fcc3e20c [FEAT,API] remove checker from JsonData and creadte @CheckerAnnotation 2025-01-28 23:15:11 +01:00
d028eb2261 Manage correct rights 2025-01-26 23:38:48 +01:00
ca18d3759d [FIX] congig in config 2025-01-25 15:48:35 +01:00
a7c9bb5e1b [FIX] do not set the table name at unknow when null is find 2025-01-25 15:48:04 +01:00
d011b3a587 [FEAT] add decorator CollectionNotEmpty 2025-01-25 14:41:39 +01:00
6974adbfdf [FEAT] add decorator CollectionItemUnique 2025-01-25 14:41:20 +01:00
461aece7a0 [FEAT] add decorator @CollectionItemNotNull 2025-01-25 14:40:07 +01:00
3f15d560ed [FEAT] better interface for dataJson 2025-01-25 14:38:53 +01:00
8f3c14e28d [FEAT] assert when try to insart null data 2025-01-25 12:16:39 +01:00
0d419f651e [FIX] dataJson checker is not responsive for null data pointer 2025-01-25 12:16:13 +01:00
9dad14d200 [FIX] many-many support every key type 2025-01-25 12:15:25 +01:00
990b7c08da [FIX] correct right: set it when no element is requested 2025-01-18 09:57:55 +01:00
7f393a9e44 [FIX] set User block boolean nullable for creation capability (front pb) 2025-01-18 09:57:52 +01:00
c91291dbce [DOC] log better display of path when call filter authentication 2025-01-18 09:57:47 +01:00
d684b5eaa9 [VERSION] update dev tag version 2025-01-11 17:29:13 +01:00
85b27c0b31 [RELEASE] Release v0.21.0 2025-01-11 17:29:10 +01:00
135 changed files with 5819 additions and 2127 deletions

View File

@@ -30,15 +30,6 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</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">
<attributes>
<attribute name="optional" value="true"/>

View File

@@ -1,12 +1,24 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
---
version: 2
updates:
- package-ecosystem: "" # See documentation for possible values
directory: "/" # Location of package manifests
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
interval: "daily"
time: "05:00"
timezone: "Europe/Paris"
commit-message:
prefix: "[DEV-OPS] (dependabot) "
assignees:
- HeeroYui
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "daily"
time: "06:00"
timezone: "Europe/Paris"
commit-message:
prefix: "[DEV-OPS] (dependabot) "
assignees:
- HeeroYui

View File

@@ -9,6 +9,8 @@ on:
- synchronize
- ready_for_review
- reopened
- labeled
- unlabeled
jobs:
check-title:
@@ -17,7 +19,7 @@ jobs:
- 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 ,.'\\-!]+$"
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}]

89
pom.xml
View File

@@ -3,17 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId>
<version>0.20.5-SNAPSHOT</version>
<properties>
<java.version>21</java.version>
<maven.compiler.version>3.1</maven.compiler.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.dependency.version>3.1.1</maven.dependency.version>
<jersey.version>3.1.5</jersey.version>
<jaxb.version>2.3.1</jaxb.version>
<istack.version>4.1.1</istack.version>
</properties>
<version>0.25.6</version>
<repositories>
<repository>
<id>gitea</id>
@@ -35,7 +25,7 @@
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<version>4.0.0-M2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -58,13 +48,13 @@
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-webp</artifactId>
<version>3.11.0</version>
<version>3.12.0</version>
</dependency>
<!-- Decode JPEG image -->
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.11.0</version>
<version>3.12.0</version>
</dependency>
<!-- Encode file in webp -->
<dependency>
@@ -76,7 +66,7 @@
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>3.0.0-BETA2</version>
<version>3.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-multipart -->
<dependency>
@@ -95,10 +85,19 @@
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-http</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb.version}</version>
<version>2.4.0-b180830.0359</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>4.0.5</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
@@ -110,15 +109,10 @@
<artifactId>jakarta.ws.rs-api</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>com.sun.istack</groupId>
<artifactId>istack-commons-runtime</artifactId>
<version>${istack.version}</version>
<version>4.2.0</version>
</dependency>
<!-- continu to be needed ??? -->
<dependency>
@@ -134,18 +128,18 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.18.1</version>
<version>2.18.3</version>
</dependency>
<!-- encode output in CSV -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
<version>2.18.1</version>
<version>2.18.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.18.1</version>
<version>2.18.3</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
@@ -157,18 +151,18 @@
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.0.0</version>
<version>9.2.0</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.46.1.0</version>
<version>3.49.1.0</version>
</dependency>
<!-- Interface for JWT token -->
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.41.1</version>
<version>10.0.2</version>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
@@ -179,32 +173,34 @@
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-jakarta</artifactId>
<version>2.2.23</version>
<version>2.2.29</version>
</dependency>
<!-- spotbug tooling -->
<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId>
<version>4.8.6</version>
<version>4.9.3</version>
<scope>compile</scope>
</dependency>
<!-- Morphia -->
<dependency>
<groupId>dev.morphia.morphia</groupId>
<artifactId>morphia-core</artifactId>
<version>2.3.0</version>
<version>2.4.15</version>
</dependency>
<!-- MongoDB Java Driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.3.0</version>
<!--<version>5.3.1</version>-->
<!--Morphia 2.4.x does not support version upper than 4.x-->
<version>4.11.5</version>
</dependency>
<!-- Bean Validation (JSR 303 / 380) -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>7.0.0.Final</version>
<version>9.0.0.CR1</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
@@ -219,24 +215,24 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.11.0</version>
<version>5.12.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.11.0</version>
<version>5.12.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.24.1</version>
<version>2.25.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.5.0</version>
<version>3.6.0</version>
</dependency>
</dependencies>
<build>
@@ -256,18 +252,17 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<version>3.14.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<!--<encoding>${project.build.sourceEncoding}</encoding>-->
<source>21</source>
<target>21</target>
</configuration>
</plugin>
<!-- Create the source bundle -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
<version>3.3.1</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -295,7 +290,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<version>3.5.2</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
@@ -314,7 +309,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.0</version>
<configuration>
<show>private</show>
<nohelp>true</nohelp>
@@ -324,7 +319,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
<version>3.6.0</version>
<configuration>
<configLocation>CheckStyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
@@ -336,7 +331,7 @@
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.23.0</version>
<version>2.24.1</version>
<configuration>
<encoding>UTF-8</encoding>
<lineEnding>LF</lineEnding>
@@ -385,7 +380,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.0</version>
<configuration>
<show>public</show>
</configuration>

View File

@@ -2,9 +2,14 @@ package org.kar.archidata.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import org.kar.archidata.annotation.checker.Checker;
import org.kar.archidata.annotation.checker.CollectionItemNotNull;
import org.kar.archidata.annotation.checker.CollectionItemUnique;
import org.kar.archidata.annotation.checker.CollectionNotEmpty;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.options.OptionRenameColumn;
import org.kar.archidata.dataAccess.options.OverrideTableName;
@@ -24,6 +29,8 @@ import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
@@ -35,6 +42,60 @@ import jakarta.ws.rs.DefaultValue;
public class AnnotationTools {
static final Logger LOGGER = LoggerFactory.getLogger(AnnotationTools.class);
public static <TYPE extends Annotation> TYPE get(final Parameter param, final Class<TYPE> clazz) {
final TYPE[] annotations = param.getDeclaredAnnotationsByType(clazz);
if (annotations.length == 0) {
return null;
}
return annotations[0];
}
public static <TYPE extends Annotation> TYPE[] gets(final Parameter param, final Class<TYPE> clazz) {
final TYPE[] annotations = param.getDeclaredAnnotationsByType(clazz);
if (annotations.length == 0) {
return null;
}
return annotations;
}
public static <TYPE extends Annotation> TYPE get(final Field element, final Class<TYPE> clazz) {
final TYPE[] annotations = element.getDeclaredAnnotationsByType(clazz);
if (annotations.length == 0) {
return null;
}
return annotations[0];
}
public static <TYPE extends Annotation> TYPE[] gets(final Field element, final Class<TYPE> clazz) {
final TYPE[] annotations = element.getDeclaredAnnotationsByType(clazz);
if (annotations.length == 0) {
return null;
}
return annotations;
}
public static <TYPE extends Annotation> TYPE get(final Class<?> classObject, final Class<TYPE> clazz) {
final TYPE[] annotations = classObject.getDeclaredAnnotationsByType(clazz);
if (annotations.length == 0) {
return null;
}
return annotations[0];
}
public static <TYPE extends Annotation> TYPE[] gets(final Class<?> classObject, final Class<TYPE> clazz) {
final TYPE[] annotations = classObject.getDeclaredAnnotationsByType(clazz);
if (annotations.length == 0) {
return null;
}
return annotations;
}
// For SQL declaration table Name
public static String getTableName(final Class<?> clazz, final QueryOptions options) throws DataAccessException {
if (options != null) {
@@ -85,12 +146,16 @@ public class AnnotationTools {
return tmp;
}
public static boolean getSchemaReadOnly(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
if (annotation.length == 0) {
return false;
}
return ((Schema) annotation[0]).readOnly();
public static CollectionItemNotNull getCollectionItemNotNull(final Field element) {
return get(element, CollectionItemNotNull.class);
}
public static CollectionItemUnique getCollectionItemUnique(final Field element) {
return get(element, CollectionItemUnique.class);
}
public static CollectionNotEmpty getCollectionNotEmpty(final Field element) {
return get(element, CollectionNotEmpty.class);
}
public static String getSchemaExample(final Class<?> element) {
@@ -101,14 +166,6 @@ public class AnnotationTools {
return ((Schema) annotation[0]).example();
}
public static boolean getNoWriteSpecificMode(final Class<?> element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(NoWriteSpecificMode.class);
if (annotation.length == 0) {
return false;
}
return true;
}
public static String getSchemaDescription(final Class<?> element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
if (annotation.length == 0) {
@@ -134,51 +191,39 @@ public class AnnotationTools {
}
public static ManyToOne getManyToOne(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(ManyToOne.class);
if (annotation.length == 0) {
return null;
}
return (ManyToOne) annotation[0];
return get(element, ManyToOne.class);
}
public static ManyToMany getManyToMany(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(ManyToMany.class);
if (annotation.length == 0) {
return null;
}
return (ManyToMany) annotation[0];
return get(element, ManyToMany.class);
}
public static OneToMany getOneToMany(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(OneToMany.class);
if (annotation.length == 0) {
return null;
}
return (OneToMany) annotation[0];
return get(element, OneToMany.class);
}
public static DataJson getDataJson(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataJson.class);
if (annotation.length == 0) {
return null;
}
return (DataJson) annotation[0];
return get(element, DataJson.class);
}
public static Long getConstraintsMax(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Max.class);
if (annotation.length == 0) {
return null;
}
return ((Max) annotation[0]).value();
public static Checker[] getConstraintsCheckers(final Field element) {
return gets(element, Checker.class);
}
public static Long getConstraintsMin(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Min.class);
if (annotation.length == 0) {
return null;
}
return ((Min) annotation[0]).value();
public static DecimalMin getConstraintsDecimalMin(final Field element) {
return get(element, DecimalMin.class);
}
public static DecimalMax getConstraintsDecimalMax(final Field element) {
return get(element, DecimalMax.class);
}
public static Max getConstraintsMax(final Field element) {
return get(element, Max.class);
}
public static Min getConstraintsMin(final Field element) {
return get(element, Min.class);
}
public static int getLimitSize(final Field element) {
@@ -191,27 +236,15 @@ public class AnnotationTools {
}
public static Size getConstraintsSize(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Size.class);
if (annotation.length == 0) {
return null;
}
return (Size) annotation[0];
return get(element, Size.class);
}
public static String getConstraintsPattern(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Pattern.class);
if (annotation.length == 0) {
return null;
}
return ((Pattern) annotation[0]).regexp();
public static Pattern getConstraintsPattern(final Field element) {
return get(element, Pattern.class);
}
public static boolean getConstraintsEmail(final Field element) {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Email.class);
if (annotation.length == 0) {
return false;
}
return true;
public static Email getConstraintsEmail(final Field element) {
return get(element, Email.class);
}
public static boolean isAnnotationGroup(final Field field, final Class<?> annotationType) {
@@ -416,7 +449,7 @@ public class AnnotationTools {
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue;
}
if (AnnotationTools.getFieldNameRaw(field).equals(name)) {
if (field.getName().equals(name)) {
return true;
}
}

View File

@@ -5,6 +5,39 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The CreationTimestamp annotation is used to automatically set the creation
* date of an object in the database. This annotation ensures that the field
* marked with @CreationTimestamp is populated with the current timestamp
* when the object is first created.
*
* <p>Usage:
* - Target: This annotation can be applied to fields within a class.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle data persistence logic.
*
* <p>Behavior:
* - When a field is annotated with @CreationTimestamp, it will automatically
* be set to the current date and time when the object is inserted into the
* database.
* - This annotation is typically used in conjunction with other annotations
* such as @Column to define database column properties.
*
* <p>Example:
* <pre>{@code
* public class MyEntity {
* @DataNotRead
* @CreationTimestamp
* @Column(nullable = false, insertable = false, updatable = false)
* @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX") // optional depend on the configuration
* @Nullable
* public Date createdAt = null;
* }
* }</pre>
*
* In this example, the createdAt field will be automatically set to the
* current timestamp when a new User object is created in the database.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CreationTimestamp {

View File

@@ -5,6 +5,40 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The DataDeleted annotation is used to manage a boolean variable that marks
* an object as 'deleted' in the database. This annotation helps in soft deletion
* by excluding marked objects from being automatically retrieved unless
* explicitly specified.
*
* <p>Usage:
* - Target: This annotation can be applied to fields within a class.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle data retrieval logic.
*
* <p>Behavior:
* - When a field is annotated with @DataDeleted, it will not be included in the
* default data retrieval process from the database if its value is false.
* - To override this behavior and access deleted items, the query must include
* the option AccessDeletedItems.
*
* <p>Example:
* <pre>{@code
* public class MyEntity {
* public String username;
*
* @DataDeleted
* @DataNotRead
* @Column(nullable = false)
* @DefaultValue("'0'")
* public Boolean deleted = null;
* }
* }</pre>
*
* In this example, objects with `deleted` set to true will not be retrieved
* by default. To include them in the query results, the AccessDeletedItems
* option must be specified in the query.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataDeleted {

View File

@@ -5,13 +5,43 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.kar.archidata.dataAccess.options.CheckFunctionInterface;
import org.kar.archidata.dataAccess.options.CheckFunctionVoid;
/**
* The DataJson annotation is used to convert fields or classes to JSON format
* for storage in a database. This annotation allows storing complex data types
* such as lists, maps, and other objects in SQL databases as JSON or STRING
* (for SQLite).
*
* <p>Usage:
* - Target: This annotation can be applied to both fields and classes.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle data persistence logic.
*
* <p>Behavior:
* - When applied to a field or class, the DataJson annotation enables the
* conversion of the annotated element to JSON format before storing it in
* the database.
* - This is particularly useful in SQL databases where only basic data types
* (char, short, int, long, float, string, timestamp) can be stored directly.
* The DataJson annotation makes it possible to store complex data structures
* by converting them to JSON.
*
* <p>Attributes:
* - targetEntity: Specifies the target entity class to which the JSON data
* should be mapped. Defaults auto-detect if not specified.
*
* <p>Example:
* <pre>{@code
* public class User {
* @DataJson
* public Map<String, Object> additionalData;
* }
* }</pre>
*
* In this example, the additionalData field can store complex data structures
* as JSON in the database.
*/
@Target({ ElementType.TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DataJson {
Class<? extends CheckFunctionInterface> checker() default CheckFunctionVoid.class;
Class<?> targetEntity() default Void.class;
}

View File

@@ -5,6 +5,37 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The DataNotRead annotation is used to mark fields in a class that should not
* be automatically read from the database. This annotation helps in optimizing
* data retrieval by excluding certain fields from being fetched unless
* explicitly specified.
*
* <p>Usage:
* - Target: This annotation can be applied to fields within a class.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle data retrieval logic.
*
* <p>Behavior:
* - When a field is annotated with @DataNotRead, it will not be included in the
* default data retrieval process from the database.
* - To override this behavior and read all columns, including those marked with
* @DataNotRead, the query must include the option ReadAllColumn.
*
* <p>Example:
* <pre>{@code
* public class MyEntity {
* public String username;
*
* @DataNotRead
* private String sensitiveData;
* }
* }</pre>
*
* In this example, the sensitiveData field will not be read from the database
* by default. To include it in the query results, the ReadAllColumn option must
* be specified in the query.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataNotRead {

View File

@@ -1,12 +0,0 @@
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

@@ -1,13 +0,0 @@
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;
/** When we wend to have only One type for read and write mode (Wrapping API). */
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface NoWriteSpecificMode {
}

View File

@@ -1,11 +0,0 @@
package org.kar.archidata.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** In case of the update parameter with String input to detect null element. */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeScriptProgress {}

View File

@@ -5,6 +5,38 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The UpdateTimestamp annotation is used to automatically update the timestamp
* of an object in the database whenever it is modified. This annotation ensures
* that the field marked with @UpdateTimestamp is set to the current timestamp
* each time the object is updated.
*
* <p>Usage:
* - Target: This annotation can be applied to fields within a class.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle data persistence logic.
*
* <p>Behavior:
* - When a field is annotated with @UpdateTimestamp, it will automatically
* be updated to the current date and time whenever the object is modified
* in the database.
* - This annotation is typically used in conjunction with other annotations
* such as @Column to define database column properties.
*
* <p>Example:
* <pre>{@code
* public class MyEntity {
* @UpdateTimestamp
* @Column(nullable = false, insertable = false, updatable = false)
* @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
* @Nullable
* public Date updatedAt = null;
* }
* }</pre>
*
* In this example, the updatedAt field will be automatically set to the
* current timestamp whenever the User object is modified in the database.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UpdateTimestamp {

View File

@@ -0,0 +1,21 @@
package org.kar.archidata.annotation.apiGenerator;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(FIELD)
public @interface ApiAccessLimitation {
/**
* (Optional) The field is accessible in creation (POST)
*/
boolean creatable() default true;
/**
* (Optional) The field is accessible in update mode (PUT, PATCH)
*/
boolean updatable() default true;
}

View File

@@ -1,4 +1,4 @@
package org.kar.archidata.annotation;
package org.kar.archidata.annotation.apiGenerator;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -8,7 +8,7 @@ import java.lang.annotation.Target;
/** In case of the update parameter with String input to detect null element. */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface AsyncType {
public @interface ApiAsyncType {
// Possible class values.
Class<?>[] value();

View File

@@ -0,0 +1,57 @@
package org.kar.archidata.annotation.apiGenerator;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The ApiGenerationMode annotation is used to indicate the generation mode for
* API operations when producing code for other languages. This annotation is
* particularly useful in code generators for client libraries where specific
* data structures for read, create, and update operations may or may not be needed.
*
* <p>Usage:
* - Target: This annotation can be applied to class types.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle code generation logic.
*
* <p>Behavior:
* - When applied to a class, the ApiGenerationMode annotation specifies
* which API operations (read, create, update) should generate specific
* data structures. This can simplify the generated code by avoiding the
* creation of unnecessary structures.
*
* <p>Example:
* <pre>{@code
* @ApiGenerationMode(creatable=false, updatable=false)
* public class User {
* public String username;
* public String email;
* }
* }</pre>
*
* In this example, the User class will not generate separate data structures
* for create and update operations in the client code, only for read operations.
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiGenerationMode {
/**
* (Optional) Enable the generation of specific code for read access
* (generate object: MyClass).
*/
boolean read() default true;
/**
* (Optional) Enable the generation of specific code for create access
* (generate object: MyClassCreate).
*/
boolean create() default false;
/**
* (Optional) Enable the generation of specific code for update access
* (generate object: MyClassUpdate).
*/
boolean update() default false;
}

View File

@@ -0,0 +1,76 @@
package org.kar.archidata.annotation.apiGenerator;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The FormDataOptional annotation is used to indicate that a form data parameter
* is optional when generating client code. By default, form data parameters are
* required, but this annotation allows them to be optional, enabling the creation
* of polymorphic APIs.
*
* <p>Usage:
* - Target: This annotation can be applied to method parameters.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle code generation logic.
*
* <p>Behavior:
* - When applied to a parameter, the FormDataOptional annotation specifies that
* the parameter is optional in the generated client code. This allows for
* more flexible API designs where certain inputs can be omitted.
*
* <p>Example:
* <pre>{@code
* public class AlbumService {
*
* @POST
* @Path("{id}/cover")
* @RolesAllowed("ADMIN")
* @Consumes({ MediaType.MULTIPART_FORM_DATA })
* @Operation(description = "Add a cover on a specific album")
* @TypeScriptProgress
* public Album uploadCover(@PathParam("id") final Long id,
* @ApiInputOptional @FormDataParam("uri") final String uri,
* @ApiInputOptional @FormDataParam("file") final InputStream fileInputStream,
* @ApiInputOptional @FormDataParam("file") final FormDataContentDisposition fileMetaData)
* throws Exception {
* // some code
* }
* }
* }</pre>
*
* Note: @FormDataParam must be allway at the last position.
*
* In this example, the uri, fileInputStream, and fileMetaData parameters are
* marked as optional, allowing the client to omit them when calling the API.
*
* <p>Generated TypeScript code example:
* <pre>{@code
* //Add a cover on a specific album
* export function uploadCover({
* restConfig,
* params,
* data,
* callbacks,
* }: {
* restConfig: RESTConfig,
* params: {
* id: Long,
* },
* data: {
* file?: File, // element is optional
* uri?: string, // element is optional
* },
* callbacks?: RESTCallbacks,
* }): Promise<Album> { ...
* }</pre>
*
* The generated TypeScript function reflects the optional nature of the form data parameters.
*/
@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiInputOptional {
}

View File

@@ -0,0 +1,71 @@
package org.kar.archidata.annotation.apiGenerator;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The TypeScriptProgress annotation is used to specify that an API method
* will take a significant amount of time to complete, and thus requires a
* callback API to provide more precise progress tracking, particularly for
* upload operations.
*
* <p>Usage:
* - Target: This annotation can be applied to method parameters and methods.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle code generation logic.
*
* <p>Behavior:
* - When applied to a method or parameter, the TypeScriptProgress annotation
* indicates that the client code generator should provide a callback API
* for tracking the progress of the operation.
* - Note: The use of this annotation implies that the standard browser fetch
* API is not used, as the callback API is not yet operational. Instead,
* the older XMLHttpRequest interface is utilized.
*
* <p>Example:
* <pre>{@code
* public class SeasonService {
*
* @POST
* @Path("{id}/cover")
* @RolesAllowed("ADMIN")
* @Consumes(MediaType.MULTIPART_FORM_DATA)
* @Operation(description = "Upload a new season cover season", tags = "GLOBAL")
* @TypeScriptProgress
* public Season uploadCover(@PathParam("id") final Long id,
* @FormDataParam("file") final InputStream fileInputStream,
* @FormDataParam("file") final FormDataContentDisposition fileMetaData)
* throws Exception {
* // Upload logic
* }
* }
* }</pre>
*
* In this example, the uploadCover method will generate a client-side API
* with progress tracking capabilities using XMLHttpRequest.
*
* <p>Generated TypeScript code example:
* <pre>{@code
* export function uploadCover({
* restConfig,
* params,
* data,
* callbacks, // add this callback handle
* }: {
* restConfig: RESTConfig,
* params: {
* id: Long,
* },
* data: {
* file: File,
* },
* callbacks?: RESTCallbacks,
* }): Promise<Season> {...
* }</pre>
*
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiTypeScriptProgress {}

View File

@@ -0,0 +1,22 @@
package org.kar.archidata.annotation.checker;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = CheckForeignKeyValidator.class)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckForeignKey {
Class<?> target();
String message() default "Foreign-key does not exist in the DB";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,43 @@
package org.kar.archidata.annotation.checker;
import java.util.Collection;
import org.kar.archidata.dataAccess.DataAccess;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CheckForeignKeyValidator implements ConstraintValidator<CheckForeignKey, Object> {
Class<?> target = null;
@Override
public void initialize(final CheckForeignKey annotation) {
this.target = annotation.target();
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value != null) {
return true;
}
if (value instanceof final Collection<?> tmpCollection) {
final Object[] elements = tmpCollection.toArray();
for (final Object element : elements) {
if (element == null) {
continue;
}
try {
final long count = DataAccess.count(this.target, element);
if (count != 1) {
return false;
}
} catch (final Exception e) {
// TODO ...
return false;
}
}
}
return true;
}
}

View File

@@ -0,0 +1,52 @@
package org.kar.archidata.annotation.checker;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.kar.archidata.dataAccess.options.CheckFunctionInterface;
/**
* The Checker annotation is used to specify a checker class that automatically
* validates data for a parent class. This annotation can be applied to both
* classes and fields to enforce validation rules defined in the checker class.
*
* <p>Usage:
* - Target: This annotation can be applied to types (classes) and fields.
* - Retention: The annotation is retained at runtime, allowing it to be
* processed by frameworks or libraries that handle data validation logic.
*
* <p>Behavior:
* - When applied to a class or field, the Checker annotation specifies a
* checker class that implements the CheckFunctionInterface. This checker
* class is responsible for validating the data associated with the annotated
* element.
* - The validation is automatically triggered when the data of the parent class
* is validated, ensuring that the data adheres to the specified rules.
*
* <p>Attributes:
* - value: Specifies the checker class that implements the validation logic.
* This class must extend the CheckFunctionInterface.
*
* <p>Example:
* <pre>{@code
* public class User {
*
* @Checker(UserDataChecker.class)
* public String email;
* }
*
* public class UserDataChecker implements CheckFunctionInterface {
* ...
* }
* }</pre>
*
* In this example, the email field in the User class is validated using the
* UserDataChecker class whenever the User class data is validated.
*/
@Target({ ElementType.TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Checker {
Class<? extends CheckFunctionInterface> value();
}

View File

@@ -0,0 +1,20 @@
package org.kar.archidata.annotation.checker;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = CollectionItemNotNullValidator.class)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CollectionItemNotNull {
String message() default "Collection can not contain NULL item";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,31 @@
package org.kar.archidata.annotation.checker;
import java.util.Collection;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CollectionItemNotNullValidator implements ConstraintValidator<CollectionItemNotNull, Object> {
@Override
public void initialize(final CollectionItemNotNull annotation) {
// nothing to do...
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value == null) {
return true;
}
if (value instanceof final Collection<?> tmpCollection) {
final Object[] elements = tmpCollection.toArray();
for (final Object element : elements) {
if (element == null) {
return false;
//throw new InputException(baseName + fieldName + '[' + iii + ']', "Collection can not contain NULL item");
}
}
}
return true;
}
}

View File

@@ -0,0 +1,21 @@
package org.kar.archidata.annotation.checker;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = CollectionItemUniqueValidator.class)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CollectionItemUnique {
String message() default "Cannot insert multiple times the same elements";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,30 @@
package org.kar.archidata.annotation.checker;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CollectionItemUniqueValidator implements ConstraintValidator<CollectionItemUnique, Object> {
@Override
public void initialize(final CollectionItemUnique annotation) {
// nothing to do...
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value == null) {
return true;
}
if (value instanceof final Collection<?> tmpCollection) {
final Set<Object> uniqueValues = new HashSet<>(tmpCollection);
if (uniqueValues.size() != tmpCollection.size()) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,21 @@
package org.kar.archidata.annotation.checker;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = CollectionNotEmptyValidator.class)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface CollectionNotEmpty {
String message() default "Collection can not be empty";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,27 @@
package org.kar.archidata.annotation.checker;
import java.util.Collection;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class CollectionNotEmptyValidator implements ConstraintValidator<CollectionNotEmpty, Object> {
@Override
public void initialize(final CollectionNotEmpty annotation) {
// nothing to do...
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value == null) {
return true;
}
if (value instanceof final Collection<?> tmpCollection) {
if (tmpCollection.isEmpty()) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,20 @@
package org.kar.archidata.annotation.checker;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Constraint(validatedBy = ReadOnlyFieldValidator.class)
@Target({ ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnlyField {
String message() default "Field can not be set, it is a read-only field.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,20 @@
package org.kar.archidata.annotation.checker;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class ReadOnlyFieldValidator implements ConstraintValidator<ReadOnlyField, Object> {
@Override
public void initialize(final ReadOnlyField annotation) {
// nothing to do...
}
@Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
if (value != null) {
return false;
}
return true;
}
}

View File

@@ -1,4 +1,4 @@
package org.kar.archidata.annotation;
package org.kar.archidata.annotation.method;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -1,4 +1,4 @@
package org.kar.archidata.annotation;
package org.kar.archidata.annotation.method;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@@ -24,6 +24,7 @@ import javax.imageio.ImageIO;
import org.bson.types.ObjectId;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.apiGenerator.ApiInputOptional;
import org.kar.archidata.annotation.security.PermitTokenInURI;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition;
@@ -426,7 +427,7 @@ public class DataResource {
public Response retrieveDataFull(
@Context final SecurityContext sc,
@QueryParam(HttpHeaders.AUTHORIZATION) final String token,
@HeaderParam("Range") final String range,
@ApiInputOptional @HeaderParam("Range") final String range,
@PathParam("oid") final ObjectId oid,
@PathParam("name") final String name) throws Exception {
final GenericContext gc = (GenericContext) sc.getUserPrincipal();

View File

@@ -0,0 +1,47 @@
package org.kar.archidata.catcher;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.validation.ConstraintViolationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
public class ConstraintViolationExceptionCatcher implements ExceptionMapper<ConstraintViolationException> {
private static final Logger LOGGER = LoggerFactory.getLogger(ConstraintViolationExceptionCatcher.class);
@Override
public Response toResponse(final ConstraintViolationException exception) {
LOGGER.warn("Catch ConstraintViolationException: {}", exception.getLocalizedMessage());
final RestErrorResponse ret = build(exception);
LOGGER.error("Error OID={}", ret.oid);
return Response.status(Response.Status.BAD_REQUEST).entity(ret).type(MediaType.APPLICATION_JSON).build();
}
private RestErrorResponse build(final ConstraintViolationException exception) {
final List<RestInputError> inputError = new ArrayList<>();
for (final var cv : exception.getConstraintViolations()) {
if (cv == null) {
continue;
}
inputError.add(new RestInputError(cv.getPropertyPath(), cv.getMessage()));
}
Collections.sort(inputError, Comparator.comparing(RestInputError::getFullPath));
String errorType = "Multiple error on input";
if (inputError.size() == 0) {
errorType = "Constraint Violation";
} else if (inputError.size() == 1) {
errorType = "Error on input='" + inputError.get(0).path + "'";
}
return new RestErrorResponse(Response.Status.BAD_REQUEST, Instant.now().toString(), errorType,
exception.getMessage(), inputError);
}
}

View File

@@ -19,6 +19,7 @@ public class GenericCatcher {
rc.register(FailExceptionCatcher.class);
// generic Exception catcher
rc.register(ExceptionCatcher.class);
rc.register(ConstraintViolationExceptionCatcher.class);
}
}

View File

@@ -1,15 +1,20 @@
package org.kar.archidata.catcher;
import java.time.Instant;
import java.util.List;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.NoWriteSpecificMode;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.core.Response;
@NoWriteSpecificMode
@ApiGenerationMode
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RestErrorResponse {
public ObjectId oid = new ObjectId();
@NotNull
@@ -27,6 +32,19 @@ public class RestErrorResponse {
@Column(length = 0)
final public String statusMessage;
@Nullable
final public List<RestInputError> inputError;
public RestErrorResponse(final Response.Status status, final String time, final String error, final String message,
final List<RestInputError> inputError) {
this.time = time;
this.name = error;
this.message = message;
this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase();
this.inputError = inputError;
}
public RestErrorResponse(final Response.Status status, final String time, final String error,
final String message) {
this.time = time;
@@ -34,6 +52,7 @@ public class RestErrorResponse {
this.message = message;
this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase();
this.inputError = null;
}
public RestErrorResponse(final Response.Status status, final String error, final String message) {
@@ -42,6 +61,7 @@ public class RestErrorResponse {
this.message = message;
this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase();
this.inputError = null;
}
public RestErrorResponse(final Response.Status status) {
@@ -50,6 +70,7 @@ public class RestErrorResponse {
this.time = Instant.now().toString();
this.status = status.getStatusCode();
this.statusMessage = status.getReasonPhrase();
this.inputError = null;
}
}

View File

@@ -0,0 +1,53 @@
package org.kar.archidata.catcher;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.persistence.Column;
import jakarta.validation.Path;
import jakarta.validation.constraints.NotNull;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RestInputError {
private static Pattern PATTERN = Pattern.compile("^([^.]+)\\.([^.]+)(\\.(.*))?");
@Column(length = 0)
public String argument;
@Column(length = 0)
public String path;
@NotNull
@Column(length = 0)
public String message;
@Override
public String toString() {
return "RestInputError [argument=" + this.argument + ", path=" + this.path + ", message=" + this.message + "]";
}
public RestInputError() {}
public RestInputError(final Path path, final String message) {
final Matcher matcher = PATTERN.matcher(path.toString());
if (matcher.find()) {
//String firstPart = matcher.group(1); this is the request base element ==> not needed
this.argument = matcher.group(2);
this.path = matcher.group(4);
} else {
this.path = path.toString();
}
this.message = message;
}
public RestInputError(final String path, final String message) {
this.path = path;
this.message = message;
}
String getFullPath() {
if (this.path == null) {
return this.argument;
}
return this.argument + "." + this.path;
}
}

View File

@@ -0,0 +1,898 @@
package org.kar.archidata.checker;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.checker.CheckForeignKey;
import org.kar.archidata.annotation.checker.Checker;
import org.kar.archidata.annotation.checker.CollectionItemNotNull;
import org.kar.archidata.annotation.checker.CollectionItemUnique;
import org.kar.archidata.annotation.checker.CollectionNotEmpty;
import org.kar.archidata.dataAccess.DBAccess;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.options.CheckFunctionInterface;
import org.kar.archidata.dataAccess.options.CheckFunctionVoid;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.ConditionChecker;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.exception.InputException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.persistence.ManyToOne;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Size;
public class CheckJPA<T> implements CheckFunctionInterface {
private static final Logger LOGGER = LoggerFactory.getLogger(CheckJPA.class);
private final Class<?> clazz;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public interface CheckInterface<K> {
/** This function implementation is design to check if the updated class is valid of not for insertion
* @param ioDb Access on the Data-Base
* @param baseName Base of the name input that is displayed in exception generated.
* @param data The object that might be injected.
* @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. */
void check(
final DBAccess ioDb,
final String baseName,
final K data,
List<String> modifiedValue,
final QueryOptions options) throws Exception;
}
protected Map<String, List<CheckInterface<T>>> checking = null;
protected void add(final String field, final CheckInterface<T> checkFunction) throws DataAccessException {
if (!AnnotationTools.hasFieldsName(this.clazz, field)) {
LOGGER.error("Try to add a JPA Filter on an inexistant Field: '{}' not in {}", field,
AnnotationTools.getAllFieldsNames(this.clazz));
throw new DataAccessException("Try to add a JPA Filter on an inexistant Field: '" + field + "' not in "
+ AnnotationTools.getAllFieldsNames(this.clazz));
}
List<CheckInterface<T>> actions = this.checking.get(field);
if (actions == null) {
actions = new ArrayList<>();
this.checking.put(field, actions);
}
actions.add(checkFunction);
}
public CheckJPA(final Class<T> clazz) {
this.clazz = clazz;
}
public void initialize() throws Exception {
if (this.checking != null) {
return;
}
try {
this.checking = new HashMap<>();
// create Table:
final List<String> primaryKeys = new ArrayList<>();
for (final Field field : this.clazz.getFields()) {
final String fieldName = field.getName(); // AnnotationTools.getFieldName(field);
if (AnnotationTools.isPrimaryKey(field)) {
add(fieldName,
(
final DBAccess ioDb,
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)) {
add(fieldName,
(
final DBAccess ioDb,
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)) {
add(fieldName,
(
final DBAccess ioDb,
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();
if (type == Long.class || type == long.class) {
final DecimalMax maxValueDecimal = AnnotationTools.getConstraintsDecimalMax(field);
if (maxValueDecimal != null) {
final long maxValue = Long.parseLong(maxValueDecimal.value());
final boolean inclusive = maxValueDecimal.inclusive();
final String exceptionComment = "Value too height max=" + maxValue
+ (inclusive ? " (inclusive)" : " (exclusive)");
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (inclusive) {
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
} else if (elemTyped >= maxValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
final DecimalMin minValueDecimal = AnnotationTools.getConstraintsDecimalMin(field);
if (minValueDecimal != null) {
final long minValue = Long.parseLong(minValueDecimal.value());
final boolean inclusive = minValueDecimal.inclusive();
final String exceptionComment = "Value too low min=" + minValue
+ (inclusive ? " (inclusive)" : " (exclusive)");
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (inclusive) {
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
} else if (elemTyped <= minValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
final Max maxValue = AnnotationTools.getConstraintsMax(field);
if (maxValue != null) {
final Long maxValueTmp = maxValue.value();
final String exceptionComment = "Value too height max=" + maxValueTmp + " (inclusive)";
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (elemTyped > maxValueTmp) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
final Min minValue = AnnotationTools.getConstraintsMin(field);
if (minValue != null) {
final Long minValueTmp = minValue.value();
final String exceptionComment = "Value too low min=" + minValueTmp + " (inclusive)";
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Long elemTyped = (Long) elem;
if (elemTyped < minValueTmp) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
} else if (type == Integer.class || type == int.class) {
final DecimalMax maxValueDecimal = AnnotationTools.getConstraintsDecimalMax(field);
if (maxValueDecimal != null) {
final int maxValue = Integer.parseInt(maxValueDecimal.value());
final boolean inclusive = maxValueDecimal.inclusive();
final String exceptionComment = "Value too height max=" + maxValue
+ (inclusive ? " (inclusive)" : " (exclusive)");
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Integer elemTyped = (Integer) elem;
if (inclusive) {
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
} else if (elemTyped >= maxValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
final DecimalMin minValueDecimal = AnnotationTools.getConstraintsDecimalMin(field);
if (minValueDecimal != null) {
final int minValue = Integer.parseInt(minValueDecimal.value());
final boolean inclusive = minValueDecimal.inclusive();
final String exceptionComment = "Value too low min=" + minValue
+ (inclusive ? " (inclusive)" : " (exclusive)");
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Integer elemTyped = (Integer) elem;
if (inclusive) {
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
} else if (elemTyped <= minValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
final Max maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
final int maxValue = (int) maxValueRoot.value();
final String exceptionComment = "Value too height max=" + maxValue + " (inclusive)";
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Integer elemTyped = (Integer) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
final Min minValueRoot = AnnotationTools.getConstraintsMin(field);
if (minValueRoot != null) {
final int minValue = (int) minValueRoot.value();
final String exceptionComment = "Value too low min=" + minValue + " (inclusive)";
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Integer elemTyped = (Integer) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
} else if (type == Boolean.class || type == boolean.class) {
} else if (type == Float.class || type == float.class) {
final DecimalMax maxValueDecimal = AnnotationTools.getConstraintsDecimalMax(field);
if (maxValueDecimal != null) {
final float maxValue = Float.parseFloat(maxValueDecimal.value());
final boolean inclusive = maxValueDecimal.inclusive();
final String exceptionComment = "Value too height max=" + maxValue
+ (inclusive ? " (inclusive)" : " (exclusive)");
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Float elemTyped = (Float) elem;
if (inclusive) {
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
} else if (elemTyped >= maxValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
final DecimalMin minValueDecimal = AnnotationTools.getConstraintsDecimalMin(field);
if (minValueDecimal != null) {
final float minValue = Float.parseFloat(minValueDecimal.value());
final boolean inclusive = minValueDecimal.inclusive();
final String exceptionComment = "Value too low min=" + minValue
+ (inclusive ? " (inclusive)" : " (exclusive)");
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Float elemTyped = (Float) elem;
if (inclusive) {
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
} else if (elemTyped <= minValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
final Max maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
final float maxValue = maxValueRoot.value();
final String exceptionComment = "Value too height max=" + maxValue + " (inclusive)";
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Float elemTyped = (Float) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
final Min minValueRoot = AnnotationTools.getConstraintsMin(field);
if (minValueRoot != null) {
final float minValue = minValueRoot.value();
final String exceptionComment = "Value too low min=" + minValue + " (inclusive)";
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Float elemTyped = (Float) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
} else if (type == Double.class || type == double.class) {
final DecimalMax maxValueDecimal = AnnotationTools.getConstraintsDecimalMax(field);
if (maxValueDecimal != null) {
final double maxValue = Float.parseFloat(maxValueDecimal.value());
final boolean inclusive = maxValueDecimal.inclusive();
final String exceptionComment = "Value too height max=" + maxValue
+ (inclusive ? " (inclusive)" : " (exclusive)");
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Double elemTyped = (Double) elem;
if (inclusive) {
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
} else if (elemTyped >= maxValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
final DecimalMin minValueDecimal = AnnotationTools.getConstraintsDecimalMin(field);
if (minValueDecimal != null) {
final double minValue = Float.parseFloat(minValueDecimal.value());
final boolean inclusive = minValueDecimal.inclusive();
final String exceptionComment = "Value too low min=" + minValue
+ (inclusive ? " (inclusive)" : " (exclusive)");
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Double elemTyped = (Double) elem;
if (inclusive) {
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName,
"Value too Low min: " + minValue);
}
} else if (elemTyped <= minValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
final Max maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
final double maxValue = maxValueRoot.value();
final String exceptionComment = "Value too height max=" + maxValue + " (inclusive)";
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Double elemTyped = (Double) elem;
if (elemTyped > maxValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
final Min minValueRoot = AnnotationTools.getConstraintsMin(field);
if (minValueRoot != null) {
final double minValue = minValueRoot.value();
final String exceptionComment = "Value too low min=" + minValue + " (inclusive)";
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final Double elemTyped = (Double) elem;
if (elemTyped < minValue) {
throw new InputException(baseName + fieldName, exceptionComment);
}
});
}
} else if (type == Date.class || type == Timestamp.class) {
} else if (type == LocalDate.class) {
} else if (type == LocalTime.class) {
} else if (type == String.class) {
final Size limitSize = AnnotationTools.getConstraintsSize(field);
if (limitSize != null) {
add(fieldName,
(
final DBAccess ioDb,
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 (elemTyped.length() > limitSize.max()) {
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 jakarta.validation.constraints.Pattern patternString = AnnotationTools
.getConstraintsPattern(field);
if (patternString != null && patternString.regexp() != null) {
final Pattern pattern = Pattern.compile(patternString.regexp());
add(fieldName,
(
final DBAccess ioDb,
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 (constraints) must be '" + pattern
+ "'");
}
});
}
if (AnnotationTools.getConstraintsEmail(field) != null) {
final String emailPattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
final Pattern pattern = Pattern.compile(emailPattern);
add(fieldName,
(
final DBAccess ioDb,
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.isEnum()) {
// nothing to do.
}
final Checker[] checkers = AnnotationTools.getConstraintsCheckers(field);
if (checkers != null) {
for (final Checker checker : checkers) {
if (checker == null || checker.value() == CheckFunctionVoid.class) {
continue;
}
final CheckFunctionInterface checkerInstance = checker.value().getDeclaredConstructor()
.newInstance();
if (Collection.class.isAssignableFrom(field.getType())) {
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
// get the field of the specific element
final Object tmpData = field.get(data);
// It is not the objective of this element to check if it is authorize to set NULL
if (tmpData == null) {
return;
}
final Collection<?> tmpCollection = (Collection<?>) tmpData;
final Object[] elements = tmpCollection.toArray();
for (int iii = 0; iii < elements.length; iii++) {
if (elements[iii] != null) {
checkerInstance.check(ioDb, baseName + fieldName + '[' + iii + "].",
elements[iii], null, options);
}
}
});
} else {
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
// get the field of the specific element
final Object tmpData = field.get(data);
// It is not the objective of this element to check if it is authorize to set NULL
if (tmpData == null) {
return;
}
checkerInstance.check(ioDb, baseName + fieldName + '.', tmpData, null, options);
});
}
}
}
final CheckForeignKey foreighKey = AnnotationTools.get(field, CheckForeignKey.class);
if (foreighKey != null) {
if (Collection.class.isAssignableFrom(field.getType())) {
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
// get the field of the specific element
final Object tmpData = field.get(data);
// It is not the objective of this element to check if it is authorize to set NULL
if (tmpData == null) {
return;
}
final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class);
final Condition conditionCheck = condCheckers.isEmpty() ? null
: condCheckers.get(0).toCondition();
final Collection<?> tmpCollection = (Collection<?>) tmpData;
final Object[] elements = tmpCollection.toArray();
for (int iii = 0; iii < elements.length; iii++) {
if (elements[iii] == null) {
continue;
}
final Long count = ioDb.count(foreighKey.target(), elements[iii],
conditionCheck);
if (count != 1) {
throw new InputException(baseName + fieldName + '[' + iii + ']',
"Foreign-key does not exist in the DB:" + elements[iii]);
}
}
});
} else {
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object tmpData = field.get(data);
if (tmpData == null) {
return;
}
final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class);
final Condition conditionCheck = condCheckers.isEmpty() ? null
: condCheckers.get(0).toCondition();
final Long count = ioDb.count(foreighKey.target(), tmpData, conditionCheck);
if (count != 1) {
throw new InputException(baseName + fieldName,
"Foreign-key does not exist in the DB:" + tmpData);
}
});
}
}
// check if we really want to keep it ...
final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field);
if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) {
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class);
final Condition conditionCheck = condCheckers.isEmpty() ? null
: condCheckers.get(0).toCondition();
final long count = ioDb.count(annotationManyToOne.targetEntity(), elem, conditionCheck);
if (count == 0) {
throw new InputException(baseName + fieldName,
"Foreign element does not exist in the DB:" + elem);
}
});
}
final CollectionItemUnique collectionUnique = AnnotationTools.getCollectionItemUnique(field);
if (collectionUnique != null) {
if (!Collection.class.isAssignableFrom(field.getType())) {
throw new DataAccessException(
"Request @CollectionItemUnique on a non collection field: '" + fieldName + "'");
}
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object tmpData = field.get(data);
if (tmpData == null) {
return;
}
final Collection<?> tmpCollection = (Collection<?>) tmpData;
final Set<Object> uniqueValues = new HashSet<>(tmpCollection);
if (uniqueValues.size() != tmpCollection.size()) {
throw new InputException(baseName + fieldName,
"Cannot insert multiple times the same elements");
}
});
}
final CollectionItemNotNull collectionNotNull = AnnotationTools.getCollectionItemNotNull(field);
if (collectionNotNull != null) {
if (!Collection.class.isAssignableFrom(field.getType())) {
throw new DataAccessException(
"Request @CollectionItemNotNull on a non collection field: '" + fieldName + "'");
}
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object tmpData = field.get(data);
if (tmpData == null) {
return;
}
final Collection<?> tmpCollection = (Collection<?>) tmpData;
final Object[] elements = tmpCollection.toArray();
for (int iii = 0; iii < elements.length; iii++) {
if (elements[iii] == null) {
throw new InputException(baseName + fieldName + '[' + iii + ']',
"Collection can not contain NULL item");
}
}
});
}
final CollectionNotEmpty collectionNotEmpty = AnnotationTools.getCollectionNotEmpty(field);
if (collectionNotEmpty != null) {
if (!Collection.class.isAssignableFrom(field.getType())) {
throw new DataAccessException(
"Request @collectionNotEmpty on a non collection field: '" + fieldName + "'");
}
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object tmpData = field.get(data);
if (tmpData == null) {
return;
}
final Collection<?> tmpCollection = (Collection<?>) tmpData;
if (tmpCollection.isEmpty()) {
throw new InputException(baseName + fieldName, "Collection can not be empty");
}
});
}
// keep this is last ==> take more time...
if (AnnotationTools.isUnique(field)) {
// Create the request ...
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class);
Object other = null;
if (condCheckers.isEmpty()) {
other = ioDb.getWhere(this.clazz,
new Condition(new QueryCondition(fieldName, "==", field.get(data))));
} else {
other = ioDb.getWhere(this.clazz,
new Condition(new QueryCondition(fieldName, "==", field.get(data))),
condCheckers.get(0).toCondition());
}
if (other != null) {
throw new InputException(baseName + fieldName,
"The field is already exist in the DB");
}
});
}
}
} catch (final Exception ex) {
this.checking = null;
throw ex;
}
}
public void check(final Object data) throws Exception {
check(null, "", data, null, null);
}
public void check(final String baseName, final Object data) throws Exception {
check(null, baseName, data, null, null);
}
public void check(final DBAccess ioDb, final String baseName, final Object data) throws Exception {
check(ioDb, baseName, data, null, null);
}
public void check(final DBAccess ioDb, final String baseName, final Object data, final List<String> modifiedValue)
throws Exception {
check(ioDb, baseName, data, modifiedValue, null);
}
@Override
public void check(
final DBAccess ioDb,
final String baseName,
final Object data,
List<String> modifiedValue,
final QueryOptions options) throws Exception {
if (this.checking == null) {
initialize();
}
if (modifiedValue == null) {
modifiedValue = AnnotationTools.getAllFieldsNames(this.clazz);
}
if (!(this.clazz.isAssignableFrom(data.getClass()))) {
throw new DataAccessException("Incompatatyble type of Object" + data.getClass().getCanonicalName());
}
@SuppressWarnings("unchecked")
final T dataCasted = (T) data;
for (final String filter : modifiedValue) {
final List<CheckInterface<T>> actions = this.checking.get(filter);
if (actions == null) {
continue;
}
for (final CheckInterface<T> action : actions) {
action.check(ioDb, baseName, dataCasted, modifiedValue, options);
}
}
checkTyped(dataCasted, modifiedValue, options);
}
public void checkTyped(final T data, final List<String> modifiedValue, final QueryOptions options)
throws Exception {
// nothing to do ...
}
}

View File

@@ -18,6 +18,7 @@ import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.annotation.CreationTimestamp;
import org.kar.archidata.annotation.UpdateTimestamp;
import org.kar.archidata.dataAccess.addOnMongo.AddOnManyToOne;
@@ -27,6 +28,7 @@ import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.FilterValue;
import org.kar.archidata.dataAccess.options.Limit;
import org.kar.archidata.dataAccess.options.OptionSpecifyType;
import org.kar.archidata.dataAccess.options.OrderBy;
import org.kar.archidata.dataAccess.options.QueryOption;
import org.kar.archidata.db.DbIoMorphia;
@@ -128,7 +130,7 @@ public class DBAccessMorphia extends DBAccess {
return groups;
}
protected <T> void setValuedb(
protected <T> void setValueToDb(
final Class<?> type,
final T data,
final Field field,
@@ -136,7 +138,9 @@ public class DBAccessMorphia extends DBAccess {
final Document docSet,
final Document docUnSet) throws Exception {
if (field.get(data) == null) {
docUnSet.append(fieldName, "");
if (docUnSet != null) {
docUnSet.append(fieldName, "");
}
return;
}
if (type == long.class) {
@@ -151,7 +155,7 @@ public class DBAccessMorphia extends DBAccess {
docSet.append(fieldName, field.getFloat(data));
return;
}
if (type == Double.class) {
if (type == double.class) {
docSet.append(fieldName, field.getDouble(data));
return;
}
@@ -196,56 +200,40 @@ public class DBAccessMorphia extends DBAccess {
docSet.append(fieldName, tmp);
return;
}
if (type == ObjectId.class) {
docSet.append(fieldName, tmp);
return;
}
if (type == UUID.class) {
docSet.append(fieldName, tmp);
return;
}
if (type == Date.class) {
// TODO ...
/*
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final Timestamp sqlDate = java.sql.Timestamp.from(((Date) tmp).toInstant());
ps.setTimestamp(iii.value, sqlDate);
}*/
*/
}
if (type == Instant.class) {
/*
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final String sqlDate = ((Instant) tmp).toString();
ps.setString(iii.value, sqlDate);
}
*/
}
if (type == LocalDate.class) {
/*
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final java.sql.Date sqlDate = java.sql.Date.valueOf((LocalDate) tmp);
ps.setDate(iii.value, sqlDate);
}
*/
}
if (type == LocalTime.class) {
/*
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final java.sql.Time sqlDate = java.sql.Time.valueOf((LocalTime) tmp);
ps.setTime(iii.value, sqlDate);
}
*/
}
throw new DataAccessException("Unknown Field Type");
docSet.append(fieldName, tmp);
//throw new DataAccessException("Unknown Field Type");
}
public <T> void setValueFromDoc(
@@ -265,6 +253,11 @@ public class DBAccessMorphia extends DBAccess {
field.set(data, value);
return;
}
if (type == ObjectId.class) {
final ObjectId value = doc.get(fieldName, ObjectId.class);
field.set(data, value);
return;
}
if (type == Long.class || type == long.class) {
final Long value = doc.getLong(fieldName);
field.set(data, value);
@@ -349,8 +342,8 @@ public class DBAccessMorphia extends DBAccess {
final Object value = doc.get(fieldName, field.getType());
field.set(data, value);
} else {
final Object value = createObjectFromDocument(doc.get(fieldName, Document.class), field.getType(), null,
lazyCall);
final Object value = createObjectFromDocument(doc.get(fieldName, Document.class), field.getType(),
new QueryOptions(), lazyCall);
field.set(data, value);
}
@@ -459,32 +452,45 @@ public class DBAccessMorphia extends DBAccess {
Object uniqueId = null;
// real add in the BDD:
ObjectId insertedId = null;
final List<OptionSpecifyType> specificTypes = options.get(OptionSpecifyType.class);
try {
final MongoCollection<Document> collection = this.db.getDatastore().getDatabase()
.getCollection(collectionName);
final Document doc = new Document();
final Document docSet = new Document();
final Document docUnSet = new Document();
for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue;
}
final String tableFieldName = AnnotationTools.getFieldName(field, options).inTable();
final FieldName tableFieldName = AnnotationTools.getFieldName(field, options);
Object currentInsertValue = field.get(data);
if (AnnotationTools.isPrimaryKey(field)) {
primaryKeyField = field;
if (primaryKeyField.getType() == UUID.class) {
final UUID uuid = UuidUtils.nextUUID();
uniqueId = uuid;
doc.append(tableFieldName, uuid);
docSet.append(tableFieldName.inTable(), uuid);
continue;
} else if (primaryKeyField.getType() == Long.class || primaryKeyField.getType() == long.class) {
// By default the MongoDB does not manage the
final long id = getNextSequenceLongValue(collectionName, tableFieldName);
final long id = getNextSequenceLongValue(collectionName, tableFieldName.inTable());
uniqueId = id;
doc.append(tableFieldName, id);
docSet.append(tableFieldName.inTable(), id);
continue;
}
LOGGER.error("TODO: Manage the ID primary key for type: ");
LOGGER.error("TODO: Manage the ID primary key for type: {}=>{}", clazz.getCanonicalName(),
primaryKeyField.getType());
continue;
}
final boolean createTime = field.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
if (createTime) {
docSet.append(tableFieldName.inTable(), Date.from(Instant.now()));
continue;
}
final boolean updateTime = field.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
if (updateTime) {
docSet.append(tableFieldName.inTable(), Date.from(Instant.now()));
continue;
}
final DataAccessAddOn addOn = findAddOnforField(field);
@@ -495,19 +501,8 @@ public class DBAccessMorphia extends DBAccess {
}
continue;
}
final boolean createTime = field.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
if (createTime) {
doc.append(tableFieldName, Date.from(Instant.now()));
continue;
}
final boolean updateTime = field.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
if (updateTime) {
doc.append(tableFieldName, Date.from(Instant.now()));
continue;
}
if (currentInsertValue == null && !field.getClass().isPrimitive()) {
final DefaultValue[] defaultValue = field.getDeclaredAnnotationsByType(DefaultValue.class);
LOGGER.error("TODO: convert default value in the correct value for the DB...");
if (defaultValue.length == 0) {
continue;
} else {
@@ -518,9 +513,24 @@ public class DBAccessMorphia extends DBAccess {
currentInsertValue = convertDefaultField(value, field);
}
}
doc.append(tableFieldName, currentInsertValue);
// conversion table ...
//doc.append(tableFieldName, currentInsertValue);
if (addOn != null) {
addOn.insertData(this, field, data, options, docSet, docUnSet);
} else {
final Class<?> type = field.getType();
if (!type.isPrimitive()) {
if (field.get(data) == null) {
if (currentInsertValue != null) {
docSet.append(tableFieldName.inTable(), currentInsertValue);
}
continue;
}
}
setValueToDb(type, data, field, tableFieldName.inTable(), docSet, null);
}
}
final InsertOneResult result = collection.insertOne(doc);
final InsertOneResult result = collection.insertOne(docSet);
// Get the Object of inserted object:
insertedId = result.getInsertedId().asObjectId().getValue();
LOGGER.info("Document inserted with ID: " + insertedId);
@@ -587,14 +597,14 @@ public class DBAccessMorphia extends DBAccess {
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue;
}
final String fieldName = AnnotationTools.getFieldName(field, options).inTable();
final FieldName fieldName = AnnotationTools.getFieldName(field, options);
// update field is not conditioned by filter:
final boolean updateTime = field.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
if (updateTime) {
docSet.append(fieldName, Date.from(Instant.now()));
docSet.append(fieldName.inTable(), Date.from(Instant.now()));
continue;
}
if (!filterKey.getValues().contains(fieldName)) {
if (!filterKey.getValues().contains(fieldName.inStruct())) {
continue;
} else if (AnnotationTools.isGenericField(field)) {
continue;
@@ -624,12 +634,10 @@ public class DBAccessMorphia extends DBAccess {
continue;
}
}
setValuedb(type, data, field, fieldName, docSet, docUnSet);
setValueToDb(type, data, field, fieldName.inTable(), docSet, docUnSet);
}
}
// Do the query ...
final MongoCollection<Document> collection = this.db.getDatastore().getDatabase()
.getCollection(collectionName);
final Document actions = new Document();
@@ -652,7 +660,6 @@ public class DBAccessMorphia extends DBAccess {
}
public List<String> generateSelectField(final Class<?> clazz, final QueryOptions options) throws Exception {
// TODO: list of user select fields.
final boolean readAllfields = QueryOptions.readAllColomn(options);
final List<String> fieldsName = new ArrayList<>();
@@ -776,6 +783,8 @@ public class DBAccessMorphia extends DBAccess {
final Class<?> clazz,
final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception {
final List<OptionSpecifyType> specificTypes = options.get(OptionSpecifyType.class);
LOGGER.info("createObjectFromDocument: {}", clazz.getCanonicalName());
final boolean readAllfields = QueryOptions.readAllColomn(options);
// TODO: manage class that is defined inside a class ==> Not manage for now...
Object data = null;
@@ -790,23 +799,37 @@ public class DBAccessMorphia extends DBAccess {
"Can not find the default constructor for the class: " + clazz.getCanonicalName());
}
for (final Field elem : clazz.getFields()) {
LOGGER.info(" Inspect field: name='{}' type='{}'", elem.getName(), elem.getType().getCanonicalName());
// static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) {
LOGGER.info(" ==> static");
continue;
}
final DataAccessAddOn addOn = findAddOnforField(elem);
if (addOn != null && !addOn.canRetrieve(elem)) {
LOGGER.info(" ==> Can not retreive this field");
continue;
}
final boolean notRead = AnnotationTools.isDefaultNotRead(elem);
if (!readAllfields && notRead) {
LOGGER.info(" ==> Not read this element");
continue;
}
if (addOn != null) {
LOGGER.error("TODO: Add on not managed .6. ");
addOn.fillFromDoc(this, doc, elem, data, options, lazyCall);
} else {
setValueFromDoc(elem.getType(), data, elem, doc, lazyCall, options);
Class<?> type = elem.getType();
if (type == Object.class) {
for (final OptionSpecifyType specify : specificTypes) {
if (specify.name.equals(elem.getName())) {
type = specify.clazz;
LOGGER.info("Detect overwrite of typing var={} ... '{}' => '{}'", elem.getName(),
elem.getType().getCanonicalName(), specify.clazz.getCanonicalName());
break;
}
}
}
setValueFromDoc(type, data, elem, doc, lazyCall, options);
}
}
return data;

View File

@@ -158,7 +158,7 @@ public class DBAccessSQL extends DBAccess {
} else {
// TODO : Maybe connect with a temporary not specified connection interface to a db ...
final PreparedStatement ps = this.db.getConnection()
.prepareStatement("SHOW TABLES IN `" + this.db.getCongig().getDbName() + "`");
.prepareStatement("SHOW TABLES IN `" + this.db.getConfig().getDbName() + "`");
final ResultSet rs = ps.executeQuery();
// LOGGER.info("List all tables: equals? '{}'", name);
while (rs.next()) {
@@ -223,16 +223,35 @@ public class DBAccessSQL extends DBAccess {
return out;
}
public byte[][] splitIntoGroupsOf12Bytes(final byte[] input) {
final int inputLength = input.length;
final int numOfGroups = (inputLength + 11) / 12; // Calculate the number of groups needed
final byte[][] groups = new byte[numOfGroups][12];
for (int i = 0; i < numOfGroups; i++) {
final int startIndex = i * 12;
final int endIndex = Math.min(startIndex + 12, inputLength);
groups[i] = Arrays.copyOfRange(input, startIndex, endIndex);
}
return groups;
}
public List<ObjectId> getListOfRawOIDs(final ResultSet rs, final int iii) throws SQLException, DataAccessException {
final byte[] trackString = rs.getBytes(iii);
if (rs.wasNull()) {
return null;
}
final byte[][] elements = splitIntoGroupsOf16Bytes(trackString);
final byte[][] elements = splitIntoGroupsOf12Bytes(trackString);
final List<ObjectId> out = new ArrayList<>();
for (final byte[] elem : elements) {
final ObjectId tmp = new ObjectId(elem);
out.add(tmp);
try {
final ObjectId tmp = new ObjectId(elem);
out.add(tmp);
} catch (final IllegalArgumentException ex) {
ex.printStackTrace();
LOGGER.error("Fail to parse the OID element: {}", elem);
}
}
return out;
}
@@ -259,8 +278,81 @@ public class DBAccessSQL extends DBAccess {
final CountInOut iii,
final Field field,
final PreparedStatement ps) throws Exception {
if (type == ObjectId.class) {
final Object tmp = field.get(data);
if (type == long.class) {
ps.setLong(iii.value, field.getLong(data));
iii.inc();
return;
}
if (type == int.class) {
ps.setInt(iii.value, field.getInt(data));
iii.inc();
return;
}
if (type == float.class) {
ps.setFloat(iii.value, field.getFloat(data));
iii.inc();
return;
}
if (type == double.class) {
ps.setDouble(iii.value, field.getDouble(data));
iii.inc();
return;
}
if (type == boolean.class) {
ps.setBoolean(iii.value, field.getBoolean(data));
iii.inc();
return;
}
final Object tmp = field.get(data);
if (type.isEnum()) {
if (tmp == null) {
ps.setNull(iii.value, Types.VARCHAR);
} else {
ps.setString(iii.value, tmp.toString());
}
} else if (type == Long.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.BIGINT);
} else {
ps.setLong(iii.value, (Long) tmp);
}
} else if (type == Integer.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setInt(iii.value, (Integer) tmp);
}
} else if (type == Float.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.FLOAT);
} else {
ps.setFloat(iii.value, (Float) tmp);
}
} else if (type == Double.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.DOUBLE);
} else {
ps.setDouble(iii.value, (Double) tmp);
}
} else if (type == Boolean.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setBoolean(iii.value, (Boolean) tmp);
}
} else if (type == String.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.VARCHAR);
} else {
ps.setString(iii.value, (String) tmp);
}
} else if (type == Timestamp.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setTimestamp(iii.value, (Timestamp) tmp);
}
} else if (type == ObjectId.class) {
if (tmp == null) {
ps.setNull(iii.value, Types.BINARY);
} else {
@@ -268,67 +360,13 @@ public class DBAccessSQL extends DBAccess {
ps.setBytes(iii.value, dataByte);
}
} else if (type == UUID.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.BINARY);
} else {
final byte[] dataByte = UuidUtils.asBytes((UUID) tmp);
ps.setBytes(iii.value, dataByte);
}
} else if (type == Long.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.BIGINT);
} else {
ps.setLong(iii.value, (Long) tmp);
}
} else if (type == long.class) {
ps.setLong(iii.value, field.getLong(data));
} else if (type == Integer.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setInt(iii.value, (Integer) tmp);
}
} else if (type == int.class) {
ps.setInt(iii.value, field.getInt(data));
} else if (type == Float.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.FLOAT);
} else {
ps.setFloat(iii.value, (Float) tmp);
}
} else if (type == float.class) {
ps.setFloat(iii.value, field.getFloat(data));
} else if (type == Double.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.DOUBLE);
} else {
ps.setDouble(iii.value, (Double) tmp);
}
} else if (type == Double.class) {
ps.setDouble(iii.value, field.getDouble(data));
} else if (type == Boolean.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setBoolean(iii.value, (Boolean) tmp);
}
} else if (type == boolean.class) {
ps.setBoolean(iii.value, field.getBoolean(data));
} else if (type == Timestamp.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
ps.setTimestamp(iii.value, (Timestamp) tmp);
}
} else if (type == Date.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
@@ -336,7 +374,6 @@ public class DBAccessSQL extends DBAccess {
ps.setTimestamp(iii.value, sqlDate);
}
} else if (type == Instant.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
@@ -344,7 +381,6 @@ public class DBAccessSQL extends DBAccess {
ps.setString(iii.value, sqlDate);
}
} else if (type == LocalDate.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
@@ -352,27 +388,12 @@ public class DBAccessSQL extends DBAccess {
ps.setDate(iii.value, sqlDate);
}
} else if (type == LocalTime.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.INTEGER);
} else {
final java.sql.Time sqlDate = java.sql.Time.valueOf((LocalTime) tmp);
ps.setTime(iii.value, sqlDate);
}
} else if (type == String.class) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.VARCHAR);
} else {
ps.setString(iii.value, (String) tmp);
}
} else if (type.isEnum()) {
final Object tmp = field.get(data);
if (tmp == null) {
ps.setNull(iii.value, Types.VARCHAR);
} else {
ps.setString(iii.value, tmp.toString());
}
} else {
throw new DataAccessException("Unknown Field Type: " + type.getCanonicalName());
}
@@ -815,6 +836,9 @@ public class DBAccessSQL extends DBAccess {
@Override
@SuppressFBWarnings("SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING")
public <T> T insert(final T data, final QueryOption... option) throws Exception {
if (data == null) {
throw new DataAccessException("Try to check a null data ==> wrong API");
}
final Class<?> clazz = data.getClass();
final QueryOptions options = new QueryOptions(option);
@@ -1273,7 +1297,7 @@ public class DBAccessSQL extends DBAccess {
) throws Exception {
final boolean readAllfields = QueryOptions.readAllColomn(options);
final String tableName = AnnotationTools.getTableName(clazz, options);
final String primaryKey = AnnotationTools.getPrimaryKeyField(clazz).getName();
final String primaryKey = AnnotationTools.getFieldNameRaw(AnnotationTools.getPrimaryKeyField(clazz));
boolean firstField = true;
for (final Field elem : clazz.getFields()) {

View File

@@ -9,6 +9,7 @@ import org.kar.archidata.exception.DataAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.umd.cs.findbugs.annotations.Nullable;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.ws.rs.InternalServerErrorException;
@@ -189,6 +190,7 @@ public class DataAccess {
}
}
@Nullable
public static <T, ID_TYPE> T get(final Class<T> clazz, final ID_TYPE id, final QueryOption... options)
throws Exception {
try (DBAccess db = DBAccess.createInterface()) {

View File

@@ -1,174 +0,0 @@
package org.kar.archidata.dataAccess.addOnMongo;
import java.lang.reflect.Field;
import java.util.List;
import org.bson.Document;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.exception.DataAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonValue;
import jakarta.validation.constraints.NotNull;
public class AddOnDataJson implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnDataJson.class);
@Override
public Class<?> getAnnotationClass() {
return DataJson.class;
}
@Override
public String getSQLFieldType(final Field elem, final QueryOptions options) throws DataAccessException {
final String fieldName = AnnotationTools.getFieldName(elem, options).inTable();
return DataFactory.convertTypeInSQL(String.class, fieldName);
}
@Override
public boolean isCompatibleField(final Field elem) {
final DataJson decorators = elem.getDeclaredAnnotation(DataJson.class);
return decorators != null;
}
@Override
public void insertData(
final DBAccessMorphia ioDb,
final Field field,
final Object rootObject,
final QueryOptions options,
final Document docSet,
final Document docUnSet) throws Exception {
/*
final Object data = field.get(rootObject);
if (data == null) {
ps.setNull(iii.value, Types.VARCHAR);
}
final ObjectMapper objectMapper = ContextGenericTools.createObjectMapper();
final String dataString = objectMapper.writeValueAsString(data);
ps.setString(iii.value, dataString);
iii.inc();
*/
}
@Override
public boolean canInsert(final Field field) {
return true;
}
@Override
public boolean isInsertAsync(final Field field) {
return false;
}
@Override
public boolean canRetrieve(final Field field) {
return true;
}
@Override
public void generateQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
count.inc();
return;
}
@Override
public void fillFromDoc(
final DBAccessMorphia ioDb,
final Document doc,
final Field field,
final Object data,
final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception {
/*
final String fieldName = AnnotationTools.getFieldName(field);
if (!doc.containsKey(fieldName)) {
field.set(data, null);
return;
}
final String jsonData = rs.getString(count.value);
if (!rs.wasNull()) {
final ObjectMapper objectMapper = ContextGenericTools.createObjectMapper();
if (field.getType() == List.class) {
final ParameterizedType listType = (ParameterizedType) field.getGenericType();
final Class<?> listClass = (Class<?>) listType.getActualTypeArguments()[0];
if (listClass == Long.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Long>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == Float.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Float>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == Double.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Double>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == Integer.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Integer>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == Short.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Short>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == String.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<String>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
if (listClass == UUID.class) {
final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<UUID>>() {});// field.getType());
field.set(data, dataParsed);
return;
}
LOGGER.warn("Maybe fail to translate Model in datajson list: List<{}>", listClass.getCanonicalName());
}
final Object dataParsed = objectMapper.readValue(jsonData, field.getType());
field.set(data, dataParsed);
}
*/
}
@Override
public void createTables(
final String tableName,
final Field primaryField,
final Field field,
final StringBuilder mainTableBuilder,
final List<String> preActionList,
final List<String> postActionList,
final boolean createIfNotExist,
final boolean createDrop,
final int fieldId,
final QueryOptions options) throws Exception {
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, JsonValue.class, options);
}
}

View File

@@ -173,34 +173,6 @@ public class AddOnManyToMany implements DataAccessAddOn {
count.inc();
}
@Override
public void generateQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
if (field.getType() != List.class) {
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options);
}
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
if (decorators == null) {
return;
}
if (objectClass == decorators.targetEntity()) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options);
}
}
@Override
public void fillFromDoc(
final DBAccessMorphia ioDb,

View File

@@ -5,9 +5,9 @@ import java.util.List;
import java.util.UUID;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
@@ -16,7 +16,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.persistence.ManyToOne;
import jakarta.validation.constraints.NotNull;
public class AddOnManyToOne implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class);
@@ -52,7 +51,7 @@ public class AddOnManyToOne implements DataAccessAddOn {
final Document docUnSet) throws Exception {
final FieldName fieldName = AnnotationTools.getFieldName(field, options);
final Object data = field.get(rootObject);
if (field.get(data) == null) {
if (data == null) {
docUnSet.append(fieldName.inTable(), "");
return;
} else if (field.getType() == Long.class) {
@@ -70,6 +69,9 @@ public class AddOnManyToOne implements DataAccessAddOn {
} else if (field.getType() == UUID.class) {
final UUID dataTyped = (UUID) data;
docSet.append(fieldName.inTable(), dataTyped);
} else if (field.getType() == ObjectId.class) {
final ObjectId dataTyped = (ObjectId) data;
docSet.append(fieldName.inTable(), dataTyped);
} else {
final Field idField = AnnotationTools.getFieldOfId(field.getType());
final Object uid = idField.get(data);
@@ -84,7 +86,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
@Override
public boolean canInsert(final Field field) {
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) {
|| field.getType() == String.class || field.getType() == UUID.class
|| field.getType() == ObjectId.class) {
return true;
}
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@@ -103,7 +106,7 @@ public class AddOnManyToOne implements DataAccessAddOn {
public boolean canRetrieve(final Field field) {
final Class<?> classType = field.getType();
if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) {
|| classType == String.class || classType == UUID.class || classType == ObjectId.class) {
return true;
}
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@@ -113,36 +116,6 @@ public class AddOnManyToOne implements DataAccessAddOn {
return false;
}
@Override
public void generateQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) {
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
count.inc();
return;
}
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
if (field.getType() == decorators.targetEntity()) {
// no eager possible for no sql
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(name);
count.inc();
}
}
@Override
public void fillFromDoc(
final DBAccessMorphia ioDb,
@@ -159,7 +132,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
}
// local field to manage no remote object to retrieve.
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) {
|| field.getType() == String.class || field.getType() == UUID.class
|| field.getType() == ObjectId.class) {
ioDb.setValueFromDoc(field.getType(), data, field, doc, lazyCall, options);
return;
}
@@ -174,9 +148,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
final Class<?> remotePrimaryKeyType = remotePrimaryKeyField.getType();
if (remotePrimaryKeyType == Long.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final Long foreignKey = doc.getLong(fieldName);
final Long foreignKey = doc.getLong(fieldName.inTable());
if (foreignKey != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types ....
final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey);
@@ -189,9 +162,22 @@ public class AddOnManyToOne implements DataAccessAddOn {
}
} else if (remotePrimaryKeyType == UUID.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final UUID foreignKey = doc.get(fieldName, UUID.class);
final UUID foreignKey = doc.get(fieldName.inTable(), UUID.class);
if (foreignKey != null) {
final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types ....
final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey);
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
} else if (remotePrimaryKeyType == ObjectId.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final ObjectId foreignKey = doc.get(fieldName.inTable(), ObjectId.class);
if (foreignKey != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types ....
final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey);
@@ -221,7 +207,7 @@ public class AddOnManyToOne implements DataAccessAddOn {
final QueryOptions options) throws Exception {
final Class<?> classType = field.getType();
if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) {
|| classType == String.class || classType == UUID.class || classType == ObjectId.class) {
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, classType, options);
} else {

View File

@@ -2,62 +2,28 @@ package org.kar.archidata.dataAccess.addOnMongo;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import jakarta.validation.constraints.NotNull;
public class AddOnOneToMany implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnOneToMany.class);
static final String SEPARATOR_LONG = "-";
/** Convert the list if external id in a string '-' separated
* @param ids List of value (null are removed)
* @return '-' string separated */
protected static String getStringOfIds(final List<Long> ids) {
final List<Long> tmp = new ArrayList<>(ids);
return tmp.stream().map(String::valueOf).collect(Collectors.joining("-"));
}
/** extract a list of "-" separated element from a SQL input data.
* @param rs Result Set of the BDD
* @param iii Id in the result set
* @return The list of Long value
* @throws SQLException if an error is generated in the sql request. */
protected static List<Long> getListOfIds(final ResultSet rs, final int iii) throws SQLException {
final String trackString = rs.getString(iii);
if (rs.wasNull()) {
return null;
}
final List<Long> out = new ArrayList<>();
final String[] elements = trackString.split("-");
for (final String elem : elements) {
final Long tmp = Long.parseLong(elem);
out.add(tmp);
}
return out;
}
@Override
public Class<?> getAnnotationClass() {
return OneToMany.class;
@@ -98,7 +64,7 @@ public class AddOnOneToMany implements DataAccessAddOn {
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class) {
if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
return true;
}
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
@@ -111,104 +77,7 @@ public class AddOnOneToMany implements DataAccessAddOn {
return false;
}
public void generateConcatQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options,
final Class<?> targetEntity,
final String mappedBy) throws Exception {
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final String remoteTableName = AnnotationTools.getTableName(targetEntity);
final FieldName remoteTablePrimaryKeyName = AnnotationTools
.getFieldName(AnnotationTools.getPrimaryKeyField(targetEntity), options);
final String tmpRemoteVariable = "tmp_" + Integer.toString(count.value);
final String remoteDeletedFieldName = AnnotationTools.getDeletedFieldName(targetEntity);
querySelect.append(" (SELECT GROUP_CONCAT(");
querySelect.append(tmpRemoteVariable);
querySelect.append(".");
querySelect.append(remoteTablePrimaryKeyName.inTable());
querySelect.append(" ");
if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
querySelect.append(", ");
} else {
querySelect.append("SEPARATOR ");
}
querySelect.append("'");
if (objectClass == Long.class) {
querySelect.append(SEPARATOR_LONG);
}
querySelect.append("') FROM ");
querySelect.append(remoteTableName);
querySelect.append(" ");
querySelect.append(tmpRemoteVariable);
querySelect.append(" WHERE ");
if (remoteDeletedFieldName != null) {
querySelect.append(tmpRemoteVariable);
querySelect.append(".");
querySelect.append(remoteDeletedFieldName);
querySelect.append(" = false");
querySelect.append(" AND ");
}
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(primaryKey);
querySelect.append(" = ");
querySelect.append(tmpRemoteVariable);
querySelect.append(".");
querySelect.append(mappedBy);
querySelect.append(" ");
querySelect.append(") AS ");
querySelect.append(name);
querySelect.append(" ");
}
@Override
public void generateQuery(
@NotNull final String tableName,
@NotNull final String primaryKey,
@NotNull final Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull final String name,
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
if (field.getType() != List.class) {
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return;
}
// TODO: manage better the eager and lazy !!
if (objectClass == Long.class || objectClass == UUID.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options,
decorators.targetEntity(), decorators.mappedBy());
return;
}
if (objectClass == decorators.targetEntity()) {
if (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else {
// Force a copy of the primaryKey to permit the async retrieve of the data
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
querySelect.append(primaryKey);
querySelect.append(" AS tmp_");
querySelect.append(Integer.toString(count.value));
}
}
}
// in first implementation we did not keep the data in the 2 Objects, bun we will do it after to have a faster table interactions.
@Override
public void fillFromDoc(
final DBAccessMorphia ioDb,
@@ -222,11 +91,12 @@ public class AddOnOneToMany implements DataAccessAddOn {
return;
}
final String fieldName = AnnotationTools.getFieldName(field, options).inTable();
if (!doc.containsKey(fieldName)) {
field.set(data, null);
return;
}
final FieldName fieldName = AnnotationTools.getFieldName(field, options);
// in step 1 the fields are not stored in the local element
// if (!doc.containsKey(fieldName.inTable())) {
// field.set(data, null);
// return;
// }
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
@@ -234,62 +104,46 @@ public class AddOnOneToMany implements DataAccessAddOn {
if (decorators == null) {
return;
}
if (objectClass == Long.class || objectClass == UUID.class) {
final Object value = doc.get(fieldName, field.getType());
field.set(data, value);
if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
if (true) {
// DEVELOPMENT step 1 we search all the element in the list:
// get the curentObject primary key
final Field primaryField = AnnotationTools.getPrimaryKeyField(data.getClass());
final String primaryKeyName = AnnotationTools.getFieldNameRaw(primaryField);
final Object primaryKey = doc.get(primaryKeyName, primaryField.getType());
// get the remotes objects
final List<?> returnValue = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(decorators.mappedBy(), "=", primaryKey)));
// extract the primary key of the remote objects
final Field remotePrimaryField = AnnotationTools.getPrimaryKeyField(decorators.targetEntity());
final String remotePrimaryKeyName = AnnotationTools.getFieldNameRaw(remotePrimaryField);
final List<Object> listOfRemoteKeys = new ArrayList<>();
for (final var item : returnValue) {
listOfRemoteKeys.add(remotePrimaryField.get(item));
}
// inject in the current data field
if (listOfRemoteKeys.size() != 0) {
field.set(data, listOfRemoteKeys);
}
} else {
// DEVELOPMENT In step 2 this will work well:
final Object value = doc.get(fieldName.inTable(), field.getType());
field.set(data, value);
}
return;
}
if (objectClass == decorators.targetEntity()) {
Long parentIdTmp = null;
UUID parendUuidTmp = null;
try {
final Object value = doc.get(fieldName);
if (value instanceof final Long valueCasted) {
parentIdTmp = valueCasted;
} else if (value instanceof final UUID valueCasted) {
parendUuidTmp = valueCasted;
}
} catch (final Exception ex) {
LOGGER.error("fail to find the correct type... {}", ex.getMessage());
}
final Long parentId = parentIdTmp;
final UUID parendUuid = parendUuidTmp;
final String mappingKey = decorators.mappedBy();
// We get the parent ID ... ==> need to request the list of elements
if (objectClass == Long.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
return;
} else if (objectClass == UUID.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
return;
}
if (objectClass == decorators.targetEntity()) {
if (parentId != null) {
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parentId)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
} else if (parendUuid != null) {
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parendUuid)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
// Maybe in a second step we do not like this but this way is efficient too.
// get the curentObject primary key
final Field primaryField = AnnotationTools.getPrimaryKeyField(data.getClass());
final String primaryKeyName = AnnotationTools.getFieldNameRaw(primaryField);
final Object primaryKey = doc.get(primaryKeyName, primaryField.getType());
// get the remotes objects
final List<?> returnValue = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(decorators.mappedBy(), "=", primaryKey)));
// inject in the current data field
if (returnValue.size() != 0) {
field.set(data, returnValue);
}
}
}

View File

@@ -5,13 +5,10 @@ import java.sql.SQLException;
import java.util.List;
import org.bson.Document;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.LazyGetter;
import org.kar.archidata.dataAccess.QueryOptions;
import jakarta.validation.constraints.NotNull;
public interface DataAccessAddOn {
/** Get the Class of the declaration annotation
* @return The annotation class */
@@ -57,16 +54,6 @@ public interface DataAccessAddOn {
return false;
}
void generateQuery(
@NotNull String tableName,
@NotNull final String primaryKey,
@NotNull Field field,
@NotNull final StringBuilder querySelect,
@NotNull final StringBuilder query,
@NotNull String name,
@NotNull CountInOut count,
QueryOptions options) throws Exception;
// Return the number of colomn read
void fillFromDoc(
final DBAccessMorphia ioDb,

View File

@@ -16,7 +16,6 @@ import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.DBAccess;
import org.kar.archidata.dataAccess.DBAccessMorphia;
import org.kar.archidata.dataAccess.DBAccessSQL;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.LazyGetter;
@@ -220,68 +219,62 @@ public class AddOnDataJson implements DataAccessAddOn {
public static void addLink(
final DBAccess ioDb,
final Class<?> clazz,
final String columnId,
final Object id,
final String columnList,
final Object remoteKey) throws Exception {
final String clazzPrimaryKeyName,
final Object clazzPrimaryKeyValue,
final String fieldNameToUpdate,
final Object valueToAdd) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final QueryOptions options = new QueryOptions(new OverrideTableName(tableName),
new OptionSpecifyType("id", id.getClass()),
new OptionSpecifyType("covers", remoteKey.getClass(), true));
if (columnId != null && !columnId.equals("id")) {
options.add(new OptionRenameColumn("id", columnId));
new OptionSpecifyType("id", clazzPrimaryKeyValue.getClass()),
new OptionSpecifyType("covers", valueToAdd.getClass(), true));
if (clazzPrimaryKeyName != null && !clazzPrimaryKeyName.equals("id")) {
options.add(new OptionRenameColumn("id", clazzPrimaryKeyName));
}
if (columnList != null && !columnList.equals("covers")) {
options.add(new OptionRenameColumn("covers", columnList));
if (fieldNameToUpdate != null && !fieldNameToUpdate.equals("covers")) {
options.add(new OptionRenameColumn("covers", fieldNameToUpdate));
}
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, id, options.getAllArray());
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, clazzPrimaryKeyValue, options.getAllArray());
if (data.covers == null) {
data.covers = new ArrayList<>();
}
for (final Object elem : data.covers) {
if (elem.equals(remoteKey)) {
if (elem.equals(valueToAdd)) {
return;
}
}
data.covers.add(remoteKey);
data.covers.add(valueToAdd);
ioDb.update(data, data.id, List.of("covers"), options.getAllArray());
}
public static void removeLink(
final DBAccess ioDb,
final Class<?> clazz,
final String columnId,
final Object id,
final String columnList,
final Object remoteKey) throws Exception {
if (ioDb instanceof final DBAccessSQL daSQL) {
final String tableName = AnnotationTools.getTableName(clazz);
final QueryOptions options = new QueryOptions(new OverrideTableName(tableName),
new OptionSpecifyType("id", id.getClass()),
new OptionSpecifyType("covers", remoteKey.getClass(), true));
if (columnId != null && !columnId.equals("id")) {
options.add(new OptionRenameColumn("id", columnId));
}
if (columnList != null && !columnList.equals("covers")) {
options.add(new OptionRenameColumn("covers", columnList));
}
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, id, options.getAllArray());
if (data.covers == null) {
return;
}
final List<Object> newList = new ArrayList<>();
for (final Object elem : data.covers) {
if (elem.equals(remoteKey)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
ioDb.update(data, data.id, List.of("covers"), options.getAllArray());
} else if (ioDb instanceof final DBAccessMorphia dam) {
} else {
throw new DataAccessException("DataAccess Not managed");
final String clazzPrimaryKeyName,
final Object clazzPrimaryKeyValue,
final String fieldNameToUpdate,
final Object valueToRemove) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final QueryOptions options = new QueryOptions(new OverrideTableName(tableName),
new OptionSpecifyType("id", clazzPrimaryKeyValue.getClass()),
new OptionSpecifyType("covers", valueToRemove.getClass(), true));
if (clazzPrimaryKeyName != null && !clazzPrimaryKeyName.equals("id")) {
options.add(new OptionRenameColumn("id", clazzPrimaryKeyName));
}
if (fieldNameToUpdate != null && !fieldNameToUpdate.equals("covers")) {
options.add(new OptionRenameColumn("covers", fieldNameToUpdate));
}
final TableCoversGeneric data = ioDb.get(TableCoversGeneric.class, clazzPrimaryKeyValue, options.getAllArray());
if (data.covers == null) {
return;
}
final List<Object> newList = new ArrayList<>();
for (final Object elem : data.covers) {
if (elem.equals(valueToRemove)) {
continue;
}
newList.add(elem);
}
data.covers = newList;
ioDb.update(data, data.id, List.of("covers"), options.getAllArray());
}
}

View File

@@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.dataAccess.CountInOut;
@@ -72,7 +73,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class) {
if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
return true;
}
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
@@ -135,7 +136,11 @@ public class AddOnManyToMany implements DataAccessAddOn {
querySelect.append("'");
if (objectClass == Long.class) {
querySelect.append(SEPARATOR_LONG);
} else if (objectClass == UUID.class) {} else {
} else if (objectClass == UUID.class) {
// ???
} else if (objectClass == ObjectId.class) {
// ???
} else {
final Class<?> foreignKeyType = AnnotationTools.getPrimaryKeyField(objectClass).getType();
if (foreignKeyType == Long.class) {
querySelect.append(SEPARATOR_LONG);
@@ -193,7 +198,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
// TODO: manage better the eager and lazy !!
if (objectClass == Long.class || objectClass == UUID.class) {
if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options);
}
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
@@ -234,6 +239,11 @@ public class AddOnManyToMany implements DataAccessAddOn {
field.set(data, idList);
count.inc();
return;
} else if (objectClass == ObjectId.class) {
final List<ObjectId> idList = ioDb.getListOfRawOIDs(rs, count.value);
field.set(data, idList);
count.inc();
return;
}
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
if (decorators == null) {
@@ -285,6 +295,27 @@ public class AddOnManyToMany implements DataAccessAddOn {
};
lazyCall.add(lambda);
}
} else if (foreignKeyType == ObjectId.class) {
final List<ObjectId> idList = ioDb.getListOfRawOIDs(rs, count.value);
// field.set(data, idList);
count.inc();
if (idList != null && idList.size() > 0) {
final FieldName idField = AnnotationTools.getFieldName(AnnotationTools.getIdField(objectClass),
options);
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
final List<ObjectId> childs = new ArrayList<>(idList);
// TODO: update to have get with abstract types ....
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryInList<>(idField.inTable(), childs)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
}
}
}
@@ -309,9 +340,10 @@ public class AddOnManyToMany implements DataAccessAddOn {
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<"
+ objectClass.getCanonicalName() + ">");
if (objectClass != Long.class && objectClass != UUID.class && objectClass != ObjectId.class) {
throw new DataAccessException(
"Can not ManyToMany with other than List<Long> or List<UUID> or List<ObjectId> Model: List<"
+ objectClass.getCanonicalName() + ">");
}
final FieldName columnName = AnnotationTools.getFieldName(field, options);
final String linkTableName = generateLinkTableName(tableName, columnName.inTable());
@@ -348,19 +380,20 @@ public class AddOnManyToMany implements DataAccessAddOn {
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass != Long.class && objectClass != UUID.class) {
throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<"
+ objectClass.getCanonicalName() + ">");
if (objectClass != Long.class && objectClass != UUID.class && objectClass != ObjectId.class) {
throw new DataAccessException(
"Can not ManyToMany with other than List<Long> or List<UUID> or List<ObjectId> Model: List<"
+ objectClass.getCanonicalName() + ">");
}
final FieldName columnName = AnnotationTools.getFieldName(field, options);
final String linkTableName = generateLinkTableName(tableName, columnName.inTable());
@SuppressWarnings("unchecked")
final List<Long> dataCasted = (List<Long>) data;
final List<Object> dataCasted = (List<Object>) data;
if (dataCasted.size() == 0) {
return;
}
final List<LinkTableGeneric> insertElements = new ArrayList<>();
for (final Long remoteKey : dataCasted) {
for (final Object remoteKey : dataCasted) {
if (remoteKey == null) {
throw new DataAccessException("Try to insert remote key with null value");
}

View File

@@ -7,6 +7,7 @@ import java.sql.Types;
import java.util.List;
import java.util.UUID;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.dataAccess.CountInOut;
@@ -66,6 +67,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
ps.setNull(iii.value, Types.VARCHAR);
} else if (field.getType() == UUID.class) {
ps.setNull(iii.value, Types.BINARY);
} else if (field.getType() == ObjectId.class) {
ps.setNull(iii.value, Types.BINARY);
}
} else if (field.getType() == Long.class) {
final Long dataTyped = (Long) data;
@@ -84,6 +87,11 @@ public class AddOnManyToOne implements DataAccessAddOn {
LOGGER.info("Generate UUTD for DB: {}", dataTyped);
final byte[] dataByte = UuidUtils.asBytes(dataTyped);
ps.setBytes(iii.value, dataByte);
} else if (field.getType() == ObjectId.class) {
final ObjectId dataTyped = (ObjectId) data;
LOGGER.info("Generate ObjectId for DB: {}", dataTyped);
final byte[] dataByte = dataTyped.toByteArray();
ps.setBytes(iii.value, dataByte);
} else {
final Field idField = AnnotationTools.getFieldOfId(field.getType());
final Object uid = idField.get(data);
@@ -101,7 +109,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
@Override
public boolean canInsert(final Field field) {
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) {
|| field.getType() == String.class || field.getType() == UUID.class
|| field.getType() == ObjectId.class) {
return true;
}
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@@ -120,7 +129,7 @@ public class AddOnManyToOne implements DataAccessAddOn {
public boolean canRetrieve(final Field field) {
final Class<?> classType = field.getType();
if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) {
|| classType == String.class || classType == UUID.class || classType == ObjectId.class) {
return true;
}
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
@@ -141,7 +150,8 @@ public class AddOnManyToOne implements DataAccessAddOn {
@NotNull final CountInOut count,
final QueryOptions options) throws Exception {
if (field.getType() == Long.class || field.getType() == Integer.class || field.getType() == Short.class
|| field.getType() == String.class || field.getType() == UUID.class) {
|| field.getType() == String.class || field.getType() == UUID.class
|| field.getType() == ObjectId.class) {
querySelect.append(" ");
querySelect.append(tableName);
querySelect.append(".");
@@ -230,6 +240,15 @@ public class AddOnManyToOne implements DataAccessAddOn {
}
return;
}
if (field.getType() == ObjectId.class) {
final byte[] tmp = rs.getBytes(count.value);
count.inc();
if (!rs.wasNull()) {
final ObjectId foreignKey = new ObjectId(tmp);
field.set(data, foreignKey);
}
return;
}
final Class<?> objectClass = field.getType();
final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class);
if (decorators == null) {
@@ -279,6 +298,22 @@ public class AddOnManyToOne implements DataAccessAddOn {
};
lazyCall.add(lambda);
}
} else if (remotePrimaryKeyType == ObjectId.class) {
// here we have the field, the data and the the remote value ==> can create callback that generate the update of the value ...
final ObjectId foreignKey = ioDb.getListOfRawOID(rs, count.value);
count.inc();
if (foreignKey != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
// TODO: update to have get with abstract types ....
final Object foreignData = ioDb.get(decorators.targetEntity(), foreignKey);
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
}
}
}
@@ -298,7 +333,7 @@ public class AddOnManyToOne implements DataAccessAddOn {
final QueryOptions options) throws Exception {
final Class<?> classType = field.getType();
if (classType == Long.class || classType == Integer.class || classType == Short.class
|| classType == String.class || classType == UUID.class) {
|| classType == String.class || classType == UUID.class || classType == ObjectId.class) {
DataFactory.createTablesSpecificType(tableName, primaryField, field, mainTableBuilder, preActionList,
postActionList, createIfNotExist, createDrop, fieldId, classType, options);
} else {

View File

@@ -10,6 +10,7 @@ import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.dataAccess.CountInOut;
@@ -110,7 +111,7 @@ public class AddOnOneToMany implements DataAccessAddOn {
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
if (objectClass == Long.class || objectClass == UUID.class) {
if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
return true;
}
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
@@ -201,7 +202,7 @@ public class AddOnOneToMany implements DataAccessAddOn {
return;
}
// TODO: manage better the eager and lazy !!
if (objectClass == Long.class || objectClass == UUID.class) {
if (objectClass == Long.class || objectClass == UUID.class || objectClass == ObjectId.class) {
generateConcatQuery(tableName, primaryKey, field, querySelect, query, name, count, options,
decorators.targetEntity(), decorators.mappedBy());
return;
@@ -230,81 +231,115 @@ public class AddOnOneToMany implements DataAccessAddOn {
final CountInOut count,
final QueryOptions options,
final List<LazyGetter> lazyCall) throws Exception {
if (field.getType() != List.class) {
LOGGER.error("Can not OneToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return;
}
if (objectClass == Long.class) {
final List<Long> idList = ioDb.getListOfIds(rs, count.value, SEPARATOR_LONG);
field.set(data, idList);
count.inc();
return;
} else if (objectClass == UUID.class) {
final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
field.set(data, idList);
count.inc();
return;
}
if (objectClass == decorators.targetEntity()) {
Long parentIdTmp = null;
UUID parendUuidTmp = null;
try {
final String modelData = rs.getString(count.value);
parentIdTmp = Long.valueOf(modelData);
count.inc();
} catch (final NumberFormatException ex) {
final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
parendUuidTmp = idList.get(0);
count.inc();
try {
if (field.getType() != List.class) {
LOGGER.error("Can not OneToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0];
final OneToMany decorators = field.getDeclaredAnnotation(OneToMany.class);
if (decorators == null) {
return;
}
final Long parentId = parentIdTmp;
final UUID parendUuid = parendUuidTmp;
final String mappingKey = decorators.mappedBy();
// We get the parent ID ... ==> need to request the list of elements
if (objectClass == Long.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
final List<Long> idList = ioDb.getListOfIds(rs, count.value, SEPARATOR_LONG);
field.set(data, idList);
count.inc();
return;
} else if (objectClass == UUID.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
field.set(data, idList);
count.inc();
return;
} else if (objectClass == ObjectId.class) {
final List<ObjectId> idList = ioDb.getListOfRawOIDs(rs, count.value);
field.set(data, idList);
count.inc();
return;
}
if (objectClass == decorators.targetEntity()) {
if (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else if (parentId != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parentId)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
} else if (parendUuid != null) {
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parendUuid)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
final String destinationField = decorators.mappedBy();
final Field typeDestination = AnnotationTools.getFieldNamed(objectClass, destinationField);
final Class<?> destinationClass = typeDestination.getType();
Long parentIdTmp = null;
UUID parendUuidTmp = null;
ObjectId parendOidTmp = null;
if (destinationClass == Long.class) {
final String modelData = rs.getString(count.value);
parentIdTmp = Long.valueOf(modelData);
count.inc();
} else if (destinationClass == UUID.class) {
final List<UUID> idList = ioDb.getListOfRawUUIDs(rs, count.value);
parendUuidTmp = idList.get(0);
count.inc();
} else if (destinationClass == ObjectId.class) {
final List<ObjectId> idList = ioDb.getListOfRawOIDs(rs, count.value);
parendOidTmp = idList.get(0);
count.inc();
}
final Long parentId = parentIdTmp;
final UUID parendUuid = parendUuidTmp;
final ObjectId parendOid = parendOidTmp;
final String mappingKey = decorators.mappedBy();
// We get the parent ID ... ==> need to request the list of elements
if (objectClass == Long.class) {
LOGGER.error("Need to retreive all primary key of all elements.");
//field.set(data, idList);
return;
} else if (objectClass == UUID.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
return;
} else if (objectClass == ObjectId.class) {
LOGGER.error("Need to retreive all primary key of all elements");
//field.set(data, idList);
return;
}
if (objectClass == decorators.targetEntity()) {
if (decorators.fetch() == FetchType.EAGER) {
throw new DataAccessException("EAGER is not supported for list of element...");
} else if (parentId != null) {
// In the lazy mode, the request is done in asynchronous mode, they will be done after...
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parentId)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
} else if (parendUuid != null) {
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parendUuid)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
} else if (parendOid != null) {
final LazyGetter lambda = () -> {
@SuppressWarnings("unchecked")
final Object foreignData = ioDb.getsWhere(decorators.targetEntity(),
new Condition(new QueryCondition(mappingKey, "=", parendOid)));
if (foreignData == null) {
return;
}
field.set(data, foreignData);
};
lazyCall.add(lambda);
}
}
}
} catch (final Exception ex) {
ex.printStackTrace();
LOGGER.error("Fail to parse remote {}", ex.getMessage());
}
}

View File

@@ -9,7 +9,7 @@ import org.kar.archidata.dataAccess.QueryOptions;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public interface CheckFunctionInterface {
/** 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 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. */

View File

@@ -1,575 +0,0 @@
package org.kar.archidata.dataAccess.options;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Pattern;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.DBAccess;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.exception.InputException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonValue;
import jakarta.persistence.ManyToOne;
import jakarta.validation.constraints.Size;
public class CheckJPA<T> implements CheckFunctionInterface {
private static final Logger LOGGER = LoggerFactory.getLogger(CheckJPA.class);
private final Class<?> clazz;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public interface CheckInterface<K> {
/** This function implementation is design to check if the updated class is valid of not for insertion
* @param ioDb Access on the Data-Base
* @param baseName Base of the name input that is displayed in exception generated.
* @param data The object that might be injected.
* @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. */
void check(
final DBAccess ioDb,
final String baseName,
final K data,
List<String> modifiedValue,
final QueryOptions options) throws Exception;
}
protected Map<String, List<CheckInterface<T>>> checking = null;
protected void add(final String field, final CheckInterface<T> checkFunction) throws DataAccessException {
if (!AnnotationTools.hasFieldsName(this.clazz, field)) {
LOGGER.error("Try to add a JPA Filter on an inexistant Field: '{}' not in {}", field,
AnnotationTools.getAllFieldsNames(this.clazz));
throw new DataAccessException("Try to add a JPA Filter on an inexistant Field: '" + field + "' not in "
+ AnnotationTools.getAllFieldsNames(this.clazz));
}
List<CheckInterface<T>> actions = this.checking.get(field);
if (actions == null) {
actions = new ArrayList<>();
this.checking.put(field, actions);
}
actions.add(checkFunction);
}
public CheckJPA(final Class<T> clazz) {
this.clazz = clazz;
}
public void initialize() throws Exception {
if (this.checking != null) {
return;
}
try {
this.checking = new HashMap<>();
// create Table:
final List<String> primaryKeys = new ArrayList<>();
for (final Field field : this.clazz.getFields()) {
final String fieldName = field.getName(); // AnnotationTools.getFieldName(field);
if (AnnotationTools.isPrimaryKey(field)) {
add(fieldName,
(
final DBAccess ioDb,
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)) {
add(fieldName,
(
final DBAccess ioDb,
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)) {
add(fieldName,
(
final DBAccess ioDb,
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();
if (type == Long.class || type == long.class) {
final Long maxValue = AnnotationTools.getConstraintsMax(field);
if (maxValue != null) {
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
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);
if (minValue != null) {
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
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);
if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) {
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
return;
}
final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class);
final Condition conditionCheck = condCheckers.isEmpty() ? null
: 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) {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
final int maxValue = maxValueRoot.intValue();
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
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);
if (minValueRoot != null) {
final int minValue = minValueRoot.intValue();
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
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);
if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) {
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
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) {
final ManyToOne annotationManyToOne = AnnotationTools.getManyToOne(field);
if (annotationManyToOne != null && annotationManyToOne.targetEntity() != null) {
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
if (elem == null) {
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 == Float.class || type == float.class) {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
final float maxValue = maxValueRoot.floatValue();
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
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);
if (minValueRoot != null) {
final float minValue = minValueRoot.floatValue();
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
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) {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
final double maxValue = maxValueRoot.doubleValue();
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
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);
if (minValueRoot != null) {
final double minValue = minValueRoot.doubleValue();
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final Object elem = field.get(data);
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 == LocalDate.class) {
} else if (type == LocalTime.class) {
} else if (type == String.class) {
final int maxSizeString = AnnotationTools.getLimitSize(field);
if (maxSizeString > 0) {
add(fieldName,
(
final DBAccess ioDb,
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 (elemTyped.length() > maxSizeString) {
throw new InputException(baseName + fieldName,
"Too long size must be <= " + maxSizeString);
}
});
}
final Size limitSize = AnnotationTools.getConstraintsSize(field);
if (limitSize != null) {
add(fieldName,
(
final DBAccess ioDb,
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 (elemTyped.length() > limitSize.max()) {
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);
if (patternString != null) {
final Pattern pattern = Pattern.compile(patternString);
add(fieldName,
(
final DBAccess ioDb,
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 (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 DBAccess ioDb,
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) {
final DataJson jsonAnnotation = AnnotationTools.getDataJson(field);
if (jsonAnnotation != null && jsonAnnotation.checker() != CheckFunctionVoid.class) {
// Here if we have an error it crash at start and no new instance after creation...
final CheckFunctionInterface instance = jsonAnnotation.checker().getDeclaredConstructor()
.newInstance();
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
instance.checkAll(ioDb, baseName + fieldName + ".", field.get(data), options);
});
}
} else if (type.isEnum()) {
// nothing to do.
}
final DataJson dataJson = AnnotationTools.getDataJson(field);
if (dataJson != null && dataJson.checker() != null) {
final CheckFunctionInterface checkerInstance = dataJson.checker().getDeclaredConstructor()
.newInstance();
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
// get the field of the specific element
final Object tmpData = field.get(data);
checkerInstance.check(ioDb, baseName, tmpData, null, options);
});
}
// keep this is last ==> take more time...
if (AnnotationTools.isUnique(field)) {
// Create the request ...
add(fieldName,
(
final DBAccess ioDb,
final String baseName,
final T data,
final List<String> modifiedValue,
final QueryOptions options) -> {
final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class);
Object other = null;
if (condCheckers.isEmpty()) {
other = DataAccess.getWhere(this.clazz,
new Condition(new QueryCondition(fieldName, "==", field.get(data))));
} else {
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");
}
});
}
}
} catch (final Exception ex) {
this.checking = null;
throw ex;
}
}
public void check(final Object data) throws Exception {
check(null, "", data, null, null);
}
public void check(final String baseName, final Object data) throws Exception {
check(null, baseName, data, null, null);
}
public void check(final DBAccess ioDb, final String baseName, final Object data) throws Exception {
check(ioDb, baseName, data, null, null);
}
public void check(final DBAccess ioDb, final String baseName, final Object data, final List<String> modifiedValue)
throws Exception {
check(ioDb, baseName, data, modifiedValue, null);
}
@Override
public void check(
final DBAccess ioDb,
final String baseName,
final Object data,
List<String> modifiedValue,
final QueryOptions options) throws Exception {
if (this.checking == null) {
initialize();
}
if (modifiedValue == null) {
modifiedValue = AnnotationTools.getAllFieldsNames(this.clazz);
}
if (!(this.clazz.isAssignableFrom(data.getClass()))) {
throw new DataAccessException("Incompatatyble type of Object" + data.getClass().getCanonicalName());
}
@SuppressWarnings("unchecked")
final T dataCasted = (T) data;
for (final String filter : modifiedValue) {
final List<CheckInterface<T>> actions = this.checking.get(filter);
if (actions == null) {
continue;
}
for (final CheckInterface<T> action : actions) {
action.check(ioDb, baseName, dataCasted, modifiedValue, options);
}
}
checkTyped(dataCasted, modifiedValue, options);
}
public void checkTyped(final T data, final List<String> modifiedValue, final QueryOptions options)
throws Exception {
// nothing to do ...
}
}

View File

@@ -76,7 +76,7 @@ public class Condition extends QueryOption {
}
final List<Bson> filter = new ArrayList<>();
if (exclude_deleted && deletedFieldName != null) {
filter.add(Filters.eq(deletedFieldName, false));
filter.add(Filters.or(Filters.eq(deletedFieldName, false), Filters.exists(deletedFieldName, false)));
}
// Check if we have a condition to generate
if (this.condition != null) {

View File

@@ -76,7 +76,7 @@ public abstract class DbIo implements Closeable {
return this.config.equals(config);
}
public DbConfig getCongig() {
public DbConfig getConfig() {
return this.config;
}
}

View File

@@ -16,6 +16,6 @@ public class InputException extends Exception {
public InputException(final String variable, final String message) {
super(message);
this.missingVariable = variable;
this.status = Response.Status.NOT_ACCEPTABLE;
this.status = Response.Status.BAD_REQUEST;
}
}

View File

@@ -1,6 +1,9 @@
package org.kar.archidata.exception;
import java.util.List;
import org.bson.types.ObjectId;
import org.kar.archidata.catcher.RestInputError;
public class RESTErrorResponseException extends Exception {
private static final long serialVersionUID = 1L;
@@ -10,6 +13,7 @@ public class RESTErrorResponseException extends Exception {
public String message;
public int status;
public String statusMessage;
public List<RestInputError> inputError;
public RESTErrorResponseException() {
this.oid = new ObjectId();
@@ -18,16 +22,18 @@ public class RESTErrorResponseException extends Exception {
this.message = null;
this.status = 0;
this.statusMessage = null;
this.inputError = null;
}
public RESTErrorResponseException(final ObjectId oid, final String time, final String name, final String message,
final int status, final String statusMessage) {
final int status, final String statusMessage, final List<RestInputError> inputError) {
this.oid = oid;
this.time = time;
this.name = name;
this.message = message;
this.status = status;
this.statusMessage = statusMessage;
this.inputError = inputError;
}
@Override

View File

@@ -151,45 +151,6 @@ public class DotClassElement {
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();

View File

@@ -9,9 +9,13 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.kar.archidata.annotation.AnnotationTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.core.Context;
public class ApiModel {
static final Logger LOGGER = LoggerFactory.getLogger(ApiModel.class);
@@ -37,6 +41,8 @@ public class ApiModel {
public String name;
// list of all parameters (/{key}/...
public final Map<String, List<ClassModel>> parameters = new HashMap<>();
// list of all headers of the request (/{key}/...
public final Map<String, OptionalClassModel> headers = new HashMap<>();
// list of all query (?key...)
public final Map<String, List<ClassModel>> queries = new HashMap<>();
// when request multi-part, need to separate it.
@@ -153,11 +159,12 @@ public class ApiModel {
} else {
parameterModel.add(previousModel.add(parameterType));
}
final Context contextAnnotation = AnnotationTools.get(parameter, Context.class);
final HeaderParam headerParam = AnnotationTools.get(parameter, HeaderParam.class);
final String pathParam = ApiTool.apiAnnotationGetPathParam(parameter);
final String queryParam = ApiTool.apiAnnotationGetQueryParam(parameter);
final String formDataParam = ApiTool.apiAnnotationGetFormDataParam(parameter);
final boolean formDataParamOptional = ApiTool.apiAnnotationGetFormDataOptional(parameter);
final boolean apiInputOptional = ApiTool.apiAnnotationGetApiInputOptional(parameter);
if (queryParam != null) {
if (!this.queries.containsKey(queryParam)) {
this.queries.put(queryParam, parameterModel);
@@ -169,7 +176,13 @@ public class ApiModel {
} else if (formDataParam != null) {
if (!this.multiPartParameters.containsKey(formDataParam)) {
this.multiPartParameters.put(formDataParam,
new OptionalClassModel(parameterModel, formDataParamOptional));
new OptionalClassModel(parameterModel, apiInputOptional));
}
} else if (contextAnnotation != null) {
// out of scope parameters
} else if (headerParam != null) {
if (!this.headers.containsKey(headerParam.value())) {
this.headers.put(headerParam.value(), new OptionalClassModel(parameterModel, apiInputOptional));
}
} else {
this.unnamedElement.addAll(parameterModel);

View File

@@ -7,11 +7,11 @@ import java.util.Arrays;
import java.util.List;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.ARCHIVE;
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.apiGenerator.ApiInputOptional;
import org.kar.archidata.annotation.apiGenerator.ApiAsyncType;
import org.kar.archidata.annotation.apiGenerator.ApiTypeScriptProgress;
import org.kar.archidata.annotation.method.ARCHIVE;
import org.kar.archidata.annotation.method.RESTORE;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.ws.rs.Consumes;
@@ -53,7 +53,7 @@ public class ApiTool {
}
public static boolean apiAnnotationTypeScriptProgress(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(TypeScriptProgress.class);
final Annotation[] annotation = element.getDeclaredAnnotationsByType(ApiTypeScriptProgress.class);
if (annotation.length == 0) {
return false;
}
@@ -159,8 +159,8 @@ public class ApiTool {
return ((QueryParam) annotation[0]).value();
}
public static boolean apiAnnotationGetFormDataOptional(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(FormDataOptional.class);
public static boolean apiAnnotationGetApiInputOptional(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(ApiInputOptional.class);
if (annotation.length == 0) {
return false;
}
@@ -176,19 +176,19 @@ public class ApiTool {
}
public static Class<?>[] apiAnnotationGetAsyncType(final Parameter element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(AsyncType.class);
final Annotation[] annotation = element.getDeclaredAnnotationsByType(ApiAsyncType.class);
if (annotation.length == 0) {
return null;
}
return ((AsyncType) annotation[0]).value();
return ((ApiAsyncType) annotation[0]).value();
}
public static Class<?>[] apiAnnotationGetAsyncType(final Method element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(AsyncType.class);
final Annotation[] annotation = element.getDeclaredAnnotationsByType(ApiAsyncType.class);
if (annotation.length == 0) {
return null;
}
return ((AsyncType) annotation[0]).value();
return ((ApiAsyncType) annotation[0]).value();
}
public static List<String> apiAnnotationGetConsumes(final Method element) throws Exception {

View File

@@ -6,11 +6,14 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class ClassEnumModel extends ClassModel {
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import org.kar.archidata.tools.AnnotationCreator;
public class ClassEnumModel extends ClassModel {
protected ClassEnumModel(final Class<?> clazz) {
this.originClasses = clazz;
this.noWriteSpecificMode = true;
this.apiGenerationMode = AnnotationCreator.createAnnotation(ApiGenerationMode.class, "readable", true,
"creatable", false, "updatable", false);
}
@Override

View File

@@ -8,18 +8,24 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import org.kar.archidata.tools.AnnotationCreator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class ClassModel {
private static final Logger LOGGER = LoggerFactory.getLogger(ClassModel.class);
protected boolean analyzeDone = false;
protected Class<?> originClasses = null;
protected boolean noWriteSpecificMode = false;
protected ApiGenerationMode apiGenerationMode = AnnotationCreator.createAnnotation(ApiGenerationMode.class);
protected List<ClassModel> dependencyModels = new ArrayList<>();
public Class<?> getOriginClasses() {
return this.originClasses;
}
public boolean isNoWriteSpecificMode() {
return this.noWriteSpecificMode;
public ApiGenerationMode getApiGenerationMode() {
return this.apiGenerationMode;
}
protected boolean isCompatible(final Class<?> clazz) {
@@ -76,7 +82,15 @@ public abstract class ClassModel {
public abstract Set<ClassModel> getAlls();
public List<String> getReadOnlyField() {
public List<String> getReadOnlyFields() {
return List.of();
}
public List<String> getCreateFields() {
return List.of();
}
public List<String> getUpdateFields() {
return List.of();
}

View File

@@ -11,13 +11,22 @@ import java.util.List;
import java.util.Set;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.apiGenerator.ApiAccessLimitation;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.AnnotationCreator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
public class ClassObjectModel extends ClassModel {
@@ -25,6 +34,10 @@ public class ClassObjectModel extends ClassModel {
public ClassObjectModel(final Class<?> clazz) {
this.originClasses = clazz;
final ApiGenerationMode tmp = AnnotationTools.get(clazz, ApiGenerationMode.class);
if (tmp != null) {
this.apiGenerationMode = tmp;
}
}
@Override
@@ -61,44 +74,45 @@ public class ClassObjectModel extends ClassModel {
ClassModel model,
ClassModel linkClass, // link class when use remote ID (ex: list<UUID>)
String comment,
int sizeMin, // String SizeMin
int sizeMax, // String SizeMax
Long min, // number min value
Long max, // number max value
Boolean readOnly,
Size stringSize, // String Size
Min min, // number min value
Max max, // number max value
DecimalMin decimalMin,
DecimalMax decimalMax,
Pattern pattern,
Email email,
ApiAccessLimitation accessLimitation,
Boolean notNull,
Boolean columnNotNull,
Boolean nullable) {
public FieldProperty(final String name, final ClassModel model, final ClassModel linkClass,
final String comment, final int sizeMin, final int sizeMax, final Long min, final Long max,
final Boolean readOnly, final Boolean notNull, final Boolean columnNotNull, final Boolean nullable) {
final String comment, final Size stringSize, final Min min, final Max max, final DecimalMin decimalMin,
final DecimalMax decimalMax, final Pattern pattern, final Email email,
final ApiAccessLimitation accessLimitation, final Boolean notNull, final Boolean columnNotNull,
final Boolean nullable) {
this.name = name;
this.model = model;
this.linkClass = linkClass;
this.comment = comment;
this.sizeMin = sizeMin;
this.sizeMax = sizeMax;
this.stringSize = stringSize;
this.decimalMin = decimalMin;
this.decimalMax = decimalMax;
this.pattern = pattern;
this.email = email;
this.min = min;
this.max = max;
this.readOnly = readOnly;
if (accessLimitation == null) {
this.accessLimitation = AnnotationCreator.createAnnotation(ApiAccessLimitation.class);
} else {
this.accessLimitation = accessLimitation;
}
this.notNull = notNull;
this.columnNotNull = columnNotNull;
this.nullable = nullable;
}
private static int getStringMinSize(final Field field) throws DataAccessException {
final Size size = AnnotationTools.getConstraintsSize(field);
return size != null ? size.min() : 0;
}
private static int getStringMaxSize(final Field field) throws DataAccessException {
final Size size = AnnotationTools.getConstraintsSize(field);
final int colomnLimitSize = AnnotationTools.getLimitSize(field);
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) {
@@ -137,11 +151,14 @@ public class ClassObjectModel extends ClassModel {
ClassModel.getModel(field.getGenericType(), previous), //
getSubModelIfExist(field, previous), //
AnnotationTools.getSchemaDescription(field), //
getStringMinSize(field), //
getStringMaxSize(field), //
AnnotationTools.getConstraintsSize(field), //
AnnotationTools.getConstraintsMin(field), //
AnnotationTools.getConstraintsMax(field), //
AnnotationTools.getSchemaReadOnly(field), //
AnnotationTools.getConstraintsDecimalMin(field), //
AnnotationTools.getConstraintsDecimalMax(field), //
AnnotationTools.getConstraintsPattern(field), //
AnnotationTools.getConstraintsEmail(field), //
AnnotationTools.get(field, ApiAccessLimitation.class), //
AnnotationTools.getConstraintsNotNull(field), //
AnnotationTools.getColumnNotNull(field), //
AnnotationTools.getNullable(field));
@@ -187,7 +204,6 @@ public class ClassObjectModel extends ClassModel {
}
this.analyzeDone = true;
final Class<?> clazz = this.originClasses;
this.noWriteSpecificMode = AnnotationTools.getNoWriteSpecificMode(clazz);
this.isPrimitive = clazz.isPrimitive();
if (this.isPrimitive) {
return;
@@ -254,15 +270,43 @@ public class ClassObjectModel extends ClassModel {
}
@Override
public List<String> getReadOnlyField() {
public List<String> getReadOnlyFields() {
final List<String> out = new ArrayList<>();
for (final FieldProperty field : this.fields) {
if (field.readOnly()) {
if (!field.accessLimitation().creatable() && !field.accessLimitation().updatable()) {
out.add(field.name);
}
}
if (this.extendsClass != null) {
out.addAll(this.extendsClass.getReadOnlyField());
out.addAll(this.extendsClass.getReadOnlyFields());
}
return out;
}
@Override
public List<String> getCreateFields() {
final List<String> out = new ArrayList<>();
for (final FieldProperty field : this.fields) {
if (field.accessLimitation().creatable()) {
out.add(field.name);
}
}
if (this.extendsClass != null) {
out.addAll(this.extendsClass.getReadOnlyFields());
}
return out;
}
@Override
public List<String> getUpdateFields() {
final List<String> out = new ArrayList<>();
for (final FieldProperty field : this.fields) {
if (field.accessLimitation().updatable()) {
out.add(field.name);
}
}
if (this.extendsClass != null) {
out.addAll(this.extendsClass.getReadOnlyFields());
}
return out;
}

View File

@@ -45,7 +45,9 @@ public class TsApiGeneration {
final ClassEnumModel model,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final Set<ClassModel> importWrite) throws IOException {
final Set<ClassModel> importUpdate,
final Set<ClassModel> importCreate,
final boolean partialObject) throws IOException {
imports.add(model);
final TsClassElement tsModel = tsGroup.find(model);
return tsModel.tsTypeName;
@@ -55,34 +57,47 @@ public class TsApiGeneration {
final ClassObjectModel model,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final Set<ClassModel> importWrite) throws IOException {
final Set<ClassModel> importUpdate,
final Set<ClassModel> importCreate,
final boolean partialObject) throws IOException {
final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType != DefinedPosition.NATIVE) {
if (importWrite == null || tsModel.models.get(0).isNoWriteSpecificMode()) {
imports.add(model);
if (importCreate != null && tsModel.models.get(0).getApiGenerationMode().create()) {
importCreate.add(model);
} else if (importUpdate != null && tsModel.models.get(0).getApiGenerationMode().update()) {
importUpdate.add(model);
} else {
importWrite.add(model);
imports.add(model);
}
}
String out = tsModel.tsTypeName;
if (tsModel.nativeType != DefinedPosition.NORMAL) {
return tsModel.tsTypeName;
out = tsModel.tsTypeName;
} else if (importCreate != null && tsModel.models.get(0).getApiGenerationMode().create()) {
out = tsModel.tsTypeName + TsClassElement.MODEL_TYPE_CREATE;
} else if (importUpdate != null && tsModel.models.get(0).getApiGenerationMode().update()) {
out = tsModel.tsTypeName + TsClassElement.MODEL_TYPE_UPDATE;
}
if (importWrite != null && !tsModel.models.get(0).isNoWriteSpecificMode()) {
return tsModel.tsTypeName + "Write";
if (partialObject) {
return "Partial<" + out + ">";
}
return tsModel.tsTypeName;
return out;
}
public static String generateClassMapModelTypescript(
final ClassMapModel model,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final Set<ClassModel> importWrite) throws IOException {
final Set<ClassModel> importUpdate,
final Set<ClassModel> importCreate,
final boolean partialObject) throws IOException {
final StringBuilder out = new StringBuilder();
out.append("{[key: ");
out.append(generateClassModelTypescript(model.keyModel, tsGroup, imports, importWrite));
out.append(generateClassModelTypescript(model.keyModel, tsGroup, imports, importUpdate, importCreate,
partialObject));
out.append("]: ");
out.append(generateClassModelTypescript(model.valueModel, tsGroup, imports, importWrite));
out.append(generateClassModelTypescript(model.valueModel, tsGroup, imports, importUpdate, importCreate,
partialObject));
out.append(";}");
return out.toString();
}
@@ -91,9 +106,12 @@ public class TsApiGeneration {
final ClassListModel model,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final Set<ClassModel> importWrite) throws IOException {
final Set<ClassModel> importUpdate,
final Set<ClassModel> importCreate,
final boolean partialObject) throws IOException {
final StringBuilder out = new StringBuilder();
out.append(generateClassModelTypescript(model.valueModel, tsGroup, imports, importWrite));
out.append(generateClassModelTypescript(model.valueModel, tsGroup, imports, importUpdate, importCreate,
partialObject));
out.append("[]");
return out.toString();
}
@@ -102,18 +120,24 @@ public class TsApiGeneration {
final ClassModel model,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final Set<ClassModel> importWrite) throws IOException {
final Set<ClassModel> importUpdate,
final Set<ClassModel> importCreate,
final boolean partialObject) throws IOException {
if (model instanceof final ClassObjectModel objectModel) {
return generateClassObjectModelTypescript(objectModel, tsGroup, imports, importWrite);
return generateClassObjectModelTypescript(objectModel, tsGroup, imports, importUpdate, importCreate,
partialObject);
}
if (model instanceof final ClassListModel listModel) {
return generateClassListModelTypescript(listModel, tsGroup, imports, importWrite);
return generateClassListModelTypescript(listModel, tsGroup, imports, importUpdate, importCreate,
partialObject);
}
if (model instanceof final ClassMapModel mapModel) {
return generateClassMapModelTypescript(mapModel, tsGroup, imports, importWrite);
return generateClassMapModelTypescript(mapModel, tsGroup, imports, importUpdate, importCreate,
partialObject);
}
if (model instanceof final ClassEnumModel enumModel) {
return generateClassEnumModelTypescript(enumModel, tsGroup, imports, importWrite);
return generateClassEnumModelTypescript(enumModel, tsGroup, imports, importUpdate, importCreate,
partialObject);
}
throw new IOException("Impossible model:" + model);
}
@@ -122,7 +146,9 @@ public class TsApiGeneration {
final List<ClassModel> models,
final TsClassElementGroup tsGroup,
final Set<ClassModel> imports,
final Set<ClassModel> importWrite) throws IOException {
final Set<ClassModel> importUpdate,
final Set<ClassModel> importCreate,
final boolean partialObject) throws IOException {
if (models.size() == 0) {
return "void";
}
@@ -134,7 +160,8 @@ public class TsApiGeneration {
} else {
out.append(" | ");
}
final String data = generateClassModelTypescript(model, tsGroup, imports, importWrite);
final String data = generateClassModelTypescript(model, tsGroup, imports, importUpdate, importCreate,
partialObject);
out.append(data);
}
return out.toString();
@@ -159,7 +186,8 @@ public class TsApiGeneration {
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<ClassModel> updateImports = new HashSet<>();
final Set<ClassModel> createImports = new HashSet<>();
final Set<String> toolImports = new HashSet<>();
for (final ApiModel interfaceElement : element.interfaces) {
final List<String> consumes = interfaceElement.consumes;
@@ -172,6 +200,7 @@ public class TsApiGeneration {
data.append("\n\n");
data.append(returnComplexModel.replaceAll("(?m)^", "\t"));
for (final ClassModel elem : interfaceElement.returnTypes) {
// TODO maybe need to update this with the type of zod requested (like update, create ...
zodImports.addAll(elem.getDependencyGroupModels());
}
}
@@ -195,6 +224,9 @@ public class TsApiGeneration {
if (interfaceElement.unnamedElement.size() == 1 || interfaceElement.multiPartParameters.size() != 0) {
data.append("\n\t\t\tdata,");
}
if (!interfaceElement.headers.isEmpty()) {
data.append("\n\t\t\theaders,");
}
if (needGenerateProgress) {
data.append("\n\t\t\tcallbacks,");
}
@@ -207,7 +239,8 @@ public class TsApiGeneration {
data.append("\n\t\t\t");
data.append(queryEntry.getKey());
data.append("?: ");
data.append(generateClassModelsTypescript(queryEntry.getValue(), tsGroup, imports, null));
data.append(
generateClassModelsTypescript(queryEntry.getValue(), tsGroup, imports, null, null, false));
data.append(",");
}
data.append("\n\t\t},");
@@ -218,15 +251,27 @@ public class TsApiGeneration {
data.append("\n\t\t\t");
data.append(paramEntry.getKey());
data.append(": ");
data.append(generateClassModelsTypescript(paramEntry.getValue(), tsGroup, imports, null));
data.append(
generateClassModelsTypescript(paramEntry.getValue(), tsGroup, imports, null, null, false));
data.append(",");
}
data.append("\n\t\t},");
}
if (interfaceElement.unnamedElement.size() == 1) {
data.append("\n\t\tdata: ");
data.append(generateClassModelTypescript(interfaceElement.unnamedElement.get(0), tsGroup, imports,
writeImports));
if (interfaceElement.restTypeRequest == RestTypeRequest.POST) {
data.append(generateClassModelTypescript(interfaceElement.unnamedElement.get(0), tsGroup, imports,
null, createImports, false));
} else if (interfaceElement.restTypeRequest == RestTypeRequest.PUT) {
data.append(generateClassModelTypescript(interfaceElement.unnamedElement.get(0), tsGroup, imports,
updateImports, null, false));
} else if (interfaceElement.restTypeRequest == RestTypeRequest.PATCH) {
data.append(generateClassModelTypescript(interfaceElement.unnamedElement.get(0), tsGroup, imports,
updateImports, null, true));
} else {
data.append(generateClassModelTypescript(interfaceElement.unnamedElement.get(0), tsGroup, imports,
null, null, true));
}
data.append(",");
} else if (interfaceElement.multiPartParameters.size() != 0) {
data.append("\n\t\tdata: {");
@@ -238,8 +283,34 @@ public class TsApiGeneration {
data.append("?");
}
data.append(": ");
data.append(generateClassModelsTypescript(pathEntry.getValue().model(), tsGroup, imports,
writeImports));
if (interfaceElement.restTypeRequest == RestTypeRequest.POST) {
data.append(generateClassModelsTypescript(pathEntry.getValue().model(), tsGroup, imports, null,
createImports, false));
} else if (interfaceElement.restTypeRequest == RestTypeRequest.PUT) {
data.append(generateClassModelsTypescript(pathEntry.getValue().model(), tsGroup, imports,
updateImports, null, false));
} else if (interfaceElement.restTypeRequest == RestTypeRequest.PATCH) {
data.append(generateClassModelsTypescript(pathEntry.getValue().model(), tsGroup, imports,
updateImports, null, true));
} else {
data.append(generateClassModelsTypescript(pathEntry.getValue().model(), tsGroup, imports, null,
null, true));
}
data.append(",");
}
data.append("\n\t\t},");
}
if (!interfaceElement.headers.isEmpty()) {
data.append("\n\t\theaders?: {");
for (final Entry<String, OptionalClassModel> headerEntry : interfaceElement.headers.entrySet()) {
data.append("\n\t\t\t");
data.append(headerEntry.getKey());
if (headerEntry.getValue().optional()) {
data.append("?");
}
data.append(": ");
data.append(generateClassModelsTypescript(headerEntry.getValue().model(), tsGroup, imports, null,
null, false));
data.append(",");
}
data.append("\n\t\t},");
@@ -287,7 +358,7 @@ public class TsApiGeneration {
toolImports.add("RESTRequestJson");
} else {
final String returnType = generateClassModelsTypescript(interfaceElement.returnTypes, tsGroup, imports,
null);
null, null, false);
data.append(returnType);
data.append("> {");
if ("void".equals(returnType)) {
@@ -332,7 +403,7 @@ public class TsApiGeneration {
data.append("\n\t\t\t\taccept: produce,");
} else {
final String returnType = generateClassModelsTypescript(interfaceElement.returnTypes, tsGroup,
imports, null);
imports, null, null, false);
if (!"void".equals(returnType)) {
for (final String elem : produces) {
if (MediaType.APPLICATION_JSON.equals(elem)) {
@@ -360,6 +431,9 @@ public class TsApiGeneration {
if (needGenerateProgress) {
data.append("\n\t\t\tcallbacks,");
}
if (!interfaceElement.headers.isEmpty()) {
data.append("\n\t\t\theaders,");
}
data.append("\n\t\t}");
if (returnComplexModel != null) {
data.append(", is");
@@ -419,17 +493,30 @@ public class TsApiGeneration {
if (tsModel.nativeType == DefinedPosition.NATIVE) {
continue;
}
if (!tsModel.models.get(0).getApiGenerationMode().read()) {
continue;
}
finalImportSet.add("Zod" + tsModel.tsTypeName);
}
for (final ClassModel model : writeImports) {
for (final ClassModel model : updateImports) {
final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType != DefinedPosition.NORMAL) {
continue;
}
if (tsModel.models.get(0).isNoWriteSpecificMode()) {
if (!tsModel.models.get(0).getApiGenerationMode().update()) {
continue;
}
finalImportSet.add(tsModel.tsTypeName + "Write");
finalImportSet.add(tsModel.tsTypeName + TsClassElement.MODEL_TYPE_UPDATE);
}
for (final ClassModel model : createImports) {
final TsClassElement tsModel = tsGroup.find(model);
if (tsModel.nativeType != DefinedPosition.NORMAL) {
continue;
}
if (!tsModel.models.get(0).getApiGenerationMode().create()) {
continue;
}
finalImportSet.add(tsModel.tsTypeName + TsClassElement.MODEL_TYPE_CREATE);
}
if (finalImportSet.size() != 0) {

View File

@@ -27,6 +27,9 @@ public class TsClassElement {
NORMAL // Normal Object to interpret.
}
public static final String MODEL_TYPE_UPDATE = "Update";
public static final String MODEL_TYPE_CREATE = "Create";
public List<ClassModel> models;
public String zodName;
public String tsTypeName;
@@ -138,7 +141,7 @@ public class TsClassElement {
out.append(this.tsTypeName);
out.append(");\n");
}
out.append(generateExportCheckFunctionWrite(""));
out.append(generateExportCheckFunctionAppended(""));
return out.toString();
}
@@ -168,9 +171,9 @@ public class TsClassElement {
return out.toString();
}
private String generateExportCheckFunctionWrite(final String writeString) {
return generateExportCheckFunction(this.tsCheckType + writeString, this.tsTypeName + writeString,
this.zodName + writeString);
private String generateExportCheckFunctionAppended(final String appendString) {
return generateExportCheckFunction(this.tsCheckType + appendString, this.tsTypeName + appendString,
this.zodName + appendString);
}
public String generateImports(final List<ClassModel> depModels, final TsClassElementGroup tsGroup)
@@ -180,11 +183,23 @@ public class TsClassElement {
final TsClassElement tsModel = tsGroup.find(depModel);
if (tsModel.nativeType != DefinedPosition.NATIVE) {
out.append("import {");
out.append(tsModel.zodName);
if (tsModel.nativeType == DefinedPosition.NORMAL && !(tsModel.models.get(0).isNoWriteSpecificMode())) {
if (tsModel.nativeType != DefinedPosition.NORMAL
|| tsModel.models.get(0).getApiGenerationMode().read()) {
out.append(tsModel.zodName);
}
if (tsModel.nativeType == DefinedPosition.NORMAL
&& tsModel.models.get(0).getApiGenerationMode().update()) {
out.append(", ");
out.append(tsModel.zodName);
out.append("Write ");
out.append(MODEL_TYPE_UPDATE);
out.append(" ");
}
if (tsModel.nativeType == DefinedPosition.NORMAL
&& tsModel.models.get(0).getApiGenerationMode().create()) {
out.append(", ");
out.append(tsModel.zodName);
out.append(MODEL_TYPE_CREATE);
out.append(" ");
}
out.append("} from \"./");
out.append(tsModel.fileName);
@@ -259,28 +274,61 @@ public class TsClassElement {
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.stringSize() != null) {
if (field.stringSize().min() > 0) {
// A string size can not be lower at 0
builder.append(".min(");
builder.append(field.stringSize().min());
builder.append(")");
}
if (field.stringSize().max() != Integer.MAX_VALUE) {
builder.append(".max(");
builder.append(field.stringSize().max());
builder.append(")");
}
}
if (field.sizeMax() > 0) {
builder.append(".max(");
builder.append(field.sizeMax());
/*Must be tested before
if (field.pattern() != null) {
builder.append(".regex((");
builder.append(field.pattern().regexp());
builder.append(")");
}
}*/
/*Must be tested before
if (field.email() != null) {
builder.append(".regex((");
builder.append(field.email().regexp());
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());
if (field.min() != null) {
builder.append(".gte(");
builder.append(field.min().value());
builder.append(")");
}
if (field.max() != null && field.max() > 0) {
builder.append(".max(");
builder.append(field.max());
if (field.max() != null) {
builder.append(".lte(");
builder.append(field.max().value());
builder.append(")");
}
if (field.decimalMax() != null) {
if (field.decimalMax().inclusive()) {
builder.append(".lte(");
} else {
builder.append(".lt(");
}
builder.append(field.decimalMax().value());
builder.append(")");
}
if (field.decimalMin() != null) {
if (field.decimalMin().inclusive()) {
builder.append(".gte(");
} else {
builder.append(".gt(");
}
builder.append(field.decimalMin().value());
builder.append(")");
}
}
@@ -288,7 +336,7 @@ public class TsClassElement {
}
public String readOnlyZod(final FieldProperty field) {
if (field.readOnly()) {
if (!field.accessLimitation().creatable() && !field.accessLimitation().updatable()) {
return ".readonly()";
}
return "";
@@ -310,26 +358,43 @@ public class TsClassElement {
public String generateObject(final ClassObjectModel model, final TsClassElementGroup tsGroup) throws IOException {
final StringBuilder out = new StringBuilder();
out.append(getBaseHeader());
out.append(generateImports(model.getDependencyModels(), tsGroup));
out.append("\n");
// ------------------------------------------------------------------------
// -- Generate read mode
// ------------------------------------------------------------------------
if (model.getApiGenerationMode().read()) {
out.append(generateObjectRead(model, tsGroup));
}
if (model.getApiGenerationMode().update()) {
out.append(generateObjectUpdate(model, tsGroup));
}
if (model.getApiGenerationMode().create()) {
out.append(generateObjectCreate(model, tsGroup));
}
return out.toString();
}
public String generateObjectRead(final ClassObjectModel model, final TsClassElementGroup tsGroup)
throws IOException {
final StringBuilder out = new StringBuilder();
out.append(generateComment(model));
out.append("export const ");
out.append(this.zodName);
out.append(" = ");
// Check if the object is empty:
final boolean isEmpty = model.getFields().size() == 0;
if (model.getExtendsClass() != null) {
final ClassModel parentClass = model.getExtendsClass();
final TsClassElement tsParentModel = tsGroup.find(parentClass);
out.append(tsParentModel.zodName);
out.append(".extend({");
if (!isEmpty) {
out.append(".extend({\n");
}
} else {
out.append("zod.object({");
out.append("zod.object({\n");
}
out.append("\n");
for (final FieldProperty field : model.getFields()) {
final ClassModel fieldModel = field.model();
if (field.comment() != null) {
@@ -356,89 +421,138 @@ public class TsClassElement {
out.append(optionalTypeZod(field));
out.append(",\n");
}
final List<String> omitField = model.getReadOnlyField();
out.append("\n});\n");
out.append(generateZodInfer(this.tsTypeName, this.zodName));
out.append(generateExportCheckFunctionWrite(""));
// check if we need to generate write mode :
if (!model.isNoWriteSpecificMode()) {
// ------------------------------------------------------------------------
// -- Generate write mode
// ------------------------------------------------------------------------
//out.append(generateComment(model));
out.append("export const ");
out.append(this.zodName);
out.append("Write = ");
if (model.getExtendsClass() != null) {
final ClassModel parentClass = model.getExtendsClass();
final TsClassElement tsParentModel = tsGroup.find(parentClass);
out.append(tsParentModel.zodName);
out.append("Write");
out.append(".extend({");
} else {
out.append("zod.object({");
}
out.append("\n");
for (final FieldProperty field : model.getFields()) {
// remove all readOnly field
if (field.readOnly()) {
continue;
}
final ClassModel fieldModel = field.model();
if (field.comment() != null) {
out.append("\t/**\n");
out.append("\t * ");
out.append(field.comment());
out.append("\n\t */\n");
}
out.append("\t");
out.append(field.name());
out.append(": ");
if (fieldModel instanceof ClassEnumModel || fieldModel instanceof ClassObjectModel) {
final TsClassElement tsFieldModel = tsGroup.find(fieldModel);
out.append(tsFieldModel.zodName);
} else if (fieldModel instanceof final ClassListModel fieldListModel) {
final String data = generateTsList(fieldListModel, tsGroup);
out.append(data);
} else if (fieldModel instanceof final ClassMapModel fieldMapModel) {
final String data = generateTsMap(fieldMapModel, tsGroup);
out.append(data);
}
out.append(maxSizeZod(field));
out.append(optionalWriteTypeZod(field));
// all write field are optional
if (field.model() instanceof final ClassObjectModel plop) {
if (!plop.isPrimitive()) {
out.append(".optional()");
}
} else {
out.append(".optional()");
}
out.append(",\n");
}
if (model.getExtendsClass() != null && isEmpty) {
out.append(";\n");
} else {
out.append("\n});\n");
out.append(generateZodInfer(this.tsTypeName + "Write", this.zodName + "Write"));
// Check only the input value ==> no need of the output
out.append(generateExportCheckFunctionWrite("Write"));
// Generate the Write Type associated.
/*
out.append("\nexport const ");
out.append(this.zodName);
out.append("Write = ");
out.append(this.zodName);
if (omitField.size() != 0) {
out.append(".omit({\n");
for (final String elem : omitField) {
out.append("\t");
out.append(elem);
out.append(": true,\n");
}
out.append("\n})");
}
out.append(".partial();\n");
*/
}
out.append(generateZodInfer(this.tsTypeName, this.zodName));
out.append(generateExportCheckFunctionAppended(""));
return out.toString();
}
public String generateObjectUpdate(final ClassObjectModel model, final TsClassElementGroup tsGroup)
throws IOException {
final StringBuilder out = new StringBuilder();
final String modeleType = MODEL_TYPE_UPDATE;
out.append("export const ");
out.append(this.zodName);
out.append(modeleType);
out.append(" = ");
// Check if at minimum One fiend is updatable to generate the local object
final boolean isEmpty = model.getFields().stream().filter(field -> field.accessLimitation().updatable())
.count() == 0;
if (model.getExtendsClass() != null) {
final ClassModel parentClass = model.getExtendsClass();
final TsClassElement tsParentModel = tsGroup.find(parentClass);
out.append(tsParentModel.zodName);
out.append(modeleType);
if (!isEmpty) {
out.append(".extend({\n");
}
} else {
out.append("zod.object({\n");
}
for (final FieldProperty field : model.getFields()) {
// remove all readOnly field
if (!field.accessLimitation().updatable()) {
continue;
}
final ClassModel fieldModel = field.model();
if (field.comment() != null) {
out.append("\t/**\n");
out.append("\t * ");
out.append(field.comment());
out.append("\n\t */\n");
}
out.append("\t");
out.append(field.name());
out.append(": ");
if (fieldModel instanceof ClassEnumModel || fieldModel instanceof ClassObjectModel) {
final TsClassElement tsFieldModel = tsGroup.find(fieldModel);
out.append(tsFieldModel.zodName);
} else if (fieldModel instanceof final ClassListModel fieldListModel) {
final String data = generateTsList(fieldListModel, tsGroup);
out.append(data);
} else if (fieldModel instanceof final ClassMapModel fieldMapModel) {
final String data = generateTsMap(fieldMapModel, tsGroup);
out.append(data);
}
out.append(maxSizeZod(field));
out.append(optionalWriteTypeZod(field));
out.append(optionalTypeZod(field));
out.append(",\n");
}
if (model.getExtendsClass() != null && isEmpty) {
out.append(";\n");
} else {
out.append("\n});\n");
}
out.append(generateZodInfer(this.tsTypeName + modeleType, this.zodName + modeleType));
// Check only the input value ==> no need of the output
out.append(generateExportCheckFunctionAppended(modeleType));
return out.toString();
}
public String generateObjectCreate(final ClassObjectModel model, final TsClassElementGroup tsGroup)
throws IOException {
final StringBuilder out = new StringBuilder();
final String modeleType = MODEL_TYPE_CREATE;
out.append("export const ");
out.append(this.zodName);
out.append(modeleType);
out.append(" = ");
final boolean isEmpty = model.getFields().stream().filter(field -> field.accessLimitation().creatable())
.count() == 0;
if (model.getExtendsClass() != null) {
final ClassModel parentClass = model.getExtendsClass();
final TsClassElement tsParentModel = tsGroup.find(parentClass);
out.append(tsParentModel.zodName);
out.append(modeleType);
if (!isEmpty) {
out.append(".extend({\n");
}
} else {
out.append("zod.object({\n");
}
for (final FieldProperty field : model.getFields()) {
// remove all readOnly field
if (!field.accessLimitation().creatable()) {
continue;
}
final ClassModel fieldModel = field.model();
if (field.comment() != null) {
out.append("\t/**\n");
out.append("\t * ");
out.append(field.comment());
out.append("\n\t */\n");
}
out.append("\t");
out.append(field.name());
out.append(": ");
if (fieldModel instanceof ClassEnumModel || fieldModel instanceof ClassObjectModel) {
final TsClassElement tsFieldModel = tsGroup.find(fieldModel);
out.append(tsFieldModel.zodName);
} else if (fieldModel instanceof final ClassListModel fieldListModel) {
final String data = generateTsList(fieldListModel, tsGroup);
out.append(data);
} else if (fieldModel instanceof final ClassMapModel fieldMapModel) {
final String data = generateTsMap(fieldMapModel, tsGroup);
out.append(data);
}
out.append(maxSizeZod(field));
out.append(optionalWriteTypeZod(field));
out.append(optionalTypeZod(field));
out.append(",\n");
}
if (model.getExtendsClass() != null && isEmpty) {
out.append(";\n");
} else {
out.append("\n});\n");
}
out.append(generateZodInfer(this.tsTypeName + modeleType, this.zodName + modeleType));
// Check only the input value ==> no need of the output
out.append(generateExportCheckFunctionAppended(modeleType));
return out.toString();
}

View File

@@ -4,7 +4,6 @@ import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
// https://stackoverflow.com/questions/26777083/best-practice-for-rest-token-based-authentication-with-jax-rs-and-jersey
// https://stackoverflow.com/questions/26777083/best-practice-for-rest-token-based-authentication-with-jax-rs-and-jersey/45814178#45814178
@@ -119,7 +118,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
final boolean isApplicationToken = apikeyHeader != null;
final boolean isJwtToken = isTokenBasedAuthentication(authorizationHeader);
if (!isApplicationToken && !isJwtToken) {
LOGGER.warn("REJECTED unauthorized: {}", requestContext.getUriInfo().getPath());
LOGGER.warn("REJECTED unauthorized: /{}", requestContext.getUriInfo().getPath());
abortWithUnauthorized(requestContext, "REJECTED unauthorized: " + requestContext.getUriInfo().getPath());
return;
}
@@ -246,7 +245,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
final Object rowRight = ret.getClaim("right");
if (rowRight != null) {
LOGGER.info("Detect right in Authentication Filter: {}", rowRight);
user.right = (Map<String, Map<String, Object>>) ret.getClaim("right");
user.right = RightSafeCaster.safeCastAndTransform(ret.getClaim("right"));
/*
if (rights.containsKey(this.applicationName)) {
user.right = rights.get(this.applicationName);

View File

@@ -26,7 +26,7 @@ public class MySecurityContext implements SecurityContext {
return this.contextPrincipale;
}
public Object getRightOfRoleInGroup(final String group, final String role) {
public PartRight getRightOfRoleInGroup(final String group, final String role) {
if (this.contextPrincipale.userByToken != null) {
return this.contextPrincipale.userByToken.getRight(group, role);
}
@@ -67,21 +67,14 @@ public class MySecurityContext implements SecurityContext {
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()) {
final PartRight rightPart = getRightOfRoleInGroup(group, role);
if (PartRight.READ_WRITE.equals(rightPart)) {
return true;
}
if (!needRead && needWrite && dataRight == PartRight.WRITE.getValue()) {
if (!needRead && needWrite && PartRight.WRITE.equals(rightPart)) {
return true;
}
if (needRead && !needWrite && dataRight == PartRight.READ.getValue()) {
if (needRead && !needWrite && PartRight.READ.equals(rightPart)) {
return true;
}
return false;

View File

@@ -27,4 +27,27 @@ public enum PartRight {
}
throw new IllegalArgumentException("PartRight: Unknown value: " + value);
}
public static PartRight fromValue(final long value) {
for (final PartRight element : values()) {
if (element.getValue() == value) {
return element;
}
}
throw new IllegalArgumentException("PartRight: Unknown value: " + value);
}
public static PartRight fromString(final String value) {
if (value == null) {
throw new IllegalArgumentException("La chaîne ne peut pas être nulle");
}
return switch (value.toUpperCase()) {
case "NONE" -> NONE;
case "READ" -> READ;
case "WRITE" -> WRITE;
case "READ_WRITE" -> READ_WRITE;
default -> throw new IllegalArgumentException("Valeur inconnue pour PartRight : " + value);
};
}
}

View File

@@ -0,0 +1,61 @@
package org.kar.archidata.filter;
import java.util.Map;
public class RightSafeCaster {
@SuppressWarnings("unchecked")
public static Map<String, Map<String, PartRight>> safeCastAndTransform(final Object obj) {
if (!(obj instanceof Map)) {
throw new IllegalArgumentException("L'objet n'est pas un Map");
}
final Map<?, ?> outerMap = (Map<?, ?>) obj;
// Résultat final après vérification et transformation
final Map<String, Map<String, PartRight>> resultMap = new java.util.HashMap<>();
for (final Map.Entry<?, ?> outerEntry : outerMap.entrySet()) {
if (!(outerEntry.getKey() instanceof String)) {
throw new IllegalArgumentException("Une clé du Map externe n'est pas de type String");
}
if (!(outerEntry.getValue() instanceof Map)) {
throw new IllegalArgumentException("Une valeur du Map externe n'est pas un Map");
}
final String outerKey = (String) outerEntry.getKey();
final Map<?, ?> innerMap = (Map<?, ?>) outerEntry.getValue();
final Map<String, PartRight> transformedInnerMap = new java.util.HashMap<>();
for (final Map.Entry<?, ?> innerEntry : innerMap.entrySet()) {
if (!(innerEntry.getKey() instanceof String)) {
throw new IllegalArgumentException("Une clé du Map interne n'est pas de type String");
}
final String innerKey = (String) innerEntry.getKey();
final Object value = innerEntry.getValue();
PartRight partRight;
if (value instanceof PartRight) {
partRight = (PartRight) value;
} else if (value instanceof final Integer valueCasted) {
partRight = PartRight.fromValue(valueCasted);
} else if (value instanceof final Long valueCasted) {
partRight = PartRight.fromValue(valueCasted);
} else if (value instanceof final String valueCasted) {
partRight = PartRight.fromString(valueCasted);
} else {
throw new IllegalArgumentException("The Map Value is neither PartRight nor String nor Integer");
}
transformedInnerMap.put(innerKey, partRight);
}
resultMap.put(outerKey, transformedInnerMap);
}
return resultMap;
}
}

View File

@@ -11,6 +11,7 @@ import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.db.DbConfig;
import org.kar.archidata.migration.model.Migration;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -130,35 +131,42 @@ public class MigrationEngine {
}
private void createTableIfAbleOrWaitAdmin(final DbConfig configInput) throws MigrationException {
final DbConfig config = configInput.clone();
config.setDbName(null);
final String dbName = configInput.getDbName();
LOGGER.info("Verify existance of '{}'", dbName);
try (final DBAccess da = DBAccess.createInterface(config)) {
boolean exist = da.isDBExist(dbName);
if (!exist) {
LOGGER.warn("DB: '{}' DOES NOT EXIST ==> create one", dbName);
// create the local DB:
da.createDB(dbName);
if (ConfigBaseVariable.getDBAbleToCreate()) {
final DbConfig config = configInput.clone();
if (!"MONGO".equalsIgnoreCase(config.getType())) {
config.setDbName(null);
}
exist = da.isDBExist(dbName);
while (!exist) {
LOGGER.error("DB: '{}' DOES NOT EXIST after trying to create one ", dbName);
LOGGER.error("Waiting administrator create a new one, we check after 30 seconds...");
try {
Thread.sleep(30000);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
final String dbName = configInput.getDbName();
LOGGER.info("Verify existance of '{}'", dbName);
try (final DBAccess da = DBAccess.createInterface(config)) {
boolean exist = da.isDBExist(dbName);
if (!exist) {
LOGGER.warn("DB: '{}' DOES NOT EXIST ==> create one", dbName);
// create the local DB:
da.createDB(dbName);
}
exist = da.isDBExist(dbName);
while (!exist) {
LOGGER.error("DB: '{}' DOES NOT EXIST after trying to create one ", dbName);
LOGGER.error("Waiting administrator create a new one, we check after 30 seconds...");
try {
Thread.sleep(30000);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
exist = da.isDBExist(dbName);
}
} catch (final InternalServerErrorException e) {
e.printStackTrace();
throw new MigrationException("TODO ...");
} catch (final IOException e) {
e.printStackTrace();
throw new MigrationException("TODO ...");
}
} catch (final InternalServerErrorException e) {
e.printStackTrace();
throw new MigrationException("TODO ...");
} catch (final IOException e) {
e.printStackTrace();
throw new MigrationException("TODO ...");
} else {
final String dbName = configInput.getDbName();
LOGGER.warn("DB: '{}' is not check if it EXIST", dbName);
}
}

View File

@@ -1,12 +1,14 @@
package org.kar.archidata.model;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.apiGenerator.ApiAccessLimitation;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Size;
@Table(name = "data")
@DataIfNotExists
@@ -14,11 +16,16 @@ import jakarta.persistence.Table;
public class Data extends OIDGenericDataSoftDelete {
@Column(length = 128, nullable = false)
@Schema(description = "Sha512 of the data")
@Size(max = 512)
@ApiAccessLimitation(creatable = false, updatable = false)
public String sha512;
@Column(length = 128, nullable = false)
@Schema(description = "Mime -type of the media")
@Size(max = 512)
@ApiAccessLimitation(creatable = false, updatable = false)
public String mimeType;
@Column(nullable = false)
@Schema(description = "Size in Byte of the data")
@ApiAccessLimitation(creatable = false, updatable = false)
public Long size;
}

View File

@@ -1,15 +1,20 @@
package org.kar.archidata.model;
import org.kar.archidata.annotation.apiGenerator.ApiAccessLimitation;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@ApiGenerationMode(create = true, update = true)
public class GenericData extends GenericTiming {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, unique = true)
@Schema(description = "Unique Id of the object", required = false, readOnly = true, example = "123456")
@Schema(description = "Unique Id of the object", example = "123456")
@ApiAccessLimitation(creatable = false, updatable = false)
public Long id = null;
}

View File

@@ -2,18 +2,22 @@ package org.kar.archidata.model;
import org.kar.archidata.annotation.DataDeleted;
import org.kar.archidata.annotation.DataNotRead;
import org.kar.archidata.annotation.apiGenerator.ApiAccessLimitation;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.ws.rs.DefaultValue;
@ApiGenerationMode(create = true, update = true)
public class GenericDataSoftDelete extends GenericData {
@DataNotRead
@Column(nullable = false)
@DefaultValue("'0'")
@DataDeleted
@Schema(description = "Deleted state", hidden = true, required = false, readOnly = true)
@Schema(description = "Deleted state", hidden = true)
@Nullable
@ApiAccessLimitation(creatable = false, updatable = false)
public Boolean deleted = null;
}

View File

@@ -5,6 +5,8 @@ import java.util.Date;
import org.kar.archidata.annotation.CreationTimestamp;
import org.kar.archidata.annotation.DataNotRead;
import org.kar.archidata.annotation.UpdateTimestamp;
import org.kar.archidata.annotation.apiGenerator.ApiAccessLimitation;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import com.fasterxml.jackson.annotation.JsonFormat;
@@ -12,20 +14,22 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
@ApiGenerationMode(create = true, update = true)
public class GenericTiming {
@DataNotRead
@CreationTimestamp
@Column(nullable = false)
@Schema(description = "Create time of the object", required = false, example = "2000-01-23T01:23:45.678+01:00", readOnly = true)
@Column(nullable = false, insertable = false, updatable = false)
@Schema(description = "Create time of the object", example = "2000-01-23T01:23:45.678+01:00")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
@Nullable
@ApiAccessLimitation(creatable = false, updatable = false)
public Date createdAt = null;
@DataNotRead
@UpdateTimestamp
@Column(nullable = false)
@Schema(description = "When update the object", required = false, example = "2000-01-23T00:23:45.678Z", readOnly = true)
@Column(nullable = false, insertable = false, updatable = false)
@Schema(description = "When update the object", example = "2000-01-23T00:23:45.678Z")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
// public Instant updatedAt = null;
@Nullable
@ApiAccessLimitation(creatable = false, updatable = false)
public Date updatedAt = null;
}

View File

@@ -1,17 +1,21 @@
package org.kar.archidata.model;
import org.bson.types.ObjectId;
import org.kar.archidata.annotation.apiGenerator.ApiAccessLimitation;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import dev.morphia.annotations.Id;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.validation.constraints.NotNull;
@ApiGenerationMode(create = true, update = true)
public class OIDGenericData extends GenericTiming {
@Id
@jakarta.persistence.Id
@Column(nullable = false, unique = true, name = "_id")
@Schema(description = "Unique ObjectID of the object", required = false, readOnly = true, example = "65161616841351")
@Schema(description = "Unique ObjectID of the object", example = "65161616841351")
@NotNull
@ApiAccessLimitation(creatable = false, updatable = false)
public ObjectId oid = null;
}

View File

@@ -2,18 +2,22 @@ package org.kar.archidata.model;
import org.kar.archidata.annotation.DataDeleted;
import org.kar.archidata.annotation.DataNotRead;
import org.kar.archidata.annotation.apiGenerator.ApiAccessLimitation;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.ws.rs.DefaultValue;
@ApiGenerationMode(create = true, update = true)
public class OIDGenericDataSoftDelete extends OIDGenericData {
@DataNotRead
@Column(nullable = false)
@DefaultValue("'0'")
@DataDeleted
@Schema(description = "Deleted state", hidden = true, required = false, readOnly = true)
@Schema(description = "Deleted state", hidden = true)
@Nullable
@ApiAccessLimitation(creatable = false, updatable = false)
public Boolean deleted = null;
}

View File

@@ -2,17 +2,22 @@ package org.kar.archidata.model;
import java.util.UUID;
import org.kar.archidata.annotation.apiGenerator.ApiAccessLimitation;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Id;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.DefaultValue;
@ApiGenerationMode(create = true, update = true)
public class UUIDGenericData extends GenericTiming {
@Id
@DefaultValue("(UUID_TO_BIN(UUID(), TRUE))")
@Column(nullable = false, unique = true)
@Schema(description = "Unique UUID of the object", required = false, readOnly = true, example = "e6b33c1c-d24d-11ee-b616-02420a030102")
@Schema(description = "Unique UUID of the object", example = "e6b33c1c-d24d-11ee-b616-02420a030102")
@NotNull
@ApiAccessLimitation(creatable = false, updatable = false)
public UUID uuid = null;
}

View File

@@ -2,18 +2,22 @@ package org.kar.archidata.model;
import org.kar.archidata.annotation.DataDeleted;
import org.kar.archidata.annotation.DataNotRead;
import org.kar.archidata.annotation.apiGenerator.ApiAccessLimitation;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.ws.rs.DefaultValue;
@ApiGenerationMode(create = true, update = true)
public class UUIDGenericDataSoftDelete extends UUIDGenericData {
@DataNotRead
@Column(nullable = false)
@DefaultValue("'0'")
@DataDeleted
@Schema(description = "Deleted state", hidden = true, required = false, readOnly = true)
@Schema(description = "Deleted state", hidden = true)
@Nullable
@ApiAccessLimitation(creatable = false, updatable = false)
public Boolean deleted = null;
}

View File

@@ -20,6 +20,7 @@ import java.util.UUID;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -36,6 +37,7 @@ import jakarta.ws.rs.DefaultValue;
@Table(name = "user")
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiGenerationMode(create = true, update = true)
public class User extends GenericDataSoftDelete {
@NotNull
@Column(length = 128)
@@ -48,8 +50,10 @@ public class User extends GenericDataSoftDelete {
@DefaultValue("'0'")
@Column(nullable = false)
public boolean blocked = false;
@Nullable
public Boolean blocked = false;
@Column(length = 512)
@Size(max = 512)
public String blockedReason;
@Schema(description = "List of Id of the specific covers")

View File

@@ -4,6 +4,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.kar.archidata.filter.PartRight;
public class UserByToken {
public static final int TYPE_USER = -1;
public static final int TYPE_APPLICATION = -2;
@@ -11,10 +13,11 @@ public class UserByToken {
public Integer type = null;
public Long id = null;
public Long parentId = null; // FOr application, this is the id of the application, and of user token, this is the USERID
// For application, this is the id of the application, and of user token, this is the USERID
public Long parentId = null;
public String name = null;
// Right map
public Map<String, Map<String, Object>> right = new HashMap<>();
public Map<String, Map<String, PartRight>> right = new HashMap<>();
public Set<String> getGroups() {
return this.right.keySet();
@@ -27,11 +30,11 @@ public class UserByToken {
return this.right.containsKey(group);
}
public Object getRight(final String group, final String key) {
public PartRight getRight(final String group, final String key) {
if (!this.right.containsKey(group)) {
return null;
}
final Map<String, Object> rightGroup = this.right.get(group);
final Map<String, PartRight> rightGroup = this.right.get(group);
if (!rightGroup.containsKey(key)) {
return null;
}

View File

@@ -0,0 +1,13 @@
package org.kar.archidata.model.token;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
public class JwtHeader {
@Size(max = 128)
@NotNull
public String typ;
@Size(max = 128)
@NotNull
public String alg;
}

View File

@@ -0,0 +1,29 @@
package org.kar.archidata.model.token;
import java.util.Map;
import jakarta.validation.constraints.NotNull;
public class JwtPayload {
// User identification
@NotNull
public String sub;
// Application destination
@NotNull
public String application;
// Emitter of the token
@NotNull
public String iss;
// Access Right Map<application, Map< section, right>>
@NotNull
public Map<String, Map<String, Long>> right;
// user name
@NotNull
public String login;
// Expiration (timestamp)
@NotNull
public Long exp;
// Create time (timestamp)
@NotNull
public Long iat;
}

View File

@@ -0,0 +1,12 @@
package org.kar.archidata.model.token;
import jakarta.validation.constraints.NotNull;
public class JwtToken {
@NotNull
public JwtHeader header;
@NotNull
public JwtPayload payload;
@NotNull
public String signature;
}

View File

@@ -0,0 +1,48 @@
package org.kar.archidata.tools;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.kar.archidata.annotation.apiGenerator.ApiGenerationMode;
public class AnnotationCreator {
@SuppressWarnings("unchecked")
public static <A extends Annotation> A createAnnotation(final Class<A> annotationClass, final Object... values) {
return (A) Proxy.newProxyInstance(annotationClass.getClassLoader(), new Class<?>[] { annotationClass },
new InvocationHandler() {
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
throws Throwable {
if ("annotationType".equals(method.getName())) {
return annotationClass;
}
if ("toString".equals(method.getName())) {
return "@" + annotationClass.getName() + values;
}
for (int i = 0; i < values.length; i += 2) {
if (method.getName().equals(values[i])) {
return values[i + 1];
}
}
return method.getDefaultValue();
}
});
}
public static void main(final String[] args) {
final ApiGenerationMode myAnnotation = AnnotationCreator.createAnnotation(ApiGenerationMode.class, "readable",
true, "creatable", false, "updatable", false);
System.out.println("readable: " + myAnnotation.read()); // Output: example
System.out.println("creatable: " + myAnnotation.create()); // Output: 100
System.out.println("updatable: " + myAnnotation.update()); // Output: 100
final ApiGenerationMode myAnnotation2 = AnnotationCreator.createAnnotation(ApiGenerationMode.class);
System.out.println("readable: " + myAnnotation2.read()); // Output: example
System.out.println("creatable: " + myAnnotation2.create()); // Output: 100
System.out.println("updatable: " + myAnnotation2.update()); // Output: 100
}
}

View File

@@ -3,6 +3,7 @@ package org.kar.archidata.tools;
public class ConfigBaseVariable {
static public String tmpDataFolder;
static public String dataFolder;
static public String dbAbleToCreate;
static public String dbType;
static public String dbHost;
static public String dbPort;
@@ -23,6 +24,7 @@ public class ConfigBaseVariable {
public static void clearAllValue() {
tmpDataFolder = System.getenv("DATA_TMP_FOLDER");
dataFolder = System.getenv("DATA_FOLDER");
dbAbleToCreate = System.getenv("DB_ABLE_TO_CREATE");
dbType = System.getenv("DB_TYPE");
dbHost = System.getenv("DB_HOST");
dbPort = System.getenv("DB_PORT");
@@ -58,6 +60,13 @@ public class ConfigBaseVariable {
return dataFolder;
}
public static boolean getDBAbleToCreate() {
if (dbAbleToCreate == null) {
return true;
}
return Boolean.getBoolean(dbAbleToCreate);
}
public static String getDBType() {
if (dbType == null) {
return "mysql";
@@ -100,9 +109,6 @@ public class ConfigBaseVariable {
}
public static String getDBName() {
if (bdDatabase == null) {
return "unknown";
}
return bdDatabase;
}

View File

@@ -5,6 +5,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
@@ -17,13 +18,17 @@ import java.util.List;
import org.apache.tika.Tika;
import org.bson.types.ObjectId;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.AnnotationTools.FieldName;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.dataAccess.DBAccess;
import org.kar.archidata.dataAccess.QueryAnd;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.addOnSQL.AddOnDataJson;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.ReadAllColumn;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.exception.FailException;
import org.kar.archidata.exception.InputException;
import org.kar.archidata.model.Data;
@@ -391,6 +396,13 @@ public class DataTools {
}
// Fist step: retrieve all the Id of each parents:...
LOGGER.info("Find typeNode");
AddOnDataJson.addLink(ioDb, clazz, null, id, null, data.oid);
final Field idField = AnnotationTools.getIdField(clazz);
if (idField == null) {
throw new DataAccessException(
"The class have no annotation @Id ==> can not determine the default type searching");
}
final FieldName fieldName = AnnotationTools.getFieldName(idField, new QueryOptions());
AddOnDataJson.addLink(ioDb, clazz, fieldName.inTable(), id, null, data.oid);
}
}

View File

@@ -198,7 +198,7 @@ public class JWTWrapper {
.claim("login", userLogin).claim("application", application).issuer(isuer).issueTime(now)
.expirationTime(expiration); // Do not ask why we need a "-" here ... this have no meaning
// add right if needed:
if (rights != null && !rights.isEmpty()) {
if (rights != null) {
builder.claim("right", rights);
}
// Prepare JWT with claims set

View File

@@ -1,5 +1,6 @@
package org.kar.archidata.tools;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
@@ -8,15 +9,19 @@ import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpRequest.Builder;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.bson.types.ObjectId;
import org.kar.archidata.exception.RESTErrorResponseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import jakarta.ws.rs.core.HttpHeaders;
@@ -36,7 +41,7 @@ public class RESTApi {
this.token = token;
}
public <T> List<T> gets(final Class<T> clazz, final String urlOffset)
public <TYPE_RESPONSE> List<TYPE_RESPONSE> gets(final Class<TYPE_RESPONSE> clazz, final String urlOffset)
throws RESTErrorResponseException, IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient();
Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1)
@@ -56,63 +61,105 @@ public class RESTApi {
"Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body());
}
}
//return this.mapper.readValue(httpResponse.body(), new TypeReference<List<T>>() {});
return this.mapper.readValue(httpResponse.body(),
this.mapper.getTypeFactory().constructCollectionType(List.class, clazz));
}
public <T> T get(final Class<T> clazz, final String urlOffset)
public <TYPE_RESPONSE> TYPE_RESPONSE get(final Class<TYPE_RESPONSE> clazz, final String urlOffset)
throws RESTErrorResponseException, IOException, InterruptedException {
return modelSendJson("GET", clazz, urlOffset, null);
}
public <T, U> T post(final Class<T> clazz, final String urlOffset, final U data)
throws RESTErrorResponseException, IOException, InterruptedException {
public HttpResponse<byte[]> getRaw(final String urlOffset) throws IOException, InterruptedException {
final Builder requestBuilding = createRequestBuilder(urlOffset);
final HttpRequest request = requestBuilding.method("GET", BodyPublishers.ofString("")).build();
final HttpClient client = HttpClient.newHttpClient();
// client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
return client.send(request, HttpResponse.BodyHandlers.ofByteArray());
}
public <TYPE_RESPONSET, TYPE_BODY> TYPE_RESPONSET postMultipart(
final Class<TYPE_RESPONSET> clazz,
final String urlOffset,
final Map<String, Object> data) throws RESTErrorResponseException, IOException, InterruptedException {
return modelSendMultipart("POST", clazz, urlOffset, data);
}
public <TYPE_RESPONSET, TYPE_BODY> TYPE_RESPONSET post(
final Class<TYPE_RESPONSET> clazz,
final String urlOffset,
final TYPE_BODY data) throws RESTErrorResponseException, IOException, InterruptedException {
return modelSend("POST", clazz, urlOffset, data);
}
public <T, U> T postJson(final Class<T> clazz, final String urlOffset, final String body)
throws RESTErrorResponseException, IOException, InterruptedException {
public <TYPE_RESPONSE> TYPE_RESPONSE postJson(
final Class<TYPE_RESPONSE> clazz,
final String urlOffset,
final String body) throws RESTErrorResponseException, IOException, InterruptedException {
return modelSendJson("POST", clazz, urlOffset, body);
}
public <T> T postMap(final Class<T> clazz, final String urlOffset, final Map<String, Object> data)
throws RESTErrorResponseException, IOException, InterruptedException {
public <TYPE_RESPONSE> TYPE_RESPONSE postMap(
final Class<TYPE_RESPONSE> clazz,
final String urlOffset,
final Map<String, Object> data) throws RESTErrorResponseException, IOException, InterruptedException {
return modelSendMap("POST", clazz, urlOffset, data);
}
public <T, U> T put(final Class<T> clazz, final String urlOffset, final U data)
throws RESTErrorResponseException, IOException, InterruptedException {
public <TYPE_RESPONSE, TYPE_BODY> TYPE_RESPONSE put(
final Class<TYPE_RESPONSE> clazz,
final String urlOffset,
final TYPE_BODY data) throws RESTErrorResponseException, IOException, InterruptedException {
return modelSend("PUT", clazz, urlOffset, data);
}
public <T, U> T putJson(final Class<T> clazz, final String urlOffset, final String body)
throws RESTErrorResponseException, IOException, InterruptedException {
public <TYPE_RESPONSE> TYPE_RESPONSE putJson(
final Class<TYPE_RESPONSE> clazz,
final String urlOffset,
final String body) throws RESTErrorResponseException, IOException, InterruptedException {
return modelSendJson("PUT", clazz, urlOffset, body);
}
public <T> T putMap(final Class<T> clazz, final String urlOffset, final Map<String, Object> data)
throws RESTErrorResponseException, IOException, InterruptedException {
public <TYPE_RESPONSE> TYPE_RESPONSE putMap(
final Class<TYPE_RESPONSE> clazz,
final String urlOffset,
final Map<String, Object> data) throws RESTErrorResponseException, IOException, InterruptedException {
return modelSendMap("PUT", clazz, urlOffset, data);
}
public <T, U> T patch(final Class<T> clazz, final String urlOffset, final U data)
throws RESTErrorResponseException, IOException, InterruptedException {
public <TYPE_RESPONSE> TYPE_RESPONSE putMultipart(
final Class<TYPE_RESPONSE> clazz,
final String urlOffset,
final Map<String, Object> data) throws RESTErrorResponseException, IOException, InterruptedException {
return modelSendMultipart("PUT", clazz, urlOffset, data);
}
public <TYPE_RESPONSE, TYPE_BODY> TYPE_RESPONSE patch(
final Class<TYPE_RESPONSE> clazz,
final String urlOffset,
final TYPE_BODY data) throws RESTErrorResponseException, IOException, InterruptedException {
return modelSend("PATCH", clazz, urlOffset, data);
}
public <T, U> T patchJson(final Class<T> clazz, final String urlOffset, final String body)
throws RESTErrorResponseException, IOException, InterruptedException {
public <TYPE_RESPONSE, TYPE_BODY> TYPE_RESPONSE patchJson(
final Class<TYPE_RESPONSE> clazz,
final String urlOffset,
final String body) throws RESTErrorResponseException, IOException, InterruptedException {
return modelSendJson("PATCH", clazz, urlOffset, body);
}
public <T> T patchMap(final Class<T> clazz, final String urlOffset, final Map<String, Object> data)
throws RESTErrorResponseException, IOException, InterruptedException {
public <TYPE_RESPONSE> TYPE_RESPONSE patchMap(
final Class<TYPE_RESPONSE> clazz,
final String urlOffset,
final Map<String, Object> data) throws RESTErrorResponseException, IOException, InterruptedException {
return modelSendMap("PATCH", clazz, urlOffset, data);
}
protected <T, U> T modelSend(final String model, final Class<T> clazz, final String urlOffset, final U data)
throws RESTErrorResponseException, IOException, InterruptedException {
protected <TYPE_RESPONSE, TYPE_BODY> TYPE_RESPONSE modelSend(
final String model,
final Class<TYPE_RESPONSE> clazz,
final String urlOffset,
final TYPE_BODY data) throws RESTErrorResponseException, IOException, InterruptedException {
if (data == null) {
return modelSendJson(model, clazz, urlOffset, null);
} else {
@@ -121,18 +168,74 @@ public class RESTApi {
}
}
@SuppressWarnings("unchecked")
public <T, U> T modelSendJson(final String model, final Class<T> clazz, final String urlOffset, String body)
throws RESTErrorResponseException, IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient();
// client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
public Builder createRequestBuilder(final String urlOffset) {
Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1)
.uri(URI.create(this.baseUrl + urlOffset));
LOGGER.trace("call {}: {}", model, URI.create(this.baseUrl + urlOffset));
LOGGER.trace("DATA: {}", body);
if (this.token != null) {
requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Bearer " + this.token);
}
return requestBuilding;
}
public <TYPE_RESPONSE> TYPE_RESPONSE modelSendMultipart(
final String model,
final Class<TYPE_RESPONSE> clazzReturn,
final String urlOffset,
final Map<String, Object> params) throws RESTErrorResponseException, IOException, InterruptedException {
LOGGER.trace("call (MULTIPART) {}: {}", model, URI.create(this.baseUrl + urlOffset));
Builder requestBuilding = createRequestBuilder(urlOffset);
// Create multipart key element
final String boundary = (new ObjectId()).toString();
requestBuilding = requestBuilding.header("Content-Type", "multipart/form-data; boundary=" + boundary);
// create the body;
final List<byte[]> bodyParts = new ArrayList<>();
for (final Map.Entry<String, Object> entry : params.entrySet()) {
final StringBuilder partHeader = new StringBuilder();
partHeader.append("--").append(boundary).append("\r\n");
if (entry.getValue() instanceof File) {
final File file = (File) entry.getValue();
partHeader.append("Content-Disposition: form-data; name=\"").append(entry.getKey())
.append("\"; filename=\"").append(file.getName()).append("\"\r\n");
partHeader.append("Content-Type: application/octet-stream\r\n\r\n");
bodyParts.add(partHeader.toString().getBytes());
bodyParts.add(Files.readAllBytes(file.toPath()));
bodyParts.add("\r\n".getBytes());
} else {
partHeader.append("Content-Disposition: form-data; name=\"").append(entry.getKey())
.append("\"\r\n\r\n");
if (entry.getValue() == null) {
partHeader.append("null\r\n");
} else {
partHeader.append(entry.getValue().toString()).append("\r\n");
}
bodyParts.add(partHeader.toString().getBytes());
}
}
bodyParts.add(("--" + boundary + "--\r\n").getBytes());
final int totalSize = bodyParts.stream().mapToInt(b -> b.length).sum();
final byte[] finalBody = new byte[totalSize];
int position = 0;
for (final byte[] part : bodyParts) {
System.arraycopy(part, 0, finalBody, position, part.length);
position += part.length;
}
final HttpRequest request = requestBuilding.method(model, BodyPublishers.ofByteArray(finalBody)).build();
return callAndParseRequest(clazzReturn, request);
}
public <TYPE_RESPONSE> TYPE_RESPONSE modelSendJson(
final String model,
final Class<TYPE_RESPONSE> clazzReturn,
final String urlOffset,
String body) throws RESTErrorResponseException, IOException, InterruptedException {
LOGGER.trace("DATA: {}", body);
Builder requestBuilding = createRequestBuilder(urlOffset);
if (body == null) {
body = "";
} else {
@@ -140,37 +243,56 @@ public class RESTApi {
}
LOGGER.trace("publish body: {}", body);
final HttpRequest request = requestBuilding.method(model, BodyPublishers.ofString(body)).build();
return callAndParseRequest(clazzReturn, request);
}
@SuppressWarnings("unchecked")
public <TYPE_RESPONSE> TYPE_RESPONSE callAndParseRequest(
final Class<TYPE_RESPONSE> clazzReturn,
final HttpRequest request) throws RESTErrorResponseException, IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient();
// client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
if (clazzReturn == HttpResponse.class) {
return (TYPE_RESPONSE) httpResponse;
}
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
LOGGER.trace("Receive Error: {}", httpResponse.body());
try {
final RESTErrorResponseException out = this.mapper.readValue(httpResponse.body(),
RESTErrorResponseException.class);
throw out;
} catch (final InvalidDefinitionException ex) {
ex.printStackTrace();
LOGGER.error("body: {}", httpResponse.body());
throw new IOException("RestAPI Fail to parse the error " + ex.getClass().getName() + " ["
+ httpResponse.statusCode() + "] " + httpResponse.body());
} catch (final MismatchedInputException ex) {
throw new IOException(
"Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body());
ex.printStackTrace();
LOGGER.error("body: {}", httpResponse.body());
throw new IOException("RestAPI Fail to parse the error " + ex.getClass().getName() + " ["
+ httpResponse.statusCode() + "] " + httpResponse.body());
} catch (final JsonParseException ex) {
ex.printStackTrace();
LOGGER.error("body: {}", httpResponse.body());
throw new IOException(
"Fail to get the ERROR data [" + httpResponse.statusCode() + "] " + httpResponse.body());
throw new IOException("RestAPI Fail to parse the error " + ex.getClass().getName() + " ["
+ httpResponse.statusCode() + "] " + httpResponse.body());
}
}
if (clazz == Void.class || clazz == void.class) {
if (clazzReturn == Void.class || clazzReturn == void.class) {
return null;
}
if (clazz.equals(String.class)) {
return (T) httpResponse.body();
if (clazzReturn.equals(String.class)) {
return (TYPE_RESPONSE) httpResponse.body();
}
LOGGER.trace("Receive model: {} with data: '{}'", clazz.getCanonicalName(), httpResponse.body());
return this.mapper.readValue(httpResponse.body(), clazz);
LOGGER.trace("Receive model: {} with data: '{}'", clazzReturn.getCanonicalName(), httpResponse.body());
return this.mapper.readValue(httpResponse.body(), clazzReturn);
}
@SuppressWarnings("unchecked")
public <T> T modelSendMap(
public <TYPE_RESPONSE> TYPE_RESPONSE modelSendMap(
final String model,
final Class<T> clazz,
final Class<TYPE_RESPONSE> clazz,
final String urlOffset,
final Map<String, Object> data) throws RESTErrorResponseException, IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient();
@@ -202,7 +324,7 @@ public class RESTApi {
return null;
}
if (clazz.equals(String.class)) {
return (T) httpResponse.body();
return (TYPE_RESPONSE) httpResponse.body();
}
return this.mapper.readValue(httpResponse.body(), clazz);
}
@@ -217,12 +339,12 @@ public class RESTApi {
/**
* Call a DELETE on a REST API with retrieving some data
* @param <T> Type of data that might be received.
* @param <TYPE_RESPONSE> Type of data that might be received.
* @param clazz Class model of the data that might be parsed.
* @param urlOffset Offset to call the API
* @return The parsed object received.
*/
public <T> T delete(final Class<T> clazz, final String urlOffset)
public <TYPE_RESPONSE> TYPE_RESPONSE delete(final Class<TYPE_RESPONSE> clazz, final String urlOffset)
throws RESTErrorResponseException, IOException, InterruptedException {
return simpleRequest("DELETE", clazz, urlOffset);
}
@@ -237,12 +359,12 @@ public class RESTApi {
/**
* Call a ARCHIVE on a REST API with retrieving some data
* @param <T> Type of data that might be received.
* @param <TYPE_RESPONSE> 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)
public <TYPE_RESPONSE> TYPE_RESPONSE archive(final Class<TYPE_RESPONSE> clazz, final String urlOffset)
throws RESTErrorResponseException, IOException, InterruptedException {
return simpleRequest("ARCHIVE", clazz, urlOffset);
}
@@ -269,14 +391,16 @@ public class RESTApi {
/**
* Call a key on a REST API with retrieving some data
* @param <T> Type of data that might be received.
* @param <TYPE_RESPONSE> 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 RESTErrorResponseException, IOException, InterruptedException {
public <TYPE_RESPONSE> TYPE_RESPONSE simpleRequest(
final String model,
final Class<TYPE_RESPONSE> clazz,
final String urlOffset) throws RESTErrorResponseException, IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient();
Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1)
.uri(URI.create(this.baseUrl + urlOffset));
@@ -299,7 +423,7 @@ public class RESTApi {
return null;
}
if (clazz.equals(String.class)) {
return (T) httpResponse.body();
return (TYPE_RESPONSE) httpResponse.body();
}
return this.mapper.readValue(httpResponse.body(), clazz);
}

View File

@@ -3,30 +3,29 @@
* @copyright 2024, Edouard DUPIN, all right reserved
* @license MPL-2
*/
import { RestErrorResponse, isRestErrorResponse } from "./model";
import { RestErrorResponse, isRestErrorResponse } from './model';
export enum HTTPRequestModel {
ARCHIVE = "ARCHIVE",
DELETE = "DELETE",
HEAD = "HEAD",
GET = "GET",
OPTION = "OPTION",
PATCH = "PATCH",
POST = "POST",
PUT = "PUT",
RESTORE = "RESTORE",
ARCHIVE = 'ARCHIVE',
DELETE = 'DELETE',
HEAD = 'HEAD',
GET = 'GET',
OPTION = 'OPTION',
PATCH = 'PATCH',
POST = 'POST',
PUT = 'PUT',
RESTORE = 'RESTORE',
}
export enum HTTPMimeType {
ALL = "*/*",
CSV = "text/csv",
IMAGE = "image/*",
IMAGE_JPEG = "image/jpeg",
IMAGE_PNG = "image/png",
JSON = "application/json",
MULTIPART = "multipart/form-data",
OCTET_STREAM = "application/octet-stream",
TEXT_PLAIN = "text/plain",
ALL = '*/*',
CSV = 'text/csv',
IMAGE = 'image/*',
IMAGE_JPEG = 'image/jpeg',
IMAGE_PNG = 'image/png',
JSON = 'application/json',
MULTIPART = 'multipart/form-data',
OCTET_STREAM = 'application/octet-stream',
TEXT_PLAIN = 'text/plain',
}
export interface RESTConfig {
@@ -54,6 +53,14 @@ export interface ModelResponseHttp {
data: any;
}
export type ErrorRestApiCallback = (response: Response) => void;
let errorApiGlobalCallback: ErrorRestApiCallback | undefined = undefined;
export const setErrorApiGlobalCallback = (callback: ErrorRestApiCallback) => {
errorApiGlobalCallback = callback;
};
function isNullOrUndefined(data: any): data is undefined | null {
return data === undefined || data === null;
}
@@ -78,6 +85,7 @@ export interface RESTRequestType {
data?: any;
params?: object;
queries?: object;
headers?: any;
callbacks?: RESTCallbacks;
}
@@ -87,15 +95,15 @@ function replaceAll(input, searchValue, replaceValue) {
function removeTrailingSlashes(input: string): string {
if (isNullOrUndefined(input)) {
return "undefined";
return 'undefined';
}
return input.replace(/\/+$/, "");
return input.replace(/\/+$/, '');
}
function removeLeadingSlashes(input: string): string {
if (isNullOrUndefined(input)) {
return "";
return '';
}
return input.replace(/^\/+/, "");
return input.replace(/^\/+/, '');
}
export function RESTUrl({
@@ -133,9 +141,9 @@ export function RESTUrl({
}
}
if (restConfig.token !== undefined && restModel.tokenInUrl === true) {
searchParams.append("Authorization", `Bearer ${restConfig.token}`);
searchParams.append('Authorization', `Bearer ${restConfig.token}`);
}
return generateUrl + "?" + searchParams.toString();
return generateUrl + '?' + searchParams.toString();
}
export function fetchProgress(
@@ -159,7 +167,7 @@ export function fetchProgress(
return new Promise((resolve, reject) => {
// Stream the upload progress
if (progressUpload) {
xhr.io?.upload.addEventListener("progress", (dataEvent) => {
xhr.io?.upload.addEventListener('progress', (dataEvent) => {
if (dataEvent.lengthComputable) {
progressUpload(dataEvent.loaded, dataEvent.total);
}
@@ -167,7 +175,7 @@ export function fetchProgress(
}
// Stream the download progress
if (progressDownload) {
xhr.io?.addEventListener("progress", (dataEvent) => {
xhr.io?.addEventListener('progress', (dataEvent) => {
if (dataEvent.lengthComputable) {
progressDownload(dataEvent.loaded, dataEvent.total);
}
@@ -187,19 +195,19 @@ export function fetchProgress(
};
}
// Check if we have an internal Fail:
xhr.io?.addEventListener("error", () => {
xhr.io?.addEventListener('error', () => {
xhr.io = undefined;
reject(new TypeError("Failed to fetch"));
reject(new TypeError('Failed to fetch'));
});
// Capture the end of the stream
xhr.io?.addEventListener("loadend", () => {
xhr.io?.addEventListener('loadend', () => {
if (xhr.io?.readyState !== XMLHttpRequest.DONE) {
return;
}
if (xhr.io?.status === 0) {
//the stream has been aborted
reject(new TypeError("Fetch has been aborted"));
reject(new TypeError('Fetch has been aborted'));
return;
}
// Stream is ended, transform in a generic response:
@@ -209,17 +217,17 @@ export function fetchProgress(
});
const headersArray = replaceAll(
xhr.io.getAllResponseHeaders().trim(),
"\r\n",
"\n"
).split("\n");
'\r\n',
'\n'
).split('\n');
headersArray.forEach(function (header) {
const firstColonIndex = header.indexOf(":");
const firstColonIndex = header.indexOf(':');
if (firstColonIndex !== -1) {
const key = header.substring(0, firstColonIndex).trim();
const value = header.substring(firstColonIndex + 1).trim();
response.headers.set(key, value);
} else {
response.headers.set(header, "");
response.headers.set(header, '');
}
});
xhr.io = undefined;
@@ -241,27 +249,29 @@ export function RESTRequest({
data,
params,
queries,
headers = {},
callbacks,
}: RESTRequestType): Promise<ModelResponseHttp> {
// Create the URL PATH:
let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries });
let headers: any = {};
if (restConfig.token !== undefined && restModel.tokenInUrl !== true) {
headers["Authorization"] = `Bearer ${restConfig.token}`;
headers['Authorization'] = `Bearer ${restConfig.token}`;
}
if (restModel.accept !== undefined) {
headers["Accept"] = restModel.accept;
headers['Accept'] = restModel.accept;
}
if (restModel.requestType !== HTTPRequestModel.GET &&
restModel.requestType !== HTTPRequestModel.ARCHIVE &&
restModel.requestType !== HTTPRequestModel.RESTORE
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 (restModel.contentType !== HTTPMimeType.MULTIPART &&
restModel.contentType !== undefined
) {
if (
restModel.contentType !== HTTPMimeType.MULTIPART &&
restModel.contentType !== undefined
) {
// 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;
}
}
let body = data;
@@ -302,19 +312,27 @@ export function RESTRequest({
}
action
.then((response: Response) => {
if (
errorApiGlobalCallback &&
400 <= response.status &&
response.status <= 499
) {
// Detect an error and trigger the generic error callback:
errorApiGlobalCallback(response);
}
if (response.status >= 200 && response.status <= 299) {
const contentType = response.headers.get("Content-Type");
const contentType = response.headers.get('Content-Type');
if (
!isNullOrUndefined(restModel.accept) &&
restModel.accept !== contentType
) {
reject({
name: "Model accept type incompatible",
name: 'Model accept type incompatible',
time: Date().toString(),
status: 901,
message: `REST Content type are not compatible: ${restModel.accept} != ${contentType}`,
statusMessage: "Fetch error",
error: "rest-tools.ts Wrong type in the message return type",
statusMessage: 'Fetch error',
error: 'rest-tools.ts Wrong type in the message return type',
} as RestErrorResponse);
} else if (contentType === HTTPMimeType.JSON) {
response
@@ -324,12 +342,12 @@ export function RESTRequest({
})
.catch((reason: Error) => {
reject({
name: "API serialization error",
name: 'API serialization error',
time: Date().toString(),
status: 902,
message: `REST parse json fail: ${reason}`,
statusMessage: "Fetch parse error",
error: "rest-tools.ts Wrong message model to parse",
statusMessage: 'Fetch parse error',
error: 'rest-tools.ts Wrong message model to parse',
} as RestErrorResponse);
});
} else {
@@ -349,22 +367,22 @@ export function RESTRequest({
.text()
.then((dataError: string) => {
reject({
name: "API serialization error",
name: 'API serialization error',
time: Date().toString(),
status: 903,
message: `REST parse error json with wrong type fail. ${dataError}`,
statusMessage: "Fetch parse error",
error: "rest-tools.ts Wrong message model to parse",
statusMessage: 'Fetch parse error',
error: 'rest-tools.ts Wrong message model to parse',
} as RestErrorResponse);
})
.catch((reason: any) => {
reject({
name: "API serialization error",
name: 'API serialization error',
time: Date().toString(),
status: response.status,
message: `unmanaged error model: ??? with error: ${reason}`,
statusMessage: "Fetch ERROR parse error",
error: "rest-tools.ts Wrong message model to parse",
statusMessage: 'Fetch ERROR parse error',
error: 'rest-tools.ts Wrong message model to parse',
} as RestErrorResponse);
});
}
@@ -374,22 +392,22 @@ export function RESTRequest({
.text()
.then((dataError: string) => {
reject({
name: "API serialization error",
name: 'API serialization error',
time: Date().toString(),
status: response.status,
message: `unmanaged error model: ${dataError} with error: ${reason}`,
statusMessage: "Fetch ERROR TEXT parse error",
error: "rest-tools.ts Wrong message model to parse",
statusMessage: 'Fetch ERROR TEXT parse error',
error: 'rest-tools.ts Wrong message model to parse',
} as RestErrorResponse);
})
.catch((reason: any) => {
reject({
name: "API serialization error",
name: 'API serialization error',
time: Date().toString(),
status: response.status,
message: `unmanaged error model: ??? with error: ${reason}`,
statusMessage: "Fetch ERROR TEXT FAIL",
error: "rest-tools.ts Wrong message model to parse",
statusMessage: 'Fetch ERROR TEXT FAIL',
error: 'rest-tools.ts Wrong message model to parse',
} as RestErrorResponse);
});
});
@@ -400,12 +418,12 @@ export function RESTRequest({
reject(error);
} else {
reject({
name: "Request fail",
name: 'Request fail',
time: Date(),
status: 999,
message: error,
statusMessage: "Fetch catch error",
error: "rest-tools.ts detect an error in the fetch request",
statusMessage: 'Fetch catch error',
error: 'rest-tools.ts detect an error in the fetch request',
});
}
});
@@ -426,12 +444,12 @@ export function RESTRequestJson<TYPE>(
resolve(value.data);
} else {
reject({
name: "Model check fail",
name: 'Model check fail',
time: Date().toString(),
status: 950,
error: "REST Fail to verify the data",
statusMessage: "API cast ERROR",
message: "api.ts Check type as fail",
error: 'REST Fail to verify the data',
statusMessage: 'API cast ERROR',
message: 'api.ts Check type as fail',
} as RestErrorResponse);
}
})

View File

@@ -16,18 +16,18 @@ import test.kar.archidata.dataAccess.model.SerializeAsJson;
import test.kar.archidata.dataAccess.model.SerializeListAsJson;
import test.kar.archidata.dataAccess.model.SimpleTable;
import test.kar.archidata.dataAccess.model.SimpleTableSoftDelete;
import test.kar.archidata.dataAccess.model.TypeManyToManyRemote;
import test.kar.archidata.dataAccess.model.TypeManyToManyRoot;
import test.kar.archidata.dataAccess.model.TypeManyToManyRootExpand;
import test.kar.archidata.dataAccess.model.TypeManyToOneRemote;
import test.kar.archidata.dataAccess.model.TypeManyToOneRoot;
import test.kar.archidata.dataAccess.model.TypeManyToOneRootExpand;
import test.kar.archidata.dataAccess.model.TypeManyToManyLongRemote;
import test.kar.archidata.dataAccess.model.TypeManyToManyLongRoot;
import test.kar.archidata.dataAccess.model.TypeManyToManyLongRootExpand;
import test.kar.archidata.dataAccess.model.TypeManyToOneLongRemote;
import test.kar.archidata.dataAccess.model.TypeManyToOneLongRoot;
import test.kar.archidata.dataAccess.model.TypeManyToOneLongRootExpand;
import test.kar.archidata.dataAccess.model.TypeManyToOneUUIDRemote;
import test.kar.archidata.dataAccess.model.TypeManyToOneUUIDRoot;
import test.kar.archidata.dataAccess.model.TypeManyToOneUUIDRootExpand;
import test.kar.archidata.dataAccess.model.TypeOneToManyRemote;
import test.kar.archidata.dataAccess.model.TypeOneToManyRoot;
import test.kar.archidata.dataAccess.model.TypeOneToManyRootExpand;
import test.kar.archidata.dataAccess.model.TypeOneToManyLongRemote;
import test.kar.archidata.dataAccess.model.TypeOneToManyLongRoot;
import test.kar.archidata.dataAccess.model.TypeOneToManyLongRootExpand;
import test.kar.archidata.dataAccess.model.TypeOneToManyUUIDRemote;
import test.kar.archidata.dataAccess.model.TypeOneToManyUUIDRoot;
import test.kar.archidata.dataAccess.model.TypeOneToManyUUIDRootExpand;
@@ -56,18 +56,18 @@ public class ConfigureDb {
SerializeListAsJson.class, //
SimpleTable.class, //
SimpleTableSoftDelete.class, //
TypeManyToManyRemote.class, //
TypeManyToManyRoot.class, //
TypeManyToManyRootExpand.class, //
TypeManyToOneRemote.class, //
TypeManyToOneRoot.class, //
TypeManyToOneRootExpand.class, //
TypeManyToManyLongRemote.class, //
TypeManyToManyLongRoot.class, //
TypeManyToManyLongRootExpand.class, //
TypeManyToOneLongRemote.class, //
TypeManyToOneLongRoot.class, //
TypeManyToOneLongRootExpand.class, //
TypeManyToOneUUIDRemote.class, //
TypeManyToOneUUIDRoot.class, //
TypeManyToOneUUIDRootExpand.class, //
TypeOneToManyRemote.class, //
TypeOneToManyRoot.class, //
TypeOneToManyRootExpand.class, //
TypeOneToManyLongRemote.class, //
TypeOneToManyLongRoot.class, //
TypeOneToManyLongRootExpand.class, //
TypeOneToManyUUIDRemote.class, //
TypeOneToManyUUIDRoot.class, //
TypeOneToManyUUIDRootExpand.class, //
@@ -122,7 +122,9 @@ public class ConfigureDb {
LOGGER.error("Fail to clean the DB");
return;
}
config.setDbName(null);
if (!"MONGO".equalsIgnoreCase(modeTest)) {
config.setDbName(null);
}
LOGGER.info("Remove the DB and create a new one '{}'", config.getDbName());
try (final DBAccess daRoot = DBAccess.createInterface(config)) {
if ("SQLITE-MEMORY".equalsIgnoreCase(modeTest)) {

View File

@@ -30,8 +30,6 @@ public class TestTime {
static WebLauncherTest webInterface = null;
static RESTApi api = null;
private static Long idTest = 0L;
@BeforeAll
public static void configureWebServer() throws Exception {
ConfigureDb.configure();

View File

@@ -4,9 +4,9 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.kar.archidata.annotation.ARCHIVE;
import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.RESTORE;
import org.kar.archidata.annotation.apiGenerator.ApiAsyncType;
import org.kar.archidata.annotation.method.ARCHIVE;
import org.kar.archidata.annotation.method.RESTORE;
import org.kar.archidata.exception.NotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -100,7 +100,7 @@ public class TestResource {
@Consumes(MediaType.APPLICATION_JSON)
public SimpleArchiveTable patch(
@PathParam("id") final Long id,
@AsyncType(SimpleArchiveTable.class) final String jsonRequest) throws Exception {
@ApiAsyncType(SimpleArchiveTable.class) final String jsonRequest) throws Exception {
LOGGER.info("patch({})", id);
throw new NotFoundException("element does not exist: " + id);
}

View File

@@ -2,9 +2,9 @@ package test.kar.archidata.apiExtern.resource;
import java.util.List;
import org.kar.archidata.annotation.ARCHIVE;
import org.kar.archidata.annotation.AsyncType;
import org.kar.archidata.annotation.RESTORE;
import org.kar.archidata.annotation.apiGenerator.ApiAsyncType;
import org.kar.archidata.annotation.method.ARCHIVE;
import org.kar.archidata.annotation.method.RESTORE;
import org.kar.archidata.dataAccess.DataAccess;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -66,7 +66,7 @@ public class TestResourceSample {
@Path("{id}")
@PermitAll
@Consumes(MediaType.APPLICATION_JSON)
public SimpleTable patch(@PathParam("id") final Long id, @AsyncType(SimpleTable.class) final String jsonRequest)
public SimpleTable patch(@PathParam("id") final Long id, @ApiAsyncType(SimpleTable.class) final String jsonRequest)
throws Exception {
DataAccess.updateWithJson(SimpleTable.class, id, jsonRequest);
return DataAccess.get(SimpleTable.class, id);

View File

@@ -0,0 +1,240 @@
package test.kar.archidata.checker;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.kar.archidata.exception.InputException;
import test.kar.archidata.checker.model.JpaBaseModel;
import test.kar.archidata.checker.model.JpaBaseModel.JpaBaseModelChecker;
public class TestJPACheckerDecimalMax {
@Test
public void testDecimalMaxIncludeInteger() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMaxIncludeInteger = 75;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeInteger = 74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeInteger = 76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxIncludeInteger = 100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxIncludeIntegerObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMaxIncludeIntegerObject = 75;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeIntegerObject = 74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeIntegerObject = 76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxIncludeIntegerObject = 100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxIncludeLong() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMaxIncludeLong = 75;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeLong = 74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeLong = 76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxIncludeLong = 100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxIncludeLongObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMaxIncludeLongObject = 75L;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeLongObject = 74L;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeLongObject = 76L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxIncludeLongObject = 100L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxIncludeFloat() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
// can not be tested
//data.testDecimalMaxIncludeFloat = 75.56f;
//Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeFloat = 75.5599f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeFloat = 75.5601f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxIncludeFloat = 100f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxIncludeFloatObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMaxIncludeFloatObject = 75.56f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeFloatObject = 75.5599f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeFloatObject = 75.5601f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxIncludeFloatObject = 100f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxIncludeDouble() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
// can not be tested
//data.testDecimalMaxIncludeDouble = 75.56d;
//Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeDouble = 75.5599d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeDouble = 75.5601d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxIncludeDouble = 100d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxIncludeDoubleObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
//data.testDecimalMaxIncludeDoubleObject = 75.56d;
//Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeDoubleObject = 75.5599d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxIncludeDoubleObject = 75.5601d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxIncludeDoubleObject = 100d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
// exclude
@Test
public void testDecimalMaxExcludeInteger() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMaxExcludeInteger = 75;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeInteger = 74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxExcludeInteger = 76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeInteger = 100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxExcludeIntegerObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMaxExcludeIntegerObject = 75;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeIntegerObject = 74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxExcludeIntegerObject = 76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeIntegerObject = 100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxExcludeLong() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMaxExcludeLong = 75;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeLong = 74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxExcludeLong = 76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeLong = 100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxExcludeLongObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMaxExcludeLongObject = 75L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeLongObject = 74L;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxExcludeLongObject = 76L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeLongObject = 100L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxExcludeFloat() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMaxExcludeFloat = 75.56f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeFloat = 75.5599f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxExcludeFloat = 75.5601f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeFloat = 100f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxExcludeFloatObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMaxExcludeFloatObject = 75.56f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeFloatObject = 75.5599f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxExcludeFloatObject = 75.5601f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeFloatObject = 100f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxExcludeDouble() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMaxExcludeDouble = 75.56d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeDouble = 75.5599d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxExcludeDouble = 75.5601d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeDouble = 100d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMaxExcludeDoubleObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMaxExcludeDoubleObject = 75.56d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeDoubleObject = 75.5599d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMaxExcludeDoubleObject = 75.5601d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMaxExcludeDoubleObject = 100d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
}

View File

@@ -0,0 +1,240 @@
package test.kar.archidata.checker;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.kar.archidata.exception.InputException;
import test.kar.archidata.checker.model.JpaBaseModel;
import test.kar.archidata.checker.model.JpaBaseModel.JpaBaseModelChecker;
public class TestJPACheckerDecimalMin {
@Test
public void testDecimalMinIncludeInteger() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinIncludeInteger = -75;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeInteger = -74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeInteger = -76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinIncludeInteger = -100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinIncludeIntegerObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinIncludeIntegerObject = -75;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeIntegerObject = -74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeIntegerObject = -76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinIncludeIntegerObject = -100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinIncludeLong() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinIncludeLong = -75;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeLong = -74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeLong = -76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinIncludeLong = -100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinIncludeLongObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinIncludeLongObject = -75L;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeLongObject = -74L;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeLongObject = -76L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinIncludeLongObject = -100L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinIncludeFloat() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinIncludeFloat = -75.56f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeFloat = -75.5599f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeFloat = -75.5601f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinIncludeFloat = -100f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinIncludeFloatObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinIncludeFloatObject = -75.56f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeFloatObject = -75.5599f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeFloatObject = -75.5601f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinIncludeFloatObject = -100f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinIncludeDouble() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
// can not be tested
//data.testDecimalMinIncludeDouble = -75.56d;
//Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeDouble = -75.5599d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeDouble = -75.5601d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinIncludeDouble = -100d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinIncludeDoubleObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
// can not be tested
//data.testDecimalMinIncludeDoubleObject = -75.56d;
//Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeDoubleObject = -75.5599d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinIncludeDoubleObject = -75.5601d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinIncludeDoubleObject = -100d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
// exclude
@Test
public void testDecimalMinExcludeInteger() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinExcludeInteger = -75;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeInteger = -74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinExcludeInteger = -76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeInteger = -100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinExcludeIntegerObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinExcludeIntegerObject = -75;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeIntegerObject = -74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinExcludeIntegerObject = -76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeIntegerObject = -100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinExcludeLong() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinExcludeLong = -75;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeLong = -74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinExcludeLong = -76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeLong = -100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinExcludeLongObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinExcludeLongObject = -75L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeLongObject = -74L;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinExcludeLongObject = -76L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeLongObject = -100L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinExcludeFloat() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinExcludeFloat = -75.56f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeFloat = -75.5599f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinExcludeFloat = -75.5601f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeFloat = -100f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinExcludeFloatObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinExcludeFloatObject = -75.56f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeFloatObject = -75.5599f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinExcludeFloatObject = -75.5601f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeFloatObject = -100f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinExcludeDouble() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinExcludeDouble = -75.56d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeDouble = -75.5599d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinExcludeDouble = -75.5601d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeDouble = -100d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testDecimalMinExcludeDoubleObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testDecimalMinExcludeDoubleObject = -75.56d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeDoubleObject = -75.5599d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testDecimalMinExcludeDoubleObject = -75.5601d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testDecimalMinExcludeDoubleObject = -100d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
}

View File

@@ -0,0 +1,30 @@
package test.kar.archidata.checker;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.kar.archidata.exception.InputException;
import test.kar.archidata.checker.model.JpaBaseModel;
import test.kar.archidata.checker.model.JpaBaseModel.JpaBaseModelChecker;
public class TestJPACheckerEMail {
@Test
public void testEMail() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testEMail = "s@s.ds";
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testEMail = "yuio.sdf@sqdf.com";
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testEMail = "s@s.s";
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testEMail = "sq@qsd";
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testEMail = "sqsdfsdf";
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testEMail = "56465456";
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
}

View File

@@ -0,0 +1,123 @@
package test.kar.archidata.checker;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.kar.archidata.exception.InputException;
import test.kar.archidata.checker.model.JpaBaseModel;
import test.kar.archidata.checker.model.JpaBaseModel.JpaBaseModelChecker;
public class TestJPACheckerMax {
@Test
public void testMaxInteger() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMaxInteger = 75;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxInteger = 74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxInteger = 76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMaxInteger = 100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMaxIntegerObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMaxIntegerObject = 75;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxIntegerObject = 74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxIntegerObject = 76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMaxIntegerObject = 100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMaxLong() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMaxLong = 75;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxLong = 74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxLong = 76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMaxLong = 100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMaxLongObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMaxLongObject = 75L;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxLongObject = 74L;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxLongObject = 76L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMaxLongObject = 100L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMaxFloat() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMaxFloat = 75f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxFloat = 74.99f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxFloat = 75.01f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMaxFloat = 100f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMaxFloatObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMaxFloatObject = 75f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxFloatObject = 74.99f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxFloatObject = 75.01f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMaxFloatObject = 100f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMaxDouble() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMaxDouble = 75d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxDouble = 74.99d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxDouble = 75.01d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMaxDouble = 100d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMaxDoubleObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMaxDoubleObject = 75d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxDoubleObject = 74.99d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMaxDoubleObject = 75.01d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMaxDoubleObject = 100d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
}

View File

@@ -0,0 +1,123 @@
package test.kar.archidata.checker;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.kar.archidata.exception.InputException;
import test.kar.archidata.checker.model.JpaBaseModel;
import test.kar.archidata.checker.model.JpaBaseModel.JpaBaseModelChecker;
public class TestJPACheckerMin {
@Test
public void testMinInteger() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMinInteger = -75;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinInteger = -74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinInteger = -76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMinInteger = -100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMinIntegerObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMinIntegerObject = -75;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinIntegerObject = -74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinIntegerObject = -76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMinIntegerObject = -100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMinLong() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMinLong = -75;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinLong = -74;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinLong = -76;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMinLong = -100;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMinLongObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMinLongObject = -75L;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinLongObject = -74L;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinLongObject = -76L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMinLongObject = -100L;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMinFloat() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMinFloat = -75f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinFloat = -74.99f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinFloat = -75.01f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMinFloat = -100f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMinFloatObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMinFloatObject = -75f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinFloatObject = -74.99f;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinFloatObject = -75.01f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMinFloatObject = -100f;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMinDouble() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMinDouble = -75d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinDouble = -74.99d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinDouble = -75.01d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMinDouble = -100d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
@Test
public void testMinDoubleObject() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testMinDoubleObject = -75d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinDoubleObject = -74.99d;
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testMinDoubleObject = -75.01d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testMinDoubleObject = -100d;
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
}

View File

@@ -0,0 +1,26 @@
package test.kar.archidata.checker;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.kar.archidata.exception.InputException;
import test.kar.archidata.checker.model.JpaBaseModel;
import test.kar.archidata.checker.model.JpaBaseModel.JpaBaseModelChecker;
public class TestJPACheckerPattern {
@Test
public void testPattern() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testPattern = "0";
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testPattern = "1234567890";
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testPattern = "q";
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testPattern = "qsdf4653";
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
}

View File

@@ -0,0 +1,26 @@
package test.kar.archidata.checker;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.kar.archidata.exception.InputException;
import test.kar.archidata.checker.model.JpaBaseModel;
import test.kar.archidata.checker.model.JpaBaseModel.JpaBaseModelChecker;
public class TestJPACheckerSize {
@Test
public void testSize() throws Exception {
final JpaBaseModelChecker checker = new JpaBaseModelChecker();
final JpaBaseModel data = new JpaBaseModel();
data.testSize = "000";
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testSize = "00000000";
Assertions.assertDoesNotThrow(() -> checker.check(data));
data.testSize = "00";
Assertions.assertThrows(InputException.class, () -> checker.check(data));
data.testSize = "000000000";
Assertions.assertThrows(InputException.class, () -> checker.check(data));
}
}

View File

@@ -0,0 +1,132 @@
package test.kar.archidata.checker.model;
import org.kar.archidata.checker.CheckJPA;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
public class JpaBaseModel {
// Simple checker declaration
public static class JpaBaseModelChecker extends CheckJPA<JpaBaseModel> {
public JpaBaseModelChecker() {
super(JpaBaseModel.class);
}
}
// Simple data to verify if the checker is active
@Size(min = 3, max = 8)
public String testSize;
@Pattern(regexp = "^[0-9]+$")
public String testPattern;
@Email
public String testEMail;
@Min(-75)
public int testMinInteger;
@Min(-75)
public Integer testMinIntegerObject;
@Min(-75)
public long testMinLong;
@Min(-75)
public Long testMinLongObject;
@Min(-75)
public float testMinFloat;
@Min(-75)
public Float testMinFloatObject;
@Min(-75)
public double testMinDouble;
@Min(-75)
public Double testMinDoubleObject;
@Max(75)
public int testMaxInteger;
@Max(75)
public Integer testMaxIntegerObject;
@Max(75)
public long testMaxLong;
@Max(75)
public Long testMaxLongObject;
@Max(75)
public float testMaxFloat;
@Max(75)
public Float testMaxFloatObject;
@Max(75)
public double testMaxDouble;
@Max(75)
public Double testMaxDoubleObject;
@DecimalMin("-75")
public int testDecimalMinIncludeInteger;
@DecimalMin("-75")
public Integer testDecimalMinIncludeIntegerObject;
@DecimalMin("-75")
public long testDecimalMinIncludeLong;
@DecimalMin("-75")
public Long testDecimalMinIncludeLongObject;
@DecimalMin("-75.56")
public float testDecimalMinIncludeFloat;
@DecimalMin("-75.56")
public Float testDecimalMinIncludeFloatObject;
@DecimalMin("-75.56")
public double testDecimalMinIncludeDouble;
@DecimalMin("-75.56")
public Double testDecimalMinIncludeDoubleObject;
@DecimalMax("75")
public int testDecimalMaxIncludeInteger;
@DecimalMax("75")
public Integer testDecimalMaxIncludeIntegerObject;
@DecimalMax("75")
public long testDecimalMaxIncludeLong;
@DecimalMax("75")
public Long testDecimalMaxIncludeLongObject;
@DecimalMax("75.56")
public float testDecimalMaxIncludeFloat;
@DecimalMax("75.56")
public Float testDecimalMaxIncludeFloatObject;
@DecimalMax("75.56")
public double testDecimalMaxIncludeDouble;
@DecimalMax("75.56")
public Double testDecimalMaxIncludeDoubleObject;
@DecimalMin(value = "-75", inclusive = false)
public int testDecimalMinExcludeInteger;
@DecimalMin(value = "-75", inclusive = false)
public Integer testDecimalMinExcludeIntegerObject;
@DecimalMin(value = "-75", inclusive = false)
public long testDecimalMinExcludeLong;
@DecimalMin(value = "-75", inclusive = false)
public Long testDecimalMinExcludeLongObject;
@DecimalMin(value = "-75.56", inclusive = false)
public float testDecimalMinExcludeFloat;
@DecimalMin(value = "-75.56", inclusive = false)
public Float testDecimalMinExcludeFloatObject;
@DecimalMin(value = "-75.56", inclusive = false)
public double testDecimalMinExcludeDouble;
@DecimalMin(value = "-75.56", inclusive = false)
public Double testDecimalMinExcludeDoubleObject;
@DecimalMax(value = "75", inclusive = false)
public int testDecimalMaxExcludeInteger;
@DecimalMax(value = "75", inclusive = false)
public Integer testDecimalMaxExcludeIntegerObject;
@DecimalMax(value = "75", inclusive = false)
public long testDecimalMaxExcludeLong;
@DecimalMax(value = "75", inclusive = false)
public Long testDecimalMaxExcludeLongObject;
@DecimalMax(value = "75.56", inclusive = false)
public float testDecimalMaxExcludeFloat;
@DecimalMax(value = "75.56", inclusive = false)
public Float testDecimalMaxExcludeFloatObject;
@DecimalMax(value = "75.56", inclusive = false)
public double testDecimalMaxExcludeDouble;
@DecimalMax(value = "75.56", inclusive = false)
public Double testDecimalMaxExcludeDoubleObject;
}

View File

@@ -19,14 +19,14 @@ import org.slf4j.LoggerFactory;
import test.kar.archidata.ConfigureDb;
import test.kar.archidata.StepwiseExtension;
import test.kar.archidata.dataAccess.model.TypeManyToManyRemote;
import test.kar.archidata.dataAccess.model.TypeManyToManyRoot;
import test.kar.archidata.dataAccess.model.TypeManyToManyRootExpand;
import test.kar.archidata.dataAccess.model.TypeManyToManyLongRemote;
import test.kar.archidata.dataAccess.model.TypeManyToManyLongRoot;
import test.kar.archidata.dataAccess.model.TypeManyToManyLongRootExpand;
@ExtendWith(StepwiseExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TestManyToMany {
final static private Logger LOGGER = LoggerFactory.getLogger(TestManyToMany.class);
public class TestManyToManyLong {
final static private Logger LOGGER = LoggerFactory.getLogger(TestManyToManyLong.class);
@BeforeAll
public static void configureWebServer() throws Exception {
@@ -41,8 +41,8 @@ public class TestManyToMany {
@Order(1)
@Test
public void testCreateTable() throws Exception {
final List<String> sqlCommand2 = DataFactory.createTable(TypeManyToManyRoot.class);
final List<String> sqlCommand = DataFactory.createTable(TypeManyToManyRemote.class);
final List<String> sqlCommand2 = DataFactory.createTable(TypeManyToManyLongRoot.class);
final List<String> sqlCommand = DataFactory.createTable(TypeManyToManyLongRemote.class);
sqlCommand.addAll(sqlCommand2);
if (ConfigureDb.da instanceof final DBAccessSQL daSQL) {
for (final String elem : sqlCommand) {
@@ -55,16 +55,16 @@ public class TestManyToMany {
@Order(2)
@Test
public void testSimpleInsertAndRetieve() throws Exception {
final TypeManyToManyRoot test = new TypeManyToManyRoot();
final TypeManyToManyLongRoot test = new TypeManyToManyLongRoot();
test.otherData = "kjhlkjlkj";
final TypeManyToManyRoot insertedData = ConfigureDb.da.insert(test);
final TypeManyToManyLongRoot insertedData = ConfigureDb.da.insert(test);
Assertions.assertNotNull(insertedData);
Assertions.assertNotNull(insertedData.id);
Assertions.assertTrue(insertedData.id >= 0);
Assertions.assertNull(insertedData.remote);
// Try to retrieve all the data:
final TypeManyToManyRoot retrieve = ConfigureDb.da.get(TypeManyToManyRoot.class, insertedData.id);
final TypeManyToManyLongRoot retrieve = ConfigureDb.da.get(TypeManyToManyLongRoot.class, insertedData.id);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
@@ -73,33 +73,33 @@ public class TestManyToMany {
Assertions.assertEquals(insertedData.otherData, retrieve.otherData);
Assertions.assertNull(retrieve.remote);
ConfigureDb.da.delete(TypeManyToManyRoot.class, insertedData.id);
ConfigureDb.da.delete(TypeManyToManyLongRoot.class, insertedData.id);
}
@Order(3)
@Test
public void testSimpleInsertAndRetieveSubValues() throws Exception {
TypeManyToManyRemote remote = new TypeManyToManyRemote();
TypeManyToManyLongRemote remote = new TypeManyToManyLongRemote();
remote.data = "remote1";
final TypeManyToManyRemote insertedRemote1 = ConfigureDb.da.insert(remote);
final TypeManyToManyLongRemote insertedRemote1 = ConfigureDb.da.insert(remote);
Assertions.assertEquals(insertedRemote1.data, remote.data);
remote = new TypeManyToManyRemote();
remote = new TypeManyToManyLongRemote();
remote.data = "remote2";
final TypeManyToManyRemote insertedRemote2 = ConfigureDb.da.insert(remote);
final TypeManyToManyLongRemote insertedRemote2 = ConfigureDb.da.insert(remote);
Assertions.assertEquals(insertedRemote2.data, remote.data);
final TypeManyToManyRoot test = new TypeManyToManyRoot();
final TypeManyToManyLongRoot test = new TypeManyToManyLongRoot();
test.otherData = "kjhlkjlkj";
final TypeManyToManyRoot insertedData = ConfigureDb.da.insert(test);
final TypeManyToManyLongRoot insertedData = ConfigureDb.da.insert(test);
Assertions.assertNotNull(insertedData);
Assertions.assertNotNull(insertedData.id);
Assertions.assertTrue(insertedData.id >= 0);
Assertions.assertNull(insertedData.remote);
// Try to retrieve all the data:
TypeManyToManyRoot retrieve = ConfigureDb.da.get(TypeManyToManyRoot.class, insertedData.id);
TypeManyToManyLongRoot retrieve = ConfigureDb.da.get(TypeManyToManyLongRoot.class, insertedData.id);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
@@ -109,10 +109,12 @@ public class TestManyToMany {
Assertions.assertNull(retrieve.remote);
// Add remote elements
AddOnManyToMany.addLink(ConfigureDb.da, TypeManyToManyRoot.class, retrieve.id, "remote", insertedRemote1.id);
AddOnManyToMany.addLink(ConfigureDb.da, TypeManyToManyRoot.class, retrieve.id, "remote", insertedRemote2.id);
AddOnManyToMany.addLink(ConfigureDb.da, TypeManyToManyLongRoot.class, retrieve.id, "remote",
insertedRemote1.id);
AddOnManyToMany.addLink(ConfigureDb.da, TypeManyToManyLongRoot.class, retrieve.id, "remote",
insertedRemote2.id);
retrieve = ConfigureDb.da.get(TypeManyToManyRoot.class, insertedData.id);
retrieve = ConfigureDb.da.get(TypeManyToManyLongRoot.class, insertedData.id);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
@@ -124,7 +126,7 @@ public class TestManyToMany {
Assertions.assertEquals(retrieve.remote.get(0), insertedRemote1.id);
Assertions.assertEquals(retrieve.remote.get(1), insertedRemote2.id);
final TypeManyToManyRootExpand retrieveExpand = ConfigureDb.da.get(TypeManyToManyRootExpand.class,
final TypeManyToManyLongRootExpand retrieveExpand = ConfigureDb.da.get(TypeManyToManyLongRootExpand.class,
insertedData.id);
Assertions.assertNotNull(retrieveExpand);
@@ -138,11 +140,11 @@ public class TestManyToMany {
Assertions.assertEquals(retrieveExpand.remote.get(1).id, insertedRemote2.id);
// Remove an element
long count = AddOnManyToMany.removeLink(ConfigureDb.da, TypeManyToManyRoot.class, retrieve.id, "remote",
long count = AddOnManyToMany.removeLink(ConfigureDb.da, TypeManyToManyLongRoot.class, retrieve.id, "remote",
insertedRemote1.id);
Assertions.assertEquals(1, count);
retrieve = ConfigureDb.da.get(TypeManyToManyRoot.class, insertedData.id);
retrieve = ConfigureDb.da.get(TypeManyToManyLongRoot.class, insertedData.id);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
@@ -154,11 +156,11 @@ public class TestManyToMany {
Assertions.assertEquals(retrieve.remote.get(0), insertedRemote2.id);
// Remove the second element
count = AddOnManyToMany.removeLink(ConfigureDb.da, TypeManyToManyRoot.class, retrieve.id, "remote",
count = AddOnManyToMany.removeLink(ConfigureDb.da, TypeManyToManyLongRoot.class, retrieve.id, "remote",
insertedRemote2.id);
Assertions.assertEquals(1, count);
retrieve = ConfigureDb.da.get(TypeManyToManyRoot.class, insertedData.id);
retrieve = ConfigureDb.da.get(TypeManyToManyLongRoot.class, insertedData.id);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
@@ -167,7 +169,7 @@ public class TestManyToMany {
Assertions.assertEquals(insertedData.otherData, retrieve.otherData);
Assertions.assertNull(retrieve.remote);
ConfigureDb.da.delete(TypeManyToManyRoot.class, insertedData.id);
ConfigureDb.da.delete(TypeManyToManyLongRoot.class, insertedData.id);
}
/* API TODO: - Replace list (permet de les ordonnées) - remove all links - delete en cascade .... (compliqué...) */

Some files were not shown because too many files have changed in this diff Show More