Compare commits

...

35 Commits

Author SHA1 Message Date
62435cd97e [RELEASE] new version 0.6.0 2024-01-06 21:24:21 +01:00
d983210a10 [DE] update rteadme and .gitignore 2024-01-06 21:03:03 +01:00
a704008f7d [DEV] format pom.xml 2024-01-06 09:07:03 +01:00
04a2014799 Update LICENSE 2024-01-05 23:57:34 +01:00
b74b404e72 wrong position of dependabot 2024-01-05 23:56:15 +01:00
42245e026b Create dependabot.yml 2024-01-05 23:55:37 +01:00
284c00a88c [CI] update build path to generic path 2024-01-05 23:52:20 +01:00
0ae8f9ad16 [DEV] remove codacity 2024-01-05 15:28:41 +01:00
7b31c66ce5 [DEV] correct delete API for test 2024-01-05 15:27:55 +01:00
fe63c6b954 try to find a correct maven plugin for dependecy
Signed-off-by: Edouard DUPIN <yui.heero@gmail.com>
2024-01-05 00:11:09 +01:00
b4b6e40250 [DEV] update generation of dependency 2024-01-04 23:51:48 +01:00
4d1f70a637 [DEV] add dependabot 2024-01-04 23:51:44 +01:00
86dc0351fe Update maven.yml 2024-01-04 23:37:49 +01:00
a049d3ca30 Create maven.yml 2024-01-04 23:22:50 +01:00
c90af0bdc9 Create codacy.yml 2024-01-04 23:21:50 +01:00
f3ea7983df [DEV] add clean all for test mode 2024-01-04 21:45:52 +01:00
ba314a97ed [DEV] reduce step time between migration 2024-01-03 00:18:43 +01:00
c99fb84ef4 [DEV] add list at insert 2024-01-03 00:18:20 +01:00
eff5513705 [DEV] Add drop table to improve testing 2024-01-02 12:05:44 +01:00
66796d9591 [DEV] try to fix the ManyToMany link table insertion and update to simplify API 2023-12-31 09:34:46 +01:00
554e2493aa [DEBUG] correct link table (remove the deleted field) 2023-12-31 09:34:10 +01:00
49654698a6 [DEV] remove the timeout control in test mode to permit @swagger to have generic token 2023-12-31 09:32:40 +01:00
61f3e70835 update REST API caller 2023-12-31 09:31:56 +01:00
6ac2f1dcfc [DEV] update comment of swagger 2023-12-31 09:31:34 +01:00
5b7fdce349 [DEV] add specific jackson catcher 2023-12-31 09:29:34 +01:00
058fb2e640 [DEV] fix size error in string 2023-12-31 09:28:42 +01:00
dfc7c77f47 [DEV] update with openAPI 2023-12-27 11:08:18 +01:00
1c82fb1a86 [DEV] upgrade library start to be really cool 2023-12-22 23:34:22 +01:00
3b0c73bd55 [DEV] continue normalisation API 2023-12-18 07:32:46 +01:00
d9fcacc812 [DEV] rework to finalize API 2023-12-16 11:09:53 +01:00
5fc45a23d3 [DEV] add a simple JPA checker to simplify client code 2023-12-10 23:58:38 +01:00
81b6438a7c [DEV] correct the test mode 2023-12-10 23:25:02 +01:00
26ba20d964 [DEV] continue integrations 2023-12-08 20:15:20 +01:00
659f9ca306 [FIX] test unit and style 2023-12-08 20:15:20 +01:00
9f337db62c [DEV] update dev tag version 2023-11-29 19:15:02 +01:00
61 changed files with 2190 additions and 497 deletions

View File

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

12
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# 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
schedule:
interval: "weekly"

