Compare commits
	
		
			89 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cb0ee69820 | |||
| cfc7ec2ff1 | |||
| 0605ef0824 | |||
| 3309abe053 | |||
| f069ce7cc3 | |||
| a82c3e725d | |||
| 08124f212a | |||
| 631999e135 | |||
| 7674d9a299 | |||
| 9cd71ab601 | |||
| 4cb8c84312 | |||
| 5acdbfb0c7 | |||
| d35c83fcbf | |||
| 0fe769a203 | |||
| dbe1b469f6 | |||
| 875b4b313b | |||
| 15a7814a3b | |||
| 530d254bf2 | |||
| e1d9da70c8 | |||
| c13f910620 | |||
| 6944331761 | |||
| 151aa498e5 | |||
| db6a20fa02 | |||
| 8e913b54ad | |||
| 092bef5a7b | |||
| e5d75bc97b | |||
| 7c217716c9 | |||
| 526f902eae | |||
| 42ac2f3056 | |||
| 389e4138c3 | |||
| 6584022861 | |||
| 41fb181545 | |||
| 56609e4f59 | |||
| e0b7ed1e1e | |||
| 48c32a3744 | |||
| fc521d2287 | |||
| 15125e2d6d | |||
| 6f1dd991c5 | |||
| 009831f5db | |||
| 1bcdb1df8c | |||
| 7ae948bb79 | |||
| 29402fc27e | |||
| eca28292d5 | |||
| aef4cdabc3 | |||
| 04114aa0cf | |||
| 575102fbfa | |||
| 19fa13849e | |||
| 9ffebf1d1d | |||
| e35a1ae879 | |||
| 9c9cf85f92 | |||
| 1a2302f548 | |||
| 808784889b | |||
| f394254f38 | |||
| 0574d5da82 | |||
| 62435cd97e | |||
| d983210a10 | |||
| a704008f7d | |||
| 04a2014799 | |||
| b74b404e72 | |||
| 42245e026b | |||
| 284c00a88c | |||
| 0ae8f9ad16 | |||
| 7b31c66ce5 | |||
| fe63c6b954 | |||
| b4b6e40250 | |||
| 4d1f70a637 | |||
| 86dc0351fe | |||
| a049d3ca30 | |||
| c90af0bdc9 | |||
| f3ea7983df | |||
| ba314a97ed | |||
| c99fb84ef4 | |||
| eff5513705 | |||
| 66796d9591 | |||
| 554e2493aa | |||
| 49654698a6 | |||
| 61f3e70835 | |||
| 6ac2f1dcfc | |||
| 5b7fdce349 | |||
| 058fb2e640 | |||
| dfc7c77f47 | |||
| 1c82fb1a86 | |||
| 3b0c73bd55 | |||
| d9fcacc812 | |||
| 5fc45a23d3 | |||
| 81b6438a7c | |||
| 26ba20d964 | |||
| 659f9ca306 | |||
| 9f337db62c | 
							
								
								
									
										19
									
								
								.classpath
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								.classpath
									
									
									
									
									
								
							| @@ -1,12 +1,12 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <classpath> | ||||
| 	<classpathentry including="**/*.java" kind="src" output="out/maven/classes" path="src"> | ||||
| 	<classpathentry including="**/*.java" kind="src" output="target/classes" path="src"> | ||||
| 		<attributes> | ||||
| 			<attribute name="optional" value="true"/> | ||||
| 			<attribute name="maven.pomderived" value="true"/> | ||||
| 		</attributes> | ||||
| 	</classpathentry> | ||||
| 	<classpathentry kind="src" output="out/maven/test-classes" path="test/src"> | ||||
| 	<classpathentry kind="src" output="target/test-classes" path="test/src"> | ||||
| 		<attributes> | ||||
| 			<attribute name="test" value="true"/> | ||||
| 			<attribute name="optional" value="true"/> | ||||
| @@ -18,7 +18,7 @@ | ||||
| 			<attribute name="maven.pomderived" value="true"/> | ||||
| 		</attributes> | ||||
| 	</classpathentry> | ||||
| 	<classpathentry excluding="**" kind="src" output="out/maven/test-classes" path="test/resources"> | ||||
| 	<classpathentry excluding="**" kind="src" output="target/test-classes" path="test/resources"> | ||||
| 		<attributes> | ||||
| 			<attribute name="test" value="true"/> | ||||
| 			<attribute name="maven.pomderived" value="true"/> | ||||
| @@ -30,5 +30,16 @@ | ||||
| 			<attribute name="maven.pomderived" value="true"/> | ||||
| 		</attributes> | ||||
| 	</classpathentry> | ||||
| 	<classpathentry kind="output" path="out/maven/classes"/> | ||||
| 	<classpathentry kind="src" path="target/generated-sources/annotations"> | ||||
| 		<attributes> | ||||
| 			<attribute name="optional" value="true"/> | ||||
| 		</attributes> | ||||
| 	</classpathentry> | ||||
| 	<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations"> | ||||
| 		<attributes> | ||||
| 			<attribute name="test" value="true"/> | ||||
| 			<attribute name="optional" value="true"/> | ||||
| 		</attributes> | ||||
| 	</classpathentry> | ||||
| 	<classpathentry kind="output" path="target/classes"/> | ||||
| </classpath> | ||||
|   | ||||
							
								
								
									
										12
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal 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
									
								
							
							
						
						
									
										37
									
								
								.github/workflows/maven.yml
									
									
									
									
										vendored
									
									
										Normal 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
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,4 @@ | ||||
| out/ | ||||
| target/ | ||||
| *.class | ||||
| .settings/ | ||||
|   | ||||
							
								
								
									
										4
									
								
								.island/release.bash
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										4
									
								
								.island/release.bash
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| mvn versions:set -DnewVersion=$(cat version.txt) | ||||
|  | ||||
							
								
								
									
										375
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										375
									
								
								LICENSE
									
									
									
									
									
								
							| @@ -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. | ||||
|   | ||||
							
								
								
									
										74
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								README.md
									
									
									
									
									
								
							| @@ -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,22 @@ 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 +123,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
									
								
							
							
						
						
									
										1
									
								
								dependabot.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
