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"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <classpath> | <classpath> | ||||||
| 	<classpathentry including="**/*.java" kind="src" output="out/maven/classes" path="src"> | 	<classpathentry including="**/*.java" kind="src" output="target/classes" path="src"> | ||||||
| 		<attributes> | 		<attributes> | ||||||
| 			<attribute name="optional" value="true"/> | 			<attribute name="optional" value="true"/> | ||||||
| 			<attribute name="maven.pomderived" value="true"/> | 			<attribute name="maven.pomderived" value="true"/> | ||||||
| 		</attributes> | 		</attributes> | ||||||
| 	</classpathentry> | 	</classpathentry> | ||||||
| 	<classpathentry kind="src" output="out/maven/test-classes" path="test/src"> | 	<classpathentry kind="src" output="target/test-classes" path="test/src"> | ||||||
| 		<attributes> | 		<attributes> | ||||||
| 			<attribute name="test" value="true"/> | 			<attribute name="test" value="true"/> | ||||||
| 			<attribute name="optional" value="true"/> | 			<attribute name="optional" value="true"/> | ||||||
| @@ -18,7 +18,7 @@ | |||||||
| 			<attribute name="maven.pomderived" value="true"/> | 			<attribute name="maven.pomderived" value="true"/> | ||||||
| 		</attributes> | 		</attributes> | ||||||
| 	</classpathentry> | 	</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> | 		<attributes> | ||||||
| 			<attribute name="test" value="true"/> | 			<attribute name="test" value="true"/> | ||||||
| 			<attribute name="maven.pomderived" value="true"/> | 			<attribute name="maven.pomderived" value="true"/> | ||||||
| @@ -30,5 +30,16 @@ | |||||||
| 			<attribute name="maven.pomderived" value="true"/> | 			<attribute name="maven.pomderived" value="true"/> | ||||||
| 		</attributes> | 		</attributes> | ||||||
| 	</classpathentry> | 	</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> | </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/ | out/ | ||||||
|  | target/ | ||||||
| *.class | *.class | ||||||
| .settings/ | .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 | mvn install | ||||||
|  | ``` | ||||||
|  |  | ||||||
| // create a single package jar | Run the test | ||||||
| mvn clean compile assembly:single | ```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: | Somes tools: | ||||||
| @@ -21,7 +63,7 @@ Somes tools: | |||||||
| Auto-update dependency: | Auto-update dependency: | ||||||
| ----------------------- | ----------------------- | ||||||
|  |  | ||||||
| auto-update to the last version dependency: | Auto-update to the last version dependency: | ||||||
|  |  | ||||||
| ```bash | ```bash | ||||||
| mvn versions:use-latest-versions | mvn versions:use-latest-versions | ||||||
| @@ -36,6 +78,22 @@ Simply run the cmd-line: | |||||||
| mvn formatter:format | 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: | Add Gitea in the dependency for the registry: | ||||||
| ============================================= | ============================================= | ||||||
| @@ -65,8 +123,8 @@ edit file: ```~/.m2/settings.xml``` | |||||||
| release: | release: | ||||||
| ======== | ======== | ||||||
|  |  | ||||||
| ``` | ```bash | ||||||
| export PATH=/usr/lib/jvm/java-19-openjdk/bin:$PATH | export PATH=$(ls -d --color=never /usr/lib/jvm/java-2*-openjdk)/bin:$PATH | ||||||
| mvn install | mvn install | ||||||
| mvn deploy | mvn deploy | ||||||
| ``` | ``` | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								dependabot.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								dependabot.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  |  | ||||||
							
								
								
									
										136
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										136
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -3,19 +3,16 @@ | |||||||
| 	<modelVersion>4.0.0</modelVersion> | 	<modelVersion>4.0.0</modelVersion> | ||||||
| 	<groupId>kangaroo-and-rabbit</groupId> | 	<groupId>kangaroo-and-rabbit</groupId> | ||||||
| 	<artifactId>archidata</artifactId> | 	<artifactId>archidata</artifactId> | ||||||
| 	<version>0.5.0</version> | 	<version>0.8.0</version> | ||||||
| 	<properties> | 	<properties> | ||||||
| 		<maven.compiler.version>3.1</maven.compiler.version> | 		<maven.compiler.version>3.1</maven.compiler.version> | ||||||
| 		<maven.compiler.source>21</maven.compiler.source> | 		<maven.compiler.source>21</maven.compiler.source> | ||||||
| 		<maven.compiler.target>21</maven.compiler.target> | 		<maven.compiler.target>21</maven.compiler.target> | ||||||
|  |  | ||||||
| 		<maven.dependency.version>3.1.1</maven.dependency.version> | 		<maven.dependency.version>3.1.1</maven.dependency.version> | ||||||
|  | 		<jersey.version>3.1.5</jersey.version> | ||||||
| 		<jersey.version>3.1.1</jersey.version> |  | ||||||
| 		<jaxb.version>2.3.1</jaxb.version> | 		<jaxb.version>2.3.1</jaxb.version> | ||||||
| 		<istack.version>4.1.1</istack.version> | 		<istack.version>4.1.1</istack.version> | ||||||
| 	</properties> | 	</properties> | ||||||
|  |  | ||||||
| 	<repositories> | 	<repositories> | ||||||
| 		<repository> | 		<repository> | ||||||
| 			<id>gitea</id> | 			<id>gitea</id> | ||||||
| @@ -32,7 +29,6 @@ | |||||||
| 			<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url> | 			<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url> | ||||||
| 		</snapshotRepository> | 		</snapshotRepository> | ||||||
| 	</distributionManagement> | 	</distributionManagement> | ||||||
|  |  | ||||||
| 	<dependencyManagement> | 	<dependencyManagement> | ||||||
| 		<dependencies> | 		<dependencies> | ||||||
| 			<dependency> | 			<dependency> | ||||||
| @@ -44,7 +40,6 @@ | |||||||
| 			</dependency> | 			</dependency> | ||||||
| 		</dependencies> | 		</dependencies> | ||||||
| 	</dependencyManagement> | 	</dependencyManagement> | ||||||
| 	 |  | ||||||
| 	<dependencies> | 	<dependencies> | ||||||
| 		<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api --> | 		<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api --> | ||||||
| 		<dependency> | 		<dependency> | ||||||
| @@ -64,8 +59,8 @@ | |||||||
| 			<artifactId>jersey-media-multipart</artifactId> | 			<artifactId>jersey-media-multipart</artifactId> | ||||||
| 		</dependency> | 		</dependency> | ||||||
| 		<dependency> | 		<dependency> | ||||||
|     		<groupId>org.glassfish.jersey.core</groupId> | 			<groupId>org.glassfish.jersey.core</groupId> | ||||||
|     		<artifactId>jersey-client</artifactId> | 			<artifactId>jersey-client</artifactId> | ||||||
| 		</dependency> | 		</dependency> | ||||||
| 		<dependency> | 		<dependency> | ||||||
| 			<groupId>org.glassfish.jersey.inject</groupId> | 			<groupId>org.glassfish.jersey.inject</groupId> | ||||||
| @@ -81,14 +76,14 @@ | |||||||
| 			<version>${jaxb.version}</version> | 			<version>${jaxb.version}</version> | ||||||
| 		</dependency> | 		</dependency> | ||||||
| 		<dependency> | 		<dependency> | ||||||
| 		    <groupId>org.glassfish.jaxb</groupId> | 			<groupId>org.glassfish.jaxb</groupId> | ||||||
| 		    <artifactId>jaxb-runtime</artifactId> | 			<artifactId>jaxb-runtime</artifactId> | ||||||
| 		    <version>4.0.4</version> | 			<version>4.0.4</version> | ||||||
| 		</dependency> | 		</dependency> | ||||||
| 		<dependency> | 		<dependency> | ||||||
| 		    <groupId>jakarta.ws.rs</groupId> | 			<groupId>jakarta.ws.rs</groupId> | ||||||
| 		    <artifactId>jakarta.ws.rs-api</artifactId> | 			<artifactId>jakarta.ws.rs-api</artifactId> | ||||||
| 		    <version>3.1.0</version> | 			<version>3.1.0</version> | ||||||
| 		</dependency> | 		</dependency> | ||||||
| 		<dependency> | 		<dependency> | ||||||
| 			<groupId>com.sun.xml.bind</groupId> | 			<groupId>com.sun.xml.bind</groupId> | ||||||
| @@ -103,45 +98,62 @@ | |||||||
| 		<dependency> | 		<dependency> | ||||||
| 			<groupId>org.glassfish.jersey.test-framework.providers</groupId> | 			<groupId>org.glassfish.jersey.test-framework.providers</groupId> | ||||||
| 			<artifactId>jersey-test-framework-provider-grizzly2</artifactId> | 			<artifactId>jersey-test-framework-provider-grizzly2</artifactId> | ||||||
| 			<scope>test</scope>  | 			<scope>test</scope> | ||||||
| 		</dependency> | 		</dependency> | ||||||
| 		<dependency> | 		<dependency> | ||||||
| 			<groupId>org.glassfish.jersey.media</groupId> | 			<groupId>org.glassfish.jersey.media</groupId> | ||||||
| 			<artifactId>jersey-media-json-jackson</artifactId> | 			<artifactId>jersey-media-json-jackson</artifactId> | ||||||
| 		</dependency> | 		</dependency> | ||||||
|         <dependency> |  | ||||||
|             <groupId>com.fasterxml.jackson.core</groupId> |  | ||||||
|             <artifactId>jackson-databind</artifactId> |  | ||||||
|             <version>2.16.0</version> |  | ||||||
|         </dependency> |  | ||||||
|         <dependency> |  | ||||||
| 		    <groupId>jakarta.servlet</groupId> |  | ||||||
| 		    <artifactId>jakarta.servlet-api</artifactId> |  | ||||||
| 		    <version>6.1.0-M1</version> |  | ||||||
| 		    <scope>provided</scope> |  | ||||||
|         </dependency> |  | ||||||
|         <!-- Interface for My-sql & sqlite DB --> |  | ||||||
| 		<dependency> | 		<dependency> | ||||||
| 		    <groupId>com.mysql</groupId> | 			<groupId>com.fasterxml.jackson.core</groupId> | ||||||
| 		    <artifactId>mysql-connector-j</artifactId> | 			<artifactId>jackson-databind</artifactId> | ||||||
| 		    <version>8.2.0</version> | 			<version>2.16.1</version> | ||||||
| 		</dependency> | 		</dependency> | ||||||
| 		<dependency> | 		<dependency> | ||||||
| 	        <groupId>org.xerial</groupId> | 			<groupId>com.fasterxml.jackson.dataformat</groupId> | ||||||
| 	        <artifactId>sqlite-jdbc</artifactId> | 			<artifactId>jackson-dataformat-csv</artifactId> | ||||||
| 	        <version>3.44.1.0</version> | 			<version>2.16.1</version> | ||||||
| 	    </dependency> | 		</dependency> | ||||||
|         <!-- Interface for JWT token --> |  | ||||||
| 		<dependency> | 		<dependency> | ||||||
| 		    <groupId>com.nimbusds</groupId> | 			<groupId>com.fasterxml.jackson.datatype</groupId> | ||||||
| 		    <artifactId>nimbus-jose-jwt</artifactId> | 			<artifactId>jackson-datatype-jsr310</artifactId> | ||||||
| 		    <version>9.37.1</version> | 			<version>2.16.1</version> | ||||||
|  | 		</dependency> | ||||||
|  |  | ||||||
|  | 		<dependency> | ||||||
|  | 			<groupId>jakarta.servlet</groupId> | ||||||
|  | 			<artifactId>jakarta.servlet-api</artifactId> | ||||||
|  | 			<version>6.1.0-M1</version> | ||||||
|  | 			<scope>provided</scope> | ||||||
|  | 		</dependency> | ||||||
|  | 		<!-- Interface for My-sql & sqlite DB --> | ||||||
|  | 		<dependency> | ||||||
|  | 			<groupId>com.mysql</groupId> | ||||||
|  | 			<artifactId>mysql-connector-j</artifactId> | ||||||
|  | 			<version>8.2.0</version> | ||||||
|  | 		</dependency> | ||||||
|  | 		<dependency> | ||||||
|  | 			<groupId>org.xerial</groupId> | ||||||
|  | 			<artifactId>sqlite-jdbc</artifactId> | ||||||
|  | 			<version>3.40.0.0</version> | ||||||
|  | 		</dependency> | ||||||
|  | 		<!-- Interface for JWT token --> | ||||||
|  | 		<dependency> | ||||||
|  | 			<groupId>com.nimbusds</groupId> | ||||||
|  | 			<artifactId>nimbus-jose-jwt</artifactId> | ||||||
|  | 			<version>9.37.1</version> | ||||||
| 		</dependency> | 		</dependency> | ||||||
| 		<dependency> | 		<dependency> | ||||||
| 			<groupId>jakarta.persistence</groupId> | 			<groupId>jakarta.persistence</groupId> | ||||||
| 			<artifactId>jakarta.persistence-api</artifactId> | 			<artifactId>jakarta.persistence-api</artifactId> | ||||||
| 			<version>3.2.0-M1</version> | 			<version>3.2.0-M1</version> | ||||||
| 		</dependency> | 		</dependency> | ||||||
|  | 		<!-- Swagger dependencies --> | ||||||
|  | 		<dependency> | ||||||
|  | 			<groupId>io.swagger.core.v3</groupId> | ||||||
|  | 			<artifactId>swagger-jaxrs2-jakarta</artifactId> | ||||||
|  | 			<version>2.1.10</version> | ||||||
|  | 		</dependency> | ||||||
| 		<!-- | 		<!-- | ||||||
| 		************************************************************  | 		************************************************************  | ||||||
| 		** TEST dependency                                        ** | 		** TEST dependency                                        ** | ||||||
| @@ -153,18 +165,32 @@ | |||||||
| 			<version>5.10.1</version> | 			<version>5.10.1</version> | ||||||
| 			<scope>test</scope> | 			<scope>test</scope> | ||||||
| 		</dependency> | 		</dependency> | ||||||
| 	    <dependency> | 		<dependency> | ||||||
| 	        <groupId>org.junit.jupiter</groupId> | 			<groupId>org.junit.jupiter</groupId> | ||||||
| 	        <artifactId>junit-jupiter-engine</artifactId> | 			<artifactId>junit-jupiter-engine</artifactId> | ||||||
| 			<version>5.10.1</version> | 			<version>5.10.1</version> | ||||||
| 	        <scope>test</scope> | 	        <scope>test</scope> | ||||||
| 	    </dependency> | 	    </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> |     </dependencies> | ||||||
|  |  | ||||||
| 	<build> | 	<build> | ||||||
| 		<sourceDirectory>src</sourceDirectory> | 		<sourceDirectory>src</sourceDirectory> | ||||||
|  | 		<resources> | ||||||
|  | 			<resource> | ||||||
|  | 				<directory>${basedir}/src/resources</directory> | ||||||
|  | 			</resource> | ||||||
|  | 		</resources> | ||||||
| 		<testSourceDirectory>test/src</testSourceDirectory> | 		<testSourceDirectory>test/src</testSourceDirectory> | ||||||
|     	<directory>${project.basedir}/out/maven/</directory> |  | ||||||
| 		<testResources> | 		<testResources> | ||||||
| 			<testResource> | 			<testResource> | ||||||
| 				<directory>${basedir}/test/resources</directory> | 				<directory>${basedir}/test/resources</directory> | ||||||
| @@ -185,6 +211,7 @@ | |||||||
| 			<plugin> | 			<plugin> | ||||||
| 				<groupId>org.apache.maven.plugins</groupId> | 				<groupId>org.apache.maven.plugins</groupId> | ||||||
| 				<artifactId>maven-source-plugin</artifactId> | 				<artifactId>maven-source-plugin</artifactId> | ||||||
|  | 				<version>3.3.0</version> | ||||||
| 				<executions> | 				<executions> | ||||||
| 					<execution> | 					<execution> | ||||||
| 						<id>attach-sources</id> | 						<id>attach-sources</id> | ||||||
| @@ -194,13 +221,27 @@ | |||||||
| 					</execution> | 					</execution> | ||||||
| 				</executions> | 				</executions> | ||||||
| 			</plugin> | 			</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 --> | 			<!-- junit results --> | ||||||
| 			<plugin> | 			<plugin> | ||||||
| 				<groupId>org.apache.maven.plugins</groupId> | 				<groupId>org.apache.maven.plugins</groupId> | ||||||
| 				<artifactId>maven-surefire-plugin</artifactId> | 				<artifactId>maven-surefire-plugin</artifactId> | ||||||
| 				<version>3.0.0-M5</version> | 				<version>3.0.0-M5</version> | ||||||
| 			</plugin> | 			</plugin> | ||||||
| 		    <plugin> | 			<plugin> | ||||||
| 				<artifactId>maven-assembly-plugin</artifactId> | 				<artifactId>maven-assembly-plugin</artifactId> | ||||||
| 				<configuration> | 				<configuration> | ||||||
| 					<archive> | 					<archive> | ||||||
| @@ -212,7 +253,7 @@ | |||||||
| 						<descriptorRef>jar-with-dependencies</descriptorRef> | 						<descriptorRef>jar-with-dependencies</descriptorRef> | ||||||
| 					</descriptorRefs> | 					</descriptorRefs> | ||||||
| 				</configuration> | 				</configuration> | ||||||
| 		    </plugin> | 			</plugin> | ||||||
| 			<!-- Java-doc generation for stand-alone site --> | 			<!-- Java-doc generation for stand-alone site --> | ||||||
| 			<plugin> | 			<plugin> | ||||||
| 				<groupId>org.apache.maven.plugins</groupId> | 				<groupId>org.apache.maven.plugins</groupId> | ||||||
| @@ -227,7 +268,7 @@ | |||||||
| 			<plugin> | 			<plugin> | ||||||
| 				<groupId>org.apache.maven.plugins</groupId> | 				<groupId>org.apache.maven.plugins</groupId> | ||||||
| 				<artifactId>maven-checkstyle-plugin</artifactId> | 				<artifactId>maven-checkstyle-plugin</artifactId> | ||||||
| 				<version>3.1.0</version> | 		    	<version>3.3.1</version> | ||||||
| 				<configuration> | 				<configuration> | ||||||
| 					<configLocation>CheckStyle.xml</configLocation> | 					<configLocation>CheckStyle.xml</configLocation> | ||||||
| 					<consoleOutput>true</consoleOutput> | 					<consoleOutput>true</consoleOutput> | ||||||
| @@ -239,7 +280,7 @@ | |||||||
| 			<plugin> | 			<plugin> | ||||||
| 				<groupId>net.revelc.code.formatter</groupId> | 				<groupId>net.revelc.code.formatter</groupId> | ||||||
| 				<artifactId>formatter-maven-plugin</artifactId> | 				<artifactId>formatter-maven-plugin</artifactId> | ||||||
| 				<version>2.12.2</version> | 		    	<version>2.23.0</version> | ||||||
| 				<configuration> | 				<configuration> | ||||||
| 					<encoding>UTF-8</encoding> | 					<encoding>UTF-8</encoding> | ||||||
| 					<lineEnding>LF</lineEnding> | 					<lineEnding>LF</lineEnding> | ||||||
| @@ -278,5 +319,4 @@ | |||||||
| 			</plugin> | 			</plugin> | ||||||
| 		</plugins> | 		</plugins> | ||||||
| 	</reporting> | 	</reporting> | ||||||
|  |  | ||||||
| </project> | </project> | ||||||
|   | |||||||
| @@ -5,6 +5,9 @@ import java.sql.PreparedStatement; | |||||||
| import java.sql.SQLException; | import java.sql.SQLException; | ||||||
|  |  | ||||||
| import org.kar.archidata.dataAccess.DataAccess; | 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.db.DBEntry; | ||||||
| import org.kar.archidata.model.User; | import org.kar.archidata.model.User; | ||||||
|  |  | ||||||
| @@ -12,21 +15,22 @@ public class UserDB { | |||||||
|  |  | ||||||
| 	public UserDB() {} | 	public UserDB() {} | ||||||
|  |  | ||||||
| 	public static User getUsers(final long userId) throws Exception { | 	public static User getUsers(final long userId, QueryOption... option) throws Exception { | ||||||
| 		return DataAccess.get(User.class, userId); | 		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); | 		final User user = getUsers(userId); | ||||||
| 		if (user != null) { | 		if (user != null) { | ||||||
| 			return user; | 			return user; | ||||||
| 		} | 		} | ||||||
| 		createUsersInfoFromOAuth(userId, userLogin); | 		createUsersInfoFromOAuth(userId, userLogin, option); | ||||||
| 		return getUsers(userId); | 		return getUsers(userId); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	private static void createUsersInfoFromOAuth(final long userId, final String login) throws IOException { | 	private static void createUsersInfoFromOAuth(final long userId, final String login, QueryOption... option) throws IOException { | ||||||
| 		final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig); | 		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')"; | 		final String query = "INSERT INTO `user` (`id`, `login`, `lastConnection`, `admin`, `blocked`, `removed`) VALUE (?,?,now(3),'0','0','0')"; | ||||||
| 		try { | 		try { | ||||||
| 			final PreparedStatement ps = entry.connection.prepareStatement(query); | 			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.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import io.swagger.v3.oas.annotations.media.Schema; | ||||||
| import jakarta.persistence.Column; | import jakarta.persistence.Column; | ||||||
| import jakarta.persistence.GeneratedValue; | import jakarta.persistence.GeneratedValue; | ||||||
| import jakarta.persistence.GenerationType; | import jakarta.persistence.GenerationType; | ||||||
| import jakarta.persistence.Id; | import jakarta.persistence.Id; | ||||||
|  | import jakarta.persistence.ManyToOne; | ||||||
| import jakarta.persistence.Table; | 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 { | public class AnnotationTools { | ||||||
| 	static final Logger LOGGER = LoggerFactory.getLogger(AnnotationTools.class); | 	static final Logger LOGGER = LoggerFactory.getLogger(AnnotationTools.class); | ||||||
| @@ -45,10 +53,54 @@ public class AnnotationTools { | |||||||
| 		return tmp; | 		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 { | 	public static String getComment(final Field element) throws Exception { | ||||||
| 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataComment.class); | 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataComment.class); | ||||||
| 		if (annotation.length == 0) { | 		if (annotation.length == 0) { | ||||||
| 			return null; | 			return getSchemaDescription(element); | ||||||
| 		} | 		} | ||||||
| 		if (annotation.length > 1) { | 		if (annotation.length > 1) { | ||||||
| 			throw new Exception("Must not have more than 1 element @DataComment on " + element.getClass().getCanonicalName()); | 			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 { | 	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) { | 		if (annotation.length == 0) { | ||||||
| 			return null; | 			return null; | ||||||
| 		} | 		} | ||||||
| 		if (annotation.length > 1) { | 		if (annotation.length > 1) { | ||||||
| 			throw new Exception("Must not have more than 1 element @DataDefault on " + element.getClass().getCanonicalName()); | 			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); | 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class); | ||||||
| 		if (annotation.length == 0) { | 		if (annotation.length == 0) { | ||||||
| 			return 255; | 			return 255; | ||||||
| @@ -76,7 +172,29 @@ public class AnnotationTools { | |||||||
| 			throw new Exception("Must not have more than 1 element @Column on " + element.getClass().getCanonicalName()); | 			throw new Exception("Must not have more than 1 element @Column on " + element.getClass().getCanonicalName()); | ||||||
| 		} | 		} | ||||||
| 		final int length = ((Column) annotation[0]).length(); | 		final int length = ((Column) annotation[0]).length(); | ||||||
| 		return length <= 0 ? 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) { | 	public static boolean isAnnotationGroup(final Field field, final Class<?> annotationType) { | ||||||
| @@ -117,7 +235,7 @@ public class AnnotationTools { | |||||||
| 		return name; | 		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); | 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class); | ||||||
| 		if (annotation.length == 0) { | 		if (annotation.length == 0) { | ||||||
| 			return false; | 			return false; | ||||||
| @@ -128,7 +246,39 @@ public class AnnotationTools { | |||||||
| 		return !((Column) annotation[0]).nullable(); | 		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 { | 	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); | 		final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class); | ||||||
| 		if (annotation.length == 0) { | 		if (annotation.length == 0) { | ||||||
| 			return false; | 			return false; | ||||||
| @@ -245,7 +395,7 @@ public class AnnotationTools { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public static boolean isGenericField(final Field elem) throws Exception { | 	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 { | 	public static Field getFieldOfId(final Class<?> clazz) throws Exception { | ||||||
| @@ -261,4 +411,17 @@ public class AnnotationTools { | |||||||
| 		return null; | 		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 }) | @Target({ ElementType.TYPE, ElementType.FIELD }) | ||||||
| @Retention(RetentionPolicy.RUNTIME) | @Retention(RetentionPolicy.RUNTIME) | ||||||
|  | @Deprecated(since = "0.5.2") | ||||||
| public @interface DataComment { | public @interface DataComment { | ||||||
|  |  | ||||||
| 	String value(); | 	String value(); | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,6 +5,13 @@ import java.lang.annotation.Retention; | |||||||
| import java.lang.annotation.RetentionPolicy; | import java.lang.annotation.RetentionPolicy; | ||||||
| import java.lang.annotation.Target; | import java.lang.annotation.Target; | ||||||
|  |  | ||||||
|  | import org.kar.archidata.dataAccess.options.CheckFunctionInterface; | ||||||
|  | import org.kar.archidata.dataAccess.options.CheckFunctionVoid; | ||||||
|  |  | ||||||
| @Target({ ElementType.TYPE, ElementType.FIELD }) | @Target({ ElementType.TYPE, ElementType.FIELD }) | ||||||
| @Retention(RetentionPolicy.RUNTIME) | @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.RetentionPolicy; | ||||||
| import java.lang.annotation.Target; | 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) | @Retention(RetentionPolicy.RUNTIME) | ||||||
| public @interface DataDefault { | public @interface TypeScriptProgress {} | ||||||
| 
 |  | ||||||
| 	String value(); |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| @@ -16,6 +16,7 @@ import java.nio.file.StandardCopyOption; | |||||||
| import java.security.MessageDigest; | import java.security.MessageDigest; | ||||||
| import java.security.NoSuchAlgorithmException; | import java.security.NoSuchAlgorithmException; | ||||||
| import java.util.Date; | import java.util.Date; | ||||||
|  | import java.util.UUID; | ||||||
|  |  | ||||||
| import javax.imageio.ImageIO; | 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.annotation.security.PermitTokenInURI; | ||||||
| import org.kar.archidata.dataAccess.DataAccess; | import org.kar.archidata.dataAccess.DataAccess; | ||||||
| import org.kar.archidata.dataAccess.QueryCondition; | import org.kar.archidata.dataAccess.QueryCondition; | ||||||
|  | import org.kar.archidata.dataAccess.options.Condition; | ||||||
| import org.kar.archidata.filter.GenericContext; | import org.kar.archidata.filter.GenericContext; | ||||||
| import org.kar.archidata.model.Data; | import org.kar.archidata.model.Data; | ||||||
| import org.kar.archidata.tools.ConfigBaseVariable; | import org.kar.archidata.tools.ConfigBaseVariable; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import io.swagger.v3.oas.annotations.Operation; | ||||||
| import jakarta.annotation.security.RolesAllowed; | import jakarta.annotation.security.RolesAllowed; | ||||||
| import jakarta.ws.rs.Consumes; | import jakarta.ws.rs.Consumes; | ||||||
| import jakarta.ws.rs.GET; | import jakarta.ws.rs.GET; | ||||||
| @@ -54,7 +57,7 @@ import jakarta.ws.rs.core.StreamingOutput; | |||||||
| @Path("/data") | @Path("/data") | ||||||
| @Produces(MediaType.APPLICATION_JSON) | @Produces(MediaType.APPLICATION_JSON) | ||||||
| public class DataResource { | 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 = 1024 * 1024; // 1MB chunks | ||||||
| 	private final static int CHUNK_SIZE_IN = 50 * 1024 * 1024; // 1MB chunks | 	private final static int CHUNK_SIZE_IN = 50 * 1024 * 1024; // 1MB chunks | ||||||
| 	/** Upload some datas */ | 	/** Upload some datas */ | ||||||
| @@ -81,7 +84,7 @@ public class DataResource { | |||||||
| 		return filePath; | 		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"; | 		final String filePath = ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator + "data"; | ||||||
| 		try { | 		try { | ||||||
| 			createFolder(ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator); | 			createFolder(ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator); | ||||||
| @@ -91,10 +94,30 @@ public class DataResource { | |||||||
| 		return filePath; | 		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) { | 	public static Data getWithSha512(final String sha512) { | ||||||
| 		LOGGER.info("find sha512 = {}", sha512); | 		LOGGER.info("find sha512 = {}", sha512); | ||||||
| 		try { | 		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) { | 		} catch (final Exception e) { | ||||||
| 			// TODO Auto-generated catch block | 			// TODO Auto-generated catch block | ||||||
| 			e.printStackTrace(); | 			e.printStackTrace(); | ||||||
| @@ -135,7 +158,6 @@ public class DataResource { | |||||||
| 		try { | 		try { | ||||||
| 			injectedData = DataAccess.insert(injectedData); | 			injectedData = DataAccess.insert(injectedData); | ||||||
| 		} catch (final Exception e) { | 		} catch (final Exception e) { | ||||||
| 			// TODO Auto-generated catch block |  | ||||||
| 			e.printStackTrace(); | 			e.printStackTrace(); | ||||||
| 			return null; | 			return null; | ||||||
| 		} | 		} | ||||||
| @@ -147,6 +169,25 @@ public class DataResource { | |||||||
| 		return injectedData; | 		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) { | 	public static String saveTemporaryFile(final InputStream uploadedInputStream, final long idData) { | ||||||
| 		return saveFile(uploadedInputStream, DataResource.getTmpFileInData(idData)); | 		return saveFile(uploadedInputStream, DataResource.getTmpFileInData(idData)); | ||||||
| 	} | 	} | ||||||
| @@ -205,7 +246,7 @@ public class DataResource { | |||||||
| 		return sb.toString(); | 		return sb.toString(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public Data getSmall(final Long id) { | 	public Data getSmall(final UUID id) { | ||||||
| 		try { | 		try { | ||||||
| 			return DataAccess.get(Data.class, id); | 			return DataAccess.get(Data.class, id); | ||||||
| 		} catch (final Exception e) { | 		} catch (final Exception e) { | ||||||
| @@ -219,7 +260,8 @@ public class DataResource { | |||||||
| 	@Path("/upload/") | 	@Path("/upload/") | ||||||
| 	@Consumes({ MediaType.MULTIPART_FORM_DATA }) | 	@Consumes({ MediaType.MULTIPART_FORM_DATA }) | ||||||
| 	@RolesAllowed("ADMIN") | 	@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(); | 		final GenericContext gc = (GenericContext) sc.getUserPrincipal(); | ||||||
| 		LOGGER.info("==================================================="); | 		LOGGER.info("==================================================="); | ||||||
| 		LOGGER.info("== DATA uploadFile {}", (gc == null ? "null" : gc.userByToken)); | 		LOGGER.info("== DATA uploadFile {}", (gc == null ? "null" : gc.userByToken)); | ||||||
| @@ -233,8 +275,6 @@ public class DataResource { | |||||||
| 			e.printStackTrace(); | 			e.printStackTrace(); | ||||||
| 		} | 		} | ||||||
| 		saveFile(fileInputStream, filePath); | 		saveFile(fileInputStream, filePath); | ||||||
| 		return Response.ok("Data uploaded successfully !!").build(); |  | ||||||
| 		// return null; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@GET | 	@GET | ||||||
| @@ -242,17 +282,18 @@ public class DataResource { | |||||||
| 	@PermitTokenInURI | 	@PermitTokenInURI | ||||||
| 	@RolesAllowed("USER") | 	@RolesAllowed("USER") | ||||||
| 	@Produces(MediaType.APPLICATION_OCTET_STREAM) | 	@Produces(MediaType.APPLICATION_OCTET_STREAM) | ||||||
| 	public Response retriveDataId(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range, | 	@Operation(description = "Get back some data from the data environment", tags = "SYSTEM") | ||||||
| 			@PathParam("id") final Long id) throws Exception { | 	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(); | 		final GenericContext gc = (GenericContext) sc.getUserPrincipal(); | ||||||
| 		// logger.info("==================================================="); | 		// 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("==================================================="); | 		// logger.info("==================================================="); | ||||||
| 		final Data value = getSmall(id); | 		final Data value = getSmall(id); | ||||||
| 		if (value == null) { | 		if (value == null) { | ||||||
| 			Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build(); | 			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 | 	@GET | ||||||
| @@ -260,18 +301,19 @@ public class DataResource { | |||||||
| 	@RolesAllowed("USER") | 	@RolesAllowed("USER") | ||||||
| 	@PermitTokenInURI | 	@PermitTokenInURI | ||||||
| 	@Produces(MediaType.APPLICATION_OCTET_STREAM) | 	@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) | 	// @CacheMaxAge(time = 10, unit = TimeUnit.DAYS) | ||||||
| 	public Response retriveDataThumbnailId(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range, | 	public Response retrieveDataThumbnailId(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range, | ||||||
| 			@PathParam("id") final Long id) throws Exception { | 			@PathParam("id") final UUID id) throws Exception { | ||||||
| 		// GenericContext gc = (GenericContext) sc.getUserPrincipal(); | 		// GenericContext gc = (GenericContext) sc.getUserPrincipal(); | ||||||
| 		// logger.info("==================================================="); | 		// logger.info("==================================================="); | ||||||
| 		// logger.info("== DATA retriveDataThumbnailId ? {}", (gc==null?"null":gc.user)); | 		// logger.info("== DATA retrieveDataThumbnailId ? {}", (gc==null?"null":gc.user)); | ||||||
| 		// logger.info("==================================================="); | 		// logger.info("==================================================="); | ||||||
| 		final Data value = getSmall(id); | 		final Data value = getSmall(id); | ||||||
| 		if (value == null) { | 		if (value == null) { | ||||||
| 			return Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build(); | 			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); | 		final File inputFile = new File(filePathName); | ||||||
| 		if (!inputFile.exists()) { | 		if (!inputFile.exists()) { | ||||||
| 			return Response.status(404).entity("{\"error\":\"media Does not exist: " + id + "\"}").type("application/json").build(); | 			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); | 		return buildStream(filePathName, range, value.mimeType); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// @Secured |  | ||||||
| 	@GET | 	@GET | ||||||
| 	@Path("{id}/{name}") | 	@Path("{id}/{name}") | ||||||
| 	@PermitTokenInURI | 	@PermitTokenInURI | ||||||
| 	@RolesAllowed("USER") | 	@RolesAllowed("USER") | ||||||
| 	@Produces(MediaType.APPLICATION_OCTET_STREAM) | 	@Produces(MediaType.APPLICATION_OCTET_STREAM) | ||||||
| 	public Response retriveDataFull(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range, | 	@Operation(description = "Get back some data from the data environment (with a beautiful name (permit download with basic name)", tags = "SYSTEM") | ||||||
| 			@PathParam("id") final Long id, @PathParam("name") final String name) throws Exception { | 	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(); | 		final GenericContext gc = (GenericContext) sc.getUserPrincipal(); | ||||||
| 		// logger.info("==================================================="); | 		// 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("==================================================="); | 		// logger.info("==================================================="); | ||||||
| 		final Data value = getSmall(id); | 		final Data value = getSmall(id); | ||||||
| 		if (value == null) { | 		if (value == null) { | ||||||
| 			Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build(); | 			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 | 	/** 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.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import io.swagger.v3.oas.annotations.Operation; | ||||||
| import jakarta.annotation.security.PermitAll; | import jakarta.annotation.security.PermitAll; | ||||||
| import jakarta.ws.rs.GET; | import jakarta.ws.rs.GET; | ||||||
| import jakarta.ws.rs.NotFoundException; | import jakarta.ws.rs.NotFoundException; | ||||||
| @@ -88,6 +89,7 @@ public class FrontGeneric { | |||||||
|  |  | ||||||
| 	@GET | 	@GET | ||||||
| 	@PermitAll() | 	@PermitAll() | ||||||
|  | 	@Operation(description = "Retrieve native element (index)", tags = "SYSTEM") | ||||||
| 	// @Produces(MediaType.APPLICATION_OCTET_STREAM) | 	// @Produces(MediaType.APPLICATION_OCTET_STREAM) | ||||||
| 	// @CacheMaxAge(time = 1, unit = TimeUnit.DAYS) | 	// @CacheMaxAge(time = 1, unit = TimeUnit.DAYS) | ||||||
| 	public Response retrive0() throws Exception { | 	public Response retrive0() throws Exception { | ||||||
| @@ -97,6 +99,7 @@ public class FrontGeneric { | |||||||
| 	@GET | 	@GET | ||||||
| 	@Path("{any: .*}") | 	@Path("{any: .*}") | ||||||
| 	@PermitAll() | 	@PermitAll() | ||||||
|  | 	@Operation(description = "Get specific file from the front environment", tags = "SYSTEM") | ||||||
| 	// @Produces(MediaType.APPLICATION_OCTET_STREAM) | 	// @Produces(MediaType.APPLICATION_OCTET_STREAM) | ||||||
| 	// @CacheMaxAge(time = 10, unit = TimeUnit.DAYS) | 	// @CacheMaxAge(time = 10, unit = TimeUnit.DAYS) | ||||||
| 	public Response retrive1(@PathParam("any") final List<PathSegment> segments) throws Exception { | 	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 | 	@Override | ||||||
| 	public Response toResponse(final FailException exception) { | 	public Response toResponse(final FailException exception) { | ||||||
|  | 		LOGGER.warn("Catch FailException:"); | ||||||
| 		final RestErrorResponse ret = build(exception); | 		final RestErrorResponse ret = build(exception); | ||||||
| 		LOGGER.error("Error UUID={}", ret.uuid); | 		LOGGER.error("Error UUID={}", ret.uuid); | ||||||
| 		// Not display backtrace ==> this may be a normal case ... | 		// Not display backtrace ==> this may be a normal case ... | ||||||
|   | |||||||
| @@ -13,9 +13,10 @@ public class InputExceptionCatcher implements ExceptionMapper<InputException> { | |||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public Response toResponse(final InputException exception) { | 	public Response toResponse(final InputException exception) { | ||||||
|  | 		LOGGER.warn("Catch InputException:"); | ||||||
| 		final RestErrorResponse ret = build(exception); | 		final RestErrorResponse ret = build(exception); | ||||||
| 		LOGGER.error("Error UUID={}", ret.uuid); | 		LOGGER.error("Error UUID={} ==> '{}'=>'{}'", ret.uuid, exception.missingVariable, exception.getLocalizedMessage()); | ||||||
| 		exception.printStackTrace(); | 		// exception.printStackTrace(); | ||||||
| 		return Response.status(exception.status).entity(ret).type(MediaType.APPLICATION_JSON).build(); | 		return Response.status(exception.status).entity(ret).type(MediaType.APPLICATION_JSON).build(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										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.time.Instant; | ||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
|  |  | ||||||
|  | import org.kar.archidata.tools.UuidUtils; | ||||||
|  |  | ||||||
| import jakarta.ws.rs.core.Response; | import jakarta.ws.rs.core.Response; | ||||||
|  |  | ||||||
| public class RestErrorResponse { | 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 time; | ||||||
| 	public String error; |  | ||||||
| 	public String message; |  | ||||||
| 	final public int status; | 	final public int status; | ||||||
| 	final public String statusMessage; | 	final public String statusMessage; | ||||||
|  |  | ||||||
| 	public RestErrorResponse(final Response.Status status, final String time, final String error, final String message) { | 	public RestErrorResponse(final Response.Status status, final String time, final String error, final String message) { | ||||||
| 		this.time = time; | 		this.time = time; | ||||||
| 		this.error = error; | 		this.name = error; | ||||||
| 		this.message = message; | 		this.message = message; | ||||||
| 		this.status = status.getStatusCode(); | 		this.status = status.getStatusCode(); | ||||||
| 		this.statusMessage = status.getReasonPhrase(); | 		this.statusMessage = status.getReasonPhrase(); | ||||||
| @@ -23,13 +25,15 @@ public class RestErrorResponse { | |||||||
|  |  | ||||||
| 	public RestErrorResponse(final Response.Status status, final String error, final String message) { | 	public RestErrorResponse(final Response.Status status, final String error, final String message) { | ||||||
| 		this.time = Instant.now().toString(); | 		this.time = Instant.now().toString(); | ||||||
| 		this.error = error; | 		this.name = error; | ||||||
| 		this.message = message; | 		this.message = message; | ||||||
| 		this.status = status.getStatusCode(); | 		this.status = status.getStatusCode(); | ||||||
| 		this.statusMessage = status.getReasonPhrase(); | 		this.statusMessage = status.getReasonPhrase(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public RestErrorResponse(final Response.Status status) { | 	public RestErrorResponse(final Response.Status status) { | ||||||
|  | 		this.name = "generic"; | ||||||
|  | 		this.message = ""; | ||||||
| 		this.time = Instant.now().toString(); | 		this.time = Instant.now().toString(); | ||||||
| 		this.status = status.getStatusCode(); | 		this.status = status.getStatusCode(); | ||||||
| 		this.statusMessage = status.getReasonPhrase(); | 		this.statusMessage = status.getReasonPhrase(); | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ public class SystemExceptionCatcher implements ExceptionMapper<SystemException> | |||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public Response toResponse(final SystemException exception) { | 	public Response toResponse(final SystemException exception) { | ||||||
|  | 		LOGGER.warn("Catch SystemException:"); | ||||||
| 		final RestErrorResponse ret = build(exception); | 		final RestErrorResponse ret = build(exception); | ||||||
| 		LOGGER.error("Error UUID={}", ret.uuid); | 		LOGGER.error("Error UUID={}", ret.uuid); | ||||||
| 		exception.printStackTrace(); | 		exception.printStackTrace(); | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -31,14 +31,22 @@ public interface DataAccessAddOn { | |||||||
| 	 * @throws SQLException */ | 	 * @throws SQLException */ | ||||||
| 	void insertData(PreparedStatement ps, final Field field, Object data, CountInOut iii) throws Exception, SQLException, IllegalArgumentException, IllegalAccessException; | 	void insertData(PreparedStatement ps, final Field field, Object data, CountInOut iii) throws Exception, SQLException, IllegalArgumentException, IllegalAccessException; | ||||||
|  |  | ||||||
| 	// Element can insert in the single request | 	/** Element can insert in the single request | ||||||
| 	boolean canInsert(final Field field); | 	 * @param field | ||||||
|  | 	 * @return */ | ||||||
|  | 	default boolean canInsert(final Field field) { | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Element can be retrieve with the specific mode | 	/** Element can be retrieve with the specific mode | ||||||
| 	boolean canRetrieve(final Field field); | 	 * @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, | 	void generateQuerry(@NotNull String tableName, @NotNull Field field, @NotNull final StringBuilder querySelect, @NotNull final StringBuilder query, @NotNull String name, @NotNull CountInOut count, | ||||||
| 			@NotNull CountInOut count, QueryOptions options) throws Exception; | 			QueryOptions options) throws Exception; | ||||||
|  |  | ||||||
| 	// Return the number of colomn read | 	// Return the number of colomn read | ||||||
| 	void fillFromQuerry(ResultSet rs, Field field, Object data, CountInOut count, QueryOptions options, final List<LazyGetter> lazyCall) | 	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 createDrop | ||||||
| 	 * @param fieldId | 	 * @param fieldId | ||||||
| 	 * @throws Exception */ | 	 * @throws Exception */ | ||||||
| 	void createTables(String tableName, Field field, StringBuilder mainTableBuilder, List<String> preActionList, List<String> postActionList, boolean createIfNotExist, boolean createDrop, int fieldId) | 	void createTables(// | ||||||
| 			throws Exception; | 			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.lang.reflect.Field; | ||||||
| import java.sql.Timestamp; | import java.sql.Timestamp; | ||||||
|  | import java.time.Instant; | ||||||
| import java.time.LocalDate; | import java.time.LocalDate; | ||||||
| import java.time.LocalTime; | import java.time.LocalTime; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Date; | import java.util.Date; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.UUID; | ||||||
|  |  | ||||||
| import org.kar.archidata.annotation.AnnotationTools; | import org.kar.archidata.annotation.AnnotationTools; | ||||||
| import org.kar.archidata.annotation.CreationTimestamp; | 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 { | 	public static String convertTypeInSQL(final Class<?> type, final String fieldName) throws Exception { | ||||||
| 		if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | 		if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | ||||||
|  | 			if (type == UUID.class) { | ||||||
|  | 				return "binary(16)"; | ||||||
|  | 			} | ||||||
| 			if (type == Long.class || type == long.class) { | 			if (type == Long.class || type == long.class) { | ||||||
| 				return "bigint"; | 				return "bigint"; | ||||||
| 			} | 			} | ||||||
| @@ -42,6 +47,9 @@ public class DataFactory { | |||||||
| 			if (type == Double.class || type == double.class) { | 			if (type == Double.class || type == double.class) { | ||||||
| 				return "double"; | 				return "double"; | ||||||
| 			} | 			} | ||||||
|  | 			if (type == Instant.class) { | ||||||
|  | 				return "varchar(33)"; | ||||||
|  | 			} | ||||||
| 			if (type == Date.class || type == Timestamp.class) { | 			if (type == Date.class || type == Timestamp.class) { | ||||||
| 				return "timestamp(3)"; | 				return "timestamp(3)"; | ||||||
| 			} | 			} | ||||||
| @@ -75,6 +83,9 @@ public class DataFactory { | |||||||
| 				return out.toString(); | 				return out.toString(); | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
|  | 			if (type == UUID.class) { | ||||||
|  | 				return "BINARY(16)"; | ||||||
|  | 			} | ||||||
| 			if (type == Long.class || type == long.class) { | 			if (type == Long.class || type == long.class) { | ||||||
| 				return "INTEGER"; | 				return "INTEGER"; | ||||||
| 			} | 			} | ||||||
| @@ -90,6 +101,9 @@ public class DataFactory { | |||||||
| 			if (type == Double.class || type == double.class) { | 			if (type == Double.class || type == double.class) { | ||||||
| 				return "REAL"; | 				return "REAL"; | ||||||
| 			} | 			} | ||||||
|  | 			if (type == Instant.class) { | ||||||
|  | 				return "text"; | ||||||
|  | 			} | ||||||
| 			if (type == Date.class || type == Timestamp.class) { | 			if (type == Date.class || type == Timestamp.class) { | ||||||
| 				return "DATETIME"; | 				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, | 	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 boolean createIfNotExist, final boolean createDrop, final int fieldId, final Class<?> classModel) throws Exception { | ||||||
| 		final String name = AnnotationTools.getFieldName(elem); | 		final String name = AnnotationTools.getFieldName(elem); | ||||||
| 		final Integer limitSize = AnnotationTools.getLimitSize(elem); | 		final int limitSize = AnnotationTools.getLimitSize(elem); | ||||||
| 		final boolean notNull = AnnotationTools.getNotNull(elem); | 		final boolean notNull = AnnotationTools.getColumnNotNull(elem); | ||||||
|  |  | ||||||
| 		final boolean primaryKey = AnnotationTools.isPrimaryKey(elem); | 		final boolean primaryKey = AnnotationTools.isPrimaryKey(elem); | ||||||
| 		final GenerationType strategy = AnnotationTools.getStrategy(elem); | 		final GenerationType strategy = AnnotationTools.getStrategy(elem); | ||||||
| @@ -152,7 +166,7 @@ public class DataFactory { | |||||||
| 		String typeValue = null; | 		String typeValue = null; | ||||||
| 		typeValue = convertTypeInSQL(classModel, name); | 		typeValue = convertTypeInSQL(classModel, name); | ||||||
| 		if ("text".equals(typeValue) && !"sqlite".equals(ConfigBaseVariable.getDBType())) { | 		if ("text".equals(typeValue) && !"sqlite".equals(ConfigBaseVariable.getDBType())) { | ||||||
| 			if (limitSize != null) { | 			if (limitSize > 0) { | ||||||
| 				mainTableBuilder.append("varchar("); | 				mainTableBuilder.append("varchar("); | ||||||
| 				mainTableBuilder.append(limitSize); | 				mainTableBuilder.append(limitSize); | ||||||
| 				mainTableBuilder.append(")"); | 				mainTableBuilder.append(")"); | ||||||
| @@ -172,16 +186,24 @@ public class DataFactory { | |||||||
| 			} | 			} | ||||||
| 			if (defaultValue == null) { | 			if (defaultValue == null) { | ||||||
| 				if (updateTime || createTime) { | 				if (updateTime || createTime) { | ||||||
| 					mainTableBuilder.append("DEFAULT CURRENT_TIMESTAMP"); | 					if ("varchar(33)".equals(typeValue)) { | ||||||
| 					if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | 						mainTableBuilder.append("DEFAULT DATE_FORMAT(now(6), '%Y-%m-%dT%H:%m:%s.%fZ')"); | ||||||
| 						mainTableBuilder.append("(3)"); | 					} else { | ||||||
|  | 						mainTableBuilder.append("DEFAULT CURRENT_TIMESTAMP"); | ||||||
|  | 						if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | ||||||
|  | 							mainTableBuilder.append("(3)"); | ||||||
|  | 						} | ||||||
| 					} | 					} | ||||||
| 					mainTableBuilder.append(" "); | 					mainTableBuilder.append(" "); | ||||||
| 				} | 				} | ||||||
| 				if (updateTime) { | 				if (updateTime) { | ||||||
| 					if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | 					if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | ||||||
| 						mainTableBuilder.append("ON UPDATE CURRENT_TIMESTAMP"); | 						if ("varchar(33)".equals(typeValue)) { | ||||||
| 						mainTableBuilder.append("(3)"); | 							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 { | 					} else { | ||||||
| 						// TODO: add trigger: | 						// 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; */ | 						/* 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(" SET "); | ||||||
| 						triggerBuilder.append(name); | 						triggerBuilder.append(name); | ||||||
| 						// triggerBuilder.append(" = datetime('now') WHERE id = NEW.id; \n"); | 						// triggerBuilder.append(" = datetime('now') WHERE id = NEW.id; \n"); | ||||||
| 						triggerBuilder.append(" = strftime('%Y-%m-%d %H:%M:%f', '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;"); | 						triggerBuilder.append("END;"); | ||||||
|  |  | ||||||
| 						postOtherTables.add(triggerBuilder.toString()); | 						postOtherTables.add(triggerBuilder.toString()); | ||||||
| @@ -241,8 +267,11 @@ public class DataFactory { | |||||||
| 			mainTableBuilder.append("PRIMARY KEY "); | 			mainTableBuilder.append("PRIMARY KEY "); | ||||||
|  |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (strategy == GenerationType.IDENTITY) { | 		if (strategy == GenerationType.IDENTITY) { | ||||||
| 			if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | 			if ("binary(16)".equals(typeValue)) { | ||||||
|  |  | ||||||
|  | 			} else if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | ||||||
| 				mainTableBuilder.append("AUTO_INCREMENT "); | 				mainTableBuilder.append("AUTO_INCREMENT "); | ||||||
| 			} else { | 			} else { | ||||||
| 				mainTableBuilder.append("AUTOINCREMENT "); | 				mainTableBuilder.append("AUTOINCREMENT "); | ||||||
| @@ -310,6 +339,7 @@ public class DataFactory { | |||||||
| 		LOGGER.debug("===> TABLE `{}`", tableName); | 		LOGGER.debug("===> TABLE `{}`", tableName); | ||||||
| 		final List<String> primaryKeys = new ArrayList<>(); | 		final List<String> primaryKeys = new ArrayList<>(); | ||||||
|  |  | ||||||
|  | 		final Field primaryField = AnnotationTools.getPrimaryKeyField(clazz); | ||||||
| 		for (final Field elem : clazz.getFields()) { | 		for (final Field elem : clazz.getFields()) { | ||||||
| 			// DEtect the primary key (support only one primary key right now... | 			// DEtect the primary key (support only one primary key right now... | ||||||
| 			if (AnnotationTools.isPrimaryKey(elem)) { | 			if (AnnotationTools.isPrimaryKey(elem)) { | ||||||
| @@ -344,7 +374,7 @@ public class DataFactory { | |||||||
| 					final DataAccessAddOn addOn = DataAccess.findAddOnforField(elem); | 					final DataAccessAddOn addOn = DataAccess.findAddOnforField(elem); | ||||||
| 					LOGGER.trace("Create type for: {} ==> {} (ADD-ON)", AnnotationTools.getFieldName(elem), elem.getType()); | 					LOGGER.trace("Create type for: {} ==> {} (ADD-ON)", AnnotationTools.getFieldName(elem), elem.getType()); | ||||||
| 					if (addOn != null) { | 					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 { | 					} else { | ||||||
| 						throw new DataAccessException( | 						throw new DataAccessException( | ||||||
| 								"Element matked as add-on but add-on does not loaded: table:" + tableName + " field name=" + AnnotationTools.getFieldName(elem) + " type=" + elem.getType()); | 								"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); | 		Collections.addAll(this.childs, items); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	public void add(final QueryItem... items) { | ||||||
|  | 		Collections.addAll(this.childs, items); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void generateQuerry(final StringBuilder querry, final String tableName) { | 	public void generateQuerry(final StringBuilder query, final String tableName) { | ||||||
| 		if (this.childs.size() >= 1) { | 		if (this.childs.size() >= 1) { | ||||||
| 			querry.append(" ("); | 			query.append(" ("); | ||||||
| 		} | 		} | ||||||
| 		boolean first = true; | 		boolean first = true; | ||||||
| 		for (final QueryItem elem : this.childs) { | 		for (final QueryItem elem : this.childs) { | ||||||
| 			if (first) { | 			if (first) { | ||||||
| 				first = false; | 				first = false; | ||||||
| 			} else { | 			} else { | ||||||
| 				querry.append(" AND "); | 				query.append(" AND "); | ||||||
| 			} | 			} | ||||||
| 			elem.generateQuerry(querry, tableName); | 			elem.generateQuerry(query, tableName); | ||||||
| 		} | 		} | ||||||
| 		if (this.childs.size() >= 1) { | 		if (this.childs.size() >= 1) { | ||||||
| 			querry.append(")"); | 			query.append(")"); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -43,4 +47,8 @@ public class QueryAnd implements QueryItem { | |||||||
| 			elem.injectQuerry(ps, iii); | 			elem.injectQuerry(ps, iii); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	public int size() { | ||||||
|  | 		return this.childs.size(); | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,14 +14,15 @@ public class QueryCondition implements QueryItem { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void generateQuerry(final StringBuilder querry, final String tableName) { | 	public void generateQuerry(final StringBuilder query, final String tableName) { | ||||||
| 		querry.append(tableName); | 		if (tableName != null) { | ||||||
| 		querry.append("."); | 			query.append(tableName); | ||||||
| 		querry.append(this.key); | 			query.append("."); | ||||||
| 		querry.append(" "); | 		} | ||||||
| 		querry.append(this.comparator); | 		query.append(this.key); | ||||||
| 		querry.append(" ?"); | 		query.append(" "); | ||||||
|  | 		query.append(this.comparator); | ||||||
|  | 		query.append(" ?"); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
|   | |||||||
| @@ -18,22 +18,28 @@ public class QueryInList<T> implements QueryItem { | |||||||
| 		this(key, "IN", value); | 		this(key, "IN", value); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	public QueryInList(final String key, final T... value) { | ||||||
|  | 		this(key, "IN", List.of(value)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void generateQuerry(final StringBuilder querry, final String tableName) { | 	public void generateQuerry(final StringBuilder query, final String tableName) { | ||||||
| 		querry.append(tableName); | 		if (tableName != null) { | ||||||
| 		querry.append("."); | 			query.append(tableName); | ||||||
| 		querry.append(this.key); | 			query.append("."); | ||||||
| 		querry.append(" "); | 		} | ||||||
| 		querry.append(this.comparator); | 		query.append(this.key); | ||||||
| 		querry.append(" ("); | 		query.append(" "); | ||||||
|  | 		query.append(this.comparator); | ||||||
|  | 		query.append(" ("); | ||||||
| 		for (int iii = 0; iii < this.value.size(); iii++) { | 		for (int iii = 0; iii < this.value.size(); iii++) { | ||||||
| 			if (iii != 0) { | 			if (iii != 0) { | ||||||
| 				querry.append(",?"); | 				query.append(",?"); | ||||||
| 			} else { | 			} else { | ||||||
| 				querry.append("?"); | 				query.append("?"); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		querry.append(")"); | 		query.append(")"); | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ package org.kar.archidata.dataAccess; | |||||||
| import java.sql.PreparedStatement; | import java.sql.PreparedStatement; | ||||||
|  |  | ||||||
| public interface QueryItem { | public interface QueryItem { | ||||||
| 	void generateQuerry(StringBuilder querry, String tableName); | 	void generateQuerry(StringBuilder query, String tableName); | ||||||
|  |  | ||||||
| 	void injectQuerry(PreparedStatement ps, CountInOut iii) throws Exception; | 	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<>(); | 	private final List<QueryOption> options = new ArrayList<>(); | ||||||
|  |  | ||||||
|  | 	public QueryOptions() {} | ||||||
|  |  | ||||||
| 	public QueryOptions(final QueryOption... elems) { | 	public QueryOptions(final QueryOption... elems) { | ||||||
|  | 		if (elems == null || elems.length == 0) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
| 		Collections.addAll(this.options, elems); | 		Collections.addAll(this.options, elems); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public QueryOptions() {} |  | ||||||
|  |  | ||||||
| 	public void add(final QueryOption option) { | 	public void add(final QueryOption option) { | ||||||
| 		this.options.add(option); | 		this.options.add(option); | ||||||
| 	} | 	} | ||||||
| @@ -32,6 +35,10 @@ public class QueryOptions { | |||||||
| 		return this.options; | 		return this.options; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	public QueryOption[] getAllArray() { | ||||||
|  | 		return this.options.toArray(new QueryOption[0]); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	@SuppressWarnings("unchecked") | 	@SuppressWarnings("unchecked") | ||||||
| 	public <T> T get(final Class<T> type) { | 	public <T> T get(final Class<T> type) { | ||||||
| 		for (final QueryOption elem : this.options) { | 		for (final QueryOption elem : this.options) { | ||||||
| @@ -57,5 +64,4 @@ public class QueryOptions { | |||||||
| 		} | 		} | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,22 +10,26 @@ public class QueryOr implements QueryItem { | |||||||
| 		this.childs = childs; | 		this.childs = childs; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	public QueryOr(final QueryItem... childs) { | ||||||
|  | 		this.childs = List.of(childs); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void generateQuerry(final StringBuilder querry, final String tableName) { | 	public void generateQuerry(final StringBuilder query, final String tableName) { | ||||||
| 		if (this.childs.size() >= 1) { | 		if (this.childs.size() >= 1) { | ||||||
| 			querry.append(" ("); | 			query.append(" ("); | ||||||
| 		} | 		} | ||||||
| 		boolean first = true; | 		boolean first = true; | ||||||
| 		for (final QueryItem elem : this.childs) { | 		for (final QueryItem elem : this.childs) { | ||||||
| 			if (first) { | 			if (first) { | ||||||
| 				first = false; | 				first = false; | ||||||
| 			} else { | 			} else { | ||||||
| 				querry.append(" OR "); | 				query.append(" OR "); | ||||||
| 			} | 			} | ||||||
| 			elem.generateQuerry(querry, tableName); | 			elem.generateQuerry(query, tableName); | ||||||
| 		} | 		} | ||||||
| 		if (this.childs.size() >= 1) { | 		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; | package org.kar.archidata.dataAccess.addOn; | ||||||
|  |  | ||||||
| import java.lang.reflect.Field; | import java.lang.reflect.Field; | ||||||
|  | import java.lang.reflect.ParameterizedType; | ||||||
| import java.sql.PreparedStatement; | import java.sql.PreparedStatement; | ||||||
| import java.sql.ResultSet; | import java.sql.ResultSet; | ||||||
| import java.sql.Types; | import java.sql.Types; | ||||||
|  | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.UUID; | ||||||
|  |  | ||||||
| import org.kar.archidata.annotation.AnnotationTools; | import org.kar.archidata.annotation.AnnotationTools; | ||||||
| import org.kar.archidata.annotation.DataJson; | import org.kar.archidata.annotation.DataJson; | ||||||
| import org.kar.archidata.dataAccess.CountInOut; | import org.kar.archidata.dataAccess.CountInOut; | ||||||
|  | import org.kar.archidata.dataAccess.DataAccess; | ||||||
| import org.kar.archidata.dataAccess.DataAccessAddOn; | import org.kar.archidata.dataAccess.DataAccessAddOn; | ||||||
| import org.kar.archidata.dataAccess.DataFactory; | import org.kar.archidata.dataAccess.DataFactory; | ||||||
| import org.kar.archidata.dataAccess.LazyGetter; | import org.kar.archidata.dataAccess.LazyGetter; | ||||||
| import org.kar.archidata.dataAccess.QueryOptions; | 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.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
| import com.fasterxml.jackson.annotation.JsonValue; | import com.fasterxml.jackson.annotation.JsonValue; | ||||||
|  | import com.fasterxml.jackson.core.type.TypeReference; | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper; | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
|  |  | ||||||
| import jakarta.validation.constraints.NotNull; | import jakarta.validation.constraints.NotNull; | ||||||
| @@ -58,6 +68,11 @@ public class AddOnDataJson implements DataAccessAddOn { | |||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public boolean isInsertAsync(final Field field) throws Exception { | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public boolean canRetrieve(final Field field) { | 	public boolean canRetrieve(final Field field) { | ||||||
| 		return true; | 		return true; | ||||||
| @@ -80,14 +95,196 @@ public class AddOnDataJson implements DataAccessAddOn { | |||||||
| 		count.inc(); | 		count.inc(); | ||||||
| 		if (!rs.wasNull()) { | 		if (!rs.wasNull()) { | ||||||
| 			final ObjectMapper objectMapper = new ObjectMapper(); | 			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()); | 			final Object dataParsed = objectMapper.readValue(jsonData, field.getType()); | ||||||
| 			field.set(data, dataParsed); | 			field.set(data, dataParsed); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void createTables(final String tableName, final Field field, final StringBuilder mainTableBuilder, final List<String> preActionList, final List<String> postActionList, | 	public void createTables(// | ||||||
| 			final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception { | 			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); | 		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.sql.SQLException; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.UUID; | ||||||
|  |  | ||||||
| import org.kar.archidata.annotation.AnnotationTools; | import org.kar.archidata.annotation.AnnotationTools; | ||||||
| import org.kar.archidata.dataAccess.CountInOut; | 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.QueryCondition; | ||||||
| import org.kar.archidata.dataAccess.QueryInList; | import org.kar.archidata.dataAccess.QueryInList; | ||||||
| import org.kar.archidata.dataAccess.QueryOptions; | 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.dataAccess.options.OverrideTableName; | ||||||
| import org.kar.archidata.exception.DataAccessException; | import org.kar.archidata.exception.DataAccessException; | ||||||
| import org.kar.archidata.tools.ConfigBaseVariable; | import org.kar.archidata.tools.ConfigBaseVariable; | ||||||
| @@ -31,7 +36,8 @@ import jakarta.validation.constraints.NotNull; | |||||||
|  |  | ||||||
| public class AddOnManyToMany implements DataAccessAddOn { | public class AddOnManyToMany implements DataAccessAddOn { | ||||||
| 	static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class); | 	static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class); | ||||||
| 	static final String SEPARATOR = "-"; | 	static final String SEPARATOR_LONG = "-"; | ||||||
|  | 	static final String SEPARATOR_UUID = "_"; | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public Class<?> getAnnotationClass() { | 	public Class<?> getAnnotationClass() { | ||||||
| @@ -61,7 +67,15 @@ public class AddOnManyToMany implements DataAccessAddOn { | |||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public boolean canRetrieve(final Field field) { | 	public boolean canRetrieve(final Field field) { | ||||||
| 		return true; | 		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 { | 	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; | 		return tableName + "_link_" + localName; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public void generateConcatQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, | 	public void generateConcatQuerry( // | ||||||
| 			@NotNull final String name, @NotNull final CountInOut elemCount, final QueryOptions options) { | 			@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 String linkTableName = generateLinkTableName(tableName, name); | ||||||
|  | 		final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; | ||||||
| 		final String tmpVariable = "tmp_" + Integer.toString(elemCount.value); | 		final String tmpVariable = "tmp_" + Integer.toString(elemCount.value); | ||||||
| 		querrySelect.append(" (SELECT GROUP_CONCAT("); | 		querrySelect.append(" (SELECT GROUP_CONCAT("); | ||||||
| 		querrySelect.append(tmpVariable); | 		if (objectClass == Long.class) { | ||||||
| 		querrySelect.append(".object2Id "); | 			querrySelect.append(tmpVariable); | ||||||
| 		if (ConfigBaseVariable.getDBType().equals("sqlite")) { | 			querrySelect.append(".object2Id "); | ||||||
|  | 		} else { | ||||||
|  | 			querrySelect.append("BIN_TO_UUID("); | ||||||
|  | 			querrySelect.append(tmpVariable); | ||||||
|  | 			querrySelect.append(".object2Id) "); | ||||||
|  | 		} | ||||||
|  | 		if ("sqlite".equals(ConfigBaseVariable.getDBType())) { | ||||||
| 			querrySelect.append(", "); | 			querrySelect.append(", "); | ||||||
| 		} else { | 		} else { | ||||||
| 			querrySelect.append("SEPARATOR "); | 			querrySelect.append("SEPARATOR "); | ||||||
| 		} | 		} | ||||||
| 		querrySelect.append("'"); | 		querrySelect.append("'"); | ||||||
| 		querrySelect.append(SEPARATOR); | 		if (objectClass == Long.class) { | ||||||
|  | 			querrySelect.append(SEPARATOR_LONG); | ||||||
|  | 		} else { | ||||||
|  | 			querrySelect.append(SEPARATOR_UUID); | ||||||
|  |  | ||||||
|  | 		} | ||||||
| 		querrySelect.append("') FROM "); | 		querrySelect.append("') FROM "); | ||||||
| 		querrySelect.append(linkTableName); | 		querrySelect.append(linkTableName); | ||||||
| 		querrySelect.append(" "); | 		querrySelect.append(" "); | ||||||
| 		querrySelect.append(tmpVariable); | 		querrySelect.append(tmpVariable); | ||||||
| 		querrySelect.append(" WHERE "); | 		querrySelect.append(" WHERE "); | ||||||
| 		querrySelect.append(tmpVariable); | 		/* querrySelect.append(tmpVariable); querrySelect.append(".deleted = false AND "); */ | ||||||
| 		querrySelect.append(".deleted = false AND "); |  | ||||||
| 		querrySelect.append(tableName); | 		querrySelect.append(tableName); | ||||||
| 		querrySelect.append(".id = "); | 		querrySelect.append(".id = "); | ||||||
| 		querrySelect.append(tmpVariable); | 		querrySelect.append(tmpVariable); | ||||||
| 		querrySelect.append("."); | 		querrySelect.append("."); | ||||||
| 		querrySelect.append("object1Id "); | 		querrySelect.append("object1Id "); | ||||||
| 		if (!ConfigBaseVariable.getDBType().equals("sqlite")) { | 		if (!"sqlite".equals(ConfigBaseVariable.getDBType())) { | ||||||
| 			querrySelect.append(" GROUP BY "); | 			querrySelect.append(" GROUP BY "); | ||||||
| 			querrySelect.append(tmpVariable); | 			querrySelect.append(tmpVariable); | ||||||
| 			querrySelect.append(".object1Id"); | 			querrySelect.append(".object1Id"); | ||||||
| @@ -118,13 +149,20 @@ public class AddOnManyToMany implements DataAccessAddOn { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void generateQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, @NotNull final String name, | 	public void generateQuerry( // | ||||||
| 			@NotNull final CountInOut elemCount, final QueryOptions options) throws Exception { | 			@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) { | 		if (field.getType() != List.class) { | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 		final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; | 		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); | 			generateConcatQuerry(tableName, field, querrySelect, querry, name, elemCount, options); | ||||||
| 		} | 		} | ||||||
| 		final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class); | 		final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class); | ||||||
| @@ -141,15 +179,29 @@ public class AddOnManyToMany implements DataAccessAddOn { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@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) { | 		if (field.getType() != List.class) { | ||||||
|  | 			LOGGER.error("Can not ManyToMany with other than List Model: {}", field.getType().getCanonicalName()); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 		final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; | 		final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; | ||||||
| 		if (objectClass == Long.class) { | 		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); | 			field.set(data, idList); | ||||||
| 			count.inc(); | 			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); | 		final ManyToMany decorators = field.getDeclaredAnnotation(ManyToMany.class); | ||||||
| 		if (decorators == null) { | 		if (decorators == null) { | ||||||
| @@ -158,8 +210,8 @@ public class AddOnManyToMany implements DataAccessAddOn { | |||||||
| 		if (objectClass == decorators.targetEntity()) { | 		if (objectClass == decorators.targetEntity()) { | ||||||
| 			if (decorators.fetch() == FetchType.EAGER) { | 			if (decorators.fetch() == FetchType.EAGER) { | ||||||
| 				throw new DataAccessException("EAGER is not supported for list of element..."); | 				throw new DataAccessException("EAGER is not supported for list of element..."); | ||||||
| 			} else { | 			} else 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); | 				// field.set(data, idList); | ||||||
| 				count.inc(); | 				count.inc(); | ||||||
| 				if (idList != null && idList.size() > 0) { | 				if (idList != null && idList.size() > 0) { | ||||||
| @@ -169,7 +221,26 @@ public class AddOnManyToMany implements DataAccessAddOn { | |||||||
| 						final List<Long> childs = new ArrayList<>(idList); | 						final List<Long> childs = new ArrayList<>(idList); | ||||||
| 						// TODO: update to have get with abstract types .... | 						// TODO: update to have get with abstract types .... | ||||||
| 						@SuppressWarnings("unchecked") | 						@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) { | 						if (foreignData == null) { | ||||||
| 							return; | 							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 { | 	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 tableName = AnnotationTools.getTableName(clazz); | ||||||
| 		final String linkTableName = generateLinkTableName(tableName, column); | 		final String linkTableName = generateLinkTableName(tableName, column); | ||||||
| 		final LinkTable insertElement = new LinkTable(localKey, remoteKey); | 		/* final Class<?> objectClass = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; if (objectClass != Long.class && objectClass != UUID.class) { throw new | ||||||
| 		final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName)); | 		 * DataAccessException("Can not ManyToMany with other than List<Long> or List<UUID> Model: List<" + objectClass.getCanonicalName() + ">"); } */ | ||||||
| 		DataAccess.insert(insertElement, options); | 		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 { | 	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 tableName = AnnotationTools.getTableName(clazz); | ||||||
| 		final String linkTableName = generateLinkTableName(tableName, column); | 		final String linkTableName = generateLinkTableName(tableName, column); | ||||||
| 		final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName)); | 		return DataAccess.deleteWhere(LinkTableLongLong.class, new OverrideTableName(linkTableName), | ||||||
| 		final QueryAnd condition = new QueryAnd(new QueryCondition("object1Id", "=", localKey), new QueryCondition("object2Id", "=", remoteKey)); | 				new Condition(new QueryAnd(new QueryCondition("object1Id", "=", localKey), new QueryCondition("object2Id", "=", remoteKey)))); | ||||||
| 		return DataAccess.deleteWhere(LinkTable.class, condition, options); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void createTables(final String tableName, final Field field, final StringBuilder mainTableBuilder, final List<String> preActionList, final List<String> postActionList, | 	public void createTables( // | ||||||
| 			final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception { | 			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 String linkTableName = generateLinkTableNameField(tableName, field); | ||||||
| 		final QueryOptions options = new QueryOptions(new OverrideTableName(linkTableName)); | 		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]; | ||||||
| 		postActionList.addAll(sqlCommand); | 		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.ResultSet; | ||||||
| import java.sql.Types; | import java.sql.Types; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.UUID; | ||||||
|  |  | ||||||
| import org.kar.archidata.annotation.AnnotationTools; | import org.kar.archidata.annotation.AnnotationTools; | ||||||
| import org.kar.archidata.dataAccess.CountInOut; | 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.LazyGetter; | ||||||
| import org.kar.archidata.dataAccess.QueryOptions; | import org.kar.archidata.dataAccess.QueryOptions; | ||||||
| import org.kar.archidata.exception.DataAccessException; | import org.kar.archidata.exception.DataAccessException; | ||||||
|  | import org.kar.archidata.tools.UuidUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
| @@ -30,12 +32,11 @@ public class AddOnManyToOne implements DataAccessAddOn { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public String getSQLFieldType(final Field elem) throws Exception { | 	public String getSQLFieldType(final Field field) throws Exception { | ||||||
| 		final String fieldName = AnnotationTools.getFieldName(elem); | 		final String fieldName = AnnotationTools.getFieldName(field); | ||||||
| 		try { | 		try { | ||||||
| 			return DataFactory.convertTypeInSQL(Long.class, fieldName); | 			return DataFactory.convertTypeInSQL(field.getType(), fieldName); | ||||||
| 		} catch (final Exception e) { | 		} catch (final Exception e) { | ||||||
| 			// TODO Auto-generated catch block |  | ||||||
| 			e.printStackTrace(); | 			e.printStackTrace(); | ||||||
| 		} | 		} | ||||||
| 		return null; | 		return null; | ||||||
| @@ -43,18 +44,41 @@ public class AddOnManyToOne implements DataAccessAddOn { | |||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public boolean isCompatibleField(final Field elem) { | 	public boolean isCompatibleField(final Field elem) { | ||||||
| 		final ManyToOne decorators = elem.getDeclaredAnnotation(ManyToOne.class); | 		return elem.getDeclaredAnnotation(ManyToOne.class) != null; | ||||||
| 		return decorators != null; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii) throws Exception { | 	public void insertData(final PreparedStatement ps, final Field field, final Object rootObject, final CountInOut iii) throws Exception { | ||||||
| 		final Object data = field.get(rootObject); | 		final Object data = field.get(rootObject); | ||||||
| 		if (data == null) { | 		if (data == null) { | ||||||
| 			ps.setNull(iii.value, Types.BIGINT); | 			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) { | 		} else if (field.getType() == Long.class) { | ||||||
| 			final Long dataLong = (Long) data; | 			final Long dataTyped = (Long) data; | ||||||
| 			ps.setLong(iii.value, dataLong); | 			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 { | 		} else { | ||||||
| 			final Field idField = AnnotationTools.getFieldOfId(field.getType()); | 			final Field idField = AnnotationTools.getFieldOfId(field.getType()); | ||||||
| 			final Object uid = idField.get(data); | 			final Object uid = idField.get(data); | ||||||
| @@ -71,7 +95,11 @@ public class AddOnManyToOne implements DataAccessAddOn { | |||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public boolean canInsert(final Field field) { | 	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; | 			return true; | ||||||
| 		} | 		} | ||||||
| 		final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); | 		final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); | ||||||
| @@ -81,9 +109,19 @@ public class AddOnManyToOne implements DataAccessAddOn { | |||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public boolean isInsertAsync(final Field field) throws Exception { | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public boolean canRetrieve(final Field field) { | 	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; | 			return true; | ||||||
| 		} | 		} | ||||||
| 		final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); | 		final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); | ||||||
| @@ -94,9 +132,20 @@ public class AddOnManyToOne implements DataAccessAddOn { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void generateQuerry(@NotNull final String tableName, @NotNull final Field field, @NotNull final StringBuilder querrySelect, @NotNull final StringBuilder querry, @NotNull final String name, | 	public void generateQuerry( // | ||||||
| 			@NotNull final CountInOut elemCount, final QueryOptions options) throws Exception { | 			@NotNull final String tableName, // | ||||||
| 		if (field.getType() == Long.class) { | 			@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(" "); | ||||||
| 			querrySelect.append(tableName); | 			querrySelect.append(tableName); | ||||||
| 			querrySelect.append("."); | 			querrySelect.append("."); | ||||||
| @@ -145,6 +194,39 @@ public class AddOnManyToOne implements DataAccessAddOn { | |||||||
| 			} | 			} | ||||||
| 			return; | 			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 Class<?> objectClass = field.getType(); | ||||||
| 		final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); | 		final ManyToOne decorators = field.getDeclaredAnnotation(ManyToOne.class); | ||||||
| 		if (decorators == null) { | 		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 | 	// TODO : refacto this table to manage a generic table with dynamic name to be serialisable with the default system | ||||||
| 	@Override | 	@Override | ||||||
| 	public void createTables(final String tableName, final Field field, final StringBuilder mainTableBuilder, final List<String> preActionList, final List<String> postActionList, | 	public void createTables(// | ||||||
| 			final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception { | 			final String tableName, // | ||||||
| 		DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, Long.class); | 			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; | 		return false; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public boolean isInsertAsync(final Field field) throws Exception { | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public boolean canRetrieve(final Field field) { | 	public boolean canRetrieve(final Field field) { | ||||||
| 		return false; | 		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 | 	// TODO : refacto this table to manage a generic table with dynamic name to be serializable with the default system | ||||||
| 	@Override | 	@Override | ||||||
| 	public void createTables(final String tableName, final Field field, final StringBuilder mainTableBuilder, final List<String> preActionList, final List<String> postActionList, | 	public void createTables(// | ||||||
| 			final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception { | 			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); | 		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; | import jakarta.validation.constraints.NotNull; | ||||||
|  |  | ||||||
|  | // TODO: maybe deprecated ==> use DataJson instead... | ||||||
| public class AddOnSQLTableExternalForeinKeyAsList implements DataAccessAddOn { | public class AddOnSQLTableExternalForeinKeyAsList implements DataAccessAddOn { | ||||||
| 	static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class); | 	static final Logger LOGGER = LoggerFactory.getLogger(AddOnManyToMany.class); | ||||||
| 	static final String SEPARATOR = "-"; | 	static final String SEPARATOR = "-"; | ||||||
| @@ -72,12 +73,17 @@ public class AddOnSQLTableExternalForeinKeyAsList implements DataAccessAddOn { | |||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public boolean canInsert(final Field field) { | 	public boolean canInsert(final Field field) { | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	@Override | ||||||
|  | 	public boolean isInsertAsync(final Field field) throws Exception { | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public boolean canRetrieve(final Field field) { | 	public boolean canRetrieve(final Field field) { | ||||||
| 		return false; | 		return true; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| @@ -99,8 +105,17 @@ public class AddOnSQLTableExternalForeinKeyAsList implements DataAccessAddOn { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void createTables(final String tableName, final Field field, final StringBuilder mainTableBuilder, final List<String> preActionList, final List<String> postActionList, | 	public void createTables(// | ||||||
| 			final boolean createIfNotExist, final boolean createDrop, final int fieldId) throws Exception { | 			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 | 		// TODO Auto-generated method stub | ||||||
|  |  | ||||||
| 		DataFactory.createTablesSpecificType(tableName, field, mainTableBuilder, preActionList, postActionList, createIfNotExist, createDrop, fieldId, String.class); | 		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 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. */ | /** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */ | ||||||
| public interface CheckFunctionInterface { | public interface CheckFunctionInterface { | ||||||
| 	/** This function implementation is design to check if the updated class is valid of not for insertion | 	/** This function implementation is design to check if the updated class is valid of not for insertion | ||||||
|  | 	 * @param baseName NAme of the object to be precise with the use of what fail. | ||||||
| 	 * @param data The object that might be injected. | 	 * @param data The object that might be injected. | ||||||
| 	 * @param filterValue List of fields that might be check. If null, then all column must be checked. | 	 * @param 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. */ | 	 * @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 { | 		try { | ||||||
| 			this.connection = DriverManager.getConnection(this.config.getUrl(true), this.config.getLogin(), this.config.getPassword()); | 			this.connection = DriverManager.getConnection(this.config.getUrl(true), this.config.getLogin(), this.config.getPassword()); | ||||||
| 		} catch (final SQLException ex) { | 		} 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 { | 		try { | ||||||
| 			this.connection = DriverManager.getConnection(this.config.getUrl(), this.config.getLogin(), this.config.getPassword()); | 			this.connection = DriverManager.getConnection(this.config.getUrl(), this.config.getLogin(), this.config.getPassword()); | ||||||
| 		} catch (final SQLException ex) { | 		} 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 class RESTErrorResponseExeption extends Exception { | ||||||
| 	public UUID uuid; | 	public UUID uuid; | ||||||
| 	public String time; | 	public String time; | ||||||
| 	public String error; | 	public String name; | ||||||
| 	public String message; | 	public String message; | ||||||
| 	public int status; | 	public int status; | ||||||
| 	public String statusMessage; | 	public String statusMessage; | ||||||
| @@ -13,16 +13,16 @@ public class RESTErrorResponseExeption extends Exception { | |||||||
| 	public RESTErrorResponseExeption() { | 	public RESTErrorResponseExeption() { | ||||||
| 		this.uuid = null; | 		this.uuid = null; | ||||||
| 		this.time = null; | 		this.time = null; | ||||||
| 		this.error = null; | 		this.name = null; | ||||||
| 		this.message = null; | 		this.message = null; | ||||||
| 		this.status = 0; | 		this.status = 0; | ||||||
| 		this.statusMessage = null; | 		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.uuid = uuid; | ||||||
| 		this.time = time; | 		this.time = time; | ||||||
| 		this.error = error; | 		this.name = name; | ||||||
| 		this.message = message; | 		this.message = message; | ||||||
| 		this.status = status; | 		this.status = status; | ||||||
| 		this.statusMessage = statusMessage; | 		this.statusMessage = statusMessage; | ||||||
| @@ -30,7 +30,7 @@ public class RESTErrorResponseExeption extends Exception { | |||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public String toString() { | 	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 + "]"; | 				+ this.statusMessage + "]"; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -38,13 +38,13 @@ import jakarta.ws.rs.ext.Provider; | |||||||
| @Provider | @Provider | ||||||
| @Priority(Priorities.AUTHENTICATION) | @Priority(Priorities.AUTHENTICATION) | ||||||
| public class AuthenticationFilter implements ContainerRequestFilter { | public class AuthenticationFilter implements ContainerRequestFilter { | ||||||
| 	final Logger logger = LoggerFactory.getLogger(AuthenticationFilter.class); | 	private final static Logger LOGGER = LoggerFactory.getLogger(AuthenticationFilter.class); | ||||||
| 	@Context | 	@Context | ||||||
| 	private ResourceInfo resourceInfo; | 	private ResourceInfo resourceInfo; | ||||||
| 	protected final String applicationName; | 	protected final String applicationName; | ||||||
|  |  | ||||||
| 	private static final String AUTHENTICATION_SCHEME = "Yota"; | 	public static final String AUTHENTICATION_SCHEME = "Bearer"; | ||||||
| 	private static final String AUTHENTICATION_TOKEN_SCHEME = "Zota"; | 	public static final String APIKEY = "ApiKey"; | ||||||
|  |  | ||||||
| 	public AuthenticationFilter(final String applicationName) { | 	public AuthenticationFilter(final String applicationName) { | ||||||
| 		this.applicationName = applicationName; | 		this.applicationName = applicationName; | ||||||
| @@ -57,8 +57,8 @@ public class AuthenticationFilter implements ContainerRequestFilter { | |||||||
| 		final Method method = this.resourceInfo.getResourceMethod(); | 		final Method method = this.resourceInfo.getResourceMethod(); | ||||||
| 		// Access denied for all | 		// Access denied for all | ||||||
| 		if (method.isAnnotationPresent(DenyAll.class)) { | 		if (method.isAnnotationPresent(DenyAll.class)) { | ||||||
| 			this.logger.debug("   ==> deny all {}", requestContext.getUriInfo().getPath()); | 			LOGGER.debug("   ==> deny all {}", requestContext.getUriInfo().getPath()); | ||||||
| 			requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access blocked !!!").build()); | 			abortWithForbidden(requestContext, "Access blocked !!!"); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -70,64 +70,62 @@ public class AuthenticationFilter implements ContainerRequestFilter { | |||||||
| 		} | 		} | ||||||
| 		// this is a security guard, all the API must define their access level: | 		// this is a security guard, all the API must define their access level: | ||||||
| 		if (!method.isAnnotationPresent(RolesAllowed.class)) { | 		if (!method.isAnnotationPresent(RolesAllowed.class)) { | ||||||
| 			this.logger.error("   ==> missing @RolesAllowed {}", requestContext.getUriInfo().getPath()); | 			LOGGER.error("   ==> missing @RolesAllowed {}", requestContext.getUriInfo().getPath()); | ||||||
| 			requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access ILLEGAL !!!").build()); | 			abortWithForbidden(requestContext, "Access ILLEGAL !!!"); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Get the Authorization header from the request | 		// Get the Authorization header from the request | ||||||
| 		String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION); | 		String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION); | ||||||
|  | 		String apikeyHeader = requestContext.getHeaderString(APIKEY); | ||||||
| 		// logger.debug("authorizationHeader: {}", authorizationHeader); | 		// 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(); | 			final MultivaluedMap<String, String> quaryparam = requestContext.getUriInfo().getQueryParameters(); | ||||||
| 			for (final Entry<String, List<String>> item : quaryparam.entrySet()) { | 			for (final Entry<String, List<String>> item : quaryparam.entrySet()) { | ||||||
| 				if (item.getKey().equals(HttpHeaders.AUTHORIZATION)) { | 				if ((authorizationHeader == null && HttpHeaders.AUTHORIZATION.equals(item.getKey())) && !item.getValue().isEmpty()) { | ||||||
| 					if (!item.getValue().isEmpty()) { | 					authorizationHeader = item.getValue().get(0); | ||||||
| 						authorizationHeader = item.getValue().get(0); | 				} | ||||||
| 					} | 				if ((apikeyHeader == null && APIKEY.equals(item.getKey())) && !item.getValue().isEmpty()) { | ||||||
| 					break; | 					apikeyHeader = item.getValue().get(0); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		// logger.debug("authorizationHeader: {}", authorizationHeader); | 		// logger.debug("authorizationHeader: {}", authorizationHeader); | ||||||
| 		final boolean isApplicationToken = isApplicationTokenBasedAuthentication(authorizationHeader); | 		final boolean isApplicationToken = apikeyHeader != null; | ||||||
| 		final boolean isJwtToken = isTokenBasedAuthentication(authorizationHeader); | 		final boolean isJwtToken = isTokenBasedAuthentication(authorizationHeader); | ||||||
| 		// Validate the Authorization header data Model "Yota jwt.to.ken" "Zota tokenId:hash(token)" |  | ||||||
| 		if (!isApplicationToken && !isJwtToken) { | 		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()); | 			abortWithUnauthorized(requestContext, "REJECTED unauthorized: " + requestContext.getUriInfo().getPath()); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 		UserByToken userByToken = null; | 		UserByToken userByToken = null; | ||||||
| 		if (isJwtToken) { | 		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(); | 			final String token = authorizationHeader.substring(AUTHENTICATION_SCHEME.length()).trim(); | ||||||
| 			// logger.debug("token: {}", token); | 			// logger.debug("token: {}", token); | ||||||
| 			try { | 			try { | ||||||
| 				userByToken = validateJwtToken(token); | 				userByToken = validateJwtToken(token); | ||||||
| 			} catch (final Exception e) { | 			} 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()); | 				abortWithUnauthorized(requestContext, "Fail to validate token: " + e.getMessage()); | ||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 			if (userByToken == null) { | 			if (userByToken == null) { | ||||||
| 				this.logger.warn("get a NULL user ..."); | 				LOGGER.warn("get a NULL user ..."); | ||||||
| 				abortWithUnauthorized(requestContext, "get a NULL user ..."); | 				abortWithUnauthorized(requestContext, "get a NULL user ..."); | ||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			// Extract the token from the Authorization header (Remove "Zota ") | 			final String token = apikeyHeader.trim(); | ||||||
| 			final String token = authorizationHeader.substring(AUTHENTICATION_TOKEN_SCHEME.length()).trim(); |  | ||||||
| 			// logger.debug("token: {}", token); |  | ||||||
| 			try { | 			try { | ||||||
| 				userByToken = validateToken(token); | 				userByToken = validateToken(token); | ||||||
| 			} catch (final Exception e) { | 			} 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()); | 				abortWithUnauthorized(requestContext, "Fail to validate token: " + e.getMessage()); | ||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 			if (userByToken == null) { | 			if (userByToken == null) { | ||||||
| 				this.logger.warn("get a NULL application ..."); | 				LOGGER.warn("get a NULL application ..."); | ||||||
| 				abortWithUnauthorized(requestContext, "get a NULL application ..."); | 				abortWithUnauthorized(requestContext, "get a NULL application ..."); | ||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| @@ -149,7 +147,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { | |||||||
| 		} | 		} | ||||||
| 		// Is user valid? | 		// Is user valid? | ||||||
| 		if (!haveRight) { | 		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()); | 			requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Not enought RIGHT !!!").build()); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| @@ -164,26 +162,25 @@ public class AuthenticationFilter implements ContainerRequestFilter { | |||||||
| 		return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " "); | 		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) { | 	private void abortWithUnauthorized(final ContainerRequestContext requestContext, final String message) { | ||||||
|  |  | ||||||
| 		// Abort the filter chain with a 401 status code response | 		// Abort the filter chain with a 401 status code response | ||||||
| 		// The WWW-Authenticate header is sent along with the 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); | 		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) | 		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()); | 				.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 { | 	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; | 		return null; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -193,7 +190,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { | |||||||
| 		final JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth", null); | 		final JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth", null); | ||||||
| 		// check the token is valid !!! (signed and coherent issuer... | 		// check the token is valid !!! (signed and coherent issuer... | ||||||
| 		if (ret == null) { | 		if (ret == null) { | ||||||
| 			this.logger.error("The token is not valid: '{}'", authorization); | 			LOGGER.error("The token is not valid: '{}'", authorization); | ||||||
| 			return null; | 			return null; | ||||||
| 		} | 		} | ||||||
| 		// check userID | 		// check userID | ||||||
| @@ -209,7 +206,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { | |||||||
| 			if (rights.containsKey(this.applicationName)) { | 			if (rights.containsKey(this.applicationName)) { | ||||||
| 				user.right = rights.get(this.applicationName); | 				user.right = rights.get(this.applicationName); | ||||||
| 			} else { | 			} 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); | 		// 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()); | 		// System.err.println("filter cors ..." + request.toString()); | ||||||
|  |  | ||||||
| 		response.getHeaders().add("Access-Control-Allow-Origin", "*"); | 		response.getHeaders().add("Access-Control-Allow-Origin", "*"); | ||||||
| 		response.getHeaders().add("Access-Control-Allow-Headers", "*"); | 		response.getHeaders().add("Access-Control-Allow-Headers", "Origin, content-type, Content-type, Accept, Authorization, mime-type, filename"); | ||||||
| 		// "Origin, content-type, Content-type, Accept, authorization, mime-type, filename"); |  | ||||||
| 		response.getHeaders().add("Access-Control-Allow-Credentials", "true"); | 		response.getHeaders().add("Access-Control-Allow-Credentials", "true"); | ||||||
| 		response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, 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 | 	@Override | ||||||
| 	public boolean isSecure() { | 	public boolean isSecure() { | ||||||
| 		return this.sheme.equalsIgnoreCase("https"); | 		return "https".equalsIgnoreCase(this.sheme); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public String getAuthenticationScheme() { | 	public String getAuthenticationScheme() { | ||||||
| 		if (this.contextPrincipale.userByToken != null) { | 		if (this.contextPrincipale.userByToken != null) { | ||||||
| 			return "Zota"; | 			return "Bearer"; | ||||||
| 		} | 		} | ||||||
| 		return null; | 		return null; | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -2,6 +2,9 @@ package org.kar.archidata.filter; | |||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  |  | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
| import jakarta.ws.rs.container.ContainerRequestContext; | import jakarta.ws.rs.container.ContainerRequestContext; | ||||||
| import jakarta.ws.rs.container.ContainerRequestFilter; | import jakarta.ws.rs.container.ContainerRequestFilter; | ||||||
| import jakarta.ws.rs.container.PreMatching; | import jakarta.ws.rs.container.PreMatching; | ||||||
| @@ -11,8 +14,11 @@ import jakarta.ws.rs.ext.Provider; | |||||||
| @Provider | @Provider | ||||||
| @PreMatching | @PreMatching | ||||||
| public class OptionFilter implements ContainerRequestFilter { | public class OptionFilter implements ContainerRequestFilter { | ||||||
|  | 	private static final Logger LOGGER = LoggerFactory.getLogger(OptionFilter.class); | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void filter(final ContainerRequestContext requestContext) throws IOException { | 	public void filter(final ContainerRequestContext requestContext) throws IOException { | ||||||
|  | 		LOGGER.warn("Receive message from: [{}] {}", requestContext.getMethod(), requestContext.getUriInfo().getPath()); | ||||||
| 		if (requestContext.getMethod().contentEquals("OPTIONS")) { | 		if (requestContext.getMethod().contentEquals("OPTIONS")) { | ||||||
| 			requestContext.abortWith(Response.status(Response.Status.NO_CONTENT).build()); | 			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 { | 		try { | ||||||
| 			List<Migration> data = null; | 			List<Migration> data = null; | ||||||
| 			try { | 			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) { | 			} catch (final Exception e) { | ||||||
| 				// Previous version does not have the same timeCode... | 				// Previous version does not have the same timeCode... | ||||||
| 				data = DataAccess.gets(Migration.class); | 				data = DataAccess.gets(Migration.class); | ||||||
|   | |||||||
| @@ -13,13 +13,21 @@ import org.kar.archidata.tools.ConfigBaseVariable; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | 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) { | 	public Action(final String action) { | ||||||
| 		this(action, List.of()); | 		this(action, null, List.of()); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public Action(final String action, final String filterDB) { | 	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++) { | 		for (int iii = 0; iii < this.actions.size(); iii++) { | ||||||
| 			final Action action = this.actions.get(iii); | 			final Action action = this.actions.get(iii); | ||||||
| 			LOGGER.info(" >>>> SQL ACTION : {}/{} ==> filter='{}'\n{}", iii, this.actions.size(), action.filterDB(), action.action()); | 			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()); | 			LOGGER.info(" >>>> SQL ACTION : {}/{}", iii + 1, this.actions.size()); | ||||||
| 			final Action action = this.actions.get(iii); | 			final Action action = this.actions.get(iii); | ||||||
|  |  | ||||||
| 			LOGGER.info("SQL request: ```{}``` on '{}' current={}", action.action(), action.filterDB(), ConfigBaseVariable.getDBType()); | 			if (action.action() != null) { | ||||||
| 			log.append("SQL: " + action.action() + " on " + action.filterDB() + "\n"); | 				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; | 			boolean isValid = true; | ||||||
| 			if (action.filterDB() != null && action.filterDB().size() > 0) { | 			if (action.filterDB() != null && action.filterDB().size() > 0) { | ||||||
| 				isValid = false; | 				isValid = false; | ||||||
| @@ -80,7 +97,11 @@ public class MigrationSqlStep implements MigrationInterface { | |||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| 			try { | 			try { | ||||||
| 				DataAccess.executeQuerry(action.action()); | 				if (action.action() != null) { | ||||||
|  | 					DataAccess.executeQuerry(action.action()); | ||||||
|  | 				} else { | ||||||
|  | 					action.async().doRequest(); | ||||||
|  | 				} | ||||||
| 			} catch (SQLException | IOException ex) { | 			} catch (SQLException | IOException ex) { | ||||||
| 				ex.printStackTrace(); | 				ex.printStackTrace(); | ||||||
| 				LOGGER.info("SQL request ERROR: ", ex.getMessage()); | 				LOGGER.info("SQL request ERROR: ", ex.getMessage()); | ||||||
| @@ -105,9 +126,8 @@ public class MigrationSqlStep implements MigrationInterface { | |||||||
| 				e.printStackTrace(); | 				e.printStackTrace(); | ||||||
| 			} | 			} | ||||||
| 			try { | 			try { | ||||||
| 				Thread.sleep(100); | 				Thread.sleep(2); | ||||||
| 			} catch (final InterruptedException e) { | 			} catch (final InterruptedException e) { | ||||||
| 				// TODO Auto-generated catch block |  | ||||||
| 				e.printStackTrace(); | 				e.printStackTrace(); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -124,10 +144,18 @@ public class MigrationSqlStep implements MigrationInterface { | |||||||
| 		this.actions.add(new Action(action)); | 		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) { | 	public void addAction(final String action, final String filterdBType) { | ||||||
| 		this.actions.add(new Action(action, 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 { | 	public void addClass(final Class<?> clazz) throws Exception { | ||||||
| 		final List<String> tmp = DataFactory.createTable(clazz); | 		final List<String> tmp = DataFactory.createTable(clazz); | ||||||
| 		for (final String elem : tmp) { | 		for (final String elem : tmp) { | ||||||
|   | |||||||
| @@ -1,34 +1,41 @@ | |||||||
| package org.kar.archidata.migration.model; | 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.DataIfNotExists; | ||||||
|  | import org.kar.archidata.annotation.DataNotRead; | ||||||
| import org.kar.archidata.model.GenericDataSoftDelete; | import org.kar.archidata.model.GenericDataSoftDelete; | ||||||
|  |  | ||||||
| import com.fasterxml.jackson.annotation.JsonInclude; | import com.fasterxml.jackson.annotation.JsonInclude; | ||||||
|  |  | ||||||
|  | import io.swagger.v3.oas.annotations.media.Schema; | ||||||
| import jakarta.persistence.Column; | import jakarta.persistence.Column; | ||||||
| import jakarta.persistence.Table; | import jakarta.persistence.Table; | ||||||
|  | import jakarta.ws.rs.DefaultValue; | ||||||
|  |  | ||||||
| // For logs only | // For logs only | ||||||
| //public static final String TABLE_NAME = "KAR_migration"; | //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") | @Table(name = "KAR_migration") | ||||||
| @DataIfNotExists | @DataIfNotExists | ||||||
| @JsonInclude(JsonInclude.Include.NON_NULL) | @JsonInclude(JsonInclude.Include.NON_NULL) | ||||||
| public class Migration extends GenericDataSoftDelete { | 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) | 	@Column(length = 256) | ||||||
| 	public String name; | 	public String name; | ||||||
|  | 	@DataNotRead | ||||||
|  | 	@DefaultValue("'2'") | ||||||
|  | 	@Schema(description = "Version of the migration engine") | ||||||
|  | 	public Integer version; | ||||||
| 	@Column(nullable = false) | 	@Column(nullable = false) | ||||||
| 	@DataDefault("'0'") | 	@DefaultValue("'0'") | ||||||
| 	@DataComment("if the migration is well terminated or not") | 	@Schema(description = "if the migration is well terminated or not") | ||||||
| 	public Boolean terminated = false; | 	public Boolean terminated = false; | ||||||
| 	@DataComment("index in the migration progression") | 	@Schema(description = "index in the migration progression") | ||||||
| 	public Integer stepId = 0; | 	public Integer stepId = 0; | ||||||
| 	@DataComment("number of element in the migration") | 	@Schema(description = "number of element in the migration") | ||||||
| 	public Integer count; | 	public Integer count; | ||||||
| 	@DataComment("Log generate by the migration") | 	@Schema(description = "Log generate by the migration") | ||||||
| 	@Column(length = 0) | 	@Column(length = -1) | ||||||
| 	public String log = ""; | 	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; | package org.kar.archidata.model; | ||||||
|  |  | ||||||
| import org.kar.archidata.annotation.DataComment; |  | ||||||
| import org.kar.archidata.annotation.DataIfNotExists; | import org.kar.archidata.annotation.DataIfNotExists; | ||||||
|  |  | ||||||
| import com.fasterxml.jackson.annotation.JsonInclude; | import com.fasterxml.jackson.annotation.JsonInclude; | ||||||
|  |  | ||||||
|  | import io.swagger.v3.oas.annotations.media.Schema; | ||||||
| import jakarta.persistence.Column; | import jakarta.persistence.Column; | ||||||
| import jakarta.persistence.Table; | import jakarta.persistence.Table; | ||||||
|  |  | ||||||
| @Table(name = "data") | @Table(name = "data") | ||||||
| @DataIfNotExists | @DataIfNotExists | ||||||
| @JsonInclude(JsonInclude.Include.NON_NULL) | @JsonInclude(JsonInclude.Include.NON_NULL) | ||||||
| public class Data extends GenericDataSoftDelete { | public class Data extends UUIDGenericDataSoftDelete { | ||||||
|  |  | ||||||
| 	@Column(length = 128, nullable = false) | 	@Column(length = 128, nullable = false) | ||||||
| 	@DataComment("Sha512 of the data") | 	@Schema(description = "Sha512 of the data") | ||||||
| 	public String sha512; | 	public String sha512; | ||||||
| 	@Column(length = 128, nullable = false) | 	@Column(length = 128, nullable = false) | ||||||
| 	@DataComment("Mime -type of the media") | 	@Schema(description = "Mime -type of the media") | ||||||
| 	public String mimeType; | 	public String mimeType; | ||||||
| 	@Column(nullable = false) | 	@Column(nullable = false) | ||||||
| 	@DataComment("Size in Byte of the data") | 	@Schema(description = "Size in Byte of the data") | ||||||
| 	public Long size; | 	public Long size; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,35 +1,15 @@ | |||||||
| package org.kar.archidata.model; | package org.kar.archidata.model; | ||||||
|  |  | ||||||
| import java.util.Date; | import io.swagger.v3.oas.annotations.media.Schema; | ||||||
|  |  | ||||||
| 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 jakarta.persistence.Column; | import jakarta.persistence.Column; | ||||||
| import jakarta.persistence.GeneratedValue; | import jakarta.persistence.GeneratedValue; | ||||||
| import jakarta.persistence.GenerationType; | import jakarta.persistence.GenerationType; | ||||||
| import jakarta.persistence.Id; | import jakarta.persistence.Id; | ||||||
| import jakarta.persistence.Temporal; |  | ||||||
| import jakarta.persistence.TemporalType; |  | ||||||
|  |  | ||||||
| public class GenericData { | public class GenericData extends GenericTiming { | ||||||
| 	@Id | 	@Id | ||||||
| 	@GeneratedValue(strategy = GenerationType.IDENTITY) | 	@GeneratedValue(strategy = GenerationType.IDENTITY) | ||||||
| 	@Column(nullable = false, unique = true) | 	@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; | 	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; | 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.DataDeleted; | ||||||
| import org.kar.archidata.annotation.DataNotRead; | import org.kar.archidata.annotation.DataNotRead; | ||||||
|  |  | ||||||
|  | import io.swagger.v3.oas.annotations.media.Schema; | ||||||
| import jakarta.persistence.Column; | import jakarta.persistence.Column; | ||||||
|  | import jakarta.validation.constraints.NotNull; | ||||||
|  | import jakarta.ws.rs.DefaultValue; | ||||||
|  |  | ||||||
| public class GenericDataSoftDelete extends GenericData { | public class GenericDataSoftDelete extends GenericData { | ||||||
| 	@DataNotRead | 	@DataNotRead | ||||||
| 	@Column(nullable = false) | 	@Column(nullable = false) | ||||||
| 	@DataDefault("'0'") | 	@DefaultValue("'0'") | ||||||
| 	@DataDeleted | 	@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; | 	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.sql.Timestamp; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| import org.kar.archidata.annotation.DataDefault; |  | ||||||
| import org.kar.archidata.annotation.DataIfNotExists; | import org.kar.archidata.annotation.DataIfNotExists; | ||||||
|  |  | ||||||
| import com.fasterxml.jackson.annotation.JsonInclude; | import com.fasterxml.jackson.annotation.JsonInclude; | ||||||
| @@ -26,6 +25,7 @@ import jakarta.persistence.Column; | |||||||
| import jakarta.persistence.FetchType; | import jakarta.persistence.FetchType; | ||||||
| import jakarta.persistence.ManyToMany; | import jakarta.persistence.ManyToMany; | ||||||
| import jakarta.persistence.Table; | import jakarta.persistence.Table; | ||||||
|  | import jakarta.ws.rs.DefaultValue; | ||||||
|  |  | ||||||
| @Table(name = "user") | @Table(name = "user") | ||||||
| @DataIfNotExists | @DataIfNotExists | ||||||
| @@ -35,13 +35,13 @@ public class User extends GenericDataSoftDelete { | |||||||
| 	public String login = null; | 	public String login = null; | ||||||
|  |  | ||||||
| 	public Timestamp lastConnection = null; | 	public Timestamp lastConnection = null; | ||||||
| 	@DataDefault("'0'") | 	@DefaultValue("'0'") | ||||||
| 	@Column(nullable = false) | 	@Column(nullable = false) | ||||||
| 	public boolean admin = false; | 	public boolean admin = false; | ||||||
| 	@DataDefault("'0'") | 	@DefaultValue("'0'") | ||||||
| 	@Column(nullable = false) | 	@Column(nullable = false) | ||||||
| 	public boolean blocked = false; | 	public boolean blocked = false; | ||||||
| 	@DataDefault("'0'") | 	@DefaultValue("'0'") | ||||||
| 	@Column(nullable = false) | 	@Column(nullable = false) | ||||||
| 	public boolean removed = false; | 	public boolean removed = false; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,6 +14,9 @@ public class ConfigBaseVariable { | |||||||
| 	static public String ssoAdress; | 	static public String ssoAdress; | ||||||
| 	static public String ssoToken; | 	static public String ssoToken; | ||||||
| 	static public String testMode; | 	static public String testMode; | ||||||
|  | 	static public String eMailFrom; | ||||||
|  | 	static public String eMailLogin; | ||||||
|  | 	static public String eMailPassword; | ||||||
|  |  | ||||||
| 	// For test only | 	// For test only | ||||||
| 	public static void clearAllValue() { | 	public static void clearAllValue() { | ||||||
| @@ -30,6 +33,9 @@ public class ConfigBaseVariable { | |||||||
| 		ssoAdress = System.getenv("SSO_ADDRESS"); | 		ssoAdress = System.getenv("SSO_ADDRESS"); | ||||||
| 		ssoToken = System.getenv("SSO_TOKEN"); | 		ssoToken = System.getenv("SSO_TOKEN"); | ||||||
| 		testMode = System.getenv("TEST_MODE"); | 		testMode = System.getenv("TEST_MODE"); | ||||||
|  | 		eMailFrom = System.getenv("EMAIL_FROM"); | ||||||
|  | 		eMailLogin = System.getenv("EMAIL_LOGIN"); | ||||||
|  | 		eMailPassword = System.getenv("EMAIL_PASSWORD"); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	static { | 	static { | ||||||
| @@ -120,4 +126,15 @@ public class ConfigBaseVariable { | |||||||
| 		} | 		} | ||||||
| 		return Boolean.parseBoolean(testMode); | 		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.security.NoSuchAlgorithmException; | ||||||
| import java.sql.SQLException; | import java.sql.SQLException; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.UUID; | ||||||
|  |  | ||||||
| import org.glassfish.jersey.media.multipart.FormDataContentDisposition; | import org.glassfish.jersey.media.multipart.FormDataContentDisposition; | ||||||
|  | import org.kar.archidata.api.DataResource; | ||||||
| import org.kar.archidata.dataAccess.DataAccess; | import org.kar.archidata.dataAccess.DataAccess; | ||||||
| import org.kar.archidata.dataAccess.QueryAnd; | import org.kar.archidata.dataAccess.QueryAnd; | ||||||
| import org.kar.archidata.dataAccess.QueryCondition; | 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.kar.archidata.model.Data; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| @@ -63,19 +67,9 @@ public class DataTools { | |||||||
| 		return filePath; | 		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) { | 	public static Data getWithSha512(final String sha512) { | ||||||
| 		try { | 		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) { | 		} catch (final Exception e) { | ||||||
| 			// TODO Auto-generated catch block | 			// TODO Auto-generated catch block | ||||||
| 			e.printStackTrace(); | 			e.printStackTrace(); | ||||||
| @@ -85,7 +79,7 @@ public class DataTools { | |||||||
|  |  | ||||||
| 	public static Data getWithId(final long id) { | 	public static Data getWithId(final long id) { | ||||||
| 		try { | 		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) { | 		} catch (final Exception e) { | ||||||
| 			// TODO Auto-generated catch block | 			// TODO Auto-generated catch block | ||||||
| 			e.printStackTrace(); | 			e.printStackTrace(); | ||||||
| @@ -109,22 +103,19 @@ public class DataTools { | |||||||
| 		final String tmpPath = getTmpFileInData(tmpUID); | 		final String tmpPath = getTmpFileInData(tmpUID); | ||||||
| 		final long fileSize = Files.size(Paths.get(tmpPath)); | 		final long fileSize = Files.size(Paths.get(tmpPath)); | ||||||
| 		Data out = new Data(); | 		Data out = new Data(); | ||||||
| 		; |  | ||||||
| 		try { | 		try { | ||||||
| 			out.sha512 = sha512; | 			out.sha512 = sha512; | ||||||
| 			out.mimeType = mimeType; | 			out.mimeType = mimeType; | ||||||
| 			out.size = fileSize; | 			out.size = fileSize; | ||||||
| 			out = DataAccess.insert(out); | 			out = DataAccess.insert(out); | ||||||
| 		} catch (final SQLException ex) { |  | ||||||
| 			ex.printStackTrace(); |  | ||||||
| 			return null; |  | ||||||
| 		} catch (final Exception e) { | 		} catch (final Exception e) { | ||||||
| 			// TODO Auto-generated catch block | 			// TODO Auto-generated catch block | ||||||
| 			e.printStackTrace(); | 			e.printStackTrace(); | ||||||
| 			return null; | 			return null; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		final String mediaPath = getFileData(out.id); | 		final String mediaPath = DataResource.getFileData(out.id); | ||||||
| 		LOGGER.info("src = {}", tmpPath); | 		LOGGER.info("src = {}", tmpPath); | ||||||
| 		LOGGER.info("dst = {}", mediaPath); | 		LOGGER.info("dst = {}", mediaPath); | ||||||
| 		Files.move(Paths.get(tmpPath), Paths.get(mediaPath), StandardCopyOption.ATOMIC_MOVE); | 		Files.move(Paths.get(tmpPath), Paths.get(mediaPath), StandardCopyOption.ATOMIC_MOVE); | ||||||
| @@ -134,7 +125,7 @@ public class DataTools { | |||||||
| 		return out; | 		return out; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public static void undelete(final Long id) { | 	public static void undelete(final UUID id) { | ||||||
| 		try { | 		try { | ||||||
| 			DataAccess.unsetDelete(Data.class, id); | 			DataAccess.unsetDelete(Data.class, id); | ||||||
| 		} catch (final Exception e) { | 		} catch (final Exception e) { | ||||||
| @@ -220,7 +211,8 @@ public class DataTools { | |||||||
| 		return data; | 		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 { | 		try { | ||||||
| 			// correct input string stream : | 			// correct input string stream : | ||||||
| 			fileName = multipartCorrection(fileName); | 			fileName = multipartCorrection(fileName); | ||||||
| @@ -231,7 +223,7 @@ public class DataTools { | |||||||
| 			LOGGER.info("    - file_name: ", fileName); | 			LOGGER.info("    - file_name: ", fileName); | ||||||
| 			LOGGER.info("    - fileInputStream: {}", fileInputStream); | 			LOGGER.info("    - fileInputStream: {}", fileInputStream); | ||||||
| 			LOGGER.info("    - fileMetaData: {}", fileMetaData); | 			LOGGER.info("    - fileMetaData: {}", fileMetaData); | ||||||
| 			final T media = DataAccess.get(clazz, id); | 			final CLASS_TYPE media = DataAccess.get(clazz, id); | ||||||
| 			if (media == null) { | 			if (media == null) { | ||||||
| 				return Response.notModified("Media Id does not exist or removed...").build(); | 				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:... | 			// Fist step: retrieve all the Id of each parents:... | ||||||
| 			LOGGER.info("Find typeNode"); | 			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(); | 			return Response.ok(DataAccess.get(clazz, id)).build(); | ||||||
| 		} catch (final Exception ex) { | 		} catch (final Exception ex) { | ||||||
| 			System.out.println("Cat ann unexpected error ... "); | 			System.out.println("Cat ann unexpected error ... "); | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import java.util.Map; | |||||||
| import java.util.Set; | import java.util.Set; | ||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
|  |  | ||||||
|  | import org.kar.archidata.filter.AuthenticationFilter; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
| @@ -51,20 +52,20 @@ class TestSigner implements JWSSigner { | |||||||
| 	@Override | 	@Override | ||||||
| 	public Set<JWSAlgorithm> supportedJWSAlgorithms() { | 	public Set<JWSAlgorithm> supportedJWSAlgorithms() { | ||||||
| 		// TODO Auto-generated method stub | 		// TODO Auto-generated method stub | ||||||
| 		return null; | 		return Set.of(JWSAlgorithm.RS256); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public JCAContext getJCAContext() { | 	public JCAContext getJCAContext() { | ||||||
| 		// TODO Auto-generated method stub | 		// TODO Auto-generated method stub | ||||||
| 		return null; | 		return new JCAContext(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| public class JWTWrapper { | public class JWTWrapper { | ||||||
| 	static final Logger LOGGER = LoggerFactory.getLogger(JWTWrapper.class); | 	static final Logger LOGGER = LoggerFactory.getLogger(JWTWrapper.class); | ||||||
|  |  | ||||||
| 	private static RSAKey rsaJWK = null;; | 	private static RSAKey rsaJWK = null; | ||||||
| 	private static RSAKey rsaPublicJWK = null; | 	private static RSAKey rsaPublicJWK = null; | ||||||
|  |  | ||||||
| 	public static class PublicKey { | 	public static class PublicKey { | ||||||
| @@ -89,7 +90,7 @@ public class JWTWrapper { | |||||||
| 		con.setRequestProperty("Accept", "application/json"); | 		con.setRequestProperty("Accept", "application/json"); | ||||||
| 		final String ssoToken = ConfigBaseVariable.ssoToken(); | 		final String ssoToken = ConfigBaseVariable.ssoToken(); | ||||||
| 		if (ssoToken != null) { | 		if (ssoToken != null) { | ||||||
| 			con.setRequestProperty("Authorization", "Zota " + ssoToken); | 			con.setRequestProperty(AuthenticationFilter.APIKEY, ssoToken); | ||||||
| 		} | 		} | ||||||
| 		final int responseCode = con.getResponseCode(); | 		final int responseCode = con.getResponseCode(); | ||||||
|  |  | ||||||
| @@ -209,8 +210,9 @@ public class JWTWrapper { | |||||||
| 		try { | 		try { | ||||||
| 			// On the consumer side, parse the JWS and verify its RSA signature | 			// On the consumer side, parse the JWS and verify its RSA signature | ||||||
| 			final SignedJWT signedJWT = SignedJWT.parse(signedToken); | 			final SignedJWT signedJWT = SignedJWT.parse(signedToken); | ||||||
|  | 			if (ConfigBaseVariable.getTestMode() && signedToken.endsWith(TestSigner.test_signature)) { | ||||||
| 			if (rsaPublicJWK == null) { | 				LOGGER.warn("Someone use a test token: {}", signedToken); | ||||||
|  | 			} else if (rsaPublicJWK == null) { | ||||||
| 				LOGGER.warn("JWT public key is not present !!!"); | 				LOGGER.warn("JWT public key is not present !!!"); | ||||||
| 				if (!ConfigBaseVariable.getTestMode()) { | 				if (!ConfigBaseVariable.getTestMode()) { | ||||||
| 					return null; | 					return null; | ||||||
| @@ -228,7 +230,7 @@ public class JWTWrapper { | |||||||
| 					return null; | 					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()); | 				LOGGER.error("JWT token is expired now = " + new Date() + " with=" + signedJWT.getJWTClaimsSet().getExpirationTime()); | ||||||
| 				return null; | 				return null; | ||||||
| 			} | 			} | ||||||
| @@ -243,24 +245,22 @@ public class JWTWrapper { | |||||||
| 			// LOGGER.debug("JWT token is verified 'alice' =?= '" + signedJWT.getJWTClaimsSet().getSubject() + "'"); | 			// LOGGER.debug("JWT token is verified 'alice' =?= '" + signedJWT.getJWTClaimsSet().getSubject() + "'"); | ||||||
| 			// LOGGER.debug("JWT token isuer 'https://c2id.com' =?= '" + signedJWT.getJWTClaimsSet().getIssuer() + "'"); | 			// LOGGER.debug("JWT token isuer 'https://c2id.com' =?= '" + signedJWT.getJWTClaimsSet().getIssuer() + "'"); | ||||||
| 			return signedJWT.getJWTClaimsSet(); | 			return signedJWT.getJWTClaimsSet(); | ||||||
| 		} catch (final JOSEException ex) { | 		} catch (final JOSEException | ParseException e) { | ||||||
| 			ex.printStackTrace(); |  | ||||||
| 		} catch (final ParseException e) { |  | ||||||
| 			e.printStackTrace(); | 			e.printStackTrace(); | ||||||
| 		} | 		} | ||||||
| 		return null; | 		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()) { | 		if (!ConfigBaseVariable.getTestMode()) { | ||||||
| 			LOGGER.error("Test mode disable !!!!!"); | 			LOGGER.error("Test mode disable !!!!!"); | ||||||
| 			return null; | 			return null; | ||||||
| 		} | 		} | ||||||
| 		try { | 		try { | ||||||
| 			final int timeOutInMunites = 3600 * 24 * 31; | 			final int timeOutInMunites = 3600; | ||||||
|  |  | ||||||
| 			final Date now = new Date(); | 			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) | 			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 | 					.expirationTime(expiration); // Do not ask why we need a "-" here ... this have no meaning | ||||||
| @@ -278,7 +278,8 @@ public class JWTWrapper { | |||||||
| 			// serialize the output... | 			// serialize the output... | ||||||
| 			return signedJWT.serialize(); | 			return signedJWT.serialize(); | ||||||
| 		} catch (final Exception ex) { | 		} catch (final Exception ex) { | ||||||
| 			LOGGER.error("Can not generate Test Token..."); | 			ex.printStackTrace(); | ||||||
|  | 			LOGGER.error("Can not generate Test Token... {}", ex.getLocalizedMessage()); | ||||||
| 		} | 		} | ||||||
| 		return null; | 		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.io.IOException; | ||||||
| import java.net.URI; | import java.net.URI; | ||||||
| import java.net.http.HttpClient; | import java.net.http.HttpClient; | ||||||
|  | import java.net.http.HttpClient.Version; | ||||||
| import java.net.http.HttpRequest; | import java.net.http.HttpRequest; | ||||||
| import java.net.http.HttpRequest.BodyPublishers; | import java.net.http.HttpRequest.BodyPublishers; | ||||||
| import java.net.http.HttpRequest.Builder; | import java.net.http.HttpRequest.Builder; | ||||||
| @@ -14,8 +15,10 @@ import org.kar.archidata.exception.RESTErrorResponseExeption; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import com.fasterxml.jackson.core.JsonParseException; | ||||||
| import com.fasterxml.jackson.core.type.TypeReference; | import com.fasterxml.jackson.core.type.TypeReference; | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper; | import com.fasterxml.jackson.databind.ObjectMapper; | ||||||
|  | import com.fasterxml.jackson.databind.exc.MismatchedInputException; | ||||||
|  |  | ||||||
| import jakarta.ws.rs.core.HttpHeaders; | import jakarta.ws.rs.core.HttpHeaders; | ||||||
|  |  | ||||||
| @@ -23,6 +26,7 @@ public class RESTApi { | |||||||
| 	final static Logger LOGGER = LoggerFactory.getLogger(RESTApi.class); | 	final static Logger LOGGER = LoggerFactory.getLogger(RESTApi.class); | ||||||
| 	final String baseUrl; | 	final String baseUrl; | ||||||
| 	private String token = null; | 	private String token = null; | ||||||
|  | 	final ObjectMapper mapper = new ObjectMapper(); | ||||||
|  |  | ||||||
| 	public RESTApi(final String baseUrl) { | 	public RESTApi(final String baseUrl) { | ||||||
| 		this.baseUrl = 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 { | 	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(); | 		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) { | 		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 HttpRequest request = requestBuilding.GET().build(); | ||||||
| 		final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); | 		final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); | ||||||
| 		if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { | 		if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { | ||||||
| 			final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); | 			try { | ||||||
| 			throw out; | 				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 this.mapper.readValue(httpResponse.body(), new TypeReference<List<T>>() {}); | ||||||
| 		return out; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public <T> T get(final Class<T> clazz, final String urlOffset) throws RESTErrorResponseExeption, IOException, InterruptedException { | 	public <T> T get(final Class<T> clazz, final String urlOffset) throws RESTErrorResponseExeption, IOException, InterruptedException { | ||||||
| 		final ObjectMapper mapper = new ObjectMapper(); | 		return modelSendJson("GET", clazz, urlOffset, null); | ||||||
| 		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; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public <T, U> T post(final Class<T> clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException { | 	public <T, U> T post(final Class<T> clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException { | ||||||
| 		final ObjectMapper mapper = new ObjectMapper(); | 		return modelSend("POST", clazz, urlOffset, data); | ||||||
| 		final HttpClient client = HttpClient.newHttpClient(); | 	} | ||||||
| 		final String body = mapper.writeValueAsString(data); |  | ||||||
| 		Builder requestBuilding = HttpRequest.newBuilder().uri(URI.create(this.baseUrl + urlOffset)); | 	public <T, U> T postJson(final Class<T> clazz, final String urlOffset, final String body) throws RESTErrorResponseExeption, IOException, InterruptedException { | ||||||
| 		if (this.token != null) { | 		return modelSendJson("POST", clazz, urlOffset, body); | ||||||
| 			requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token); |  | ||||||
| 		} |  | ||||||
| 		requestBuilding = requestBuilding.header("Content-Type", "application/json"); |  | ||||||
| 		final HttpRequest request = requestBuilding.POST(BodyPublishers.ofString(body)).build(); |  | ||||||
| 		final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); |  | ||||||
| 		if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { |  | ||||||
| 			LOGGER.error("status code: {}", httpResponse.statusCode()); |  | ||||||
| 			LOGGER.error("data: {}", httpResponse.body()); |  | ||||||
| 			final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); |  | ||||||
| 			throw out; |  | ||||||
| 		} |  | ||||||
| 		if (clazz.equals(String.class)) { |  | ||||||
| 			return (T) httpResponse.body(); |  | ||||||
| 		} |  | ||||||
| 		final T out = mapper.readValue(httpResponse.body(), clazz); |  | ||||||
| 		return out; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public <T> T postMap(final Class<T> clazz, final String urlOffset, final Map<String, Object> data) throws RESTErrorResponseExeption, IOException, InterruptedException { | 	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(); | 		return modelSendMap("POST", clazz, urlOffset, data); | ||||||
| 		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; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public <T, U> T put(final Class<T> clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException { | 	public <T, U> T put(final Class<T> clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException { | ||||||
| 		final ObjectMapper mapper = new ObjectMapper(); | 		return modelSend("PUT", clazz, urlOffset, data); | ||||||
| 		final HttpClient client = HttpClient.newHttpClient(); | 	} | ||||||
| 		final String body = mapper.writeValueAsString(data); |  | ||||||
| 		Builder requestBuilding = HttpRequest.newBuilder().uri(URI.create(this.baseUrl + urlOffset)); | 	public <T, U> T putJson(final Class<T> clazz, final String urlOffset, final String body) throws RESTErrorResponseExeption, IOException, InterruptedException { | ||||||
| 		if (this.token != null) { | 		return modelSendJson("PUT", clazz, urlOffset, body); | ||||||
| 			requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token); |  | ||||||
| 		} |  | ||||||
| 		requestBuilding = requestBuilding.header("Content-Type", "application/json"); |  | ||||||
| 		final HttpRequest request = requestBuilding.PUT(BodyPublishers.ofString(body)).build(); |  | ||||||
| 		final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); |  | ||||||
| 		if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { |  | ||||||
| 			final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); |  | ||||||
| 			throw out; |  | ||||||
| 		} |  | ||||||
| 		if (clazz.equals(String.class)) { |  | ||||||
| 			return (T) httpResponse.body(); |  | ||||||
| 		} |  | ||||||
| 		final T out = mapper.readValue(httpResponse.body(), clazz); |  | ||||||
| 		return out; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public <T> T putMap(final Class<T> clazz, final String urlOffset, final Map<String, Object> data) throws RESTErrorResponseExeption, IOException, InterruptedException { | 	public <T> T putMap(final Class<T> clazz, final String urlOffset, final Map<String, Object> data) throws RESTErrorResponseExeption, IOException, InterruptedException { | ||||||
| 		final ObjectMapper mapper = new ObjectMapper(); | 		return modelSendMap("PUT", clazz, urlOffset, data); | ||||||
| 		final HttpClient client = HttpClient.newHttpClient(); | 	} | ||||||
| 		final String body = mapper.writeValueAsString(data); |  | ||||||
| 		Builder requestBuilding = HttpRequest.newBuilder().uri(URI.create(this.baseUrl + urlOffset)); | 	public <T, U> T patch(final Class<T> clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException { | ||||||
| 		if (this.token != null) { | 		return modelSend("PATCH", clazz, urlOffset, data); | ||||||
| 			requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token); | 	} | ||||||
|  |  | ||||||
|  | 	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); | ||||||
| 		} | 		} | ||||||
| 		requestBuilding = requestBuilding.header("Content-Type", "application/json"); | 	} | ||||||
| 		final HttpRequest request = requestBuilding.PUT(BodyPublishers.ofString(body)).build(); |  | ||||||
|  | 	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.method(model, BodyPublishers.ofString(body)).build(); | ||||||
| 		final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); | 		final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); | ||||||
| 		if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { | 		if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { | ||||||
| 			final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); | 			try { | ||||||
| 			throw out; | 				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)) { | 		if (clazz.equals(String.class)) { | ||||||
| 			return (T) httpResponse.body(); | 			return (T) httpResponse.body(); | ||||||
| 		} | 		} | ||||||
| 		final T out = mapper.readValue(httpResponse.body(), clazz); | 		return this.mapper.readValue(httpResponse.body(), clazz); | ||||||
| 		return out; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	public <T, U> T delete(final Class<T> clazz, final String urlOffset) throws RESTErrorResponseExeption, IOException, InterruptedException { | 	protected <T> T modelSendMap(final String model, 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 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) { | 		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 HttpRequest request = requestBuilding.DELETE().build(); | ||||||
| 		final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); | 		final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); | ||||||
| 		if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { | 		if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { | ||||||
| 			final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); | 			try { | ||||||
| 			throw out; | 				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); | 		final List<String> sqlCommand = DataFactory.createTable(SerializeAsJson.class); | ||||||
| 		for (final String elem : sqlCommand) { | 		for (final String elem : sqlCommand) { | ||||||
| 			LOGGER.debug("request: '{}'", elem); | 			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); | 		sqlCommand.addAll(sqlCommand2); | ||||||
| 		for (final String elem : sqlCommand) { | 		for (final String elem : sqlCommand) { | ||||||
| 			LOGGER.debug("request: '{}'", elem); | 			LOGGER.debug("request: '{}'", elem); | ||||||
| 			DataAccess.executeSimpleQuerry(elem, false); | 			DataAccess.executeSimpleQuerry(elem); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -56,7 +56,7 @@ public class TestManyToOne { | |||||||
| 		sqlCommand.addAll(sqlCommand2); | 		sqlCommand.addAll(sqlCommand2); | ||||||
| 		for (final String elem : sqlCommand) { | 		for (final String elem : sqlCommand) { | ||||||
| 			LOGGER.debug("request: '{}'", elem); | 			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); | 		final List<String> sqlCommand = DataFactory.createTable(TypesTable.class); | ||||||
| 		for (final String elem : sqlCommand) { | 		for (final String elem : sqlCommand) { | ||||||
| 			LOGGER.debug("request: '{}'", elem); | 			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); | 		final List<String> sqlCommand = DataFactory.createTable(SimpleTable.class); | ||||||
| 		for (final String elem : sqlCommand) { | 		for (final String elem : sqlCommand) { | ||||||
| 			LOGGER.debug("request: '{}'", elem); | 			LOGGER.debug("request: '{}'", elem); | ||||||
| 			DataAccess.executeSimpleQuerry(elem, false); | 			DataAccess.executeSimpleQuerry(elem); | ||||||
| 		} | 		} | ||||||
| 		final SimpleTable test = new SimpleTable(); | 		final SimpleTable test = new SimpleTable(); | ||||||
| 		test.data = TestSimpleTable.DATA_INJECTED; | 		test.data = TestSimpleTable.DATA_INJECTED; | ||||||
| @@ -91,7 +91,7 @@ public class TestSimpleTable { | |||||||
| 	@Test | 	@Test | ||||||
| 	public void testReadAllValuesUnreadable() throws Exception { | 	public void testReadAllValuesUnreadable() throws Exception { | ||||||
| 		// check the full values | 		// 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); | ||||||
| 		Assertions.assertNotNull(retrieve.id); | 		Assertions.assertNotNull(retrieve.id); | ||||||
| @@ -114,7 +114,7 @@ public class TestSimpleTable { | |||||||
| 		final SimpleTable test = new SimpleTable(); | 		final SimpleTable test = new SimpleTable(); | ||||||
| 		test.data = TestSimpleTable.DATA_INJECTED_2; | 		test.data = TestSimpleTable.DATA_INJECTED_2; | ||||||
| 		DataAccess.update(test, TestSimpleTable.idOfTheObject, List.of("data")); | 		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); | ||||||
| 		Assertions.assertNotNull(retrieve.id); | 		Assertions.assertNotNull(retrieve.id); | ||||||
| 		Assertions.assertEquals(TestSimpleTable.idOfTheObject, retrieve.id); | 		Assertions.assertEquals(TestSimpleTable.idOfTheObject, retrieve.id); | ||||||
| @@ -139,7 +139,7 @@ public class TestSimpleTable { | |||||||
| 	public void testReadDeletedObject() throws Exception { | 	public void testReadDeletedObject() throws Exception { | ||||||
|  |  | ||||||
| 		// check if we set get deleted element | 		// 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); | 		Assertions.assertNull(retrieve); | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
| @@ -148,7 +148,7 @@ public class TestSimpleTable { | |||||||
| 	@Test | 	@Test | ||||||
| 	public void testReadAllValuesUnreadableOfDeletedObject() throws Exception { | 	public void testReadAllValuesUnreadableOfDeletedObject() throws Exception { | ||||||
| 		// check if we set get deleted element with all data | 		// 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); | 		Assertions.assertNull(retrieve); | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -65,7 +65,7 @@ public class TestSimpleTableSoftDelete { | |||||||
| 		final List<String> sqlCommand = DataFactory.createTable(SimpleTableSoftDelete.class); | 		final List<String> sqlCommand = DataFactory.createTable(SimpleTableSoftDelete.class); | ||||||
| 		for (final String elem : sqlCommand) { | 		for (final String elem : sqlCommand) { | ||||||
| 			LOGGER.debug("request: '{}'", elem); | 			LOGGER.debug("request: '{}'", elem); | ||||||
| 			DataAccess.executeSimpleQuerry(elem, false); | 			DataAccess.executeSimpleQuerry(elem); | ||||||
| 		} | 		} | ||||||
| 		final SimpleTableSoftDelete test = new SimpleTableSoftDelete(); | 		final SimpleTableSoftDelete test = new SimpleTableSoftDelete(); | ||||||
| 		test.data = TestSimpleTableSoftDelete.DATA_INJECTED; | 		test.data = TestSimpleTableSoftDelete.DATA_INJECTED; | ||||||
| @@ -92,7 +92,7 @@ public class TestSimpleTableSoftDelete { | |||||||
| 	@Test | 	@Test | ||||||
| 	public void testReadAllValuesUnreadable() throws Exception { | 	public void testReadAllValuesUnreadable() throws Exception { | ||||||
| 		// check the full values | 		// 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); | ||||||
| 		Assertions.assertNotNull(retrieve.id); | 		Assertions.assertNotNull(retrieve.id); | ||||||
| @@ -118,8 +118,7 @@ public class TestSimpleTableSoftDelete { | |||||||
| 		final SimpleTableSoftDelete test = new SimpleTableSoftDelete(); | 		final SimpleTableSoftDelete test = new SimpleTableSoftDelete(); | ||||||
| 		test.data = TestSimpleTableSoftDelete.DATA_INJECTED_2; | 		test.data = TestSimpleTableSoftDelete.DATA_INJECTED_2; | ||||||
| 		DataAccess.update(test, TestSimpleTableSoftDelete.idOfTheObject, List.of("data")); | 		DataAccess.update(test, TestSimpleTableSoftDelete.idOfTheObject, List.of("data")); | ||||||
| 		final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, | 		final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN); | ||||||
| 				new QueryOptions(QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN)); |  | ||||||
| 		Assertions.assertNotNull(retrieve); | 		Assertions.assertNotNull(retrieve); | ||||||
| 		Assertions.assertNotNull(retrieve.id); | 		Assertions.assertNotNull(retrieve.id); | ||||||
| 		Assertions.assertEquals(TestSimpleTableSoftDelete.idOfTheObject, retrieve.id); | 		Assertions.assertEquals(TestSimpleTableSoftDelete.idOfTheObject, retrieve.id); | ||||||
| @@ -151,7 +150,7 @@ public class TestSimpleTableSoftDelete { | |||||||
| 	public void testReadDeletedObject() throws Exception { | 	public void testReadDeletedObject() throws Exception { | ||||||
|  |  | ||||||
| 		// check if we set get deleted element | 		// 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); | ||||||
| 		Assertions.assertNotNull(retrieve.id); | 		Assertions.assertNotNull(retrieve.id); | ||||||
| 		Assertions.assertEquals(TestSimpleTableSoftDelete.idOfTheObject, retrieve.id); | 		Assertions.assertEquals(TestSimpleTableSoftDelete.idOfTheObject, retrieve.id); | ||||||
| @@ -166,8 +165,7 @@ public class TestSimpleTableSoftDelete { | |||||||
| 	@Test | 	@Test | ||||||
| 	public void testReadAllValuesUnreadableOfDeletedObject() throws Exception { | 	public void testReadAllValuesUnreadableOfDeletedObject() throws Exception { | ||||||
| 		// check if we set get deleted element with all data | 		// check if we set get deleted element with all data | ||||||
| 		final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, | 		final SimpleTableSoftDelete retrieve = DataAccess.get(SimpleTableSoftDelete.class, TestSimpleTableSoftDelete.idOfTheObject, QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN); | ||||||
| 				new QueryOptions(QueryOptions.ACCESS_DELETED_ITEMS, QueryOptions.READ_ALL_COLOMN)); |  | ||||||
| 		Assertions.assertNotNull(retrieve); | 		Assertions.assertNotNull(retrieve); | ||||||
| 		Assertions.assertNotNull(retrieve.id); | 		Assertions.assertNotNull(retrieve.id); | ||||||
| 		Assertions.assertEquals(TestSimpleTableSoftDelete.idOfTheObject, 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