37
.github/workflows/maven.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Java CI with Maven
on:
push:
branches: [ "develop" ]
pull_request:
branches: [ "develop" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B package --file pom.xml
- name: find the element
run: find . -name "dependency-graph.json"
# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
- name: Update dependency graph
uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
out/
target/
*.class
.settings/

375
LICENSE
View File

@@ -1,6 +1,373 @@
PROPIETARY licence
==================
Mozilla Public License Version 2.0
==================================
Copyright at Edouard DUPIN
1. Definitions
--------------
you have no right
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View File

@@ -1,18 +1,60 @@
Generic backend for archidata in java
===================================
Archi-data
==========
Archi-data is a framework that simplify:
- Creating a REST server with:
- Right control
- Swagger display interface
- Normalize error generate by the server
- Access to the DB:
- introspect Object and insert in the TD (SQLITE & MY-SQL)
- Manage migration
- JPA checker for many generic request
- simplify the request of the Test-service
Develop in cmd-line:
--------------------
The first step is configuring your JAVA version (or select the JVM with the OS)
```bash
export PATH=$(ls -d --color=never /usr/lib/jvm/java-2*-openjdk)/bin:$PATH
```
Install the dependency:
```bash
mvn install
```
// create a single package jar
mvn clean compile assembly:single
Run the test
```bash
mvn test
```
Install it for external use
```bash
mvn install
```
Develop With Eclipse:
--------------------
generic interface for all KAR web application
Import the project:
- Open a (new) project on eclipse
- `File` -> `Import`
- `Maven` -> `Existing Maven project`
- Select the `pom.xml` file and click on import
Run the Test:
- Open the package `test.kar.archidata`
- Click right on it
- Select `Debug As` -> `JUnit Test`
Install in the local maven repository:
- Click right on the `pom.xml` file
- Select `Run As` -> `Maven install`
Somes tools:
@@ -21,7 +63,7 @@ Somes tools:
Auto-update dependency:
-----------------------
auto-update to the last version dependency:
Auto-update to the last version dependency:
```bash
mvn versions:use-latest-versions
@@ -36,6 +78,21 @@ Simply run the cmd-line:
mvn formatter:format
```
Reformat XML file like the pom.xml
```bash
XMLLINT_INDENT=" " xmllint --format "back/pom.xml" -o "back/pom.xml"
```
Enable the pre-commit checker
-----------------------------
```bash
./tools/configure_precommit.bash
```
> **_Note_**: You can change the code in `.git/hooks/pre-commit` by replacing `formatter:verify` with `formatter:format` to auto format the code @ every commit
Add Gitea in the dependency for the registry:
=============================================
@@ -65,8 +122,8 @@ edit file: ```~/.m2/settings.xml```
release:
========
```
export PATH=/usr/lib/jvm/java-19-openjdk/bin:$PATH
```bash
export PATH=$(ls -d --color=never /usr/lib/jvm/java-2*-openjdk)/bin:$PATH
mvn install
mvn deploy
```

1
dependabot.yml Normal file
View File

@@ -0,0 +1 @@

111
pom.xml
View File

@@ -3,19 +3,16 @@
<modelVersion>4.0.0</modelVersion>
<groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId>
<version>0.5.0</version>
<version>0.6.0</version>
<properties>
<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.1</jersey.version>
<jersey.version>3.1.5</jersey.version>
<jaxb.version>2.3.1</jaxb.version>
<istack.version>4.1.1</istack.version>
</properties>
<repositories>
<repository>
<id>gitea</id>
@@ -32,7 +29,6 @@
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
</snapshotRepository>
</distributionManagement>
<dependencyManagement>
<dependencies>
<dependency>
@@ -44,7 +40,6 @@
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
@@ -64,8 +59,8 @@
<artifactId>jersey-media-multipart</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
@@ -81,14 +76,14 @@
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.4</version>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.4</version>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.1.0</version>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
@@ -109,39 +104,45 @@
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.0</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0-M1</version>
<scope>provided</scope>
</dependency>
<!-- Interface for My-sql & sqlite DB -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.0</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.44.1.0</version>
</dependency>
<!-- Interface for JWT token -->
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0-M1</version>
<scope>provided</scope>
</dependency>
<!-- Interface for My-sql & sqlite DB -->
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.37.1</version>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.40.0.0</version>
</dependency>
<!-- Interface for JWT token -->
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.37.1</version>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.2.0-M1</version>
</dependency>
<!-- Swagger dependencies -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-jakarta</artifactId>
<version>2.1.10</version>
</dependency>
<!--
************************************************************
** TEST dependency **
@@ -153,18 +154,16 @@
<version>5.10.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<testSourceDirectory>test/src</testSourceDirectory>
<directory>${project.basedir}/out/maven/</directory>
<testResources>
<testResource>
<directory>${basedir}/test/resources</directory>
@@ -185,6 +184,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -194,13 +194,27 @@
</execution>
</executions>
</plugin>
<!-- For dependabot plugin -->
<plugin>
<groupId>org.apache.servicemix.tooling</groupId>
<artifactId>depends-maven-plugin</artifactId>
<version>1.5.0</version>
<executions>
<execution>
<id>generate-depends-file</id>
<goals>
<goal>generate-depends-file</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- junit results -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
<plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
@@ -212,7 +226,7 @@
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugin>
<!-- Java-doc generation for stand-alone site -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -278,5 +292,4 @@
</plugin>
</plugins>
</reporting>
</project>

View File

@@ -10,11 +10,18 @@ import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
public class AnnotationTools {
static final Logger LOGGER = LoggerFactory.getLogger(AnnotationTools.class);
@@ -45,10 +52,21 @@ public class AnnotationTools {
return tmp;
}
public static String getSchemedescription(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
}
return ((Schema) annotation[0]).description();
}
public static String getComment(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataComment.class);
if (annotation.length == 0) {
return null;
return getSchemedescription(element);
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @DataComment on " + element.getClass().getCanonicalName());
@@ -67,7 +85,51 @@ public class AnnotationTools {
return ((DataDefault) annotation[0]).value();
}
public static Integer getLimitSize(final Field element) throws Exception {
public static ManyToOne getManyToOne(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(ManyToOne.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @ManyToOne on " + element.getClass().getCanonicalName());
}
return (ManyToOne) annotation[0];
}
public static DataJson getDataJson(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataJson.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @ManyToOne on " + element.getClass().getCanonicalName());
}
return (DataJson) annotation[0];
}
public static Long getConstraintsMax(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Max.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
}
return ((Max) annotation[0]).value();
}
public static Long getConstraintsMin(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Min.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
}
return ((Min) annotation[0]).value();
}
public static int getLimitSize(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) {
return 255;
@@ -76,7 +138,29 @@ public class AnnotationTools {
throw new Exception("Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
}
final int length = ((Column) annotation[0]).length();
return length <= 0 ? null : length;
return length <= 0 ? 0 : length;
}
public static Size getConstraintsSize(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Size.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
}
return (Size) annotation[0];
}
public static String getConstraintsPattern(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Pattern.class);
if (annotation.length == 0) {
return null;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @Pattern on " + element.getClass().getCanonicalName());
}
return ((Pattern) annotation[0]).regexp();
}
public static boolean isAnnotationGroup(final Field field, final Class<?> annotationType) {
@@ -117,7 +201,7 @@ public class AnnotationTools {
return name;
}
public static boolean getNotNull(final Field element) throws Exception {
public static boolean getColumnNotNull(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) {
return false;
@@ -128,7 +212,26 @@ public class AnnotationTools {
return !((Column) annotation[0]).nullable();
}
public static boolean getConstraintsNotNull(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(NotNull.class);
if (annotation.length == 0) {
return false;
}
if (annotation.length > 1) {
throw new Exception("Must not have more than 1 element @NotNull on " + element.getClass().getCanonicalName());
}
return true;
}
public static boolean isPrimaryKey(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Id.class);
if (annotation.length == 0) {
return false;
}
return true;
}
public static boolean isUnique(final Field element) throws Exception {
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
if (annotation.length == 0) {
return false;
@@ -245,7 +348,7 @@ public class AnnotationTools {
}
public static boolean isGenericField(final Field elem) throws Exception {
return AnnotationTools.isPrimaryKey(elem) || AnnotationTools.isCreatedAtField(elem) || AnnotationTools.isUpdateAtField(elem);
return AnnotationTools.isPrimaryKey(elem) || AnnotationTools.isCreatedAtField(elem) || AnnotationTools.isUpdateAtField(elem) || AnnotationTools.isDeletedField(elem);
}
public static Field getFieldOfId(final Class<?> clazz) throws Exception {

View File

@@ -7,8 +7,7 @@ import java.lang.annotation.Target;
@Target({ ElementType.TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Deprecated(since = "0.5.2")
public @interface DataComment {
String value();
}

View File

@@ -5,6 +5,11 @@ 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;
@Target({ ElementType.TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DataJson {}
public @interface DataJson {
Class<? extends CheckFunctionInterface> checker() default CheckFunctionVoid.class;
}

View File

@@ -24,12 +24,14 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.security.PermitTokenInURI;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.filter.GenericContext;
import org.kar.archidata.model.Data;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
@@ -94,7 +96,7 @@ public class DataResource {
public static Data getWithSha512(final String sha512) {
LOGGER.info("find sha512 = {}", sha512);
try {
return DataAccess.getWhere(Data.class, new QueryCondition("sha512", "=", sha512));
return DataAccess.getWhere(Data.class, new Condition(new QueryCondition("sha512", "=", sha512)));
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
@@ -219,6 +221,7 @@ public class DataResource {
@Path("/upload/")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@RolesAllowed("ADMIN")
@Operation(description = "Insert a new data in the data environment", tags = "SYSTEM")
public Response uploadFile(@Context final SecurityContext sc, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData) {
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
LOGGER.info("===================================================");
@@ -242,6 +245,7 @@ public class DataResource {
@PermitTokenInURI
@RolesAllowed("USER")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Operation(description = "Get back some data from the data environment", tags = "SYSTEM")
public Response retriveDataId(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range,
@PathParam("id") final Long id) throws Exception {
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
@@ -260,6 +264,7 @@ public class DataResource {
@RolesAllowed("USER")
@PermitTokenInURI
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Operation(description = "Get a thumbnail of from the data environment (if resize is possible)", tags = "SYSTEM")
// @CacheMaxAge(time = 10, unit = TimeUnit.DAYS)
public Response retriveDataThumbnailId(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range,
@PathParam("id") final Long id) throws Exception {
@@ -319,6 +324,7 @@ public class DataResource {
@PermitTokenInURI
@RolesAllowed("USER")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Operation(description = "Get back some data from the data environment (with a beautifull name (permit download with basic name)", tags = "SYSTEM")
public Response retriveDataFull(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range,
@PathParam("id") final Long id, @PathParam("name") final String name) throws Exception {
final GenericContext gc = (GenericContext) sc.getUserPrincipal();

View File

@@ -6,6 +6,7 @@ import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.PermitAll;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
@@ -88,6 +89,7 @@ public class FrontGeneric {
@GET
@PermitAll()
@Operation(description = "Retrieve native element (index)", tags = "SYSTEM")
// @Produces(MediaType.APPLICATION_OCTET_STREAM)
// @CacheMaxAge(time = 1, unit = TimeUnit.DAYS)
public Response retrive0() throws Exception {
@@ -97,6 +99,7 @@ public class FrontGeneric {
@GET
@Path("{any: .*}")
@PermitAll()
@Operation(description = "Get specific file from the front environment", tags = "SYSTEM")
// @Produces(MediaType.APPLICATION_OCTET_STREAM)
// @CacheMaxAge(time = 10, unit = TimeUnit.DAYS)
public Response retrive1(@PathParam("any") final List<PathSegment> segments) throws Exception {

View File

@@ -0,0 +1,33 @@
package org.kar.archidata.api;
import io.swagger.v3.jaxrs2.integration.resources.BaseOpenApiResource;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.security.PermitAll;
import jakarta.servlet.ServletConfig;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Application;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;
@Path("/openapi")
public class openApiResource extends BaseOpenApiResource {
@Context
ServletConfig config;
@Context
Application app;
@GET
@Path("swagger.json")
@Produces({ MediaType.APPLICATION_JSON })
@PermitAll
@Operation(hidden = true, description = "Get the OPEN-API description", tags = "SYSTEM")
public Response getDescription(@Context final HttpHeaders headers, @Context final UriInfo uriInfo) throws Exception {
return getOpenApi(headers, this.config, this.app, uriInfo, "json");
}
}

View File

@@ -14,8 +14,8 @@ public class InputExceptionCatcher implements ExceptionMapper<InputException> {
@Override
public Response toResponse(final InputException exception) {
final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={}", ret.uuid);
exception.printStackTrace();
LOGGER.error("Error UUID={} ==> '{}'=>'{}'", ret.uuid, exception.missingVariable, exception.getLocalizedMessage());
// exception.printStackTrace();
return Response.status(exception.status).entity(ret).type(MediaType.APPLICATION_JSON).build();
}

View File

@@ -0,0 +1,28 @@
package org.kar.archidata.catcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
public class JacksonCatcher implements ExceptionMapper<JsonProcessingException> {
private static final Logger LOGGER = LoggerFactory.getLogger(JacksonCatcher.class);
@Override
public Response toResponse(final JsonProcessingException exception) {
LOGGER.warn("Catch exception Input data parsing:");
final RestErrorResponse ret = build(exception);
LOGGER.error("Error UUID={}", ret.uuid);
exception.printStackTrace();
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ret).type(MediaType.APPLICATION_JSON).build();
}
private RestErrorResponse build(final Exception exception) {
return new RestErrorResponse(Response.Status.INTERNAL_SERVER_ERROR, "Catch Unknown Exception", exception.getMessage());
}
}

View File

@@ -1,6 +1,7 @@
package org.kar.archidata.dataAccess;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -9,9 +10,7 @@ import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -25,9 +24,10 @@ import org.kar.archidata.dataAccess.addOn.AddOnDataJson;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.addOn.AddOnManyToOne;
import org.kar.archidata.dataAccess.addOn.AddOnSQLTableExternalForeinKeyAsList;
import org.kar.archidata.dataAccess.options.AccessDeletedItems;
import org.kar.archidata.dataAccess.options.CheckFunction;
import org.kar.archidata.dataAccess.options.Limit;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.FilterValue;
import org.kar.archidata.dataAccess.options.TransmitKey;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;
@@ -460,7 +460,8 @@ public class DataAccess {
return null;
}
public static <T> List<T> insertMultiple(final List<T> data, final QueryOptions options) throws Exception {
// TODO: manage insert batch...
public static <T> List<T> insertMultiple(final List<T> data, final QueryOption... options) throws Exception {
final List<T> out = new ArrayList<>();
for (final T elem : data) {
final T tmp = insert(elem, options);
@@ -469,25 +470,22 @@ public class DataAccess {
return out;
}
public static <T> T insert(final T data) throws Exception {
return insert(data, null);
}
public static <T> T insert(final T data, final QueryOptions options) throws Exception {
public static <T> T insert(final T data, final QueryOption... option) throws Exception {
final Class<?> clazz = data.getClass();
final QueryOptions options = new QueryOptions(option);
// External checker of data:
if (options != null) {
final CheckFunction check = options.get(CheckFunction.class);
if (check != null) {
check.getChecker().check(data, null);
}
final CheckFunction check = options.get(CheckFunction.class);
if (check != null) {
check.getChecker().check("", data, AnnotationTools.getFieldsNames(clazz));
}
DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
final List<Field> asyncFieldUpdate = new ArrayList<>();
Long uniqueSQLID = null;
final String tableName = AnnotationTools.getTableName(clazz, options);
// real add in the BDD:
try {
final String tableName = AnnotationTools.getTableName(clazz, options);
// boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
final StringBuilder query = new StringBuilder();
query.append("INSERT INTO `");
@@ -496,34 +494,37 @@ public class DataAccess {
boolean firstField = true;
int count = 0;
for (final Field elem : clazz.getFields()) {
for (final Field field : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) {
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
continue;
}
if (AnnotationTools.isPrimaryKey(elem)) {
if (AnnotationTools.isPrimaryKey(field)) {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(elem);
if (addOn != null && !addOn.canInsert(elem)) {
final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && !addOn.canInsert(field)) {
if (addOn.isInsertAsync(field)) {
asyncFieldUpdate.add(field);
}
continue;
}
final boolean createTime = elem.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
final boolean createTime = field.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
if (createTime) {
continue;
}
final boolean updateTime = elem.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
final boolean updateTime = field.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
if (updateTime) {
continue;
}
if (!elem.getClass().isPrimitive()) {
final Object tmp = elem.get(data);
if (tmp == null && elem.getDeclaredAnnotationsByType(DataDefault.class).length != 0) {
if (!field.getClass().isPrimitive()) {
final Object tmp = field.get(data);
if (tmp == null && field.getDeclaredAnnotationsByType(DataDefault.class).length != 0) {
continue;
}
}
count++;
final String name = AnnotationTools.getFieldName(elem);
final String name = AnnotationTools.getFieldName(field);
if (firstField) {
firstField = false;
} else {
@@ -544,6 +545,10 @@ public class DataAccess {
query.append("?");
}
query.append(")");
final OrderBy orders = options.get(OrderBy.class);
if (orders != null) {
orders.generateQuerry(query, tableName);
}
LOGGER.warn("generate the query: '{}'", query.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
@@ -591,7 +596,6 @@ public class DataAccess {
if (affectedRows == 0) {
throw new SQLException("Creating node failed, no rows affected.");
}
Long uniqueSQLID = null;
// Retrieve uid inserted
try (ResultSet generatedKeys = ps.getGeneratedKeys()) {
if (generatedKeys.next()) {
@@ -620,6 +624,14 @@ public class DataAccess {
entry.close();
entry = null;
}
final List<LazyGetter> asyncActions = new ArrayList<>();
for (final Field field : asyncFieldUpdate) {
final DataAccessAddOn addOn = findAddOnforField(field);
addOn.asyncInsert(tableName, uniqueSQLID, field, field.get(data), asyncActions);
}
for (final LazyGetter action : asyncActions) {
action.doRequest();
}
return data;
}
@@ -628,7 +640,6 @@ public class DataAccess {
final ObjectMapper mapper = new ObjectMapper();
// parse the object to be sure the data are valid:
final T data = mapper.readValue(jsonData, clazz);
return insert(data);
}
@@ -640,7 +651,13 @@ public class DataAccess {
}
// check the compatibility of the id and the declared ID
final Class<?> typeClass = idField.getType();
if (idKey == typeClass) {
if (idKey == null) {
throw new DataAccessException("Try to identify the ID type and object wa null.");
}
if (idKey.getClass() != typeClass) {
if (idKey.getClass() == Condition.class) {
throw new DataAccessException("Try to identify the ID type on a condition 'close' internal API error use xxxWhere(...) instead.");
}
throw new DataAccessException("Request update with the wrong type ...");
}
return new QueryCondition(AnnotationTools.getFieldName(idField), "=", idKey);
@@ -655,11 +672,23 @@ public class DataAccess {
* @param jsonData Json data (partial) values to update
* @return the number of object updated
* @throws Exception */
public static <T, ID_TYPE> int updateWithJson(final Class<T> clazz, final ID_TYPE id, final String jsonData) throws Exception {
return updateWhereWithJson(clazz, getTableIdCondition(clazz, id), jsonData);
public static <T, ID_TYPE> int updateWithJson(final Class<T> clazz, final ID_TYPE id, final String jsonData, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
final Condition condition = options.get(Condition.class);
if (condition != null) {
throw new DataAccessException("request a updateWithJson with a condition");
}
options.add(new Condition(getTableIdCondition(clazz, id)));
options.add(new TransmitKey(id));
return updateWhereWithJson(clazz, jsonData, options.getAllArray());
}
public static <T> int updateWhereWithJson(final Class<T> clazz, final QueryItem condition, final String jsonData) throws Exception {
public static <T> int updateWhereWithJson(final Class<T> clazz, final String jsonData, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
final Condition condition = options.get(Condition.class);
if (condition == null) {
throw new DataAccessException("request a updateWhereWithJson without any condition");
}
final ObjectMapper mapper = new ObjectMapper();
// parse the object to be sure the data are valid:
final T data = mapper.readValue(jsonData, clazz);
@@ -668,15 +697,12 @@ public class DataAccess {
final List<String> keys = new ArrayList<>();
final var iterator = root.fieldNames();
iterator.forEachRemaining(e -> keys.add(e));
return updateWhere(data, condition, null, keys);
options.add(new FilterValue(keys));
return updateWhere(data, options.getAllArray());
}
public static <T, ID_TYPE> int update(final T data, final ID_TYPE id) throws Exception {
return update(data, id, null);
}
public static <T> int updateWhere(final T data, final QueryItem condition) throws Exception {
return updateWhere(data, condition, null, null);
return update(data, id, AnnotationTools.getFieldsNames(data.getClass()));
}
/** @param <T>
@@ -685,22 +711,29 @@ public class DataAccess {
* @param filterValue
* @return the affected rows.
* @throws Exception */
public static <T, ID_TYPE> int update(final T data, final ID_TYPE id, final List<String> filterValue) throws Exception {
return updateWhere(data, getTableIdCondition(data.getClass(), id), null, filterValue);
public static <T, ID_TYPE> int update(final T data, final ID_TYPE id, final List<String> updateColomn) throws Exception {
return updateWhere(data, new Condition(getTableIdCondition(data.getClass(), id)), new FilterValue(updateColomn), new TransmitKey(id));
}
public static <T> int updateWhere(final T data, final QueryItem condition, final QueryOptions options, final List<String> filterValue) throws Exception {
public static <T> int updateWhere(final T data, final QueryOption... option) throws Exception {
final Class<?> clazz = data.getClass();
// public static NodeSmall createNode(String typeInNode, String name, String description, Long parentId) {
final QueryOptions options = new QueryOptions(option);
final Condition condition = options.get(Condition.class);
if (condition == null) {
throw new DataAccessException("request a gets without any condition");
}
final FilterValue filter = options.get(FilterValue.class);
if (filter == null) {
throw new DataAccessException("request a gets without any filter values");
}
// External checker of data:
if (options != null) {
final CheckFunction check = options.get(CheckFunction.class);
if (check != null) {
check.getChecker().check(data, filterValue);
check.getChecker().check("", data, filter.getValues());
}
}
final List<LazyGetter> asyncActions = new ArrayList<>();
DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
// real add in the BDD:
try {
@@ -718,15 +751,20 @@ public class DataAccess {
continue;
}
final String name = AnnotationTools.getFieldName(field);
if (filterValue != null) {
if (!filterValue.contains(name)) {
continue;
}
if (!filter.getValues().contains(name)) {
continue;
} else if (AnnotationTools.isGenericField(field)) {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && !addOn.canInsert(field)) {
if (addOn.isInsertAsync(field)) {
final TransmitKey transmitKey = options.get(TransmitKey.class);
if (transmitKey == null) {
throw new DataAccessException("Fail to transmit Key to update the async update...");
}
addOn.asyncUpdate(tableName, transmitKey.getKey(), field, field.get(data), asyncActions);
}
continue;
}
if (!field.getClass().isPrimitive()) {
@@ -745,122 +783,108 @@ public class DataAccess {
query.append("` = ? ");
}
query.append(" ");
final OrderBy orders = options.get(OrderBy.class);
if (orders != null) {
orders.generateQuerry(query, tableName);
}
query.append(" ");
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
whereAppendQuery(query, tableName, condition, null, deletedFieldName);
firstField = true;
LOGGER.debug("generate the query: '{}'", query.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
final CountInOut iii = new CountInOut(1);
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 name = AnnotationTools.getFieldName(field);
if (filterValue != null) {
if (!filterValue.contains(name)) {
condition.whereAppendQuery(query, tableName, null, deletedFieldName);
// If the first field is not set, then nothing to update n the main base:
if (!firstField) {
LOGGER.debug("generate the query: '{}'", query.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
final CountInOut iii = new CountInOut(1);
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;
}
} else if (AnnotationTools.isGenericField(field)) {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && !addOn.canInsert(field)) {
continue;
}
if (addOn == null) {
final Class<?> type = field.getType();
if (!type.isPrimitive()) {
final Object tmp = field.get(data);
if (tmp == null && field.getDeclaredAnnotationsByType(DataDefault.class).length != 0) {
continue;
}
final String name = AnnotationTools.getFieldName(field);
if (!filter.getValues().contains(name)) {
continue;
} else if (AnnotationTools.isGenericField(field)) {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && !addOn.canInsert(field)) {
continue;
}
if (addOn == null) {
final Class<?> type = field.getType();
if (!type.isPrimitive()) {
final Object tmp = field.get(data);
if (tmp == null && field.getDeclaredAnnotationsByType(DataDefault.class).length != 0) {
continue;
}
}
setValuedb(type, data, iii, field, ps);
} else {
addOn.insertData(ps, field, data, iii);
}
setValuedb(type, data, iii, field, ps);
} else {
addOn.insertData(ps, field, data, iii);
}
condition.injectQuerry(ps, iii);
return ps.executeUpdate();
}
whereInjectValue(ps, condition, iii);
return ps.executeUpdate();
} catch (final SQLException ex) {
ex.printStackTrace();
} finally {
entry.close();
entry = null;
}
for (final LazyGetter action : asyncActions) {
action.doRequest();
}
return 0;
}
static void addElement(final PreparedStatement ps, final Object value, final CountInOut iii) throws Exception {
if (value instanceof final Long tmp) {
LOGGER.debug("Inject Long => {}", tmp);
ps.setLong(iii.value, tmp);
} else if (value instanceof final Integer tmp) {
LOGGER.debug("Inject Integer => {}", tmp);
ps.setInt(iii.value, tmp);
} else if (value instanceof final String tmp) {
LOGGER.debug("Inject String => {}", tmp);
ps.setString(iii.value, tmp);
} else if (value instanceof final Short tmp) {
LOGGER.debug("Inject Short => {}", tmp);
ps.setShort(iii.value, tmp);
} else if (value instanceof final Byte tmp) {
LOGGER.debug("Inject Byte => {}", tmp);
ps.setByte(iii.value, tmp);
} else if (value instanceof final Float tmp) {
LOGGER.debug("Inject Float => {}", tmp);
ps.setFloat(iii.value, tmp);
} else if (value instanceof final Double tmp) {
LOGGER.debug("Inject Double => {}", tmp);
ps.setDouble(iii.value, tmp);
} else if (value instanceof final Boolean tmp) {
LOGGER.debug("Inject Boolean => {}", tmp);
ps.setBoolean(iii.value, tmp);
} else if (value instanceof final Timestamp tmp) {
LOGGER.debug("Inject Timestamp => {}", tmp);
ps.setTimestamp(iii.value, tmp);
} else if (value instanceof final Date tmp) {
LOGGER.debug("Inject Date => {}", tmp);
ps.setTimestamp(iii.value, java.sql.Timestamp.from((tmp).toInstant()));
} else if (value instanceof final LocalDate tmp) {
LOGGER.debug("Inject LocalDate => {}", tmp);
ps.setDate(iii.value, java.sql.Date.valueOf(tmp));
} else if (value instanceof final LocalTime tmp) {
LOGGER.debug("Inject LocalTime => {}", tmp);
ps.setTime(iii.value, java.sql.Time.valueOf(tmp));
} else if (value.getClass().isEnum()) {
LOGGER.debug("Inject ENUM => {}", value.toString());
ps.setString(iii.value, value.toString());
} else {
throw new DataAccessException("Not manage type ==> need to add it ...");
}
}
public static void whereAppendQuery(final StringBuilder query, final String tableName, final QueryItem condition, final QueryOptions options, final String deletedFieldName) {
boolean exclude_deleted = true;
if (options != null) {
exclude_deleted = !options.exist(AccessDeletedItems.class);
}
// Check if we have a condition to generate
if (condition == null) {
if (exclude_deleted && deletedFieldName != null) {
query.append(" WHERE ");
query.append(tableName);
query.append(".");
query.append(deletedFieldName);
query.append(" = false ");
}
return;
}
query.append(" WHERE (");
condition.generateQuerry(query, tableName);
query.append(") ");
if (exclude_deleted && deletedFieldName != null) {
query.append("AND ");
query.append(tableName);
query.append(".");
query.append(deletedFieldName);
query.append(" = false ");
}
}
public static void whereInjectValue(final PreparedStatement ps, final QueryItem condition, final CountInOut iii) throws Exception {
// Check if we have a condition to generate
if (condition != null) {
condition.injectQuerry(ps, iii);
}
}
public static int executeSimpleQuerry(final String query, final boolean root) throws SQLException, IOException {
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig, root);
final Statement stmt = entry.connection.createStatement();
@@ -881,29 +905,17 @@ public class DataAccess {
return executeQuerry(query, false);
}
public static <T> T getWhere(final Class<T> clazz, final QueryItem condition) throws Exception {
return getWhere(clazz, condition, null);
}
public static <T> T getWhere(final Class<T> clazz, final QueryItem condition, QueryOptions options) throws Exception {
if (options == null) {
options = new QueryOptions();
}
public static <T> T getWhere(final Class<T> clazz, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
options.add(new Limit(1));
final List<T> values = getsWhere(clazz, condition, options);
final List<T> values = getsWhere(clazz, options);
if (values.size() == 0) {
return null;
}
return values.get(0);
}
public static <T> List<T> getsWhere(final Class<T> clazz, final QueryItem condition) throws Exception {
return getsWhere(clazz, condition, null, null);
}
public static <T> List<T> getsWhere(final Class<T> clazz, final QueryItem condition, final QueryOptions options) throws Exception {
return getsWhere(clazz, condition, null, options);
}
/* public static <T> List<T> getsWhere(final Class<T> clazz, final QueryItem condition) throws Exception { return getsWhere(clazz, condition, null); } */
public static void generateSelectField(final StringBuilder querySelect, final StringBuilder query, final Class<?> clazz, final QueryOptions options, final CountInOut count) throws Exception {
final boolean readAllfields = QueryOptions.readAllColomn(options);
@@ -941,8 +953,14 @@ public class DataAccess {
}
}
public static <T> List<T> getsWhere(final Class<T> clazz, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
return getsWhere(clazz, options);
}
@SuppressWarnings("unchecked")
public static <T> List<T> getsWhere(final Class<T> clazz, final QueryItem condition, final String orderBy, final QueryOptions options) throws Exception {
public static <T> List<T> getsWhere(final Class<T> clazz, final QueryOptions options) throws Exception {
final Condition condition = options.get(Condition.class);
final List<LazyGetter> lazyCall = new ArrayList<>();
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
@@ -961,29 +979,27 @@ public class DataAccess {
generateSelectField(querySelect, query, clazz, options, count);
querySelect.append(query.toString());
query = querySelect;
whereAppendQuery(query, tableName, condition, options, deletedFieldName);
if (orderBy != null && orderBy.length() >= 1) {
query.append(" ORDER BY ");
query.append(orderBy);
if (condition != null) {
condition.whereAppendQuery(query, tableName, options, deletedFieldName);
}
if (options != null) {
final Limit limit = options.get(Limit.class);
if (limit != null) {
if (limit.getLimit() >= 1) {
query.append(" LIMIT " + limit.getLimit());
} else {
LOGGER.warn("Limit is equal @ {}", limit.getLimit());
entry.close();
entry = null;
return outs;
}
}
final OrderBy orders = options.get(OrderBy.class);
if (orders != null) {
orders.generateQuerry(query, tableName);
}
final Limit limit = options.get(Limit.class);
if (limit != null) {
limit.generateQuerry(query, tableName);
}
LOGGER.warn("generate the query: '{}'", query.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
final CountInOut iii = new CountInOut(1);
whereInjectValue(ps, condition, iii);
if (condition != null) {
condition.injectQuerry(ps, iii);
}
if (limit != null) {
limit.injectQuerry(ps, iii);
}
// execute the request
final ResultSet rs = ps.executeQuery();
while (rs.next()) {
@@ -1013,7 +1029,15 @@ public class DataAccess {
final List<LazyGetter> lazyCall) throws Exception {
final boolean readAllfields = QueryOptions.readAllColomn(options);
// TODO: manage class that is defined inside a class ==> Not manage for now...
final Object data = clazz.getConstructors()[0].newInstance();
Object data = null;
for (final Constructor<?> contructor : clazz.getConstructors()) {
if (contructor.getParameterCount() == 0) {
data = contructor.newInstance();
}
}
if (data == null) {
throw new DataAccessException("Can not find the default constructor for the class: " + clazz.getCanonicalName());
}
for (final Field elem : clazz.getFields()) {
// static field is only for internal global declaration ==> remove it ..
if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) {
@@ -1036,24 +1060,69 @@ public class DataAccess {
return data;
}
public static <T, ID_TYPE> T get(final Class<T> clazz, final ID_TYPE id) throws Exception {
return get(clazz, id, null);
public static <ID_TYPE> long count(final Class<?> clazz, final ID_TYPE id) throws Exception {
return DataAccess.countWhere(clazz, new Condition(getTableIdCondition(clazz, id)));
}
public static <T, ID_TYPE> T get(final Class<T> clazz, final ID_TYPE id, final QueryOptions options) throws Exception {
return DataAccess.getWhere(clazz, getTableIdCondition(clazz, id), options);
public static long countWhere(final Class<?> clazz, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
final Condition condition = options.get(Condition.class);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
long count = 0;
// real add in the BDD:
try {
final StringBuilder query = new StringBuilder();
final String tableName = AnnotationTools.getTableName(clazz, options);
query.append("SELECT COUNT(*) AS count FROM `");
query.append(tableName);
query.append("` ");
if (condition != null) {
condition.whereAppendQuery(query, tableName, options, deletedFieldName);
}
final Limit limit = options.get(Limit.class);
if (limit != null) {
limit.generateQuerry(query, tableName);
}
LOGGER.warn("generate the query: '{}'", query.toString());
// prepare the request:
final PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
final CountInOut iii = new CountInOut(1);
if (condition != null) {
condition.injectQuerry(ps, iii);
}
if (limit != null) {
limit.injectQuerry(ps, iii);
}
// execute the request
final ResultSet rs = ps.executeQuery();
if (rs.next()) {
count = rs.getLong("count");
}
} catch (final SQLException ex) {
ex.printStackTrace();
throw ex;
} catch (final Exception ex) {
ex.printStackTrace();
} finally {
entry.close();
entry = null;
}
return count;
}
public static String getCurrentTimeStamp() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
public static <T, ID_TYPE> T get(final Class<T> clazz, final ID_TYPE id, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
options.add(new Condition(getTableIdCondition(clazz, id)));
return DataAccess.getWhere(clazz, options.getAllArray());
}
public static <T> List<T> gets(final Class<T> clazz) throws Exception {
return getsWhere(clazz, null);
return getsWhere(clazz);
}
public static <T> List<T> gets(final Class<T> clazz, final QueryOptions options) throws Exception {
return getsWhere(clazz, null, options);
public static <T> List<T> gets(final Class<T> clazz, final QueryOption... option) throws Exception {
return getsWhere(clazz, option);
}
public static <ID_TYPE> int delete(final Class<?> clazz, final ID_TYPE id) throws Exception {
@@ -1066,7 +1135,7 @@ public class DataAccess {
* @param id Unique Id of the model
* @param options (Optional) Options of the request
* @return Number of element that is removed. */
public static <ID_TYPE> int delete(final Class<?> clazz, final ID_TYPE id, final QueryOptions options) throws Exception {
public static <ID_TYPE> int delete(final Class<?> clazz, final ID_TYPE id, final QueryOption... options) throws Exception {
final String hasDeletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
if (hasDeletedFieldName != null) {
return deleteSoft(clazz, id, options);
@@ -1080,20 +1149,28 @@ public class DataAccess {
* @param condition Condition to remove elements.
* @param options (Optional) Options of the request.
* @return Number of element that is removed. */
public static int deleteWhere(final Class<?> clazz, final QueryItem condition, final QueryOptions options) throws Exception {
public static int deleteWhere(final Class<?> clazz, final QueryOption... option) throws Exception {
final String hasDeletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
if (hasDeletedFieldName != null) {
return deleteSoftWhere(clazz, condition, options);
return deleteSoftWhere(clazz, option);
} else {
return deleteHardWhere(clazz, condition, options);
return deleteHardWhere(clazz, option);
}
}
public static <ID_TYPE> int deleteHard(final Class<?> clazz, final ID_TYPE id, final QueryOptions options) throws Exception {
return deleteHardWhere(clazz, getTableIdCondition(clazz, id), options);
public static <ID_TYPE> int deleteHard(final Class<?> clazz, final ID_TYPE id, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
options.add(new Condition(getTableIdCondition(clazz, id)));
return deleteHardWhere(clazz, options.getAllArray());
}
public static int deleteHardWhere(final Class<?> clazz, final QueryItem condition, final QueryOptions options) throws Exception {
public static int deleteHardWhere(final Class<?> clazz, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
final Condition condition = options.get(Condition.class);
if (condition == null) {
throw new DataAccessException("request a gets without any condition");
}
final String tableName = AnnotationTools.getTableName(clazz, options);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
// find the deleted field
@@ -1103,12 +1180,12 @@ public class DataAccess {
query.append("DELETE FROM `");
query.append(tableName);
query.append("` ");
whereAppendQuery(query, tableName, condition, null, deletedFieldName);
condition.whereAppendQuery(query, tableName, null, deletedFieldName);
try {
LOGGER.debug("APPLY: {}", query.toString());
final PreparedStatement ps = entry.connection.prepareStatement(query.toString());
final CountInOut iii = new CountInOut(1);
whereInjectValue(ps, condition, iii);
condition.injectQuerry(ps, iii);
return ps.executeUpdate();
} finally {
entry.close();
@@ -1116,11 +1193,18 @@ public class DataAccess {
}
}
private static <ID_TYPE> int deleteSoft(final Class<?> clazz, final ID_TYPE id, final QueryOptions options) throws Exception {
return deleteSoftWhere(clazz, getTableIdCondition(clazz, id), options);
private static <ID_TYPE> int deleteSoft(final Class<?> clazz, final ID_TYPE id, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
options.add(new Condition(getTableIdCondition(clazz, id)));
return deleteSoftWhere(clazz, options.getAllArray());
}
public static int deleteSoftWhere(final Class<?> clazz, final QueryItem condition, final QueryOptions options) throws Exception {
public static int deleteSoftWhere(final Class<?> clazz, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
final Condition condition = options.get(Condition.class);
if (condition == null) {
throw new DataAccessException("request a gets without any condition");
}
final String tableName = AnnotationTools.getTableName(clazz, options);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
/* String updateFieldName = null; if ("sqlite".equalsIgnoreCase(ConfigBaseVariable.getDBType())) { updateFieldName = AnnotationTools.getUpdatedFieldName(clazz); } */
@@ -1135,12 +1219,12 @@ public class DataAccess {
query.append("`=true ");
/* The trigger work well, but the timestamp is store @ seconds... if (updateFieldName != null) { // done only in SQLite (the trigger does not work... query.append(", `");
* query.append(updateFieldName); query.append("`=DATE()"); } */
whereAppendQuery(query, tableName, condition, null, deletedFieldName);
condition.whereAppendQuery(query, tableName, null, deletedFieldName);
try {
LOGGER.debug("APPLY UPDATE: {}", query.toString());
final PreparedStatement ps = entry.connection.prepareStatement(query.toString());
final CountInOut iii = new CountInOut(1);
whereInjectValue(ps, condition, iii);
condition.injectQuerry(ps, iii);
return ps.executeUpdate();
} finally {
entry.close();
@@ -1149,14 +1233,21 @@ public class DataAccess {
}
public static <ID_TYPE> int unsetDelete(final Class<?> clazz, final ID_TYPE id) throws Exception {
return unsetDeleteWhere(clazz, getTableIdCondition(clazz, id), null);
return unsetDeleteWhere(clazz, new Condition(getTableIdCondition(clazz, id)));
}
public static <ID_TYPE> int unsetDelete(final Class<?> clazz, final ID_TYPE id, final QueryOptions options) throws Exception {
return unsetDeleteWhere(clazz, getTableIdCondition(clazz, id), options);
public static <ID_TYPE> int unsetDelete(final Class<?> clazz, final ID_TYPE id, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
options.add(new Condition(getTableIdCondition(clazz, id)));
return unsetDeleteWhere(clazz, options.getAllArray());
}
public static int unsetDeleteWhere(final Class<?> clazz, final QueryItem condition, final QueryOptions options) throws Exception {
public static int unsetDeleteWhere(final Class<?> clazz, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
final Condition condition = options.get(Condition.class);
if (condition == null) {
throw new DataAccessException("request a gets without any condition");
}
final String tableName = AnnotationTools.getTableName(clazz, options);
final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz);
if (deletedFieldName == null) {
@@ -1171,11 +1262,11 @@ public class DataAccess {
query.append("`=false ");
// need to disable the deleted false because the model must be unselected to be updated.
options.add(QueryOptions.ACCESS_DELETED_ITEMS);
whereAppendQuery(query, tableName, condition, options, deletedFieldName);
condition.whereAppendQuery(query, tableName, options, deletedFieldName);
try {
final PreparedStatement ps = entry.connection.prepareStatement(query.toString());
final CountInOut iii = new CountInOut(1);
whereInjectValue(ps, condition, iii);
condition.injectQuerry(ps, iii);
return ps.executeUpdate();
} finally {
entry.close();
@@ -1183,4 +1274,70 @@ public class DataAccess {
}
}
public static void drop(final Class<?> clazz, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
final String tableName = AnnotationTools.getTableName(clazz, options);
DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
final StringBuilder query = new StringBuilder();
query.append("DROP TABLE IF EXISTS `");
query.append(tableName);
query.append("`");
try {
LOGGER.trace("Execute Querry: {}", query.toString());
// Remove main table
final PreparedStatement ps = entry.connection.prepareStatement(query.toString());
ps.executeUpdate();
// search subTable:
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;
}
if (AnnotationTools.isGenericField(field)) {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && !addOn.canInsert(field)) {
addOn.drop(tableName, field);
}
}
} finally {
entry.close();
entry = null;
}
}
public static void cleanAll(final Class<?> clazz, final QueryOption... option) throws Exception {
final QueryOptions options = new QueryOptions(option);
final String tableName = AnnotationTools.getTableName(clazz, options);
DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
final StringBuilder query = new StringBuilder();
query.append("DELETE FROM `");
query.append(tableName);
query.append("`");
try {
LOGGER.trace("Execute Querry: {}", query.toString());
// Remove main table
final PreparedStatement ps = entry.connection.prepareStatement(query.toString());
ps.executeUpdate();
// search subTable:
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;
}
if (AnnotationTools.isGenericField(field)) {
continue;
}
final DataAccessAddOn addOn = findAddOnforField(field);
if (addOn != null && !addOn.canInsert(field)) {
addOn.cleanAll(tableName, field);
}
}
} finally {
entry.close();
entry = null;
}
}
}

View File

@@ -31,14 +31,22 @@ public interface DataAccessAddOn {
* @throws SQLException */
void insertData(PreparedStatement ps, final Field field, Object data, CountInOut iii) throws Exception, SQLException, IllegalArgumentException, IllegalAccessException;
// Element can insert in the single request
boolean canInsert(final Field field);
/** Element can insert in the single request
* @param field
* @return */
default boolean canInsert(final Field field) {
return false;
}
// Element can be retrieve with the specific mode
boolean canRetrieve(final Field field);
/** Element can be retrieve with the specific mode
* @param field
* @return */
default boolean canRetrieve(final Field field) {
return false;
}
void generateQuerry(@NotNull String tableName, @NotNull Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, @NotNull String name,
@NotNull CountInOut count, QueryOptions options) throws Exception;
void generateQuerry(@NotNull String tableName, @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 fillFromQuerry(ResultSet rs, Field field, Object data, CountInOut count, QueryOptions options, final List<LazyGetter> lazyCall)
@@ -56,4 +64,46 @@ public interface DataAccessAddOn {
void createTables(String tableName, Field field, StringBuilder mainTableBuilder, List<String> preActionList, List<String> postActionList, boolean createIfNotExist, boolean createDrop, int fieldId)
throws Exception;
/** Some action must be done asynchronously for update or remove element
* @param field
* @return */
default boolean isInsertAsync(final Field field) throws Exception {
return false;
}
/** When insert is mark async, this function permit to create or update the data
* @param tableName Name of the Table.
* @param localId Local ID of the current table
* @param field Field that is updated.
* @param data Data that might be inserted.
* @param actions Asynchronous action to do after main request. */
default void asyncInsert(final String tableName, final Object localId, final Field field, final Object data, final List<LazyGetter> actions) throws Exception {
}
/** Some action must be done asynchronously for update or remove element
* @param field
* @return */
default boolean isUpdateAsync(final Field field) throws Exception {
return false;
}
/** When insert is mark async, this function permit to create or update the data
* @param tableName Name of the Table.
* @param localId Local ID of the current table
* @param field Field that is updated.
* @param data Data that might be inserted.
* @param actions Asynchronous action to do after main request. */
default void asyncUpdate(final String tableName, final Object localId, final Field field, final Object data, final List<LazyGetter> actions) throws Exception {
}
default void drop(final String tableName, final Field field) throws Exception {
}
default void cleanAll(final String tableName, final Field field) throws Exception {
}
}

View File

@@ -131,8 +131,8 @@ public class DataFactory {
public static void createTablesSpecificType(final String tableName, final Field elem, final StringBuilder mainTableBuilder, final List<String> preOtherTables, final List<String> postOtherTables,
final boolean createIfNotExist, final boolean createDrop, final int fieldId, final Class<?> classModel) throws Exception {
final String name = AnnotationTools.getFieldName(elem);
final Integer limitSize = AnnotationTools.getLimitSize(elem);
final boolean notNull = AnnotationTools.getNotNull(elem);
final int limitSize = AnnotationTools.getLimitSize(elem);
final boolean notNull = AnnotationTools.getColumnNotNull(elem);
final boolean primaryKey = AnnotationTools.isPrimaryKey(elem);
final GenerationType strategy = AnnotationTools.getStrategy(elem);
@@ -152,7 +152,7 @@ public class DataFactory {
String typeValue = null;
typeValue = convertTypeInSQL(classModel, name);
if ("text".equals(typeValue) && !"sqlite".equals(ConfigBaseVariable.getDBType())) {
if (limitSize != null) {
if (limitSize > 0) {
mainTableBuilder.append("varchar(");
mainTableBuilder.append(limitSize);
mainTableBuilder.append(")");

View File

@@ -0,0 +1,20 @@
package org.kar.archidata.dataAccess;
import java.sql.PreparedStatement;
public class Limit extends QueryOption {
protected final long limit;
public Limit(final long limit) {
this.limit = limit;
}
public void generateQuerry(final StringBuilder query, final String tableName) {
query.append(" LIMIT ? ");
}
public void injectQuerry(final PreparedStatement ps, final CountInOut iii) throws Exception {
DataAccess.addElement(ps, this.limit, iii);
iii.inc();
}
}

View File

@@ -0,0 +1,41 @@
package org.kar.archidata.dataAccess;
import java.sql.PreparedStatement;
import java.util.List;
public class OrderBy extends QueryOption {
protected final List<OrderItem> childs;
public OrderBy(final List<OrderItem> childs) {
this.childs = childs;
}
public OrderBy(final OrderItem... childs) {
this.childs = List.of(childs);
}
public void generateQuerry(final StringBuilder query, final String tableName) {
if (this.childs.size() >= 1) {
query.append(" ORDER BY ");
}
boolean first = true;
for (final OrderItem elem : this.childs) {
if (first) {
first = false;
} else {
query.append(", ");
}
query.append("`");
query.append(elem.value);
query.append("` ");
query.append(elem.order.toString());
}
if (this.childs.size() >= 1) {
query.append(")");
}
}
public void injectQuerry(final PreparedStatement ps, final CountInOut iii) throws Exception {
// nothing to add.
}
}

View File

@@ -0,0 +1,16 @@
package org.kar.archidata.dataAccess;
public class OrderItem {
public enum Order {
ASC, DESC
};
public final String value;
public final Order order;
public OrderItem(final String value, final Order order) {
this.value = value;
this.order = order;
}
}

View File

@@ -18,21 +18,21 @@ public class QueryAnd implements QueryItem {
}
@Override
public void generateQuerry(final StringBuilder querry, final String tableName) {
public void generateQuerry(final StringBuilder query, final String tableName) {
if (this.childs.size() >= 1) {
querry.append(" (");
query.append(" (");
}
boolean first = true;
for (final QueryItem elem : this.childs) {
if (first) {
first = false;
} else {
querry.append(" AND ");
query.append(" AND ");
}
elem.generateQuerry(querry, tableName);
elem.generateQuerry(query, tableName);
}
if (this.childs.size() >= 1) {
querry.append(")");
query.append(")");
}
}

View File

@@ -14,14 +14,13 @@ public class QueryCondition implements QueryItem {
}
@Override
public void generateQuerry(final StringBuilder querry, final String tableName) {
querry.append(tableName);
querry.append(".");
querry.append(this.key);
querry.append(" ");
querry.append(this.comparator);
querry.append(" ?");
public void generateQuerry(final StringBuilder query, final String tableName) {
query.append(tableName);
query.append(".");
query.append(this.key);
query.append(" ");
query.append(this.comparator);
query.append(" ?");
}
@Override

View File

@@ -18,22 +18,26 @@ public class QueryInList<T> implements QueryItem {
this(key, "IN", value);
}
public QueryInList(final String key, final T... value) {
this(key, "IN", List.of(value));
}
@Override
public void generateQuerry(final StringBuilder querry, final String tableName) {
querry.append(tableName);
querry.append(".");
querry.append(this.key);
querry.append(" ");
querry.append(this.comparator);
querry.append(" (");
public void generateQuerry(final StringBuilder query, final String tableName) {
query.append(tableName);
query.append(".");
query.append(this.key);
query.append(" ");
query.append(this.comparator);
query.append(" (");
for (int iii = 0; iii < this.value.size(); iii++) {
if (iii != 0) {
querry.append(",?");
query.append(",?");
} else {
querry.append("?");
query.append("?");
}
}
querry.append(")");
query.append(")");
}

View File

@@ -3,7 +3,7 @@ package org.kar.archidata.dataAccess;
import java.sql.PreparedStatement;
public interface QueryItem {
void generateQuerry(StringBuilder querry, String tableName);
void generateQuerry(StringBuilder query, String tableName);
void injectQuerry(PreparedStatement ps, CountInOut iii) throws Exception;
}

View File

@@ -18,12 +18,15 @@ public class QueryOptions {
private final List<QueryOption> options = new ArrayList<>();
public QueryOptions() {}
public QueryOptions(final QueryOption... elems) {
if (elems == null || elems.length == 0) {
return;
}
Collections.addAll(this.options, elems);
}
public QueryOptions() {}
public void add(final QueryOption option) {
this.options.add(option);
}
@@ -32,6 +35,10 @@ public class QueryOptions {
return this.options;
}
public QueryOption[] getAllArray() {
return this.options.toArray(new QueryOption[0]);
}
@SuppressWarnings("unchecked")
public <T> T get(final Class<T> type) {
for (final QueryOption elem : this.options) {

View File

@@ -10,22 +10,26 @@ public class QueryOr implements QueryItem {
this.childs = childs;
}
public QueryOr(final QueryItem... childs) {
this.childs = List.of(childs);
}
@Override
public void generateQuerry(final StringBuilder querry, final String tableName) {
public void generateQuerry(final StringBuilder query, final String tableName) {
if (this.childs.size() >= 1) {
querry.append(" (");
query.append(" (");
}
boolean first = true;
for (final QueryItem elem : this.childs) {
if (first) {
first = false;
} else {
querry.append(" OR ");
query.append(" OR ");
}
elem.generateQuerry(querry, tableName);
elem.generateQuerry(query, tableName);
}
if (this.childs.size() >= 1) {
querry.append(")");
query.append(")");
}
}

View File

@@ -58,6 +58,11 @@ public class AddOnDataJson implements DataAccessAddOn {
return true;
}
@Override
public boolean isInsertAsync(final Field field) throws Exception {
return false;
}
@Override
public boolean canRetrieve(final Field field) {
return true;

View File

@@ -19,6 +19,7 @@ import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.QueryInList;
import org.kar.archidata.dataAccess.QueryOptions;
import org.kar.archidata.dataAccess.addOn.model.LinkTable;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.dataAccess.options.OverrideTableName;
import org.kar.archidata.exception.DataAccessException;
import org.kar.archidata.tools.ConfigBaseVariable;
@@ -85,7 +86,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
querrySelect.append(" (SELECT GROUP_CONCAT(");
querrySelect.append(tmpVariable);
querrySelect.append(".object2Id ");
if (ConfigBaseVariable.getDBType().equals("sqlite")) {
if ("sqlite".equals(ConfigBaseVariable.getDBType())) {
querrySelect.append(", ");
} else {
querrySelect.append("SEPARATOR ");
@@ -97,14 +98,13 @@ public class AddOnManyToMany implements DataAccessAddOn {
querrySelect.append(" ");
querrySelect.append(tmpVariable);
querrySelect.append(" WHERE ");
querrySelect.append(tmpVariable);
querrySelect.append(".deleted = false AND ");
/* querrySelect.append(tmpVariable); querrySelect.append(".deleted = false AND "); */
querrySelect.append(tableName);
querrySelect.append(".id = ");
querrySelect.append(tmpVariable);
querrySelect.append(".");
querrySelect.append("object1Id ");
if (!ConfigBaseVariable.getDBType().equals("sqlite")) {
if (!"sqlite".equals(ConfigBaseVariable.getDBType())) {
querrySelect.append(" GROUP BY ");
querrySelect.append(tmpVariable);
querrySelect.append(".object1Id");
@@ -143,6 +143,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
@Override
public void fillFromQuerry(final ResultSet rs, final Field field, final Object data, final CountInOut count, final QueryOptions options, final List<LazyGetter> lazyCall) throws Exception {
if (field.getType() != List.class) {
LOGGER.error("Can not ManyToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
@@ -150,6 +151,10 @@ public class AddOnManyToMany implements DataAccessAddOn {
final List<Long> idList = DataAccess.getListOfIds(rs, count.value, SEPARATOR);
field.set(data, idList);
count.inc();
return;
// } else {
// LOGGER.error("Can not ManyToMany with other than List<Long> Model: List<{}>", objectClass.getCanonicalName());
// return;
}
final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class);
if (decorators == null) {
@@ -169,7 +174,7 @@ public class AddOnManyToMany implements DataAccessAddOn {
final List<Long> childs = new ArrayList<>(idList);
// TODO: update to have get with abstract types ....
@SuppressWarnings("unchecked")
final Object foreignData = DataAccess.getsWhere(decorators.targetEntity(), new QueryInList<>(idField, childs), null);
final Object foreignData = DataAccess.getsWhere(decorators.targetEntity(), new Condition(new QueryInList<>(idField, childs)));
if (foreignData == null) {
return;
}
@@ -181,21 +186,98 @@ public class AddOnManyToMany implements DataAccessAddOn {
}
}
@Override
public boolean isUpdateAsync(final Field field) {
return true;
}
@Override
public void asyncUpdate(final String tableName, final Object localKey, final Field field, final Object data, final List<LazyGetter> actions) throws Exception {
if (field.getType() != List.class) {
LOGGER.error("Can not ManyToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
actions.add(() -> {
DataAccess.deleteWhere(LinkTable.class, new OverrideTableName(linkTableName), new Condition(new QueryCondition("object1Id", "=", localKey)));
});
asyncInsert(tableName, localKey, field, data, actions);
}
@Override
public boolean isInsertAsync(final Field field) {
return true;
}
@Override
public void asyncInsert(final String tableName, final Object localKey, final Field field, final Object data, final List<LazyGetter> actions) throws Exception {
if (data == null) {
return;
}
if (field.getType() != List.class) {
LOGGER.error("Can not ManyToMany with other than List Model: {}", field.getType().getCanonicalName());
return;
}
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
if (objectClass != Long.class) {
LOGGER.error("Can not ManyToMany with other than List<Long> Model: List<{}>", objectClass.getCanonicalName());
return;
}
@SuppressWarnings("unchecked")
final List<Long> dataCasted = (List<Long>) data;
if (dataCasted.size() == 0) {
return;
}
final List<LinkTable> insertElements = new ArrayList<>();
for (final Long remoteKey : dataCasted) {
if (remoteKey == null) {
throw new DataAccessException("Try to insert remote key with null value");
}
if (localKey instanceof final Long localKeyLong) {
insertElements.add(new LinkTable(localKeyLong, remoteKey));
} else {
throw new DataAccessException("Not manage access of remte key like ManyToMany other than Long: " + localKey.getClass().getCanonicalName());
}
}
if (insertElements.size() == 0) {
LOGGER.warn("Insert multiple link without any value (may have null in the list): {}", dataCasted);
return;
}
actions.add(() -> {
DataAccess.insertMultiple(insertElements, new OverrideTableName(linkTableName));
});
}
@Override
public void drop(final String tableName, final Field field) throws Exception {
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
DataAccess.drop(LinkTable.class, new OverrideTableName(linkTableName));
}
@Override
public void cleanAll(final String tableName, final Field field) throws Exception {
final String columnName = AnnotationTools.getFieldName(field);
final String linkTableName = generateLinkTableName(tableName, columnName);
DataAccess.cleanAll(LinkTable.class, new OverrideTableName(linkTableName));
}
public static void addLink(final Class<?> clazz, final long localKey, final String column, final long remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final String linkTableName = generateLinkTableName(tableName, column);
final LinkTable insertElement = new LinkTable(localKey, remoteKey);
final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName));
DataAccess.insert(insertElement, options);
DataAccess.insert(insertElement, new OverrideTableName(linkTableName));
}
public static int removeLink(final Class<?> clazz, final long localKey, final String column, final long remoteKey) throws Exception {
final String tableName = AnnotationTools.getTableName(clazz);
final String linkTableName = generateLinkTableName(tableName, column);
final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName));
final QueryAnd condition = new QueryAnd(new QueryCondition("object1Id", "=", localKey), new QueryCondition("object2Id", "=", remoteKey));
return DataAccess.deleteWhere(LinkTable.class, condition, options);
return DataAccess.deleteWhere(LinkTable.class, new OverrideTableName(linkTableName),
new Condition(new QueryAnd(new QueryCondition("object1Id", "=", localKey), new QueryCondition("object2Id", "=", remoteKey))));
}
@Override

View File

@@ -81,6 +81,11 @@ public class AddOnManyToOne implements DataAccessAddOn {
return false;
}
@Override
public boolean isInsertAsync(final Field field) throws Exception {
return false;
}
@Override
public boolean canRetrieve(final Field field) {
if (field.getType() == Long.class) {

View File

@@ -92,6 +92,11 @@ public class AddOnOneToMany implements DataAccessAddOn {
return false;
}
@Override
public boolean isInsertAsync(final Field field) throws Exception {
return false;
}
@Override
public boolean canRetrieve(final Field field) {
return false;

View File

@@ -22,6 +22,7 @@ import org.slf4j.LoggerFactory;
import jakarta.validation.constraints.NotNull;
// TODO: maybe deprecated ==> use DataJson instead...
public class AddOnSQLTableExternalForeinKeyAsList implements DataAccessAddOn {
static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class);
static final String SEPARATOR = "-";
@@ -75,6 +76,11 @@ public class AddOnSQLTableExternalForeinKeyAsList implements DataAccessAddOn {
return false;
}
@Override
public boolean isInsertAsync(final Field field) throws Exception {
return false;
}
@Override
public boolean canRetrieve(final Field field) {
return false;

View File

@@ -1,11 +1,11 @@
package org.kar.archidata.dataAccess.addOn.model;
import org.kar.archidata.annotation.DataComment;
import org.kar.archidata.model.GenericDataSoftDelete;
import org.kar.archidata.model.GenericData;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
public class LinkTable extends GenericDataSoftDelete {
public class LinkTable extends GenericData {
public LinkTable() {
// nothing to do...
}
@@ -15,10 +15,10 @@ public class LinkTable extends GenericDataSoftDelete {
this.object2Id = object2Id;
}
@DataComment("Object reference 1")
@Schema(description = "Object reference 1")
@Column(nullable = false)
public Long object1Id;
@DataComment("Object reference 2")
@Schema(description = "Object reference 2")
@Column(nullable = false)
public Long object2Id;

View File

@@ -2,12 +2,19 @@ package org.kar.archidata.dataAccess.options;
import java.util.List;
import org.kar.archidata.annotation.AnnotationTools;
/** 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 data The object that might be injected.
* @param filterValue 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. */
void check(Object data, List<String> filterValue) throws Exception;
void check(final String baseName, Object data, List<String> filterValue) throws Exception;
default void checkAll(final String baseName, final Object data) throws Exception {
check(baseName, data, AnnotationTools.getAllFieldsNames(data.getClass()));
}
}

View File

@@ -0,0 +1,12 @@
package org.kar.archidata.dataAccess.options;
import java.util.List;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public class CheckFunctionVoid implements CheckFunctionInterface {
@Override
public void check(final String baseName, Object data, List<String> filterValue) {
}
}

View File

@@ -0,0 +1,335 @@
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.regex.Pattern;
import org.kar.archidata.annotation.AnnotationTools;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition;
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 data The object that might be injected.
* @param filterValue 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. */
void check(final String baseName, final K data) throws Exception;
}
protected Map<String, List<CheckInterface<T>>> checking = null;
protected void add(final String field, final CheckInterface<T> checkFunction) {
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 String baseName, final T data) -> {
throw new InputException(baseName + fieldName, "This is a '@Id' (primaryKey) ==> can not be change");
});
}
if (AnnotationTools.getConstraintsNotNull(field)) {
add(fieldName, (final String baseName, final T data) -> {
if (field.get(data) == null) {
throw new InputException(baseName + fieldName, "Can not be null");
}
});
}
if (AnnotationTools.isCreatedAtField(field) || AnnotationTools.isUpdateAtField(field)) {
add(fieldName, (final String baseName, final T data) -> {
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 String baseName, final T data) -> {
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 String baseName, final T data) -> {
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 String baseName, final T data) -> {
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 == Integer.class || type == int.class) {
final Long maxValueRoot = AnnotationTools.getConstraintsMax(field);
if (maxValueRoot != null) {
final int maxValue = maxValueRoot.intValue();
add(fieldName, (final String baseName, final T data) -> {
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 String baseName, final T data) -> {
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 String baseName, final T data) -> {
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 String baseName, final T data) -> {
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 String baseName, final T data) -> {
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 String baseName, final T data) -> {
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 String baseName, final T data) -> {
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 String baseName, final T data) -> {
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 String baseName, final T data) -> {
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.max());
}
});
}
final String patternString = AnnotationTools.getConstraintsPattern(field);
if (patternString != null) {
final Pattern pattern = Pattern.compile(patternString);
add(fieldName, (final String baseName, final T data) -> {
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 + "'");
}
});
}
} 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 String baseName, final T data) -> {
instance.checkAll(baseName + fieldName + ".", field.get(data));
});
}
} else if (type.isEnum()) {
// nothing to do.
}
// keep this is last ==> take more time...
if (AnnotationTools.isUnique(field)) {
// Create the request ...
add(fieldName, (final String baseName, final T data) -> {
final Object other = DataAccess.getWhere(this.clazz, new Condition(new QueryCondition(fieldName, "==", field.get(data))));
if (other != null) {
throw new InputException(baseName + fieldName, "Name already exist in the DB");
}
});
}
}
} catch (final Exception ex) {
this.checking = null;
throw ex;
}
}
@Override
public void check(final String baseName, final Object data, final List<String> filterValue) throws Exception {
if (this.checking == null) {
initialize();
}
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 : filterValue) {
final List<CheckInterface<T>> actions = this.checking.get(filter);
if (actions == null) {
continue;
}
for (final CheckInterface<T> action : actions) {
action.check(baseName, dataCasted);
}
}
checkTyped(dataCasted, filterValue);
}
public void checkTyped(final T data, final List<String> filterValue) throws Exception {
// nothing to do ...
}
}

View File

@@ -0,0 +1,62 @@
package org.kar.archidata.dataAccess.options;
import java.sql.PreparedStatement;
import org.kar.archidata.dataAccess.CountInOut;
import org.kar.archidata.dataAccess.QueryItem;
import org.kar.archidata.dataAccess.QueryOption;
import org.kar.archidata.dataAccess.QueryOptions;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public class Condition extends QueryOption {
public final QueryItem condition;
public Condition(final QueryItem items) {
this.condition = items;
}
public Condition() {
this.condition = null;
}
public void generateQuerry(final StringBuilder query, final String tableName) {
if (this.condition != null) {
this.condition.generateQuerry(query, tableName);
}
}
public void injectQuerry(final PreparedStatement ps, final CountInOut iii) throws Exception {
if (this.condition != null) {
this.condition.injectQuerry(ps, iii);
}
}
public void whereAppendQuery(final StringBuilder query, final String tableName, final QueryOptions options, final String deletedFieldName) {
boolean exclude_deleted = true;
if (options != null) {
exclude_deleted = !options.exist(AccessDeletedItems.class);
}
// Check if we have a condition to generate
if (this.condition == null) {
if (exclude_deleted && deletedFieldName != null) {
query.append(" WHERE ");
query.append(tableName);
query.append(".");
query.append(deletedFieldName);
query.append(" = false ");
}
return;
}
query.append(" WHERE (");
this.condition.generateQuerry(query, tableName);
query.append(") ");
if (exclude_deleted && deletedFieldName != null) {
query.append("AND ");
query.append(tableName);
query.append(".");
query.append(deletedFieldName);
query.append(" = false ");
}
}
}

View File

@@ -0,0 +1,22 @@
package org.kar.archidata.dataAccess.options;
import java.util.List;
import org.kar.archidata.dataAccess.QueryOption;
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
public class FilterValue extends QueryOption {
public final List<String> filterValue;
public FilterValue(final List<String> filterValue) {
this.filterValue = filterValue;
}
public FilterValue(final String... filterValue) {
this.filterValue = List.of(filterValue);
}
public List<String> getValues() {
return this.filterValue;
}
}

View File

@@ -1,17 +0,0 @@
package org.kar.archidata.dataAccess.options;
import org.kar.archidata.dataAccess.QueryOption;
/** Option that permit to access to a table structure with an other name that is define in the structure. Note: Internal use for link tables (see:
* org.kar.archidata.dataAccess.addOn.model.LinkTable). */
public class Limit extends QueryOption {
private final int limit;
public Limit(final int limit) {
this.limit = limit;
}
public int getLimit() {
return this.limit;
}
}

View File

@@ -0,0 +1,16 @@
package org.kar.archidata.dataAccess.options;
import org.kar.archidata.dataAccess.QueryOption;
/** Internal option that permit to transmit the Key when updating the ManyToMany values (first step). */
public class TransmitKey extends QueryOption {
private final Object key;
public TransmitKey(final Object key) {
this.key = key;
}
public Object getKey() {
return this.key;
}
}

View File

@@ -38,7 +38,7 @@ import jakarta.ws.rs.ext.Provider;
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
final Logger logger = LoggerFactory.getLogger(AuthenticationFilter.class);
private final static Logger LOGGER = LoggerFactory.getLogger(AuthenticationFilter.class);
@Context
private ResourceInfo resourceInfo;
protected final String applicationName;
@@ -57,7 +57,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
final Method method = this.resourceInfo.getResourceMethod();
// Access denied for all
if (method.isAnnotationPresent(DenyAll.class)) {
this.logger.debug(" ==> deny all {}", requestContext.getUriInfo().getPath());
LOGGER.debug(" ==> deny all {}", requestContext.getUriInfo().getPath());
requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access blocked !!!").build());
return;
}
@@ -70,7 +70,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
}
// this is a security guard, all the API must define their access level:
if (!method.isAnnotationPresent(RolesAllowed.class)) {
this.logger.error(" ==> missing @RolesAllowed {}", requestContext.getUriInfo().getPath());
LOGGER.error(" ==> missing @RolesAllowed {}", requestContext.getUriInfo().getPath());
requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access ILLEGAL !!!").build());
return;
}
@@ -94,7 +94,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
final boolean isJwtToken = isTokenBasedAuthentication(authorizationHeader);
// Validate the Authorization header data Model "Yota jwt.to.ken" "Zota tokenId:hash(token)"
if (!isApplicationToken && !isJwtToken) {
this.logger.warn("REJECTED unauthorized: {}", requestContext.getUriInfo().getPath());
LOGGER.warn("REJECTED unauthorized: {}", requestContext.getUriInfo().getPath());
abortWithUnauthorized(requestContext, "REJECTED unauthorized: " + requestContext.getUriInfo().getPath());
return;
}
@@ -106,12 +106,12 @@ public class AuthenticationFilter implements ContainerRequestFilter {
try {
userByToken = validateJwtToken(token);
} catch (final Exception e) {
this.logger.error("Fail to validate token: {}", e.getMessage());
LOGGER.error("Fail to validate token: {}", e.getMessage());
abortWithUnauthorized(requestContext, "Fail to validate token: " + e.getMessage());
return;
}
if (userByToken == null) {
this.logger.warn("get a NULL user ...");
LOGGER.warn("get a NULL user ...");
abortWithUnauthorized(requestContext, "get a NULL user ...");
return;
}
@@ -122,12 +122,12 @@ public class AuthenticationFilter implements ContainerRequestFilter {
try {
userByToken = validateToken(token);
} catch (final Exception e) {
this.logger.error("Fail to validate token: {}", e.getMessage());
LOGGER.error("Fail to validate token: {}", e.getMessage());
abortWithUnauthorized(requestContext, "Fail to validate token: " + e.getMessage());
return;
}
if (userByToken == null) {
this.logger.warn("get a NULL application ...");
LOGGER.warn("get a NULL application ...");
abortWithUnauthorized(requestContext, "get a NULL application ...");
return;
}
@@ -149,7 +149,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
}
// Is user valid?
if (!haveRight) {
this.logger.error("REJECTED not enought right : {} require: {}", requestContext.getUriInfo().getPath(), roles);
LOGGER.error("REJECTED not enought right : {} require: {}", requestContext.getUriInfo().getPath(), roles);
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Not enought RIGHT !!!").build());
return;
}
@@ -175,15 +175,15 @@ public class AuthenticationFilter implements ContainerRequestFilter {
// Abort the filter chain with a 401 status code response
// The WWW-Authenticate header is sent along with the response
this.logger.warn("abortWithUnauthorized:");
LOGGER.warn("abortWithUnauthorized:");
final RestErrorResponse ret = new RestErrorResponse(Response.Status.UNAUTHORIZED, "Unauthorized", message);
this.logger.error("Error UUID={}", ret.uuid);
LOGGER.error("Error UUID={}", ret.uuid);
requestContext.abortWith(Response.status(ret.status).header(HttpHeaders.WWW_AUTHENTICATE, AUTHENTICATION_SCHEME + " base64(HEADER).base64(CONTENT).base64(KEY)").entity(ret)
.type(MediaType.APPLICATION_JSON).build());
}
protected UserByToken validateToken(final String authorization) throws Exception {
this.logger.info("Must be Override by the application implmentation, otherwise it dose not work");
LOGGER.info("Must be Override by the application implmentation, otherwise it dose not work");
return null;
}
@@ -193,7 +193,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
final JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth", null);
// check the token is valid !!! (signed and coherent issuer...
if (ret == null) {
this.logger.error("The token is not valid: '{}'", authorization);
LOGGER.error("The token is not valid: '{}'", authorization);
return null;
}
// check userID
@@ -209,7 +209,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
if (rights.containsKey(this.applicationName)) {
user.right = rights.get(this.applicationName);
} else {
this.logger.error("Connect with no right for this application='{}' full Right='{}'", this.applicationName, rights);
LOGGER.error("Connect with no right for this application='{}' full Right='{}'", this.applicationName, rights);
}
}
// logger.debug("request user: '{}' right: '{}' row='{}'", userUID, user.right, rowRight);

View File

@@ -18,6 +18,6 @@ public class CORSFilter implements ContainerResponseFilter {
response.getHeaders().add("Access-Control-Allow-Headers", "*");
// "Origin, content-type, Content-type, Accept, authorization, mime-type, filename");
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD");
}
}

View File

@@ -57,7 +57,7 @@ public class MigrationEngine {
try {
List<Migration> data = null;
try {
data = DataAccess.gets(Migration.class, new QueryOptions(QueryOptions.READ_ALL_COLOMN));
data = DataAccess.gets(Migration.class, QueryOptions.READ_ALL_COLOMN);
} catch (final Exception e) {
// Previous version does not have the same timeCode...
data = DataAccess.gets(Migration.class);

View File

@@ -105,9 +105,8 @@ public class MigrationSqlStep implements MigrationInterface {
e.printStackTrace();
}
try {
Thread.sleep(100);
Thread.sleep(2);
} catch (final InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

View File

@@ -1,34 +1,41 @@
package org.kar.archidata.migration.model;
import org.kar.archidata.annotation.DataComment;
import org.kar.archidata.annotation.DataDefault;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.annotation.DataNotRead;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
// For logs only
//public static final String TABLE_NAME = "KAR_migration";
// TODO: Add a migration Hash to be sure that the current migration init is correct and has not change...
@Table(name = "KAR_migration")
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Migration extends GenericDataSoftDelete {
@DataComment("Name of the migration")
final static int VERSION_MIGRATION = 2;
@Schema(description = "Name of the migration")
@Column(length = 256)
public String name;
@DataNotRead
@DataDefault("'2'")
@Schema(description = "Version of the migration engine")
public Integer version;
@Column(nullable = false)
@DataDefault("'0'")
@DataComment("if the migration is well terminated or not")
@Schema(description = "if the migration is well terminated or not")
public Boolean terminated = false;
@DataComment("index in the migration progression")
@Schema(description = "index in the migration progression")
public Integer stepId = 0;
@DataComment("number of element in the migration")
@Schema(description = "number of element in the migration")
public Integer count;
@DataComment("Log generate by the migration")
@Column(length = 0)
@Schema(description = "Log generate by the migration")
@Column(length = -1)
public String log = "";
}

View File

@@ -0,0 +1,35 @@
package org.kar.archidata.migration.model;
import org.kar.archidata.annotation.DataDefault;
import org.kar.archidata.annotation.DataIfNotExists;
import org.kar.archidata.model.GenericDataSoftDelete;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
// For logs only
//public static final String TABLE_NAME = "KAR_migration";
@Table(name = "KAR_migration")
@DataIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Migration1 extends GenericDataSoftDelete {
final static int VERSION_MIGRATION = 1;
@Schema(description = "Name of the migration")
@Column(length = 256)
public String name;
@Column(nullable = false)
@DataDefault("'0'")
@Schema(description = "if the migration is well terminated or not")
public Boolean terminated = false;
@Schema(description = "index in the migration progression")
public Integer stepId = 0;
@Schema(description = "number of element in the migration")
public Integer count;
@Schema(description = "Log generate by the migration")
@Column(length = 0)
public String log = "";
}

View File

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

View File

@@ -3,33 +3,36 @@ package org.kar.archidata.model;
import java.util.Date;
import org.kar.archidata.annotation.CreationTimestamp;
import org.kar.archidata.annotation.DataComment;
import org.kar.archidata.annotation.DataNotRead;
import org.kar.archidata.annotation.UpdateTimestamp;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
import jakarta.validation.constraints.NotNull;
public class GenericData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false, unique = true)
@DataComment("Primary key of the base")
@Schema(description = "Unique Id of the object", required = false, readOnly = true, example = "123456")
public Long id = null;
@DataNotRead
@CreationTimestamp
@Column(nullable = false)
@Temporal(TemporalType.TIMESTAMP)
@DataComment("Create time of the object")
@NotNull
@Schema(description = "Create time of the object", required = false, example = "2000-01-23T01:23:45.678+01:00", readOnly = true)
public Date createdAt = null;
@DataNotRead
@UpdateTimestamp
@Column(nullable = false)
@Temporal(TemporalType.TIMESTAMP)
@DataComment("When update the object")
@NotNull
@Schema(description = "When update the object", required = false, example = "2000-01-23T00:23:45.678Z", readOnly = true)
public Date updatedAt = null;
}

View File

@@ -1,17 +1,19 @@
package org.kar.archidata.model;
import org.kar.archidata.annotation.DataComment;
import org.kar.archidata.annotation.DataDefault;
import org.kar.archidata.annotation.DataDeleted;
import org.kar.archidata.annotation.DataNotRead;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.Column;
import jakarta.validation.constraints.NotNull;
public class GenericDataSoftDelete extends GenericData {
@DataNotRead
@Column(nullable = false)
@DataDefault("'0'")
@DataDeleted
@DataComment("When delete, they are not removed, they are just set in a deleted state")
@NotNull
@Schema(description = "Deleted state", hidden = true, required = false, readOnly = true)
public Boolean deleted = null;
}

View File

@@ -18,6 +18,7 @@ import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryAnd;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.addOn.AddOnManyToMany;
import org.kar.archidata.dataAccess.options.Condition;
import org.kar.archidata.model.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -75,7 +76,7 @@ public class DataTools {
public static Data getWithSha512(final String sha512) {
try {
return DataAccess.getWhere(Data.class, new QueryCondition("sha512", "=", sha512));
return DataAccess.getWhere(Data.class, new Condition(new QueryCondition("sha512", "=", sha512)));
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
@@ -85,7 +86,7 @@ public class DataTools {
public static Data getWithId(final long id) {
try {
return DataAccess.getWhere(Data.class, new QueryAnd(List.of(new QueryCondition("deleted", "=", false), new QueryCondition("id", "=", id))));
return DataAccess.getWhere(Data.class, new Condition(new QueryAnd(List.of(new QueryCondition("deleted", "=", false), new QueryCondition("id", "=", id)))));
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
@@ -109,15 +110,12 @@ public class DataTools {
final String tmpPath = getTmpFileInData(tmpUID);
final long fileSize = Files.size(Paths.get(tmpPath));
Data out = new Data();
;
try {
out.sha512 = sha512;
out.mimeType = mimeType;
out.size = fileSize;
out = DataAccess.insert(out);
} catch (final SQLException ex) {
ex.printStackTrace();
return null;
} catch (final Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();

View File

@@ -51,13 +51,13 @@ class TestSigner implements JWSSigner {
@Override
public Set<JWSAlgorithm> supportedJWSAlgorithms() {
// TODO Auto-generated method stub
return null;
return Set.of(JWSAlgorithm.RS256);
}
@Override
public JCAContext getJCAContext() {
// TODO Auto-generated method stub
return null;
return new JCAContext();
}
}
@@ -209,8 +209,9 @@ public class JWTWrapper {
try {
// On the consumer side, parse the JWS and verify its RSA signature
final SignedJWT signedJWT = SignedJWT.parse(signedToken);
if (rsaPublicJWK == null) {
if (ConfigBaseVariable.getTestMode() && signedToken.endsWith(TestSigner.test_signature)) {
LOGGER.warn("Someone use a test token: {}", signedToken);
} else if (rsaPublicJWK == null) {
LOGGER.warn("JWT public key is not present !!!");
if (!ConfigBaseVariable.getTestMode()) {
return null;
@@ -228,7 +229,7 @@ public class JWTWrapper {
return null;
}
}
if (!new Date().before(signedJWT.getJWTClaimsSet().getExpirationTime())) {
if (!ConfigBaseVariable.getTestMode() && !new Date().before(signedJWT.getJWTClaimsSet().getExpirationTime())) {
LOGGER.error("JWT token is expired now = " + new Date() + " with=" + signedJWT.getJWTClaimsSet().getExpirationTime());
return null;
}
@@ -251,16 +252,16 @@ public class JWTWrapper {
return null;
}
public static String createJwtTestToken(final long userID, final String userLogin, final String isuer, final String application, final Map<String, Object> rights) {
public static String createJwtTestToken(final long userID, final String userLogin, final String isuer, final String application, final Map<String, Map<String, Object>> rights) {
if (!ConfigBaseVariable.getTestMode()) {
LOGGER.error("Test mode disable !!!!!");
return null;
}
try {
final int timeOutInMunites = 3600 * 24 * 31;
final int timeOutInMunites = 3600;
final Date now = new Date();
final Date expiration = new Date(new Date().getTime() - 60 * timeOutInMunites * 1000 /* millisecond */);
final Date expiration = new Date(new Date().getTime() + timeOutInMunites * 1000 /* ms */);
final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().subject(Long.toString(userID)).claim("login", userLogin).claim("application", application).issuer(isuer).issueTime(now)
.expirationTime(expiration); // Do not ask why we need a "-" here ... this have no meaning
@@ -278,7 +279,8 @@ public class JWTWrapper {
// serialize the output...
return signedJWT.serialize();
} catch (final Exception ex) {
LOGGER.error("Can not generate Test Token...");
ex.printStackTrace();
LOGGER.error("Can not generate Test Token... {}", ex.getLocalizedMessage());
}
return null;
}

View File

@@ -0,0 +1,31 @@
package org.kar.archidata.tools;
import java.util.List;
public class ListTools {
public static boolean checkListIdentical(final List<?> list1, final List<?> list2) {
if (list1 == list2) {
return true;
}
if (list1 == null) {
return false;
}
if (list1.size() != list2.size()) {
return false;
}
for (int iii = 0; iii < list1.size(); iii++) {
final Object aaa = list1.get(iii);
final Object bbb = list2.get(iii);
if (aaa == bbb) {
continue;
}
if (aaa == null) {
return false;
}
if (!aaa.equals(bbb)) {
return false;
}
}
return true;
}
}

View File

@@ -3,6 +3,7 @@ package org.kar.archidata.tools;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Version;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpRequest.Builder;
@@ -14,8 +15,10 @@ import org.kar.archidata.exception.RESTErrorResponseExeption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import jakarta.ws.rs.core.HttpHeaders;
@@ -23,6 +26,7 @@ public class RESTApi {
final static Logger LOGGER = LoggerFactory.getLogger(RESTApi.class);
final String baseUrl;
private String token = null;
final ObjectMapper mapper = new ObjectMapper();
public RESTApi(final String baseUrl) {
this.baseUrl = baseUrl;
@@ -33,152 +37,151 @@ public class RESTApi {
}
public <T> List<T> gets(final Class<T> clazz, final String urlOffset) throws RESTErrorResponseExeption, IOException, InterruptedException {
final ObjectMapper mapper = new ObjectMapper();
final HttpClient client = HttpClient.newHttpClient();
Builder requestBuilding = HttpRequest.newBuilder().uri(URI.create(this.baseUrl + urlOffset));
Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1).uri(URI.create(this.baseUrl + urlOffset));
if (this.token != null) {
requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token);
}
final HttpRequest request = requestBuilding.GET().build();
final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw out;
try {
final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw new RESTErrorResponseExeption(out.uuid, out.time, out.error, out.message, out.status, out.statusMessage);
} catch (final MismatchedInputException ex) {
throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body());
}
}
final List<T> out = mapper.readValue(httpResponse.body(), new TypeReference<List<T>>() {});
return out;
return this.mapper.readValue(httpResponse.body(), new TypeReference<List<T>>() {});
}
public <T> T get(final Class<T> clazz, final String urlOffset) throws RESTErrorResponseExeption, IOException, InterruptedException {
final ObjectMapper mapper = new ObjectMapper();
final HttpClient client = HttpClient.newHttpClient();
Builder requestBuilding = HttpRequest.newBuilder().uri(URI.create(this.baseUrl + urlOffset));
if (this.token != null) {
requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token);
}
final HttpRequest request = requestBuilding.GET().build();
final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
// LOGGER.error("catch error from REST API: {}", httpResponse.body());
final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw new RESTErrorResponseExeption(out.uuid, out.time, out.error, out.message, out.status, out.statusMessage);
}
// LOGGER.error("status code: {}", httpResponse.statusCode());
// LOGGER.error("data: {}", httpResponse.body());
if (clazz.equals(String.class)) {
return (T) httpResponse.body();
}
final T out = mapper.readValue(httpResponse.body(), clazz);
return out;
return modelSendJson("GET", clazz, urlOffset, null);
}
public <T, U> T post(final Class<T> clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException {
final ObjectMapper mapper = new ObjectMapper();
final HttpClient client = HttpClient.newHttpClient();
final String body = mapper.writeValueAsString(data);
Builder requestBuilding = HttpRequest.newBuilder().uri(URI.create(this.baseUrl + urlOffset));
if (this.token != null) {
requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token);
}
requestBuilding = requestBuilding.header("Content-Type", "application/json");
final HttpRequest request = requestBuilding.POST(BodyPublishers.ofString(body)).build();
final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
LOGGER.error("status code: {}", httpResponse.statusCode());
LOGGER.error("data: {}", httpResponse.body());
final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw out;
}
if (clazz.equals(String.class)) {
return (T) httpResponse.body();
}
final T out = mapper.readValue(httpResponse.body(), clazz);
return out;
return modelSend("POST", clazz, urlOffset, data);
}
public <T, U> T postJson(final Class<T> clazz, final String urlOffset, final String body) throws RESTErrorResponseExeption, 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 RESTErrorResponseExeption, IOException, InterruptedException {
final ObjectMapper mapper = new ObjectMapper();
final HttpClient client = HttpClient.newHttpClient();
final String body = mapper.writeValueAsString(data);
Builder requestBuilding = HttpRequest.newBuilder().uri(URI.create(this.baseUrl + urlOffset));
if (this.token != null) {
requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token);
}
requestBuilding = requestBuilding.header("Content-Type", "application/json");
final HttpRequest request = requestBuilding.POST(BodyPublishers.ofString(body)).build();
final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw out;
}
if (clazz.equals(String.class)) {
return (T) httpResponse.body();
}
final T out = mapper.readValue(httpResponse.body(), clazz);
return out;
return modelSendMap("POST", clazz, urlOffset, data);
}
public <T, U> T put(final Class<T> clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException {
final ObjectMapper mapper = new ObjectMapper();
final HttpClient client = HttpClient.newHttpClient();
final String body = mapper.writeValueAsString(data);
Builder requestBuilding = HttpRequest.newBuilder().uri(URI.create(this.baseUrl + urlOffset));
if (this.token != null) {
requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token);
}
requestBuilding = requestBuilding.header("Content-Type", "application/json");
final HttpRequest request = requestBuilding.PUT(BodyPublishers.ofString(body)).build();
final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw out;
}
if (clazz.equals(String.class)) {
return (T) httpResponse.body();
}
final T out = mapper.readValue(httpResponse.body(), clazz);
return out;
return modelSend("PUT", clazz, urlOffset, data);
}
public <T, U> T putJson(final Class<T> clazz, final String urlOffset, final String body) throws RESTErrorResponseExeption, 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 RESTErrorResponseExeption, IOException, InterruptedException {
final ObjectMapper mapper = new ObjectMapper();
return modelSendMap("PUT", clazz, urlOffset, data);
}
public <T, U> T patch(final Class<T> clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException {
return modelSend("PATCH", clazz, urlOffset, data);
}
public <T, U> T patchJson(final Class<T> clazz, final String urlOffset, final String body) throws RESTErrorResponseExeption, 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 RESTErrorResponseExeption, 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 RESTErrorResponseExeption, IOException, InterruptedException {
if (data == null) {
return modelSendJson(model, clazz, urlOffset, null);
} else {
final String body = this.mapper.writeValueAsString(data);
return modelSendJson(model, clazz, urlOffset, body);
}
}
protected <T, U> T modelSendJson(final String model, final Class<T> clazz, final String urlOffset, String body) throws RESTErrorResponseExeption, IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient();
final String body = mapper.writeValueAsString(data);
Builder requestBuilding = HttpRequest.newBuilder().uri(URI.create(this.baseUrl + urlOffset));
// client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
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, "Yota " + this.token);
}
requestBuilding = requestBuilding.header("Content-Type", "application/json");
final HttpRequest request = requestBuilding.PUT(BodyPublishers.ofString(body)).build();
if (body == null) {
body = "";
} else {
requestBuilding = requestBuilding.header("Content-Type", "application/json");
}
final HttpRequest request = requestBuilding.method(model, BodyPublishers.ofString(body)).build();
final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw out;
try {
final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw new RESTErrorResponseExeption(out.uuid, out.time, out.error, out.message, out.status, out.statusMessage);
} catch (final MismatchedInputException ex) {
throw new IOException("Fail to get the data [" + 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());
}
}
if (clazz.equals(String.class)) {
return (T) httpResponse.body();
}
final T out = mapper.readValue(httpResponse.body(), clazz);
return out;
return this.mapper.readValue(httpResponse.body(), clazz);
}
public <T, U> T delete(final Class<T> clazz, final String urlOffset) throws RESTErrorResponseExeption, IOException, InterruptedException {
final ObjectMapper mapper = new ObjectMapper();
protected <T> T modelSendMap(final String model, final Class<T> clazz, final String urlOffset, final Map<String, Object> data) throws RESTErrorResponseExeption, IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient();
Builder requestBuilding = HttpRequest.newBuilder().uri(URI.create(this.baseUrl + urlOffset));
String body = null;
Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1).uri(URI.create(this.baseUrl + urlOffset));
if (this.token != null) {
requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token);
}
if (data == null) {
body = "";
} else {
body = this.mapper.writeValueAsString(data);
requestBuilding = requestBuilding.header("Content-Type", "application/json");
}
final HttpRequest request = requestBuilding.method(model, BodyPublishers.ofString(body)).build();
final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
try {
final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw new RESTErrorResponseExeption(out.uuid, out.time, out.error, out.message, out.status, out.statusMessage);
} catch (final MismatchedInputException ex) {
throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body());
}
}
if (clazz.equals(String.class)) {
return (T) httpResponse.body();
}
return this.mapper.readValue(httpResponse.body(), clazz);
}
public <T, U> void delete(final Class<T> clazz, final String urlOffset) throws RESTErrorResponseExeption, IOException, InterruptedException {
final HttpClient client = HttpClient.newHttpClient();
Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1).uri(URI.create(this.baseUrl + urlOffset));
if (this.token != null) {
requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token);
}
final HttpRequest request = requestBuilding.DELETE().build();
final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw out;
try {
final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw new RESTErrorResponseExeption(out.uuid, out.time, out.error, out.message, out.status, out.statusMessage);
} catch (final MismatchedInputException ex) {
throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body());
}
}
if (clazz.equals(String.class)) {
return (T) httpResponse.body();
}
final T out = mapper.readValue(httpResponse.body(), clazz);
return out;
}
}

View File

@@ -0,0 +1,99 @@
package test.kar.archidata;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.DataFactory;
import org.kar.archidata.db.DBEntry;
import org.kar.archidata.tools.ConfigBaseVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import test.kar.archidata.model.SerializeListAsJson;
@ExtendWith(StepwiseExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TestListJson {
final static private Logger LOGGER = LoggerFactory.getLogger(TestListJson.class);
@BeforeAll
public static void configureWebServer() throws Exception {
if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) {
ConfigBaseVariable.dbType = "sqlite";
ConfigBaseVariable.dbHost = "memory";
// for test we need to connect all time the DB
ConfigBaseVariable.dbKeepConnected = "true";
}
// Connect the dataBase...
final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig);
entry.connect();
}
@AfterAll
public static void removeDataBase() throws IOException {
LOGGER.info("Remove the test db");
DBEntry.closeAllForceMode();
ConfigBaseVariable.clearAllValue();
}
@Order(1)
@Test
public void testTableInsertAndRetrieve() throws Exception {
final List<String> sqlCommand = DataFactory.createTable(SerializeListAsJson.class);
for (final String elem : sqlCommand) {
LOGGER.debug("request: '{}'", elem);
DataAccess.executeSimpleQuerry(elem, false);
}
}
@Order(2)
@Test
public void testIO() throws Exception {
final SerializeListAsJson test = new SerializeListAsJson();
test.data = new ArrayList<>();
test.data.add(5);
test.data.add(2);
test.data.add(8);
test.data.add(6);
test.data.add(51);
final SerializeListAsJson insertedData = DataAccess.insert(test);
Assertions.assertNotNull(insertedData);
Assertions.assertNotNull(insertedData.id);
Assertions.assertTrue(insertedData.id >= 0);
Assertions.assertNotNull(insertedData.data);
Assertions.assertEquals(5, insertedData.data.size());
Assertions.assertEquals(test.data.get(0), insertedData.data.get(0));
Assertions.assertEquals(test.data.get(1), insertedData.data.get(1));
Assertions.assertEquals(test.data.get(2), insertedData.data.get(2));
Assertions.assertEquals(test.data.get(3), insertedData.data.get(3));
Assertions.assertEquals(test.data.get(4), insertedData.data.get(4));
// Try to retrieve all the data:
final SerializeListAsJson retrieve = DataAccess.get(SerializeListAsJson.class, insertedData.id);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
Assertions.assertTrue(retrieve.id >= 0);
Assertions.assertNotNull(retrieve.data);
Assertions.assertEquals(5, retrieve.data.size());
Assertions.assertEquals(test.data.get(0), retrieve.data.get(0));
Assertions.assertEquals(test.data.get(1), retrieve.data.get(1));
Assertions.assertEquals(test.data.get(2), retrieve.data.get(2));
Assertions.assertEquals(test.data.get(3), retrieve.data.get(3));
Assertions.assertEquals(test.data.get(4), retrieve.data.get(4));
}
}

View File

@@ -91,7 +91,7 @@ public class TestSimpleTable {
@Test
public void testReadAllValuesUnreadable() throws Exception {
// check the full values
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, new QueryOptions(QueryOptions.READ_ALL_COLOMN));
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, QueryOptions.READ_ALL_COLOMN);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
@@ -114,7 +114,7 @@ public class TestSimpleTable {
final SimpleTable test = new SimpleTable();
test.data = TestSimpleTable.DATA_INJECTED_2;
DataAccess.update(test, TestSimpleTable.idOfTheObject, List.of("data"));
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, new QueryOptions(QueryOptions.READ_ALL_COLOMN));
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, QueryOptions.READ_ALL_COLOMN);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
Assertions.assertEquals(TestSimpleTable.idOfTheObject, retrieve.id);
@@ -139,7 +139,7 @@ public class TestSimpleTable {
public void testReadDeletedObject() throws Exception {
// check if we set get deleted element
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, new QueryOptions(QueryOptions.ACCESS_DELETED_ITEMS));
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, QueryOptions.ACCESS_DELETED_ITEMS);
Assertions.assertNull(retrieve);
}
@@ -148,7 +148,7 @@ public class TestSimpleTable {
@Test
public void testReadAllValuesUnreadableOfDeletedObject() throws Exception {
// check if we set get deleted element with all data
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, new QueryOptions(QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN));
final SimpleTable retrieve = DataAccess.get(SimpleTable.class, TestSimpleTable.idOfTheObject, QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN);
Assertions.assertNull(retrieve);
}

View File

@@ -92,7 +92,7 @@ public class TestSimpleTableSoftDelete {
@Test
public void testReadAllValuesUnreadable() throws Exception {
// check the full values
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, new QueryOptions(QueryOptions.READ_ALL_COLOMN));
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, QueryOptions.READ_ALL_COLOMN);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
@@ -118,8 +118,7 @@ public class TestSimpleTableSoftDelete {
final SimpleTableSoftDelete test = new SimpleTableSoftDelete();
test.data = TestSimpleTableSoftDelete.DATA_INJECTED_2;
DataAccess.update(test, TestSimpleTableSoftDelete.idOfTheObject, List.of("data"));
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject,
new QueryOptions(QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN));
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
Assertions.assertEquals(TestSimpleTableSoftDelete.idOfTheObject, retrieve.id);
@@ -151,7 +150,7 @@ public class TestSimpleTableSoftDelete {
public void testReadDeletedObject() throws Exception {
// check if we set get deleted element
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, new QueryOptions(QueryOptions.ACCESS_DELETED_ITEMS));
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, QueryOptions.ACCESS_DELETED_ITEMS);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
Assertions.assertEquals(TestSimpleTableSoftDelete.idOfTheObject, retrieve.id);
@@ -166,8 +165,7 @@ public class TestSimpleTableSoftDelete {
@Test
public void testReadAllValuesUnreadableOfDeletedObject() throws Exception {
// check if we set get deleted element with all data
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject,
new QueryOptions(QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN));
final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
Assertions.assertEquals(TestSimpleTableSoftDelete.idOfTheObject, retrieve.id);

View File

@@ -449,7 +449,7 @@ public class TestTypes {
"varcharData": null
}
""";
final int nbUpdate = DataAccess.updateWithJson(TypesTable.class, insertedData.id, jsonData);
final int nbUpdate = DataAccess.updateWithJson(TypesTable.class, insertedData.id, jsonData, null);
Assertions.assertEquals(1, nbUpdate);
// Get new data

View File

@@ -0,0 +1,13 @@
package test.kar.archidata.model;
import java.util.List;
import org.kar.archidata.annotation.DataJson;
import org.kar.archidata.model.GenericData;
public class SerializeListAsJson extends GenericData {
@DataJson
public List<Integer> data;
}

View File

@@ -18,7 +18,7 @@ function __run() #(step, name, cmd)
if [[ 0 == $exitcode ]]; then
echo -e "${C_GREEN}OK!${C_RESET}"
else
echo -e "${C_RED}NOK! (${exitcode})${C_RESET}\n\n$output"
echo -e "${C_RED}ERROR! (${exitcode})${C_RESET}\n\n$output"
exit 1
fi
}

View File

@@ -1 +1 @@
0.5.0
0.6.0