|  | ||||
							
								
								
									
										66
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -3,19 +3,16 @@ | ||||
| 	<modelVersion>4.0.0</modelVersion> | ||||
| 	<groupId>kangaroo-and-rabbit</groupId> | ||||
| 	<artifactId>archidata</artifactId> | ||||
| 	<version>0.5.0</version> | ||||
| 	<version>0.8.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> | ||||
| @@ -112,8 +107,19 @@ | ||||
| 		<dependency> | ||||
| 			<groupId>com.fasterxml.jackson.core</groupId> | ||||
| 			<artifactId>jackson-databind</artifactId> | ||||
|             <version>2.16.0</version> | ||||
| 			<version>2.16.1</version> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>com.fasterxml.jackson.dataformat</groupId> | ||||
| 			<artifactId>jackson-dataformat-csv</artifactId> | ||||
| 			<version>2.16.1</version> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 			<groupId>com.fasterxml.jackson.datatype</groupId> | ||||
| 			<artifactId>jackson-datatype-jsr310</artifactId> | ||||
| 			<version>2.16.1</version> | ||||
| 		</dependency> | ||||
|  | ||||
| 		<dependency> | ||||
| 			<groupId>jakarta.servlet</groupId> | ||||
| 			<artifactId>jakarta.servlet-api</artifactId> | ||||
| @@ -129,7 +135,7 @@ | ||||
| 		<dependency> | ||||
| 			<groupId>org.xerial</groupId> | ||||
| 			<artifactId>sqlite-jdbc</artifactId> | ||||
| 	        <version>3.44.1.0</version> | ||||
| 			<version>3.40.0.0</version> | ||||
| 		</dependency> | ||||
| 		<!-- Interface for JWT token --> | ||||
| 		<dependency> | ||||
| @@ -142,6 +148,12 @@ | ||||
| 			<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                                        ** | ||||
| @@ -159,12 +171,26 @@ | ||||
| 			<version>5.10.1</version> | ||||
| 	        <scope>test</scope> | ||||
| 	    </dependency> | ||||
| 	    <dependency> | ||||
| 		    <groupId>net.revelc.code.formatter</groupId> | ||||
| 		    <artifactId>formatter-maven-plugin</artifactId> | ||||
| 		    <version>2.23.0</version> | ||||
| 		</dependency> | ||||
| 		<dependency> | ||||
| 		    <groupId>org.apache.maven.plugins</groupId> | ||||
| 		    <artifactId>maven-checkstyle-plugin</artifactId> | ||||
| 		    <version>3.3.1</version> | ||||
| 		</dependency> | ||||
|     </dependencies> | ||||
|  | ||||
| 	<build> | ||||
| 		<sourceDirectory>src</sourceDirectory> | ||||
| 		<resources> | ||||
| 			<resource> | ||||
| 				<directory>${basedir}/src/resources</directory> | ||||
| 			</resource> | ||||
| 		</resources> | ||||
| 		<testSourceDirectory>test/src</testSourceDirectory> | ||||
|     	<directory>${project.basedir}/out/maven/</directory> | ||||
| 		<testResources> | ||||
| 			<testResource> | ||||
| 				<directory>${basedir}/test/resources</directory> | ||||
| @@ -185,6 +211,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,6 +221,20 @@ | ||||
| 					</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> | ||||
| @@ -227,7 +268,7 @@ | ||||
| 			<plugin> | ||||
| 				<groupId>org.apache.maven.plugins</groupId> | ||||
| 				<artifactId>maven-checkstyle-plugin</artifactId> | ||||
| 				<version>3.1.0</version> | ||||
| 		    	<version>3.3.1</version> | ||||
| 				<configuration> | ||||
| 					<configLocation>CheckStyle.xml</configLocation> | ||||
| 					<consoleOutput>true</consoleOutput> | ||||
| @@ -239,7 +280,7 @@ | ||||
| 			<plugin> | ||||
| 				<groupId>net.revelc.code.formatter</groupId> | ||||
| 				<artifactId>formatter-maven-plugin</artifactId> | ||||
| 				<version>2.12.2</version> | ||||
| 		    	<version>2.23.0</version> | ||||
| 				<configuration> | ||||
| 					<encoding>UTF-8</encoding> | ||||
| 					<lineEnding>LF</lineEnding> | ||||
| @@ -278,5 +319,4 @@ | ||||
| 			</plugin> | ||||
| 		</plugins> | ||||
| 	</reporting> | ||||
|  | ||||
| </project> | ||||
|   | ||||
| @@ -5,6 +5,9 @@ import java.sql.PreparedStatement; | ||||
| import java.sql.SQLException; | ||||
|  | ||||
| import org.kar.archidata.dataAccess.DataAccess; | ||||
| import org.kar.archidata.dataAccess.QueryOption; | ||||
| import org.kar.archidata.dataAccess.QueryOptions; | ||||
| import org.kar.archidata.dataAccess.options.DBInterfaceOption; | ||||
| import org.kar.archidata.db.DBEntry; | ||||
| import org.kar.archidata.model.User; | ||||
|  | ||||
| @@ -12,21 +15,22 @@ public class UserDB { | ||||
|  | ||||
| 	public UserDB() {} | ||||
|  | ||||
| 	public static User getUsers(final long userId) throws Exception { | ||||
| 		return DataAccess.get(User.class, userId); | ||||
| 	public static User getUsers(final long userId, QueryOption... option) throws Exception { | ||||
| 		return DataAccess.get(User.class, userId, option); | ||||
| 	} | ||||
|  | ||||
| 	public static User getUserOrCreate(final long userId, final String userLogin) throws Exception { | ||||
| 	public static User getUserOrCreate(final long userId, final String userLogin, QueryOption... option) throws Exception { | ||||
| 		final User user = getUsers(userId); | ||||
| 		if (user != null) { | ||||
| 			return user; | ||||
| 		} | ||||
| 		createUsersInfoFromOAuth(userId, userLogin); | ||||
| 		createUsersInfoFromOAuth(userId, userLogin, option); | ||||
| 		return getUsers(userId); | ||||
| 	} | ||||
|  | ||||
| 	private static void createUsersInfoFromOAuth(final long userId, final String login) throws IOException { | ||||
| 		final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig); | ||||
| 	private static void createUsersInfoFromOAuth(final long userId, final String login, QueryOption... option) throws IOException { | ||||
| 		QueryOptions options = new QueryOptions(option); | ||||
| 		final DBEntry entry = DBInterfaceOption.getAutoEntry(options); | ||||
| 		final String query = "INSERT INTO `user` (`id`, `login`, `lastConnection`, `admin`, `blocked`, `removed`) VALUE (?,?,now(3),'0','0','0')"; | ||||
| 		try { | ||||
| 			final PreparedStatement ps = entry.connection.prepareStatement(query); | ||||
|   | ||||
| @@ -10,11 +10,19 @@ 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; | ||||
| import jakarta.ws.rs.DefaultValue; | ||||
|  | ||||
| public class AnnotationTools { | ||||
| 	static final Logger LOGGER = LoggerFactory.getLogger(AnnotationTools.class); | ||||
| @@ -45,10 +53,54 @@ public class AnnotationTools { | ||||
| 		return tmp; | ||||
| 	} | ||||
|  | ||||
| 	public static boolean getSchemaReadOnly(final Field element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return false; | ||||
| 		} | ||||
| 		if (annotation.length > 1) { | ||||
| 			throw new Exception("Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName()); | ||||
| 		} | ||||
| 		return ((Schema) annotation[0]).readOnly(); | ||||
| 	} | ||||
|  | ||||
| 	public static String getSchemaExample(final Class<?> 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]).example(); | ||||
| 	} | ||||
|  | ||||
| 	public static String getSchemaDescription(final Class<?> 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 getSchemaDescription(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 getSchemaDescription(element); | ||||
| 		} | ||||
| 		if (annotation.length > 1) { | ||||
| 			throw new Exception("Must not have more than 1 element @DataComment on " + element.getClass().getCanonicalName()); | ||||
| @@ -57,17 +109,61 @@ public class AnnotationTools { | ||||
| 	} | ||||
|  | ||||
| 	public static String getDefault(final Field element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataDefault.class); | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(DefaultValue.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		if (annotation.length > 1) { | ||||
| 			throw new Exception("Must not have more than 1 element @DataDefault on " + element.getClass().getCanonicalName()); | ||||
| 		} | ||||
| 		return ((DataDefault) annotation[0]).value(); | ||||
| 		return ((DefaultValue) 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 +172,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 +235,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 +246,39 @@ 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 Field getPrimaryKeyField(final Class<?> clazz) throws Exception { | ||||
| 		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.isPrimaryKey(field)) { | ||||
| 				return field; | ||||
| 			} | ||||
| 		} | ||||
| 		return null; | ||||
| 	} | ||||
|  | ||||
| 	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 +395,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 { | ||||
| @@ -261,4 +411,17 @@ public class AnnotationTools { | ||||
| 		return null; | ||||
| 	} | ||||
|  | ||||
| 	public static Field getFieldNamed(final Class<?> clazz, final String name) throws Exception { | ||||
| 		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.getFieldName(field).equals(name)) { | ||||
| 				return field; | ||||
| 			} | ||||
| 		} | ||||
| 		return null; | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										17
									
								
								src/org/kar/archidata/annotation/AsyncType.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/org/kar/archidata/annotation/AsyncType.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| package org.kar.archidata.annotation; | ||||
|  | ||||
| import java.lang.annotation.ElementType; | ||||
| import java.lang.annotation.Retention; | ||||
| import java.lang.annotation.RetentionPolicy; | ||||
| import java.lang.annotation.Target; | ||||
|  | ||||
| /** In case of the update parameter with String input to detect null element. */ | ||||
| @Target({ ElementType.PARAMETER, ElementType.METHOD }) | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| public @interface AsyncType { | ||||
| 	// Possible class values. | ||||
| 	Class<?>[] value(); | ||||
|  | ||||
| 	// direct copy value in the TypeScript (separate with type by a | | ||||
| 	String[] tsComplement() default {}; | ||||
| } | ||||
| @@ -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(); | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,13 @@ 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; | ||||
|  | ||||
| 	Class<?> targetEntity() default Void.class; | ||||
| } | ||||
|   | ||||
| @@ -5,10 +5,7 @@ import java.lang.annotation.Retention; | ||||
| import java.lang.annotation.RetentionPolicy; | ||||
| import java.lang.annotation.Target; | ||||
| 
 | ||||
| @Target({ ElementType.TYPE, ElementType.FIELD }) | ||||
| /** In case of the update parameter with String input to detect null element. */ | ||||
| @Target({ ElementType.PARAMETER, ElementType.METHOD }) | ||||
| @Retention(RetentionPolicy.RUNTIME) | ||||
| public @interface DataDefault { | ||||
| 
 | ||||
| 	String value(); | ||||
| 
 | ||||
| } | ||||
| public @interface TypeScriptProgress {} | ||||
| @@ -16,6 +16,7 @@ import java.nio.file.StandardCopyOption; | ||||
| import java.security.MessageDigest; | ||||
| import java.security.NoSuchAlgorithmException; | ||||
| import java.util.Date; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import javax.imageio.ImageIO; | ||||
|  | ||||
| @@ -24,12 +25,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; | ||||
| @@ -54,7 +57,7 @@ import jakarta.ws.rs.core.StreamingOutput; | ||||
| @Path("/data") | ||||
| @Produces(MediaType.APPLICATION_JSON) | ||||
| public class DataResource { | ||||
| 	private static final Logger LOGGER = LoggerFactory.getLogger(MediaType.class); | ||||
| 	private static final Logger LOGGER = LoggerFactory.getLogger(DataResource.class); | ||||
| 	private final static int CHUNK_SIZE = 1024 * 1024; // 1MB chunks | ||||
| 	private final static int CHUNK_SIZE_IN = 50 * 1024 * 1024; // 1MB chunks | ||||
| 	/** Upload some datas */ | ||||
| @@ -81,7 +84,7 @@ public class DataResource { | ||||
| 		return filePath; | ||||
| 	} | ||||
|  | ||||
| 	public static String getFileData(final long tmpFolderId) { | ||||
| 	public static String getFileDataOld(final long tmpFolderId) { | ||||
| 		final String filePath = ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator + "data"; | ||||
| 		try { | ||||
| 			createFolder(ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator); | ||||
| @@ -91,10 +94,30 @@ public class DataResource { | ||||
| 		return filePath; | ||||
| 	} | ||||
|  | ||||
| 	public static String getFileData(final UUID uuid) { | ||||
| 		final String stringUUID = uuid.toString(); | ||||
| 		final String part1 = stringUUID.substring(0, 2); | ||||
| 		final String part2 = stringUUID.substring(2, 4); | ||||
| 		final String part3 = stringUUID.substring(4); | ||||
| 		final String finalPath = part1 + File.separator + part2; | ||||
| 		String filePath = ConfigBaseVariable.getMediaDataFolder() + "_uuid" + File.separator + finalPath + File.separator; | ||||
| 		try { | ||||
| 			createFolder(filePath); | ||||
| 		} catch (final IOException e) { | ||||
| 			e.printStackTrace(); | ||||
| 		} | ||||
| 		filePath += part3; | ||||
| 		return filePath; | ||||
| 	} | ||||
|  | ||||
| 	public static String getFileMetaData(final UUID uuid) { | ||||
| 		return getFileData(uuid) + ".json"; | ||||
| 	} | ||||
|  | ||||
| 	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(); | ||||
| @@ -135,7 +158,6 @@ public class DataResource { | ||||
| 		try { | ||||
| 			injectedData = DataAccess.insert(injectedData); | ||||
| 		} catch (final Exception e) { | ||||
| 			// TODO Auto-generated catch block | ||||
| 			e.printStackTrace(); | ||||
| 			return null; | ||||
| 		} | ||||
| @@ -147,6 +169,25 @@ public class DataResource { | ||||
| 		return injectedData; | ||||
| 	} | ||||
|  | ||||
| 	public static void modeFileOldModelToNewModel(final long id, final UUID uuid) throws IOException { | ||||
| 		String mediaCurentPath = getFileDataOld(id); | ||||
| 		String mediaDestPath = getFileData(uuid); | ||||
| 		LOGGER.info("src = {}", mediaCurentPath); | ||||
| 		LOGGER.info("dst = {}", mediaDestPath); | ||||
| 		if (Files.exists(Paths.get(mediaCurentPath))) { | ||||
| 			LOGGER.info("move: {} ==> {}", mediaCurentPath, mediaDestPath); | ||||
| 			Files.move(Paths.get(mediaCurentPath), Paths.get(mediaDestPath), StandardCopyOption.ATOMIC_MOVE); | ||||
| 		} | ||||
| 		// Move old meta-data... | ||||
| 		mediaCurentPath = mediaCurentPath.substring(mediaCurentPath.length() - 4) + "meta.json"; | ||||
| 		mediaDestPath = mediaCurentPath.substring(mediaDestPath.length() - 4) + "meta.json"; | ||||
| 		if (Files.exists(Paths.get(mediaCurentPath))) { | ||||
| 			LOGGER.info("moveM: {} ==> {}", mediaCurentPath, mediaDestPath); | ||||
| 			Files.move(Paths.get(mediaCurentPath), Paths.get(mediaDestPath), StandardCopyOption.ATOMIC_MOVE); | ||||
| 		} | ||||
| 		LOGGER.info("Move done"); | ||||
| 	} | ||||
|  | ||||
| 	public static String saveTemporaryFile(final InputStream uploadedInputStream, final long idData) { | ||||
| 		return saveFile(uploadedInputStream, DataResource.getTmpFileInData(idData)); | ||||
| 	} | ||||
| @@ -205,7 +246,7 @@ public class DataResource { | ||||
| 		return sb.toString(); | ||||
| 	} | ||||
|  | ||||
| 	public Data getSmall(final Long id) { | ||||
| 	public Data getSmall(final UUID id) { | ||||
| 		try { | ||||
| 			return DataAccess.get(Data.class, id); | ||||
| 		} catch (final Exception e) { | ||||
| @@ -219,7 +260,8 @@ public class DataResource { | ||||
| 	@Path("/upload/") | ||||
| 	@Consumes({ MediaType.MULTIPART_FORM_DATA }) | ||||
| 	@RolesAllowed("ADMIN") | ||||
| 	public Response uploadFile(@Context final SecurityContext sc, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData) { | ||||
| 	@Operation(description = "Insert a new data in the data environment", tags = "SYSTEM") | ||||
| 	public void uploadFile(@Context final SecurityContext sc, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData) { | ||||
| 		final GenericContext gc = (GenericContext) sc.getUserPrincipal(); | ||||
| 		LOGGER.info("==================================================="); | ||||
| 		LOGGER.info("== DATA uploadFile {}", (gc == null ? "null" : gc.userByToken)); | ||||
| @@ -233,8 +275,6 @@ public class DataResource { | ||||
| 			e.printStackTrace(); | ||||
| 		} | ||||
| 		saveFile(fileInputStream, filePath); | ||||
| 		return Response.ok("Data uploaded successfully !!").build(); | ||||
| 		// return null; | ||||
| 	} | ||||
|  | ||||
| 	@GET | ||||
| @@ -242,17 +282,18 @@ public class DataResource { | ||||
| 	@PermitTokenInURI | ||||
| 	@RolesAllowed("USER") | ||||
| 	@Produces(MediaType.APPLICATION_OCTET_STREAM) | ||||
| 	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 { | ||||
| 	@Operation(description = "Get back some data from the data environment", tags = "SYSTEM") | ||||
| 	public Response retrieveDataId(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range, | ||||
| 			@PathParam("id") final UUID id) throws Exception { | ||||
| 		final GenericContext gc = (GenericContext) sc.getUserPrincipal(); | ||||
| 		// logger.info("==================================================="); | ||||
| 		LOGGER.info("== DATA retriveDataId ? id={} user={}", id, (gc == null ? "null" : gc.userByToken)); | ||||
| 		LOGGER.info("== DATA retrieveDataId ? id={} user={}", id, (gc == null ? "null" : gc.userByToken)); | ||||
| 		// logger.info("==================================================="); | ||||
| 		final Data value = getSmall(id); | ||||
| 		if (value == null) { | ||||
| 			Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build(); | ||||
| 		} | ||||
| 		return buildStream(ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data", range, value.mimeType); | ||||
| 		return buildStream(getFileData(id), range, value.mimeType); | ||||
| 	} | ||||
|  | ||||
| 	@GET | ||||
| @@ -260,18 +301,19 @@ 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 { | ||||
| 	public Response retrieveDataThumbnailId(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range, | ||||
| 			@PathParam("id") final UUID id) throws Exception { | ||||
| 		// GenericContext gc = (GenericContext) sc.getUserPrincipal(); | ||||
| 		// logger.info("==================================================="); | ||||
| 		// logger.info("== DATA retriveDataThumbnailId ? {}", (gc==null?"null":gc.user)); | ||||
| 		// logger.info("== DATA retrieveDataThumbnailId ? {}", (gc==null?"null":gc.user)); | ||||
| 		// logger.info("==================================================="); | ||||
| 		final Data value = getSmall(id); | ||||
| 		if (value == null) { | ||||
| 			return Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build(); | ||||
| 		} | ||||
| 		final String filePathName = ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data"; | ||||
| 		final String filePathName = getFileData(id); | ||||
| 		final File inputFile = new File(filePathName); | ||||
| 		if (!inputFile.exists()) { | ||||
| 			return Response.status(404).entity("{\"error\":\"media Does not exist: " + id + "\"}").type("application/json").build(); | ||||
| @@ -313,23 +355,23 @@ public class DataResource { | ||||
| 		return buildStream(filePathName, range, value.mimeType); | ||||
| 	} | ||||
|  | ||||
| 	// @Secured | ||||
| 	@GET | ||||
| 	@Path("{id}/{name}") | ||||
| 	@PermitTokenInURI | ||||
| 	@RolesAllowed("USER") | ||||
| 	@Produces(MediaType.APPLICATION_OCTET_STREAM) | ||||
| 	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 { | ||||
| 	@Operation(description = "Get back some data from the data environment (with a beautiful name (permit download with basic name)", tags = "SYSTEM") | ||||
| 	public Response retrieveDataFull(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range, | ||||
| 			@PathParam("id") final UUID id, @PathParam("name") final String name) throws Exception { | ||||
| 		final GenericContext gc = (GenericContext) sc.getUserPrincipal(); | ||||
| 		// logger.info("==================================================="); | ||||
| 		LOGGER.info("== DATA retriveDataFull ? id={} user={}", id, (gc == null ? "null" : gc.userByToken)); | ||||
| 		LOGGER.info("== DATA retrieveDataFull ? id={} user={}", id, (gc == null ? "null" : gc.userByToken)); | ||||
| 		// logger.info("==================================================="); | ||||
| 		final Data value = getSmall(id); | ||||
| 		if (value == null) { | ||||
| 			Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build(); | ||||
| 		} | ||||
| 		return buildStream(ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data", range, value.mimeType); | ||||
| 		return buildStream(getFileData(id), range, value.mimeType); | ||||
| 	} | ||||
|  | ||||
| 	/** Adapted from http://stackoverflow.com/questions/12768812/video-streaming-to-ipad-does-not-work-with-tapestry5/12829541#12829541 | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
							
								
								
									
										33
									
								
								src/org/kar/archidata/api/openApiResource.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/org/kar/archidata/api/openApiResource.java
									
									
									
									
									
										Normal 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"); | ||||
| 	} | ||||
| } | ||||
| @@ -13,6 +13,7 @@ public class FailExceptionCatcher implements ExceptionMapper<FailException> { | ||||
|  | ||||
| 	@Override | ||||
| 	public Response toResponse(final FailException exception) { | ||||
| 		LOGGER.warn("Catch FailException:"); | ||||
| 		final RestErrorResponse ret = build(exception); | ||||
| 		LOGGER.error("Error UUID={}", ret.uuid); | ||||
| 		// Not display backtrace ==> this may be a normal case ... | ||||
|   | ||||
| @@ -13,9 +13,10 @@ public class InputExceptionCatcher implements ExceptionMapper<InputException> { | ||||
|  | ||||
| 	@Override | ||||
| 	public Response toResponse(final InputException exception) { | ||||
| 		LOGGER.warn("Catch InputException:"); | ||||
| 		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(); | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										28
									
								
								src/org/kar/archidata/catcher/JacksonCatcher.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/org/kar/archidata/catcher/JacksonCatcher.java
									
									
									
									
									
										Normal 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 JSON Exception", exception.getMessage()); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -3,19 +3,21 @@ package org.kar.archidata.catcher; | ||||
| import java.time.Instant; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.tools.UuidUtils; | ||||
|  | ||||
| import jakarta.ws.rs.core.Response; | ||||
|  | ||||
| public class RestErrorResponse { | ||||
| 	public UUID uuid = UUID.randomUUID(); | ||||
| 	public UUID uuid = UuidUtils.nextUUID(); | ||||
| 	public String name; // Mandatory for TS generic error | ||||
| 	public String message; // Mandatory for TS generic error | ||||
| 	public String time; | ||||
| 	public String error; | ||||
| 	public String message; | ||||
| 	final public int status; | ||||
| 	final public String statusMessage; | ||||
|  | ||||
| 	public RestErrorResponse(final Response.Status status, final String time, final String error, final String message) { | ||||
| 		this.time = time; | ||||
| 		this.error = error; | ||||
| 		this.name = error; | ||||
| 		this.message = message; | ||||
| 		this.status = status.getStatusCode(); | ||||
| 		this.statusMessage = status.getReasonPhrase(); | ||||
| @@ -23,13 +25,15 @@ public class RestErrorResponse { | ||||
|  | ||||
| 	public RestErrorResponse(final Response.Status status, final String error, final String message) { | ||||
| 		this.time = Instant.now().toString(); | ||||
| 		this.error = error; | ||||
| 		this.name = error; | ||||
| 		this.message = message; | ||||
| 		this.status = status.getStatusCode(); | ||||
| 		this.statusMessage = status.getReasonPhrase(); | ||||
| 	} | ||||
|  | ||||
| 	public RestErrorResponse(final Response.Status status) { | ||||
| 		this.name = "generic"; | ||||
| 		this.message = ""; | ||||
| 		this.time = Instant.now().toString(); | ||||
| 		this.status = status.getStatusCode(); | ||||
| 		this.statusMessage = status.getReasonPhrase(); | ||||
|   | ||||
| @@ -13,6 +13,7 @@ public class SystemExceptionCatcher implements ExceptionMapper<SystemException> | ||||
|  | ||||
| 	@Override | ||||
| 	public Response toResponse(final SystemException exception) { | ||||
| 		LOGGER.warn("Catch SystemException:"); | ||||
| 		final RestErrorResponse ret = build(exception); | ||||
| 		LOGGER.error("Error UUID={}", ret.uuid); | ||||
| 		exception.printStackTrace(); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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) | ||||
| @@ -53,7 +61,58 @@ public interface DataAccessAddOn { | ||||
| 	 * @param createDrop | ||||
| 	 * @param fieldId | ||||
| 	 * @throws Exception */ | ||||
| 	void createTables(String tableName, Field field, StringBuilder mainTableBuilder, List<String> preActionList, List<String> postActionList, boolean createIfNotExist, boolean createDrop, int fieldId) | ||||
| 			throws Exception; | ||||
| 	void createTables(// | ||||
| 			String tableName, // | ||||
| 			final Field primaryField, // | ||||
| 			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 { | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										399
									
								
								src/org/kar/archidata/dataAccess/DataExport.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										399
									
								
								src/org/kar/archidata/dataAccess/DataExport.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,399 @@ | ||||
| package org.kar.archidata.dataAccess; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.sql.PreparedStatement; | ||||
| import java.sql.ResultSet; | ||||
| import java.sql.ResultSetMetaData; | ||||
| import java.sql.SQLException; | ||||
| import java.sql.Statement; | ||||
| import java.sql.Timestamp; | ||||
| import java.time.Instant; | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalTime; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.dataAccess.exportTools.TableQuery; | ||||
| import org.kar.archidata.dataAccess.exportTools.TableQueryTypes; | ||||
| import org.kar.archidata.dataAccess.options.Condition; | ||||
| import org.kar.archidata.dataAccess.options.DBInterfaceOption; | ||||
| import org.kar.archidata.db.DBEntry; | ||||
| import org.kar.archidata.exception.DataAccessException; | ||||
| import org.kar.archidata.tools.DateTools; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||||
|  | ||||
| import jakarta.ws.rs.core.MediaType; | ||||
| import jakarta.ws.rs.core.Response; | ||||
|  | ||||
| public class DataExport { | ||||
| 	static final Logger LOGGER = LoggerFactory.getLogger(DataExport.class); | ||||
| 	public static final String CSV_TYPE = "text/csv"; | ||||
|  | ||||
| 	@SuppressWarnings("unchecked") | ||||
| 	protected static RetreiveFromDB createSetValueFromDbCallbackTable(final int count, final Class<?> type, final int id) throws Exception { | ||||
| 		if (type == UUID.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final UUID tmp = rs.getObject(count, UUID.class); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == Long.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final Long tmp = rs.getLong(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == long.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final Long tmp = rs.getLong(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == Integer.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final Integer tmp = rs.getInt(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == int.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final Integer tmp = rs.getInt(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == Float.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final Float tmp = rs.getFloat(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == float.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final Float tmp = rs.getFloat(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == Double.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final Double tmp = rs.getDouble(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == double.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final Double tmp = rs.getDouble(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == Boolean.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final Boolean tmp = rs.getBoolean(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == boolean.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final Boolean tmp = rs.getBoolean(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == Timestamp.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final Timestamp tmp = rs.getTimestamp(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == Date.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				try { | ||||
| 					final Timestamp tmp = rs.getTimestamp(count); | ||||
| 					if (!rs.wasNull()) { | ||||
| 						final List<Object> data = (List<Object>) (obj); | ||||
| 						data.set(id, Date.from(tmp.toInstant())); | ||||
| 					} | ||||
| 				} catch (final SQLException ex) { | ||||
| 					final String tmp = rs.getString(count); | ||||
| 					LOGGER.error("Fail to parse the SQL time !!! {}", tmp); | ||||
| 					if (!rs.wasNull()) { | ||||
| 						final Date date = DateTools.parseDate(tmp); | ||||
| 						LOGGER.error("Fail to parse the SQL time !!! {}", date); | ||||
| 						final List<Object> data = (List<Object>) (obj); | ||||
| 						data.set(id, date); | ||||
| 					} | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == Instant.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final String tmp = rs.getString(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final Instant date = Instant.parse(tmp); | ||||
| 					LOGGER.error("Fail to parse the SQL time !!! {}", date); | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, date); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == LocalDate.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final java.sql.Date tmp = rs.getDate(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp.toLocalDate()); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == LocalTime.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final java.sql.Time tmp = rs.getTime(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp.toLocalTime()); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type == String.class) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final String tmp = rs.getString(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					final List<Object> data = (List<Object>) (obj); | ||||
| 					data.set(id, tmp); | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		if (type.isEnum()) { | ||||
| 			return (final ResultSet rs, final Object obj) -> { | ||||
| 				final String tmp = rs.getString(count); | ||||
| 				if (!rs.wasNull()) { | ||||
| 					boolean find = false; | ||||
| 					final Object[] arr = type.getEnumConstants(); | ||||
| 					for (final Object elem : arr) { | ||||
| 						if (elem.toString().equals(tmp)) { | ||||
| 							final List<Object> data = (List<Object>) (obj); | ||||
| 							data.set(id, elem); | ||||
| 							find = true; | ||||
| 							break; | ||||
| 						} | ||||
| 					} | ||||
| 					if (!find) { | ||||
| 						throw new DataAccessException("Enum value does not exist in the Model: '" + tmp + "'"); | ||||
| 					} | ||||
| 				} | ||||
| 			}; | ||||
| 		} | ||||
| 		throw new DataAccessException("Unknown Field Type"); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	private static int getQueryPropertyId(final List<TableQueryTypes> properties, final String name) throws DataAccessException { | ||||
| 		for (int iii = 0; iii < properties.size(); iii++) { | ||||
| 			if (properties.get(iii).name.equals(name)) { | ||||
| 				return iii; | ||||
| 			} | ||||
| 		} | ||||
| 		throw new DataAccessException("Querry with unknown field: '" + name + "'"); | ||||
| 	} | ||||
|  | ||||
| 	public static TableQuery queryTable(final List<TableQueryTypes> headers, final String query, final List<Object> parameters, final QueryOption... option) throws Exception { | ||||
| 		final QueryOptions options = new QueryOptions(option); | ||||
| 		return queryTable(headers, query, parameters, options); | ||||
| 	} | ||||
|  | ||||
| 	public static TableQuery queryTable(final List<TableQueryTypes> headers, final String queryBase, final List<Object> parameters, final QueryOptions options) throws Exception { | ||||
| 		final List<LazyGetter> lazyCall = new ArrayList<>(); | ||||
| 		// TODO ... final String deletedFieldName = AnnotationTools.getDeletedFieldName(clazz); | ||||
| 		final DBEntry entry = DBInterfaceOption.getAutoEntry(options); | ||||
|  | ||||
| 		Condition condition = options.get(Condition.class); | ||||
| 		if (condition == null) { | ||||
| 			condition = new Condition(); | ||||
| 		} | ||||
| 		final StringBuilder query = new StringBuilder(queryBase); | ||||
| 		final TableQuery out = new TableQuery(headers); | ||||
| 		// real add in the BDD: | ||||
| 		try { | ||||
| 			final CountInOut count = new CountInOut(); | ||||
| 			condition.whereAppendQuery(query, null, options, null); | ||||
|  | ||||
| 			final GroupBy groups = options.get(GroupBy.class); | ||||
| 			if (groups != null) { | ||||
| 				groups.generateQuerry(query, null); | ||||
| 			} | ||||
| 			final OrderBy orders = options.get(OrderBy.class); | ||||
| 			if (orders != null) { | ||||
| 				orders.generateQuerry(query, null); | ||||
| 			} | ||||
| 			final Limit limit = options.get(Limit.class); | ||||
| 			if (limit != null) { | ||||
| 				limit.generateQuerry(query, null); | ||||
| 			} | ||||
| 			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 (parameters != null) { | ||||
| 				for (final Object elem : parameters) { | ||||
| 					DataAccess.addElement(ps, elem, iii); | ||||
| 				} | ||||
| 				iii.inc(); | ||||
| 			} | ||||
| 			condition.injectQuerry(ps, iii); | ||||
| 			if (limit != null) { | ||||
| 				limit.injectQuerry(ps, iii); | ||||
| 			} | ||||
| 			// execute the request | ||||
| 			final ResultSet rs = ps.executeQuery(); | ||||
| 			final ResultSetMetaData rsmd = rs.getMetaData(); | ||||
| 			final List<RetreiveFromDB> actionToRetreive = new ArrayList<>(); | ||||
| 			LOGGER.info("Field:"); | ||||
| 			for (int jjj = 0; jjj < rsmd.getColumnCount(); jjj++) { | ||||
| 				final String label = rsmd.getColumnLabel(jjj + 1); | ||||
| 				final String typeName = rsmd.getColumnTypeName(jjj + 1); | ||||
| 				final int typeId = rsmd.getColumnType(jjj + 1); | ||||
| 				final int id = getQueryPropertyId(headers, label); | ||||
| 				LOGGER.info("    - {}:{} type=[{}] {}  REQUEST={}", jjj, label, typeId, typeName, headers.get(id).type.getCanonicalName()); | ||||
| 				// create the callback... | ||||
| 				final RetreiveFromDB element = createSetValueFromDbCallbackTable(jjj + 1, headers.get(id).type, id); | ||||
| 				actionToRetreive.add(element); | ||||
| 			} | ||||
| 			while (rs.next()) { | ||||
| 				count.value = 1; | ||||
| 				final List<Object> data = Arrays.asList(new Object[headers.size()]); | ||||
| 				for (final RetreiveFromDB action : actionToRetreive) { | ||||
| 					action.doRequest(rs, data); | ||||
| 				} | ||||
| 				out.values.add(data); | ||||
| 			} | ||||
| 			LOGGER.info("Async calls: {}", lazyCall.size()); | ||||
| 			for (final LazyGetter elem : lazyCall) { | ||||
| 				elem.doRequest(); | ||||
| 			} | ||||
| 		} catch (final SQLException ex) { | ||||
| 			ex.printStackTrace(); | ||||
| 			throw ex; | ||||
| 		} catch (final Exception ex) { | ||||
| 			ex.printStackTrace(); | ||||
| 		} finally { | ||||
| 			entry.close(); | ||||
| 		} | ||||
| 		return out; | ||||
| 	} | ||||
|  | ||||
| 	public static String CSVReplaceSeparator(final String data, final String separator) { | ||||
| 		if (data == null) { | ||||
| 			return ""; | ||||
| 		} | ||||
| 		final String separatorLine = "\n"; | ||||
| 		final String separatorLineReplace = "\\n"; | ||||
| 		return data.replaceAll(separator, "__SEP__").replaceAll(separatorLine, separatorLineReplace); | ||||
| 	} | ||||
|  | ||||
| 	public static String tableToCSV(final TableQuery data) { | ||||
| 		final String separator = ";"; | ||||
| 		final StringBuilder out = new StringBuilder(); | ||||
| 		// generate header: | ||||
| 		boolean first = true; | ||||
| 		for (final TableQueryTypes elem : data.headers) { | ||||
| 			if (!first) { | ||||
| 				out.append(separator); | ||||
| 			} else { | ||||
| 				first = false; | ||||
| 			} | ||||
| 			out.append(CSVReplaceSeparator(elem.title, separator)); | ||||
| 		} | ||||
| 		out.append("\n"); | ||||
| 		// generate body: | ||||
| 		first = true; | ||||
| 		for (final List<Object> line : data.values) { | ||||
| 			for (final Object elem : line) { | ||||
| 				if (!first) { | ||||
| 					out.append(separator); | ||||
| 				} else { | ||||
| 					first = false; | ||||
| 				} | ||||
| 				if (elem != null) { | ||||
| 					out.append(CSVReplaceSeparator(elem.toString(), separator)); | ||||
| 				} | ||||
| 			} | ||||
| 			out.append("\n"); | ||||
| 		} | ||||
| 		return out.toString(); | ||||
| 	} | ||||
|  | ||||
| 	public static Response convertInResponse(final TableQuery dataOut, final String accept) throws DataAccessException, IOException { | ||||
| 		if (CSV_TYPE.equals(accept)) { | ||||
| 			// CSV serialization | ||||
| 			String out = null; | ||||
| 			try { | ||||
| 				out = DataExport.tableToCSV(dataOut); | ||||
| 			} catch (final Exception e) { | ||||
| 				LOGGER.error("Fail to generate CSV...."); | ||||
| 				e.printStackTrace(); | ||||
| 				throw new DataAccessException("Fail in CSV convertion data"); | ||||
| 			} | ||||
| 			return Response.ok(out).header("Content-Type", CSV_TYPE).build(); | ||||
| 		} | ||||
| 		if (MediaType.APPLICATION_JSON.equals(accept)) { | ||||
| 			LOGGER.info("Start mapping josn"); | ||||
| 			final ObjectMapper objectMapper = new ObjectMapper(); | ||||
| 			LOGGER.info("Start find modules josn"); | ||||
| 			objectMapper.findAndRegisterModules(); | ||||
| 			LOGGER.info("Start map object"); | ||||
| 			String out; | ||||
| 			try { | ||||
| 				out = objectMapper.writeValueAsString(dataOut); | ||||
| 			} catch (final JsonProcessingException e) { | ||||
| 				LOGGER.error("Fail to generate JSON...."); | ||||
| 				e.printStackTrace(); | ||||
| 				throw new DataAccessException("Fail in JSON convertion data"); | ||||
| 			} | ||||
| 			LOGGER.info("generate response"); | ||||
| 			return Response.ok(out).header("Content-Type", MediaType.APPLICATION_JSON).build(); | ||||
| 		} | ||||
| 		throw new IOException("This type is not managed: '" + accept + "'"); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -2,11 +2,13 @@ package org.kar.archidata.dataAccess; | ||||
|  | ||||
| import java.lang.reflect.Field; | ||||
| import java.sql.Timestamp; | ||||
| import java.time.Instant; | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalTime; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.annotation.AnnotationTools; | ||||
| import org.kar.archidata.annotation.CreationTimestamp; | ||||
| @@ -27,6 +29,9 @@ public class DataFactory { | ||||
|  | ||||
| 	public static String convertTypeInSQL(final Class<?> type, final String fieldName) throws Exception { | ||||
| 		if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | ||||
| 			if (type == UUID.class) { | ||||
| 				return "binary(16)"; | ||||
| 			} | ||||
| 			if (type == Long.class || type == long.class) { | ||||
| 				return "bigint"; | ||||
| 			} | ||||
| @@ -42,6 +47,9 @@ public class DataFactory { | ||||
| 			if (type == Double.class || type == double.class) { | ||||
| 				return "double"; | ||||
| 			} | ||||
| 			if (type == Instant.class) { | ||||
| 				return "varchar(33)"; | ||||
| 			} | ||||
| 			if (type == Date.class || type == Timestamp.class) { | ||||
| 				return "timestamp(3)"; | ||||
| 			} | ||||
| @@ -75,6 +83,9 @@ public class DataFactory { | ||||
| 				return out.toString(); | ||||
| 			} | ||||
| 		} else { | ||||
| 			if (type == UUID.class) { | ||||
| 				return "BINARY(16)"; | ||||
| 			} | ||||
| 			if (type == Long.class || type == long.class) { | ||||
| 				return "INTEGER"; | ||||
| 			} | ||||
| @@ -90,6 +101,9 @@ public class DataFactory { | ||||
| 			if (type == Double.class || type == double.class) { | ||||
| 				return "REAL"; | ||||
| 			} | ||||
| 			if (type == Instant.class) { | ||||
| 				return "text"; | ||||
| 			} | ||||
| 			if (type == Date.class || type == Timestamp.class) { | ||||
| 				return "DATETIME"; | ||||
| 			} | ||||
| @@ -131,8 +145,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 +166,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(")"); | ||||
| @@ -172,16 +186,24 @@ public class DataFactory { | ||||
| 			} | ||||
| 			if (defaultValue == null) { | ||||
| 				if (updateTime || createTime) { | ||||
| 					if ("varchar(33)".equals(typeValue)) { | ||||
| 						mainTableBuilder.append("DEFAULT DATE_FORMAT(now(6), '%Y-%m-%dT%H:%m:%s.%fZ')"); | ||||
| 					} else { | ||||
| 						mainTableBuilder.append("DEFAULT CURRENT_TIMESTAMP"); | ||||
| 						if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | ||||
| 							mainTableBuilder.append("(3)"); | ||||
| 						} | ||||
| 					} | ||||
| 					mainTableBuilder.append(" "); | ||||
| 				} | ||||
| 				if (updateTime) { | ||||
| 					if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | ||||
| 						if ("varchar(33)".equals(typeValue)) { | ||||
| 							mainTableBuilder.append("ON UPDATE DATE_FORMAT(now(6), '%Y-%m-%dT%H:%m:%s.%fZ')"); | ||||
| 						} else { | ||||
| 							mainTableBuilder.append("ON UPDATE CURRENT_TIMESTAMP"); | ||||
| 							mainTableBuilder.append("(3)"); | ||||
| 						} | ||||
| 					} else { | ||||
| 						// TODO: add trigger: | ||||
| 						/* CREATE TRIGGER your_table_trig AFTER UPDATE ON your_table BEGIN update your_table SET updated_on = datetime('now') WHERE user_id = NEW.user_id; END; */ | ||||
| @@ -195,7 +217,11 @@ public class DataFactory { | ||||
| 						triggerBuilder.append(" SET "); | ||||
| 						triggerBuilder.append(name); | ||||
| 						// triggerBuilder.append(" = datetime('now') WHERE id = NEW.id; \n"); | ||||
| 						if ("varchar(33)".equals(typeValue)) { | ||||
| 							triggerBuilder.append(" = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = NEW.id; \n"); | ||||
| 						} else { | ||||
| 							triggerBuilder.append(" = strftime('%Y-%m-%d %H:%M:%f', 'now') WHERE id = NEW.id; \n"); | ||||
| 						} | ||||
| 						triggerBuilder.append("END;"); | ||||
|  | ||||
| 						postOtherTables.add(triggerBuilder.toString()); | ||||
| @@ -241,8 +267,11 @@ public class DataFactory { | ||||
| 			mainTableBuilder.append("PRIMARY KEY "); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if (strategy == GenerationType.IDENTITY) { | ||||
| 			if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | ||||
| 			if ("binary(16)".equals(typeValue)) { | ||||
|  | ||||
| 			} else if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | ||||
| 				mainTableBuilder.append("AUTO_INCREMENT "); | ||||
| 			} else { | ||||
| 				mainTableBuilder.append("AUTOINCREMENT "); | ||||
| @@ -310,6 +339,7 @@ public class DataFactory { | ||||
| 		LOGGER.debug("===> TABLE `{}`", tableName); | ||||
| 		final List<String> primaryKeys = new ArrayList<>(); | ||||
|  | ||||
| 		final Field primaryField = AnnotationTools.getPrimaryKeyField(clazz); | ||||
| 		for (final Field elem : clazz.getFields()) { | ||||
| 			// DEtect the primary key (support only one primary key right now... | ||||
| 			if (AnnotationTools.isPrimaryKey(elem)) { | ||||
| @@ -344,7 +374,7 @@ public class DataFactory { | ||||
| 					final DataAccessAddOn addOn = DataAccess.findAddOnforField(elem); | ||||
| 					LOGGER.trace("Create type for: {} ==> {} (ADD-ON)", AnnotationTools.getFieldName(elem), elem.getType()); | ||||
| 					if (addOn != null) { | ||||
| 						addOn.createTables(tableName, elem, tmpOut, preActionList, postActionList, createIfNotExist, createDrop, fieldId); | ||||
| 						addOn.createTables(tableName, primaryField, elem, tmpOut, preActionList, postActionList, createIfNotExist, createDrop, fieldId); | ||||
| 					} else { | ||||
| 						throw new DataAccessException( | ||||
| 								"Element matked as add-on but add-on does not loaded: table:" + tableName + " field name=" + AnnotationTools.getFieldName(elem) + " type=" + elem.getType()); | ||||
|   | ||||
							
								
								
									
										652
									
								
								src/org/kar/archidata/dataAccess/DataFactoryTsApi.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										652
									
								
								src/org/kar/archidata/dataAccess/DataFactoryTsApi.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,652 @@ | ||||
| package org.kar.archidata.dataAccess; | ||||
|  | ||||
| import java.io.BufferedReader; | ||||
| import java.io.File; | ||||
| import java.io.FileWriter; | ||||
| import java.io.InputStream; | ||||
| import java.io.InputStreamReader; | ||||
| import java.lang.annotation.Annotation; | ||||
| import java.lang.reflect.Method; | ||||
| import java.lang.reflect.Parameter; | ||||
| import java.lang.reflect.ParameterizedType; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.HashMap; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Map.Entry; | ||||
| import java.util.Set; | ||||
|  | ||||
| import org.glassfish.jersey.media.multipart.FormDataParam; | ||||
| import org.kar.archidata.annotation.AsyncType; | ||||
| import org.kar.archidata.annotation.TypeScriptProgress; | ||||
| import org.kar.archidata.catcher.RestErrorResponse; | ||||
| import org.kar.archidata.dataAccess.DataFactoryZod.ClassElement; | ||||
| import org.kar.archidata.dataAccess.DataFactoryZod.GeneratedTypes; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.Operation; | ||||
| import jakarta.ws.rs.Consumes; | ||||
| import jakarta.ws.rs.DELETE; | ||||
| import jakarta.ws.rs.GET; | ||||
| import jakarta.ws.rs.PATCH; | ||||
| import jakarta.ws.rs.POST; | ||||
| import jakarta.ws.rs.PUT; | ||||
| import jakarta.ws.rs.Path; | ||||
| import jakarta.ws.rs.PathParam; | ||||
| import jakarta.ws.rs.Produces; | ||||
| import jakarta.ws.rs.QueryParam; | ||||
| import jakarta.ws.rs.core.Context; | ||||
| import jakarta.ws.rs.core.MediaType; | ||||
| import jakarta.ws.rs.core.Response; | ||||
|  | ||||
| public class DataFactoryTsApi { | ||||
| 	static final Logger LOGGER = LoggerFactory.getLogger(DataFactoryTsApi.class); | ||||
|  | ||||
| 	record APIModel(String data, String className) { | ||||
| 	} | ||||
|  | ||||
| 	/** Request the generation of the TypeScript file for the "Zod" export model | ||||
| 	 * @param classs List of class used in the model | ||||
| 	 * @throws Exception */ | ||||
| 	public static List<String> createApi(final List<Class<?>> classs, final GeneratedTypes previous, final String pathPackage) throws Exception { | ||||
| 		final List<String> apis = new ArrayList<>(); | ||||
| 		final String globalheader = """ | ||||
| 				/** | ||||
| 				 * API of the server (auto-generated code) | ||||
| 				 */ | ||||
| 				import { | ||||
| 				  HTTPMimeType, | ||||
| 				  HTTPRequestModel, | ||||
| 				  ModelResponseHttp, | ||||
| 				  RESTCallbacks, | ||||
| 				  RESTConfig, | ||||
| 				  RESTRequestJson, | ||||
| 				  RESTRequestJsonArray, | ||||
| 				  RESTRequestVoid | ||||
| 				} from "./rest-tools" | ||||
| 				import {"""; | ||||
|  | ||||
| 		for (final Class<?> clazz : classs) { | ||||
| 			final Set<Class<?>> includeModel = new HashSet<>(); | ||||
| 			final Set<Class<?>> includeCheckerModel = new HashSet<>(); | ||||
| 			final APIModel api = createSingleApi(clazz, includeModel, includeCheckerModel, previous); | ||||
| 			final StringBuilder generatedData = new StringBuilder(); | ||||
| 			generatedData.append(globalheader); | ||||
| 			for (final Class<?> elem : includeModel) { | ||||
| 				if (elem == null) { | ||||
| 					continue; | ||||
| 				} | ||||
| 				final ClassElement classElement = DataFactoryZod.createTable(elem, previous); | ||||
| 				if (classElement.nativeType) { | ||||
| 					continue; | ||||
| 				} | ||||
| 				generatedData.append("\n  "); | ||||
| 				generatedData.append(classElement.tsTypeName); | ||||
| 				generatedData.append(","); | ||||
| 			} | ||||
| 			for (final Class<?> elem : includeCheckerModel) { | ||||
| 				if (elem == null) { | ||||
| 					continue; | ||||
| 				} | ||||
| 				final ClassElement classElement = DataFactoryZod.createTable(elem, previous); | ||||
| 				if (classElement.nativeType) { | ||||
| 					continue; | ||||
| 				} | ||||
| 				generatedData.append("\n  "); | ||||
| 				generatedData.append(classElement.tsCheckType); | ||||
| 				generatedData.append(","); | ||||
| 			} | ||||
| 			generatedData.append("\n} from \"./model\"\n"); | ||||
| 			generatedData.append(api.data()); | ||||
|  | ||||
| 			String fileName = api.className(); | ||||
| 			fileName = fileName.replaceAll("([A-Z])", "-$1").toLowerCase(); | ||||
| 			fileName = fileName.replaceAll("^\\-*", ""); | ||||
| 			apis.add(fileName); | ||||
| 			final FileWriter myWriter = new FileWriter(pathPackage + File.separator + fileName + ".ts"); | ||||
| 			myWriter.write(generatedData.toString()); | ||||
| 			myWriter.close(); | ||||
| 		} | ||||
| 		return apis; | ||||
| 	} | ||||
|  | ||||
| 	public static String apiAnnotationGetPath(final Class<?> element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(Path.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		return ((Path) annotation[0]).value(); | ||||
| 	} | ||||
|  | ||||
| 	public static List<String> apiAnnotationProduces(final Class<?> element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(Produces.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		return Arrays.asList(((Produces) annotation[0]).value()); | ||||
| 	} | ||||
|  | ||||
| 	public static List<String> apiAnnotationProduces(final Method element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(Produces.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		return Arrays.asList(((Produces) annotation[0]).value()); | ||||
| 	} | ||||
|  | ||||
| 	public static boolean apiAnnotationTypeScriptProgress(final Method element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(TypeScriptProgress.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return false; | ||||
| 		} | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	public static List<String> apiAnnotationProduces(final Class<?> clazz, final Method method) throws Exception { | ||||
| 		final List<String> data = apiAnnotationProduces(method); | ||||
| 		if (data != null) { | ||||
| 			return data; | ||||
| 		} | ||||
| 		return apiAnnotationProduces(clazz); | ||||
| 	} | ||||
|  | ||||
| 	public static String apiAnnotationGetOperationDescription(final Method element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(Operation.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		return ((Operation) annotation[0]).description(); | ||||
| 	} | ||||
|  | ||||
| 	public static String apiAnnotationGetPath(final Method element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(Path.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		return ((Path) annotation[0]).value(); | ||||
| 	} | ||||
|  | ||||
| 	public static String apiAnnotationGetTypeRequest(final Method element) throws Exception { | ||||
| 		if (element.getDeclaredAnnotationsByType(GET.class).length == 1) { | ||||
| 			return "GET"; | ||||
| 		} | ||||
| 		if (element.getDeclaredAnnotationsByType(POST.class).length == 1) { | ||||
| 			return "POST"; | ||||
| 		} | ||||
| 		if (element.getDeclaredAnnotationsByType(PUT.class).length == 1) { | ||||
| 			return "PUT"; | ||||
| 		} | ||||
| 		if (element.getDeclaredAnnotationsByType(PATCH.class).length == 1) { | ||||
| 			return "PATCH"; | ||||
| 		} | ||||
| 		if (element.getDeclaredAnnotationsByType(DELETE.class).length == 1) { | ||||
| 			return "DELETE"; | ||||
| 		} | ||||
| 		return null; | ||||
| 	} | ||||
|  | ||||
| 	public static String apiAnnotationGetPathParam(final Parameter element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(PathParam.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		return ((PathParam) annotation[0]).value(); | ||||
| 	} | ||||
|  | ||||
| 	public static String apiAnnotationGetQueryParam(final Parameter element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(QueryParam.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		return ((QueryParam) annotation[0]).value(); | ||||
| 	} | ||||
|  | ||||
| 	public static String apiAnnotationGetFormDataParam(final Parameter element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(FormDataParam.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		return ((FormDataParam) annotation[0]).value(); | ||||
| 	} | ||||
|  | ||||
| 	public static Class<?>[] apiAnnotationGetAsyncType(final Parameter element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(AsyncType.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		return ((AsyncType) annotation[0]).value(); | ||||
| 	} | ||||
|  | ||||
| 	public static Class<?>[] apiAnnotationGetAsyncType(final Method element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(AsyncType.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		return ((AsyncType) annotation[0]).value(); | ||||
| 	} | ||||
|  | ||||
| 	public static List<String> apiAnnotationGetConsumes(final Method element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(Consumes.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		return Arrays.asList(((Consumes) annotation[0]).value()); | ||||
| 	} | ||||
|  | ||||
| 	public static List<String> apiAnnotationGetConsumes(final Class<?> element) throws Exception { | ||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(Consumes.class); | ||||
| 		if (annotation.length == 0) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		return Arrays.asList(((Consumes) annotation[0]).value()); | ||||
| 	} | ||||
|  | ||||
| 	public static List<String> apiAnnotationGetConsumes(final Class<?> clazz, final Method method) throws Exception { | ||||
| 		final List<String> data = apiAnnotationGetConsumes(method); | ||||
| 		if (data != null) { | ||||
| 			return data; | ||||
| 		} | ||||
| 		return apiAnnotationGetConsumes(clazz); | ||||
| 	} | ||||
|  | ||||
| 	public static boolean apiAnnotationIsContext(final Parameter element) throws Exception { | ||||
| 		return element.getDeclaredAnnotationsByType(Context.class).length != 0; | ||||
| 	} | ||||
|  | ||||
| 	public static String convertInTypeScriptType(final List<ClassElement> tmp, final boolean isList) { | ||||
| 		String out = ""; | ||||
| 		for (final ClassElement elem : tmp) { | ||||
| 			if (out.length() != 0) { | ||||
| 				out += " | "; | ||||
| 			} | ||||
| 			out += elem.tsTypeName; | ||||
| 			if (isList) { | ||||
| 				out += "[]"; | ||||
| 			} | ||||
| 		} | ||||
| 		return out; | ||||
| 	} | ||||
| 	public static String convertInTypeScriptCheckType(final List<ClassElement> tmp) { | ||||
| 		String out = ""; | ||||
| 		for (final ClassElement elem : tmp) { | ||||
| 			if (out.length() != 0) { | ||||
| 				out += " | "; | ||||
| 			} | ||||
| 			out += elem.tsCheckType; | ||||
| 		} | ||||
| 		return out; | ||||
| 	} | ||||
|  | ||||
| 	public static APIModel createSingleApi(final Class<?> clazz, final Set<Class<?>> includeModel, final Set<Class<?>> includeCheckerModel, final GeneratedTypes previous) throws Exception { | ||||
| 		final StringBuilder builder = new StringBuilder(); | ||||
| 		// the basic path has no specific elements... | ||||
| 		final String basicPath = apiAnnotationGetPath(clazz); | ||||
| 		final String classSimpleName = clazz.getSimpleName(); | ||||
|  | ||||
| 		builder.append("export namespace "); | ||||
| 		builder.append(classSimpleName); | ||||
| 		builder.append(" {\n"); | ||||
| 		LOGGER.info("Parse Class for path: {} => {}", classSimpleName, basicPath); | ||||
| 		for (final Method method : clazz.getDeclaredMethods()) { | ||||
| 			final String methodName = method.getName(); | ||||
| 			final String methodPath = apiAnnotationGetPath(method); | ||||
| 			final String methodType = apiAnnotationGetTypeRequest(method); | ||||
| 			if (methodType == null) { | ||||
| 				LOGGER.error("    [{}] {} => {}/{} ==> No methode type @PATH, @GET ...", methodType, methodName, basicPath, methodPath); | ||||
| 				continue; | ||||
| 			} | ||||
| 			final String methodDescription = apiAnnotationGetOperationDescription(method); | ||||
| 			final List<String> consumes = apiAnnotationGetConsumes(clazz, method); | ||||
| 			final List<String> produces = apiAnnotationProduces(clazz, method); | ||||
| 			LOGGER.trace("    [{}] {} => {}/{}", methodType, methodName, basicPath, methodPath); | ||||
| 			if (methodDescription != null) { | ||||
| 				LOGGER.trace("         description: {}", methodDescription); | ||||
| 			} | ||||
| 			final boolean needGenerateProgress = apiAnnotationTypeScriptProgress(method); | ||||
| 			Class<?>[] returnTypeModel = apiAnnotationGetAsyncType(method); | ||||
| 			boolean isUnmanagedReturnType = false; | ||||
| 			boolean returnModelIsArray = false; | ||||
| 			List<ClassElement> tmpReturn; | ||||
| 			if (returnTypeModel == null) { | ||||
| 				Class<?> returnTypeModelRaw = method.getReturnType(); | ||||
| 				LOGGER.info("Get type: {}", returnTypeModelRaw); | ||||
| 				if (returnTypeModelRaw == Response.class) { | ||||
| 					LOGGER.info("Get type: {}", returnTypeModelRaw); | ||||
| 				} | ||||
| 				if (returnTypeModelRaw == Response.class) { | ||||
| 					isUnmanagedReturnType = true; | ||||
| 					returnTypeModel = new Class<?>[] { Void.class }; | ||||
| 					tmpReturn = new ArrayList<>(); | ||||
| 				} else if (returnTypeModelRaw == List.class) { | ||||
| 					final ParameterizedType listType = (ParameterizedType) method.getGenericReturnType(); | ||||
| 					returnTypeModelRaw = (Class<?>) listType.getActualTypeArguments()[0]; | ||||
| 					returnModelIsArray = true; | ||||
| 					returnTypeModel = new Class<?>[] { returnTypeModelRaw }; | ||||
| 					tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous); | ||||
| 				} else { | ||||
| 					returnTypeModel = new Class<?>[] { returnTypeModelRaw }; | ||||
| 					tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous); | ||||
| 				} | ||||
| 			} else { | ||||
| 				tmpReturn = DataFactoryZod.createTables(returnTypeModel, previous); | ||||
| 			} | ||||
| 			for (final ClassElement elem : tmpReturn) { | ||||
| 				includeModel.add(elem.model[0]); | ||||
| 				includeCheckerModel.add(elem.model[0]); | ||||
| 			} | ||||
| 			LOGGER.trace("         return: {}", tmpReturn.size()); | ||||
| 			for (final ClassElement elem : tmpReturn) { | ||||
| 				LOGGER.trace("             - {}", elem.tsTypeName); | ||||
| 			} | ||||
| 			final Map<String, String> queryParams = new HashMap<>(); | ||||
| 			final Map<String, String> pathParams = new HashMap<>(); | ||||
| 			final Map<String, String> formDataParams = new HashMap<>(); | ||||
| 			final List<String> emptyElement = new ArrayList<>(); | ||||
| 			// LOGGER.info(" Parameters:"); | ||||
| 			for (final Parameter parameter : method.getParameters()) { | ||||
| 				// Security context are internal parameter (not available from API) | ||||
| 				if (apiAnnotationIsContext(parameter)) { | ||||
| 					continue; | ||||
| 				} | ||||
| 				final Class<?> parameterType = parameter.getType(); | ||||
| 				String parameterTypeString; | ||||
| 				final Class<?>[] asyncType = apiAnnotationGetAsyncType(parameter); | ||||
| 				if (parameterType == List.class) { | ||||
| 					if (asyncType == null) { | ||||
| 						LOGGER.warn("Detect List param ==> not managed type ==> any[] !!!"); | ||||
| 						parameterTypeString = "any[]"; | ||||
| 					} else { | ||||
| 						final List<ClassElement> tmp = DataFactoryZod.createTables(asyncType, previous); | ||||
| 						for (final ClassElement elem : tmp) { | ||||
| 							includeModel.add(elem.model[0]); | ||||
| 						} | ||||
| 						parameterTypeString = convertInTypeScriptType(tmp, true); | ||||
| 					} | ||||
| 				} else if (asyncType == null) { | ||||
| 					final ClassElement tmp = DataFactoryZod.createTable(parameterType, previous); | ||||
| 					includeModel.add(tmp.model[0]); | ||||
| 					parameterTypeString = tmp.tsTypeName; | ||||
| 				} else { | ||||
| 					final List<ClassElement> tmp = DataFactoryZod.createTables(asyncType, previous); | ||||
| 					for (final ClassElement elem : tmp) { | ||||
| 						includeModel.add(elem.model[0]); | ||||
| 					} | ||||
| 					parameterTypeString = convertInTypeScriptType(tmp, true); | ||||
| 				} | ||||
| 				final String pathParam = apiAnnotationGetPathParam(parameter); | ||||
| 				final String queryParam = apiAnnotationGetQueryParam(parameter); | ||||
| 				final String formDataParam = apiAnnotationGetFormDataParam(parameter); | ||||
| 				if (queryParam != null) { | ||||
| 					queryParams.put(queryParam, parameterTypeString); | ||||
| 				} else if (pathParam != null) { | ||||
| 					pathParams.put(pathParam, parameterTypeString); | ||||
| 				} else if (formDataParam != null) { | ||||
| 					formDataParams.put(formDataParam, parameterTypeString); | ||||
| 				} else if (asyncType != null) { | ||||
| 					final List<ClassElement> tmp = DataFactoryZod.createTables(asyncType, previous); | ||||
| 					parameterTypeString = ""; | ||||
| 					for (final ClassElement elem : tmp) { | ||||
| 						includeModel.add(elem.model[0]); | ||||
| 						if (parameterTypeString.length() != 0) { | ||||
| 							parameterTypeString += " | "; | ||||
| 						} | ||||
| 						parameterTypeString += elem.tsTypeName; | ||||
| 					} | ||||
| 					emptyElement.add(parameterTypeString); | ||||
| 				} else if (parameterType == List.class) { | ||||
| 					parameterTypeString = "any[]"; | ||||
| 					final Class<?> plop = parameterType.arrayType(); | ||||
| 					LOGGER.info("ArrayType = {}", plop); | ||||
| 					emptyElement.add(parameterTypeString); | ||||
| 				} else { | ||||
| 					final ClassElement tmp = DataFactoryZod.createTable(parameterType, previous); | ||||
| 					includeModel.add(tmp.model[0]); | ||||
| 					emptyElement.add(tmp.tsTypeName); | ||||
| 				} | ||||
| 			} | ||||
| 			if (!queryParams.isEmpty()) { | ||||
| 				LOGGER.trace("         Query parameter:"); | ||||
| 				for (final Entry<String, String> queryEntry : queryParams.entrySet()) { | ||||
| 					LOGGER.trace("             - {}: {}", queryEntry.getKey(), queryEntry.getValue()); | ||||
| 				} | ||||
| 			} | ||||
| 			if (!pathParams.isEmpty()) { | ||||
| 				LOGGER.trace("         Path parameter:"); | ||||
| 				for (final Entry<String, String> pathEntry : pathParams.entrySet()) { | ||||
| 					LOGGER.trace("             - {}: {}", pathEntry.getKey(), pathEntry.getValue()); | ||||
| 				} | ||||
| 			} | ||||
| 			if (emptyElement.size() > 1) { | ||||
| 				LOGGER.error("         Fail to parse: Too much element in the model for the data ..."); | ||||
| 				continue; | ||||
| 			} else if (emptyElement.size() == 1 && formDataParams.size() != 0) { | ||||
| 				LOGGER.error("         Fail to parse: Incompatible form data & direct data ..."); | ||||
| 				continue; | ||||
| 			} else if (emptyElement.size() == 1) { | ||||
| 				LOGGER.trace("         data type: {}", emptyElement.get(0)); | ||||
| 			} | ||||
| 			// ALL is good can generate the Elements | ||||
|  | ||||
| 			if (methodDescription != null) { | ||||
| 				builder.append("\n\t/**\n\t * "); | ||||
| 				builder.append(methodDescription); | ||||
| 				builder.append("\n\t */"); | ||||
| 			} | ||||
| 			if (isUnmanagedReturnType) { | ||||
| 				builder.append("\n\t// TODO: unmanaged \"Response\" type: please specify @AsyncType or considered as 'void'."); | ||||
| 			} | ||||
| 			builder.append("\n\texport function "); | ||||
| 			builder.append(methodName); | ||||
| 			builder.append("({\n\t\t\trestConfig,"); | ||||
| 			if (!queryParams.isEmpty()) { | ||||
| 				builder.append("\n\t\t\tqueries,"); | ||||
| 			} | ||||
| 			if (!pathParams.isEmpty()) { | ||||
| 				builder.append("\n\t\t\tparams,"); | ||||
| 			} | ||||
| 			if (produces.size() > 1) { | ||||
| 				builder.append("\n\t\t\tproduce,"); | ||||
| 			} | ||||
| 			if (emptyElement.size() == 1 || formDataParams.size() != 0) { | ||||
| 				builder.append("\n\t\t\tdata,"); | ||||
| 			} | ||||
| 			if (needGenerateProgress) { | ||||
| 				builder.append("\n\t\t\tcallback,"); | ||||
| 			} | ||||
| 			builder.append("\n\t\t}: {"); | ||||
| 			builder.append("\n\t\trestConfig: RESTConfig,"); | ||||
| 			if (!queryParams.isEmpty()) { | ||||
| 				builder.append("\n\t\tqueries: {"); | ||||
| 				for (final Entry<String, String> queryEntry : queryParams.entrySet()) { | ||||
| 					builder.append("\n\t\t\t"); | ||||
| 					builder.append(queryEntry.getKey()); | ||||
| 					builder.append("?: "); | ||||
| 					builder.append(queryEntry.getValue()); | ||||
| 					builder.append(","); | ||||
| 				} | ||||
| 				builder.append("\n\t\t},"); | ||||
| 			} | ||||
| 			if (!pathParams.isEmpty()) { | ||||
| 				builder.append("\n\t\tparams: {"); | ||||
| 				for (final Entry<String, String> pathEntry : pathParams.entrySet()) { | ||||
| 					builder.append("\n\t\t\t"); | ||||
| 					builder.append(pathEntry.getKey()); | ||||
| 					builder.append(": "); | ||||
| 					builder.append(pathEntry.getValue()); | ||||
| 					builder.append(","); | ||||
| 				} | ||||
| 				builder.append("\n\t\t},"); | ||||
| 			} | ||||
| 			if (emptyElement.size() == 1) { | ||||
| 				builder.append("\n\t\tdata: "); | ||||
| 				builder.append(emptyElement.get(0)); | ||||
| 				builder.append(","); | ||||
| 			} else if (formDataParams.size() != 0) { | ||||
| 				builder.append("\n\t\tdata: {"); | ||||
| 				for (final Entry<String, String> pathEntry : formDataParams.entrySet()) { | ||||
| 					builder.append("\n\t\t\t"); | ||||
| 					builder.append(pathEntry.getKey()); | ||||
| 					builder.append(": "); | ||||
| 					builder.append(pathEntry.getValue()); | ||||
| 					builder.append(","); | ||||
| 				} | ||||
| 				builder.append("\n\t\t},"); | ||||
| 			} | ||||
| 			if (produces.size() > 1) { | ||||
| 				builder.append("\n\t\tproduce: "); | ||||
| 				String isFist = null; | ||||
| 				for (final String elem : produces) { | ||||
| 					String lastElement = null; | ||||
|  | ||||
| 					if (MediaType.APPLICATION_JSON.equals(elem)) { | ||||
| 						lastElement = "HTTPMimeType.JSON"; | ||||
| 					} | ||||
| 					if (MediaType.MULTIPART_FORM_DATA.equals(elem)) { | ||||
| 						lastElement = "HTTPMimeType.MULTIPART"; | ||||
| 					} | ||||
| 					if (DataExport.CSV_TYPE.equals(elem)) { | ||||
| 						lastElement = "HTTPMimeType.CSV"; | ||||
| 					} | ||||
| 					if (lastElement != null) { | ||||
| 						if (isFist == null) { | ||||
| 							isFist = lastElement; | ||||
| 						} else { | ||||
| 							builder.append(" | "); | ||||
| 						} | ||||
| 						builder.append(lastElement); | ||||
| 					} else { | ||||
| 						LOGGER.error("Unmanaged model type: {}", elem); | ||||
| 					} | ||||
| 				} | ||||
| 				builder.append(","); | ||||
| 			} | ||||
| 			if (needGenerateProgress) { | ||||
| 				builder.append("\n\t\tcallback?: RESTCallbacks,"); | ||||
| 			} | ||||
| 			builder.append("\n\t}): Promise<"); | ||||
| 			if (tmpReturn.size() == 0 // | ||||
| 					|| tmpReturn.get(0).tsTypeName == null // | ||||
| 					|| tmpReturn.get(0).tsTypeName.equals("void")) { | ||||
| 				builder.append("void"); | ||||
| 			} else { | ||||
| 				builder.append(convertInTypeScriptType(tmpReturn, returnModelIsArray)); | ||||
| 			} | ||||
| 			builder.append("> {"); | ||||
| 			if (tmpReturn.size() == 0 // | ||||
| 					|| tmpReturn.get(0).tsTypeName == null // | ||||
| 					|| tmpReturn.get(0).tsTypeName.equals("void")) { | ||||
| 				builder.append("\n\t\treturn RESTRequestVoid({"); | ||||
| 			} else if (returnModelIsArray) { | ||||
| 				builder.append("\n\t\treturn RESTRequestJsonArray({"); | ||||
| 			} else { | ||||
| 				builder.append("\n\t\treturn RESTRequestJson({"); | ||||
| 			} | ||||
| 			builder.append("\n\t\t\trestModel: {"); | ||||
| 			builder.append("\n\t\t\t\tendPoint: \""); | ||||
| 			builder.append(basicPath); | ||||
| 			if (methodPath != null) { | ||||
| 				builder.append("/"); | ||||
| 				builder.append(methodPath); | ||||
| 			} | ||||
| 			builder.append("\","); | ||||
| 			builder.append("\n\t\t\t\trequestType: HTTPRequestModel."); | ||||
| 			builder.append(methodType); | ||||
| 			builder.append(","); | ||||
| 			if (consumes != null) { | ||||
| 				for (final String elem : consumes) { | ||||
| 					if (MediaType.APPLICATION_JSON.equals(elem)) { | ||||
| 						builder.append("\n\t\t\t\tcontentType: HTTPMimeType.JSON,"); | ||||
| 						break; | ||||
| 					} else if (MediaType.MULTIPART_FORM_DATA.equals(elem)) { | ||||
| 						builder.append("\n\t\t\t\tcontentType: HTTPMimeType.MULTIPART,"); | ||||
| 						break; | ||||
| 					} else if (MediaType.TEXT_PLAIN.equals(elem)) { | ||||
| 						builder.append("\n\t\t\t\tcontentType: HTTPMimeType.TEXT_PLAIN,"); | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 			} else if ("DELETE".equals(methodType)) { | ||||
| 				builder.append("\n\t\t\t\tcontentType: HTTPMimeType.TEXT_PLAIN,"); | ||||
| 			} | ||||
| 			if (produces != null) { | ||||
| 				if (produces.size() > 1) { | ||||
| 					builder.append("\n\t\t\t\taccept: produce,"); | ||||
| 				} else { | ||||
| 					for (final String elem : produces) { | ||||
| 						if (MediaType.APPLICATION_JSON.equals(elem)) { | ||||
| 							builder.append("\n\t\t\t\taccept: HTTPMimeType.JSON,"); | ||||
| 							break; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			builder.append("\n\t\t\t},"); | ||||
| 			builder.append("\n\t\t\trestConfig,"); | ||||
| 			if (!pathParams.isEmpty()) { | ||||
| 				builder.append("\n\t\t\tparams,"); | ||||
| 			} | ||||
| 			if (!queryParams.isEmpty()) { | ||||
| 				builder.append("\n\t\t\tqueries,"); | ||||
| 			} | ||||
| 			if (emptyElement.size() == 1) { | ||||
| 				builder.append("\n\t\t\tdata,"); | ||||
| 			} else if (formDataParams.size() != 0) { | ||||
| 				builder.append("\n\t\t\tdata,"); | ||||
| 			} | ||||
| 			if (needGenerateProgress) { | ||||
| 				builder.append("\n\t\t\tcallback,"); | ||||
| 			} | ||||
| 			builder.append("\n\t\t}"); | ||||
| 			if (tmpReturn.size() != 0 && tmpReturn.get(0).tsTypeName != null && !tmpReturn.get(0).tsTypeName.equals("void")) { | ||||
| 				builder.append(", "); | ||||
| 				// TODO: correct this it is really bad ... | ||||
| 				builder.append(convertInTypeScriptCheckType(tmpReturn)); | ||||
| 			} | ||||
| 			builder.append(");"); | ||||
| 			builder.append("\n\t};"); | ||||
| 		} | ||||
| 		builder.append("\n}\n"); | ||||
| 		return new APIModel(builder.toString(), classSimpleName); | ||||
| 	} | ||||
|  | ||||
| 	public static void generatePackage(final List<Class<?>> classApi, final List<Class<?>> classModel, final String pathPackage) throws Exception { | ||||
| 		final GeneratedTypes previous = DataFactoryZod.createBasicType(); | ||||
| 		DataFactoryZod.createTable(RestErrorResponse.class, previous); | ||||
| 		final List<String> listApi = createApi(classApi, previous, pathPackage); | ||||
| 		final String packageApi = DataFactoryZod.createTables(new ArrayList<>(classModel), previous); | ||||
| 		FileWriter myWriter = new FileWriter(pathPackage + File.separator + "model.ts"); | ||||
| 		myWriter.write(packageApi.toString()); | ||||
| 		myWriter.close(); | ||||
|  | ||||
| 		final StringBuilder index = new StringBuilder(""" | ||||
| 				/** | ||||
| 				 * Global import of the package | ||||
| 				 */ | ||||
| 				export * from "./model"; | ||||
| 				"""); | ||||
| 		for (final String api : listApi) { | ||||
| 			index.append("export * from \"./").append(api).append("\";\n"); | ||||
| 		} | ||||
| 		myWriter = new FileWriter(pathPackage + File.separator + "index.ts"); | ||||
| 		myWriter.write(index.toString()); | ||||
| 		myWriter.close(); | ||||
| 		final InputStream ioStream = DataFactoryTsApi.class.getClassLoader().getResourceAsStream("rest-tools.ts"); | ||||
| 		if (ioStream == null) { | ||||
| 			throw new IllegalArgumentException("rest-tools.ts is not found"); | ||||
| 		} | ||||
| 		final BufferedReader buffer = new BufferedReader(new InputStreamReader(ioStream)); | ||||
| 		myWriter = new FileWriter(pathPackage + File.separator + "rest-tools.ts"); | ||||
| 		String line; | ||||
| 		while ((line = buffer.readLine()) != null) { | ||||
| 			myWriter.write(line); | ||||
| 			myWriter.write("\n"); | ||||
| 		} | ||||
| 		ioStream.close(); | ||||
| 		myWriter.close(); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										461
									
								
								src/org/kar/archidata/dataAccess/DataFactoryZod.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										461
									
								
								src/org/kar/archidata/dataAccess/DataFactoryZod.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,461 @@ | ||||
| package org.kar.archidata.dataAccess; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileWriter; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.lang.reflect.Field; | ||||
| import java.lang.reflect.ParameterizedType; | ||||
| import java.sql.Timestamp; | ||||
| import java.time.Instant; | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalTime; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.glassfish.jersey.media.multipart.FormDataContentDisposition; | ||||
| import org.kar.archidata.annotation.AnnotationTools; | ||||
| import org.kar.archidata.exception.DataAccessException; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import jakarta.ws.rs.core.Response; | ||||
|  | ||||
| public class DataFactoryZod { | ||||
| 	static final Logger LOGGER = LoggerFactory.getLogger(DataFactoryZod.class); | ||||
|  | ||||
| 	static public class ClassElement { | ||||
| 		public Class<?>[] model; | ||||
| 		public String zodName; | ||||
| 		public String tsTypeName; | ||||
| 		public String tsCheckType; | ||||
| 		public String declaration; | ||||
| 		public String comment = null; | ||||
| 		public boolean isEnum = false; | ||||
| 		public boolean nativeType; | ||||
|  | ||||
| 		public ClassElement(final Class<?> model[], final String zodName, final String tsTypeName, final String tsCheckType, final String declaration, final boolean nativeType) { | ||||
| 			this.model = model; | ||||
| 			this.zodName = zodName; | ||||
| 			this.tsTypeName = tsTypeName; | ||||
| 			this.tsCheckType = tsCheckType; | ||||
| 			this.declaration = declaration; | ||||
| 			this.nativeType = nativeType; | ||||
| 		} | ||||
|  | ||||
| 		public ClassElement(final Class<?> model) { | ||||
| 			this(new Class<?>[] { model }); | ||||
| 		} | ||||
|  | ||||
| 		public ClassElement(final Class<?> model[]) { | ||||
| 			this.model = model; | ||||
| 			this.zodName = "Zod" + model[0].getSimpleName(); | ||||
| 			this.tsTypeName = model[0].getSimpleName(); | ||||
| 			this.tsCheckType = "is" + model[0].getSimpleName(); | ||||
| 			this.declaration = null; | ||||
| 			this.nativeType = false; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public static class GeneratedTypes { | ||||
| 		final List<ClassElement> previousGeneration = new ArrayList<>(); | ||||
| 		final List<Class<?>> order = new ArrayList<>(); | ||||
|  | ||||
| 		public ClassElement find(final Class<?> clazz) { | ||||
| 			for (final ClassElement elem : this.previousGeneration) { | ||||
| 				for (final Class<?> elemClass : elem.model) { | ||||
| 					if (elemClass == clazz) { | ||||
| 						return elem; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			return null; | ||||
| 		} | ||||
|  | ||||
| 		public void add(final ClassElement elem) { | ||||
| 			this.previousGeneration.add(elem); | ||||
| 		} | ||||
|  | ||||
| 		public void add(final ClassElement elem, final boolean addOrder) { | ||||
| 			this.previousGeneration.add(elem); | ||||
| 			if (addOrder) { | ||||
| 				this.order.add(elem.model[0]); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		public void addOrder(final ClassElement elem) { | ||||
| 			this.order.add(elem.model[0]); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public static ClassElement convertTypeZodEnum(final Class<?> clazz, final GeneratedTypes previous) throws Exception { | ||||
| 		final ClassElement element = new ClassElement(clazz); | ||||
| 		previous.add(element); | ||||
| 		final Object[] arr = clazz.getEnumConstants(); | ||||
| 		final StringBuilder out = new StringBuilder(); | ||||
| 		if (System.getenv("ARCHIDATA_GENERATE_ZOD_ENUM") != null) { | ||||
| 			boolean first = true; | ||||
| 			out.append("zod.enum(["); | ||||
| 			for (final Object elem : arr) { | ||||
| 				if (!first) { | ||||
| 					out.append(",\n\t"); | ||||
| 				} else { | ||||
| 					out.append("\n\t"); | ||||
| 					first = false; | ||||
| 				} | ||||
| 				out.append("'"); | ||||
| 				out.append(elem.toString()); | ||||
| 				out.append("'"); | ||||
| 			} | ||||
| 			if (first) { | ||||
| 				out.append("]}"); | ||||
| 			} else { | ||||
| 				out.append("\n\t])"); | ||||
| 			} | ||||
| 		} else { | ||||
| 			element.isEnum = true; | ||||
| 			boolean first = true; | ||||
| 			out.append("{"); | ||||
| 			for (final Object elem : arr) { | ||||
| 				if (!first) { | ||||
| 					out.append(",\n\t"); | ||||
| 				} else { | ||||
| 					out.append("\n\t"); | ||||
| 					first = false; | ||||
| 				} | ||||
| 				out.append(elem.toString()); | ||||
| 				out.append(" = '"); | ||||
| 				out.append(elem.toString()); | ||||
| 				out.append("'"); | ||||
| 			} | ||||
| 			if (first) { | ||||
| 				out.append("}"); | ||||
| 			} else { | ||||
| 				out.append(",\n\t}"); | ||||
| 			} | ||||
| 		} | ||||
| 		element.declaration = out.toString(); | ||||
| 		previous.addOrder(element); | ||||
| 		return element; | ||||
| 	} | ||||
|  | ||||
| 	public static String convertTypeZod(final Class<?> type, final GeneratedTypes previous) throws Exception { | ||||
| 		final ClassElement previousType = previous.find(type); | ||||
| 		if (previousType != null) { | ||||
| 			return previousType.zodName; | ||||
| 		} | ||||
| 		if (type.isEnum()) { | ||||
| 			return convertTypeZodEnum(type, previous).zodName; | ||||
| 		} | ||||
| 		if (type == List.class) { | ||||
| 			throw new DataAccessException("Imcompatible type of element in object for: " + type.getCanonicalName() + " Unmanaged List of List ... "); | ||||
| 		} | ||||
| 		final ClassElement elemCreated = createTable(type, previous); | ||||
| 		if (elemCreated != null) { | ||||
| 			return elemCreated.zodName; | ||||
| 		} | ||||
| 		throw new DataAccessException("Imcompatible type of element in object for: " + type.getCanonicalName()); | ||||
| 	} | ||||
|  | ||||
| 	public static String convertTypeZod(final Field field, final GeneratedTypes previous) throws Exception { | ||||
| 		final Class<?> type = field.getType(); | ||||
| 		final ClassElement previousType = previous.find(type); | ||||
| 		if (previousType != null) { | ||||
| 			return previousType.zodName; | ||||
| 		} | ||||
| 		if (type.isEnum()) { | ||||
| 			return convertTypeZodEnum(type, previous).zodName; | ||||
| 		} | ||||
| 		if (type == List.class) { | ||||
| 			final ParameterizedType listType = (ParameterizedType) field.getGenericType(); | ||||
| 			final Class<?> listClass = (Class<?>) listType.getActualTypeArguments()[0]; | ||||
| 			final String simpleSubType = convertTypeZod(listClass, previous); | ||||
| 			return "zod.array(" + simpleSubType + ")"; | ||||
| 		} | ||||
| 		final ClassElement elemCreated = createTable(type, previous); | ||||
| 		if (elemCreated != null) { | ||||
| 			return elemCreated.zodName; | ||||
| 		} | ||||
| 		throw new DataAccessException("Imcompatible type of element in object for: " + type.getCanonicalName()); | ||||
| 	} | ||||
|  | ||||
| 	public static String optionalTypeZod(final Class<?> type) throws Exception { | ||||
| 		if (type.isPrimitive()) { | ||||
| 			return ""; | ||||
| 		} | ||||
| 		return ".optional()"; | ||||
| 	} | ||||
|  | ||||
| 	public static void createTablesSpecificType(final Field elem, final int fieldId, final StringBuilder builder, final GeneratedTypes previous) throws Exception { | ||||
| 		final String name = elem.getName(); | ||||
| 		final Class<?> classModel = elem.getType(); | ||||
| 		final int limitSize = AnnotationTools.getLimitSize(elem); | ||||
|  | ||||
| 		final String comment = AnnotationTools.getComment(elem); | ||||
|  | ||||
| 		if (fieldId != 0) { | ||||
| 			builder.append(","); | ||||
| 		} | ||||
| 		if (comment != null) { | ||||
| 			builder.append("\n\t// "); | ||||
| 			builder.append(comment); | ||||
| 		} | ||||
| 		builder.append("\n\t"); | ||||
| 		builder.append(name); | ||||
| 		builder.append(": "); | ||||
| 		builder.append(convertTypeZod(elem, previous)); | ||||
| 		if (limitSize > 0 && classModel == String.class) { | ||||
| 			builder.append(".max("); | ||||
| 			builder.append(limitSize); | ||||
| 			builder.append(")"); | ||||
| 		} | ||||
| 		if (AnnotationTools.getSchemaReadOnly(elem)) { | ||||
| 			builder.append(".readonly()"); | ||||
| 		} | ||||
| 		builder.append(optionalTypeZod(classModel)); | ||||
| 	} | ||||
|  | ||||
| 	private static boolean isFieldFromSuperClass(final Class<?> model, final String filedName) { | ||||
| 		final Class<?> superClass = model.getSuperclass(); | ||||
| 		if (superClass == null) { | ||||
| 			return false; | ||||
| 		} | ||||
| 		for (final Field field : superClass.getFields()) { | ||||
| 			String name; | ||||
| 			try { | ||||
| 				name = AnnotationTools.getFieldName(field); | ||||
| 				if (filedName.equals(name)) { | ||||
| 					return true; | ||||
| 				} | ||||
| 			} catch (final Exception e) { | ||||
| 				// TODO Auto-generated catch block | ||||
| 				LOGGER.trace("Catch error field name in parent create data table: {}", e.getMessage()); | ||||
| 			} | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	public static GeneratedTypes createBasicType() throws Exception { | ||||
| 		final GeneratedTypes previous = new GeneratedTypes(); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { Void.class, void.class }, "void", "void", null, null, true)); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { String.class }, "zod.string()", "string", null, "zod.string()", true)); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { InputStream.class, FormDataContentDisposition.class }, "z.instanceof(File)", "File", null, "z.instanceof(File)", true)); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { Boolean.class, boolean.class }, "zod.boolean()", "boolean", null, "zod.boolean()", true)); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { UUID.class }, "ZodUUID", "UUID", "isUUID", "zod.string().uuid()", false), true); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { Long.class, long.class }, "ZodLong", "Long", "isLong", | ||||
| 				// "zod.bigint()", | ||||
| 				"zod.number()", false), true); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { Integer.class, int.class }, "ZodInteger", "Integer", "isInteger", "zod.number().safe()", false), true); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { Double.class, double.class }, "ZodDouble", "Double", "isDouble", "zod.number()", true), true); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { Float.class, float.class }, "ZodFloat", "Float", "isFloat", "zod.number()", false), true); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { Instant.class }, "ZodInstant", "Instant", "isInstant", "zod.string()", false), true); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { Date.class }, "ZodDate", "Date", "isDate", "zod.date()", false), true); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { Timestamp.class }, "ZodTimestamp", "Timestamp", "isTimestamp", "zod.date()", false), true); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { LocalDate.class }, "ZodLocalDate", "LocalDate", "isLocalDate", "zod.date()", false), true); | ||||
| 		previous.add(new ClassElement(new Class<?>[] { LocalTime.class }, "ZodLocalTime", "LocalTime", "isLocalTime", "zod.date()", false), true); | ||||
| 		return previous; | ||||
| 	} | ||||
|  | ||||
| 	/** Request the generation of the TypeScript file for the "Zod" export model | ||||
| 	 * @param classs List of class used in the model | ||||
| 	 * @return A string representing the Server models | ||||
| 	 * @throws Exception */ | ||||
| 	public static String createTables(final List<Class<?>> classs) throws Exception { | ||||
| 		return createTables(classs, createBasicType()); | ||||
| 	} | ||||
|  | ||||
| 	public static String createTables(final List<Class<?>> classs, final GeneratedTypes previous) throws Exception { | ||||
| 		for (final Class<?> clazz : classs) { | ||||
| 			createTable(clazz, previous); | ||||
| 		} | ||||
|  | ||||
| 		final StringBuilder generatedData = new StringBuilder(); | ||||
| 		generatedData.append(""" | ||||
| 				/** | ||||
| 				 * Interface of the server (auto-generated code) | ||||
| 				 */ | ||||
| 				import { z as zod } from \"zod\"; | ||||
|  | ||||
| 				"""); | ||||
| 		for (final Class<?> elem : previous.order) { | ||||
| 			final ClassElement data = previous.find(elem); | ||||
| 			if (!data.nativeType) { | ||||
| 				if (data.comment != null) { | ||||
| 					generatedData.append(data.comment); | ||||
| 				} | ||||
| 				generatedData.append(createDeclaration(data)); | ||||
| 				generatedData.append("\n\n"); | ||||
| 			} | ||||
| 		} | ||||
| 		LOGGER.info("generated: {}", generatedData.toString()); | ||||
| 		return generatedData.toString(); | ||||
| 	} | ||||
|  | ||||
| 	public static List<ClassElement> createTables(final Class<?>[] classs, final GeneratedTypes previous) throws Exception { | ||||
| 		final List<ClassElement> out = new ArrayList<>(); | ||||
| 		for (final Class<?> clazz : classs) { | ||||
| 			if (clazz == Response.class) { | ||||
| 				throw new IOException("Can not generate a Zod element for an unknow type Response"); | ||||
| 			} | ||||
| 			out.add(createTable(clazz, previous)); | ||||
| 		} | ||||
| 		return out; | ||||
| 	} | ||||
|  | ||||
| 	public static ClassElement createTable(final Class<?> clazz, final GeneratedTypes previous) throws Exception { | ||||
| 		if (clazz == null) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		if (clazz == Response.class) { | ||||
| 			throw new IOException("Can not generate a Zod element for an unknow type Response"); | ||||
| 		} | ||||
| 		final ClassElement alreadyExist = previous.find(clazz); | ||||
| 		if (previous.find(clazz) != null) { | ||||
| 			return alreadyExist; | ||||
| 		} | ||||
| 		if (clazz.isPrimitive()) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		// add the current class to prevent multiple creation | ||||
| 		final ClassElement curentElementClass = new ClassElement(clazz); | ||||
| 		previous.add(curentElementClass); | ||||
| 		// Local generation of class: | ||||
| 		final StringBuilder internalBuilder = new StringBuilder(); | ||||
| 		final List<String> alreadyAdded = new ArrayList<>(); | ||||
| 		LOGGER.trace("parse class: '{}'", clazz.getCanonicalName()); | ||||
| 		int fieldId = 0; | ||||
| 		for (final Field elem : clazz.getFields()) { | ||||
| 			// static field is only for internal global declaration ==> remove it .. | ||||
| 			if (java.lang.reflect.Modifier.isStatic(elem.getModifiers())) { | ||||
| 				continue; | ||||
| 			} | ||||
| 			final String dataName = elem.getName(); | ||||
| 			if (isFieldFromSuperClass(clazz, dataName)) { | ||||
| 				LOGGER.trace("        SKIP:  '{}'", elem.getName()); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (alreadyAdded.contains(dataName)) { | ||||
| 				LOGGER.trace("        SKIP2: '{}'", elem.getName()); | ||||
| 				continue; | ||||
| 			} | ||||
| 			alreadyAdded.add(dataName); | ||||
| 			LOGGER.trace("        + '{}'", elem.getName()); | ||||
| 			if (false && DataAccess.isAddOnField(elem)) { | ||||
| 				final DataAccessAddOn addOn = DataAccess.findAddOnforField(elem); | ||||
| 				LOGGER.error("Create type for: {} ==> {} (ADD-ON) ==> Not managed now ....", AnnotationTools.getFieldName(elem), elem.getType()); | ||||
| 				/* LOGGER.trace("Create type for: {} ==> {} (ADD-ON)", AnnotationTools.getFieldName(elem), elem.getType()); if (addOn != null) { addOn.createTables(tableName, elem, tmpOut, | ||||
| 				 * preActionList, postActionList, createIfNotExist, createDrop, fieldId); } else { throw new DataAccessException( "Element matked as add-on but add-on does not loaded: table:" + | ||||
| 				 * tableName + " field name=" + AnnotationTools.getFieldName(elem) + " type=" + elem.getType()); } fieldId++; */ | ||||
| 			} else { | ||||
| 				LOGGER.trace("Create type for: {} ==> {}", AnnotationTools.getFieldName(elem), elem.getType()); | ||||
| 				DataFactoryZod.createTablesSpecificType(elem, fieldId, internalBuilder, previous); | ||||
| 				fieldId++; | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
| 		final String description = AnnotationTools.getSchemaDescription(clazz); | ||||
| 		final String example = AnnotationTools.getSchemaExample(clazz); | ||||
| 		final StringBuilder generatedCommentedData = new StringBuilder(); | ||||
| 		if (description != null || example != null) { | ||||
| 			generatedCommentedData.append("/**\n"); | ||||
| 			if (description != null) { | ||||
| 				for (final String elem : description.split("\n")) { | ||||
| 					generatedCommentedData.append(" * "); | ||||
| 					generatedCommentedData.append(elem); | ||||
| 					generatedCommentedData.append("\n"); | ||||
| 				} | ||||
| 			} | ||||
| 			if (example != null) { | ||||
| 				generatedCommentedData.append(" * Example:\n"); | ||||
| 				generatedCommentedData.append(" * ```\n"); | ||||
| 				for (final String elem : example.split("\n")) { | ||||
| 					generatedCommentedData.append(" * "); | ||||
| 					generatedCommentedData.append(elem); | ||||
| 					generatedCommentedData.append("\n"); | ||||
| 				} | ||||
| 				generatedCommentedData.append(" * ```\n"); | ||||
| 			} | ||||
| 			generatedCommentedData.append(" */\n"); | ||||
| 		} | ||||
| 		curentElementClass.comment = generatedCommentedData.toString(); | ||||
| 		final StringBuilder generatedData = new StringBuilder(); | ||||
| 		final Class<?> parentClass = clazz.getSuperclass(); | ||||
| 		if (parentClass != null && parentClass != Object.class && parentClass != Record.class) { | ||||
| 			final ClassElement parentDeclaration = createTable(parentClass, previous); | ||||
| 			generatedData.append(parentDeclaration.zodName); | ||||
| 			generatedData.append(".extend({"); | ||||
| 		} else { | ||||
| 			generatedData.append("zod.object({"); | ||||
| 		} | ||||
| 		generatedData.append(internalBuilder.toString()); | ||||
| 		generatedData.append("\n})"); | ||||
| 		// Remove the previous to reorder the map ==> parent must be inserted before us. | ||||
| 		curentElementClass.declaration = generatedData.toString(); | ||||
| 		previous.addOrder(curentElementClass); | ||||
| 		return curentElementClass; | ||||
| 	} | ||||
|  | ||||
| 	public static String createDeclaration(final ClassElement elem) { | ||||
| 		final StringBuilder generatedData = new StringBuilder(); | ||||
| 		if (elem.isEnum) { | ||||
| 			generatedData.append("export enum "); | ||||
| 			generatedData.append(elem.tsTypeName); | ||||
| 			generatedData.append(" "); | ||||
| 			generatedData.append(elem.declaration); | ||||
| 			generatedData.append(";"); | ||||
| 			generatedData.append("\nexport const "); | ||||
| 			generatedData.append(elem.zodName); | ||||
| 			generatedData.append(" = zod.nativeEnum("); | ||||
| 			generatedData.append(elem.tsTypeName); | ||||
| 			generatedData.append(");"); | ||||
| 		} else { | ||||
| 			generatedData.append("export const "); | ||||
| 			generatedData.append(elem.zodName); | ||||
| 			generatedData.append(" = "); | ||||
| 			generatedData.append(elem.declaration); | ||||
| 			generatedData.append(";"); | ||||
| 			generatedData.append("\nexport type "); | ||||
| 			generatedData.append(elem.tsTypeName); | ||||
| 			generatedData.append(" = zod.infer<typeof "); | ||||
| 			generatedData.append(elem.zodName); | ||||
| 			generatedData.append(">;"); | ||||
| 		} | ||||
| 		// declare generic isXXX: | ||||
| 		generatedData.append("\nexport function "); | ||||
| 		generatedData.append(elem.tsCheckType); | ||||
| 		generatedData.append("(data: any): data is "); | ||||
| 		generatedData.append(elem.tsTypeName); | ||||
| 		generatedData.append(" {\n\ttry {\n\t\t"); | ||||
| 		generatedData.append(elem.zodName); | ||||
| 		generatedData.append(""" | ||||
| 				.parse(data); | ||||
| 						return true; | ||||
| 					} catch (e: any) { | ||||
| 						console.log(`Fail to parse data ${e}`); | ||||
| 						return false; | ||||
| 					} | ||||
| 				} | ||||
| 				"""); | ||||
| 		return generatedData.toString(); | ||||
| 	} | ||||
|  | ||||
| 	public static void generatePackage(final List<Class<?>> classs, final String pathPackage) throws Exception { | ||||
| 		final String packageApi = createTables(classs); | ||||
| 		FileWriter myWriter = new FileWriter(pathPackage + File.separator + "model.ts"); | ||||
| 		myWriter.write(packageApi.toString()); | ||||
| 		myWriter.close(); | ||||
| 		final String index = """ | ||||
| 				/** | ||||
| 				 * Global import of the package | ||||
| 				 */ | ||||
| 				export * from "./model.ts"; | ||||
|  | ||||
| 				"""; | ||||
| 		myWriter = new FileWriter(pathPackage + File.separator + "index.ts"); | ||||
| 		myWriter.write(index); | ||||
| 		myWriter.close(); | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										39
									
								
								src/org/kar/archidata/dataAccess/GroupBy.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/org/kar/archidata/dataAccess/GroupBy.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| package org.kar.archidata.dataAccess; | ||||
|  | ||||
| import java.sql.PreparedStatement; | ||||
| import java.util.List; | ||||
|  | ||||
| public class GroupBy extends QueryOption { | ||||
| 	protected final List<String> childs; | ||||
|  | ||||
| 	public GroupBy(final List<String> childs) { | ||||
| 		this.childs = childs; | ||||
| 	} | ||||
|  | ||||
| 	public GroupBy(final String... childs) { | ||||
| 		this.childs = List.of(childs); | ||||
| 	} | ||||
|  | ||||
| 	public void generateQuerry(final StringBuilder query, final String tableName) { | ||||
| 		if (this.childs.size() == 0) { | ||||
| 			return; | ||||
| 		} | ||||
| 		query.append(" GROUP BY "); | ||||
| 		boolean first = true; | ||||
| 		for (final String elem : this.childs) { | ||||
| 			if (first) { | ||||
| 				first = false; | ||||
| 			} else { | ||||
| 				query.append(", "); | ||||
| 			} | ||||
| 			// query.append("`"); | ||||
| 			query.append(elem); | ||||
| 			// query.append("` "); | ||||
| 		} | ||||
| 		query.append("\n"); | ||||
| 	} | ||||
|  | ||||
| 	public void injectQuerry(final PreparedStatement ps, final CountInOut iii) throws Exception { | ||||
| 		// nothing to add. | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										20
									
								
								src/org/kar/archidata/dataAccess/Limit.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/org/kar/archidata/dataAccess/Limit.java
									
									
									
									
									
										Normal 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 ? \n"); | ||||
| 	} | ||||
|  | ||||
| 	public void injectQuerry(final PreparedStatement ps, final CountInOut iii) throws Exception { | ||||
| 		DataAccess.addElement(ps, this.limit, iii); | ||||
| 		iii.inc(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										41
									
								
								src/org/kar/archidata/dataAccess/OrderBy.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/org/kar/archidata/dataAccess/OrderBy.java
									
									
									
									
									
										Normal 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() == 0) { | ||||
| 			return; | ||||
| 		} | ||||
| 		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(" "); | ||||
| 			query.append(elem.order.toString()); | ||||
| 		} | ||||
| 		query.append("\n"); | ||||
| 	} | ||||
|  | ||||
| 	public void injectQuerry(final PreparedStatement ps, final CountInOut iii) throws Exception { | ||||
| 		// nothing to add. | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										16
									
								
								src/org/kar/archidata/dataAccess/OrderItem.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/org/kar/archidata/dataAccess/OrderItem.java
									
									
									
									
									
										Normal 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; | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -17,22 +17,26 @@ public class QueryAnd implements QueryItem { | ||||
| 		Collections.addAll(this.childs, items); | ||||
| 	} | ||||
|  | ||||
| 	public void add(final QueryItem... items) { | ||||
| 		Collections.addAll(this.childs, items); | ||||
| 	} | ||||
|  | ||||
| 	@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(")"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -43,4 +47,8 @@ public class QueryAnd implements QueryItem { | ||||
| 			elem.injectQuerry(ps, iii); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public int size() { | ||||
| 		return this.childs.size(); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -14,14 +14,15 @@ 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) { | ||||
| 		if (tableName != null) { | ||||
| 			query.append(tableName); | ||||
| 			query.append("."); | ||||
| 		} | ||||
| 		query.append(this.key); | ||||
| 		query.append(" "); | ||||
| 		query.append(this.comparator); | ||||
| 		query.append(" ?"); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
|   | ||||
| @@ -18,22 +18,28 @@ 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) { | ||||
| 		if (tableName != null) { | ||||
| 			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(")"); | ||||
|  | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
							
								
								
									
										24
									
								
								src/org/kar/archidata/dataAccess/QueryNotNull.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/org/kar/archidata/dataAccess/QueryNotNull.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| package org.kar.archidata.dataAccess; | ||||
|  | ||||
| import java.sql.PreparedStatement; | ||||
|  | ||||
| public class QueryNotNull implements QueryItem { | ||||
| 	private final String key; | ||||
|  | ||||
| 	public QueryNotNull(final String key) { | ||||
| 		this.key = key; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public void generateQuerry(final StringBuilder query, final String tableName) { | ||||
| 		if (tableName != null) { | ||||
| 			query.append(tableName); | ||||
| 			query.append("."); | ||||
| 		} | ||||
| 		query.append(this.key); | ||||
| 		query.append(" IS NOT NULL"); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public void injectQuerry(final PreparedStatement ps, final CountInOut iii) throws Exception {} | ||||
| } | ||||
							
								
								
									
										24
									
								
								src/org/kar/archidata/dataAccess/QueryNull.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/org/kar/archidata/dataAccess/QueryNull.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| package org.kar.archidata.dataAccess; | ||||
|  | ||||
| import java.sql.PreparedStatement; | ||||
|  | ||||
| public class QueryNull implements QueryItem { | ||||
| 	private final String key; | ||||
|  | ||||
| 	public QueryNull(final String key) { | ||||
| 		this.key = key; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public void generateQuerry(final StringBuilder query, final String tableName) { | ||||
| 		if (tableName != null) { | ||||
| 			query.append(tableName); | ||||
| 			query.append("."); | ||||
| 		} | ||||
| 		query.append(this.key); | ||||
| 		query.append(" IS NULL"); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public void injectQuerry(final PreparedStatement ps, final CountInOut iii) throws Exception {} | ||||
| } | ||||
| @@ -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) { | ||||
| @@ -57,5 +64,4 @@ public class QueryOptions { | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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(")"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										7
									
								
								src/org/kar/archidata/dataAccess/RetreiveFromDB.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/org/kar/archidata/dataAccess/RetreiveFromDB.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| package org.kar.archidata.dataAccess; | ||||
|  | ||||
| import java.sql.ResultSet; | ||||
|  | ||||
| public interface RetreiveFromDB { | ||||
| 	void doRequest(final ResultSet rs, Object obj) throws Exception; | ||||
| } | ||||
| @@ -1,22 +1,32 @@ | ||||
| package org.kar.archidata.dataAccess.addOn; | ||||
|  | ||||
| import java.lang.reflect.Field; | ||||
| import java.lang.reflect.ParameterizedType; | ||||
| import java.sql.PreparedStatement; | ||||
| import java.sql.ResultSet; | ||||
| import java.sql.Types; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.annotation.AnnotationTools; | ||||
| import org.kar.archidata.annotation.DataJson; | ||||
| import org.kar.archidata.dataAccess.CountInOut; | ||||
| import org.kar.archidata.dataAccess.DataAccess; | ||||
| import org.kar.archidata.dataAccess.DataAccessAddOn; | ||||
| import org.kar.archidata.dataAccess.DataFactory; | ||||
| import org.kar.archidata.dataAccess.LazyGetter; | ||||
| import org.kar.archidata.dataAccess.QueryOptions; | ||||
| import org.kar.archidata.dataAccess.addOn.model.TableCoversLongLong; | ||||
| import org.kar.archidata.dataAccess.addOn.model.TableCoversLongUUID; | ||||
| import org.kar.archidata.dataAccess.addOn.model.TableCoversUUIDLong; | ||||
| import org.kar.archidata.dataAccess.addOn.model.TableCoversUUIDUUID; | ||||
| import org.kar.archidata.dataAccess.options.OverrideTableName; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonValue; | ||||
| import com.fasterxml.jackson.core.type.TypeReference; | ||||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||||
|  | ||||
| import jakarta.validation.constraints.NotNull; | ||||
| @@ -58,6 +68,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; | ||||
| @@ -80,14 +95,196 @@ public class AddOnDataJson implements DataAccessAddOn { | ||||
| 		count.inc(); | ||||
| 		if (!rs.wasNull()) { | ||||
| 			final ObjectMapper objectMapper = new ObjectMapper(); | ||||
| 			if (field.getType() == List.class) { | ||||
| 				final ParameterizedType listType = (ParameterizedType) field.getGenericType(); | ||||
| 				final Class<?> listClass = (Class<?>) listType.getActualTypeArguments()[0]; | ||||
| 				if (listClass == Long.class) { | ||||
| 					final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Long>>() {});// field.getType()); | ||||
| 					field.set(data, dataParsed); | ||||
| 					return; | ||||
| 				} | ||||
| 				if (listClass == Float.class) { | ||||
| 					final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Float>>() {});// field.getType()); | ||||
| 					field.set(data, dataParsed); | ||||
| 					return; | ||||
| 				} | ||||
| 				if (listClass == Double.class) { | ||||
| 					final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Double>>() {});// field.getType()); | ||||
| 					field.set(data, dataParsed); | ||||
| 					return; | ||||
| 				} | ||||
| 				if (listClass == Integer.class) { | ||||
| 					final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Integer>>() {});// field.getType()); | ||||
| 					field.set(data, dataParsed); | ||||
| 					return; | ||||
| 				} | ||||
| 				if (listClass == Short.class) { | ||||
| 					final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<Short>>() {});// field.getType()); | ||||
| 					field.set(data, dataParsed); | ||||
| 					return; | ||||
| 				} | ||||
| 				if (listClass == String.class) { | ||||
| 					final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<String>>() {});// field.getType()); | ||||
| 					field.set(data, dataParsed); | ||||
| 					return; | ||||
| 				} | ||||
| 				if (listClass == UUID.class) { | ||||
| 					final Object dataParsed = objectMapper.readValue(jsonData, new TypeReference<List<UUID>>() {});// field.getType()); | ||||
| 					field.set(data, dataParsed); | ||||
| 					return; | ||||
| 				} | ||||
| 				LOGGER.warn("Maybe fail to translate Model in datajson list: List<{}>", listClass.getCanonicalName()); | ||||
| 			} | ||||
| 			final Object dataParsed = objectMapper.readValue(jsonData, field.getType()); | ||||
| 			field.set(data, dataParsed); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public void createTables(final String tableName, final Field field, final StringBuilder mainTableBuilder, final List<String> preActionList, final List<String> postActionList, | ||||
| 			final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception { | ||||
| 	public void createTables(// | ||||
| 			final String tableName, // | ||||
| 			final Field primaryField, // | ||||
| 			final Field field, // | ||||
| 			final StringBuilder mainTableBuilder, // | ||||
| 			final List<String> preActionList, // | ||||
| 			final List<String> postActionList, // | ||||
| 			final boolean createIfNotExist, // | ||||
| 			final boolean createDrop, // | ||||
| 			final int fieldId // | ||||
| 	) throws Exception { | ||||
| 		DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, JsonValue.class); | ||||
| 	} | ||||
|  | ||||
| 	public static void addLink(final Class<?> clazz, final Long id, final String column, final Long remoteKey) throws Exception { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversLongLong data = DataAccess.get(TableCoversLongLong.class, id, new OverrideTableName(tableName)); | ||||
| 		if (data.covers == null) { | ||||
| 			data.covers = new ArrayList<>(); | ||||
| 		} | ||||
| 		for (final Long elem : data.covers) { | ||||
| 			if (elem.equals(remoteKey)) { | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 		data.covers.add(remoteKey); | ||||
| 		DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); | ||||
| 	} | ||||
|  | ||||
| 	public static void addLink(final Class<?> clazz, final Long id, final String column, final UUID remoteKey) throws Exception { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversLongUUID data = DataAccess.get(TableCoversLongUUID.class, id, new OverrideTableName(tableName)); | ||||
| 		if (data.covers == null) { | ||||
| 			data.covers = new ArrayList<>(); | ||||
| 		} | ||||
| 		for (final UUID elem : data.covers) { | ||||
| 			if (elem.equals(remoteKey)) { | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 		data.covers.add(remoteKey); | ||||
| 		DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); | ||||
| 	} | ||||
|  | ||||
| 	public static void addLink(final Class<?> clazz, final UUID id, final String column, final UUID remoteKey) throws Exception { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, id, new OverrideTableName(tableName)); | ||||
| 		if (data.covers == null) { | ||||
| 			data.covers = new ArrayList<>(); | ||||
| 		} | ||||
| 		for (final UUID elem : data.covers) { | ||||
| 			if (elem.equals(remoteKey)) { | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 		data.covers.add(remoteKey); | ||||
| 		DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); | ||||
| 	} | ||||
|  | ||||
| 	public static void addLink(final Class<?> clazz, final UUID id, final String column, final Long remoteKey) throws Exception { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, id, new OverrideTableName(tableName)); | ||||
| 		if (data.covers == null) { | ||||
| 			data.covers = new ArrayList<>(); | ||||
| 		} | ||||
| 		for (final Long elem : data.covers) { | ||||
| 			if (elem.equals(remoteKey)) { | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 		data.covers.add(remoteKey); | ||||
| 		DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); | ||||
| 	} | ||||
|  | ||||
| 	public static void removeLink(final Class<?> clazz, final UUID id, final String column, final Long remoteKey) throws Exception { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversUUIDLong data = DataAccess.get(TableCoversUUIDLong.class, id, new OverrideTableName(tableName)); | ||||
| 		if (data.covers == null) { | ||||
| 			return; | ||||
| 		} | ||||
| 		final List<Long> newList = new ArrayList<>(); | ||||
| 		for (final Long elem : data.covers) { | ||||
| 			if (elem.equals(remoteKey)) { | ||||
| 				continue; | ||||
| 			} | ||||
| 			newList.add(elem); | ||||
| 		} | ||||
| 		data.covers = newList; | ||||
| 		DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); | ||||
| 	} | ||||
|  | ||||
| 	public static void removeLink(final Class<?> clazz, final UUID id, final String column, final UUID remoteKey) throws Exception { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversUUIDUUID data = DataAccess.get(TableCoversUUIDUUID.class, id, new OverrideTableName(tableName)); | ||||
| 		if (data.covers == null) { | ||||
| 			return; | ||||
| 		} | ||||
| 		final List<UUID> newList = new ArrayList<>(); | ||||
| 		for (final UUID elem : data.covers) { | ||||
| 			if (elem.equals(remoteKey)) { | ||||
| 				continue; | ||||
| 			} | ||||
| 			newList.add(elem); | ||||
| 		} | ||||
| 		data.covers = newList; | ||||
| 		DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); | ||||
| 	} | ||||
|  | ||||
| 	public static void removeLink(final Class<?> clazz, final Long id, final String column, final Long remoteKey) throws Exception { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversLongLong data = DataAccess.get(TableCoversLongLong.class, id, new OverrideTableName(tableName)); | ||||
| 		if (data.covers == null) { | ||||
| 			return; | ||||
| 		} | ||||
| 		final List<Long> newList = new ArrayList<>(); | ||||
| 		for (final Long elem : data.covers) { | ||||
| 			if (elem.equals(remoteKey)) { | ||||
| 				continue; | ||||
| 			} | ||||
| 			newList.add(elem); | ||||
| 		} | ||||
| 		data.covers = newList; | ||||
| 		DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); | ||||
| 	} | ||||
|  | ||||
| 	public static void removeLink(final Class<?> clazz, final Long id, final String column, final UUID remoteKey) throws Exception { | ||||
| 		final String tableName = AnnotationTools.getTableName(clazz); | ||||
| 		final TableCoversLongUUID data = DataAccess.get(TableCoversLongUUID.class, id, new OverrideTableName(tableName)); | ||||
| 		if (data.covers == null) { | ||||
| 			return; | ||||
| 		} | ||||
| 		final List<UUID> newList = new ArrayList<>(); | ||||
| 		for (final UUID elem : data.covers) { | ||||
| 			if (elem.equals(remoteKey)) { | ||||
| 				continue; | ||||
| 			} | ||||
| 			newList.add(elem); | ||||
| 		} | ||||
| 		data.covers = newList; | ||||
| 		DataAccess.update(data, data.id, List.of("covers"), new OverrideTableName(tableName)); | ||||
| 	} | ||||
| 	/* public static <TYPE> void addLink(final Class<TYPE> clazz, final Object localKey, final String column, final Object remoteKey) throws Exception { final String tableName = | ||||
| 	 * AnnotationTools.getTableName(clazz); final TYPE data = DataAccess.get(clazz, localKey); // TODO: add filter of the "column" // find the field column: // add the remoteKey in the list: // post | ||||
| 	 * new data in the DB final String linkTableName = generateLinkTableName(this.tableName, this.column); final LinkTable insertElement = new LinkTable(this.localKey, this.remoteKey); | ||||
| 	 * DataAccess.insert(insertElement, new OverrideTableName(linkTableName)); } */ | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import java.sql.ResultSet; | ||||
| import java.sql.SQLException; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.annotation.AnnotationTools; | ||||
| import org.kar.archidata.dataAccess.CountInOut; | ||||
| @@ -18,7 +19,11 @@ import org.kar.archidata.dataAccess.QueryAnd; | ||||
| 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.addOn.model.LinkTableLongLong; | ||||
| import org.kar.archidata.dataAccess.addOn.model.LinkTableLongUUID; | ||||
| import org.kar.archidata.dataAccess.addOn.model.LinkTableUUIDLong; | ||||
| import org.kar.archidata.dataAccess.addOn.model.LinkTableUUIDUUID; | ||||
| 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; | ||||
| @@ -31,7 +36,8 @@ import jakarta.validation.constraints.NotNull; | ||||
|  | ||||
| public class AddOnManyToMany implements DataAccessAddOn { | ||||
| 	static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class); | ||||
| 	static final String SEPARATOR = "-"; | ||||
| 	static final String SEPARATOR_LONG = "-"; | ||||
| 	static final String SEPARATOR_UUID = "_"; | ||||
|  | ||||
| 	@Override | ||||
| 	public Class<?> getAnnotationClass() { | ||||
| @@ -61,7 +67,15 @@ public class AddOnManyToMany implements DataAccessAddOn { | ||||
|  | ||||
| 	@Override | ||||
| 	public boolean canRetrieve(final Field field) { | ||||
| 		if (field.getType() != List.class) { | ||||
| 			return false; | ||||
| 		} | ||||
| 		final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; | ||||
| 		if (objectClass == Long.class || objectClass == UUID.class) { | ||||
| 			return true; | ||||
| 		} else { | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public static String generateLinkTableNameField(final String tableName, final Field field) throws Exception { | ||||
| @@ -77,34 +91,51 @@ public class AddOnManyToMany implements DataAccessAddOn { | ||||
| 		return tableName + "_link_" + localName; | ||||
| 	} | ||||
|  | ||||
| 	public void generateConcatQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, | ||||
| 			@NotNull final String name, @NotNull final CountInOut elemCount, final QueryOptions options) { | ||||
|  | ||||
| 	public void generateConcatQuerry( // | ||||
| 			@NotNull final String tableName, // | ||||
| 			@NotNull final Field field, // | ||||
| 			@NotNull final StringBuilder querrySelect, // | ||||
| 			@NotNull final StringBuilder querry, // | ||||
| 			@NotNull final String name, // | ||||
| 			@NotNull final CountInOut elemCount, // | ||||
| 			final QueryOptions options// | ||||
| 	) { | ||||
| 		final String linkTableName = generateLinkTableName(tableName, name); | ||||
| 		final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; | ||||
| 		final String tmpVariable = "tmp_" + Integer.toString(elemCount.value); | ||||
| 		querrySelect.append(" (SELECT GROUP_CONCAT("); | ||||
| 		if (objectClass == Long.class) { | ||||
| 			querrySelect.append(tmpVariable); | ||||
| 			querrySelect.append(".object2Id "); | ||||
| 		if (ConfigBaseVariable.getDBType().equals("sqlite")) { | ||||
| 		} else { | ||||
| 			querrySelect.append("BIN_TO_UUID("); | ||||
| 			querrySelect.append(tmpVariable); | ||||
| 			querrySelect.append(".object2Id) "); | ||||
| 		} | ||||
| 		if ("sqlite".equals(ConfigBaseVariable.getDBType())) { | ||||
| 			querrySelect.append(", "); | ||||
| 		} else { | ||||
| 			querrySelect.append("SEPARATOR "); | ||||
| 		} | ||||
| 		querrySelect.append("'"); | ||||
| 		querrySelect.append(SEPARATOR); | ||||
| 		if (objectClass == Long.class) { | ||||
| 			querrySelect.append(SEPARATOR_LONG); | ||||
| 		} else { | ||||
| 			querrySelect.append(SEPARATOR_UUID); | ||||
|  | ||||
| 		} | ||||
| 		querrySelect.append("') FROM "); | ||||
| 		querrySelect.append(linkTableName); | ||||
| 		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"); | ||||
| @@ -118,13 +149,20 @@ public class AddOnManyToMany implements DataAccessAddOn { | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public void generateQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, @NotNull final String name, | ||||
| 			@NotNull final CountInOut elemCount, final QueryOptions options) throws Exception { | ||||
| 	public void generateQuerry( // | ||||
| 			@NotNull final String tableName, // | ||||
| 			@NotNull final Field field, // | ||||
| 			@NotNull final StringBuilder querrySelect, // | ||||
| 			@NotNull final StringBuilder querry, // | ||||
| 			@NotNull final String name, // | ||||
| 			@NotNull final CountInOut elemCount, // | ||||
| 			final QueryOptions options // | ||||
| 	) throws Exception { | ||||
| 		if (field.getType() != List.class) { | ||||
| 			return; | ||||
| 		} | ||||
| 		final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; | ||||
| 		if (objectClass == Long.class) { | ||||
| 		if (objectClass == Long.class || objectClass == UUID.class) { | ||||
| 			generateConcatQuerry(tableName, field, querrySelect, querry, name, elemCount, options); | ||||
| 		} | ||||
| 		final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class); | ||||
| @@ -141,15 +179,29 @@ 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 { | ||||
| 	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]; | ||||
| 		if (objectClass == Long.class) { | ||||
| 			final List<Long> idList = DataAccess.getListOfIds(rs, count.value, SEPARATOR); | ||||
| 			final List<Long> idList = DataAccess.getListOfIds(rs, count.value, SEPARATOR_LONG); | ||||
| 			field.set(data, idList); | ||||
| 			count.inc(); | ||||
| 			return; | ||||
| 		} else if (objectClass == UUID.class) { | ||||
| 			final List<UUID> idList = DataAccess.getListOfUUIDs(rs, count.value, SEPARATOR_UUID); | ||||
| 			field.set(data, idList); | ||||
| 			count.inc(); | ||||
| 			return; | ||||
| 		} | ||||
| 		final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class); | ||||
| 		if (decorators == null) { | ||||
| @@ -158,8 +210,8 @@ public class AddOnManyToMany implements DataAccessAddOn { | ||||
| 		if (objectClass == decorators.targetEntity()) { | ||||
| 			if (decorators.fetch() == FetchType.EAGER) { | ||||
| 				throw new DataAccessException("EAGER is not supported for list of element..."); | ||||
| 			} else { | ||||
| 				final List<Long> idList = DataAccess.getListOfIds(rs, count.value, SEPARATOR); | ||||
| 			} else if (objectClass == Long.class) { | ||||
| 				final List<Long> idList = DataAccess.getListOfIds(rs, count.value, SEPARATOR_LONG); | ||||
| 				// field.set(data, idList); | ||||
| 				count.inc(); | ||||
| 				if (idList != null && idList.size() > 0) { | ||||
| @@ -169,7 +221,26 @@ 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; | ||||
| 						} | ||||
| 						field.set(data, foreignData); | ||||
| 					}; | ||||
| 					lazyCall.add(lambda); | ||||
| 				} | ||||
| 			} else if (objectClass == UUID.class) { | ||||
| 				final List<UUID> idList = DataAccess.getListOfUUIDs(rs, count.value, SEPARATOR_UUID); | ||||
| 				// field.set(data, idList); | ||||
| 				count.inc(); | ||||
| 				if (idList != null && idList.size() > 0) { | ||||
| 					final String idField = AnnotationTools.getFieldName(AnnotationTools.getIdField(objectClass)); | ||||
| 					// In the lazy mode, the request is done in asynchronous mode, they will be done after... | ||||
| 					final LazyGetter lambda = () -> { | ||||
| 						final List<UUID> childs = new ArrayList<>(idList); | ||||
| 						// TODO: update to have get with abstract types .... | ||||
| 						@SuppressWarnings("unchecked") | ||||
| 						final Object foreignData = DataAccess.getsWhere(decorators.targetEntity(), new Condition(new QueryInList<>(idField, childs))); | ||||
| 						if (foreignData == null) { | ||||
| 							return; | ||||
| 						} | ||||
| @@ -181,29 +252,250 @@ 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 Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; | ||||
| 		if (objectClass != Long.class && objectClass != UUID.class) { | ||||
| 			throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">"); | ||||
| 		} | ||||
| 		final String columnName = AnnotationTools.getFieldName(field); | ||||
| 		final String linkTableName = generateLinkTableName(tableName, columnName); | ||||
|  | ||||
| 		if (localKey instanceof final Long localKeyLong) { | ||||
| 			if (objectClass == Long.class) { | ||||
| 				actions.add(() -> { | ||||
| 					DataAccess.deleteWhere(LinkTableLongLong.class, new OverrideTableName(linkTableName), new Condition(new QueryCondition("object1Id", "=", localKeyLong))); | ||||
| 				}); | ||||
| 				asyncInsert(tableName, localKey, field, data, actions); | ||||
| 			} else { | ||||
| 				actions.add(() -> { | ||||
| 					DataAccess.deleteWhere(LinkTableLongUUID.class, new OverrideTableName(linkTableName), new Condition(new QueryCondition("object1Id", "=", localKeyLong))); | ||||
| 				}); | ||||
| 				asyncInsert(tableName, localKey, field, data, actions); | ||||
| 			} | ||||
| 		} else if (localKey instanceof final UUID localKeyUUID) { | ||||
| 			if (objectClass == Long.class) { | ||||
| 				actions.add(() -> { | ||||
| 					DataAccess.deleteWhere(LinkTableUUIDLong.class, new OverrideTableName(linkTableName), new Condition(new QueryCondition("object1Id", "=", localKeyUUID))); | ||||
| 				}); | ||||
| 				asyncInsert(tableName, localKey, field, data, actions); | ||||
| 			} else { | ||||
| 				actions.add(() -> { | ||||
| 					DataAccess.deleteWhere(LinkTableUUIDUUID.class, new OverrideTableName(linkTableName), new Condition(new QueryCondition("object1Id", "=", localKeyUUID))); | ||||
| 				}); | ||||
| 				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 Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; | ||||
| 		if (objectClass != Long.class && objectClass != UUID.class) { | ||||
| 			throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">"); | ||||
| 		} | ||||
| 		final String columnName = AnnotationTools.getFieldName(field); | ||||
| 		final String linkTableName = generateLinkTableName(tableName, columnName); | ||||
| 		if (localKey instanceof final Long localKeyLong) { | ||||
| 			if (objectClass == Long.class) { | ||||
| 				// ======================================================== | ||||
| 				// == Link a "Long" primary Key with List<Long> | ||||
| 				// ======================================================== | ||||
| 				@SuppressWarnings("unchecked") | ||||
| 				final List<Long> dataCasted = (List<Long>) data; | ||||
| 				if (dataCasted.size() == 0) { | ||||
| 					return; | ||||
| 				} | ||||
| 				final List<LinkTableLongLong> insertElements = new ArrayList<>(); | ||||
| 				for (final Long remoteKey : dataCasted) { | ||||
| 					if (remoteKey == null) { | ||||
| 						throw new DataAccessException("Try to insert remote key with null value"); | ||||
| 					} | ||||
| 					insertElements.add(new LinkTableLongLong(localKeyLong, remoteKey)); | ||||
| 				} | ||||
| 				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)); | ||||
| 				}); | ||||
| 			} else { | ||||
| 				// ======================================================== | ||||
| 				// == Link a "Long" primary Key with List<UUID> | ||||
| 				// ======================================================== | ||||
| 				@SuppressWarnings("unchecked") | ||||
| 				final List<UUID> dataCasted = (List<UUID>) data; | ||||
| 				if (dataCasted.size() == 0) { | ||||
| 					return; | ||||
| 				} | ||||
| 				final List<LinkTableLongUUID> insertElements = new ArrayList<>(); | ||||
| 				for (final UUID remoteKey : dataCasted) { | ||||
| 					if (remoteKey == null) { | ||||
| 						throw new DataAccessException("Try to insert remote key with null value"); | ||||
| 					} | ||||
| 					insertElements.add(new LinkTableLongUUID(localKeyLong, remoteKey)); | ||||
| 				} | ||||
| 				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)); | ||||
| 				}); | ||||
| 			} | ||||
| 		} else if (localKey instanceof final UUID localKeyUUID) { | ||||
| 			if (objectClass == Long.class) { | ||||
| 				// ======================================================== | ||||
| 				// == Link a "UUID" primary Key with List<Long> | ||||
| 				// ======================================================== | ||||
| 				@SuppressWarnings("unchecked") | ||||
| 				final List<Long> dataCasted = (List<Long>) data; | ||||
| 				if (dataCasted.size() == 0) { | ||||
| 					return; | ||||
| 				} | ||||
| 				final List<LinkTableUUIDLong> insertElements = new ArrayList<>(); | ||||
| 				for (final Long remoteKey : dataCasted) { | ||||
| 					if (remoteKey == null) { | ||||
| 						throw new DataAccessException("Try to insert remote key with null value"); | ||||
| 					} | ||||
| 					insertElements.add(new LinkTableUUIDLong(localKeyUUID, remoteKey)); | ||||
| 				} | ||||
| 				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)); | ||||
| 				}); | ||||
| 			} else { | ||||
| 				// ======================================================== | ||||
| 				// == Link a "UUID" primary Key with List<UUID> | ||||
| 				// ======================================================== | ||||
| 				@SuppressWarnings("unchecked") | ||||
| 				final List<UUID> dataCasted = (List<UUID>) data; | ||||
| 				if (dataCasted.size() == 0) { | ||||
| 					return; | ||||
| 				} | ||||
| 				final List<LinkTableUUIDUUID> insertElements = new ArrayList<>(); | ||||
| 				for (final UUID remoteKey : dataCasted) { | ||||
| 					if (remoteKey == null) { | ||||
| 						throw new DataAccessException("Try to insert remote key with null value"); | ||||
| 					} | ||||
| 					insertElements.add(new LinkTableUUIDUUID(localKeyUUID, remoteKey)); | ||||
| 				} | ||||
| 				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)); | ||||
| 				}); | ||||
| 			} | ||||
| 		} else { | ||||
| 			throw new DataAccessException("Not manage access of remte key like ManyToMany other than Long or UUID: " + localKey.getClass().getCanonicalName()); | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public void drop(final String tableName, final Field field) throws Exception { | ||||
| 		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 && objectClass != UUID.class) { | ||||
| 			throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">"); | ||||
| 		} | ||||
| 		DataAccess.drop(LinkTableLongLong.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); | ||||
| 		final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; | ||||
| 		if (objectClass != Long.class && objectClass != UUID.class) { | ||||
| 			throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">"); | ||||
| 		} | ||||
| 		DataAccess.cleanAll(LinkTableLongLong.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); | ||||
| 		/* final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; if (objectClass != Long.class && objectClass != UUID.class) { throw new | ||||
| 		 * DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">"); } */ | ||||
| 		final LinkTableLongLong insertElement = new LinkTableLongLong(localKey, remoteKey); | ||||
| 		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(LinkTableLongLong.class, new OverrideTableName(linkTableName), | ||||
| 				new Condition(new QueryAnd(new QueryCondition("object1Id", "=", localKey), new QueryCondition("object2Id", "=", remoteKey)))); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public void createTables(final String tableName, final Field field, final StringBuilder mainTableBuilder, final List<String> preActionList, final List<String> postActionList, | ||||
| 			final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception { | ||||
| 	public void createTables( // | ||||
| 			final String tableName, // | ||||
| 			final Field primaryField, // | ||||
| 			final Field field, // | ||||
| 			final StringBuilder mainTableBuilder, // | ||||
| 			final List<String> preActionList, // | ||||
| 			final List<String> postActionList, // | ||||
| 			final boolean createIfNotExist, // | ||||
| 			final boolean createDrop, // | ||||
| 			final int fieldId// | ||||
| 	) throws Exception { | ||||
| 		final String linkTableName = generateLinkTableNameField(tableName, field); | ||||
| 		final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName)); | ||||
| 		final List<String> sqlCommand = DataFactory.createTable(LinkTable.class, options); | ||||
| 		final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; | ||||
| 		if (objectClass != Long.class && objectClass != UUID.class) { | ||||
| 			throw new DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">"); | ||||
| 		} | ||||
| 		final Class<?> primaryType = primaryField.getType(); | ||||
| 		if (primaryType == Long.class) { | ||||
| 			if (objectClass == Long.class) { | ||||
| 				final List<String> sqlCommand = DataFactory.createTable(LinkTableLongLong.class, options); | ||||
| 				postActionList.addAll(sqlCommand); | ||||
| 			} else { | ||||
| 				final List<String> sqlCommand = DataFactory.createTable(LinkTableLongUUID.class, options); | ||||
| 				postActionList.addAll(sqlCommand); | ||||
| 			} | ||||
| 		} else if (primaryType == UUID.class) { | ||||
|  | ||||
| 			if (objectClass == Long.class) { | ||||
| 				final List<String> sqlCommand = DataFactory.createTable(LinkTableUUIDLong.class, options); | ||||
| 				postActionList.addAll(sqlCommand); | ||||
| 			} else { | ||||
| 				final List<String> sqlCommand = DataFactory.createTable(LinkTableUUIDUUID.class, options); | ||||
| 				postActionList.addAll(sqlCommand); | ||||
| 			} | ||||
| 		} else { | ||||
| 			throw new DataAccessException("Can not ManyToMany with other than primary key type Long or UUID Model: " + primaryType.getCanonicalName()); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import java.sql.PreparedStatement; | ||||
| import java.sql.ResultSet; | ||||
| import java.sql.Types; | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.annotation.AnnotationTools; | ||||
| import org.kar.archidata.dataAccess.CountInOut; | ||||
| @@ -14,6 +15,7 @@ import org.kar.archidata.dataAccess.DataFactory; | ||||
| import org.kar.archidata.dataAccess.LazyGetter; | ||||
| import org.kar.archidata.dataAccess.QueryOptions; | ||||
| import org.kar.archidata.exception.DataAccessException; | ||||
| import org.kar.archidata.tools.UuidUtils; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| @@ -30,12 +32,11 @@ public class AddOnManyToOne implements DataAccessAddOn { | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public String getSQLFieldType(final Field elem) throws Exception { | ||||
| 		final String fieldName = AnnotationTools.getFieldName(elem); | ||||
| 	public String getSQLFieldType(final Field field) throws Exception { | ||||
| 		final String fieldName = AnnotationTools.getFieldName(field); | ||||
| 		try { | ||||
| 			return DataFactory.convertTypeInSQL(Long.class, fieldName); | ||||
| 			return DataFactory.convertTypeInSQL(field.getType(), fieldName); | ||||
| 		} catch (final Exception e) { | ||||
| 			// TODO Auto-generated catch block | ||||
| 			e.printStackTrace(); | ||||
| 		} | ||||
| 		return null; | ||||
| @@ -43,18 +44,41 @@ public class AddOnManyToOne implements DataAccessAddOn { | ||||
|  | ||||
| 	@Override | ||||
| 	public boolean isCompatibleField(final Field elem) { | ||||
| 		final ManyToOne decorators = elem.getDeclaredAnnotation(ManyToOne.class); | ||||
| 		return decorators != null; | ||||
| 		return elem.getDeclaredAnnotation(ManyToOne.class) != null; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii) throws Exception { | ||||
| 		final Object data = field.get(rootObject); | ||||
| 		if (data == null) { | ||||
| 			if (field.getType() == Long.class) { | ||||
| 				ps.setNull(iii.value, Types.BIGINT); | ||||
| 			} else if (field.getType() == Integer.class) { | ||||
| 				ps.setNull(iii.value, Types.INTEGER); | ||||
| 			} else if (field.getType() == Short.class) { | ||||
| 				ps.setNull(iii.value, Types.INTEGER); | ||||
| 			} else if (field.getType() == String.class) { | ||||
| 				ps.setNull(iii.value, Types.VARCHAR); | ||||
| 			} else if (field.getType() == UUID.class) { | ||||
| 				ps.setNull(iii.value, Types.BINARY); | ||||
| 			} | ||||
| 		} else if (field.getType() == Long.class) { | ||||
| 			final Long dataLong = (Long) data; | ||||
| 			ps.setLong(iii.value, dataLong); | ||||
| 			final Long dataTyped = (Long) data; | ||||
| 			ps.setLong(iii.value, dataTyped); | ||||
| 		} else if (field.getType() == Integer.class) { | ||||
| 			final Integer dataTyped = (Integer) data; | ||||
| 			ps.setInt(iii.value, dataTyped); | ||||
| 		} else if (field.getType() == Short.class) { | ||||
| 			final Short dataTyped = (Short) data; | ||||
| 			ps.setShort(iii.value, dataTyped); | ||||
| 		} else if (field.getType() == String.class) { | ||||
| 			final String dataTyped = (String) data; | ||||
| 			ps.setString(iii.value, dataTyped); | ||||
| 		} else if (field.getType() == UUID.class) { | ||||
| 			final UUID dataTyped = (UUID) data; | ||||
| 			LOGGER.info("Generate UUTD for DB: {}", dataTyped); | ||||
| 			final byte[] dataByte = UuidUtils.asBytes(dataTyped); | ||||
| 			ps.setBytes(iii.value, dataByte); | ||||
| 		} else { | ||||
| 			final Field idField = AnnotationTools.getFieldOfId(field.getType()); | ||||
| 			final Object uid = idField.get(data); | ||||
| @@ -71,7 +95,11 @@ public class AddOnManyToOne implements DataAccessAddOn { | ||||
|  | ||||
| 	@Override | ||||
| 	public boolean canInsert(final Field field) { | ||||
| 		if (field.getType() == Long.class) { | ||||
| 		if (field.getType() == Long.class // | ||||
| 				|| field.getType() == Integer.class // | ||||
| 				|| field.getType() == Short.class // | ||||
| 				|| field.getType() == String.class // | ||||
| 				|| field.getType() == UUID.class) { | ||||
| 			return true; | ||||
| 		} | ||||
| 		final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); | ||||
| @@ -81,9 +109,19 @@ 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) { | ||||
| 		final Class<?> classType = field.getType(); | ||||
| 		if (classType == Long.class // | ||||
| 				|| classType == Integer.class // | ||||
| 				|| classType == Short.class // | ||||
| 				|| classType == String.class // | ||||
| 				|| classType == UUID.class) { | ||||
| 			return true; | ||||
| 		} | ||||
| 		final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); | ||||
| @@ -94,9 +132,20 @@ public class AddOnManyToOne implements DataAccessAddOn { | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public void generateQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, @NotNull final String name, | ||||
| 			@NotNull final CountInOut elemCount, final QueryOptions options) throws Exception { | ||||
| 		if (field.getType() == Long.class) { | ||||
| 	public void generateQuerry( // | ||||
| 			@NotNull final String tableName, // | ||||
| 			@NotNull final Field field, // | ||||
| 			@NotNull final StringBuilder querrySelect, // | ||||
| 			@NotNull final StringBuilder querry, // | ||||
| 			@NotNull final String name, // | ||||
| 			@NotNull final CountInOut elemCount, // | ||||
| 			final QueryOptions options// | ||||
| 	) throws Exception { | ||||
| 		if (field.getType() == Long.class // | ||||
| 				|| field.getType() == Integer.class // | ||||
| 				|| field.getType() == Short.class // | ||||
| 				|| field.getType() == String.class // | ||||
| 				|| field.getType() == UUID.class) { | ||||
| 			querrySelect.append(" "); | ||||
| 			querrySelect.append(tableName); | ||||
| 			querrySelect.append("."); | ||||
| @@ -145,6 +194,39 @@ public class AddOnManyToOne implements DataAccessAddOn { | ||||
| 			} | ||||
| 			return; | ||||
| 		} | ||||
| 		if (field.getType() == Integer.class) { | ||||
| 			final Integer foreignKey = rs.getInt(count.value); | ||||
| 			count.inc(); | ||||
| 			if (!rs.wasNull()) { | ||||
| 				field.set(data, foreignKey); | ||||
| 			} | ||||
| 			return; | ||||
| 		} | ||||
| 		if (field.getType() == Short.class) { | ||||
| 			final Short foreignKey = rs.getShort(count.value); | ||||
| 			count.inc(); | ||||
| 			if (!rs.wasNull()) { | ||||
| 				field.set(data, foreignKey); | ||||
| 			} | ||||
| 			return; | ||||
| 		} | ||||
| 		if (field.getType() == String.class) { | ||||
| 			final String foreignKey = rs.getString(count.value); | ||||
| 			count.inc(); | ||||
| 			if (!rs.wasNull()) { | ||||
| 				field.set(data, foreignKey); | ||||
| 			} | ||||
| 			return; | ||||
| 		} | ||||
| 		if (field.getType() == UUID.class) { | ||||
| 			final byte[] tmp = rs.getBytes(count.value); | ||||
| 			count.inc(); | ||||
| 			if (!rs.wasNull()) { | ||||
| 				final UUID foreignKey = UuidUtils.asUuid(tmp); | ||||
| 				field.set(data, foreignKey); | ||||
| 			} | ||||
| 			return; | ||||
| 		} | ||||
| 		final Class<?> objectClass = field.getType(); | ||||
| 		final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); | ||||
| 		if (decorators == null) { | ||||
| @@ -179,8 +261,27 @@ public class AddOnManyToOne implements DataAccessAddOn { | ||||
|  | ||||
| 	// TODO : refacto this table to manage a generic table with dynamic name to be serialisable with the default system | ||||
| 	@Override | ||||
| 	public void createTables(final String tableName, final Field field, final StringBuilder mainTableBuilder, final List<String> preActionList, final List<String> postActionList, | ||||
| 			final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception { | ||||
| 	public void createTables(// | ||||
| 			final String tableName, // | ||||
| 			final Field primaryField, // | ||||
| 			final Field field, // | ||||
| 			final StringBuilder mainTableBuilder, // | ||||
| 			final List<String> preActionList, // | ||||
| 			final List<String> postActionList, // | ||||
| 			final boolean createIfNotExist, // | ||||
| 			final boolean createDrop, // | ||||
| 			final int fieldId // | ||||
| 	) throws Exception { | ||||
| 		final Class<?> classType = field.getType(); | ||||
| 		if (classType == Long.class // | ||||
| 				|| classType == Integer.class // | ||||
| 				|| classType == Short.class // | ||||
| 				|| classType == String.class // | ||||
| 				|| classType == UUID.class) { | ||||
| 			DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, classType); | ||||
| 		} else { | ||||
| 			LOGGER.error("Support only the Long remote field of ecternal primary keys..."); | ||||
| 			DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, Long.class); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| @@ -120,8 +125,17 @@ public class AddOnOneToMany implements DataAccessAddOn { | ||||
|  | ||||
| 	// TODO : refacto this table to manage a generic table with dynamic name to be serializable with the default system | ||||
| 	@Override | ||||
| 	public void createTables(final String tableName, final Field field, final StringBuilder mainTableBuilder, final List<String> preActionList, final List<String> postActionList, | ||||
| 			final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception { | ||||
| 	public void createTables(// | ||||
| 			final String tableName, // | ||||
| 			final Field primaryField, // | ||||
| 			final Field field, // | ||||
| 			final StringBuilder mainTableBuilder, // | ||||
| 			final List<String> preActionList, // | ||||
| 			final List<String> postActionList, // | ||||
| 			final boolean createIfNotExist, // | ||||
| 			final boolean createDrop, // | ||||
| 			final int fieldId // | ||||
| 	) throws Exception { | ||||
| 		DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, Long.class); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -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 = "-"; | ||||
| @@ -72,12 +73,17 @@ public class AddOnSQLTableExternalForeinKeyAsList implements DataAccessAddOn { | ||||
|  | ||||
| 	@Override | ||||
| 	public boolean canInsert(final Field field) { | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public boolean isInsertAsync(final Field field) throws Exception { | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public boolean canRetrieve(final Field field) { | ||||
| 		return false; | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| @@ -99,8 +105,17 @@ public class AddOnSQLTableExternalForeinKeyAsList implements DataAccessAddOn { | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public void createTables(final String tableName, final Field field, final StringBuilder mainTableBuilder, final List<String> preActionList, final List<String> postActionList, | ||||
| 			final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception { | ||||
| 	public void createTables(// | ||||
| 			final String tableName, // | ||||
| 			final Field primaryField, // | ||||
| 			final Field field, // | ||||
| 			final StringBuilder mainTableBuilder, // | ||||
| 			final List<String> preActionList, // | ||||
| 			final List<String> postActionList, // | ||||
| 			final boolean createIfNotExist, // | ||||
| 			final boolean createDrop, // | ||||
| 			final int fieldId // | ||||
| 	) throws Exception { | ||||
| 		// TODO Auto-generated method stub | ||||
|  | ||||
| 		DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, String.class); | ||||
|   | ||||
| @@ -1,25 +0,0 @@ | ||||
| package org.kar.archidata.dataAccess.addOn.model; | ||||
|  | ||||
| import org.kar.archidata.annotation.DataComment; | ||||
| import org.kar.archidata.model.GenericDataSoftDelete; | ||||
|  | ||||
| import jakarta.persistence.Column; | ||||
|  | ||||
| public class LinkTable extends GenericDataSoftDelete { | ||||
| 	public LinkTable() { | ||||
| 		// nothing to do... | ||||
| 	} | ||||
|  | ||||
| 	public LinkTable(final long object1Id, final long object2Id) { | ||||
| 		this.object1Id = object1Id; | ||||
| 		this.object2Id = object2Id; | ||||
| 	} | ||||
|  | ||||
| 	@DataComment("Object reference 1") | ||||
| 	@Column(nullable = false) | ||||
| 	public Long object1Id; | ||||
| 	@DataComment("Object reference 2") | ||||
| 	@Column(nullable = false) | ||||
| 	public Long object2Id; | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,25 @@ | ||||
| package org.kar.archidata.dataAccess.addOn.model; | ||||
|  | ||||
| import org.kar.archidata.model.GenericData; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import jakarta.persistence.Column; | ||||
|  | ||||
| public class LinkTableLongLong extends GenericData { | ||||
| 	public LinkTableLongLong() { | ||||
| 		// nothing to do... | ||||
| 	} | ||||
|  | ||||
| 	public LinkTableLongLong(final long object1Id, final long object2Id) { | ||||
| 		this.object1Id = object1Id; | ||||
| 		this.object2Id = object2Id; | ||||
| 	} | ||||
|  | ||||
| 	@Schema(description = "Object reference 1") | ||||
| 	@Column(nullable = false) | ||||
| 	public Long object1Id; | ||||
| 	@Schema(description = "Object reference 2") | ||||
| 	@Column(nullable = false) | ||||
| 	public Long object2Id; | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| package org.kar.archidata.dataAccess.addOn.model; | ||||
|  | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.model.GenericData; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import jakarta.persistence.Column; | ||||
|  | ||||
| public class LinkTableLongUUID extends GenericData { | ||||
| 	public LinkTableLongUUID() { | ||||
| 		// nothing to do... | ||||
| 	} | ||||
|  | ||||
| 	public LinkTableLongUUID(final long object1Id, final UUID object2Id) { | ||||
| 		this.object1Id = object1Id; | ||||
| 		this.object2Id = object2Id; | ||||
| 	} | ||||
|  | ||||
| 	@Schema(description = "Object reference 1") | ||||
| 	@Column(nullable = false) | ||||
| 	public Long object1Id; | ||||
| 	@Schema(description = "Object reference 2") | ||||
| 	@Column(nullable = false) | ||||
| 	public UUID object2Id; | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| package org.kar.archidata.dataAccess.addOn.model; | ||||
|  | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.model.GenericData; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import jakarta.persistence.Column; | ||||
|  | ||||
| public class LinkTableUUIDLong extends GenericData { | ||||
| 	public LinkTableUUIDLong() { | ||||
| 		// nothing to do... | ||||
| 	} | ||||
|  | ||||
| 	public LinkTableUUIDLong(final UUID object1Id, final long object2Id) { | ||||
| 		this.object1Id = object1Id; | ||||
| 		this.object2Id = object2Id; | ||||
| 	} | ||||
|  | ||||
| 	@Schema(description = "Object reference 1") | ||||
| 	@Column(nullable = false) | ||||
| 	public UUID object1Id; | ||||
| 	@Schema(description = "Object reference 2") | ||||
| 	@Column(nullable = false) | ||||
| 	public Long object2Id; | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| package org.kar.archidata.dataAccess.addOn.model; | ||||
|  | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.model.GenericData; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import jakarta.persistence.Column; | ||||
|  | ||||
| public class LinkTableUUIDUUID extends GenericData { | ||||
| 	public LinkTableUUIDUUID() { | ||||
| 		// nothing to do... | ||||
| 	} | ||||
|  | ||||
| 	public LinkTableUUIDUUID(final UUID object1Id, final UUID object2Id) { | ||||
| 		this.object1Id = object1Id; | ||||
| 		this.object2Id = object2Id; | ||||
| 	} | ||||
|  | ||||
| 	@Schema(description = "Object reference 1") | ||||
| 	@Column(nullable = false) | ||||
| 	public UUID object1Id; | ||||
| 	@Schema(description = "Object reference 2") | ||||
| 	@Column(nullable = false) | ||||
| 	public UUID object2Id; | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,28 @@ | ||||
| package org.kar.archidata.dataAccess.addOn.model; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import org.kar.archidata.annotation.DataJson; | ||||
| import org.kar.archidata.model.GenericData; | ||||
|  | ||||
| import jakarta.persistence.Column; | ||||
| import jakarta.persistence.Id; | ||||
|  | ||||
| public class TableCoversLongLong extends GenericData { | ||||
| 	public TableCoversLongLong() { | ||||
| 		// nothing to do... | ||||
| 	} | ||||
|  | ||||
| 	public TableCoversLongLong(final Long id, final List<Long> covers) { | ||||
| 		this.id = id; | ||||
| 		this.covers = covers; | ||||
| 	} | ||||
|  | ||||
| 	@Column(nullable = false) | ||||
| 	@Id | ||||
| 	public Long id; | ||||
| 	@DataJson() | ||||
| 	@Column(nullable = false) | ||||
| 	public List<Long> covers; | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,29 @@ | ||||
| package org.kar.archidata.dataAccess.addOn.model; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.annotation.DataJson; | ||||
| import org.kar.archidata.model.GenericData; | ||||
|  | ||||
| import jakarta.persistence.Column; | ||||
| import jakarta.persistence.Id; | ||||
|  | ||||
| public class TableCoversLongUUID extends GenericData { | ||||
| 	public TableCoversLongUUID() { | ||||
| 		// nothing to do... | ||||
| 	} | ||||
|  | ||||
| 	public TableCoversLongUUID(final Long id, final List<UUID> covers) { | ||||
| 		this.id = id; | ||||
| 		this.covers = covers; | ||||
| 	} | ||||
|  | ||||
| 	@Column(nullable = false) | ||||
| 	@Id | ||||
| 	public Long id; | ||||
| 	@DataJson() | ||||
| 	@Column(nullable = false) | ||||
| 	public List<UUID> covers; | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,29 @@ | ||||
| package org.kar.archidata.dataAccess.addOn.model; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.annotation.DataJson; | ||||
| import org.kar.archidata.model.GenericData; | ||||
|  | ||||
| import jakarta.persistence.Column; | ||||
| import jakarta.persistence.Id; | ||||
|  | ||||
| public class TableCoversUUIDLong extends GenericData { | ||||
| 	public TableCoversUUIDLong() { | ||||
| 		// nothing to do... | ||||
| 	} | ||||
|  | ||||
| 	public TableCoversUUIDLong(final UUID id, final List<Long> covers) { | ||||
| 		this.id = id; | ||||
| 		this.covers = covers; | ||||
| 	} | ||||
|  | ||||
| 	@Column(nullable = false) | ||||
| 	@Id | ||||
| 	public UUID id; | ||||
| 	@DataJson() | ||||
| 	@Column(nullable = false) | ||||
| 	public List<Long> covers; | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,29 @@ | ||||
| package org.kar.archidata.dataAccess.addOn.model; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.annotation.DataJson; | ||||
| import org.kar.archidata.model.GenericData; | ||||
|  | ||||
| import jakarta.persistence.Column; | ||||
| import jakarta.persistence.Id; | ||||
|  | ||||
| public class TableCoversUUIDUUID extends GenericData { | ||||
| 	public TableCoversUUIDUUID() { | ||||
| 		// nothing to do... | ||||
| 	} | ||||
|  | ||||
| 	public TableCoversUUIDUUID(final UUID id, final List<UUID> covers) { | ||||
| 		this.id = id; | ||||
| 		this.covers = covers; | ||||
| 	} | ||||
|  | ||||
| 	@Column(nullable = false) | ||||
| 	@Id | ||||
| 	public UUID id; | ||||
| 	@DataJson() | ||||
| 	@Column(nullable = false) | ||||
| 	public List<UUID> covers; | ||||
|  | ||||
| } | ||||
							
								
								
									
										14
									
								
								src/org/kar/archidata/dataAccess/exportTools/TableQuery.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/org/kar/archidata/dataAccess/exportTools/TableQuery.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| package org.kar.archidata.dataAccess.exportTools; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| public class TableQuery { | ||||
|  | ||||
| 	public final List<TableQueryTypes> headers; | ||||
| 	public final List<List<Object>> values = new ArrayList<>(); | ||||
|  | ||||
| 	public TableQuery(final List<TableQueryTypes> headers) { | ||||
| 		this.headers = headers; | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,20 @@ | ||||
| package org.kar.archidata.dataAccess.exportTools; | ||||
|  | ||||
| public class TableQueryTypes { | ||||
|  | ||||
| 	public Class<?> type; | ||||
| 	public String name; | ||||
| 	public String title; | ||||
|  | ||||
| 	public TableQueryTypes(final Class<?> type, final String name, final String title) { | ||||
| 		this.type = type; | ||||
| 		this.name = name; | ||||
| 		this.title = title; | ||||
| 	} | ||||
|  | ||||
| 	public TableQueryTypes(final Class<?> type, final String name) { | ||||
| 		this.type = type; | ||||
| 		this.name = name; | ||||
| 		this.title = name; | ||||
| 	} | ||||
| } | ||||
| @@ -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())); | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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) { | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										349
									
								
								src/org/kar/archidata/dataAccess/options/CheckJPA.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										349
									
								
								src/org/kar/archidata/dataAccess/options/CheckJPA.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,349 @@ | ||||
| package org.kar.archidata.dataAccess.options; | ||||
|  | ||||
| import java.lang.reflect.Field; | ||||
| import java.sql.Timestamp; | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalTime; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.UUID; | ||||
| import java.util.regex.Pattern; | ||||
|  | ||||
| import org.kar.archidata.annotation.AnnotationTools; | ||||
| import org.kar.archidata.annotation.DataJson; | ||||
| import org.kar.archidata.dataAccess.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 == UUID.class) { | ||||
| 					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 ... | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										62
									
								
								src/org/kar/archidata/dataAccess/options/Condition.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/org/kar/archidata/dataAccess/options/Condition.java
									
									
									
									
									
										Normal 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 \n"); | ||||
| 			} | ||||
| 			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 "); | ||||
| 		} | ||||
| 		query.append("\n"); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,48 @@ | ||||
| package org.kar.archidata.dataAccess.options; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| import org.kar.archidata.GlobalConfiguration; | ||||
| import org.kar.archidata.dataAccess.QueryOption; | ||||
| import org.kar.archidata.dataAccess.QueryOptions; | ||||
| import org.kar.archidata.db.DBConfig; | ||||
| import org.kar.archidata.db.DBEntry; | ||||
|  | ||||
| public class DBInterfaceOption extends QueryOption { | ||||
| 	private DBEntry entry = null; | ||||
| 	private final DBConfig config; | ||||
| 	private final boolean root; | ||||
|  | ||||
| 	public DBInterfaceOption(final DBConfig config) { | ||||
| 		this.config = config; | ||||
| 		this.root = false; | ||||
| 	} | ||||
|  | ||||
| 	public DBInterfaceOption(final DBConfig config, boolean root) { | ||||
| 		this.config = config; | ||||
| 		this.root = root; | ||||
| 	} | ||||
|  | ||||
| 	public DBEntry getEntry(QueryOptions options) throws IOException { | ||||
| 		if (this.entry == null) { | ||||
| 			final DBInterfaceRoot isRoot = options.get(DBInterfaceRoot.class); | ||||
| 			this.entry = DBEntry.createInterface(this.config, isRoot != null && isRoot.getRoot()); | ||||
| 		} | ||||
| 		return this.entry; | ||||
| 	} | ||||
|  | ||||
| 	public boolean getRoot() { | ||||
| 		return this.root; | ||||
| 	} | ||||
|  | ||||
| 	public static DBEntry getAutoEntry(QueryOptions options) throws IOException { | ||||
| 		final DBInterfaceOption dbOption = options.get(DBInterfaceOption.class); | ||||
| 		if (dbOption == null) { | ||||
| 			final DBInterfaceRoot isRoot = options.get(DBInterfaceRoot.class); | ||||
| 			return DBEntry.createInterface(GlobalConfiguration.dbConfig, isRoot != null && isRoot.getRoot()); | ||||
| 		} else { | ||||
| 			return dbOption.getEntry(options); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,16 @@ | ||||
| package org.kar.archidata.dataAccess.options; | ||||
|  | ||||
| import org.kar.archidata.dataAccess.QueryOption; | ||||
|  | ||||
| public class DBInterfaceRoot extends QueryOption { | ||||
| 	private final boolean root; | ||||
|  | ||||
| 	public DBInterfaceRoot(boolean root) { | ||||
| 		this.root = root; | ||||
| 	} | ||||
|  | ||||
| 	public boolean getRoot() { | ||||
| 		return this.root; | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										22
									
								
								src/org/kar/archidata/dataAccess/options/FilterValue.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/org/kar/archidata/dataAccess/options/FilterValue.java
									
									
									
									
									
										Normal 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; | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										16
									
								
								src/org/kar/archidata/dataAccess/options/TransmitKey.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/org/kar/archidata/dataAccess/options/TransmitKey.java
									
									
									
									
									
										Normal 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; | ||||
| 	} | ||||
| } | ||||
| @@ -52,7 +52,7 @@ public class DBEntry implements Closeable { | ||||
| 		try { | ||||
| 			this.connection = DriverManager.getConnection(this.config.getUrl(true), this.config.getLogin(), this.config.getPassword()); | ||||
| 		} catch (final SQLException ex) { | ||||
| 			throw new IOException("Connection db fail: " + ex.getMessage()); | ||||
| 			throw new IOException("Connection db fail: " + ex.getMessage() + " On URL: " + this.config.getUrl(true)); | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| @@ -61,7 +61,7 @@ public class DBEntry implements Closeable { | ||||
| 		try { | ||||
| 			this.connection = DriverManager.getConnection(this.config.getUrl(), this.config.getLogin(), this.config.getPassword()); | ||||
| 		} catch (final SQLException ex) { | ||||
| 			throw new IOException("Connection db fail: " + ex.getMessage()); | ||||
| 			throw new IOException("Connection db fail: " + ex.getMessage() + " On URL: " + this.config.getUrl(true)); | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import java.util.UUID; | ||||
| public class RESTErrorResponseExeption extends Exception { | ||||
| 	public UUID uuid; | ||||
| 	public String time; | ||||
| 	public String error; | ||||
| 	public String name; | ||||
| 	public String message; | ||||
| 	public int status; | ||||
| 	public String statusMessage; | ||||
| @@ -13,16 +13,16 @@ public class RESTErrorResponseExeption extends Exception { | ||||
| 	public RESTErrorResponseExeption() { | ||||
| 		this.uuid = null; | ||||
| 		this.time = null; | ||||
| 		this.error = null; | ||||
| 		this.name = null; | ||||
| 		this.message = null; | ||||
| 		this.status = 0; | ||||
| 		this.statusMessage = null; | ||||
| 	} | ||||
|  | ||||
| 	public RESTErrorResponseExeption(final UUID uuid, final String time, final String error, final String message, final int status, final String statusMessage) { | ||||
| 	public RESTErrorResponseExeption(final UUID uuid, final String time, final String name, final String message, final int status, final String statusMessage) { | ||||
| 		this.uuid = uuid; | ||||
| 		this.time = time; | ||||
| 		this.error = error; | ||||
| 		this.name = name; | ||||
| 		this.message = message; | ||||
| 		this.status = status; | ||||
| 		this.statusMessage = statusMessage; | ||||
| @@ -30,7 +30,7 @@ public class RESTErrorResponseExeption extends Exception { | ||||
|  | ||||
| 	@Override | ||||
| 	public String toString() { | ||||
| 		return "RESTErrorResponseExeption [uuid=" + this.uuid + ", time=" + this.time + ", error=" + this.error + ", message=" + this.message + ", status=" + this.status + ", statusMessage=" | ||||
| 		return "RESTErrorResponseExeption [uuid=" + this.uuid + ", time=" + this.time + ", name=" + this.name + ", message=" + this.message + ", status=" + this.status + ", statusMessage=" | ||||
| 				+ this.statusMessage + "]"; | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -38,13 +38,13 @@ 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; | ||||
|  | ||||
| 	private static final String AUTHENTICATION_SCHEME = "Yota"; | ||||
| 	private static final String AUTHENTICATION_TOKEN_SCHEME = "Zota"; | ||||
| 	public static final String AUTHENTICATION_SCHEME = "Bearer"; | ||||
| 	public static final String APIKEY = "ApiKey"; | ||||
|  | ||||
| 	public AuthenticationFilter(final String applicationName) { | ||||
| 		this.applicationName = applicationName; | ||||
| @@ -57,8 +57,8 @@ 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()); | ||||
| 			requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access blocked !!!").build()); | ||||
| 			LOGGER.debug("   ==> deny all {}", requestContext.getUriInfo().getPath()); | ||||
| 			abortWithForbidden(requestContext, "Access blocked !!!"); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| @@ -70,64 +70,62 @@ 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()); | ||||
| 			requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access ILLEGAL !!!").build()); | ||||
| 			LOGGER.error("   ==> missing @RolesAllowed {}", requestContext.getUriInfo().getPath()); | ||||
| 			abortWithForbidden(requestContext, "Access ILLEGAL !!!"); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		// Get the Authorization header from the request | ||||
| 		String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION); | ||||
| 		String apikeyHeader = requestContext.getHeaderString(APIKEY); | ||||
| 		// logger.debug("authorizationHeader: {}", authorizationHeader); | ||||
| 		if (authorizationHeader == null && method.isAnnotationPresent(PermitTokenInURI.class)) { | ||||
| 		if (authorizationHeader == null && apikeyHeader == null && method.isAnnotationPresent(PermitTokenInURI.class)) { | ||||
| 			final MultivaluedMap<String, String> quaryparam = requestContext.getUriInfo().getQueryParameters(); | ||||
| 			for (final Entry<String, List<String>> item : quaryparam.entrySet()) { | ||||
| 				if (item.getKey().equals(HttpHeaders.AUTHORIZATION)) { | ||||
| 					if (!item.getValue().isEmpty()) { | ||||
| 				if ((authorizationHeader == null && HttpHeaders.AUTHORIZATION.equals(item.getKey())) && !item.getValue().isEmpty()) { | ||||
| 					authorizationHeader = item.getValue().get(0); | ||||
| 				} | ||||
| 					break; | ||||
| 				if ((apikeyHeader == null && APIKEY.equals(item.getKey())) && !item.getValue().isEmpty()) { | ||||
| 					apikeyHeader = item.getValue().get(0); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		// logger.debug("authorizationHeader: {}", authorizationHeader); | ||||
| 		final boolean isApplicationToken = isApplicationTokenBasedAuthentication(authorizationHeader); | ||||
| 		final boolean isApplicationToken = apikeyHeader != null; | ||||
| 		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; | ||||
| 		} | ||||
| 		UserByToken userByToken = null; | ||||
| 		if (isJwtToken) { | ||||
| 			// Extract the token from the Authorization header (Remove "Yota ") | ||||
| 			// Extract the token from the Authorization header (Remove "Bearer ") | ||||
| 			final String token = authorizationHeader.substring(AUTHENTICATION_SCHEME.length()).trim(); | ||||
| 			// logger.debug("token: {}", token); | ||||
| 			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; | ||||
| 			} | ||||
| 		} else { | ||||
| 			// Extract the token from the Authorization header (Remove "Zota ") | ||||
| 			final String token = authorizationHeader.substring(AUTHENTICATION_TOKEN_SCHEME.length()).trim(); | ||||
| 			// logger.debug("token: {}", token); | ||||
| 			final String token = apikeyHeader.trim(); | ||||
| 			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 +147,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; | ||||
| 		} | ||||
| @@ -164,26 +162,25 @@ public class AuthenticationFilter implements ContainerRequestFilter { | ||||
| 		return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " "); | ||||
| 	} | ||||
|  | ||||
| 	private boolean isApplicationTokenBasedAuthentication(final String authorizationHeader) { | ||||
| 		// Check if the Authorization header is valid | ||||
| 		// It must not be null and must be prefixed with "Bearer" plus a whitespace | ||||
| 		// The authentication scheme comparison must be case-insensitive | ||||
| 		return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_TOKEN_SCHEME.toLowerCase() + " "); | ||||
| 	} | ||||
|  | ||||
| 	private void abortWithUnauthorized(final ContainerRequestContext requestContext, final String message) { | ||||
|  | ||||
| 		// 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()); | ||||
| 	} | ||||
|  | ||||
| 	private void abortWithForbidden(final ContainerRequestContext requestContext, final String message) { | ||||
| 		final RestErrorResponse ret = new RestErrorResponse(Response.Status.FORBIDDEN, "FORBIDDEN", message); | ||||
| 		LOGGER.error("Error UUID={}", ret.uuid); | ||||
| 		requestContext.abortWith(Response.status(ret.status).header(HttpHeaders.WWW_AUTHENTICATE, message).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 +190,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 +206,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); | ||||
|   | ||||
| @@ -15,9 +15,8 @@ public class CORSFilter implements ContainerResponseFilter { | ||||
| 		// System.err.println("filter cors ..." + request.toString()); | ||||
|  | ||||
| 		response.getHeaders().add("Access-Control-Allow-Origin", "*"); | ||||
| 		response.getHeaders().add("Access-Control-Allow-Headers", "*"); | ||||
| 		// "Origin, content-type, Content-type, Accept, authorization, mime-type, filename"); | ||||
| 		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"); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -35,13 +35,13 @@ class MySecurityContext implements SecurityContext { | ||||
|  | ||||
| 	@Override | ||||
| 	public boolean isSecure() { | ||||
| 		return this.sheme.equalsIgnoreCase("https"); | ||||
| 		return "https".equalsIgnoreCase(this.sheme); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public String getAuthenticationScheme() { | ||||
| 		if (this.contextPrincipale.userByToken != null) { | ||||
| 			return "Zota"; | ||||
| 			return "Bearer"; | ||||
| 		} | ||||
| 		return null; | ||||
| 	} | ||||
|   | ||||
| @@ -2,6 +2,9 @@ package org.kar.archidata.filter; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import jakarta.ws.rs.container.ContainerRequestContext; | ||||
| import jakarta.ws.rs.container.ContainerRequestFilter; | ||||
| import jakarta.ws.rs.container.PreMatching; | ||||
| @@ -11,8 +14,11 @@ import jakarta.ws.rs.ext.Provider; | ||||
| @Provider | ||||
| @PreMatching | ||||
| public class OptionFilter implements ContainerRequestFilter { | ||||
| 	private static final Logger LOGGER = LoggerFactory.getLogger(OptionFilter.class); | ||||
|  | ||||
| 	@Override | ||||
| 	public void filter(final ContainerRequestContext requestContext) throws IOException { | ||||
| 		LOGGER.warn("Receive message from: [{}] {}", requestContext.getMethod(), requestContext.getUriInfo().getPath()); | ||||
| 		if (requestContext.getMethod().contentEquals("OPTIONS")) { | ||||
| 			requestContext.abortWith(Response.status(Response.Status.NO_CONTENT).build()); | ||||
| 		} | ||||
|   | ||||
							
								
								
									
										5
									
								
								src/org/kar/archidata/migration/AsyncCall.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/org/kar/archidata/migration/AsyncCall.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| package org.kar.archidata.migration; | ||||
|  | ||||
| public interface AsyncCall { | ||||
| 	void doRequest() throws Exception; | ||||
| } | ||||
| @@ -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); | ||||
|   | ||||
| @@ -13,13 +13,21 @@ import org.kar.archidata.tools.ConfigBaseVariable; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| record Action(String action, List<String> filterDB) { | ||||
| record Action(String action, AsyncCall async, List<String> filterDB) { | ||||
| 	public Action(final String action) { | ||||
| 		this(action, List.of()); | ||||
| 		this(action, null, List.of()); | ||||
| 	} | ||||
|  | ||||
| 	public Action(final String action, final String filterDB) { | ||||
| 		this(action, List.of(filterDB)); | ||||
| 		this(action, null, List.of(filterDB)); | ||||
| 	} | ||||
|  | ||||
| 	public Action(final AsyncCall async) { | ||||
| 		this(null, async, List.of()); | ||||
| 	} | ||||
|  | ||||
| 	public Action(final AsyncCall async, final String filterDB) { | ||||
| 		this(null, async, List.of(filterDB)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -40,7 +48,11 @@ public class MigrationSqlStep implements MigrationInterface { | ||||
| 		} | ||||
| 		for (int iii = 0; iii < this.actions.size(); iii++) { | ||||
| 			final Action action = this.actions.get(iii); | ||||
| 			if (action.action() != null) { | ||||
| 				LOGGER.info(" >>>> SQL ACTION : {}/{} ==> filter='{}'\n{}", iii, this.actions.size(), action.filterDB(), action.action()); | ||||
| 			} else { | ||||
| 				LOGGER.info(" >>>> SQL ACTION : {}/{} ==> filter='{}'\nAsync lambda", iii, this.actions.size(), action.filterDB()); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -63,8 +75,13 @@ public class MigrationSqlStep implements MigrationInterface { | ||||
| 			LOGGER.info(" >>>> SQL ACTION : {}/{}", iii + 1, this.actions.size()); | ||||
| 			final Action action = this.actions.get(iii); | ||||
|  | ||||
| 			if (action.action() != null) { | ||||
| 				LOGGER.info("SQL request: ```{}``` on '{}' current={}", action.action(), action.filterDB(), ConfigBaseVariable.getDBType()); | ||||
| 				log.append("SQL: " + action.action() + " on " + action.filterDB() + "\n"); | ||||
| 			} else { | ||||
| 				LOGGER.info("SQL request: <Lambda> on '{}' current={}", action.filterDB(), ConfigBaseVariable.getDBType()); | ||||
| 				log.append("SQL: <Lambda> on " + action.filterDB() + "\n"); | ||||
| 			} | ||||
| 			boolean isValid = true; | ||||
| 			if (action.filterDB() != null && action.filterDB().size() > 0) { | ||||
| 				isValid = false; | ||||
| @@ -80,7 +97,11 @@ public class MigrationSqlStep implements MigrationInterface { | ||||
| 				continue; | ||||
| 			} | ||||
| 			try { | ||||
| 				if (action.action() != null) { | ||||
| 					DataAccess.executeQuerry(action.action()); | ||||
| 				} else { | ||||
| 					action.async().doRequest(); | ||||
| 				} | ||||
| 			} catch (SQLException | IOException ex) { | ||||
| 				ex.printStackTrace(); | ||||
| 				LOGGER.info("SQL request ERROR: ", ex.getMessage()); | ||||
| @@ -105,9 +126,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(); | ||||
| 			} | ||||
| 		} | ||||
| @@ -124,10 +144,18 @@ public class MigrationSqlStep implements MigrationInterface { | ||||
| 		this.actions.add(new Action(action)); | ||||
| 	} | ||||
|  | ||||
| 	public void addAction(final AsyncCall async) { | ||||
| 		this.actions.add(new Action(async)); | ||||
| 	} | ||||
|  | ||||
| 	public void addAction(final String action, final String filterdBType) { | ||||
| 		this.actions.add(new Action(action, filterdBType)); | ||||
| 	} | ||||
|  | ||||
| 	public void addAction(final AsyncCall async, final String filterdBType) { | ||||
| 		this.actions.add(new Action(async, filterdBType)); | ||||
| 	} | ||||
|  | ||||
| 	public void addClass(final Class<?> clazz) throws Exception { | ||||
| 		final List<String> tmp = DataFactory.createTable(clazz); | ||||
| 		for (final String elem : tmp) { | ||||
|   | ||||
| @@ -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; | ||||
| import jakarta.ws.rs.DefaultValue; | ||||
|  | ||||
| // 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 | ||||
| 	@DefaultValue("'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") | ||||
| 	@DefaultValue("'0'") | ||||
| 	@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 = ""; | ||||
| } | ||||
|   | ||||
							
								
								
									
										35
									
								
								src/org/kar/archidata/migration/model/Migration1.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/org/kar/archidata/migration/model/Migration1.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| package org.kar.archidata.migration.model; | ||||
|  | ||||
| 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; | ||||
| import jakarta.ws.rs.DefaultValue; | ||||
|  | ||||
| // 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) | ||||
| 	@DefaultValue("'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 = ""; | ||||
| } | ||||
| @@ -1,25 +1,24 @@ | ||||
| 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; | ||||
|  | ||||
| @Table(name = "data") | ||||
| @DataIfNotExists | ||||
| @JsonInclude(JsonInclude.Include.NON_NULL) | ||||
| public class Data extends GenericDataSoftDelete { | ||||
|  | ||||
| public class Data extends UUIDGenericDataSoftDelete { | ||||
| 	@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; | ||||
| } | ||||
|   | ||||
| @@ -1,35 +1,15 @@ | ||||
| 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; | ||||
|  | ||||
| public class GenericData { | ||||
| public class GenericData extends GenericTiming { | ||||
| 	@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") | ||||
| 	public Date createdAt = null; | ||||
| 	@DataNotRead | ||||
| 	@UpdateTimestamp | ||||
| 	@Column(nullable = false) | ||||
| 	@Temporal(TemporalType.TIMESTAMP) | ||||
| 	@DataComment("When update the object") | ||||
| 	public Date updatedAt = null; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| import jakarta.ws.rs.DefaultValue; | ||||
|  | ||||
| public class GenericDataSoftDelete extends GenericData { | ||||
| 	@DataNotRead | ||||
| 	@Column(nullable = false) | ||||
| 	@DataDefault("'0'") | ||||
| 	@DefaultValue("'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; | ||||
| } | ||||
|   | ||||
							
								
								
									
										31
									
								
								src/org/kar/archidata/model/GenericTiming.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/org/kar/archidata/model/GenericTiming.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| package org.kar.archidata.model; | ||||
|  | ||||
| import java.util.Date; | ||||
|  | ||||
| import org.kar.archidata.annotation.CreationTimestamp; | ||||
| import org.kar.archidata.annotation.DataNotRead; | ||||
| import org.kar.archidata.annotation.UpdateTimestamp; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonFormat; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import jakarta.persistence.Column; | ||||
| import jakarta.validation.constraints.NotNull; | ||||
|  | ||||
| public class GenericTiming { | ||||
| 	@DataNotRead | ||||
| 	@CreationTimestamp | ||||
| 	@Column(nullable = false) | ||||
| 	@NotNull | ||||
| 	@Schema(description = "Create time of the object", required = false, example = "2000-01-23T01:23:45.678+01:00", readOnly = true) | ||||
| 	@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX") | ||||
| 	public Date createdAt = null; | ||||
| 	@DataNotRead | ||||
| 	@UpdateTimestamp | ||||
| 	@Column(nullable = false) | ||||
| 	@NotNull | ||||
| 	@Schema(description = "When update the object", required = false, example = "2000-01-23T00:23:45.678Z", readOnly = true) | ||||
| 	@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX") | ||||
| 	// public Instant updatedAt = null; | ||||
| 	public Date updatedAt = null; | ||||
| } | ||||
							
								
								
									
										16
									
								
								src/org/kar/archidata/model/UUIDGenericData.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/org/kar/archidata/model/UUIDGenericData.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| package org.kar.archidata.model; | ||||
|  | ||||
| import java.util.UUID; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
| import jakarta.persistence.Column; | ||||
| import jakarta.persistence.Id; | ||||
| import jakarta.ws.rs.DefaultValue; | ||||
|  | ||||
| public class UUIDGenericData extends GenericTiming { | ||||
| 	@Id | ||||
| 	@DefaultValue("(UUID_TO_BIN(UUID(), TRUE))") | ||||
| 	@Column(nullable = false, unique = true) | ||||
| 	@Schema(description = "Unique UUID of the object", required = false, readOnly = true, example = "e6b33c1c-d24d-11ee-b616-02420a030102") | ||||
| 	public UUID id = null; | ||||
| } | ||||
							
								
								
									
										19
									
								
								src/org/kar/archidata/model/UUIDGenericDataSoftDelete.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/org/kar/archidata/model/UUIDGenericDataSoftDelete.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| package org.kar.archidata.model; | ||||
|  | ||||
| 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; | ||||
| import jakarta.ws.rs.DefaultValue; | ||||
|  | ||||
| public class UUIDGenericDataSoftDelete extends UUIDGenericData { | ||||
| 	@DataNotRead | ||||
| 	@Column(nullable = false) | ||||
| 	@DefaultValue("'0'") | ||||
| 	@DataDeleted | ||||
| 	@NotNull | ||||
| 	@Schema(description = "Deleted state", hidden = true, required = false, readOnly = true) | ||||
| 	public Boolean deleted = null; | ||||
| } | ||||
| @@ -17,7 +17,6 @@ CREATE TABLE `user` ( | ||||
| import java.sql.Timestamp; | ||||
| import java.util.List; | ||||
|  | ||||
| import org.kar.archidata.annotation.DataDefault; | ||||
| import org.kar.archidata.annotation.DataIfNotExists; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||
| @@ -26,6 +25,7 @@ import jakarta.persistence.Column; | ||||
| import jakarta.persistence.FetchType; | ||||
| import jakarta.persistence.ManyToMany; | ||||
| import jakarta.persistence.Table; | ||||
| import jakarta.ws.rs.DefaultValue; | ||||
|  | ||||
| @Table(name = "user") | ||||
| @DataIfNotExists | ||||
| @@ -35,13 +35,13 @@ public class User extends GenericDataSoftDelete { | ||||
| 	public String login = null; | ||||
|  | ||||
| 	public Timestamp lastConnection = null; | ||||
| 	@DataDefault("'0'") | ||||
| 	@DefaultValue("'0'") | ||||
| 	@Column(nullable = false) | ||||
| 	public boolean admin = false; | ||||
| 	@DataDefault("'0'") | ||||
| 	@DefaultValue("'0'") | ||||
| 	@Column(nullable = false) | ||||
| 	public boolean blocked = false; | ||||
| 	@DataDefault("'0'") | ||||
| 	@DefaultValue("'0'") | ||||
| 	@Column(nullable = false) | ||||
| 	public boolean removed = false; | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,9 @@ public class ConfigBaseVariable { | ||||
| 	static public String ssoAdress; | ||||
| 	static public String ssoToken; | ||||
| 	static public String testMode; | ||||
| 	static public String eMailFrom; | ||||
| 	static public String eMailLogin; | ||||
| 	static public String eMailPassword; | ||||
|  | ||||
| 	// For test only | ||||
| 	public static void clearAllValue() { | ||||
| @@ -30,6 +33,9 @@ public class ConfigBaseVariable { | ||||
| 		ssoAdress = System.getenv("SSO_ADDRESS"); | ||||
| 		ssoToken = System.getenv("SSO_TOKEN"); | ||||
| 		testMode = System.getenv("TEST_MODE"); | ||||
| 		eMailFrom = System.getenv("EMAIL_FROM"); | ||||
| 		eMailLogin = System.getenv("EMAIL_LOGIN"); | ||||
| 		eMailPassword = System.getenv("EMAIL_PASSWORD"); | ||||
| 	} | ||||
|  | ||||
| 	static { | ||||
| @@ -120,4 +126,15 @@ public class ConfigBaseVariable { | ||||
| 		} | ||||
| 		return Boolean.parseBoolean(testMode); | ||||
| 	} | ||||
|  | ||||
| 	public record EMailConfig(String from, String login, String password) { | ||||
| 	}; | ||||
|  | ||||
| 	public static EMailConfig getEMailConfig() { | ||||
| 		if (eMailFrom == null || eMailLogin == null || eMailPassword == null) { | ||||
| 			return null; | ||||
| 		} | ||||
| 		return new EMailConfig(eMailFrom, eMailLogin, eMailPassword); | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -12,12 +12,16 @@ import java.security.MessageDigest; | ||||
| import java.security.NoSuchAlgorithmException; | ||||
| import java.sql.SQLException; | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.glassfish.jersey.media.multipart.FormDataContentDisposition; | ||||
| import org.kar.archidata.api.DataResource; | ||||
| 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.addOn.AddOnDataJson; | ||||
| import org.kar.archidata.dataAccess.options.Condition; | ||||
| import org.kar.archidata.dataAccess.options.ReadAllColumn; | ||||
| import org.kar.archidata.model.Data; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| @@ -63,19 +67,9 @@ public class DataTools { | ||||
| 		return filePath; | ||||
| 	} | ||||
|  | ||||
| 	public static String getFileData(final long tmpFolderId) { | ||||
| 		final String filePath = ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator + "data"; | ||||
| 		try { | ||||
| 			createFolder(ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator); | ||||
| 		} catch (final IOException e) { | ||||
| 			e.printStackTrace(); | ||||
| 		} | ||||
| 		return filePath; | ||||
| 	} | ||||
|  | ||||
| 	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)), new ReadAllColumn()); | ||||
| 		} catch (final Exception e) { | ||||
| 			// TODO Auto-generated catch block | ||||
| 			e.printStackTrace(); | ||||
| @@ -85,7 +79,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,22 +103,19 @@ 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(); | ||||
| 			return null; | ||||
| 		} | ||||
|  | ||||
| 		final String mediaPath = getFileData(out.id); | ||||
| 		final String mediaPath = DataResource.getFileData(out.id); | ||||
| 		LOGGER.info("src = {}", tmpPath); | ||||
| 		LOGGER.info("dst = {}", mediaPath); | ||||
| 		Files.move(Paths.get(tmpPath), Paths.get(mediaPath), StandardCopyOption.ATOMIC_MOVE); | ||||
| @@ -134,7 +125,7 @@ public class DataTools { | ||||
| 		return out; | ||||
| 	} | ||||
|  | ||||
| 	public static void undelete(final Long id) { | ||||
| 	public static void undelete(final UUID id) { | ||||
| 		try { | ||||
| 			DataAccess.unsetDelete(Data.class, id); | ||||
| 		} catch (final Exception e) { | ||||
| @@ -220,7 +211,8 @@ public class DataTools { | ||||
| 		return data; | ||||
| 	} | ||||
|  | ||||
| 	public static <T> Response uploadCover(final Class<T> clazz, final Long id, String fileName, final InputStream fileInputStream, final FormDataContentDisposition fileMetaData) { | ||||
| 	public static <CLASS_TYPE, ID_TYPE> Response uploadCover(final Class<CLASS_TYPE> clazz, final ID_TYPE id, String fileName, final InputStream fileInputStream, | ||||
| 			final FormDataContentDisposition fileMetaData) { | ||||
| 		try { | ||||
| 			// correct input string stream : | ||||
| 			fileName = multipartCorrection(fileName); | ||||
| @@ -231,7 +223,7 @@ public class DataTools { | ||||
| 			LOGGER.info("    - file_name: ", fileName); | ||||
| 			LOGGER.info("    - fileInputStream: {}", fileInputStream); | ||||
| 			LOGGER.info("    - fileMetaData: {}", fileMetaData); | ||||
| 			final T media = DataAccess.get(clazz, id); | ||||
| 			final CLASS_TYPE media = DataAccess.get(clazz, id); | ||||
| 			if (media == null) { | ||||
| 				return Response.notModified("Media Id does not exist or removed...").build(); | ||||
| 			} | ||||
| @@ -261,7 +253,13 @@ public class DataTools { | ||||
| 			} | ||||
| 			// Fist step: retrieve all the Id of each parents:... | ||||
| 			LOGGER.info("Find typeNode"); | ||||
| 			AddOnManyToMany.addLink(clazz, id, "cover", data.id); | ||||
| 			if (id instanceof final Long idLong) { | ||||
| 				AddOnDataJson.addLink(clazz, idLong, "covers", data.id); | ||||
| 			} else if (id instanceof final UUID idUUID) { | ||||
| 				AddOnDataJson.addLink(clazz, idUUID, "covers", data.id); | ||||
| 			} else { | ||||
| 				throw new IOException("Fail to add Cover can not detect type..."); | ||||
| 			} | ||||
| 			return Response.ok(DataAccess.get(clazz, id)).build(); | ||||
| 		} catch (final Exception ex) { | ||||
| 			System.out.println("Cat ann unexpected error ... "); | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import java.util.Map; | ||||
| import java.util.Set; | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.kar.archidata.filter.AuthenticationFilter; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| @@ -51,20 +52,20 @@ 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(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| public class JWTWrapper { | ||||
| 	static final Logger LOGGER = LoggerFactory.getLogger(JWTWrapper.class); | ||||
|  | ||||
| 	private static RSAKey rsaJWK = null;; | ||||
| 	private static RSAKey rsaJWK = null; | ||||
| 	private static RSAKey rsaPublicJWK = null; | ||||
|  | ||||
| 	public static class PublicKey { | ||||
| @@ -89,7 +90,7 @@ public class JWTWrapper { | ||||
| 		con.setRequestProperty("Accept", "application/json"); | ||||
| 		final String ssoToken = ConfigBaseVariable.ssoToken(); | ||||
| 		if (ssoToken != null) { | ||||
| 			con.setRequestProperty("Authorization", "Zota " + ssoToken); | ||||
| 			con.setRequestProperty(AuthenticationFilter.APIKEY, ssoToken); | ||||
| 		} | ||||
| 		final int responseCode = con.getResponseCode(); | ||||
|  | ||||
| @@ -209,8 +210,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 +230,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; | ||||
| 			} | ||||
| @@ -243,24 +245,22 @@ public class JWTWrapper { | ||||
| 			// LOGGER.debug("JWT token is verified 'alice' =?= '" + signedJWT.getJWTClaimsSet().getSubject() + "'"); | ||||
| 			// LOGGER.debug("JWT token isuer 'https://c2id.com' =?= '" + signedJWT.getJWTClaimsSet().getIssuer() + "'"); | ||||
| 			return signedJWT.getJWTClaimsSet(); | ||||
| 		} catch (final JOSEException ex) { | ||||
| 			ex.printStackTrace(); | ||||
| 		} catch (final ParseException e) { | ||||
| 		} catch (final JOSEException | ParseException e) { | ||||
| 			e.printStackTrace(); | ||||
| 		} | ||||
| 		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 +278,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; | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										31
									
								
								src/org/kar/archidata/tools/ListTools.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/org/kar/archidata/tools/ListTools.java
									
									
									
									
									
										Normal 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; | ||||
| 	} | ||||
| } | ||||
| @@ -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); | ||||
| 			requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Bearer " + 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); | ||||
| 			try { | ||||
| 				final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); | ||||
| 				throw out; | ||||
| 			} 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); | ||||
| 		return modelSend("POST", clazz, urlOffset, data); | ||||
| 	} | ||||
| 		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; | ||||
|  | ||||
| 	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); | ||||
| 		return modelSend("PUT", clazz, urlOffset, data); | ||||
| 	} | ||||
| 		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; | ||||
|  | ||||
| 	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(); | ||||
| 		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); | ||||
| 		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(); | ||||
| 		// 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, "Bearer " + this.token); | ||||
| 		} | ||||
| 		if (body == null) { | ||||
| 			body = ""; | ||||
| 		} else { | ||||
| 			requestBuilding = requestBuilding.header("Content-Type", "application/json"); | ||||
| 		final HttpRequest request = requestBuilding.PUT(BodyPublishers.ofString(body)).build(); | ||||
| 		} | ||||
| 		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); | ||||
| 			try { | ||||
| 				final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); | ||||
| 				throw out; | ||||
| 			} 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); | ||||
| 			requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Bearer " + 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 out; | ||||
| 			} 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, "Bearer " + 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); | ||||
| 			try { | ||||
| 				final RESTErrorResponseExeption out = this.mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); | ||||
| 				throw out; | ||||
| 			} 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; | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										69
									
								
								src/org/kar/archidata/tools/UuidUtils.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/org/kar/archidata/tools/UuidUtils.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| package org.kar.archidata.tools; | ||||
|  | ||||
| import java.math.BigInteger; | ||||
| import java.nio.ByteBuffer; | ||||
| import java.time.Instant; | ||||
| import java.time.LocalDate; | ||||
| import java.time.ZoneOffset; | ||||
| import java.time.temporal.ChronoUnit; | ||||
| import java.util.UUID; | ||||
|  | ||||
| public class UuidUtils { | ||||
|  | ||||
| 	public static UUID asUuid(final BigInteger bigInteger) { | ||||
| 		final long mostSignificantBits = bigInteger.longValue(); | ||||
| 		final long leastSignificantBits = bigInteger.shiftRight(64).longValue(); | ||||
| 		return new UUID(mostSignificantBits, leastSignificantBits); | ||||
| 	} | ||||
|  | ||||
| 	public static UUID asUuid(final byte[] bytes) { | ||||
| 		final ByteBuffer bb = ByteBuffer.wrap(bytes); | ||||
| 		final long firstLong = bb.getLong(); | ||||
| 		final long secondLong = bb.getLong(); | ||||
| 		return new UUID(firstLong, secondLong); | ||||
| 	} | ||||
|  | ||||
| 	public static byte[] asBytes(final UUID uuid) { | ||||
| 		final ByteBuffer bb = ByteBuffer.allocate(16); | ||||
| 		bb.putLong(uuid.getMostSignificantBits()); | ||||
| 		bb.putLong(uuid.getLeastSignificantBits()); | ||||
| 		return bb.array(); | ||||
| 	} | ||||
|  | ||||
| 	private static class Generator { | ||||
| 		private long base; | ||||
| 		private final long offset; | ||||
| 		private long previous; | ||||
|  | ||||
| 		public Generator() { | ||||
| 			this.offset = System.currentTimeMillis(); | ||||
| 			// The local method never generate new UUID in the past, then we use the creation function time to prevent 2038 error | ||||
| 			final Instant startingUUID = LocalDate.of(2024, 03, 19).atStartOfDay(ZoneOffset.UTC).toInstant(); | ||||
| 			this.base = startingUUID.until(Instant.now(), ChronoUnit.SECONDS); | ||||
| 			final String serveurBaseUUID = System.getenv("UUID_SERVER_ID"); | ||||
| 			if (serveurBaseUUID != null) { | ||||
| 				long serverId = Long.valueOf(serveurBaseUUID); | ||||
| 				serverId %= 0xFFFF; | ||||
| 				this.base += (serverId << (64 - 16)); | ||||
| 			} else { | ||||
| 				this.base += (1L << (64 - 16)); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		public synchronized UUID next() { | ||||
| 			long tmp = System.currentTimeMillis(); | ||||
| 			if (this.previous >= tmp) { | ||||
| 				tmp = this.previous + 1; | ||||
| 			} | ||||
| 			this.previous = tmp; | ||||
| 			tmp -= this.offset; | ||||
| 			return new UUID(Long.reverseBytes(tmp), this.base); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	private static Generator generator = new Generator(); | ||||
|  | ||||
| 	public static UUID nextUUID() { | ||||
| 		return generator.next(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										373
									
								
								src/resources/rest-tools.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										373
									
								
								src/resources/rest-tools.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,373 @@ | ||||
| /** @file | ||||
|  * @author Edouard DUPIN | ||||
|  * @copyright 2024, Edouard DUPIN, all right reserved | ||||
|  * @license MPL-2 | ||||
|  */ | ||||
|  | ||||
| import { RestErrorResponse } from "./model" | ||||
|  | ||||
| export enum HTTPRequestModel { | ||||
|     DELETE = 'DELETE', | ||||
|     GET = 'GET', | ||||
|     PATCH = 'PATCH', | ||||
|     POST = 'POST', | ||||
|     PUT = 'PUT', | ||||
| } | ||||
| export enum HTTPMimeType { | ||||
|     ALL = '*/*', | ||||
|     CSV = 'text/csv', | ||||
|     IMAGE = 'image/*', | ||||
|     IMAGE_JPEG = 'image/jpeg', | ||||
|     IMAGE_PNG = 'image/png', | ||||
|     JSON = 'application/json', | ||||
|     MULTIPART = 'multipart/form-data', | ||||
|     OCTET_STREAM = 'application/octet-stream', | ||||
|     TEXT_PLAIN = 'text/plain', | ||||
| } | ||||
|  | ||||
| export interface RESTConfig { | ||||
|     // base of the server: http(s)://my.server.org/plop/api/ | ||||
|     server: string; | ||||
|     // Token to access of the data. | ||||
|     token?: string; | ||||
| } | ||||
|  | ||||
| export interface RESTModel { | ||||
|     // base of the local API request: "sheep/{id}". | ||||
|     endPoint: string; | ||||
|     // Type of the request. | ||||
|     requestType?: HTTPRequestModel; | ||||
|     // Input type requested. | ||||
|     accept?: HTTPMimeType; | ||||
|     // Content of the local data. | ||||
|     contentType?: HTTPMimeType; | ||||
|     // Mode of the TOKEN in URL or Header (?token:${tokenInUrl}) | ||||
|     tokenInUrl?: boolean; | ||||
| } | ||||
|  | ||||
| export interface ModelResponseHttp { | ||||
|     status: number; | ||||
|     data: any; | ||||
| } | ||||
|  | ||||
| export function isArrayOf<TYPE>( | ||||
|     data: any, | ||||
|     typeChecker: (subData: any) => subData is TYPE, | ||||
|     length?: number | ||||
| ): data is TYPE[] { | ||||
|     if (!Array.isArray(data)) { | ||||
|         return false; | ||||
|     } | ||||
|     if (!data.every(typeChecker)) { | ||||
|         return false; | ||||
|     } | ||||
|     if (length !== undefined && data.length != length) { | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| function isNullOrUndefined(data: any): data is undefined | null { | ||||
|     return data === undefined || data === null; | ||||
| } | ||||
|  | ||||
| // generic progression callback | ||||
| export type ProgressCallback = (count: number, total: number) => void; | ||||
|  | ||||
| export interface RESTAbort { | ||||
|     abort?: () => boolean | ||||
| } | ||||
|  | ||||
|  | ||||
| // Rest generic callback have a basic model to upload and download advancement. | ||||
| export interface RESTCallbacks { | ||||
|     progressUpload?: ProgressCallback, | ||||
|     progressDownload?: ProgressCallback, | ||||
|     abortHandle?: RESTAbort, | ||||
| }; | ||||
|  | ||||
| export interface RESTRequestType { | ||||
|     restModel: RESTModel, | ||||
|     restConfig: RESTConfig, | ||||
|     data?: any, | ||||
|     params?: object, | ||||
|     queries?: object, | ||||
|     callback?: RESTCallbacks, | ||||
| }; | ||||
|  | ||||
| function removeTrailingSlashes(input: string): string { | ||||
|     if (isNullOrUndefined(input)) { | ||||
|         return "undefined"; | ||||
|     } | ||||
|     return input.replace(/\/+$/, ''); | ||||
| } | ||||
| function removeLeadingSlashes(input: string): string { | ||||
|     if (isNullOrUndefined(input)) { | ||||
|         return ""; | ||||
|     } | ||||
|     return input.replace(/^\/+/, ''); | ||||
| } | ||||
|  | ||||
| export function RESTUrl({ restModel, restConfig, params, queries }: RESTRequestType): string { | ||||
|     // Create the URL PATH: | ||||
|     let generateUrl = `${removeTrailingSlashes(restConfig.server)}/${removeLeadingSlashes(restModel.endPoint)}`; | ||||
|     if (params !== undefined) { | ||||
|         for (let key of Object.keys(params)) { | ||||
|             generateUrl = generateUrl.replaceAll(`{${key}}`, `${params[key]}`); | ||||
|         } | ||||
|     } | ||||
|     if (queries === undefined && (restConfig.token === undefined || restModel.tokenInUrl !== true)) { | ||||
|         return generateUrl; | ||||
|     } | ||||
|     const searchParams = new URLSearchParams(); | ||||
|     if (queries !== undefined) { | ||||
|         for (let key of Object.keys(queries)) { | ||||
|             const value = queries[key]; | ||||
|             if (Array.isArray(value)) { | ||||
|                 for (let iii = 0; iii < value.length; iii++) { | ||||
|                     searchParams.append(`${key}`, `${value[iii]}`); | ||||
|                 } | ||||
|             } else { | ||||
|                 searchParams.append(`${key}`, `${value}`); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     if (restConfig.token !== undefined && restModel.tokenInUrl === true) { | ||||
|         searchParams.append('Authorization', `Bearer ${restConfig.token}`); | ||||
|     } | ||||
|     return generateUrl + "?" + searchParams.toString(); | ||||
| } | ||||
|  | ||||
|  | ||||
| export function fetchProgress(generateUrl: string, { method, headers, body }: { | ||||
|     method: HTTPRequestModel, | ||||
|     headers: any, | ||||
|     body: any, | ||||
| }, { progressUpload, progressDownload, abortHandle }: RESTCallbacks): Promise<Response> { | ||||
|     const xhr = { | ||||
|         io: new XMLHttpRequest() | ||||
|     } | ||||
|     return new Promise((resolve, reject) => { | ||||
|         // Stream the upload progress | ||||
|         if (progressUpload) { | ||||
|             xhr.io.upload.addEventListener("progress", (dataEvent) => { | ||||
|                 if (dataEvent.lengthComputable) { | ||||
|                     //console.log(`    ==> has a progress event: ${dataEvent.loaded} / ${dataEvent.total}`); | ||||
|                     progressUpload(dataEvent.loaded, dataEvent.total); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|         // Stream the download progress | ||||
|         if (progressDownload) { | ||||
|             xhr.io.addEventListener("progress", (dataEvent) => { | ||||
|                 if (dataEvent.lengthComputable) { | ||||
|                     //console.log(`    ==> download progress:: ${dataEvent.loaded} / ${dataEvent.total}`); | ||||
|                     progressUpload(dataEvent.loaded, dataEvent.total); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|         if (abortHandle) { | ||||
|             abortHandle.abort = () => { | ||||
|                 if (xhr.io) { | ||||
|                     console.log(`Request abort on the XMLHttpRequest: ${generateUrl}`); | ||||
|                     xhr.io.abort(); | ||||
|                     return true; | ||||
|                 } | ||||
|                 console.log(`Request abort (FAIL) on the XMLHttpRequest: ${generateUrl}`); | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         // Check if we have an internal Fail: | ||||
|         xhr.io.addEventListener('error', () => { | ||||
|             xhr.io = undefined; | ||||
|             reject(new TypeError('Failed to fetch')) | ||||
|         }); | ||||
|  | ||||
|         // Capture the end of the stream | ||||
|         xhr.io.addEventListener("loadend", () => { | ||||
|             if (xhr.io.readyState !== XMLHttpRequest.DONE) { | ||||
|                 //console.log(`    ==> READY state`); | ||||
|                 return; | ||||
|             } | ||||
|             if (xhr.io.status === 0) { | ||||
|                 //the stream has been aborted | ||||
|                 reject(new TypeError('Fetch has been aborted')); | ||||
|                 return; | ||||
|             } | ||||
|             // Stream is ended, transform in a generic response: | ||||
|             const response = new Response(xhr.io.response, { | ||||
|                 status: xhr.io.status, | ||||
|                 statusText: xhr.io.statusText | ||||
|             }); | ||||
|             const headersArray = xhr.io.getAllResponseHeaders().trim().replaceAll("\r\n", "\n").split('\n'); | ||||
|             headersArray.forEach(function (header) { | ||||
|                 const firstColonIndex = header.indexOf(':'); | ||||
|                 if (firstColonIndex !== -1) { | ||||
|                     var key = header.substring(0, firstColonIndex).trim(); | ||||
|                     var value = header.substring(firstColonIndex + 1).trim(); | ||||
|                     response.headers.set(key, value); | ||||
|                 } else { | ||||
|                     response.headers.set(header, ""); | ||||
|                 } | ||||
|             }); | ||||
|             xhr.io = undefined; | ||||
|             resolve(response); | ||||
|         }); | ||||
|         xhr.io.open(method, generateUrl, true); | ||||
|         if (!isNullOrUndefined(headers)) { | ||||
|             for (const [key, value] of Object.entries(headers)) { | ||||
|                 xhr.io.setRequestHeader(key, value as string); | ||||
|             } | ||||
|         } | ||||
|         xhr.io.send(body); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export function RESTRequest({ restModel, restConfig, data, params, queries, callback }: RESTRequestType): Promise<ModelResponseHttp> { | ||||
|     // Create the URL PATH: | ||||
|     let generateUrl = RESTUrl({ restModel, restConfig, data, params, queries }); | ||||
|     let headers: any = {}; | ||||
|     if (restConfig.token !== undefined && restModel.tokenInUrl !== true) { | ||||
|         headers['Authorization'] = `Bearer ${restConfig.token}`; | ||||
|     } | ||||
|     if (restModel.accept !== undefined) { | ||||
|         headers['Accept'] = restModel.accept; | ||||
|     } | ||||
|     if (restModel.requestType !== HTTPRequestModel.GET) { | ||||
|         // if Get we have not a content type, the body is empty | ||||
|         if (restModel.contentType !== HTTPMimeType.MULTIPART) { | ||||
|             // special case of multi-part ==> no content type otherwise the browser does not set the ";bundary=--****" | ||||
|             headers['Content-Type'] = restModel.contentType; | ||||
|         } | ||||
|     } | ||||
|     let body = data; | ||||
|     if (restModel.contentType === HTTPMimeType.JSON) { | ||||
|         body = JSON.stringify(data); | ||||
|     } else if (restModel.contentType === HTTPMimeType.MULTIPART) { | ||||
|         const formData = new FormData(); | ||||
|         for (const name in data) { | ||||
|             formData.append(name, data[name]); | ||||
|         } | ||||
|         body = formData | ||||
|     } | ||||
|     return new Promise((resolve, reject) => { | ||||
|         let action: undefined | Promise<Response> = undefined; | ||||
|         if (isNullOrUndefined(callback) | ||||
|             || (isNullOrUndefined(callback.progressDownload) | ||||
|                 && isNullOrUndefined(callback.progressUpload) | ||||
|                 && isNullOrUndefined(callback.abortHandle))) { | ||||
|             // No information needed: call the generic fetch interface | ||||
|             action = fetch(generateUrl, { | ||||
|                 method: restModel.requestType, | ||||
|                 headers, | ||||
|                 body, | ||||
|             }); | ||||
|         } else { | ||||
|             // need progression information: call old fetch model (XMLHttpRequest) that permit to keep % upload and % download for HTTP1.x | ||||
|             action = fetchProgress(generateUrl, { | ||||
|                 method: restModel.requestType ?? HTTPRequestModel.GET, | ||||
|                 headers, | ||||
|                 body, | ||||
|             }, callback); | ||||
|         } | ||||
|         action.then((response: Response) => { | ||||
|             if (response.status >= 200 && response.status <= 299) { | ||||
|                 const contentType = response.headers.get('Content-Type'); | ||||
|                 if (restModel.accept !== contentType) { | ||||
|                     reject({ | ||||
|                         time: Date().toString(), | ||||
|                         status: 901, | ||||
|                         error: `REST check wrong type: ${restModel.accept} != ${contentType}`, | ||||
|                         statusMessage: "Fetch error", | ||||
|                         message: "rest-tools.ts Wrong type in the message return type" | ||||
|                     } as RestErrorResponse); | ||||
|                 } else if (contentType === HTTPMimeType.JSON) { | ||||
|                     response | ||||
|                         .json() | ||||
|                         .then((value: any) => { | ||||
|                             //console.log(`RECEIVE ==> ${response.status}=${ JSON.stringify(value, null, 2)}`); | ||||
|                             resolve({ status: response.status, data: value }); | ||||
|                         }) | ||||
|                         .catch((reason: any) => { | ||||
|                             reject({ | ||||
|                                 time: Date().toString(), | ||||
|                                 status: 902, | ||||
|                                 error: `REST parse json fail: ${reason}`, | ||||
|                                 statusMessage: "Fetch parse error", | ||||
|                                 message: "rest-tools.ts Wrong message model to parse" | ||||
|                             } as RestErrorResponse); | ||||
|                         }); | ||||
|                 } else { | ||||
|                     resolve({ status: response.status, data: response.body }); | ||||
|                 } | ||||
|             } else { | ||||
|                 reject({ | ||||
|                     time: Date().toString(), | ||||
|                     status: response.status, | ||||
|                     error: `${response.body}`, | ||||
|                     statusMessage: "Fetch code error", | ||||
|                     message: "rest-tools.ts Wrong return code" | ||||
|                 } as RestErrorResponse); | ||||
|             } | ||||
|         }).catch((error: any) => { | ||||
|             reject({ | ||||
|                 time: Date(), | ||||
|                 status: 999, | ||||
|                 error: error, | ||||
|                 statusMessage: "Fetch catch error", | ||||
|                 message: "rest-tools.ts detect an error in the fetch request" | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| export function RESTRequestJson<TYPE>(request: RESTRequestType, checker: (data: any) => data is TYPE): Promise<TYPE> { | ||||
|     return new Promise((resolve, reject) => { | ||||
|         RESTRequest(request).then((value: ModelResponseHttp) => { | ||||
|             if (checker(value.data)) { | ||||
|                 resolve(value.data); | ||||
|             } else { | ||||
|                 reject({ | ||||
|                     time: Date().toString(), | ||||
|                     status: 950, | ||||
|                     error: "REST Fail to verify the data", | ||||
|                     statusMessage: "API cast ERROR", | ||||
|                     message: "api.ts Check type as fail" | ||||
|                 } as RestErrorResponse); | ||||
|             } | ||||
|         }).catch((reason: RestErrorResponse) => { | ||||
|             reject(reason); | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
| export function RESTRequestJsonArray<TYPE>(request: RESTRequestType, checker: (data: any) => data is TYPE): Promise<TYPE[]> { | ||||
|     return new Promise((resolve, reject) => { | ||||
|         RESTRequest(request).then((value: ModelResponseHttp) => { | ||||
|             if (isArrayOf(value.data, checker)) { | ||||
|                 resolve(value.data); | ||||
|             } else { | ||||
|                 reject({ | ||||
|                     time: Date().toString(), | ||||
|                     status: 950, | ||||
|                     error: "REST Fail to verify the data", | ||||
|                     statusMessage: "API cast ERROR", | ||||
|                     message: "api.ts Check type as fail" | ||||
|                 } as RestErrorResponse); | ||||
|             } | ||||
|         }).catch((reason: RestErrorResponse) => { | ||||
|             reject(reason); | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export function RESTRequestVoid(request: RESTRequestType): Promise<void> { | ||||
|     return new Promise((resolve, reject) => { | ||||
|         RESTRequest(request).then((value: ModelResponseHttp) => { | ||||
|             resolve(); | ||||
|         }).catch((reason: RestErrorResponse) => { | ||||
|             reject(reason); | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
| @@ -53,7 +53,7 @@ public class TestJson { | ||||
| 		final List<String> sqlCommand = DataFactory.createTable(SerializeAsJson.class); | ||||
| 		for (final String elem : sqlCommand) { | ||||
| 			LOGGER.debug("request: '{}'", elem); | ||||
| 			DataAccess.executeSimpleQuerry(elem, false); | ||||
| 			DataAccess.executeSimpleQuerry(elem); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										99
									
								
								test/src/test/kar/archidata/TestListJson.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								test/src/test/kar/archidata/TestListJson.java
									
									
									
									
									
										Normal 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); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@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)); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -57,7 +57,7 @@ public class TestManyToMany { | ||||
| 		sqlCommand.addAll(sqlCommand2); | ||||
| 		for (final String elem : sqlCommand) { | ||||
| 			LOGGER.debug("request: '{}'", elem); | ||||
| 			DataAccess.executeSimpleQuerry(elem, false); | ||||
| 			DataAccess.executeSimpleQuerry(elem); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -56,7 +56,7 @@ public class TestManyToOne { | ||||
| 		sqlCommand.addAll(sqlCommand2); | ||||
| 		for (final String elem : sqlCommand) { | ||||
| 			LOGGER.debug("request: '{}'", elem); | ||||
| 			DataAccess.executeSimpleQuerry(elem, false); | ||||
| 			DataAccess.executeSimpleQuerry(elem); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -51,7 +51,7 @@ public class TestOneToMany { | ||||
| 		final List<String> sqlCommand = DataFactory.createTable(TypesTable.class); | ||||
| 		for (final String elem : sqlCommand) { | ||||
| 			LOGGER.debug("request: '{}'", elem); | ||||
| 			DataAccess.executeSimpleQuerry(elem, false); | ||||
| 			DataAccess.executeSimpleQuerry(elem); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										121
									
								
								test/src/test/kar/archidata/TestRawQuery.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								test/src/test/kar/archidata/TestRawQuery.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| package test.kar.archidata; | ||||
|  | ||||
| import java.io.IOException; | ||||
| 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.TypesTable; | ||||
|  | ||||
| @ExtendWith(StepwiseExtension.class) | ||||
| @TestMethodOrder(MethodOrderer.OrderAnnotation.class) | ||||
| public class TestRawQuery { | ||||
| 	final static private Logger LOGGER = LoggerFactory.getLogger(TestTypes.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 testCreateTable() throws Exception { | ||||
| 		final List<String> sqlCommand = DataFactory.createTable(TypesTable.class); | ||||
| 		for (final String elem : sqlCommand) { | ||||
| 			LOGGER.debug("request: '{}'", elem); | ||||
| 			DataAccess.executeSimpleQuerry(elem); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	@Order(2) | ||||
| 	@Test | ||||
| 	public void testGet() throws Exception { | ||||
|  | ||||
| 		final TypesTable test = new TypesTable(); | ||||
| 		test.intData = 95; | ||||
| 		test.floatData = 1.0F; | ||||
| 		DataAccess.insert(test); | ||||
| 		test.intData = 96; | ||||
| 		test.floatData = 2.0F; | ||||
| 		DataAccess.insert(test); | ||||
| 		test.intData = 97; | ||||
| 		test.floatData = 3.0F; | ||||
| 		DataAccess.insert(test); | ||||
| 		test.intData = 98; | ||||
| 		test.floatData = 4.0F; | ||||
| 		DataAccess.insert(test); | ||||
| 		test.intData = 99; | ||||
| 		test.floatData = 5.0F; | ||||
| 		DataAccess.insert(test); | ||||
| 		test.intData = 99; | ||||
| 		test.floatData = 6.0F; | ||||
| 		DataAccess.insert(test); | ||||
| 		test.intData = 99; | ||||
| 		test.floatData = 7.0F; | ||||
| 		DataAccess.insert(test); | ||||
| 		{ | ||||
| 			String querry = """ | ||||
| 					SELECT * | ||||
| 					FROM TypesTable | ||||
| 					WHERE `intData` = ? | ||||
| 					ORDER BY id DESC | ||||
| 					"""; | ||||
| 			List<Object> parameters = List.of(Integer.valueOf(99)); | ||||
| 			// Try to retrieve all the data: | ||||
| 			final List<TypesTable> retrieve = DataAccess.query(TypesTable.class, querry, parameters); | ||||
|  | ||||
| 			Assertions.assertNotNull(retrieve); | ||||
| 			Assertions.assertEquals(3, retrieve.size()); | ||||
| 			Assertions.assertEquals(99, retrieve.get(0).intData); | ||||
| 			Assertions.assertEquals(7.0F, retrieve.get(0).floatData); | ||||
| 			Assertions.assertEquals(6.0F, retrieve.get(1).floatData); | ||||
| 			Assertions.assertEquals(5.0F, retrieve.get(2).floatData); | ||||
| 		} | ||||
| 		{ | ||||
|  | ||||
| 			String querry = """ | ||||
| 					SELECT DISTINCT intData | ||||
| 					FROM TypesTable | ||||
| 					WHERE `intData` = ? | ||||
| 					ORDER BY id DESC | ||||
| 					"""; | ||||
| 			List<Object> parameters = List.of(Integer.valueOf(99)); | ||||
| 			// Try to retrieve all the data: | ||||
| 			final List<TypesTable> retrieve = DataAccess.query(TypesTable.class, querry, parameters); | ||||
|  | ||||
| 			Assertions.assertNotNull(retrieve); | ||||
| 			Assertions.assertEquals(1, retrieve.size()); | ||||
| 			Assertions.assertEquals(99, retrieve.get(0).intData); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -65,7 +65,7 @@ public class TestSimpleTable { | ||||
| 		final List<String> sqlCommand = DataFactory.createTable(SimpleTable.class); | ||||
| 		for (final String elem : sqlCommand) { | ||||
| 			LOGGER.debug("request: '{}'", elem); | ||||
| 			DataAccess.executeSimpleQuerry(elem, false); | ||||
| 			DataAccess.executeSimpleQuerry(elem); | ||||
| 		} | ||||
| 		final SimpleTable test = new SimpleTable(); | ||||
| 		test.data = TestSimpleTable.DATA_INJECTED; | ||||
| @@ -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); | ||||
|  | ||||
| 	} | ||||
|   | ||||
| @@ -65,7 +65,7 @@ public class TestSimpleTableSoftDelete { | ||||
| 		final List<String> sqlCommand = DataFactory.createTable(SimpleTableSoftDelete.class); | ||||
| 		for (final String elem : sqlCommand) { | ||||
| 			LOGGER.debug("request: '{}'", elem); | ||||
| 			DataAccess.executeSimpleQuerry(elem, false); | ||||
| 			DataAccess.executeSimpleQuerry(elem); | ||||
| 		} | ||||
| 		final SimpleTableSoftDelete test = new SimpleTableSoftDelete(); | ||||
| 		test.data = TestSimpleTableSoftDelete.DATA_INJECTED; | ||||
| @@ -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); | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user