Compare commits

...

903 Commits

Author SHA1 Message Date
Maverick Studer
a441909408 Merge branch 'RE-10691-fp' into 'master'
RED-10691: 500 when approving file in a dossier where dossier owner lost manager role

Closes RE-10691

See merge request redactmanager/persistence-service!930
2025-02-04 09:37:04 +01:00
maverickstuder
a7bdbb5495 RED-10691: 500 when approving file in a dossier where dossier owner lost manager role 2025-01-31 13:35:31 +01:00
Dominique Eifländer
3c0b9d36d4 Merge branch 'OPS-284' into 'master'
OPS-284: add prometheus endpoint

Closes OPS-284

See merge request redactmanager/persistence-service!928
2025-01-24 11:01:07 +01:00
Christoph Schabert
9ff3035730 OPS-284: add prometheus endpoint 2025-01-23 13:34:26 +01:00
Maverick Studer
c933793721 Merge branch 'RED-10731' into 'master'
RED-10731: Zip Bombing (~10.000 files in one zip within file limit) should be detected and aborted

Closes RED-10731

See merge request redactmanager/persistence-service!926
2025-01-21 12:21:11 +01:00
maverickstuder
4746d50627 RED-10731: Zip Bombing (~10.000 files in one zip within file limit) should be detected and aborted 2025-01-21 11:53:57 +01:00
Dominique Eifländer
fdfdba550e Merge branch 'RED-10715-masterA' into 'master'
RED-10715: Remove Saas migration fix

Closes RED-10715

See merge request redactmanager/persistence-service!925
2025-01-17 10:01:29 +01:00
Dominique Eifländer
09d53b7743 RED-10715: Remove Saas migration fix 2025-01-17 09:35:07 +01:00
Maverick Studer
d691164af6 Merge branch 'RED-10728' into 'master'
RED-10728: Endpoint to execute full OCR on specific file

Closes RED-10728

See merge request redactmanager/persistence-service!922
2025-01-16 15:54:23 +01:00
maverickstuder
43f9db59d4 RED-10728: Endpoint to execute full OCR on specific file 2025-01-16 14:38:17 +01:00
Dominique Eifländer
7669d27e7b Merge branch 'RED-10715-master' into 'master'
RED-10715: Remove Saas migration

Closes RED-10715

See merge request redactmanager/persistence-service!921
2025-01-15 13:46:38 +01:00
Dominique Eifländer
d7d46d5429 RED-10715: Remove Saas migration 2025-01-15 13:29:29 +01:00
Maverick Studer
205f9c678e Merge branch 'cleanup-truncated-indices' into 'master'
Migration fixes for 4.3 -> 4.4

See merge request redactmanager/persistence-service!918
2025-01-14 10:42:32 +01:00
Maverick Studer
cf5e235fc9 Migration fixes for 4.3 -> 4.4 2025-01-14 10:42:32 +01:00
Dominique Eifländer
25eb72ee05 Merge branch 'RED-10660' into 'master'
Migration fixes & RED-10660

Closes RED-10660

See merge request redactmanager/persistence-service!916
2025-01-13 09:07:52 +01:00
Maverick Studer
770a489f7f Migration fixes & RED-10660 2025-01-13 09:07:52 +01:00
Dominique Eifländer
18c7875844 Merge branch 'RED-10709-master' into 'master'
RED-10709: MARK_RAN for failed preCondictions in liquibase

Closes RED-10709

See merge request redactmanager/persistence-service!915
2025-01-09 14:08:05 +01:00
Dominique Eifländer
0838f30a24 RED-10709: MARK_RAN for failed preCondictions in liquibase 2025-01-09 13:53:41 +01:00
Kilian Schüttler
15e7417766 Merge branch 'hotfix-index-fp' into 'master'
hotfix: fix index preconditions

See merge request redactmanager/persistence-service!910
2025-01-09 11:31:28 +01:00
Kilian Schuettler
ab112e85c1 hotfix: fix index preconditions 2025-01-07 14:43:21 +01:00
Maverick Studer
2e3833f55d Merge branch 'RED-10669' into 'master'
RED-10669: Unassigned user can alter component values inside file viewer

Closes RED-10669

See merge request redactmanager/persistence-service!909
2025-01-07 14:23:04 +01:00
Maverick Studer
16364671d0 RED-10669: Unassigned user can alter component values inside file viewer 2025-01-07 14:23:03 +01:00
Kevin Tumma
8054806cd3 Merge branch 'hotfix-index-fp' into 'master'
hotfix: precondition check for index creation

See merge request redactmanager/persistence-service!908
2024-12-19 11:52:17 +01:00
Kilian Schuettler
d4396fe6d5 hotfix: precondition check for index creation 2024-12-19 11:31:10 +01:00
Maverick Studer
6d0354946a Merge branch 'RED-10681' into 'master'
RED-10681: Improve tracing to include metadata ids from RequestBody

Closes RED-10681

See merge request redactmanager/persistence-service!905
2024-12-13 14:50:09 +01:00
Maverick Studer
00b4ad800d RED-10681: Improve tracing to include metadata ids from RequestBody 2024-12-13 14:50:09 +01:00
Maverick Studer
34eb724170 Merge branch 'feature/RED-9998' into 'master'
RED-9998: App version history (for conditional re-analyzing the layout of a file)

Closes RED-9998

See merge request redactmanager/persistence-service!901
2024-12-12 09:58:42 +01:00
Maverick Studer
1919b1b306 RED-9998: App version history (for conditional re-analyzing the layout of a file) 2024-12-12 09:58:42 +01:00
Corina Olariu
7dae6d81c5 Merge branch 'RED-10628-fp' into 'master'
RED-10628 - Cloning dossier template after removing and editing component...

Closes RED-10628

See merge request redactmanager/persistence-service!902
2024-12-11 09:58:40 +01:00
corinaolariu
2b27e39234 RED-10628 - Cloning dossier template after removing and editing component definitions causes chain of issues
- when cloning a dossier template clone only the component definitions which are not soft deleted
- unit test added
2024-12-10 20:06:04 +02:00
Yannik Hampe
9718f8d3fd Merge branch 'feature/red-9393' into 'master'
RED-9393 user stats controller

See merge request redactmanager/persistence-service!880
2024-12-06 16:45:16 +01:00
yhampe
1a5ae41001 Merge remote-tracking branch 'origin/feature/red-9393' into feature/red-9393 2024-12-06 10:04:35 +01:00
yhampe
8ed5f3388b RED-9393 user stats controller 2024-12-06 10:04:20 +01:00
yhampe
77483b6bd0 RED-9393 user stats controller 2024-12-06 10:04:19 +01:00
yhampe
c32c2cdab0 RED-9393 user stats controller
removed filter for soft deleted files
2024-12-06 10:04:19 +01:00
yhampe
fbb8a7b519 RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-06 10:04:02 +01:00
yhampe
61e557712b RED-9393 user stats controller 2024-12-06 10:04:00 +01:00
yhampe
c28076df68 RED-9393 user stats controller 2024-12-06 10:03:59 +01:00
yhampe
032f6f87c9 RED-9393 user stats controller
removed filter for soft deleted files
2024-12-06 10:03:59 +01:00
yhampe
cfac2bcc6b RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-06 10:03:59 +01:00
yhampe
c420bda820 RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-06 10:03:59 +01:00
yhampe
9fc3aef669 RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-06 10:03:59 +01:00
yhampe
f55ebd9ecc RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-06 10:03:57 +01:00
yhampe
a3a1ee67fc RED-9393 user stats controller 2024-12-06 10:03:48 +01:00
yhampe
945e402639 RED-9393 user stats controller 2024-12-06 10:03:48 +01:00
yhampe
3624a6e49a RED-9393 user stats controller
removed filter for soft deleted files
2024-12-06 10:03:48 +01:00
yhampe
850e85ffdb RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-06 10:03:48 +01:00
yhampe
6b885f3212 RED-9393 user stats controller 2024-12-06 10:03:48 +01:00
yhampe
87ba79905c RED-9393 user stats controller 2024-12-06 10:03:48 +01:00
yhampe
ae8aecc005 RED-9393 user stats controller
removed filter for soft deleted files
2024-12-06 10:03:47 +01:00
yhampe
45c0c3d902 RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-06 10:03:47 +01:00
yhampe
3610e6c76f RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-06 10:03:47 +01:00
yhampe
7d07b1c882 RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-06 10:03:47 +01:00
yhampe
abfc9eed95 RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-06 10:03:40 +01:00
yhampe
d3e0d8f52c Merge remote-tracking branch 'origin/feature/red-9393' into feature/red-9393
# Conflicts:
#	persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/roles/ApplicationRoles.java
2024-12-06 09:34:27 +01:00
yhampe
1967468fec RED-9393 user stats controller 2024-12-06 09:33:39 +01:00
yhampe
569c24924a RED-9393 user stats controller 2024-12-06 09:33:39 +01:00
yhampe
091a648a82 RED-9393 user stats controller
removed filter for soft deleted files
2024-12-06 09:33:39 +01:00
yhampe
7a087764c1 RED-9393 user stats controller
added action roles
2024-12-06 09:33:01 +01:00
yhampe
5e60074d2f RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-06 09:32:50 +01:00
yhampe
635925b3fc RED-9393 user stats controller 2024-12-06 09:32:48 +01:00
yhampe
fad8fb3af2 RED-9393 user stats controller 2024-12-06 09:32:48 +01:00
yhampe
b3b547914b RED-9393 user stats controller
removed filter for soft deleted files
2024-12-06 09:32:48 +01:00
yhampe
556e6a4f6b RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-06 09:32:48 +01:00
yhampe
14143c9356 RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-06 09:32:48 +01:00
yhampe
34680a3972 RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-06 09:32:48 +01:00
yhampe
6068c39c33 RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-06 09:32:46 +01:00
yhampe
e0717e3466 RED-9393 user stats controller 2024-12-06 09:32:39 +01:00
yhampe
bde5c88471 RED-9393 user stats controller 2024-12-06 09:32:38 +01:00
yhampe
84c1d037c6 RED-9393 user stats controller 2024-12-06 09:32:38 +01:00
yhampe
dba47d0f0f RED-9393 user stats controller 2024-12-06 09:32:38 +01:00
yhampe
298ccff842 RED-9393 user stats controller
removed filter for soft deleted files
2024-12-06 09:32:38 +01:00
yhampe
ab7f6a1470 RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-06 09:32:38 +01:00
yhampe
bc1b6b9e6d RED-9393 user stats controller 2024-12-06 09:32:38 +01:00
yhampe
5425e06399 RED-9393 user stats controller 2024-12-06 09:32:38 +01:00
yhampe
5d0b26aca6 RED-9393 user stats controller
added filter for hard deleted files
2024-12-06 09:32:38 +01:00
yhampe
1d595eb1f0 RED-9393 user stats controller
removed filter for soft deleted files
2024-12-06 09:32:38 +01:00
yhampe
a7db55cb13 RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-06 09:32:38 +01:00
yhampe
d2fdba5658 RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-06 09:32:38 +01:00
yhampe
d1883fc5b6 RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-06 09:32:38 +01:00
yhampe
a875f94ca4 RED-9393 user stats controller
added authority check
2024-12-06 09:32:38 +01:00
yhampe
df65dac4cb RED-9393 user stats controller
added action roles
2024-12-06 09:32:38 +01:00
yhampe
9667144c9b RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-06 09:32:38 +01:00
yhampe
8548dcaf66 RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-06 09:32:38 +01:00
yhampe
52c5dbea1f RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-06 09:32:38 +01:00
Corina Olariu
65ccf32346 Merge branch 'feature/RED-10342' into 'master'
RED-10342 - File attributes in CSV export

Closes RED-10342

See merge request redactmanager/persistence-service!893
2024-12-06 09:29:56 +01:00
corinaolariu
fac80bbc5c Merge branch 'master' into feature/RED-10342
# Conflicts:
#	persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileAttributeConfigPersistenceService.java
#	persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/FileAttributeTest.java
2024-12-06 10:04:35 +02:00
Maverick Studer
ad20597434 Merge branch 'RED-5997' into 'master'
RED-5997: dossierTemplateId property null in fileAttributes- & dossierAttributes-config

Closes RED-5997

See merge request redactmanager/persistence-service!900
2024-12-06 08:10:42 +01:00
maverickstuder
66809bb136 RED-5997: dossierTemplateId property null in fileAttributes- & dossierAttributes-config 2024-12-05 15:40:50 +01:00
Maverick Studer
c7a74fed78 Merge branch 'RED-9056' into 'master'
RED-9056: Change flag name and decline requests except dossierDictionaryOnly is true

Closes RED-9056

See merge request redactmanager/persistence-service!899
2024-12-05 15:03:45 +01:00
Maverick Studer
8a5e97b9ce RED-9056: Change flag name and decline requests except dossierDictionaryOnly is true 2024-12-05 15:03:45 +01:00
Maverick Studer
08671583fe Merge branch 'RED-10615' into 'master'
RED-10615: Full Analysis loop in DM

Closes RED-10615

See merge request redactmanager/persistence-service!898
2024-12-05 11:54:56 +01:00
Corina Olariu
e7f3b47bb6 Merge branch 'feature/RED-10543' into 'master'
RED-10343 - User should be able to set own placerholder value

Closes RED-10543

See merge request redactmanager/persistence-service!897
2024-12-05 11:51:12 +01:00
maverickstuder
00b47d21dc RED-10615: Full Analysis loop in DM 2024-12-05 11:35:15 +01:00
corinaolariu
ef86d1909d RED-10342 - File attributes in CSV export
- update the dv changelog
2024-12-05 12:15:20 +02:00
corinaolariu
793444b96e RED-10342 - File attributes in CSV export
- renaumber the yaml script
2024-12-05 11:30:52 +02:00
corinaolariu
11d97683d0 RED-10342 - File attributes in CSV export
- merge master
2024-12-05 10:59:58 +02:00
corinaolariu
e3eff19de4 Merge branch 'master' into feature/RED-10342
# Conflicts:
#	persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml
2024-12-05 10:58:10 +02:00
corinaolariu
c65a62e9ac RED-10343 - User should be able to set own placerholder value
- remove old function
2024-12-05 10:31:37 +02:00
yhampe
867f0a9c02 Merge remote-tracking branch 'origin/feature/red-9393' into feature/red-9393 2024-12-05 09:17:49 +01:00
yhampe
d55a72e57a RED-9393 user stats controller 2024-12-05 09:17:39 +01:00
yhampe
f3fc2e2ce2 RED-9393 user stats controller 2024-12-05 09:17:39 +01:00
yhampe
3507130e64 RED-9393 user stats controller 2024-12-05 09:17:39 +01:00
yhampe
6a5792adf6 RED-9393 user stats controller 2024-12-05 09:17:39 +01:00
yhampe
d0c79c87cf RED-9393 user stats controller
removed filter for soft deleted files
2024-12-05 09:17:39 +01:00
yhampe
c95a5f027c RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-05 09:17:05 +01:00
yhampe
4f289c359f RED-9393 user stats controller 2024-12-05 09:16:48 +01:00
yhampe
4efc1b897a RED-9393 user stats controller 2024-12-05 09:16:48 +01:00
yhampe
0584172bbd RED-9393 user stats controller
added filter for hard deleted files
2024-12-05 09:16:48 +01:00
yhampe
295839c048 RED-9393 user stats controller
removed filter for soft deleted files
2024-12-05 09:16:48 +01:00
yhampe
5767e3465e RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-05 09:16:48 +01:00
yhampe
6f6095990f RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-05 09:16:48 +01:00
yhampe
f3032becf4 RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-05 09:16:48 +01:00
yhampe
96a8575cb6 RED-9393 user stats controller
added authority check
2024-12-05 09:16:48 +01:00
yhampe
86ff048c6b RED-9393 user stats controller
added action roles
2024-12-05 09:16:48 +01:00
yhampe
0d3e3051ab RED-9393 user stats controller
added filter for soft deleted dossiers
2024-12-05 09:16:48 +01:00
yhampe
0d7b57dd6a RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-05 09:16:48 +01:00
yhampe
15f05624ca RED-9393 user stats controller
added filter for hard deleted dossiers
2024-12-05 09:16:48 +01:00
corinaolariu
47052745c7 Merge branch 'master' into feature/RED-10543
# Conflicts:
#	persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/DossierAttributeConfigPersistenceService.java
2024-12-05 09:39:29 +02:00
corinaolariu
83668117da RED-10342 - File attributes in CSV export
- add includeToCsvExport field to FileAttributeDefinition
2024-12-05 09:27:49 +02:00
Dominique Eifländer
63c72bc613 Merge branch 'RED-9844' into 'master'
RED-9844: Added endpoint for get components with fileIds

Closes RED-9844

See merge request redactmanager/persistence-service!896
2024-12-04 12:06:12 +01:00
Dominique Eifländer
4453eab3bf RED-9844: Added endpoint for get components with fileIds 2024-12-04 11:45:20 +01:00
Maverick Studer
e700f8b785 Merge branch 'feature/RED-10135' into 'master'
RED-10135: Dossier attributes have fields displayed_in_file_list and filterable

Closes RED-10135

See merge request redactmanager/persistence-service!785
2024-12-03 09:30:25 +01:00
Maverick Studer
4adebda2ab RED-10135: Dossier attributes have fields displayed_in_file_list and filterable 2024-12-03 09:30:25 +01:00
Dominique Eifländer
bdaac65afe Merge branch 'RED-10526' into 'master'
RED-10526: Set liquibase to 4.29.2 as 4.30.0 is 3 times slower

Closes RED-10526

See merge request redactmanager/persistence-service!895
2024-12-02 10:07:34 +01:00
Dominique Eifländer
9956348a06 RED-10526: Set liquibase to 4.29.2 as 4.30.0 is 3 times slower 2024-12-02 09:48:57 +01:00
corinaolariu
0e08794271 RED-10343 - User should be able to set own placerholder value
- when placeholder is set, the placeholder is checked to be unique (conflict otherwise) and then used
- when placeholder is not set, the current behaviour is in place(with generated placeholder)
- update unit tests
2024-11-28 21:30:15 +02:00
yhampe
10f69631b0 RED-9393 user stats controller 2024-11-28 20:16:28 +01:00
Maverick Studer
d3f0d1bc87 Merge branch 'feature/RED-10347' into 'master'
RED-10347: Last download time field for approved files

Closes RED-10347

See merge request redactmanager/persistence-service!823
2024-11-28 10:02:59 +01:00
Maverick Studer
f2c41d5191 RED-10347: Last download time field for approved files 2024-11-28 10:02:58 +01:00
yhampe
28c97b446c RED-9393 user stats controller 2024-11-28 08:48:07 +01:00
corinaolariu
b461f95638 RED-10342 - File attributes in CSV export
- add includeToCsvExport field to FileAttributeConfig
- update unit test
2024-11-27 15:19:26 +02:00
yhampe
969004b542 Merge remote-tracking branch 'origin/feature/red-9393' into feature/red-9393 2024-11-27 12:12:45 +01:00
yhampe
ce27ac8d17 RED-9393 user stats controller 2024-11-27 12:12:21 +01:00
yhampe
b04bad6057 RED-9393 user stats controller 2024-11-27 12:12:21 +01:00
yhampe
5f98b16bc1 RED-9393 user stats controller
added filter for hard deleted files
2024-11-27 12:12:21 +01:00
yhampe
71f4a78a16 RED-9393 user stats controller
removed filter for soft deleted files
2024-11-27 12:12:21 +01:00
yhampe
f9a5b5aa01 RED-9393 user stats controller
added filter for soft deleted dossiers
2024-11-27 12:12:20 +01:00
yhampe
efbfd26363 RED-9393 user stats controller
added filter for hard deleted dossiers
2024-11-27 12:11:56 +01:00
yhampe
46cab2786a RED-9393 user stats controller
added filter for hard deleted dossiers
2024-11-27 12:11:51 +01:00
yhampe
17b90b1b67 RED-9393 user stats controller
added authority check
2024-11-27 12:11:16 +01:00
yhampe
59933f4a88 RED-9393 user stats controller
added action roles
2024-11-27 12:11:16 +01:00
yhampe
d36cf3c7f2 RED-9393 user stats controller
added filter for soft deleted dossiers
2024-11-27 12:11:15 +01:00
yhampe
861f1e559f RED-9393 user stats controller
added filter for hard deleted dossiers
2024-11-27 12:11:15 +01:00
yhampe
9b74db96ba RED-9393 user stats controller
added filter for hard deleted dossiers
2024-11-27 12:11:15 +01:00
yhampe
684dc3418d RED-9393 user stats controller 2024-11-27 12:10:43 +01:00
yhampe
b3bc7bb0ac RED-9393 user stats controller 2024-11-27 11:40:10 +01:00
yhampe
03d4f04b15 RED-9393 user stats controller
added filter for hard deleted files
2024-11-26 15:22:50 +01:00
Dominique Eifländer
95f1ea4a00 Merge branch 'RED-10526' into 'master'
Resolve RED-10526

Closes RED-10526

See merge request redactmanager/persistence-service!892
2024-11-26 15:21:10 +01:00
yhampe
5bbcfdffc0 RED-9393 user stats controller
removed filter for soft deleted files
2024-11-26 15:19:37 +01:00
Dominique Eifländer
5409b432d6 RED-10526: Upgrade liquibase to 4.30.0 2024-11-26 14:32:06 +01:00
Dominique Eifländer
292c92c827 RED-10526: Upgrade liquibase to 4.30.0 2024-11-26 10:51:01 +01:00
Maverick Studer
9ce067bd80 Merge branch 'RED-10529' into 'master'
RED-10529: Primary attribute removed after changing encoding type

Closes RED-10529

See merge request redactmanager/persistence-service!890
2024-11-26 09:41:43 +01:00
maverickstuder
5210b8ec40 RED-10529: Primary attribute removed after changing encoding type 2024-11-25 17:23:35 +01:00
Maverick Studer
15ca9ade53 Merge branch 'feature/RED-10514' into 'master'
RED-10514: Different issues with specific download endpoints (incl. customer api endpoint)

Closes RED-10514

See merge request redactmanager/persistence-service!889
2024-11-25 10:21:15 +01:00
Maverick Studer
8b1f63bf45 RED-10514: Different issues with specific download endpoints (incl. customer api endpoint) 2024-11-25 10:21:15 +01:00
Corina Olariu
c9b8be9405 Merge branch 'RED-10443' into 'master'
RED-10443 - 500 Error occurs when selecting ISO-8859-1 as Encoding Type for any CSV File Format

Closes RED-10443

See merge request redactmanager/persistence-service!887
2024-11-22 11:49:31 +01:00
Maverick Studer
af234311e6 Merge branch 'RED-10518' into 'master'
RED-10518: System-managed entity has not defined rank after import

Closes RED-10518

See merge request redactmanager/persistence-service!886
2024-11-22 10:52:24 +01:00
corinaolariu
e5ea667ea1 RED-10443 - 500 Error occurs when selecting ISO-8859-1 as Encoding Type for any CSV File Format
- accept only the IS-8859-1 as encoding. Meaningful message (400) is returned in case of bad encoding
- update unit tests and add unit test
2024-11-22 11:32:18 +02:00
maverickstuder
7ff7222072 RED-10518: System-managed entity has not defined rank after import 2024-11-22 10:22:30 +01:00
yhampe
d8b1a32783 Merge remote-tracking branch 'origin/feature/red-9393' into feature/red-9393 2024-11-22 08:44:38 +01:00
yhampe
3315a679a8 RED-9393 user stats controller
added authority check
2024-11-22 08:44:24 +01:00
yhampe
013d61b0d0 RED-9393 user stats controller
added action roles
2024-11-22 08:44:24 +01:00
yhampe
afe793a523 RED-9393 user stats controller
added filter for soft deleted dossiers
2024-11-22 08:44:24 +01:00
yhampe
23078c0b66 RED-9393 user stats controller
added filter for hard deleted dossiers
2024-11-22 08:44:00 +01:00
yhampe
c1fafaee6e RED-9393 user stats controller
added filter for hard deleted dossiers
2024-11-22 08:44:00 +01:00
yhampe
fa0e29095f RED-9393 user stats controller
added authority check
2024-11-22 08:43:29 +01:00
yhampe
c7a9c2ff11 RED-9393 user stats controller
added action roles
2024-11-22 08:38:51 +01:00
Maverick Studer
3be9566a2e Merge branch 'RED-8811' into 'master'
RED-8811: Merge in recreated type does not work properly

Closes RED-8811

See merge request redactmanager/persistence-service!884
2024-11-21 16:06:38 +01:00
Maverick Studer
b62a9b9ae6 RED-8811: Merge in recreated type does not work properly 2024-11-21 16:06:38 +01:00
Maverick Studer
f08a2d512a Merge branch 'RED-10511' into 'master'
RED-10511: False warnings when approving file appears after justification refactoring

Closes RED-10511

See merge request redactmanager/persistence-service!885
2024-11-21 14:54:01 +01:00
Maverick Studer
95cd4edebb Merge branch 'RED-9059-fp' into 'master'
RED-9059: Change dictionary diff status code for successful request

Closes RED-9059

See merge request redactmanager/persistence-service!882
2024-11-21 14:53:55 +01:00
maverickstuder
21b184d97a RED-10511: False warnings when approving file appears after justification refactoring 2024-11-21 14:30:08 +01:00
maverickstuder
e393d70186 RED-10511: False warnings when approving file appears after justification refactoring 2024-11-21 13:51:21 +01:00
maverickstuder
511f392203 RED-9059: Change dictionary diff status code for successful request 2024-11-21 09:34:54 +01:00
yhampe
072c965593 RED-9393 user stats controller
added filter for soft deleted dossiers
2024-11-21 09:00:30 +01:00
Corina Olariu
65041c2b7a Merge branch 'RED-10202' into 'master'
RED-10202 - Fix ComponentOverrideTest for persistence-service pipeline

Closes RED-10202

See merge request redactmanager/persistence-service!881
2024-11-21 07:48:12 +01:00
corinaolariu
a5004c6d26 RED-10202 - Fix ComponentOverrideTest for persistence-service pipeline
- revert the file name
2024-11-20 16:21:17 +02:00
corinaolariu
58d11fc7b9 RED-10202 - Fix ComponentOverrideTest for persistence-service pipeline
- include ruleFileType.Component when the application type is documine
2024-11-20 15:33:09 +02:00
yhampe
8e40b77c70 Merge remote-tracking branch 'origin/feature/red-9393' into feature/red-9393 2024-11-20 11:31:06 +01:00
yhampe
4a32f55b61 RED-9393 user stats controller
added filter for hard deleted dossiers
2024-11-20 11:31:00 +01:00
yhampe
12136e0fdc RED-9393 user stats controller
added filter for hard deleted dossiers
2024-11-20 11:31:00 +01:00
Maverick Studer
2bb20ef7fa Merge branch 'feature/RED-10115' into 'master'
RED-10115: Refactoring of justifications

Closes RED-10115

See merge request redactmanager/persistence-service!841
2024-11-20 10:59:39 +01:00
Maverick Studer
2e83a72f29 RED-10115: Refactoring of justifications 2024-11-20 10:59:39 +01:00
corinaolariu
19d7670ada RED-10202 - Fix ComponentOverrideTest for persistence-service pipeline
- enable tests from component overrides
2024-11-20 11:30:42 +02:00
yhampe
8c7e64ffad RED-9393 user stats controller
added filter for hard deleted dossiers
2024-11-20 08:57:49 +01:00
yhampe
edd6b87566 RED-9393 user stats controller
added filter for hard deleted dossiers
2024-11-20 08:57:30 +01:00
Maverick Studer
b21e34ec84 Merge branch 'RED-10415-fp' into 'master'
RED-10415: Error when deleting entities from Templates without dossiers

Closes RED-10415

See merge request redactmanager/persistence-service!879
2024-11-20 01:08:47 +01:00
maverickstuder
215afe0834 RED-10415: Error when deleting entities from Templates without dossiers 2024-11-19 15:37:47 +01:00
Yannik Hampe
70d55ac6c3 Merge branch 'feature/RED-10268' into 'master'
RED-10268:

Closes RED-10268

See merge request redactmanager/persistence-service!875
2024-11-19 10:11:51 +01:00
yhampe
39e78cb033 RED-10268:
added new filter that also removes soft and hard deleted files on download preperations
2024-11-19 09:11:57 +01:00
yhampe
d4da6befb4 RED-10268:
added new filter that also removes soft and hard deleted files on download preperations
2024-11-19 08:14:50 +01:00
Kilian Schüttler
d4448fa798 Merge branch 'RED-10463' into 'master'
RED-10463: update path slightly

Closes RED-10463

See merge request redactmanager/persistence-service!872
2024-11-18 12:10:00 +01:00
Kilian Schuettler
0f72b876d0 RED-10463: update path slightly 2024-11-18 11:41:26 +01:00
Corina Olariu
ff603599b9 Merge branch 'RED-10381' into 'master'
RED-10381 - Include system-managed entities in dossier template export

Closes RED-10381

See merge request redactmanager/persistence-service!871
2024-11-15 10:30:48 +01:00
corinaolariu
2aa354dd3a RED-10381 - Include system-managed entities in dossier template export
- The system manager entities will be exported.
- At import dossier template if system managed are present in the import then they should be used and not ignored
- update unit tests
2024-11-14 23:39:14 +02:00
Corina Olariu
a1a7f4f568 Merge branch 'RED-10425' into 'master'
RED-10425 - Annotation added twice when bulk-force while auto-analysis is disabled

Closes RED-10425

See merge request redactmanager/persistence-service!857
2024-11-14 21:46:03 +01:00
corinaolariu
bf69b806de RED-10425 - Annotation added twice when bulk-force while auto-analysis is disabled
- delete unnecessary mongo xml file
2024-11-14 19:34:49 +02:00
corinaolariu
9cabeef5d5 Merge branch 'master' into RED-10425
# Conflicts:
#	persistence-service-v1/persistence-service-processor-v1/src/main/resources/mongo/changelog/mongo.changelog-tenant.xml
2024-11-14 19:32:41 +02:00
Maverick Studer
4eff4803c6 Merge branch 'RED-10482' into 'master'
RED-10482: Indices not created correctly via liquibase

Closes RED-10482

See merge request redactmanager/persistence-service!867
2024-11-14 18:25:48 +01:00
Kilian Schüttler
81cee51661 Merge branch 'feature/RED-9139' into 'master'
RED-9139: move document to its own module, rename import migration

Closes RED-9139

See merge request redactmanager/persistence-service!858
2024-11-14 16:40:53 +01:00
Kilian Schüttler
357743e1d6 RED-9139: move document to its own module, rename import migration 2024-11-14 16:40:52 +01:00
maverickstuder
0afb6ca4f2 RED-10482: Indices not created correctly via liquibase 2024-11-14 16:07:02 +01:00
Kilian Schüttler
0fc9d40155 Merge branch 'RED-10463' into 'master'
RED-10463: added permission

Closes RED-10463

See merge request redactmanager/persistence-service!866
2024-11-14 13:51:16 +01:00
Kilian Schuettler
d61eee0a93 RED-10463: added permission 2024-11-14 12:23:19 +01:00
Kilian Schüttler
5563c19dca Merge branch 'RED-10463' into 'master'
RED-10463: unlock rule file endpoint

Closes RED-10463

See merge request redactmanager/persistence-service!860
2024-11-14 11:51:23 +01:00
Kilian Schuettler
491c57667b RED-10463: unlock rule file endpoint 2024-11-14 11:25:16 +01:00
corinaolariu
a1de121109 RED-10425 - Annotation added twice when bulk-force while auto-analysis is disabled
- use a custom query to get the dictionary entity log entries with the found value and positions list needed instead of the entire entity log
- add index for entityLogId and value
2024-11-13 16:20:01 +02:00
Maverick Studer
894be159f5 Merge branch 'hotfix-missing-dm-migration-fp' into 'master'
migration hotfixes

See merge request redactmanager/persistence-service!855
2024-11-13 11:20:47 +01:00
Maverick Studer
7e78063a0b migration hotfixes 2024-11-13 11:20:44 +01:00
corinaolariu
6ea68ccef4 RED-10425 - Annotation added twice when bulk-force while auto-analysis is disabled
- check for link with dictionary entry in the entity log after the found terms are received for an addbulklocal
- unit test added
2024-11-13 11:25:35 +02:00
Maverick Studer
632cfdb5f6 Merge branch 'RED-10442-fp' into 'master'
RED-10442: Migration Issue: False Warnings appears when re-approve migrated...

Closes RED-10442

See merge request redactmanager/persistence-service!853
2024-11-12 12:49:25 +01:00
maverickstuder
e37bdaabdb RED-10442: Migration Issue: False Warnings appears when re-approve migrated files with UNMAPPED_JUSTIFICATIONS 2024-11-12 12:31:00 +01:00
Maverick Studer
aa5ff0daf1 Merge branch 'RED-10444' into 'master'
RED-10444: Update Value/Classification for bulk-local rectangle redactions

Closes RED-10444

See merge request redactmanager/persistence-service!850
2024-11-12 11:02:06 +01:00
Maverick Studer
ee687e42b5 RED-10444: Update Value/Classification for bulk-local rectangle redactions 2024-11-12 11:02:06 +01:00
Maverick Studer
a599021b31 Merge branch 'feature/RED-10196' into 'master'
RED-10196: Backend adaptions for RM/DM unification

Closes RED-10196

See merge request redactmanager/persistence-service!788
2024-11-11 12:54:31 +01:00
Maverick Studer
4d36a3d813 RED-10196: Backend adaptions for RM/DM unification 2024-11-11 12:54:31 +01:00
Corina Olariu
b8710d57b3 Merge branch 'RED-9588' into 'master'
RED-9588 - No "You have been unassigned..." Notification after setting file status to back to new

Closes RED-9588

See merge request redactmanager/persistence-service!577
2024-11-08 19:52:18 +01:00
corinaolariu
bb5e7f5be7 RED-9588 - No "You have been unassigned..." Notification after setting file status to back to new
- update the condition for sending unassigned notification
- unit test added
2024-11-08 15:03:37 +02:00
Dominique Eifländer
01ab05b52e Merge branch 'RED-10353-master' into 'master'
RED-10353: Renealyse files in error state after rule change is no rules...

Closes RED-10353

See merge request redactmanager/persistence-service!575
2024-11-08 13:19:55 +01:00
Dominique Eifländer
0761ef6676 RED-10353: Renealyse files in error state after rule change is no rules exection timeout, fixed migration 2024-11-08 12:56:36 +01:00
Maverick Studer
f9183054f3 Merge branch 'feature/RED-10072' into 'master'
RED-10072: AI description field and toggle for entities

Closes RED-10072

See merge request redactmanager/persistence-service!784
2024-11-07 14:43:54 +01:00
Maverick Studer
31c3ce45f0 RED-10072: AI description field and toggle for entities 2024-11-07 14:43:54 +01:00
Timo Bejan
939ebe426b Merge branch 'feature/RED-10422' into 'master'
RED-10422 - New changes and get by ids endpoints

Closes RED-10422

See merge request redactmanager/persistence-service!846
2024-11-07 13:20:31 +01:00
Timo Bejan
f01ea9f6da RED-10422 - New changes and get by ids endpoints 2024-11-07 13:38:39 +02:00
Dominique Eifländer
25cc17bf09 Merge branch 'RED-4732-master' into 'master'
RED-4732: Fixed some auditlog messages

Closes RED-4732

See merge request redactmanager/persistence-service!845
2024-11-07 11:58:07 +01:00
Dominique Eifländer
1f60256117 RED-4732: Fixed some auditlog messages 2024-11-07 11:33:13 +01:00
Kilian Schüttler
584473565f Merge branch 'RED-10418' into 'master'
RED-10418: improve unprocessed merge performance

Closes RED-10418

See merge request redactmanager/persistence-service!842
2024-11-07 07:18:13 +01:00
Kilian Schuettler
508ac2d677 RED-10418: improve unprocessed merge performance 2024-11-06 15:51:11 +01:00
Kilian Schüttler
bb9bdabbae Merge branch 'RED-10264' into 'master'
RED-10264: reorder migrations for backport

Closes RED-10264

See merge request redactmanager/persistence-service!838
2024-11-06 10:32:13 +01:00
Yannik Hampe
f6caa94136 Merge branch 'feature/red-9393' into 'master'
RED-9393: user stats endpoint

See merge request redactmanager/persistence-service!789
2024-11-06 10:29:03 +01:00
yhampe
43d37b67b3 RED-9393: user stats endpoint
checkstyle
2024-11-06 09:48:50 +01:00
Kilian Schuettler
92aa781a8f RED-10264: reorder migrations for backport 2024-11-06 09:44:42 +01:00
yhampe
f23548adcf Merge remote-tracking branch 'origin/feature/red-9393' into feature/red-9393
# Conflicts:
#	persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/persistence/FileStatusPersistenceService.java
2024-11-06 09:02:03 +01:00
yhampe
c21a97420d RED-9393: user stats endpoint
added endpoint with discussed path
2024-11-06 09:00:58 +01:00
Dominique Eifländer
48a7d05ba1 Merge branch 'RED-10353-master' into 'master'
RED-10353: Added error code to file

Closes RED-10353

See merge request redactmanager/persistence-service!832
2024-11-05 16:33:22 +01:00
Dominique Eifländer
1b8b828f3d RED-10353: Added error code to file 2024-11-05 15:19:43 +01:00
Timo Bejan
1e552d22c0 Merge branch 'feature/RED-10392' into 'master'
Indexes for queries that happen often

Closes RED-10392

See merge request redactmanager/persistence-service!829
2024-11-05 14:40:08 +01:00
Timo Bejan
227dd092b4 Merge branch 'master' into 'feature/RED-10392'
# Conflicts:
#   persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml
2024-11-05 14:23:07 +01:00
Timo Bejan
a36fcff7b6 Merge branch 'feature/RED-10395' into 'master'
RED-10395 Improved component mapping query speed and performance for analysisRequired on FileStatus

Closes RED-10395

See merge request redactmanager/persistence-service!828
2024-11-05 14:22:08 +01:00
Timo Bejan
b30531f552 RED-10395 Improved component mapping query speed and performance for analysisRequired on FileStatus 2024-11-05 14:38:15 +02:00
Timo Bejan
c99f90546c Indexes for queries that happen often 2024-11-05 14:27:19 +02:00
Maverick Studer
0a8ed5d2c8 Merge branch 'RED-10354' into 'master'
RED-10354: File viewer inconsistent after selecting "Overwrite and keep manual...

Closes RED-10354

See merge request redactmanager/persistence-service!824
2024-11-05 11:27:19 +01:00
Maverick Studer
2d68469d8d RED-10354: File viewer inconsistent after selecting "Overwrite and keep manual... 2024-11-05 11:27:18 +01:00
Corina Olariu
ade8e7ed9d Merge branch 'RED-10186-fb' into 'master'
RED-10186 - Unlinked annotation with manual changes still linked and removed...

Closes RED-10186

See merge request redactmanager/persistence-service!822
2024-11-04 20:21:44 +01:00
corinaolariu
51b5201677 RED-10186 - Unlinked annotation with manual changes still linked and removed in specific corner case
- remove commented code
2024-11-04 11:23:26 +02:00
corinaolariu
1664820a82 RED-10186 - Unlinked annotation with manual changes still linked and removed in specific corner case
- update unit test
2024-11-01 15:34:00 +02:00
corinaolariu
f2df7fe783 RED-10186 - Unlinked annotation with manual changes still linked and removed in specific corner case
- when a local removal is done the MANUAL engine is not added anymore.
- removed basedOnDictAnnotationId from manual changes. The add which is always created at a local change with unlink with save the dictionary annotation id in the source id
- unit tests updated
2024-11-01 14:59:02 +02:00
Maverick Studer
48a0637fba Merge branch 'RED-10334' into 'master'
RED-10334: Wrong engine for add-to-dict dossier-level while auto-analysis is disabled

Closes RED-10334

See merge request redactmanager/persistence-service!818
2024-10-31 14:40:41 +01:00
maverickstuder
fa83069ff0 RED-10334: Wrong engine for add-to-dict dossier-level while auto-analysis is disabled 2024-10-31 12:47:27 +01:00
Dominique Eifländer
22a7f2c3af Merge branch 'RED-10353-master' into 'master'
RED-10353: Reset processingErrorCounter when rules are updated

Closes RED-10353

See merge request redactmanager/persistence-service!816
2024-10-31 11:28:59 +01:00
Dominique Eifländer
3df86ade4a RED-10353: Reset processingErrorCounter when rules are updated 2024-10-31 11:03:57 +01:00
Kilian Schüttler
a23aa12527 Merge branch 'RED-10264' into 'master'
RED-10264: add migration fixing missing legalbasis

Closes RED-10264

See merge request redactmanager/persistence-service!812
2024-10-30 14:20:21 +01:00
Kilian Schüttler
dddc608c34 RED-10264: add migration fixing missing legalbasis 2024-10-30 14:20:20 +01:00
Dominique Eifländer
b174ca57b1 Merge branch 'RED-4732-master' into 'master'
RED-4732: Added missing audit logs

Closes RED-4732

See merge request redactmanager/persistence-service!813
2024-10-30 13:21:04 +01:00
Dominique Eifländer
1fe8170ec0 RED-4732: Added missing audit logs 2024-10-30 13:00:31 +01:00
Maverick Studer
bbd8d5be9b Merge branch 'feature/RED-10297' into 'master'
RED-10297: Provide default date format file when missing on dossier template import

Closes RED-10297

See merge request redactmanager/persistence-service!808
2024-10-30 12:36:15 +01:00
Maverick Studer
258d26ede0 RED-10297: Provide default date format file when missing on dossier template import 2024-10-30 12:36:14 +01:00
Dominique Eifländer
437941fc6c Merge branch 'RED-10315-master' into 'master'
RED-10315: hardDeleteCleanupRetryTime in app config endpoint optional

Closes RED-10315

See merge request redactmanager/persistence-service!806
2024-10-30 09:56:44 +01:00
Dominique Eifländer
bb40b3fa20 RED-10315: Fixed unittest 2024-10-30 09:27:59 +01:00
Dominique Eifländer
34346b149b RED-10315: hardDeleteCleanupRetryTime in app config endpoint optional 2024-10-30 09:27:59 +01:00
Corina Olariu
0f8e78a1aa Merge branch 'RED-10071_fix' into 'master'
RED-10071 - Create system-managed entities automatically - fix component definitions

Closes RED-10071

See merge request redactmanager/persistence-service!810
2024-10-30 09:25:38 +01:00
corinaolariu
4c5f164f2e RED-10071 - Create system-managed entities automatically - fix component definitions
- when import dossier template with update before inserting component definitions, remove the existing ones
- unit test added
2024-10-29 18:51:01 +02:00
Kilian Schüttler
a2e1b4367a Merge branch 'RED-10286' into 'master'
RED-10286: handle manual changes == null

Closes RED-10286

See merge request redactmanager/persistence-service!804
2024-10-28 12:41:50 +01:00
Kilian Schuettler
b574df51bf RED-10286: handle manual changes == null 2024-10-28 12:21:38 +01:00
Kilian Schüttler
bc057e73de Merge branch 'RED-10286' into 'master'
RED-10286: Don't request all dossiers/files during file exchange

Closes RED-10286

See merge request redactmanager/persistence-service!802
2024-10-25 14:18:21 +02:00
Kilian Schüttler
13fd797148 RED-10286: Don't request all dossiers/files during file exchange 2024-10-25 14:18:21 +02:00
Kilian Schüttler
ca75ee9607 Merge branch 'mergeErrorHotfix' into 'master'
hotfix: fix merge errors

See merge request redactmanager/persistence-service!800
2024-10-25 12:52:46 +02:00
Kilian Schuettler
0f651d6a67 hotfix: fix merge errors 2024-10-25 12:28:18 +02:00
Kilian Schüttler
f69921fe59 Merge branch 'RED-10264' into 'master'
RED-10264: always include unprocessed during manual change insertion to avoid...

Closes RED-10264

See merge request redactmanager/persistence-service!796
2024-10-25 11:14:16 +02:00
Kilian Schüttler
88c5e43e51 RED-10264: always include unprocessed during manual change insertion to avoid... 2024-10-25 11:14:15 +02:00
Maverick Studer
34c1c4f51c Merge branch 'RED-10259' into 'master'
RED-10259: Failed recategorize on annotation after re-upload file without keeping changes

Closes RED-10259

See merge request redactmanager/persistence-service!798
2024-10-24 14:21:02 +02:00
maverickstuder
de0d4294ea RED-10259: Failed recategorize on annotation after re-upload file without keeping changes 2024-10-24 13:52:54 +02:00
Kilian Schüttler
3ccbac4b0e Merge branch 'feature/RED-10260-bp' into 'master'
Resolve RED-10260-bp

Closes RED-10260

See merge request redactmanager/persistence-service!794
2024-10-24 11:02:43 +02:00
Kilian Schüttler
cdde8a7da6 Resolve RED-10260-bp 2024-10-24 11:02:42 +02:00
Kilian Schüttler
ccbf977e01 Merge branch 'feature/RED-10260' into 'master'
RED-10260: add quote char to component mapping

Closes RED-10260

See merge request redactmanager/persistence-service!793
2024-10-24 11:00:59 +02:00
Kilian Schuettler
9a99398961 RED-10260: add quote char to component mapping 2024-10-23 13:56:26 +02:00
Kilian Schuettler
f86c925835 RED-10260: add quote char to component mapping 2024-10-23 13:38:53 +02:00
Kilian Schuettler
d26d24271a RED-10260: add quote char to component mapping 2024-10-23 12:11:05 +02:00
Kilian Schuettler
0ad3973d23 RED-10260: add quote char to component mapping 2024-10-23 12:09:25 +02:00
Kilian Schuettler
3fe009512f RED-10260: add quote char to component mapping 2024-10-23 12:05:22 +02:00
Kilian Schüttler
f3823a9acd Merge branch 'RED-10127' into 'master'
RED-10127: enable ocr rerun via support controller

Closes RED-10127

See merge request redactmanager/persistence-service!787
2024-10-23 10:35:12 +02:00
Kilian Schüttler
e3e3d5bc58 RED-10127: enable ocr rerun via support controller 2024-10-23 10:35:11 +02:00
Maverick Studer
60e042587e Merge branch 'RED-10224-fp2' into 'master'
RED-10224: Editing entities - Access is denied but updates value anyway

Closes RED-10224

See merge request redactmanager/persistence-service!792
2024-10-23 09:57:00 +02:00
maverickstuder
9c4804d239 RED-10224: Editing entities - Access is denied but updates value anyway 2024-10-23 09:30:36 +02:00
yhampe
f5f9247834 RED-9393: user stats endpoint
added endpoint with discussed path
2024-10-23 09:10:11 +02:00
Maverick Studer
9203993d6e Merge branch 'feature/RED-10106' into 'master'
Resolve RED-10106 "Feature/"

Closes RED-10106

See merge request redactmanager/persistence-service!765
2024-10-18 15:12:58 +02:00
maverickstuder
260851661b Merge branch 'master' into feature/RED-10106
# Conflicts:
#	persistence-service-v1/persistence-service-processor-v1/build.gradle.kts
#	persistence-service-v1/persistence-service-shared-mongo-v1/build.gradle.kts
2024-10-18 14:59:35 +02:00
Maverick Studer
0ce2b1644c Merge branch 'RED-10197' into 'master'
RED-10197: Migration preparations for RM/DM unification

Closes RED-10197

See merge request redactmanager/persistence-service!786
2024-10-17 16:32:52 +02:00
Maverick Studer
5e215c2649 RED-10197: Migration preparations for RM/DM unification 2024-10-17 16:32:52 +02:00
Corina Olariu
53bb752002 Merge branch 'feature/RED-10071' into 'master'
RED-10071 - Create system-managed entities automatically

Closes RED-10071

See merge request redactmanager/persistence-service!779
2024-10-15 10:48:05 +02:00
corinaolariu
ffca299cb4 RED-10071 - Create system-managed entities automatically
- remove commented code
2024-10-15 10:56:53 +03:00
corinaolariu
a65e9e1086 Merge branch 'master' into feature/RED-10071
# Conflicts:
#	persistence-service-v1/persistence-service-server-v1/src/test/java/com/iqser/red/service/peristence/v1/server/integration/tests/ManualRedactionTest.java
2024-10-15 10:34:12 +03:00
Kilian Schüttler
abda71fca3 Merge branch 'RED-10127' into 'master'
RED-10127: Import/Export of dossiertemplate with preview files

Closes RED-10127

See merge request redactmanager/persistence-service!782
2024-10-14 15:30:18 +02:00
Kilian Schüttler
c52dfbdf5a RED-10127: Import/Export of dossiertemplate with preview files 2024-10-14 15:30:18 +02:00
Maverick Studer
fef66f92b2 Merge branch 'RED-10104-10178' into 'master'
RED-10104 && RED-10178

Closes RED-10104

See merge request redactmanager/persistence-service!783
2024-10-14 14:30:46 +02:00
Maverick Studer
cdbf69abbf RED-10104 && RED-10178 2024-10-14 14:30:46 +02:00
Dominique Eifländer
e5bac8f964 RED-10071: Fixed archive 2024-10-10 15:16:01 +02:00
Dominique Eifländer
06f5f4f241 Merge branch 'RED-10149-master' into 'master'
RED-10149: Do not re- ocr file after pages rotations; RED-10083: Minor fix in openapi spec

Closes RED-10149

See merge request redactmanager/persistence-service!778
2024-10-10 12:18:18 +02:00
Dominique Eifländer
82661dcaf2 RED-10149: Do not re- ocr file after pages rotations; RED-10083: Minor fix in openapi spec 2024-10-10 11:23:09 +02:00
corinaolariu
0ed4759266 RED-10071 - Create system-managed entities automatically
- when exporting the types, do not export system managed types
- when importing a dossier template, ignore the system managed types from the archive
- when importing a dossier template as new or a new dossier template is created, also create the system managed types.
- unit tests updated
2024-10-10 11:16:33 +03:00
Maverick Studer
15be976352 Merge branch 'RED-10112' into 'master'
RED-10112: Wrong role permissions in API endpoints for GUEST and RED_USER_ADMIN role

Closes RED-10112

See merge request redactmanager/persistence-service!775
2024-10-09 14:40:20 +02:00
Maverick Studer
9b07def91d RED-10112: Wrong role permissions in API endpoints for GUEST and RED_USER_ADMIN role 2024-10-09 14:40:20 +02:00
Maverick Studer
ec10bbdf8b Merge branch 'hotfix-rm-migration' into 'master'
RM migration creating unnecessary date formats table entries

See merge request redactmanager/persistence-service!772
2024-10-09 10:34:39 +02:00
Maverick Studer
b184217099 RM migration creating unnecessary date formats table entries 2024-10-09 10:34:39 +02:00
Dominique Eifländer
71a895f3a2 Merge branch 'RED-9823-master' into 'master'
RED-9823: Changed audit message for force annotation

Closes RED-9823

See merge request redactmanager/persistence-service!774
2024-10-09 09:14:34 +02:00
Dominique Eifländer
ac41a902a1 RED-9823: Changed audit message for force annotation 2024-10-09 08:56:03 +02:00
Timo Bejan
4397cddcc1 Merge branch 'feature/RED-10034' into 'master'
RED-10034 Improved dossier stats performance

Closes RED-10034

See merge request redactmanager/persistence-service!764
2024-10-07 15:10:23 +02:00
Timo Bejan
a557f0ab25 RED-10034 Improved dossier stats performance 2024-10-07 15:10:22 +02:00
Maverick Studer
7d52b4b0a0 Merge branch 'RED-9123' into 'master'
RED-9123: Improve performance of re-analysis (Spike)

Closes RED-9123

See merge request redactmanager/persistence-service!771
2024-10-07 13:31:16 +02:00
Maverick Studer
d794245409 RED-9123: Improve performance of re-analysis (Spike) 2024-10-07 13:31:15 +02:00
Maverick Studer
dbe7949a1c Merge branch 'RED-10146' into 'master'
RED-10146: Include defined components in component log

Closes RED-10146

See merge request redactmanager/persistence-service!769
2024-10-07 11:08:24 +02:00
Maverick Studer
4c79b8c2f4 RED-10146: Include defined components in component log 2024-10-07 11:08:24 +02:00
Maverick Studer
2ac82ab8e7 Merge branch 'RED-10104' into 'master'
RED-10104: add comments when automatic analysis enabled

Closes RED-10104

See merge request redactmanager/persistence-service!768
2024-10-07 09:22:30 +02:00
maverickstuder
44419f2c6c RED-10104: add comments when automatic analysis enabled 2024-10-02 14:28:04 +02:00
Maverick Studer
7a13b6adc2 Merge branch 'fix-migration-version-duplicate' into 'master'
fix duplicated migration version

See merge request redactmanager/persistence-service!767
2024-09-30 17:00:49 +02:00
Maverick Studer
45e67b8796 fix duplicated migration version 2024-09-30 17:00:49 +02:00
Maverick Studer
87eeddded3 Merge branch 'RED-9933' into 'master'
RED-9933: DocuMine DateFormat config in dossier templates

Closes RED-9933

See merge request redactmanager/persistence-service!766
2024-09-30 11:33:05 +02:00
Maverick Studer
e213127b25 RED-9933: DocuMine DateFormat config in dossier templates 2024-09-30 11:33:05 +02:00
Timo Bejan
adab452380 Removed allow bean override with latest deps this now works 2024-09-28 16:08:51 +03:00
Timo Bejan
f7612ac89f Renabled missing changelog 2024-09-28 16:08:51 +03:00
Timo Bejan
8d33f19453 RED-10106 - adjusted code to work with latest common deps 2024-09-28 16:08:51 +03:00
Maverick Studer
5d704e08f9 Merge branch 'RED-10117' into 'master'
RED-10117: 500 Error in Dossier Template Admin Page

Closes RED-10117

See merge request redactmanager/persistence-service!762
2024-09-27 13:50:48 +02:00
Maverick Studer
7ea1a87567 Merge branch 'RED-10114' into 'master'
RED-10114: Download fails when multiple document versions are selected...

Closes RED-10114

See merge request redactmanager/persistence-service!760
2024-09-27 13:39:18 +02:00
Maverick Studer
1c56c4665e RED-10114: Download fails when multiple document versions are selected... 2024-09-27 13:39:17 +02:00
maverickstuder
50831dabfa RED-10117: 500 Error in Dossier Template Admin Page
* remove transactional from getAllRelevantStatusesForReanalysisScheduler and addToAnalysisQueue
2024-09-27 13:26:51 +02:00
Maverick Studer
dc42fcad40 Merge branch 'RED-9947-fix2' into 'master'
RED-9947 && RED-10104

See merge request redactmanager/persistence-service!759
2024-09-27 09:25:00 +02:00
Maverick Studer
0bc8bac5d3 RED-9947 && RED-10104 2024-09-27 09:25:00 +02:00
Dominique Eifländer
0a09725ebe Merge branch 'RED-10094' into 'master'
RED-10094: Revert back migration for technical name of justifiactions

Closes RED-10094

See merge request redactmanager/persistence-service!756
2024-09-24 16:27:55 +02:00
Dominique Eifländer
bea853cbf2 RED-10094: Revert back migration for technical name of justifiactions 2024-09-24 15:53:11 +02:00
Maverick Studer
88ae52dd52 Merge branch 'RED-9933' into 'master'
RED-9933: DocuMine DateFormat config in dossier templates

Closes RED-9933

See merge request redactmanager/persistence-service!755
2024-09-24 09:28:36 +02:00
Maverick Studer
77c2f9b9a7 RED-9933: DocuMine DateFormat config in dossier templates 2024-09-24 09:28:35 +02:00
Maverick Studer
876ee17469 Merge branch 'feature/RED-9348' into 'master'
RED-9348: move component log to mongodb

Closes RED-9348

See merge request redactmanager/persistence-service!742
2024-09-23 16:05:18 +02:00
Maverick Studer
36eed8d0a1 RED-9348: move component log to mongodb 2024-09-23 16:05:17 +02:00
Maverick Studer
0f935fcba3 Merge branch 'RED-10088' into 'master'
RED-10088: Reanalyze all files in ERROR should ignore files from deleted dossiers

Closes RED-10088

See merge request redactmanager/persistence-service!754
2024-09-20 13:42:18 +02:00
Maverick Studer
a57aedf52c RED-10088: Reanalyze all files in ERROR should ignore files from deleted dossiers 2024-09-20 13:42:17 +02:00
Dominique Eifländer
63129d7727 Merge branch 'RED-10077-master' into 'master'
RED-10077: Switch cv analysis request type to table_cells to be compatible...

Closes RED-10077

See merge request redactmanager/persistence-service!752
2024-09-20 10:12:55 +02:00
Maverick Studer
82da9860ce Merge branch 'RED-10081' into 'master'
RED-10081: Update RM API specs after adding new download file type

Closes RED-10081

See merge request redactmanager/persistence-service!750
2024-09-20 10:00:38 +02:00
Dominique Eifländer
25a8bb0610 RED-10077: Switch cv analysis request type to table_cells to be compatible with new cv analysis service version 2024-09-20 09:43:07 +02:00
Maverick Studer
47ce4fab61 Merge branch 'RED-9925' into 'master'
RED-9925: 500 when creating justification without technical name or name

Closes RED-9925

See merge request redactmanager/persistence-service!748
2024-09-20 09:39:59 +02:00
Maverick Studer
09a060e1a7 RED-9925: 500 when creating justification without technical name or name 2024-09-20 09:39:59 +02:00
maverickstuder
68937f298b RED-10081: Update RM API specs after adding new download file type 2024-09-20 09:35:28 +02:00
Maverick Studer
c6110a6d65 Merge branch 'feature/RED-9010' into 'master'
RED-9010: Remove redaction log

Closes RED-9010

See merge request redactmanager/persistence-service!745
2024-09-19 11:34:42 +02:00
Maverick Studer
62719aec6f RED-9010: Remove redaction log 2024-09-19 11:34:42 +02:00
Maverick Studer
a96372e542 Merge branch 'RED-10057' into 'master'
RED-10057: legalBasisMapVersion missing in /status endpoint

Closes RED-10057

See merge request redactmanager/persistence-service!746
2024-09-19 09:41:46 +02:00
maverickstuder
3a113207d8 RED-10057: legalBasisMapVersion missing in /status endpoint 2024-09-18 16:39:10 +02:00
Maverick Studer
cbce87430f Merge branch 'RED-9925' into 'master'
RED-9925: 500 when creating justification without technical name or name

Closes RED-9925

See merge request redactmanager/persistence-service!743
2024-09-18 13:29:39 +02:00
maverickstuder
7ce90c1986 RED-9925: 500 when creating justification without technical name or name 2024-09-18 12:00:31 +02:00
Christoph Schabert
318883981e Update .gitlab-ci.yml file 2024-09-17 12:51:16 +02:00
Maverick Studer
e08690f7ee Merge branch 'RED-9947-fix' into 'master'
RED-9947: different logic in case of disabled auto analysis

Closes RED-9947

See merge request redactmanager/persistence-service!738
2024-09-13 14:29:33 +02:00
Maverick Studer
1abcd56e97 RED-9947: different logic in case of disabled auto analysis 2024-09-13 14:29:33 +02:00
Andrei Isvoran
1de9d75883 Merge branch 'RED-9394' into 'master'
RED-9394 - Add CONFIGURABLE_SMTP_SERVER license feature flag

Closes RED-9394

See merge request redactmanager/persistence-service!737
2024-09-13 12:30:02 +02:00
Andrei Isvoran
80cbda22bf RED-9394 - Add CONFIGURABLE_SMTP_SERVER license feature flag 2024-09-13 12:30:02 +02:00
Christoph Schabert
256a50775d Merge branch 'apidocs-redocly' into 'master'
Fixed some errors in the API specification.

See merge request redactmanager/persistence-service!736
2024-09-11 12:44:37 +02:00
Kresnadi Budisantoso
635dc05bde Fixed some errors in the API specification.
Additionally, added configuration and template files for Redocly API linting and docs generation, and added instructions how to use Redocly CLI.
2024-09-10 17:58:30 +02:00
Kresnadi Budisantoso
979e6a6650 Merge branch 'RED-9782-apispec-fp' into 'master'
RED-9782 Updated DocuMine and RedactManager OpenAPI spec (forward port from release/2.465.x)

See merge request redactmanager/persistence-service!645
2024-09-10 17:55:53 +02:00
Maverick Studer
361f899c70 Merge branch 'RED-9849' into 'master'
RED-9849: Counter for re-uploaded files

Closes RED-9849

See merge request redactmanager/persistence-service!731
2024-09-10 09:58:02 +02:00
Maverick Studer
04def902cd RED-9849: Counter for re-uploaded files 2024-09-10 09:58:01 +02:00
Maverick Studer
156337b82c Merge branch 'RED-9936' into 'master'
RED-9936: Clean-up job to delete all files related to hard-deleted files

Closes RED-9936

See merge request redactmanager/persistence-service!729
2024-09-10 09:26:22 +02:00
Maverick Studer
a5dea2acf1 RED-9936: Clean-up job to delete all files related to hard-deleted files 2024-09-10 09:26:22 +02:00
Andrei Isvoran
366b737e11 Merge branch 'RED-9541' into 'master'
RED-9541 - Add extra CSV validation

Closes RED-9541

See merge request redactmanager/persistence-service!733
2024-09-10 08:00:15 +02:00
Andrei Isvoran
874fdc85ee RED-9541 - Add extra CSV validation 2024-09-09 16:10:06 +03:00
Kilian Schüttler
d144b31747 Merge branch 'RED-9995' into 'master'
RED-9995: reset document_chunks in repeatStructureAnalysis

Closes RED-9995

See merge request redactmanager/persistence-service!732
2024-09-09 13:33:26 +02:00
Kilian Schuettler
5e7627862e RED-9995: reset document_chunks in repeatStructureAnalysis 2024-09-09 13:19:23 +02:00
Kilian Schüttler
e379e9b636 Merge branch 'RED-9995' into 'master'
RED-9995: fix error handling for llm-ner

Closes RED-9995

See merge request redactmanager/persistence-service!730
2024-09-06 16:17:01 +02:00
Kilian Schuettler
44f5a45887 RED-9995: fix error handling for llm-ner 2024-09-06 15:37:02 +02:00
Kilian Schüttler
9143292e0f Merge branch 'RED-9850-fp' into 'master'
RED-9850: 500 from duplicate entityLogEntries

Closes RED-9850

See merge request redactmanager/persistence-service!727
2024-09-06 14:38:15 +02:00
Kilian Schüttler
7dc6a56902 RED-9850: 500 from duplicate entityLogEntries 2024-09-06 14:38:15 +02:00
Maverick Studer
48eadc410d Merge branch 'RED-9947' into 'master'
RED-9947: bulk-local redactions

Closes RED-9947

See merge request redactmanager/persistence-service!725
2024-09-06 10:02:35 +02:00
Maverick Studer
bf5a2dd6f6 RED-9947: bulk-local redactions 2024-09-06 10:02:35 +02:00
Corina Olariu
d7ea4214ed Merge branch 'RED-9548-update' into 'master'
RED-9495 - Remove here for locally resized dictionary entry should remove the entry completly

Closes RED-9548

See merge request redactmanager/persistence-service!726
2024-09-06 09:55:12 +02:00
corinaolariu
38a6ca6a82 RED-9495 - Remove here for locally resized dictionary entry should remove the entry completly
- update the merge of entity log for force redactions
- add DICTIONARY engine to the local entry if based on DICT entry
2024-09-05 18:32:19 +03:00
Maverick Studer
d7694b37a2 Merge branch 'RED-9796' into 'master'
RED-9796: Links should not be removed on upload

Closes RED-9796

See merge request redactmanager/persistence-service!723
2024-09-05 16:29:49 +02:00
Maverick Studer
32e0436f9e RED-9796: Links should not be removed on upload 2024-09-05 16:29:49 +02:00
Maverick Studer
84d2a6fac0 Merge branch 'RED-9540' into 'master'
RED-9540: Workload columns in dossier are not showing the "R" (redacted)...

Closes RED-9540

See merge request redactmanager/persistence-service!721
2024-09-05 15:51:53 +02:00
Maverick Studer
c03b489650 RED-9540: Workload columns in dossier are not showing the "R" (redacted)... 2024-09-05 15:51:53 +02:00
Maverick Studer
05676c7fa2 Merge branch 'RED-9796' into 'master'
RED-9796: Links should not be removed on upload

Closes RED-9796

See merge request redactmanager/persistence-service!720
2024-09-05 14:25:34 +02:00
Maverick Studer
8831a9b24e RED-9796: Links should not be removed on upload 2024-09-05 14:25:34 +02:00
Kilian Schüttler
e37947fb7a Merge branch 'RED-9995' into 'master'
RED-9995: fix queue names

Closes RED-9995

See merge request redactmanager/persistence-service!719
2024-09-04 14:00:47 +02:00
Kilian Schuettler
99b6310d60 RED-9995: fix queue names 2024-09-04 12:45:18 +02:00
Yannik Hampe
60371fb34b Merge branch 'RED-9958-fix' into 'master'
RED-9958:Several problems after technical_name refactoring

Closes RED-9958

See merge request redactmanager/persistence-service!717
2024-09-04 12:10:42 +02:00
yhampe
0d4c005862 RED-9958:Several problems after technical_name refactoring
changed it so name and technical name are unique, rest is optional
2024-09-04 11:57:50 +02:00
Maverick Studer
149ce5144d Merge branch 'RED-9947' into 'master'
RED-9947: added record for positions list for code readability

Closes RED-9947

See merge request redactmanager/persistence-service!718
2024-09-04 10:51:28 +02:00
maverickstuder
9c0c442eb6 RED-9947: added record for positions list for code readability 2024-09-04 10:38:51 +02:00
yhampe
e5271c928e RED-9958:Several problems after technical_name refactoring
changed it so name and technical name are unique, rest is optional
2024-09-04 10:28:14 +02:00
Maverick Studer
dd0096a04c Merge branch 'RED-9947' into 'master'
RED-9947: case sensitivity can now be set as param

Closes RED-9947

See merge request redactmanager/persistence-service!716
2024-09-03 15:49:16 +02:00
Maverick Studer
52bd1dea3c RED-9947: case sensitivity can now be set as param 2024-09-03 15:49:16 +02:00
Maverick Studer
11088ddfef Merge branch 'RED-9947' into 'master'
RED-9947: Bulk-local redactions (replacement for group redactions)

Closes RED-9947

See merge request redactmanager/persistence-service!714
2024-09-03 13:49:47 +02:00
Maverick Studer
342257a9c6 RED-9947: Bulk-local redactions (replacement for group redactions) 2024-09-03 13:49:47 +02:00
Dominique Eifländer
8e2bf84a9d Merge branch 'RED-9994-master' into 'master'
RED-9994: Removed not working transactions to avoid long open db connections...

Closes RED-9994

See merge request redactmanager/persistence-service!713
2024-09-03 12:27:04 +02:00
Dominique Eifländer
4349fdc152 RED-9994: Removed not working transactions to avoid long open db connections in SyncUserPermissionsJob 2024-09-03 12:11:34 +02:00
Corina Olariu
6957857b42 Merge branch 'RED-9548' into 'master'
RED-9495 - Remove here for locally resized dictionary entry should remove the entry completly

Closes RED-9548

See merge request redactmanager/persistence-service!709
2024-08-30 09:46:10 +02:00
Yannik Hampe
3d9b7fc03f Merge branch 'RED-9958' into 'master'
RED-9958:Several problems after technical_name refactoring

Closes RED-9958

See merge request redactmanager/persistence-service!711
2024-08-29 15:16:13 +02:00
Maverick Studer
ff04d2a5ef Merge branch 'tenants-retry' into 'master'
Tenant retry and queue renames

See merge request redactmanager/persistence-service!710
2024-08-29 15:11:04 +02:00
Maverick Studer
4789a89257 Tenant retry and queue renames 2024-08-29 15:11:03 +02:00
yhampe
c506aefdb4 RED-9958:Several problems after technical_name refactoring
fixed tests
2024-08-29 13:51:35 +02:00
yhampe
3f9afa49e8 RED-9958:Several problems after technical_name refactoring
changed a lot of filtering from name to technical name
2024-08-29 13:31:22 +02:00
Kilian Schüttler
cfcff1f069 Merge branch 'RED-9864' into 'master'
RED-9864: prevent ocr being queued multiple times

Closes RED-9864

See merge request redactmanager/persistence-service!701
2024-08-29 12:14:21 +02:00
Kilian Schüttler
5188ab416e RED-9864: prevent ocr being queued multiple times 2024-08-29 12:14:20 +02:00
corinaolariu
a42e24d7f4 RED-9495 - Remove here for locally resized dictionary entry should remove the entry completly
- update db.changelog
2024-08-29 11:43:30 +03:00
Corina Olariu
65c6cdb3c9 Merge branch 'RED-9868-update' into 'master'
RED-9868 - Import older template zip with incompatible component rules breaks the stack

Closes RED-9868

See merge request redactmanager/persistence-service!702
2024-08-29 10:40:35 +02:00
corinaolariu
78e0859c02 Merge branch 'master' into RED-9548
# Conflicts:
#	persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EntityLogMergeService.java
#	persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml
2024-08-29 11:16:18 +03:00
Maverick Studer
0cc48f8dfd Merge branch 'REVERT-RED-7237' into 'master'
Revert "RED-7237 - Change to group annotation"

Closes RED-7237

See merge request redactmanager/persistence-service!708
2024-08-29 09:24:00 +02:00
Maverick Studer
f1b1f270d6 Revert "RED-7237 - Change to group annotation" 2024-08-29 09:24:00 +02:00
corinaolariu
5fdb7d3773 RED-9548 - Actions results in wrong results for deadlocked annotation
- fix unit test
2024-08-29 09:34:26 +03:00
corinaolariu
a8af78c501 RED-9495 - Remove here for locally resized dictionary entry should remove the entry completly
- update unit test
2024-08-28 14:53:41 +03:00
corinaolariu
84ebccfe79 RED-9495 - Remove here for locally resized dictionary entry should remove the entry completly
- update unit test
2024-08-28 13:45:06 +03:00
corinaolariu
2ad5232e41 RED-9495 - Remove here for locally resized dictionary entry should remove the entry completly
- any force made to a dictionary entry will be unlinked and will create a new id to be used and will introduce 2 manual changes: local add and the manual change.
- unit tests updated
2024-08-28 11:11:46 +03:00
Dominique Eifländer
daf6f835c1 Merge branch 'RED-9959-master' into 'master'
RED-9959: Delete and recreate acl entries that can not be created after they are not found.

Closes RED-9959

See merge request redactmanager/persistence-service!706
2024-08-28 09:56:46 +02:00
Dominique Eifländer
1377394d6a RED-9959: Delete and recreate acl entries that can not be created after they are not found. 2024-08-28 09:42:28 +02:00
Yannik Hampe
98bd00a89c Merge branch 'RED-9790-logging' into 'master'
RED-9790: manual changes not applied

Closes RED-9790

See merge request redactmanager/persistence-service!704
2024-08-28 08:44:30 +02:00
yhampe
06f7e57a74 RED-9790: manual changes not applied
added logging
2024-08-27 15:03:26 +02:00
corinaolariu
8683480c40 RED-9868 - Import older template zip with incompatible component rules breaks the stack
- specify the step in which the import process failed.
- unit test updated
2024-08-27 15:03:55 +03:00
Yannik Hampe
33cec84890 Merge branch 'RED-9717' into 'master'
Resolve RED-9717

Closes RED-9717

See merge request redactmanager/persistence-service!700
2024-08-27 12:53:49 +02:00
yhampe
6feb0d2a58 RED-9717: made fileId field required and removed it from WarningModel
fixed tests
2024-08-27 12:36:57 +02:00
Maverick Studer
7a209664cb Merge branch 'RED-9331' into 'master'
RED-9331: Explore possibilities for fair upload / analysis processing per tenant

Closes RED-9331

See merge request redactmanager/persistence-service!610
2024-08-27 11:29:14 +02:00
Maverick Studer
22bf27dd73 RED-9331: Explore possibilities for fair upload / analysis processing per tenant 2024-08-27 11:29:14 +02:00
Kilian Schüttler
bb4af4c1ce Merge branch 'SPIKE-LLM_NER' into 'master'
Spike: LLM NER

See merge request redactmanager/persistence-service!699
2024-08-27 10:28:29 +02:00
Kilian Schüttler
a99b2e8165 Spike: LLM NER 2024-08-27 10:28:29 +02:00
yhampe
2908477ce8 RED-9717: made fileId field required and removed it from WarningModel 2024-08-27 09:53:58 +02:00
yhampe
b977a2e46e RED-9717: added fileId to approveresponse 2024-08-27 09:44:47 +02:00
Kilian Schüttler
9eb3ef0181 Merge branch 'RED-9965' into 'master'
RED-9965: component definitions are outside of template folder for file export

Closes RED-9965

See merge request redactmanager/persistence-service!697
2024-08-26 17:26:13 +02:00
Kilian Schuettler
a377270141 RED-9965: component definitions are outside of template folder for file export 2024-08-26 16:10:02 +02:00
Maverick Studer
5e8d8ea6f6 Merge branch 'AZURE_NER_FP' into 'master'
RED-9918: Azure entity recognition (Spike)

See merge request redactmanager/persistence-service!696
2024-08-26 11:39:08 +02:00
Maverick Studer
5494d92f39 RED-9918: Azure entity recognition (Spike) 2024-08-26 11:39:07 +02:00
Maverick Studer
1edbf8ae56 Merge branch 'RED-9865' into 'master'
RED-9865: fix for case 2

Closes RED-9865

See merge request redactmanager/persistence-service!694
2024-08-23 15:16:08 +02:00
Yannik Hampe
3adb35bec4 Merge branch 'RED-9790' into 'master'
RED-9790: manual changes not applied when too many redaction requests was sent

Closes RED-9790

See merge request redactmanager/persistence-service!691
2024-08-23 14:28:58 +02:00
maverickstuder
2697f014e6 RED-9865: fix 2024-08-23 13:19:08 +02:00
yhampe
1ac9f7ebcf RED-9790: manual changes not applied when too many redaction requests was sent
added an exception to more easily filter the traces for the cases were no exception is produced, but the manualredactions are ignored/not processed
2024-08-23 10:30:08 +02:00
Andrei Isvoran
7a9bffa3c5 Merge branch 'RED-7327-update' into 'master'
RED-7237 - Change to group annotation

Closes RED-7327

See merge request redactmanager/persistence-service!686
2024-08-22 09:24:33 +02:00
Yannik Hampe
fd910bf189 Merge branch 'RED-3813-refactoring' into 'master'
RED-3813: image similarity refactoring

Closes RED-3813

See merge request redactmanager/persistence-service!690
2024-08-21 11:20:36 +02:00
yhampe
963cf0137b RED-3813: image similarity refactoring
seperated logic for image similarity search to service

changed some logic when filtering for scope

changed some logic for filtering bad request
2024-08-21 11:05:20 +02:00
Kilian Schüttler
1037178711 Merge branch 'RED-9902-fp' into 'master'
RED-9902: fix applicationtype check

Closes RED-9902

See merge request redactmanager/persistence-service!688
2024-08-20 17:12:51 +02:00
Kilian Schuettler
a3d15f59c1 RED-9902: fix applicationtype check 2024-08-20 16:56:28 +02:00
Andrei Isvoran
555dd3330a RED-7237 - Change to group annotation 2024-08-20 17:20:50 +03:00
Yannik Hampe
9caa1f3068 Merge branch 'RED-3813-hotfix' into 'master'
RED-3813: image similarity hotifx

Closes RED-3813

See merge request redactmanager/persistence-service!685
2024-08-20 15:48:46 +02:00
yhampe
1f5c9f12ff Merge remote-tracking branch 'origin/RED-3813-hotfix' into RED-3813-hotfix 2024-08-20 14:15:17 +02:00
yhampe
833b391f47 RED-3813: image similarity hotifx
fixed mistake in spelling
2024-08-20 14:15:05 +02:00
yhampe
630cde1f8f RED-3813: image similarity hotifx
added action role to red_admin_action_roles
2024-08-20 14:15:05 +02:00
yhampe
b6b6fad242 RED-3813: image similarity hotifx
fixed mistake in spelling
2024-08-20 13:59:58 +02:00
yhampe
1253d7d0b4 RED-3813: image similarity hotifx
added action role to red_admin_action_roles
2024-08-20 11:36:59 +02:00
Kilian Schüttler
c57e4c507e Merge branch 'RED-9902-fp' into 'master'
RED-9902: create component definitions in migration

Closes RED-9902

See merge request redactmanager/persistence-service!684
2024-08-20 11:07:20 +02:00
Kilian Schuettler
7eea40cc6c RED-9902: create component definitions in migration 2024-08-20 10:52:17 +02:00
Maverick Studer
c2ed3eeff9 Merge branch 'RED-9888-9891' into 'master'
RED-9888 && RED-9891

Closes RED-9888

See merge request redactmanager/persistence-service!681
2024-08-19 16:46:14 +02:00
Maverick Studer
4beb6e0647 RED-9888 && RED-9891 2024-08-19 16:46:14 +02:00
Andrei Isvoran
ef98112871 Merge branch 'RED-7327' into 'master'
RED-7327 - Add group redactions

Closes RED-7327

See merge request redactmanager/persistence-service!662
2024-08-19 15:17:49 +02:00
Andrei Isvoran
6d1b1ca31b RED-7327 - Add group redactions 2024-08-19 15:17:48 +02:00
Andrei Isvoran
4eeee75f33 Merge branch 'RED-9717' into 'master'
RED-9717 - Add warnings on approval

Closes RED-9717

See merge request redactmanager/persistence-service!632
2024-08-19 14:27:38 +02:00
Andrei Isvoran
2a0d9ce3f9 RED-9717 - Add warnings on approval 2024-08-19 14:27:38 +02:00
Yannik Hampe
9bcf2d177e Merge branch 'RED-3813-hotfix' into 'master'
RED-3813: image similarity hotifx

Closes RED-3813

See merge request redactmanager/persistence-service!675
2024-08-19 09:00:19 +02:00
Kilian Schüttler
53c624c2c9 Merge branch 'RED-9902-fp' into 'master'
RED-9902: copy overrides to mongo

See merge request redactmanager/persistence-service!677
2024-08-16 14:58:41 +02:00
Kilian Schüttler
b8f64a1c86 RED-9902: copy overrides to mongo 2024-08-16 14:58:41 +02:00
Kilian Schüttler
2c2347f5f0 Merge branch 'RED-9843-master' into 'master'
Resolve RED-9843 "Master"

Closes RED-9843

See merge request redactmanager/persistence-service!669
2024-08-16 14:14:16 +02:00
yhampe
91e317057d RED-3813: image similarity hotifx
renaming centroId to annotationId
2024-08-16 08:54:57 +02:00
Maverick Studer
399ad6e4a1 Merge branch 'RED-9782-fix2' into 'master'
RED-9782: fix for ignored flag excludeFromAutomaticAnalysis when overriding...

Closes RED-9782

See merge request redactmanager/persistence-service!674
2024-08-15 17:00:06 +02:00
maverickstuder
829dc2f4b3 RED-9782: fix for ignored flag excludeFromAutomaticAnalysis when overriding files that are hard deleted 2024-08-15 16:26:20 +02:00
Maverick Studer
804ad3cf88 Merge branch 'RED-9891' into 'master'
RED-9891: Re-creation of component-definition does not work

Closes RED-9891

See merge request redactmanager/persistence-service!670
2024-08-15 15:26:06 +02:00
Maverick Studer
2b7be87985 RED-9891: Re-creation of component-definition does not work 2024-08-15 15:26:06 +02:00
Maverick Studer
9c539ffd21 Merge branch 'RED-9888' into 'master'
RED-9888: DM: Components not restored after file re-upload

Closes RED-9888

See merge request redactmanager/persistence-service!665
2024-08-15 15:25:54 +02:00
Maverick Studer
e4ec187c2f RED-9888: DM: Components not restored after file re-upload 2024-08-15 15:25:54 +02:00
Dominique Eifländer
8dd9a73694 RED-9843: Changed version for AddTechnicalNameToJustifications 2024-08-15 12:38:16 +02:00
Dominique Eifländer
495ed75fc3 RED-9843: Added migration for documine to reanalyse layout on not approved files 2024-08-15 12:38:16 +02:00
Maverick Studer
dc8abbba58 Merge branch 'RED-9782-fix' into 'master'
RED-9782:Automated Analysis should be disabled when uploading a document that...

See merge request redactmanager/persistence-service!667
2024-08-15 12:34:40 +02:00
Maverick Studer
13b92135ae RED-9782:Automated Analysis should be disabled when uploading a document that... 2024-08-15 12:34:40 +02:00
Dominique Eifländer
fd8da20e72 Merge branch 'RED-9868' into 'master'
RED-9868 - Import older template zip with incompatible component rules breaks the stack

Closes RED-9868

See merge request redactmanager/persistence-service!664
2024-08-15 09:20:27 +02:00
corinaolariu
6d428cd15c RED-9868 - Import older template zip with incompatible component rules breaks the stack
- fix some sonar issues
2024-08-14 12:26:51 +03:00
corinaolariu
3c32201e94 RED-9868 - Import older template zip with incompatible component rules breaks the stack
- throwing and catching an exception inside a transaction will cause the rollback.
- return the optional rules instead of throwing an error in case they are missing. update the code
- add unit test.
2024-08-14 12:09:05 +03:00
Maverick Studer
48ce264967 Merge branch 'RED-9782-fix' into 'master'
RED-9782:Automated Analysis should be disabled when uploading a document that...

Closes RED-9782

See merge request redactmanager/persistence-service!661
2024-08-12 18:40:51 +02:00
Maverick Studer
c6faa877a1 RED-9782:Automated Analysis should be disabled when uploading a document that... 2024-08-12 18:40:51 +02:00
Yannik Hampe
b1549ba775 Merge branch 'RED-5624-migration' into 'master'
RED-5624: migration

Closes RED-5624

See merge request redactmanager/persistence-service!655
2024-08-12 11:37:31 +02:00
Corina Olariu
9be0fad408 Merge branch 'RED-9832' into 'master'
RED-9832 - FalsePositives and FalseRecommendations not imported

Closes RED-9832

See merge request redactmanager/persistence-service!658
2024-08-09 13:40:17 +02:00
yhampe
b24dcdaa86 RED-5624: migration
migration is working
2024-08-09 13:29:23 +02:00
corinaolariu
00f1b8e495 RED-9832 - FalsePositives and FalseRecommendations not imported
- update the corresponding functions for entity type import
- fix the problem for upload a dossier template and update its name because of duplicate
- added unit test for duplicate names
2024-08-09 13:17:35 +03:00
yhampe
96eef1a7a2 RED-5624: migration
working on migration
2024-08-09 10:10:06 +02:00
yhampe
79fec2e9c9 RED-5624: migration
working on migration
2024-08-09 09:38:23 +02:00
Maverick Studer
8c96f75d3a Merge branch 'RED-9802' into 'master'
RED-9802: Disable Imported Redactions Flow in DM

Closes RED-9802

See merge request redactmanager/persistence-service!656
2024-08-09 09:21:53 +02:00
maverickstuder
675e9e0c42 RED-9802: Disable Imported Redactions Flow in DM 2024-08-08 17:20:32 +02:00
yhampe
cff6826424 RED-5624: migration
working on migration
2024-08-08 16:00:36 +02:00
yhampe
535f9ce8da RED-5624: migration
added class for migration
2024-08-08 14:36:54 +02:00
Andrei Isvoran
034c880e4f Merge branch 'RED-9771-delimiter-issues-fp' into 'master'
RED-9771 - Fix CSV mapping file changing delimiter and adding quotes

Closes RED-9771

See merge request redactmanager/persistence-service!654
2024-08-08 09:57:09 +02:00
Maverick Studer
8198d6cfaa Merge branch 'RED-9376' into 'master'
RED-9736: Valid file formats cache will not scale with persistence-service

Closes RED-9376

See merge request redactmanager/persistence-service!652
2024-08-08 09:24:38 +02:00
Andrei Isvoran
c80008cbb3 RED-9771 - Fix CSV mapping file changing delimiter and adding quotes 2024-08-08 10:22:44 +03:00
maverickstuder
4016a26c2c RED-9736: Valid file formats cache will not scale with persistence-service
* add redis caching for valid file formats
2024-08-07 14:59:32 +02:00
Kilian Schüttler
3af71f2760 Merge branch 'RED-5624' into 'master'
RED-5624: Refactoring of justifications

Closes RED-5624

See merge request redactmanager/persistence-service!605
2024-08-07 13:26:29 +02:00
Corina Olariu
2361d483f0 Merge branch 'RED-9474-analyze-after-undo' into 'master'
RED-9474 - Revert only local manual changes for an annotation

Closes RED-9474

See merge request redactmanager/persistence-service!651
2024-08-07 12:19:06 +02:00
corinaolariu
ff4b3369c9 RED-9474 - Revert only local manual changes for an annotation
- trigger an analysis instead of a reanalysis after an undo call
2024-08-07 12:43:54 +03:00
yhampe
043d9ca299 RED-5624: Refactoring of justifications
fixing migration
2024-08-07 11:09:49 +02:00
Maverick Studer
4351f67d16 Merge branch 'RED-9782-fp' into 'master'
RED-9782: Automated Analysis should be disabled when uploading a document that...

Closes RED-9782

See merge request redactmanager/persistence-service!650
2024-08-07 10:05:33 +02:00
Maverick Studer
5f2b851efb RED-9782: Automated Analysis should be disabled when uploading a document that... 2024-08-07 10:05:33 +02:00
yhampe
f4a7a9a25e RED-5624: Refactoring of justifications
fixing migration
2024-08-07 09:18:38 +02:00
Christoph Schabert
514cae7fe5 Update .gitlab-ci.yml file 2024-08-06 14:50:38 +02:00
Christoph Schabert
39ac84566a publish only select projects 2024-08-06 14:40:24 +02:00
Christoph Schabert
f1058caeed Update .gitlab-ci.yml file 2024-08-06 14:28:06 +02:00
Christoph Schabert
dfc1f6016c Update .gitlab-ci.yml file 2024-08-06 14:23:12 +02:00
Christoph Schabert
81099adf64 Update .gitlab-ci.yml file 2024-08-06 14:18:31 +02:00
Christoph Schabert
47e8bee9db Update .gitlab-ci.yml file 2024-08-06 13:38:43 +02:00
yhampe
c9e3a9d086 RED-5624: Refactoring of justifications
fixed ManualRedactionTest
2024-08-06 12:58:39 +02:00
yhampe
801dc52cd2 RED-5624: Refactoring of justifications
added field for technical name to EntityLogLegalBasis
2024-08-06 12:51:14 +02:00
Dominique Eifländer
cc969350f8 Merge branch 'RED-9782-master' into 'master'
RED-9782: Enabled to upload file and import only imported redactions with...

Closes RED-9782

See merge request redactmanager/persistence-service!648
2024-08-06 12:39:21 +02:00
Kilian Schüttler
a94e4a6caf Merge branch 'hotfix-duplicate-entries-fp' into 'master'
migration-hotfix: remove duplicate entries from entityLog if present

See merge request redactmanager/persistence-service!647
2024-08-06 11:35:49 +02:00
Dominique Eifländer
b72e432741 RED-9782: Fixed bugs in upload with disabledAutomaticAnalysis 2024-08-06 11:18:09 +02:00
Dominique Eifländer
b1fb547b0e RED-9782: Enabled to upload file and import only imported redactions with disabled automatic analysis 2024-08-06 11:17:57 +02:00
Kilian Schuettler
fdf3fe5b4f migration-hotfix: remove duplicate entries from entityLog if present 2024-08-06 11:03:41 +02:00
Kresnadi Budisantoso
f5b26deb4c RED-9782 Updated DocuMine and RedactManager OpenAPI spec (forward port from release/2.465.x) 2024-08-05 13:46:55 +02:00
Christoph Schabert
b7e88e4ec1 Merge branch 'hotfix-buildpacks' into 'master'
hotfix: Load buildpacks from docker-proxy.knecon.com

See merge request redactmanager/persistence-service!639
2024-08-01 13:51:16 +02:00
Dominique Eifländer
e7ae91af47 hotfix: Load buildpacks from docker-proxy.knecon.com 2024-08-01 13:39:28 +02:00
Dominique Eifländer
f760598d8f Merge branch 'RED-9255-master' into 'master'
RED-9255: Fixed possible higher versions in entityLogs than in dictionaries or...

Closes RED-9255

See merge request redactmanager/persistence-service!638
2024-08-01 11:30:20 +02:00
Dominique Eifländer
13dbd04084 RED-9255: Fixed possible higher versions in entityLogs than in dictionaries or rules when importing existing dossier templates 2024-08-01 11:16:54 +02:00
Andrei Isvoran
6a918bf939 Merge branch 'RED-9771-fp' into 'master'
RED-9771 - Fixes for updating component mappings

Closes RED-9771

See merge request redactmanager/persistence-service!636
2024-07-31 10:32:37 +02:00
Andrei Isvoran
0f15e68623 RED-9771 - Fixes for updating component mappings 2024-07-31 10:32:36 +02:00
Ali Oezyetimoglu
66f1a86c77 Merge branch 'RED-8951' into 'master'
RED-8951: added some JUnit tests

Closes RED-8951

See merge request redactmanager/persistence-service!633
2024-07-30 16:27:54 +02:00
alioezyetimoglu
09749e8344 RED-8951: added some JUnit tests 2024-07-30 15:58:27 +02:00
Andrei Isvoran
2916c19cb1 Merge branch 'RED-8749-fix-npe' into 'master'
RED-9749 - Fix NPE

Closes RED-8749

See merge request redactmanager/persistence-service!630
2024-07-29 10:40:00 +02:00
Andrei Isvoran
3a463e1ed1 RED-9749 - Fix NPE 2024-07-29 11:06:26 +03:00
Andrei Isvoran
cd899b2774 Merge branch 'RED-9749-fp' into 'master'
RED-9749 - Remove notnull constraint

Closes RED-9749

See merge request redactmanager/persistence-service!629
2024-07-29 08:15:09 +02:00
Andrei Isvoran
e73a4cb8a3 RED-9749 - Remove notnull constraint 2024-07-26 17:04:01 +03:00
Kilian Schüttler
b7c8c2b668 Merge branch 'hotfix-migration-fp' into 'master'
migration-fp-hotfix

See merge request redactmanager/persistence-service!617
2024-07-26 14:54:00 +02:00
Kilian Schuettler
844ceb66c1 fix pipeline 2024-07-26 13:31:01 +02:00
Dominique Eifländer
32aa88f6b4 Merge branch 'RED-9740-master' into 'master'
RED-9740: Added migration to add graphic type

Closes RED-9740

See merge request redactmanager/persistence-service!624
2024-07-26 12:35:27 +02:00
Dominique Eifländer
1fcd3ecd6b RED-9740: Added migration to add graphic type 2024-07-26 12:19:48 +02:00
Andrei Isvoran
6e2c2dfbdc Merge branch 'RED-9749' into 'master'
RED-9749 - Fix various issues with component definitions

Closes RED-9749

See merge request redactmanager/persistence-service!621
2024-07-26 10:42:28 +02:00
Andrei Isvoran
8b746a5db5 RED-9749 - Fix various issues with component definitions 2024-07-26 10:42:28 +02:00
Ali Oezyetimoglu
193dfaca67 Merge branch 'RED-8951' into 'master'
RED-8951: made it possible to import entities with dictionary but hasDictionary=false

Closes RED-8951

See merge request redactmanager/persistence-service!619
2024-07-24 09:02:11 +02:00
Ali Oezyetimoglu
784b67ee5a RED-8951: made it possible to import entities with dictionary but hasDictionary=false 2024-07-24 08:00:28 +02:00
Andrei Isvoran
f72ca45689 Merge branch 'RED-9140-fix-ignored' into 'master'
RED-9140 - Correctly distinct between ChangeType REMOVED and IGNORED during EntityLog merge

Closes RED-9140

See merge request redactmanager/persistence-service!616
2024-07-23 11:48:47 +02:00
Andrei Isvoran
0c4ebbd6b9 RED-9140 - Correctly distinct between ChangeType REMOVED and IGNORED during EntityLog merge 2024-07-23 11:48:46 +02:00
yhampe
bbaaa6f952 RED-5624: Refactoring of justifications
added changelog
2024-07-23 11:45:08 +02:00
yhampe
7245f71acb RED-5624: Refactoring of justifications
refactored constructor calls
2024-07-23 10:05:01 +02:00
Kilian Schuettler
b6c8ec2f7c migration-fp-hotfix 2024-07-23 09:59:18 +02:00
Kilian Schuettler
fe3b437b80 migration-fp-hotfix 2024-07-23 09:55:19 +02:00
Kilian Schüttler
2f35d2b218 Merge branch 'RED-9272-fp' into 'master'
RED-9272: increase storageToMongoDb performance

Closes RED-9272

See merge request redactmanager/persistence-service!615
2024-07-23 00:10:29 +02:00
Kilian Schuettler
2af5e9fea7 RED-9272: increase storageToMongoDb performance 2024-07-22 16:45:25 +02:00
Andrei Isvoran
e120ff589b Merge branch 'RED-9140-add-ignored' into 'master'
RED-9140 - Add ChangeType IGNORED

Closes RED-9140

See merge request redactmanager/persistence-service!613
2024-07-22 15:47:03 +02:00
Andrei Isvoran
aa1f3e0a8b RED-9140 - Add ChangeType IGNORED 2024-07-22 16:15:40 +03:00
Kilian Schüttler
8ef1c7187d Merge branch 'RED-9255' into 'master'
Resolve RED-9255

Closes RED-9255

See merge request redactmanager/persistence-service!611
2024-07-19 11:53:04 +02:00
Kilian Schüttler
15ab5d4215 Resolve RED-9255 2024-07-19 11:53:04 +02:00
Maverick Studer
20f760b37c Merge branch 'document-data-markdown' into 'master'
CLARI: document-data-markdown

See merge request redactmanager/persistence-service!609
2024-07-19 10:35:37 +02:00
Maverick Studer
8006a4c374 CLARI: document-data-markdown 2024-07-19 10:35:36 +02:00
Dominique Eifländer
3430288026 Merge branch 'RED-9658-master' into 'master'
RED-9658: Fixed wrong mongo database name

Closes RED-9658

See merge request redactmanager/persistence-service!607
2024-07-17 11:05:35 +02:00
Dominique Eifländer
5cf4c034eb RED-9658: Fixed wrong mongo database name 2024-07-17 10:51:25 +02:00
yhampe
8d340057a4 RED-5624: Refactoring of justifications
added technicalname field to entity
2024-07-17 10:13:58 +02:00
Corina Olariu
f266582f48 Merge branch 'RED-9608' into 'master'
RED-9608 - Comments not removed after uploading multiple files via ZIP-Archive...

Closes RED-9608

See merge request redactmanager/persistence-service!601
2024-07-16 16:49:24 +02:00
Kilian Schüttler
a8ead5bea4 Merge branch 'RED-9255' into 'master'
RED-9255: return imported dossier template id

Closes RED-9255

See merge request redactmanager/persistence-service!602
2024-07-16 15:43:48 +02:00
Kilian Schüttler
5f0e4b381c RED-9255: return imported dossier template id 2024-07-16 15:43:48 +02:00
Andrei Isvoran
31c2b596be Merge branch 'RED-9606-fp' into 'master'
RED-9606 - Bulk actions on many annotations

Closes RED-9606

See merge request redactmanager/persistence-service!600
2024-07-16 13:25:17 +02:00
corinaolariu
5cfb45d3d2 RED-9608 - Comments not removed after uploading multiple files via ZIP-Archive with "Overwrite and start over" selected
- update repository
2024-07-16 14:14:34 +03:00
Andrei Isvoran
14da82e2b7 RED-9606 - Bulk actions on many annotations 2024-07-16 14:01:44 +03:00
corinaolariu
552d760ead RED-9608 - Comments not removed after uploading multiple files via ZIP-Archive with "Overwrite and start over" selected
- remove (soft-delete) all comments, not just the ones related to the manual redactions at overwrite
- restore back all comments (not just the ones related to the manual redactions) when file is restored
- unit tests added
2024-07-16 13:48:25 +03:00
Andrei Isvoran
55c6b7419d Merge branch 'RED-9555-analysis-disabled-fp' into 'master'
RED-9555 - Keep force redacted image applied after changing legal basis

Closes RED-9555

See merge request redactmanager/persistence-service!596
2024-07-15 15:43:32 +02:00
Corina Olariu
b83207f715 Merge branch 'RED-9474' into 'master'
RED-9474 - Revert only local manual changes for an annotation

Closes RED-9474

See merge request redactmanager/persistence-service!590
2024-07-15 15:02:20 +02:00
Andrei Isvoran
99c7ef2529 RED-9555 - Keep force redacted image applied after changing legal basis 2024-07-15 15:47:46 +03:00
Dominique Eifländer
89446913b1 Merge branch 'RED-9658-fp' into 'master'
RED-9658: Fixed changed order of liquibase migrations

Closes RED-9658

See merge request redactmanager/persistence-service!594
2024-07-15 13:56:56 +02:00
Dominique Eifländer
e44646234e RED-9658: Fixed changed order of liquibase migrations 2024-07-15 13:45:04 +02:00
Andrei Isvoran
30c982f2c8 Merge branch 'RED-9622-fp' into 'master'
RED-9622 - Introduce csv validation

Closes RED-9622

See merge request redactmanager/persistence-service!592
2024-07-15 10:41:17 +02:00
Andrei Isvoran
7539aa4510 RED-9622 - Introduce csv validation 2024-07-15 10:53:34 +03:00
corinaolariu
c9389e04fa RED-9474 - Revert only local manual changes for an annotation
- rework the undo logic. Introduced new flag includeOnlyLocal to filter out the local changes. Now all manual changes will be reverted for the annotations provided.
- unit tests added
2024-07-12 15:59:22 +03:00
Andrei Isvoran
6f5174cc4b Merge branch 'RED-9606' into 'master'
RED-9606 - Fix removal

Closes RED-9606

See merge request redactmanager/persistence-service!586
2024-07-11 14:36:33 +02:00
Corina Olariu
1a156e0b76 Merge branch 'RED-9495' into 'master'
RED-9495 - Remove here for locally resized dictionary entry should remove the entry completly

Closes RED-9495

See merge request redactmanager/persistence-service!583
2024-07-11 14:34:55 +02:00
Andrei Isvoran
0f3434bca1 RED-9606 - Fix removal 2024-07-11 15:11:52 +03:00
Kilian Schüttler
b5405370f4 Merge branch 'RED-8876' into 'master'
RED-8876: updated pending dictionary reasons

Closes RED-8876

See merge request redactmanager/persistence-service!409
2024-07-11 11:07:19 +02:00
Kilian Schüttler
e644afb8e6 RED-8876: updated pending dictionary reasons 2024-07-11 11:07:18 +02:00
Kilian Schüttler
cb93273528 Merge branch 'renovate/master-plugins-(non-major)' into 'master'
Update Plugins (non-major) (master)

See merge request redactmanager/persistence-service!244
2024-07-11 10:51:38 +02:00
Kilian Schüttler
67e42dc4e0 Merge branch 'renovate/master-com.fasterxml.jackson.dataformat-jackson-dataformat-xml-2.x' into 'master'
Update dependency com.fasterxml.jackson.dataformat:jackson-dataformat-xml to v2.17.2 (master)

See merge request redactmanager/persistence-service!245
2024-07-11 10:51:25 +02:00
Kilian Schüttler
84ef6e6f4c Merge branch 'renovate/master-com.iqser.red.commons-metric-commons-2.x' into 'master'
Update dependency com.iqser.red.commons:metric-commons to v2.3.0 (master)

See merge request redactmanager/persistence-service!250
2024-07-11 10:50:23 +02:00
Kilian Schüttler
4a56bbc07f Merge branch 'RED-9255' into 'master'
Some NPE fixes

Closes RED-9255

See merge request redactmanager/persistence-service!574
2024-07-11 10:44:06 +02:00
Kilian Schüttler
562e9d0761 Some NPE fixes 2024-07-11 10:44:06 +02:00
corinaolariu
3fefb8efab RED-9495 - Remove here for locally resized dictionary entry should remove the entry completly
- update unit tests
2024-07-11 10:50:51 +03:00
Andrei Isvoran
57871d11c9 Merge branch 'RED-9604-42' into 'master'
RED-9604 - Don't check that removed entries still have comments

Closes RED-9604

See merge request redactmanager/persistence-service!580
2024-07-11 08:57:44 +02:00
Renovate Bot
26cba53508 Update dependency com.iqser.red.commons:metric-commons to v2.3.0 2024-07-10 17:12:51 +00:00
Renovate Bot
49fe599c35 Update dependency com.fasterxml.jackson.dataformat:jackson-dataformat-xml to v2.17.2 2024-07-10 17:12:43 +00:00
Renovate Bot
71ffb03dfc Update Plugins (non-major) 2024-07-10 17:12:40 +00:00
Andrei Isvoran
bc760a6870 RED-9604 - Don't check that removed entries still have comments 2024-07-10 17:37:21 +03:00
corinaolariu
0bf85842c1 RED-9495 - Remove here for locally resized dictionary entry should remove the entry completly
- rollback changes for another ticket
2024-07-10 16:03:09 +03:00
corinaolariu
4fbea2e3e7 RED-9495 - Remove here for locally resized dictionary entry should remove the entry completly
- any local resize, legal basis change and recategorization made to a dictionary entry will be unlinked and will create a new id to be used and will introduce 2 manual changes: local add and the manual change.
- unit tests added
2024-07-10 15:55:57 +03:00
Ali Oezyetimoglu
fd021041f7 Merge branch 'RED-9517' into 'master'
RED-9517: Use redis for chaching report templates

Closes RED-9517

See merge request redactmanager/persistence-service!579
2024-07-10 14:54:53 +02:00
Ali Oezyetimoglu
a40962085c RED-9573: updated redaction-report-service version to enable updated cache mechanism 2024-07-10 14:43:35 +02:00
Dominique Eifländer
3d73c6f496 RED-9517: Use redis for chaching report templates 2024-07-10 13:31:51 +02:00
Corina Olariu
6cdd888967 Merge branch 'RED-9515' into 'master'
RED-9515 - When locally added annotations are locally removed while...

Closes RED-9515

See merge request redactmanager/persistence-service!577
2024-07-10 13:03:59 +02:00
corinaolariu
e631281156 RED-9515 - When locally added annotations are locally removed while auto-analysis is off, its skipped
- fix sonar issues
2024-07-10 13:19:42 +03:00
corinaolariu
a0eee59ced RED-9515 - When locally added annotations are locally removed while auto-analysis is off, its skipped
- update unit test
2024-07-10 12:53:16 +03:00
corinaolariu
531524be1e RED-9515 - When locally added annotations are locally removed while auto-analysis is off, its skipped
- removed special character from tests
2024-07-09 12:55:30 +03:00
corinaolariu
369cc6340a RED-9515 - When locally added annotations are locally removed while auto-analysis is off, its skipped
- special case when removing local add (and other local changes to this add annotation) annotations to set the state to REMOVED instead of IGNORED.
- unit tests done
2024-07-09 12:52:49 +03:00
Andrei Isvoran
539da8d80e Merge branch 'RED-9496-fix-shutdown' into 'master'
RED-9496 - Implement graceful shutdown

Closes RED-9496

See merge request redactmanager/persistence-service!571
2024-07-04 16:07:35 +02:00
Andrei Isvoran
3ad5e0300c RED-9496 - Implement graceful shutdown 2024-07-04 16:51:40 +03:00
Dominique Eifländer
9d4a942a48 Merge branch 'RED-9551' into 'master'
RED-9551: Fixed multiple analysis on upload

Closes RED-9551

See merge request redactmanager/persistence-service!569
2024-07-04 14:18:05 +02:00
Dominique Eifländer
84fb04fb30 RED-9551: Fixed multiple analysis on upload 2024-07-04 12:51:38 +02:00
Andrei Isvoran
dc20d2c2c8 Merge branch 'RED-9498-shutdown' into 'master'
RED-9496 - Implement graceful shutdown

Closes RED-9498

See merge request redactmanager/persistence-service!567
2024-07-04 12:35:00 +02:00
Andrei Isvoran
52c564b5e3 RED-9496 - Implement graceful shutdown 2024-07-04 12:50:37 +03:00
Ali Oezyetimoglu
62852d48df Merge branch 'RED-9499' into 'master'
RED-9499: increased id size of entity fo fit typeId length of a created entity

Closes RED-9499

See merge request redactmanager/persistence-service!576
2024-07-04 09:46:07 +02:00
Ali Oezyetimoglu
d8c5668a38 Update TypeTest.java 2024-07-03 16:17:30 +02:00
Ali Oezyetimoglu
6157154378 RED-9499: increased id size of entity fo fit typeId length of a created entity
* catch too long names
* added JUnit test
2024-07-03 15:42:55 +02:00
Dominique Eifländer
1c896f72e1 Merge branch 'RED-9530' into 'master'
RED-9530: Fixed single and bulk create download endpoints in api v2

Closes RED-9530

See merge request redactmanager/persistence-service!573
2024-07-03 11:31:57 +02:00
Dominique Eifländer
db09744506 RED-9530: Fixed single and bulk create download endpoints in api v2 2024-07-03 11:19:22 +02:00
Dominique Eifländer
7326a387ca Merge branch 'RED-9500' into 'master'
RED-9500: Use Redis pub/sub to syncronize websocket topic messages accross multiple pods

Closes RED-9500

See merge request redactmanager/persistence-service!572
2024-07-02 13:18:20 +02:00
Dominique Eifländer
887e56b98e RED-9500: Use Redis pub/sub to syncronize websocket topic messages accross multiple pods 2024-07-02 13:01:23 +02:00
Maverick Studer
a8a9232c4c Merge branch 'RED-9123' into 'master'
RED-9123: Improve performance of re-analysis (Spike)

Closes RED-9123

See merge request redactmanager/persistence-service!571
2024-07-02 10:56:44 +02:00
Maverick Studer
a8b7a835c0 RED-9123: Improve performance of re-analysis (Spike) 2024-07-02 10:56:44 +02:00
Dominique Eifländer
bab96ba58d Merge branch 'RED-9487' into 'master'
RED-9487: Fixed resizing of manual redactions when auto analysis is disabled

Closes RED-9487

See merge request redactmanager/persistence-service!569
2024-07-01 12:11:42 +02:00
Dominique Eifländer
9f4edce48a RED-9487: Fixed resizing of manual redactions when auto analysis is disabled 2024-07-01 11:58:09 +02:00
Andrei Isvoran
6c857290b8 Merge branch 'RED-9349-dossier' into 'master'
RED-9349 - Add check for dossier template length

Closes RED-9349

See merge request redactmanager/persistence-service!567
2024-07-01 09:33:45 +02:00
Andrei Isvoran
f12b5d8e98 RED-9349 - Add check for dossier template length 2024-07-01 10:21:47 +03:00
Kilian Schüttler
740e88fd1b Merge branch 'hotfix-logging' into 'master'
hotfix: Fixed logging

See merge request redactmanager/persistence-service!565
2024-06-28 10:31:13 +02:00
Dominique Eifländer
c4e682254b hotfix: Fixed logging 2024-06-28 10:13:48 +02:00
Kilian Schüttler
bbb2a1b464 Merge branch 'RED-9255' into 'master'
RED-9255: fix annotations, refactor for observability

Closes RED-9255

See merge request redactmanager/persistence-service!562
2024-06-28 09:55:33 +02:00
Yannik Hampe
cc9daf9180 Merge branch 'RED-3813' into 'master'
RED-3813: image recategorization

Closes RED-3813

See merge request redactmanager/persistence-service!482
2024-06-28 08:40:39 +02:00
Kilian Schuettler
41a8a3c32a RED-9255: fix annotations, refactor for observability 2024-06-27 19:06:54 +02:00
yhampe
361b11ca2b RED-3813: image recategorization
checkstyle
2024-06-27 12:44:12 +02:00
yhampe
c3aaba45cc RED-3813: image recategorization
refactor checkstyle
2024-06-27 12:20:55 +02:00
yhampe
44bce39a2a RED-3813: image recategorization
refactor checkstyle
2024-06-27 12:19:45 +02:00
yhampe
7a24702c47 RED-3813: image recategorization
readded deleted service
2024-06-27 12:16:43 +02:00
Andrei Isvoran
7a86b37c13 Merge branch 'RED-9140' into 'master'
RED-9140 - Add more information to changes

Closes RED-9140

See merge request redactmanager/persistence-service!541
2024-06-27 11:53:35 +02:00
Andrei Isvoran
e0d00dcc48 RED-9140 - Add more information to changes 2024-06-27 11:53:35 +02:00
Kilian Schüttler
c34af58aa0 Merge branch 'RED-9375' into 'master'
RED-9375: fix component mapping endpoint validation

Closes RED-9375

See merge request redactmanager/persistence-service!561
2024-06-27 09:42:21 +02:00
Kilian Schüttler
c55aa9d206 RED-9375: fix component mapping endpoint validation 2024-06-27 09:42:21 +02:00
Corina Olariu
6956e49f4a Merge branch 'RED-9347' into 'master'
RED-9347 - Make /rss controller unavailable

Closes RED-9347

See merge request redactmanager/persistence-service!560
2024-06-27 07:18:00 +02:00
yhampe
767f77f261 RED-3813: image recategorization
removed not used changes
2024-06-26 15:28:47 +02:00
yhampe
80be82e361 RED-3813: image recategorization
removed not used changes
2024-06-26 15:27:07 +02:00
yhampe
fed0caa8d2 Merge remote-tracking branch 'origin/RED-3813' into RED-3813
# Conflicts:
#	persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/queue/LayoutParsingFinishedMessageReceiver.java
2024-06-26 15:24:25 +02:00
yhampe
c2c6438187 RED-3813: image recategorization
merged with main
2024-06-26 15:23:36 +02:00
yhampe
7e65396ffe RED-3813: image recategorization
added saving images after layout parsing finishes

added endpoint for querying similiar images
2024-06-26 15:22:36 +02:00
yhampe
18a8f5215f RED-3813: image recategorization
added first simple clustering service
2024-06-26 15:21:32 +02:00
yhampe
0cdd10e985 RED-3813: image recategorization
fixed merge with main
2024-06-26 15:19:10 +02:00
yhampe
87cd0b06e1 Merge remote-tracking branch 'origin/RED-3813' into RED-3813
# Conflicts:
#	persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/queue/LayoutParsingFinishedMessageReceiver.java
2024-06-26 15:06:46 +02:00
yhampe
23b437e60a RED-3813: image recategorization
added saving images after layout parsing finishes

added endpoint for querying similiar images
2024-06-26 15:05:42 +02:00
yhampe
9c48547c36 RED-3813: image recategorization
added first simple clustering service
2024-06-26 15:04:14 +02:00
yhampe
a4426e87ce RED-3813: image recategorization
added saving images after layout parsing finishes

added endpoint for querying similiar images
2024-06-26 15:03:13 +02:00
corinaolariu
94ecfbd1b4 RED-9347 - Make /rss controller unavailable
- delete unused rss files
2024-06-26 11:40:32 +03:00
Ali Oezyetimoglu
71c81631f9 Merge branch 'RED-9392-fp' into 'master'
RED-9392: added JUnit test

Closes RED-9392

See merge request redactmanager/persistence-service!559
2024-06-21 17:33:07 +02:00
Ali Oezyetimoglu
a05bbc9c7f RED-9392: added JUnit test 2024-06-21 17:22:04 +02:00
Ali Oezyetimoglu
649b8b6a46 Merge branch 'RED-9392-fp' into 'master'
RED-9392: added new entity DownloadStatusReportEntity and corresponding...

Closes RED-9392

See merge request redactmanager/persistence-service!558
2024-06-21 16:33:49 +02:00
Ali Oezyetimoglu
4af3ada659 RED-9392: added new entity DownloadStatusReportEntity and corresponding repository DownloadStatusReportRepository 2024-06-21 16:03:07 +02:00
Maverick Studer
aafe9fa31a Merge branch 'migration-version-fix' into 'master'
fixed duplicate version numbers in migrations

See merge request redactmanager/persistence-service!556
2024-06-21 12:12:40 +02:00
maverickstuder
808ce8cbaf fixed duplicate version numbers in migrations 2024-06-21 12:00:39 +02:00
Kilian Schüttler
436368b5e6 Merge branch 'RED-9255' into 'master'
RED-9255: implement file exchange

Closes RED-9255

See merge request redactmanager/persistence-service!554
2024-06-20 16:49:00 +02:00
Kilian Schuettler
07a0bacf55 RED-9375: fix component mapping endpoint validation 2024-06-20 14:31:05 +02:00
Kilian Schuettler
a1b224adbd RED-9375: fix component mapping endpoint validation 2024-06-20 13:53:51 +02:00
Kilian Schuettler
23929d2772 RED-9375: fix component mapping endpoint validation 2024-06-20 13:43:20 +02:00
Kilian Schuettler
1a585e9fe3 RED-9375: fix component mapping endpoint validation 2024-06-20 13:42:29 +02:00
Kilian Schuettler
5dd27f682b RED-9375: fix component mapping endpoint validation 2024-06-20 11:57:18 +02:00
Kilian Schuettler
e382e4e833 RED-9255: implement file exchange
* add simplified text file to layoutparsing file export
* add validation for fileId/dossierId combinations
2024-06-20 11:55:48 +02:00
Kilian Schuettler
4b61a19a95 RED-9255: implement file exchange
* add simplified text file to layoutparsing file export
* add validation for fileId/dossierId combinations
2024-06-20 11:46:13 +02:00
Kilian Schuettler
fbf9320f6c RED-9255: implement file exchange
* add simplified text file to layoutparsing file export
* add validation for fileId/dossierId combinations
2024-06-20 11:42:51 +02:00
Kilian Schuettler
46f9523b2c RED-9255: implement file exchange
* add simplified text file to layoutparsing file export
* add validation for fileId/dossierId combinations
2024-06-20 11:40:52 +02:00
Kilian Schuettler
cdece93037 RED-9255: implement file exchange
* add simplified text file to layoutparsing file export
* add validation for fileId/dossierId combinations
2024-06-20 11:22:39 +02:00
Kilian Schuettler
999ecafdf8 RED-9255: implement file exchange
* add simplified text file to layoutparsing file export
* add validation for fileId/dossierId combinations
2024-06-20 11:21:34 +02:00
Kilian Schuettler
f77c08aac8 RED-9255: implement file exchange
* add simplified text file to layoutparsing file export
* add validation for fileId/dossierId combinations
2024-06-20 11:13:28 +02:00
Kilian Schuettler
27d2208a72 RED-9255: give reanalysis endpoint a request model 2024-06-20 11:13:28 +02:00
Ali Oezyetimoglu
856f09583a Merge branch 'RED-8339' into 'master'
RED-8339: added NonNull constraints

Closes RED-8339

See merge request redactmanager/persistence-service!553
2024-06-19 17:49:02 +02:00
Maverick Studer
596a8dba31 Merge branch 'RED-9374' into 'master'
RED-9374: Ner Entities are at wrong locations

Closes RED-9374

See merge request redactmanager/persistence-service!552
2024-06-19 16:41:30 +02:00
maverickstuder
5e5b8e54ca RED-9374: Ner Entities are at wrong locations
* upgraded search-service version
2024-06-19 16:26:37 +02:00
Ali Oezyetimoglu
d6b95dad3e RED-8339: added NonNull constraints 2024-06-19 15:27:45 +02:00
Andrei Isvoran
2b574b502e Merge branch 'RED-9387-comments' into 'master'
RED-9387 - Update hasComments flag based on entity log entries number of comments

Closes RED-9387

See merge request redactmanager/persistence-service!551
2024-06-19 12:17:53 +02:00
Andrei Isvoran
e5b23b2df7 RED-9387 - Update hasComments flag based on entity log entries number of comments 2024-06-19 12:30:43 +03:00
Ali Oezyetimoglu
dc96ff8439 Merge branch 'RED-9369' into 'master'
RED-9369: changed status code from 200 to 204 for endpoints with no response content

Closes RED-9369

See merge request redactmanager/persistence-service!549
2024-06-18 13:06:58 +02:00
Ali Oezyetimoglu
111f79a1d8 RED-9369: changed status code from 200 to 204 for endpoints with no response content 2024-06-18 11:00:12 +02:00
Corina Olariu
08c8d65f3c Merge branch 'RED-9351' into 'master'
RED-9351 - Redaction skipped after changing only type + paragraph

Closes RED-9351

See merge request redactmanager/persistence-service!546
2024-06-18 07:36:30 +02:00
Ali Oezyetimoglu
81c21b1cbe Merge branch 'RED-9360' into 'master'
RED-9360: changed "redaction" to "annotation" for manual redactions to make it DM-conform

Closes RED-9360

See merge request redactmanager/persistence-service!548
2024-06-17 17:00:30 +02:00
Ali Oezyetimoglu
ce6ec13b4c RED-9360: changed "redaction" to "annotation" for manual redactions to make it DM-conform 2024-06-17 16:49:08 +02:00
Dominique Eifländer
0d5d6a1c7a Merge branch 'RED-9297' into 'master'
RED-9297: When removing annotation with dictionary and manual always add a local remove as well

Closes RED-9297

See merge request redactmanager/persistence-service!547
2024-06-17 15:10:56 +02:00
Dominique Eifländer
c28dcbfe40 RED-9297: When removing annotation with dictionary and manual always add a local remove as well 2024-06-17 14:58:51 +02:00
Corina Olariu
11d050a92e RED-9351 - Redaction skipped after changing only type + paragraph
- add junit test

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-06-17 14:37:53 +03:00
Corina Olariu
0f782c802d RED-9351 - Redaction skipped after changing only type + paragraph
- only on this endpoint: /api/dossier-templates/{dossierTemplateId}/dossiers/{dossierId}/files/{fileId}/components check if the user has a valid role (RED_USER or RED_MANAGER)

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-06-17 10:29:34 +03:00
Kilian Schüttler
e00e5043c2 Merge branch 'RED-9255' into 'master'
RED-9255: implement full file exchange between stacks

Closes RED-9255

See merge request redactmanager/persistence-service!544
2024-06-14 09:48:24 +02:00
Kilian Schüttler
e94ff93c7d RED-9255: implement full file exchange between stacks 2024-06-14 09:48:24 +02:00
Dominique Eifländer
a58bfa2f54 Merge branch 'RED-9334-obj-master' into 'master'
RED-9334: Fixed wrong objectmapper

Closes RED-9334

See merge request redactmanager/persistence-service!543
2024-06-13 13:49:37 +02:00
Dominique Eifländer
00052b7a43 RED-9334: Fixed wrong objectmapper 2024-06-13 13:36:41 +02:00
Ali Oezyetimoglu
e8202a5dae Merge branch 'RED-8339' into 'master'
RED-8339: prevent component name being null

Closes RED-8339

See merge request redactmanager/persistence-service!539
2024-06-13 11:14:01 +02:00
Ali Oezyetimoglu
0dd9d0ed07 RED-8339: prevent component name being null 2024-06-12 17:11:46 +02:00
Dominique Eifländer
057b6d805a Merge branch 'RED-9334' into 'master'
RED-9334: Enabled to stream downloads > 2gb from blob storage

Closes RED-9334

See merge request redactmanager/persistence-service!538
2024-06-12 15:46:42 +02:00
Dominique Eifländer
c90606f4c0 RED-9334: Fixed missing dependencies 2024-06-12 15:26:27 +02:00
Dominique Eifländer
63253581a3 RED-9334: Enabled to stream downloads > 2gb from blob storage 2024-06-12 11:22:45 +02:00
Andrei Isvoran
a586ec0eeb Merge branch 'RED-9290' into 'master'
RED-9290 - Component management for dossier template clone/import/export

Closes RED-9290

See merge request redactmanager/persistence-service!529
2024-06-12 08:12:23 +02:00
Andrei Isvoran
9e1189726d RED-9290 - Component management for dossier template clone/import/export 2024-06-12 08:12:23 +02:00
Dominique Eifländer
65ca13089b Merge branch 'RED-9250-fp' into 'master'
RED-9250: Do not run any scheduled jobs in parallel

Closes RED-9250

See merge request redactmanager/persistence-service!537
2024-06-11 10:58:18 +02:00
Dominique Eifländer
4451c07f02 RED-9250: Do not run any scheduled jobs in parallel 2024-06-11 10:45:34 +02:00
Christoph Schabert
d0faf8a33d Merge branch 'RED-9114-apispec' into 'master'
RED-9225 / RED-9114: Added resource owner password credentials grant (password flow).

See merge request redactmanager/persistence-service!535
2024-06-11 07:48:40 +02:00
Kresnadi Budisantoso
78e82020ab RED-9225: Added resource owner password credentials grant (password flow). 2024-06-07 13:19:22 +02:00
Dominique Eifländer
5baaa71933 Merge branch 'RED-9225-findings-3-fp' into 'master'
RED-9225: Fixed not validated dossier template id in create download dossier

Closes RED-9225

See merge request redactmanager/persistence-service!533
2024-06-07 11:06:52 +02:00
Dominique Eifländer
16c3388ffe RED-9225: Fixed not validated dossier template id in create download dossier 2024-06-07 10:51:05 +02:00
Kilian Schüttler
2ef9800eae Merge branch 'RED-9145' into 'master'
RED-9145: remove EXPERIMENTAL role which no user has

Closes RED-9145

See merge request redactmanager/persistence-service!531
2024-06-07 10:44:53 +02:00
Kilian Schuettler
4843f79466 RED-9145: remove EXPERIMENTAL role which no user has 2024-06-07 10:32:35 +02:00
Ali Oezyetimoglu
a6c6a4111c Merge branch 'RED-8339-7' into 'master'
RED-8339: fixed misbehaving when adding an override in old endpoint

Closes RED-8339

See merge request redactmanager/persistence-service!530
2024-06-07 09:59:50 +02:00
Ali Oezyetimoglu
abccf8ca82 RED-8339: fixed misbehaving when adding an override in old endpoint 2024-06-07 08:36:10 +02:00
Dominique Eifländer
26d03e6b6f Merge branch 'RED-9225-findings2-fp' into 'master'
Resolve RED-9225 "Findings2 fp"

Closes RED-9225

See merge request redactmanager/persistence-service!528
2024-06-06 15:21:00 +02:00
Dominique Eifländer
c67fa5926b RED-9225: Added endpoint to get report templates in new customer api, fixed some bugs 2024-06-06 15:05:49 +02:00
Dominique Eifländer
0f203d39a4 RED-9270: Fixed download of report template, RED-9225: Minor fixes for new customer api 2024-06-06 14:55:42 +02:00
Dominique Eifländer
dae360a7ac Merge branch 'RED-9114-apispec' into 'master'
RED-9225 / RED-9114 RedactManager API Specs for BASF - Added possibility to...

Closes RED-9114

See merge request redactmanager/persistence-service!526
2024-06-06 14:53:26 +02:00
Kresnadi Budisantoso
64f519f100 RED-9225 / RED-9114 RedactManager API Specs for BASF - Added possibility to retrieve the available report templates
Additionally:
* Added `approverIds` to `DossierRequest` schema
* Harmonized some descriptions between RedactManager and DocuMine specs
2024-06-06 12:39:10 +02:00
Ali Oezyetimoglu
e377df12a0 Merge branch 'RED-8339-6' into 'master'
RED-8339: rolled back signature changes in old component log endpoints

Closes RED-8339

See merge request redactmanager/persistence-service!524
2024-06-06 12:30:57 +02:00
Ali Oezyetimoglu
62c81c3654 RED-8339: rolled back signature changes in old component log endpoints 2024-06-06 12:02:58 +02:00
Maverick Studer
a4683a4eed Merge branch 'RED-9254' into 'master'
RED-9254: Support Controller (Part 1)

Closes RED-9254

See merge request redactmanager/persistence-service!523
2024-06-06 10:21:26 +02:00
Maverick Studer
f9090cfa3f RED-9254: Support Controller (Part 1) 2024-06-06 10:21:26 +02:00
Andrei Isvoran
398ec390b7 Merge branch 'RED-9221-mongo' into 'master'
RED-9221 - Add duplicated text ranges for entity log in mongo

Closes RED-9221

See merge request redactmanager/persistence-service!522
2024-06-05 13:24:47 +02:00
Andrei Isvoran
b52ac441e9 RED-9221 - Add duplicated text ranges for entity log in mongo 2024-06-05 14:12:48 +03:00
Dominique Eifländer
b08a666022 Merge branch 'RED-9225-fp' into 'master'
RED-9225: Fixed findings in new customer api

Closes RED-9225

See merge request redactmanager/persistence-service!521
2024-06-05 11:23:42 +02:00
Dominique Eifländer
088a4ac21b RED-9225: Fixed findings in new customer api 2024-06-05 10:50:17 +02:00
Andrei Isvoran
06b3d236c8 Merge branch 'RED-9221' into 'master'
RED-9221 - Add duplicated text ranges to EntityLogEntry

Closes RED-9221

See merge request redactmanager/persistence-service!520
2024-06-05 10:32:38 +02:00
Andrei Isvoran
a18f9d11b8 RED-9221 - Add duplicated text ranges to EntityLogEntry 2024-06-05 10:32:38 +02:00
Andrei Isvoran
fb133a4770 Merge branch 'RED-9142' into 'master'
RED-9142 - Basic component management

Closes RED-9142

See merge request redactmanager/persistence-service!515
2024-06-05 08:13:25 +02:00
Andrei Isvoran
bb9e977e78 RED-9142 - Basic component management 2024-06-05 08:13:25 +02:00
Ali Oezyetimoglu
581206bd16 Merge branch 'RED-8339-5' into 'master'
RED-8339: rolled back signature changes in old component log endpoints

Closes RED-8339

See merge request redactmanager/persistence-service!519
2024-06-04 17:29:20 +02:00
Kilian Schüttler
46123e179d Merge branch 'RED-9145' into 'master'
RED-9145: revert accidental changes

Closes RED-9145

See merge request redactmanager/persistence-service!518
2024-06-04 17:29:17 +02:00
Ali Oezyetimoglu
6a4047effb RED-8339: rolled back signature changes in old component log endpoints 2024-06-04 17:13:58 +02:00
Kilian Schuettler
01747eab4f RED-9145: remove unnecessary logs
* revert accidental remove of retry
2024-06-04 17:13:56 +02:00
Christoph Schabert
4b6cfbb66d Update .gitlab-ci.yml file 2024-06-04 09:49:18 +02:00
Christoph Schabert
e4a5e6efb7 Update .gitlab-ci.yml file 2024-06-04 09:23:44 +02:00
Christoph Schabert
cf124d1326 add custom sonar token 2024-06-04 08:09:27 +02:00
Ali Oezyetimoglu
fe777da0f5 Merge branch 'RED-8339-4' into 'master'
Resolve RED-8339 "4"

Closes RED-8339

See merge request redactmanager/persistence-service!511
2024-06-03 15:38:31 +02:00
Dominique Eifländer
b66e436058 Merge branch 'RED-9114-apispec' into 'master'
RED-9114 - RedactManager API Specs for BASF (Cherry-picked merge-commit...

See merge request redactmanager/persistence-service!505
2024-06-03 14:54:11 +02:00
Kresnadi Budisantoso
1622228613 Fixed missing path parameter reference and removed wrong endpoint tags 2024-06-03 14:41:20 +02:00
Ali Oezyetimoglu
a5866b36c8 RED-8339: some refactoring 2024-06-03 13:13:25 +02:00
Ali Oezyetimoglu
efd777acd8 RED-8339: some refactoring 2024-06-03 13:09:45 +02:00
Maverick Studer
05faad96bc Merge branch 'RED-9254' into 'master'
RED-9254: Support Controller (Part 1)

Closes RED-9254

See merge request redactmanager/persistence-service!512
2024-06-03 12:36:20 +02:00
Maverick Studer
cfda6a8217 RED-9254: Support Controller (Part 1) 2024-06-03 12:36:20 +02:00
Ali Oezyetimoglu
f4bdff1901 Merge branch 'RED-9280' into 'master'
RED-9280: added permission check for updateFlag endpoint

Closes RED-9280

See merge request redactmanager/persistence-service!514
2024-06-03 12:17:50 +02:00
Kresnadi Budisantoso
55922a85e8 RedactManager API Specs for BASF - Improved descriptions 2024-06-03 12:02:56 +02:00
Kresnadi Budisantoso
f3758fb003 RedactManager API Specs for BASF - Fixed missing property, fields and examples
* Added missing userId property definition
* Added dossierCount to DossierStatusDefinition schema
* Added IMAGE value to type enum of DossierAttributeDefinition schema
* Added missing dossierAttributes to examples of the Dossier and DossierList schemas
* Fixed examples of fileAttributes in FileStatus and FileStatusList schemas
2024-06-03 12:02:56 +02:00
Dominique Eifländer
00a1cadb83 RED-9114 - RedactManager API Specs for BASF (Cherry-picked merge-commit 0ecd23304 from release/2.349.x) 2024-06-03 12:02:40 +02:00
Dominique Eifländer
fac9544864 Merge branch 'RED-9225-fp' into 'master'
RED-9225: Implemented new endpoints for api v2

Closes RED-9225

See merge request redactmanager/persistence-service!513
2024-06-03 11:55:36 +02:00
Ali Oezyetimoglu
5d480887a2 RED-9280: added permission check for updateFlag endpoint 2024-06-03 11:53:12 +02:00
Dominique Eifländer
3e205d0b1b RED-9225: Implemented new endpoints for api v2 2024-06-03 10:43:11 +02:00
Ali Oezyetimoglu
adb6332bf5 RED-8339: Fixes 2024-06-03 10:24:10 +02:00
Ali Oezyetimoglu
23cf4cf5e9 RED-8339: Fixes 2024-06-02 11:22:31 +02:00
Ali Oezyetimoglu
2ae40e6403 RED-8339: Updated OpenApi Specs 2024-05-29 18:24:40 +02:00
Ali Oezyetimoglu
1a4d97a14f RED-8339: Updated OpenApi Specs 2024-05-29 18:24:40 +02:00
Ali Oezyetimoglu
d921c3f6a0 RED-8339: Updated OpenApi Specs 2024-05-29 18:24:40 +02:00
Ali Oezyetimoglu
9079ae18af RED-8339: Component Overrides in DocuMine 2024-05-29 18:24:29 +02:00
Kilian Schüttler
9e70d9e199 Merge branch 'RED-9145' into 'master'
RED-9145: add component mappings

Closes RED-9145

See merge request redactmanager/persistence-service!485
2024-05-29 12:49:21 +02:00
Kilian Schüttler
6412f58d58 RED-9145: add component mappings 2024-05-29 12:49:21 +02:00
Maverick Studer
c2d02b3faa Merge branch 'RED-6821-9210' into 'master'
RED-6821: Dossier templates only soft-deleted, cannot create new template with same name && RED-9210: RED_USER and RED_MANAGER can bulk-delete dossier-templates - should return 403

Closes RED-6821

See merge request redactmanager/persistence-service!509
2024-05-29 11:58:53 +02:00
Maverick Studer
aabfd5d8b5 RED-6821: Dossier templates only soft-deleted, cannot create new template with same name && RED-9210: RED_USER and RED_MANAGER can bulk-delete dossier-templates - should return 403 2024-05-29 11:58:53 +02:00
Andrei Isvoran
1387065784 Merge branch 'RED-9164' into 'master'
RED-9164 - Change back OCR queues

Closes RED-9164

See merge request redactmanager/persistence-service!506
2024-05-28 13:18:17 +02:00
Andrei Isvoran
afa61062e4 RED-9164 - Change back OCR queues 2024-05-28 11:51:05 +03:00
Andrei Isvoran
efdf6a8868 Merge branch 'RED-9164' into 'master'
RED-9164 - Change OCR queues to match with older OCR version

Closes RED-9164

See merge request redactmanager/persistence-service!504
2024-05-27 11:27:08 +02:00
Andrei Isvoran
a71ee404ed RED-9164 - Change OCR queues to match with older OCR version 2024-05-27 12:03:35 +03:00
Maverick Studer
8485a6b48a Merge branch 'RED-8848' into 'master'
RED-8848: When cloning a dossier template some flags are not set correctly

Closes RED-8848

See merge request redactmanager/persistence-service!500
2024-05-24 13:56:47 +02:00
Maverick Studer
f0160df8d1 RED-8848: When cloning a dossier template some flags are not set correctly 2024-05-24 13:56:46 +02:00
Andrei Isvoran
f962037aaa Merge branch 'RED-9205' into 'master'
RED-9205 - Fix download job failing

Closes RED-9205

See merge request redactmanager/persistence-service!498
2024-05-24 13:43:42 +02:00
Andrei Isvoran
2a4c8c2b19 RED-9205 - Fix download job failing 2024-05-24 13:43:42 +02:00
Maverick Studer
d24ab46616 Merge branch 'RED-5870-rollback' into 'master'
Revert "RED-5870: Enable force redaction of hints"

Closes RED-5870

See merge request redactmanager/persistence-service!502
2024-05-24 13:30:19 +02:00
Maverick Studer
e723550b53 Revert "RED-5870: Enable force redaction of hints" 2024-05-24 13:30:19 +02:00
Dominique Eifländer
8f4f95b166 Merge branch 'RED-9147' into 'master'
RED-9147: Deny websocket subscription between different tenants

Closes RED-9147

See merge request redactmanager/persistence-service!496
2024-05-23 14:20:27 +02:00
Dominique Eifländer
e9ba3c8da1 RED-9147: Deny websocket subscription between different tenants 2024-05-23 13:49:51 +02:00
Maverick Studer
84d033ff54 Merge branch 'RED-5870' into 'master'
RED-5870: Enable force redaction of hints

Closes RED-5870

See merge request redactmanager/persistence-service!495
2024-05-23 10:45:58 +02:00
maverickstuder
1f6339f71d RED-5870: Enable force redaction of hints
* mergeForceRedaction no longer sets hints to skipped, now all entries are set to applied instead
2024-05-23 09:24:00 +02:00
Dominique Eifländer
64a7d24639 Merge branch 'RED-9147' into 'master'
RED-9147: Provide events via websocket

Closes RED-9147

See merge request redactmanager/persistence-service!493
2024-05-22 15:21:59 +02:00
Dominique Eifländer
2c604b1aa3 RED-9147: Removed unused class 2024-05-22 15:07:07 +02:00
Dominique Eifländer
7d875ee7eb RED-9147: Provide events via websocket 2024-05-22 14:48:54 +02:00
Maverick Studer
edb333aa29 Merge branch 'RED-8848' into 'master'
RED-8848: When cloning a dossier template some flags are not set correctly

Closes RED-8848

See merge request redactmanager/persistence-service!489
2024-05-22 11:03:51 +02:00
maverickstuder
38d3832732 RED-8848: When cloning a dossier template some flags are not set correctly
* setting the missing flags from present dossier template when cloning
2024-05-22 10:52:33 +02:00
Andrei Isvoran
0408e3986e Merge branch 'RED-9148' into 'master'
RED-9148 - Add paragraph page index to entity log

Closes RED-9148

See merge request redactmanager/persistence-service!488
2024-05-21 13:40:41 +02:00
Andrei Isvoran
1a0cea1332 RED-9148 - Add paragraph page index to entity log 2024-05-21 14:30:00 +03:00
Andrei Isvoran
05153504d3 Merge branch 'RED-9148' into 'master'
RED-9148 - Add paragraph page index to entity log

Closes RED-9148

See merge request redactmanager/persistence-service!487
2024-05-21 08:44:34 +02:00
Andrei Isvoran
5da98461f0 RED-9148 - Add paragraph page index to entity log 2024-05-20 17:02:58 +03:00
Andrei Isvoran
f5c2a115f4 Merge branch 'RED-9148' into 'master'
RED-9148 - Add paragraph page index to entity log

Closes RED-9148

See merge request redactmanager/persistence-service!486
2024-05-20 14:17:53 +02:00
Andrei Isvoran
08c6f11e88 RED-9148 - Add paragraph page index to entity log 2024-05-20 15:07:28 +03:00
Andrei Isvoran
03dc5f2d50 Merge branch 'RED-6853' into 'master'
RED-6853 - Update dictionary entries

Closes RED-6853

See merge request redactmanager/persistence-service!484
2024-05-17 11:08:47 +02:00
Andrei Isvoran
32411a668f RED-6853 - Update dictionary entries 2024-05-16 16:03:30 +03:00
yhampe
20ca0cd798 Merge remote-tracking branch 'origin/RED-3813' into RED-3813 2024-05-13 15:08:21 +02:00
yhampe
4f1c926a17 RED-3813: image recategorization
added first simple clustering service
2024-05-13 15:07:52 +02:00
yhampe
6110dd1497 RED-3813: image recategorization
added first simple clustering service
2024-05-13 15:06:25 +02:00
Andrei Isvoran
63dbc073c7 Merge branch 'RED-9146' into 'master'
RED-9146 - Change response for manual redaction endpoints to an object wrapping the list

Closes RED-9146

See merge request redactmanager/persistence-service!481
2024-05-13 12:24:56 +02:00
Andrei Isvoran
926e8b6b6b RED-9146 - Change response for manual redaction endpoints to an object wrapping the list 2024-05-13 12:42:42 +03:00
Andrei Isvoran
31165bd8f6 Merge branch 'RED-6853' into 'master'
RED-6853 - Add update entries endpoint for dictionary entries

Closes RED-6853

See merge request redactmanager/persistence-service!480
2024-05-09 13:22:50 +02:00
Andrei Isvoran
6b2fa6b9b6 RED-6853 - Add update entries endpoint for dictionary entries 2024-05-09 12:21:07 +03:00
Corina Olariu
141bf035ad Merge branch 'RED-9094' into 'master'
RED-9094 - Redaction skipped after changing only type + paragraph

Closes RED-9094

See merge request redactmanager/persistence-service!478
2024-05-07 15:25:27 +02:00
Corina Olariu
8c3d2266ba RED-9094 - Redaction skipped after changing only type + paragraph
- changed from Skipped state to Applied since it is a local recategorization (based on hint)
- add unit test

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-05-07 16:12:55 +03:00
Andrei Isvoran
c1dda3cd26 Merge branch 'RED-8998-fix' into 'master'
RED-8998 - Return merged entities or pending entries for manual changes.

Closes RED-8998

See merge request redactmanager/persistence-service!473
2024-05-07 10:11:35 +02:00
Andrei Isvoran
a300f61609 RED-8998 - Return merged entities or pending entries for manual changes. 2024-05-07 10:11:35 +02:00
Maverick Studer
4935d81b0c Merge branch 'RED-8908' into 'master'
RED-8908: DM: Opening a file in error state triggers reanalysis

Closes RED-8908

See merge request redactmanager/persistence-service!476
2024-05-07 09:58:30 +02:00
maverickstuder
a655100ec4 RED-8908: DM: Opening a file in error state triggers reanalysis
* disable reanalysis on error state
2024-05-07 09:22:12 +02:00
Maverick Studer
1e64903352 Merge branch 'RED-9084' into 'master'
RED-9084: Paragraph/Location value missing for manual redaction on re-uploaded preview file

Closes RED-9084

See merge request redactmanager/persistence-service!475
2024-05-06 15:33:15 +02:00
maverickstuder
16a31bfb65 RED-9084: Paragraph/Location value missing for manual redaction on re-uploaded preview file
* renamed to manualOverwriteSection
2024-05-06 09:58:51 +02:00
maverickstuder
24e3771b08 RED-9084: Paragraph/Location value missing for manual redaction on re-uploaded preview file
* added section to ImportedRedaction
2024-05-03 15:30:46 +02:00
Maverick Studer
8d2091edc4 Merge branch 'RED-9104' into 'master'
RED-9104: Rectangle redaction cannot be removed

Closes RED-9104

See merge request redactmanager/persistence-service!474
2024-05-03 12:50:41 +02:00
maverickstuder
7998b81bb6 RED-9104: Rectangle redaction cannot be removed
* fixed and improved mongodb queries empty array comparison
2024-05-03 12:36:50 +02:00
Andrei Isvoran
4763e6db89 Merge branch 'RED-8998' into 'master'
RED-8998 - Get specific entry from entitylog and return the merged entry on manual changes endpoints

Closes RED-8998

See merge request redactmanager/persistence-service!468
2024-04-29 10:37:07 +02:00
Andrei Isvoran
6280e342eb RED-8998 - Get specific entry from entitylog and return the merged entry on manual changes endpoints 2024-04-29 10:37:07 +02:00
Corina Olariu
83d6ef71ef Merge branch 'RED-9063' into 'master'
RED-9063 - Error 409 when requesting deleted types

Closes RED-9063

See merge request redactmanager/persistence-service!470
2024-04-26 12:37:02 +02:00
Kilian Schüttler
9ac1545fe8 Merge branch 'RED-9042' into 'master'
RED-9042: forward port for merge of legalbasis and recategorize

Closes RED-9042

See merge request redactmanager/persistence-service!464
2024-04-25 18:27:37 +02:00
Ali Oezyetimoglu
59c1401053 RED-9042: forward port for merge of legalbasis and recategorize 2024-04-25 18:27:37 +02:00
Corina Olariu
033493aaee RED-9063 - Error 409 when requesting deleted types
- add a check before creating dossier level types on the fly if the dossier template type is not deleted
- add unit test

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-04-25 15:43:18 +03:00
Dominique Eifländer
7acc0889b7 Merge branch 'RED-8826' into 'master'
RED-8826: Added experimetal flag to entity types

Closes RED-8826

See merge request redactmanager/persistence-service!462
2024-04-23 14:23:25 +02:00
Dominique Eifländer
01ca31fbbf RED-8826: Fixed old codestyle 2024-04-23 13:34:06 +02:00
Dominique Eifländer
4a6777be71 RED-8826: Added experimetal flag to entity types 2024-04-23 13:34:04 +02:00
Andrei Isvoran
7c37c16be7 Merge branch 'RED-8823' into 'master'
RED-8823 - Add soft deleted time to type response

Closes RED-8823

See merge request redactmanager/persistence-service!463
2024-04-23 13:17:31 +02:00
Andrei Isvoran
d5494660bc RED-8823 - fix pmd 2024-04-23 14:01:26 +03:00
Andrei Isvoran
2aff32f33e RED-8894 - Add soft deleted time to type response 2024-04-23 13:45:15 +03:00
Andrei Isvoran
3a93c1f715 Merge branch 'RED-8894' into 'master'
RED-8894 - Add dictionary difference endpoint

Closes RED-8894

See merge request redactmanager/persistence-service!460
2024-04-22 14:57:15 +02:00
Andrei Isvoran
1adf46179e RED-8894 - Add dictionary difference endpoint 2024-04-22 14:57:15 +02:00
Ali Oezyetimoglu
bf10c3baf0 Merge branch 'RED-8480-3' into 'master'
Resolve RED-8480 "3"

Closes RED-8480

See merge request redactmanager/persistence-service!459
2024-04-18 15:41:06 +02:00
Ali Oezyetimoglu
1d47744b30 RED-8480: small fixes and tests 2024-04-17 16:14:35 +02:00
Ali Oezyetimoglu
dcb5c0e03e RED-8480: differenciate between recategorize and legal basis change 2024-04-17 16:14:35 +02:00
Ali Oezyetimoglu
b11c95dd39 RED-8480: differenciate between recategorize and legal basis change 2024-04-17 16:14:35 +02:00
Timo Bejan
6b82815b7a Merge branch 'RED-8966' into 'master'
RED-8966 Improved performance of soft and hard delete files and dossirs, and...

Closes RED-8966

See merge request redactmanager/persistence-service!456
2024-04-17 10:05:21 +02:00
Ali Oezyetimoglu
ef18d68102 Merge branch 'RED-8762' into 'master'
RED-8762: fixed path variable

Closes RED-8762

See merge request redactmanager/persistence-service!458
2024-04-17 09:50:05 +02:00
Ali Oezyetimoglu
7f4a53ba5f RED-8762: fixed path variable 2024-04-17 09:36:57 +02:00
Timo Bejan
5a526484d6 REd-8966 - added missing transactional 2024-04-16 18:35:08 +03:00
Timo Bejan
fac8a1ac85 RED-8966 Improved performance of soft and hard delete files and dossirs, and undelete - split transaction and vastly improved query performance 2024-04-16 18:25:07 +03:00
Andrei Isvoran
11dcb2fb93 Merge branch 'RED-8768' into 'master'
RED-8768 - Validate redaction value does not contain over 4000 characters

Closes RED-8768

See merge request redactmanager/persistence-service!453
2024-04-16 14:06:40 +02:00
Andrei Isvoran
c8244781b4 RED-8768 - Validate redaction value does not contain over 4000 characters 2024-04-16 14:06:40 +02:00
Maverick Studer
a086a71833 Merge branch 'RED-8972' into 'master'
RED-8972: numberOfComments missing in entityLog

Closes RED-8972

See merge request redactmanager/persistence-service!452
2024-04-16 13:37:33 +02:00
maverickstuder
e510f07be9 RED-8972: numberOfComments missing in entityLog
* re-add numberOfComments to EntityLogEntryResponse
2024-04-16 12:52:47 +02:00
Ali Oezyetimoglu
e00ea01803 Merge branch 'RED-8762' into 'master'
RED-8762: do not update addToDictionaryAction for dossier dictionaries when...

Closes RED-8762

See merge request redactmanager/persistence-service!450
2024-04-15 14:03:41 +02:00
Ali Oezyetimoglu
a86be93177 RED-8762: do not update addToDictionaryAction for dossier dictionaries when updating dossier template dictionary ONLY if dossierDictionaryOnly 2024-04-15 13:50:10 +02:00
Ali Oezyetimoglu
f23f66f0ed Merge branch 'RED-8762' into 'master'
RED-8762: do not update addToDictionaryAction for dossier dictionaries when...

Closes RED-8762

See merge request redactmanager/persistence-service!446
2024-04-15 11:27:01 +02:00
Dominique Eifländer
04759967e2 Merge branch 'RED-8956' into 'master'
RED-8956: Fixed endless loop when flag calculation is required and file is deleted

Closes RED-8956

See merge request redactmanager/persistence-service!447
2024-04-15 11:19:05 +02:00
Dominique Eifländer
8756eea0e0 RED-8956: Fixed endless loop when flag calculation is required and file is deleted 2024-04-15 10:54:35 +02:00
Ali Oezyetimoglu
77db059a2e RED-8762: do not update addToDictionaryAction for dossier dictionaries when updating dossier template dictionary 2024-04-15 08:38:16 +02:00
Maverick Studer
b002f7d544 Merge branch 'RED-8702-fix' into 'master'
RED-8702: Explore document databases to store entityLog

Closes RED-8702

See merge request redactmanager/persistence-service!445
2024-04-12 12:40:22 +02:00
Maverick Studer
92cc2895ad RED-8702: Explore document databases to store entityLog 2024-04-12 12:40:22 +02:00
Ali Oezyetimoglu
cfeb370299 Merge branch 'RED-8762' into 'master'
RED-8762: removed unnecessary consumes from new changeFlags endpoint

Closes RED-8762

See merge request redactmanager/persistence-service!444
2024-04-11 16:41:32 +02:00
Ali Oezyetimoglu
6d1d51098b RED-8762: removed unnecessary consumes from new changeFlags endpoint and added dossierId as path variable 2024-04-11 16:24:04 +02:00
Maverick Studer
ef7c470618 Merge branch 'RED-8702-fix' into 'master'
RED-8702: Explore document databases to store entityLog

Closes RED-8702

See merge request redactmanager/persistence-service!428
2024-04-11 15:51:59 +02:00
Maverick Studer
57cb10ba1d RED-8702: Explore document databases to store entityLog 2024-04-11 15:51:58 +02:00
Ali Oezyetimoglu
9a93c0b18b Merge branch 'RED-8762' into 'master'
RED-8762: added endpoint to update flag addToDictionary for given type,...

Closes RED-8762

See merge request redactmanager/persistence-service!441
2024-04-11 15:09:55 +02:00
Ali Oezyetimoglu
623068e1a1 RED-8762: added endpoint to update flag addToDictionary for given type, dossierTemplateId and dossierId 2024-04-11 14:52:30 +02:00
Timo Bejan
f870759747 Merge branch 'RED-8879' into 'master'
removed 3.6.x temporary unique constraints

Closes RED-8879

See merge request redactmanager/persistence-service!439
2024-04-11 10:06:51 +02:00
Andrei Isvoran
07b29d9c50 Merge branch 'RED-8903' into 'master'
RED-8903 - Refactor local add redaction on manual change

Closes RED-8903

See merge request redactmanager/persistence-service!435
2024-04-10 11:24:24 +02:00
Timo Bejan
c9cda89e17 removed 3.6.x temporary unique constraints 2024-04-10 10:06:01 +03:00
Andrei Isvoran
54bfdfafe2 build 2024-04-09 16:54:46 +03:00
Andrei Isvoran
c31a29cea2 RED-8903 - Refactor local add redaction on manual change 2024-04-09 14:46:32 +03:00
Andrei Isvoran
6770d03ae1 Merge branch 'RED-8651' into 'master'
RED-8651 - Evict report templates cache on template upload

Closes RED-8651

See merge request redactmanager/persistence-service!434
2024-04-09 09:37:58 +02:00
Andrei Isvoran
1c60bc6586 RED-8651 - Evict report templates cache on template upload 2024-04-09 09:37:58 +02:00
Dominique Eifländer
ddcff217e5 Merge branch 'RED-7384' into 'master'
RED-7384: migration fixes

Closes RED-7384

See merge request redactmanager/persistence-service!432
2024-04-08 10:32:32 +02:00
Kilian Schuettler
c60c79ef06 RED-7384: migration fixes
* index files from dossier trash aswell
2024-04-08 10:15:58 +02:00
Corina Olariu
a37ac83339 Merge branch 'RED-8727-updateRank' into 'master'
RED-8727 - Add rank de-duplication to migration - fp

Closes RED-8727

See merge request redactmanager/persistence-service!431
2024-04-05 14:38:32 +02:00
Kilian Schüttler
d7e5e16351 Merge branch 'RED-7384-fp' into 'master'
RED-7384: migration fixes

Closes RED-7384

See merge request redactmanager/persistence-service!430
2024-04-05 13:50:14 +02:00
Kilian Schuettler
5bfeec1b21 RED-7384: migration fixes
* fix test
2024-04-05 13:27:36 +02:00
Kilian Schuettler
7df02d588a RED-7384: migration fixes
* index files from trash aswell
* merge manual local entries with existing ones during unprocessed changes merge
* don't add ManualRedactionEntries on local changes for Images
2024-04-05 13:06:00 +02:00
Corina Olariu
29a274fdb0 RED-8727 - Add rank de-duplication to migration - fp
- add an update rank for type query because the updateType will not cover the update of ranks of system managed types (which is not possible for the user)
- update the tests

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-04-05 13:44:15 +03:00
Kilian Schüttler
6525e0c2bc Merge branch 'RED-7384-fp' into 'master'
RED-7384: migration fixes forward port

Closes RED-7384

See merge request redactmanager/persistence-service!420
2024-04-04 15:44:38 +02:00
Kilian Schuettler
88d63b46b9 RED-7384: migration fixes
* finalize migration again for "persistence-service-ready": true details
2024-04-04 14:46:39 +02:00
Maverick Studer
9c8e584e5a Merge branch 'RED-8702-fix' into 'master'
RED-8702: Explore document databases to store entityLog

Closes RED-8702

See merge request redactmanager/persistence-service!423
2024-04-03 19:09:30 +02:00
maverickstuder
147247cfe6 RED-8702: Explore document databases to store entityLog
* rename mongo queue because of naming mismatch
2024-04-03 18:52:35 +02:00
maverickstuder
71f0756a3a RED-8702: Explore document databases to store entityLog
* rename mongo queue because of naming mismatch
2024-04-03 18:25:58 +02:00
Kilian Schuettler
910948bd2d RED-7384: migration fixes
* finalize migration again for "persistence-service-ready": true details
2024-04-03 17:35:38 +02:00
Kilian Schuettler
d4baa5d8b7 RED-7384: migration fixes
* finalize migration again for "persistence-service-ready": true details
2024-04-03 17:23:57 +02:00
Kilian Schuettler
1fc74f2a0c RED-7384: migration fixes forward port
* treat pending dicts in approved files for resize and remove in redaction-service
* disable automatic analysis job using migration status table and liquibase migration
* migrate imported redaction file
* improve logging
2024-04-03 17:05:06 +02:00
Yannik Hampe
06e0bba76c Merge branch 'RED-8706' into 'master'
RED-8706: includedeleted not working

Closes RED-8706

See merge request redactmanager/persistence-service!418
2024-04-03 13:45:36 +02:00
yhampe
72b148376e RED-8706: includedeleted not working
added method call with includedeleted flag
2024-04-03 13:34:42 +02:00
Maverick Studer
873daa2736 Merge branch 'RED-8702-backup2' into 'master'
RED-8702: Explore document databases to store entityLog

Closes RED-8702

See merge request redactmanager/persistence-service!417
2024-04-03 13:33:27 +02:00
Maverick Studer
8e8aa645d0 RED-8702: Explore document databases to store entityLog 2024-04-03 13:33:27 +02:00
Ali Oezyetimoglu
f0c6b868e8 Merge branch 'RED-8480-2' into 'master'
RED-8480: don't merge recategorizations for images, create pending entry instead

Closes RED-8480

See merge request redactmanager/persistence-service!411
2024-04-02 11:26:48 +02:00
Ali Oezyetimoglu
c9af40ddb6 RED-8480: fixed method call 2024-04-02 09:17:38 +02:00
Kilian Schuettler
729923fdea RED-8480: don't merge recategorizations for images, create pending entry instead
* TODO: remove filter in report-service and pdftron-redaction-service
2024-04-02 09:17:38 +02:00
Kilian Schuettler
efc2b200ad RED-8480: don't merge recategorizations for images, create pending entry instead
* TODO: remove filter in report-service and pdftron-redaction-service
2024-04-02 09:17:38 +02:00
Kilian Schuettler
75676c33b1 RED-8480: don't merge recategorizations for images, create pending entry instead
* TODO: remove filter in report-service and pdftron-redaction-service
2024-04-02 09:17:37 +02:00
Kilian Schuettler
5f1731444e RED-8480: don't merge recategorizations for images, create pending entry instead
* TODO: remove filter in report-service and pdftron-redaction-service
2024-04-02 09:17:37 +02:00
Andrei Isvoran
9ddcd56a67 Merge branch 'RED-8776' into 'master'
RED-8776 - Add local redaction when we do a manual change on a non-manual redaction

Closes RED-8776

See merge request redactmanager/persistence-service!404
2024-04-02 08:38:08 +02:00
Andrei Isvoran
3ab607ccbf RED-8776 - Add local redaction when we do a manual change on a non-manual redaction 2024-04-02 08:38:08 +02:00
Ali Oezyetimoglu
d46ae2834a Merge branch 'RED-8480-C' into 'master'
RED-8480: added property value for recategorizations and fixed bugs not...

Closes RED-8480

See merge request redactmanager/persistence-service!406
2024-03-27 09:55:58 +01:00
Ali Oezyetimoglu
6601ee0188 RED-8480: added property value for recategorizations and fixed bugs not updating legal basis and section 2024-03-26 17:42:15 +01:00
Kilian Schüttler
45a0ca1800 Merge branch 'RED-8610' into 'master'
RED-8610: Dictionary remove on dossier level should be displayed as skipped

Closes RED-8610

See merge request redactmanager/persistence-service!403
2024-03-21 16:38:49 +01:00
Kilian Schuettler
8c4b177ef2 RED-8610: Dictionary remove on dossier level should be displayed as skipped
* add DOSSIER_DICTIONARY engine
2024-03-21 16:26:48 +01:00
Kilian Schüttler
dec81549e0 Merge branch 'reorder-log' into 'master'
reorder-log: re-order the pending entries, such that they appear directly...

See merge request redactmanager/persistence-service!399
2024-03-21 08:44:15 +01:00
Kilian Schüttler
ba8cf68779 reorder-log: re-order the pending entries, such that they appear directly... 2024-03-21 08:44:15 +01:00
Ali Oezyetimoglu
7490402bb7 Merge branch 'RED-8480-B' into 'master'
RED-8480: integrated legal basis  endpoint in recategorize endpoint

Closes RED-8480

See merge request redactmanager/persistence-service!396
2024-03-20 18:24:09 +01:00
Ali Oezyetimoglu
a5e3c0c98a RED-8820: removed property "value" from recategorize 2024-03-20 12:54:23 +01:00
Ali Oezyetimoglu
ed052fa741 RED-8480: adjusted propertyChanges in manual changes 2024-03-19 11:42:57 +01:00
Ali Oezyetimoglu
8a4c754250 RED-8480: adjusted propertyChanges in manual changes 2024-03-19 09:20:21 +01:00
Corina Olariu
da6856f31d Merge branch 'RED-8727' into 'master'
RED-8727 - Add rank de-duplication to migration

Closes RED-8727

See merge request redactmanager/persistence-service!398
2024-03-14 16:13:28 +01:00
Corina Olariu
b2cf55b5cc RED-8727 - Add rank de-duplication to migration
- add RankDeDuplicationService and RankDeduplicationMigration16 to update the found duplicate ranks
- junit tests added

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-03-14 12:20:17 +02:00
Ali Oezyetimoglu
3fde378ade RED-8480: integrated legal basis endpoint in recategorize endpoint 2024-03-14 09:58:21 +01:00
Timo Bejan
8f4a03dd08 Merge branch 'implicitflow' into 'master'
added implicit flow swagger

See merge request redactmanager/persistence-service!394
2024-03-13 16:25:56 +01:00
Dominique Eifländer
857f2383d2 Merge branch 'RED-7968' into 'master'
RED-7968: Convert unprocessed manual addToDictionaries to local changes

Closes RED-7968

See merge request redactmanager/persistence-service!395
2024-03-13 12:33:51 +01:00
Dominique Eifländer
f3ef80df06 RED-7968: Convert unprocessed manual addToDictionaries to local changes 2024-03-13 12:22:10 +01:00
Timo Bejan
06360c5cbd added implicit flow swagger 2024-03-12 16:32:59 +02:00
Dominique Eifländer
9110e68266 Merge branch 'RED-7382-4.1' into 'master'
RED-7382: Delete section grid for migrated files

Closes RED-7382

See merge request redactmanager/persistence-service!393
2024-03-12 13:29:11 +01:00
Dominique Eifländer
36e8a2bfdb RED-7382: Delete section grid for migrated files 2024-03-12 13:20:50 +01:00
Andrei Isvoran
f2a6a90529 Merge branch 'RED-8707-fix' into 'master'
RED-8707 - Don't remove resize redactions which are not dictionary based when...

Closes RED-8707

See merge request redactmanager/persistence-service!391
2024-03-12 08:41:58 +01:00
Corina Olariu
5b4f194cad Merge branch 'RED-7049-update' into 'master'
RED-7049 - Wrong error code when adding entity with already existing rank - backport

Closes RED-7049

See merge request redactmanager/persistence-service!387
2024-03-12 08:41:20 +01:00
Andrei Isvoran
43e85af0e9 RED-8707 - Don't remove resize redactions which are not dictionary based when removing dictionary entry 2024-03-11 16:32:04 +02:00
Corina Olariu
6654dc9546 RED-7049 - Wrong error code when adding entity with already existing rank - backport
- remove commented coded

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-03-11 15:51:19 +02:00
Dominique Eifländer
7a9d90a76c Merge branch 'RED-8712-4.1' into 'master'
RED-8712: Fixed sql statement

Closes RED-8712

See merge request redactmanager/persistence-service!389
2024-03-11 14:22:09 +01:00
Dominique Eifländer
c78a4c1186 RED-8712: Fixed sql statement 2024-03-11 14:12:51 +01:00
Corina Olariu
9eb0e5e8df RED-7049 - Wrong error code when adding entity with already existing rank - backport
- reworked the junit test and renamed it

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-03-11 15:02:47 +02:00
Dominique Eifländer
eab0b3de39 Merge branch 'hotfix_dossier_redaction_4.1' into 'master'
hotfix: dossier template level dossier_redactions

See merge request redactmanager/persistence-service!386
2024-03-11 13:17:17 +01:00
Dominique Eifländer
8ae5ea89c6 hotfix: dossier template level dossier_redactions 2024-03-11 13:07:02 +01:00
Dominique Eifländer
71f760c1e4 Merge branch 'hotfix_dossier_redaction_4.1' into 'master'
hotfix: Fixed liquibase sql statement to insert missing dossier_redaction...

See merge request redactmanager/persistence-service!383
2024-03-11 12:25:59 +01:00
Dominique Eifländer
93d2b77ca5 hotfix: Fixed liquibase sql statement to insert missing dossier_redaction entity types on dossier template level 2024-03-11 12:11:46 +01:00
Dominique Eifländer
06970a1477 Merge branch 'RED-8712-4.1' into 'master'
RED-8712: Remove manual redactions on non existing pages

Closes RED-8712

See merge request redactmanager/persistence-service!382
2024-03-11 11:08:35 +01:00
Dominique Eifländer
8c8a088cc8 RED-8712: Remove manual redactions on non existing pages 2024-03-11 11:01:13 +01:00
Corina Olariu
f8645ef50c RED-7049 - Wrong error code when adding entity with already existing rank - backport
- add a check for duplicate ranks before cloning or exporting a dossier template
- added junit tests

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-03-11 11:01:56 +02:00
Maverick Studer
f5f33978ed Merge branch 'RED-7700' into 'master'
RED-7700: Safe rule execution

Closes RED-7700

See merge request redactmanager/persistence-service!378
2024-03-08 15:06:14 +01:00
Maverick Studer
d845b4cef2 RED-7700: Safe rule execution 2024-03-08 15:06:14 +01:00
Kilian Schüttler
baeadd145e Merge branch 'hotfix_gz_migration' into 'master'
hotfix: blobstorage migration gz fix

See merge request redactmanager/persistence-service!370
2024-03-08 14:51:33 +01:00
Kilian Schüttler
82ed6b0b05 Merge branch 'RED-7384' into 'master'
RED-7384: migration fixes forward port

Closes RED-7384

See merge request redactmanager/persistence-service!379
2024-03-08 14:22:54 +01:00
Kilian Schüttler
c2110cd965 RED-7384: migration fixes forward port 2024-03-08 14:22:54 +01:00
Timo Bejan
dff30d5f7d Merge branch 'clari-30' into 'master'
Updated integration of OCR service

See merge request redactmanager/persistence-service!380
2024-03-08 14:16:50 +01:00
Timo Bejan
042f3a079c Updated integration of OCR service 2024-03-08 15:06:03 +02:00
Ali Oezyetimoglu
c42983aa91 Merge branch 'RED-8561-3' into 'master'
RED-8561: added sql query to create entities for dossier dictionaries

Closes RED-8561

See merge request redactmanager/persistence-service!374
2024-03-07 13:59:22 +01:00
Ali Oezyetimoglu
333fd40449 RED-8561: flag for add to dictionary action was set to false in 3.6, but has to be true in 4.0 2024-03-06 10:41:51 +01:00
Ali Oezyetimoglu
a98cc0a6f2 RED-8561: updated sql query to update existing dossier dictionaries with dossier_dictionary_only=true 2024-03-05 20:20:10 +01:00
Ali Oezyetimoglu
6bb3c93b84 RED-8561: updated sql query to avoid conflicts with primary key in table entity 2024-03-05 17:58:46 +01:00
Ali Oezyetimoglu
01f857fcef RED-8561: updated sql query to avoid conflicts with primary key in table entity 2024-03-05 15:39:07 +01:00
Ali Oezyetimoglu
96be1ba52e RED-8561: updated sql query to avoid conflicts with primary key in table entity 2024-03-05 14:17:14 +01:00
Timo Bejan
81b66236f9 Merge branch 'clari-30' into 'master'
CLARI-30 - reworked OCR service integration to work via queue only, depercated...

See merge request redactmanager/persistence-service!372
2024-03-05 14:11:12 +01:00
Ali Oezyetimoglu
7b6af5bf3b RED-8561: updated sql query to avoid conflicts with primary key in table entity 2024-03-05 12:49:13 +01:00
Ali Oezyetimoglu
a37b2dbc32 RED-8561: added sql query to create entities for dossier dictionaries 2024-03-05 11:42:58 +01:00
Timo Bejan
47a9bc0343 Retry ocr 2024-03-05 10:48:45 +02:00
Timo Bejan
d849859def CLARI-30 - reworked OCR service integration to work via queue only, depercated old rest endpoints. Renamed queues to naming convetion. 2024-03-04 11:46:27 +02:00
Dominique Eifländer
3c4c93a634 hotfix: blobstorage migration gz fix 2024-02-29 12:14:03 +01:00
702 changed files with 549090 additions and 8062 deletions

2
.gitignore vendored
View File

@ -33,4 +33,4 @@ gradlew.bat
gradle/
**/.gradle
**/build
**/build

View File

@ -11,7 +11,7 @@ deploy:
- dind
script:
- echo "Building with gradle version ${BUILDVERSION}"
- gradle -Pversion=${BUILDVERSION} publish
- gradle -Pversion=${BUILDVERSION} publish
- gradle bootBuildImage --publishImage -PbuildbootDockerHostNetwork=true -Pversion=${BUILDVERSION}
- echo "BUILDVERSION=$BUILDVERSION" >> version.env
artifacts:
@ -20,4 +20,5 @@ deploy:
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_BRANCH =~ /^release/
- if: $CI_COMMIT_BRANCH =~ /^feature/
- if: $CI_COMMIT_TAG

57
README.md Normal file
View File

@ -0,0 +1,57 @@
![badge](https://img.shields.io/badge/license-knecon-brightgreen)
## Description
*TODO*
## Table of Contents
- [Installation](#installation)
- [Usage](#usage)
- [License](#license)
- [Tests](#tests)
- [API](#api)
## Installation
*TODO*
## Usage
*TODO*
## License
![badge](https://img.shields.io/badge/license-knecon-brightgreen)<br />
This application is covered by the proprietary knecon license.
## Tests
*TODO*
## API
### Files
The OpenAPI specifications for RedactManager and Documine are located here:<br> `./persistence-service-v1/persistence-service-external-api-v2/src/main/resources/api`
The Redocly configuration file `redocly.yaml` is located in the root directory of the project. It contains the settings for linting the specifications and generating the API documentation. If you run the redocly commands from there, you do not need to specify the path to the configuration file.
The Redocly files are located in the `redocly` directory:
* The `templates` directory contains the [Handlebars](https://handlebarsjs.com/) templates for RedactManager and Documine, respectively.
* The `build` directory is the default output directory for the generated API documentation.
### Redocly Installation
Execute the following command to install the Redocly CLI.
```bash
npm install -g @redocly/cli@latest
```
### Redocly Linting
Execute the following commands in the root directory of the project to lint the OpenAPI specifications.
Please refer to the Redocly documentation for detailed information on the linting rules.
```bash
redocly lint redactmanager
redocly lint documine
```
### Redocly Docs Generation
Execute the following commands in the root directory of the project to generate the API documentation.
```bash
redocly build-docs redactmanager --output ./redocly/build/redactmanager.html
redocly build-docs documine --output ./redocly/build/documine.html
```

View File

@ -6,10 +6,11 @@ plugins {
jacoco
}
val redactionServiceVersion by rootProject.extra { "4.246.0" }
val pdftronRedactionServiceVersion by rootProject.extra { "4.48.0" }
val redactionReportServiceVersion by rootProject.extra { "4.47.0" }
val searchServiceVersion by rootProject.extra { "2.71.0" }
val redactionServiceVersion by rootProject.extra { "4.290.0" }
val pdftronRedactionServiceVersion by rootProject.extra { "4.90.0-RED10115.0" }
val redactionReportServiceVersion by rootProject.extra { "4.81.0" }
val searchServiceVersion by rootProject.extra { "2.90.0" }
val documentVersion by rootProject.extra { "4.433.0" }
repositories {
mavenLocal()
@ -45,6 +46,8 @@ tasks.named<Test>("test") {
reports {
junitXml.outputLocation.set(layout.buildDirectory.dir("reports/junit"))
}
minHeapSize = "512m"
maxHeapSize = "2048m"
}
tasks.test {
@ -65,20 +68,12 @@ java {
}
allprojects {
publishing {
publications {
create<MavenPublication>(name) {
from(components["java"])
}
}
repositories {
maven {
url = uri("https://nexus.knecon.com/repository/red-platform-releases/")
credentials {
username = providers.gradleProperty("mavenUser").getOrNull();
password = providers.gradleProperty("mavenPassword").getOrNull();
}
}
tasks.withType<Javadoc> {
options {
this as StandardJavadocDocletOptions
addBooleanOption("Xdoclint:none", true)
addStringOption("Xmaxwarns", "1")
}
}
}

View File

@ -1,8 +1,8 @@
plugins {
id("com.iqser.red.service.java-conventions")
id("io.spring.dependency-management") version "1.1.3"
id("io.spring.dependency-management") version "1.1.6"
id("org.sonarqube") version "4.4.1.3373"
id("io.freefair.lombok") version "8.4"
id("io.freefair.lombok") version "8.6"
}
dependencies {

View File

@ -9,11 +9,17 @@ import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.ApplicationConfigurationEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.ApplicationConfigService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.iqser.red.service.persistence.service.v1.api.external.resource.ApplicationConfigurationResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.ApplicationConfig;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import jakarta.persistence.Column;
import jakarta.validation.Valid;
import lombok.Builder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -23,14 +29,24 @@ import lombok.extern.slf4j.Slf4j;
public class ApplicationConfigurationController implements ApplicationConfigurationResource {
private final ApplicationConfigService applicationConfigService;
private final AuditPersistenceService auditPersistenceService;
@Override
@PreAuthorize("hasAuthority('" + WRITE_APP_CONFIG + "')")
public ApplicationConfig createOrUpdateAppConfig(@Valid @RequestBody ApplicationConfig appConfig) {
return MagicConverter.convert(applicationConfigService.saveApplicationConfiguration(MagicConverter.convert(appConfig, ApplicationConfigurationEntity.class)),
var result = MagicConverter.convert(applicationConfigService.saveApplicationConfiguration(convert(appConfig)),
ApplicationConfig.class);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId("ApplicationConfig")
.category(AuditCategory.SETTINGS.name())
.message("Application config has been changed.")
.build());
return result;
}
@ -41,4 +57,18 @@ public class ApplicationConfigurationController implements ApplicationConfigurat
return MagicConverter.convert(applicationConfigService.getApplicationConfig(), ApplicationConfig.class);
}
private ApplicationConfigurationEntity convert(ApplicationConfig appConfig){
var entity = ApplicationConfigurationEntity.builder()
.downloadCleanupDownloadFilesHours(appConfig.getDownloadCleanupDownloadFilesHours())
.downloadCleanupNotDownloadFilesHours(appConfig.getDownloadCleanupNotDownloadFilesHours())
.softDeleteCleanupTime(appConfig.getSoftDeleteCleanupTime())
.build();
if(appConfig.getHardDeleteCleanupRetryTime() != null){
entity.setHardDeleteCleanupRetryTime(appConfig.getHardDeleteCleanupRetryTime());
}
return entity;
}
}

View File

@ -1,187 +0,0 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_RSS;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_REDACTION_LOG;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentOverrideService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.ComponentLogResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentsOverrides;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
@RestController
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class ComponentLogController implements ComponentLogResource {
ComponentLogService componentLogService;
ComponentOverrideService componentOverrideService;
AuditPersistenceService auditPersistenceService;
AccessControlService accessControlService;
@Override
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public ComponentLog getComponentLog(String dossierId, String fileId, boolean includeOverrides) {
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
return componentLogService.getComponentLog(dossierId, fileId, includeOverrides);
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void addOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ComponentsOverrides componentsOverrides) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
if (componentsOverrides.getComponentOverrides() == null || componentsOverrides.getComponentOverrides().isEmpty()) {
throw new BadRequestException("Request body cannot be empty!");
}
var componentLog = componentLogService.getComponentLog(dossierId, fileId);
var allComponents = componentLog.getComponentLogEntries();
componentOverrideService.addOverrides(dossierId, fileId, componentsOverrides);
componentsOverrides.getComponentOverrides()
.forEach((componentName, overrideValue) -> auditOverride(dossierId, fileId, componentName, overrideValue, allComponents));
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
return componentOverrideService.getOverrides(dossierId, fileId);
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void revertOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RevertOverrideRequest revertOverrideRequest) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
if (revertOverrideRequest.getComponents() == null || revertOverrideRequest.getComponents().isEmpty()) {
throw new BadRequestException("Request body cannot be empty!");
}
var componentLog = componentLogService.getComponentLog(dossierId, fileId);
var allComponents = componentLog.getComponentLogEntries();
componentOverrideService.revertOverrides(dossierId, fileId, revertOverrideRequest);
revertOverrideRequest.getComponents()
.forEach(componentNameToRevert -> auditOverrideRevert(dossierId, fileId, componentNameToRevert, allComponents));
}
private void auditOverride(String dossierId, String fileId, String componentName, String overrideValue, List<ComponentLogEntry> allComponentLogEntries) {
Optional<ComponentLogEntry> component = allComponentLogEntries.stream()
.filter(c -> c.getName().equals(componentName))
.findFirst();
String originalValue = getOriginalValue(component);
String value = getValue(component);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component is overwritten with value")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
"ComponentName",
componentName,
"Action",
"MODIFY",
"OriginalValue",
originalValue,
"OldValue",
value,
"NewValue",
overrideValue))
.build());
}
private void auditOverrideRevert(String dossierId, String fileId, String componentNameToRevert, List<ComponentLogEntry> allComponentLogEntries) {
Optional<ComponentLogEntry> component = allComponentLogEntries.stream()
.filter(c -> c.getName().equals(componentNameToRevert))
.findFirst();
String originalValue = getOriginalValue(component);
String value = getValue(component);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component override for was reverted")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
"ComponentName",
componentNameToRevert,
"Action",
"REVERT",
"OriginalValue",
originalValue,
"OldValue",
value,
"NewValue",
originalValue))
.build());
}
private String getValue(Optional<ComponentLogEntry> component) {
return component.map(ComponentLogEntry::getComponentValues)
.stream()
.map(a -> a.stream()
.map(ComponentLogEntryValue::getValue)
.collect(Collectors.joining(", ")))
.findFirst()
.orElse("");
}
private static String getOriginalValue(Optional<ComponentLogEntry> component) {
return component.map(ComponentLogEntry::getComponentValues)
.stream()
.map(a -> a.stream()
.map(ComponentLogEntryValue::getOriginalValue)
.collect(Collectors.joining(", ")))
.findFirst()
.orElse("");
}
}

View File

@ -11,8 +11,12 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.acl.custom.service.CustomPermissionService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.CustomPermissionMappingResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.permission.CustomPermissionMappingModel;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.RequiredArgsConstructor;
@ -21,6 +25,7 @@ import lombok.RequiredArgsConstructor;
public class CustomPermissionMappingController implements CustomPermissionMappingResource {
private final CustomPermissionService customPermissionService;
private final AuditPersistenceService auditPersistenceService;
@Override
@ -36,6 +41,14 @@ public class CustomPermissionMappingController implements CustomPermissionMappin
public void saveCustomPermissionMappings(@PathVariable(TARGET_OBJECT_NAME) String targetObject, @RequestBody List<CustomPermissionMappingModel> customPermissionMappingModels) {
customPermissionService.saveCustomPermissionMappings(targetObject, customPermissionMappingModels);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(targetObject)
.category(AuditCategory.SETTINGS.name())
.message("Custom permissions have been changed.")
.build());
}

View File

@ -8,6 +8,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.core.io.InputStreamResource;
@ -22,25 +23,28 @@ import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.DictionaryDifferenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.DictionaryService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils;
import com.iqser.red.service.persistence.management.v1.processor.utils.TypeValueMapper;
import com.iqser.red.service.persistence.service.v1.api.external.model.UpdateEntries;
import com.iqser.red.service.persistence.service.v1.api.external.resource.DictionaryResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.CreateTypeValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.Dictionary;
import com.iqser.red.service.persistence.service.v1.api.shared.model.TypeResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.TypeValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.UpdateTypeValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dictionary.Dictionary;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dictionary.DictionaryDifferenceResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.Colors;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.DictionaryEntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.Type;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import feign.FeignException;
import jakarta.validation.Valid;
@ -55,6 +59,7 @@ public class DictionaryController implements DictionaryResource {
private final DictionaryService dictionaryService;
private final AuditPersistenceService auditClient;
private final AccessControlService accessControlService;
private final DictionaryDifferenceService dictionaryDifferenceService;
@Override
@ -106,6 +111,31 @@ public class DictionaryController implements DictionaryResource {
}
@Override
public void updateEntries(String type, String dossierTemplateId, UpdateEntries updateEntries, String dossierId, DictionaryEntryType dictionaryEntryType) {
if (dossierId == null) {
dictionaryService.updateGlobalEntries(type, dossierTemplateId, updateEntries.entriesToAdd(), updateEntries.entriesToDelete(), dictionaryEntryType);
} else {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
dictionaryService.updateDossierEntries(type, dossierTemplateId, updateEntries.entriesToAdd(), updateEntries.entriesToDelete(), dossierId, dictionaryEntryType);
}
auditClient.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary entries were updated.")
.details(Map.of("Type",
type,
"Number of added entries",
updateEntries.entriesToAdd() == null ? 0 : updateEntries.entriesToAdd().size(),
"Number of deleted entries",
updateEntries.entriesToDelete() == null ? 0 : updateEntries.entriesToDelete().size()))
.build());
}
@Override
public void deleteEntries(@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@ -322,4 +352,29 @@ public class DictionaryController implements DictionaryResource {
.build());
}
@Override
public void changeFlags(@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@PathVariable(value = DOSSIER_ID_PARAMETER_NAME) String dossierId,
@RequestParam(value = "addToDictionaryAction") boolean addToDictionaryAction) {
dictionaryService.changeAddToDictionary(type, dossierTemplateId, dossierId, addToDictionaryAction);
}
@Override
public DictionaryDifferenceResponse getDictionaryDifference(String dossierTemplateId, Set<String> types) {
DictionaryDifferenceResponse dictionaryDifferenceResponse = dictionaryDifferenceService.calculatedDictionaryDifference(dossierTemplateId, types);
auditClient.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary differences was calculated.")
.details(Map.of("DictionaryDifferenceResponse", dictionaryDifferenceResponse, "types", types))
.build());
return dictionaryDifferenceResponse;
}
}

View File

@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.utils.DossierAttributeConfigMapper;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierAttributeConfigEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
@ -52,7 +53,7 @@ public class DossierAttributesController implements DossierAttributesResource {
var result = MagicConverter.convert(dossierAttributeConfigPersistenceService.setDossierAttributesConfig(dossierTemplateId,
MagicConverter.convert(dossierAttributesConfig.getDossierAttributeConfigs(),
DossierAttributeConfigEntity.class)),
DossierAttributeConfig.class);
DossierAttributeConfig.class, new DossierAttributeConfigMapper());
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
@ -72,7 +73,7 @@ public class DossierAttributesController implements DossierAttributesResource {
var result = MagicConverter.convert(dossierAttributeConfigPersistenceService.addOrUpdateDossierAttribute(dossierTemplateId,
MagicConverter.convert(dossierAttribute,
DossierAttributeConfigEntity.class)),
DossierAttributeConfig.class);
DossierAttributeConfig.class, new DossierAttributeConfigMapper());
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
@ -118,7 +119,7 @@ public class DossierAttributesController implements DossierAttributesResource {
public DossierAttributesConfig getDossierAttributesConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {
var result = dossierAttributeConfigPersistenceService.getDossierAttributes(dossierTemplateId);
return new DossierAttributesConfig(MagicConverter.convert(result, DossierAttributeConfig.class));
return new DossierAttributesConfig(MagicConverter.convert(result, DossierAttributeConfig.class, new DossierAttributeConfigMapper()));
}

View File

@ -10,6 +10,8 @@ import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@ -18,9 +20,15 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierAttributeEntity;
import com.iqser.red.service.persistence.management.v1.processor.model.websocket.DossierEventType;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierCreatorService;
import com.iqser.red.service.persistence.management.v1.processor.service.FilterByPermissionsService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierChangeResponseV2;
import com.iqser.red.service.persistence.service.v1.api.shared.model.JsonNode;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@ -41,7 +49,9 @@ import com.iqser.red.service.persistence.management.v1.processor.roles.Applicati
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.websocket.WebsocketService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.users.UserService;
import com.iqser.red.service.persistence.management.v1.processor.service.users.model.User;
@ -55,6 +65,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.Audit
import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.CreateOrUpdateDossierRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttributes;
import com.iqser.red.service.persistence.service.v1.api.shared.model.notification.NotificationType;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
@ -70,12 +81,15 @@ public class DossierController implements DossierResource {
private final DossierManagementService dossierManagementService;
private final UserService userService;
private final FilterByPermissionsService filterByPermissionsService;
private final FileStatusManagementService fileStatusManagementService;
private final AuditPersistenceService auditPersistenceService;
private final NotificationPersistenceService notificationPersistenceService;
private final AccessControlService accessControlService;
private final DossierACLService dossierACLService;
private final DossierCreatorService dossierCreatorService;
private final WebsocketService websocketService;
private final DossierAttributePersistenceService dossierAttributePersistenceService;
@Override
@ -98,6 +112,20 @@ public class DossierController implements DossierResource {
}
@Override
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
public DossierChangeResponseV2 changesSinceV2(@RequestBody JSONPrimitive<OffsetDateTime> since) {
DossierChangeResponseV2 changes = dossierManagementService.changesSinceV2(since);
// filter only viewables
changes.setFileChanges(filterByPermissionsService.onlyViewableHavingDossierId(changes.getFileChanges()));
changes.setDossierChanges(filterByPermissionsService.onlyViewableHavingDossierId(changes.getDossierChanges()));
return changes;
}
@Override
@PreAuthorize("hasAuthority('" + ADD_UPDATE_DOSSIER + "') && (#dossierRequest.dossierId == null || hasPermission(#dossierRequest.dossierId, 'Dossier', 'ACCESS_OBJECT') )")
public ResponseEntity<Dossier> createDossierOrUpdateDossier(@RequestBody DossierRequest dossierRequest) {
@ -217,6 +245,10 @@ public class DossierController implements DossierResource {
.target(Map.of("dossierId", dossierRequest.getDossierId()))
.build()));
websocketService.sendDossierEvent(dossierRequest.getDossierId(), DossierEventType.UPDATE);
updatedDossier.setDossierAttributes(convertDossierAttributes(dossierAttributePersistenceService.getDossierAttributes(updatedDossier.getId())));
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity<>(updatedDossier, httpHeaders, HttpStatus.OK);
@ -231,6 +263,7 @@ public class DossierController implements DossierResource {
.message("Dossier has been created.")
.build());
websocketService.sendDossierEvent(created.getId(), DossierEventType.CREATE);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return new ResponseEntity<>(created, httpHeaders, HttpStatus.CREATED);
@ -336,6 +369,7 @@ public class DossierController implements DossierResource {
.build(), members, approvers, ownerId);
dossierACLService.enhanceDossierWithACLData(newDossier);
newDossier.setDossierAttributes(new DossierAttributes());
return newDossier;
@ -367,7 +401,8 @@ public class DossierController implements DossierResource {
throw new AccessDeniedException("Can not delete dossier that is owned by a different user");
}
dossierManagementService.delete(dossierId);
dossierManagementService.softDeleteDossier(dossierId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
@ -386,6 +421,29 @@ public class DossierController implements DossierResource {
.target(Map.of("dossierId", dossierId, "dossierName", dossier.getDossierName()))
.build()));
websocketService.sendDossierEvent(dossierId, DossierEventType.SOFT_DELETE);
}
@Override
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
public JSONPrimitive<Map<String, Dossier>> getDossiersByIds(@RequestBody JSONPrimitive<Set<String>> dossierIds) {
// filter dossiers based on view
var viewableDossierIds = filterByPermissionsService.onlyViewableDossierIds(dossierIds.getValue());
// load dossiers
var dossiers = dossierManagementService.getDossiersByIds(viewableDossierIds);
// add attributes and ACL - already filtered before loading
enhanceDossiersWithAttributeAndACLData(dossiers,false);
// build response
var responseMap = new LinkedHashMap<String, Dossier>();
for (var dossier : dossiers) {
responseMap.put(dossier.getId(), dossier);
}
return new JSONPrimitive<>(responseMap);
}
@ -395,67 +453,52 @@ public class DossierController implements DossierResource {
@RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted) {
accessControlService.checkViewPermissionsToDossier(dossierId);
return dossierACLService.enhanceDossierWithACLData(dossierManagementService.getDossierById(dossierId, includeArchived, includeDeleted));
var dossier = dossierManagementService.getDossierById(dossierId, includeArchived, includeDeleted);
dossier.setDossierAttributes(convertDossierAttributes(dossierAttributePersistenceService.getDossierAttributes(dossierId)));
return dossierACLService.enhanceDossierWithACLData(dossier);
}
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
@PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
public List<Dossier> getDossiers(@RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived,
@RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted) {
return dossierManagementService.getAllDossiers(includeArchived, includeDeleted)
.stream()
.map(dossierACLService::enhanceDossierWithACLData)
.collect(Collectors.toList());
var dossiers = dossierManagementService.getAllDossiers(includeArchived, includeDeleted);
return enhanceDossiersWithAttributeAndACLData(dossiers);
}
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
@PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
public List<Dossier> getDossiersForDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived,
@RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted) {
return dossierManagementService.getAllDossiersForDossierTemplateId(dossierTemplateId, includeArchived, includeDeleted)
.stream()
.map(dossierACLService::enhanceDossierWithACLData)
.collect(Collectors.toList());
var dossiers = dossierManagementService.getAllDossiersForDossierTemplateId(dossierTemplateId, includeArchived, includeDeleted);
return enhanceDossiersWithAttributeAndACLData(dossiers);
}
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
@PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
public List<Dossier> getSoftDeletedDossiers() {
return dossierManagementService.getSoftDeletedDossiers()
.stream()
.map(dossierACLService::enhanceDossierWithACLData)
.collect(Collectors.toList());
var dossiers = dossierManagementService.getSoftDeletedDossiers();
return enhanceDossiersWithAttributeAndACLData(dossiers);
}
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
@PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
public List<Dossier> getArchivedDossiers() {
return dossierManagementService.getArchivedDossiers()
.stream()
.map(dossierACLService::enhanceDossierWithACLData)
.collect(Collectors.toList());
var dossiers = dossierManagementService.getArchivedDossiers();
return enhanceDossiersWithAttributeAndACLData(dossiers);
}
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
@PostFilter("hasPermission(filterObject.id, 'Dossier', 'VIEW_OBJECT')")
public List<Dossier> getArchivedDossiersForDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) {
return dossierManagementService.getArchivedDossiersForDossierTemplateId(dossierTemplateId)
.stream()
.map(dossierACLService::enhanceDossierWithACLData)
.collect(Collectors.toList());
var dossiers = dossierManagementService.getArchivedDossiersForDossierTemplateId(dossierTemplateId);
return enhanceDossiersWithAttributeAndACLData(dossiers);
}
@ -475,6 +518,7 @@ public class DossierController implements DossierResource {
.category(AuditCategory.DOSSIER.name())
.message("Dossier archived.")
.build());
websocketService.sendDossierEvent(dossierId, DossierEventType.ARCHIVE);
}
}
@ -492,6 +536,7 @@ public class DossierController implements DossierResource {
.category(AuditCategory.DOSSIER.name())
.message("Dossier restored from archive.")
.build());
websocketService.sendDossierEvent(dossierId, DossierEventType.UNARCHIVE);
}
}
@ -512,7 +557,7 @@ public class DossierController implements DossierResource {
.category(AuditCategory.DOSSIER.name())
.message("Dossier permanently deleted.")
.build());
websocketService.sendDossierEvent(dossierId, DossierEventType.HARD_DELETE);
}
}
@ -532,6 +577,7 @@ public class DossierController implements DossierResource {
.category(AuditCategory.DOSSIER.name())
.message("Dossier restored from trash.")
.build());
websocketService.sendDossierEvent(dossierId, DossierEventType.UNDELETE);
}
}
@ -547,5 +593,41 @@ public class DossierController implements DossierResource {
.collect(Collectors.toSet());
}
private DossierAttributes convertDossierAttributes(List<DossierAttributeEntity> dossierAttributeEntities) {
Map<String, String> attributeIdToValue = new HashMap<>();
for (DossierAttributeEntity dossierAttributeEntity : dossierAttributeEntities) {
attributeIdToValue.put(dossierAttributeEntity.getId().getDossierAttributeConfigId(), dossierAttributeEntity.getValue());
}
return new DossierAttributes(attributeIdToValue);
}
private List<Dossier> enhanceDossiersWithAttributeAndACLData(List<Dossier> dossiers) {
return enhanceDossiersWithAttributeAndACLData(dossiers, true);
}
private List<Dossier> enhanceDossiersWithAttributeAndACLData(List<Dossier> dossiers, boolean filter) {
// filter first, only load attributes and ACL for viewable dossiers
List<Dossier> filteredDossiers = filter ? filterByPermissionsService.onlyViewableDossiers(dossiers) : dossiers;
// load all attributes at once
var attributes = dossierAttributePersistenceService.getDossierAttributes(filteredDossiers.stream().map(Dossier::getId).collect(Collectors.toSet()));
var attributesMap = new HashMap<String, List<DossierAttributeEntity>>();
for (DossierAttributeEntity attribute : attributes) {
attributesMap.computeIfAbsent(attribute.getId().getDossierId(), k -> new ArrayList<>()).add(attribute);
}
for (var dossier : filteredDossiers) {
// set attributes
dossier.setDossierAttributes(convertDossierAttributes(attributesMap.getOrDefault(dossier.getId(), new ArrayList<>())));
// set ACL data
dossierACLService.enhanceDossierWithACLData(dossier);
}
return filteredDossiers;
}
}

View File

@ -14,13 +14,17 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.utils.ColorUtils;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.iqser.red.service.persistence.service.v1.api.external.resource.DossierStatusResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierStatusRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.CreateOrUpdateDossierStatusRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierStatusInfo;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -31,6 +35,7 @@ import lombok.extern.slf4j.Slf4j;
public class DossierStatusController implements DossierStatusResource {
private final DossierStatusPersistenceService dossierStatusPersistenceService;
private final AuditPersistenceService auditPersistenceService;
@Override
@ -57,6 +62,15 @@ public class DossierStatusController implements DossierStatusResource {
.rank(dossierStatusRequest.getRank())
.color(dossierStatusRequest.getColor())
.build());
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierStatusRequest.getDossierTemplateId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier states have been updated.")
.build());
return MagicConverter.convert(response, DossierStatusInfo.class);
}
@ -91,7 +105,17 @@ public class DossierStatusController implements DossierStatusResource {
public void deleteDossierStatus(@PathVariable("dossierStatusId") String dossierStatusId,
@RequestParam(value = DOSSIER_STATUS_REPLACE_ID, required = false) String replaceDossierStatusId) {
var dossierTemplateId = dossierStatusPersistenceService.getDossierStatus(dossierStatusId).getDossierTemplateId();
dossierStatusPersistenceService.deleteDossierStatus(dossierStatusId, replaceDossierStatusId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier state has been deleted.")
.build());
}
}

View File

@ -42,7 +42,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemp
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplateStats;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplateStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.importexport.ExportDownloadRequest;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.ExportDownloadRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.importexport.ImportDossierTemplateRequest;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
@ -136,7 +136,7 @@ public class DossierTemplateController implements DossierTemplateResource {
@Override
@PreAuthorize("hasAuthority('" + READ_DOSSIER_TEMPLATES + "')")
@PreAuthorize("hasAuthority('" + WRITE_DOSSIER_TEMPLATES + "')")
public void deleteDossierTemplates(@RequestBody List<String> dossierTemplateIds) {
String userId = KeycloakSecurity.getUserId();

View File

@ -7,23 +7,17 @@ import java.io.BufferedInputStream;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@ -34,6 +28,7 @@ import com.iqser.red.service.persistence.management.v1.processor.service.AccessC
import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.DownloadService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils;
@ -76,6 +71,7 @@ public class DownloadController implements DownloadResource {
private final OneTimeTokenService oneTimeTokenDownloadService;
private final AccessControlService accessControlService;
private final FileManagementStorageService fileManagementStorageService;
private final FileStatusManagementService fileStatusManagementService;
private final String REPORT_INFO = "/REPORT_INFO.json";
@ -142,7 +138,7 @@ public class DownloadController implements DownloadResource {
if (StringUtils.isBlank(dossierId)) {
throw new BadRequestException("Empty dossier id");
}
dossierService.getDossierById(dossierId, true, true);
dossierService.getDossierById(dossierId, true, false);
accessControlService.verifyUserIsDossierOwnerOrApprover(dossierId);
}
@ -152,16 +148,19 @@ public class DownloadController implements DownloadResource {
List<FileModel> validFiles = fileStatusService.getDossierStatus(request.getDossierId());
var fileIds = request.getFileIds();
if (fileIds != null && !fileIds.isEmpty()) { // validate the ids provided
if(fileIds.size() == 1) {
fileStatusManagementService.getFileStatus(fileIds.get(0), false);
}
validFiles = validFiles.stream()
.filter(f -> fileIds.contains(f.getId()))
.collect(Collectors.toList());
if (validFiles.isEmpty()) {
throw new NotFoundException("No file id provided is found");
throw new NotFoundException("No provided file id was found");
}
} // otherwise consider the files from dossier
var validFilesAndNotProcessed = validFiles.stream()
.filter(f -> !(f.getAnalysisVersion() > 0 && f.getNumberOfAnalyses() > 0))
.filter(f -> !(f.getAnalysisVersion() > 0 && f.getNumberOfAnalyses() > 0 && !f.isSoftOrHardDeleted()))
.collect(Collectors.toList());
if (!validFilesAndNotProcessed.isEmpty()) {
throw new BadRequestException("At least a file is in its initial analysis process");

View File

@ -2,6 +2,7 @@ package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_REDACTION_LOG;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.access.prepost.PreAuthorize;
@ -10,10 +11,11 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.mapper.EntityLogResponseMapper;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.EntityLogService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.EntityLogResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.FilteredEntityLogRequest;
import lombok.RequiredArgsConstructor;
@ -24,28 +26,55 @@ public class EntityLogController implements EntityLogResource {
private final EntityLogService entityLogService;
private final AccessControlService accessControlService;
private final EntityLogResponseMapper entityLogResponseMapper = EntityLogResponseMapper.INSTANCE;
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public EntityLog getEntityLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "excludedType", required = false) List<String> excludedTypes,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed) {
public EntityLogResponse getEntityLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "excludedTypes", required = false, defaultValue = "") List<String> excludedTypes,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed) {
accessControlService.checkViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
return entityLogService.getEntityLog(dossierId, fileId, excludedTypes, includeUnprocessed);
return entityLogResponseMapper.toLogResponse(entityLogService.getEntityLog(dossierId,
fileId,
excludedTypes == null ? new ArrayList<>() : excludedTypes,
includeUnprocessed));
}
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public EntityLog getFilteredEntityLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody FilteredEntityLogRequest filteredEntityLogRequest) {
public EntityLogResponse getFilteredEntityLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody FilteredEntityLogRequest filteredEntityLogRequest) {
accessControlService.checkViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
return entityLogService.getFilteredEntityLog(dossierId, fileId, filteredEntityLogRequest);
return entityLogResponseMapper.toLogResponse(entityLogService.getFilteredEntityLog(dossierId, fileId, filteredEntityLogRequest));
}
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public EntityLogResponse getEntityLogWithEntriesOnPages(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "pageNumbers", defaultValue = "") List<Integer> pageNumbers) {
accessControlService.checkViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
return entityLogResponseMapper.toLogResponse(entityLogService.getEntityLogWithEntriesOnPages(dossierId, fileId, pageNumbers));
}
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public EntityLogResponse getEntityLogDeltaUpdate(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@PathVariable(ANALYSIS_NUMBER) Integer analysisNumber,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed) {
accessControlService.checkViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
return entityLogResponseMapper.toLogResponse(entityLogService.getEntityLogWithEntriesWithEntriesSinceAnalysisNumber(dossierId, fileId, analysisNumber, includeUnprocessed));
}
}

View File

@ -13,6 +13,9 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.model.websocket.FileEventType;
import com.iqser.red.service.persistence.management.v1.processor.service.websocket.WebsocketService;
import com.iqser.red.service.persistence.management.v1.processor.utils.FileAttributeConfigMapper;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.FileAttributesGeneralConfigurationEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileAttributeConfigEntity;
@ -47,6 +50,7 @@ public class FileAttributesController implements FileAttributesResource {
private final AuditPersistenceService auditPersistenceService;
private final FileStatusService fileStatusService;
private final AccessControlService accessControlService;
private final WebsocketService websocketService;
@Override
@ -58,9 +62,9 @@ public class FileAttributesController implements FileAttributesResource {
}
fileAttributeConfigPersistenceService.setFileAttributesGeneralConfig(dossierTemplateId,
MagicConverter.convert(fileAttributesConfig, FileAttributesGeneralConfigurationEntity.class));
var result = fileAttributeConfigPersistenceService.setFileAttributesConfig(dossierTemplateId,
MagicConverter.convert(fileAttributesConfig.getFileAttributeConfigs(),
FileAttributeConfigEntity.class));
fileAttributeConfigPersistenceService.setFileAttributesConfig(dossierTemplateId,
MagicConverter.convert(fileAttributesConfig.getFileAttributeConfigs(), FileAttributeConfigEntity.class));
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
@ -71,7 +75,9 @@ public class FileAttributesController implements FileAttributesResource {
.filenameMappingColumnHeaderName(fileAttributesConfig.getFilenameMappingColumnHeaderName())
.delimiter(fileAttributesConfig.getDelimiter())
.encoding(fileAttributesConfig.getEncoding())
.fileAttributeConfigs(MagicConverter.convert(result, FileAttributeConfig.class))
.fileAttributeConfigs(MagicConverter.convert(fileAttributeConfigPersistenceService.getFileAttributes(dossierTemplateId),
FileAttributeConfig.class,
new FileAttributeConfigMapper()))
.build();
}
@ -93,7 +99,7 @@ public class FileAttributesController implements FileAttributesResource {
dossierTemplateId))
.build());
return MagicConverter.convert(result, FileAttributeConfig.class);
return MagicConverter.convert(result, FileAttributeConfig.class, new FileAttributeConfigMapper());
}
@ -142,7 +148,7 @@ public class FileAttributesController implements FileAttributesResource {
.filenameMappingColumnHeaderName(generalConfig.getFilenameMappingColumnHeaderName())
.delimiter(generalConfig.getDelimiter())
.encoding(generalConfig.getEncoding())
.fileAttributeConfigs(MagicConverter.convert(fileAttributeConfigs, FileAttributeConfig.class))
.fileAttributeConfigs(MagicConverter.convert(fileAttributeConfigs, FileAttributeConfig.class, new FileAttributeConfigMapper()))
.build();
}
@ -161,6 +167,7 @@ public class FileAttributesController implements FileAttributesResource {
}
accessControlService.verifyUserIsMemberOrApprover(dossierId);
fileAttributesManagementService.setFileAttributes(dossierId, fileId, fileAttributes.getAttributeIdToValue());
websocketService.sendFileEvent(dossierId, fileId, FileEventType.UPDATE);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)

View File

@ -232,7 +232,7 @@ public class FileManagementController implements FileManagementResource {
public void restoreFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody Set<String> fileIds) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
verifyUserIsDossierOwnerOrApproverOrAssignedReviewer(dossierId, fileIds);
accessControlService.verifyUserIsDossierOwnerOrApproverOrAssignedReviewer(dossierId, fileIds);
fileService.undeleteFiles(dossierId, fileIds);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
@ -266,11 +266,14 @@ public class FileManagementController implements FileManagementResource {
FileModel fileModel = fileStatusManagementService.getFileStatus(fileId);
if (!fileModel.isExcludedFromAutomaticAnalysis()) {
if (fileModel.getOcrStartTime() != null || fileModel.getOcrEndTime() != null) {
reanalysisService.ocrFile(dossierId, fileId, true);
} else {
fileStatusService.setStatusFullReprocess(dossierId, fileId, true, true);
}
// Re- ocr is files with pdftron ocr service is breaking files and not needed with azure.
// TODO find a better solution for this.
// if (fileModel.getOcrStartTime() != null || fileModel.getOcrEndTime() != null) {
// reanalysisService.ocrFile(dossierId, fileId, true);
// } else {
fileStatusService.setStatusFullReprocess(dossierId, fileId, true, true, false);
// }
}
auditPersistenceService.audit(AuditRequest.builder()
@ -286,20 +289,4 @@ public class FileManagementController implements FileManagementResource {
}
}
private void verifyUserIsDossierOwnerOrApproverOrAssignedReviewer(String dossierId, Set<String> fileIds) {
try {
accessControlService.verifyUserIsDossierOwnerOrApprover(dossierId);
} catch (AccessDeniedException e1) {
try {
for (String fileId : fileIds) {
accessControlService.verifyUserIsReviewer(dossierId, fileId);
}
} catch (NotAllowedException e2) {
throw new NotAllowedException("User must be dossier owner, approver or assigned reviewer of the file.");
}
}
}
}

View File

@ -15,9 +15,9 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.service.pdftron.redaction.v1.api.model.highlights.Highlights;
import com.iqser.red.service.pdftron.redaction.v1.api.model.highlights.TextHighlightConversionOperation;
import com.iqser.red.service.pdftron.redaction.v1.api.model.highlights.TextHighlightConversionRequest;
import com.iqser.red.service.pdftron.redaction.v1.api.model.highlights.TextHighlights;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
@ -47,20 +47,20 @@ public class HighlightsController implements HighlightsResource {
@SneakyThrows
@PreAuthorize("hasAuthority('" + GET_HIGHLIGHTS + "')")
public Highlights getHighlights(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
public TextHighlights getHighlights(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
fileStatusService.getStatus(fileId);
if (storageService.objectExists(TenantContext.getTenantId(), getStorageId(dossierId, fileId, FileType.TEXT_HIGHLIGHTS))) {
try (InputStream stream = fileManagementStorageService.getObject(TenantContext.getTenantId(), getStorageId(dossierId, fileId, FileType.TEXT_HIGHLIGHTS))) {
Highlights highlights = objectMapper.readValue(stream, Highlights.class);
TextHighlights highlights = objectMapper.readValue(stream, TextHighlights.class);
stream.close();
return highlights;
}
}
return new Highlights();
return new TextHighlights();
}

View File

@ -0,0 +1,58 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_SIMILAR_IMAGES;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.commons.spring.ErrorMessage;
import com.iqser.red.service.persistence.management.v1.processor.service.ImageSimilarityService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.ImageSimilaritySearchResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.image.ImageSimilaritySearchRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.image.ImageSimilaritySearchResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.ImageDocument;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequiredArgsConstructor
@Slf4j
public class ImageSimilaritySearchController implements ImageSimilaritySearchResource {
private final ImageSimilarityService imageSimilarityService;
@SneakyThrows
@PreAuthorize("hasAuthority('" + GET_SIMILAR_IMAGES + "')")
public ResponseEntity getSimilarImages(@RequestBody ImageSimilaritySearchRequest imageSimilaritySearchRequest) {
log.info("received similiar image search request {}", imageSimilaritySearchRequest);
if (imageSimilaritySearchRequest.getAnnotationId().isEmpty() || (imageSimilaritySearchRequest.getScope().getFileId().isEmpty() && imageSimilaritySearchRequest.getScope()
.getTemplateId()
.isEmpty() && imageSimilaritySearchRequest.getScope().getDossierId().isEmpty())) {
return new ResponseEntity<>(new ErrorMessage(OffsetDateTime.now(), "Required parameter missing"), HttpStatus.BAD_REQUEST);
}
List<ImageDocument> similarImages = this.imageSimilarityService.findSimilarImages(imageSimilaritySearchRequest.getAnnotationId(),
imageSimilaritySearchRequest.getDistance(),
imageSimilaritySearchRequest.getScope());
List<String> similarImagesIds = new ArrayList<>();
List<Byte> similarImagesThumbnails = new ArrayList<>();
similarImages.stream()
.forEach(doc -> {
similarImagesIds.add(doc.getImageId());
similarImagesThumbnails.add(doc.getThumbnail());
});
ImageSimilaritySearchResponse imageSimilaritySearchResponse = new ImageSimilaritySearchResponse(similarImagesIds, similarImagesThumbnails);
return new ResponseEntity<>(imageSimilaritySearchResponse, HttpStatus.OK);
}
}

View File

@ -5,22 +5,21 @@ import static com.iqser.red.service.persistence.management.v1.processor.roles.Ac
import java.util.List;
import jakarta.transaction.Transactional;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.LegalBasisMappingPersistenceService;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.iqser.red.service.persistence.service.v1.api.external.resource.LegalBasisMappingResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.legalbasis.LegalBasis;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
@RestController
@ -33,9 +32,9 @@ public class LegalBasisMappingController implements LegalBasisMappingResource {
@Override
@PreAuthorize("hasAuthority('" + WRITE_LEGAL_BASIS + "')")
public void deleteLegalBasis(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestBody List<String> legalBasisNames) {
public void deleteLegalBasis(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestBody List<String> legalBasisTechnicalNames) {
legalBasisMappingPersistenceService.deleteLegalBasis(dossierTemplateId, legalBasisNames);
legalBasisMappingPersistenceService.deleteLegalBasis(dossierTemplateId, legalBasisTechnicalNames);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)

View File

@ -6,9 +6,13 @@ import static com.iqser.red.service.persistence.management.v1.processor.roles.Ac
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.DO_MANUAL_REDACTION;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_MANUAL_REDACTIONS;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@ -16,37 +20,48 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.mapper.EntityLogResponseMapper;
import com.iqser.red.service.persistence.management.v1.processor.model.ManualChangesQueryOptions;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.CommentService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionMapper;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionUndoService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.PendingEntryFactory;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.utils.TypeIdUtils;
import com.iqser.red.service.persistence.service.v1.api.external.resource.ManualRedactionResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.CommentResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AddRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogEntryResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryState;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntryType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.Position;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AnnotationComments;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Comment;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.CommentRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ForceRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.LegalBasisChangeRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualAddResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualAnnotationResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactionResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.RecategorizationRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.RemoveRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ResizeRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Rectangle;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddCommentRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionBulkLocalRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ForceRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.LegalBasisChangeRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RecategorizationBulkLocalRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RecategorizationRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RemoveRedactionBulkLocalRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RemoveRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ResizeRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.service.EntityLogMongoService;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.AccessLevel;
@ -63,32 +78,38 @@ public class ManualRedactionController implements ManualRedactionResource {
static final String FILE_ID = "fileId";
static final String DOSSIER_ID = "dossierId";
static final String ANNOTATION_ID = "annotationId";
ManualRedactionService manualRedactionService;
ManualRedactionUndoService manualRedactionUndoService;
DossierManagementService dossierManagementService;
AuditPersistenceService auditPersistenceService;
AccessControlService accessControlService;
ManualRedactionMapper manualRedactionMapper;
CommentService commentService;
FileStatusManagementService fileStatusManagementService;
FileStatusService fileStatusService;
EntityLogMongoService entityLogMongoService;
PendingEntryFactory pendingEntryFactory;
DictionaryPersistenceService dictionaryPersistenceService;
EntityLogController entityLogController;
EntityLogResponseMapper mapper = EntityLogResponseMapper.INSTANCE;
@Override
@PreAuthorize("hasAuthority('" + DELETE_MANUAL_REDACTION + "')")
public void undo(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<String> annotationIds,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed) {
@RequestParam(value = "includeOnlyUnprocessed", required = false, defaultValue = FALSE) boolean includeOnlyUnprocessed,
@RequestParam(value = "includeOnlyLocal", required = false, defaultValue = FALSE) boolean includeOnlyLocal) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsApprover(dossierId);
manualRedactionUndoService.undo(dossierId, fileId, annotationIds, includeUnprocessed);
manualRedactionUndoService.undo(dossierId, fileId, annotationIds, includeOnlyUnprocessed, includeOnlyLocal);
}
@Override
@PreAuthorize("hasAuthority('" + DELETE_COMMENT + "')")
public void undoComment(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@ -111,7 +132,6 @@ public class ManualRedactionController implements ManualRedactionResource {
}
@Override
@PreAuthorize("hasAuthority('" + READ_MANUAL_REDACTIONS + "')")
public ManualRedactions getManualRedactions(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@ -120,12 +140,12 @@ public class ManualRedactionController implements ManualRedactionResource {
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
return manualRedactionService.getManualRedactions(fileId,
ManualChangesQueryOptions.builder().includeOnlyUnprocessed(unprocessed).includeDictChanges(includeDictChanges).build());
}
@Override
@PreAuthorize("hasAuthority('" + READ_MANUAL_REDACTIONS + "')")
public AnnotationComments getComments(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @PathVariable(ANNOTATION_ID) String annotationId) {
@ -138,7 +158,6 @@ public class ManualRedactionController implements ManualRedactionResource {
}
@Override
@PreAuthorize("hasAuthority('" + ADD_COMMENT + "')")
public CommentResponse addComment(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@ -163,108 +182,172 @@ public class ManualRedactionController implements ManualRedactionResource {
}
@Override
@PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
public List<ManualAddResponse> addRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
public ManualRedactionResponse addRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<AddRedactionRequestModel> addRedactionRequests) {
var dossier = dossierManagementService.getDossierById(dossierId, false, false);
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
if (addRedactionRequests.stream()
.anyMatch(AddRedactionRequestModel::isAddToAllDossiers)) {
accessControlService.verifyUserIsApprover(dossierId);
} else {
accessControlService.verifyUserIsMemberOrApprover(dossierId);
}
verifyAccessForDossier(dossierId,
fileId,
addRedactionRequests.stream()
.anyMatch(AddRedactionRequestModel::isAddToAllDossiers));
List<AddRedactionRequest> requests = manualRedactionMapper.toAddRedactionRequestList(dossierId, addRedactionRequests, dossier);
List<ManualAddResponse> responseList = manualRedactionService.addAddRedaction(dossierId, fileId, requests);
List<ManualAnnotationResponse> responseList = manualRedactionService.addAddRedaction(dossierId, fileId, addRedactionRequests, dossier);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Manual redaction was added.")
.message("Manual annotation was added.")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
return responseList;
return ManualRedactionResponse.builder().manualAnnotationResponses(responseList).build();
}
@PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
public List<ManualAddResponse> removeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
public ManualRedactionResponse addRedactionBulkLocal(String dossierId, String fileId, AddRedactionBulkLocalRequestModel addRedactionRequest) {
verifyAccess(dossierId, fileId);
// check if type exists before sending the request to redaction service
var dossier = dossierManagementService.getDossierById(dossierId, false, false);
dictionaryPersistenceService.getType(TypeIdUtils.toTypeId(addRedactionRequest.getType(), dossier.getDossierTemplateId()));
if (!addRedactionRequest.isRectangle()) {
fileStatusService.setStatusBulkLocalRedactionsProcessing(fileId, addRedactionRequest);
EntityLogEntry entityLogEntry = pendingEntryFactory.buildAddRedactionBulkLocalEntry(addRedactionRequest);
return ManualRedactionResponse.builder()
.manualAnnotationResponses(List.of(ManualAnnotationResponse.builder()
.annotationId(manualRedactionService.getNewAnnotationId())
.entityLogEntry(entityLogEntry)
.build()))
.build();
} else {
Set<AddRedactionRequestModel> addRedactionRequestModels = addRedactionRequest.getPageNumbers()
.stream()
.map(page -> AddRedactionRequestModel.builder()
.type(addRedactionRequest.getType())
.value(addRedactionRequest.getValue())
.reason(addRedactionRequest.getReason())
.legalBasis(addRedactionRequest.getLegalBasis())
.positions(addRedactionRequest.getPositions()
.stream()
.map(rectangle -> Rectangle.createRectangle(rectangle.getTopLeftX(),
rectangle.getTopLeftY(),
rectangle.getWidth(),
rectangle.getHeight(),
page))
.toList())
.comment(addRedactionRequest.getComment() != null ? AddCommentRequestModel.builder().text(addRedactionRequest.getComment()).build() : null)
.section(addRedactionRequest.getSection())
.rectangle(true)
.build())
.collect(Collectors.toSet());
return addRedactionBulk(dossierId, fileId, addRedactionRequestModels);
}
}
@PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
public ManualRedactionResponse removeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<RemoveRedactionRequestModel> removeRedactionRequests,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed) {
@RequestBody Set<RemoveRedactionRequestModel> removeRedactionRequests) {
var dossier = dossierManagementService.getDossierById(dossierId, false, false);
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
if (removeRedactionRequests.stream()
.anyMatch(RemoveRedactionRequestModel::isRemoveFromAllDossiers)) {
accessControlService.verifyUserIsApprover(dossierId);
} else {
accessControlService.verifyUserIsMemberOrApprover(dossierId);
}
verifyAccessForDossier(dossierId,
fileId,
removeRedactionRequests.stream()
.anyMatch(RemoveRedactionRequestModel::isRemoveFromAllDossiers));
List<RemoveRedactionRequest> requests = manualRedactionMapper.toRemoveRedactionRequestList(dossierId,
fileId,
dossier.getDossierTemplateId(),
removeRedactionRequests,
includeUnprocessed);
List<ManualAddResponse> responseList = manualRedactionService.addRemoveRedaction(dossierId, fileId, requests);
List<ManualAnnotationResponse> responseList = manualRedactionService.addRemoveRedaction(dossierId, fileId, removeRedactionRequests, dossier.getDossierTemplateId(), true);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Redaction was manually removed")
.message("Annotation was manually removed")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
return responseList;
return ManualRedactionResponse.builder().manualAnnotationResponses(responseList).build();
}
@PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
public List<ManualAddResponse> forceRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
public ManualRedactionResponse removeRedactionBulkLocal(String dossierId, String fileId, RemoveRedactionBulkLocalRequestModel removeRedactionRequest) {
verifyAccess(dossierId, fileId);
verifyRequest(removeRedactionRequest.isRectangle(), removeRedactionRequest.getPosition(), removeRedactionRequest.getValue());
Set<RemoveRedactionRequestModel> removeRedactionRequestModels;
FileModel status = fileStatusService.getStatus(fileId);
Set<EntityLogEntry> entries;
if (!status.isExcludedFromAutomaticAnalysis()) {
entries = getFilteredEntityLogEntries(dossierId,
fileId,
removeRedactionRequest.isRectangle(),
removeRedactionRequest.getValue(),
removeRedactionRequest.isCaseSensitive(),
removeRedactionRequest.getOriginTypes(),
removeRedactionRequest.getOriginLegalBases(),
removeRedactionRequest.getPageNumbers(),
removeRedactionRequest.getPosition());
} else {
entries = new HashSet<>(mapper.fromLogEntryResponses(getFilteredEntityLogResponses(dossierId,
fileId,
removeRedactionRequest.isRectangle(),
removeRedactionRequest.getValue(),
removeRedactionRequest.isCaseSensitive(),
removeRedactionRequest.getOriginTypes(),
removeRedactionRequest.getOriginLegalBases(),
removeRedactionRequest.getPageNumbers(),
removeRedactionRequest.getPosition())));
}
removeRedactionRequestModels = entries.stream()
.map(entityLogEntry -> RemoveRedactionRequestModel.builder().annotationId(entityLogEntry.getId()).comment(removeRedactionRequest.getComment()).build())
.collect(Collectors.toSet());
return removeRedactionBulk(dossierId, fileId, removeRedactionRequestModels);
}
@PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
public ManualRedactionResponse forceRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<ForceRedactionRequestModel> forceRedactionRequests) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
verifyAccessAndDossierExistence(dossierId, fileId);
List<ForceRedactionRequest> requests = manualRedactionMapper.toForceRedactionRequestList(forceRedactionRequests);
List<ManualAddResponse> responseList = manualRedactionService.addForceRedaction(dossierId, fileId, requests);
List<ManualAnnotationResponse> responseList = manualRedactionService.addForceRedaction(dossierId, fileId, forceRedactionRequests);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Skipped redaction was forced to be redacted")
.message("Skipped annotation was forced to be applied")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
return responseList;
return ManualRedactionResponse.builder().manualAnnotationResponses(responseList).build();
}
@PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
public List<ManualAddResponse> legalBasisChangeBulk(@PathVariable(DOSSIER_ID) String dossierId,
public ManualRedactionResponse legalBasisChangeBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<LegalBasisChangeRequestModel> legalBasisChangeRequests) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
verifyAccessAndDossierExistence(dossierId, fileId);
List<LegalBasisChangeRequest> requests = manualRedactionMapper.toLegalBasisChangeRequestList(legalBasisChangeRequests);
List<ManualAddResponse> responseList = manualRedactionService.addLegalBasisChange(dossierId, fileId, requests);
List<ManualAnnotationResponse> responseList = manualRedactionService.addLegalBasisChange(dossierId, fileId, legalBasisChangeRequests);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
@ -274,28 +357,19 @@ public class ManualRedactionController implements ManualRedactionResource {
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
return responseList;
return ManualRedactionResponse.builder().manualAnnotationResponses(responseList).build();
}
@PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
public List<ManualAddResponse> recategorizeBulk(@PathVariable(DOSSIER_ID) String dossierId,
public ManualRedactionResponse recategorizeBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<RecategorizationRequestModel> recategorizationRequests,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed) {
@RequestBody Set<RecategorizationRequestModel> recategorizationRequests) {
var dossier = dossierManagementService.getDossierById(dossierId, false, false);
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
verifyAccess(dossierId, fileId);
List<RecategorizationRequest> requests = manualRedactionMapper.toRecategorizationRequestList(dossierId,
fileId,
dossier.getDossierTemplateId(),
recategorizationRequests,
includeUnprocessed);
List<ManualAddResponse> responseList = manualRedactionService.addRecategorization(dossierId, fileId, requests);
List<ManualAnnotationResponse> responseList = manualRedactionService.addRecategorization(dossierId, fileId, dossier, recategorizationRequests, true);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
@ -305,32 +379,239 @@ public class ManualRedactionController implements ManualRedactionResource {
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
return responseList;
return ManualRedactionResponse.builder().manualAnnotationResponses(responseList).build();
}
@PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
public List<ManualAddResponse> resizeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
public ManualRedactionResponse recategorizeBulkLocal(String dossierId, String fileId, RecategorizationBulkLocalRequestModel recategorizationRequest) {
verifyAccess(dossierId, fileId);
verifyRequest(recategorizationRequest.isRectangle(), recategorizationRequest.getPosition(), recategorizationRequest.getValue());
Set<RecategorizationRequestModel> recategorizationRequestModels;
FileModel status = fileStatusService.getStatus(fileId);
Set<EntityLogEntry> entries;
if (!status.isExcludedFromAutomaticAnalysis()) {
entries = getFilteredEntityLogEntries(dossierId,
fileId,
recategorizationRequest.isRectangle(),
recategorizationRequest.getValue(),
recategorizationRequest.isCaseSensitive(),
recategorizationRequest.getOriginTypes(),
recategorizationRequest.getOriginLegalBases(),
recategorizationRequest.getPageNumbers(),
recategorizationRequest.getPosition());
} else {
entries = new HashSet<>(mapper.fromLogEntryResponses(getFilteredEntityLogResponses(dossierId,
fileId,
recategorizationRequest.isRectangle(),
recategorizationRequest.getValue(),
recategorizationRequest.isCaseSensitive(),
recategorizationRequest.getOriginTypes(),
recategorizationRequest.getOriginLegalBases(),
recategorizationRequest.getPageNumbers(),
recategorizationRequest.getPosition())));
}
recategorizationRequestModels = entries.stream()
.map(entry -> RecategorizationRequestModel.builder()
.annotationId(entry.getId())
.type(recategorizationRequest.isRectangle() ? entry.getType() : recategorizationRequest.getType())
.legalBasis(recategorizationRequest.getLegalBasis())
.section(recategorizationRequest.getSection())
.value(recategorizationRequest.isRectangle() ? recategorizationRequest.getValue() : entry.getValue())
.comment(recategorizationRequest.getComment())
.build())
.collect(Collectors.toSet());
return recategorizeBulk(dossierId, fileId, recategorizationRequestModels);
}
@PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
public ManualRedactionResponse resizeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<ResizeRedactionRequestModel> resizeRedactionRequests,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed) {
@RequestBody Set<ResizeRedactionRequestModel> resizeRedactionRequests) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
verifyAccessAndDossierExistence(dossierId, fileId);
List<ResizeRedactionRequest> requests = manualRedactionMapper.toResizeRedactionRequestList(resizeRedactionRequests);
List<ManualAddResponse> responseList = manualRedactionService.addResizeRedaction(dossierId, fileId, requests, includeUnprocessed);
List<ManualAnnotationResponse> responseList = manualRedactionService.addResizeRedaction(dossierId, fileId, resizeRedactionRequests, true);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Skipped redaction was resized to be redacted")
.message("Skipped annotation was resized to be redacted")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
return responseList;
return ManualRedactionResponse.builder().manualAnnotationResponses(responseList).build();
}
private Set<EntityLogEntry> getFilteredEntityLogEntries(String dossierId,
String fileId,
boolean rectangle,
String value,
boolean caseSensitive,
Set<String> originTypes,
Set<String> originLegalBases,
Set<Integer> pageNumbers,
Position position) {
Set<EntityLogEntry> entries;
if (!rectangle) {
entries = entityLogMongoService.findEntriesByValueWithFilters(dossierId, fileId, value, caseSensitive, originTypes, originLegalBases, pageNumbers);
} else {
entries = entityLogMongoService.findEntriesByMatchingFullPositionWithFilters(dossierId, fileId, position.getRectangle(), originTypes, originLegalBases, pageNumbers);
}
return entries;
}
private List<EntityLogEntryResponse> getFilteredEntityLogResponses(String dossierId,
String fileId,
boolean rectangle,
String value,
boolean caseSensitive,
Set<String> originTypes,
Set<String> originLegalBases,
Set<Integer> pageNumbers,
Position position) {
List<EntityLogEntryResponse> entityLogEntryResponses = entityLogController.getEntityLog(dossierId, fileId, Collections.emptyList(), true).getEntityLogEntry()
.stream()
.filter(entityLogEntryResponse -> !entityLogEntryResponse.getState().equals(EntryState.PENDING))
.collect(Collectors.toList());
if (!rectangle) {
entityLogEntryResponses = filterEntriesByValueWithFilters(entityLogEntryResponses, value, caseSensitive, originTypes, originLegalBases, pageNumbers);
} else {
entityLogEntryResponses = filterEntriesByMatchingPositionWithFilters(entityLogEntryResponses, position.getRectangle(), originTypes, originLegalBases, pageNumbers);
}
return entityLogEntryResponses;
}
public List<EntityLogEntryResponse> filterEntriesByValueWithFilters(List<EntityLogEntryResponse> entries,
String value,
boolean caseSensitive,
Set<String> originTypes,
Set<String> originLegalBases,
Set<Integer> pageNumbers) {
return entries.stream()
.filter(entry -> !entry.getEntryType().equals(EntryType.AREA))
.filter(entry -> filterByValue(entry, value, caseSensitive))
.filter(entry -> filterByOriginType(entry, originTypes))
.filter(entry -> filterByLegalBases(entry, originLegalBases))
.filter(entry -> filterByPageNumbers(entry, pageNumbers))
.collect(Collectors.toList());
}
public List<EntityLogEntryResponse> filterEntriesByMatchingPositionWithFilters(List<EntityLogEntryResponse> entries,
float[] rectangle,
Set<String> originTypes,
Set<String> originLegalBases,
Set<Integer> pageNumbers) {
return entries.stream()
.filter(entry -> entry.getEntryType().equals(EntryType.AREA))
.filter(entry -> filterByRectangle(entry, rectangle))
.filter(entry -> filterByOriginType(entry, originTypes))
.filter(entry -> filterByLegalBases(entry, originLegalBases))
.filter(entry -> filterByPageNumbers(entry, pageNumbers))
.collect(Collectors.toList());
}
private boolean filterByValue(EntityLogEntryResponse entry, String value, boolean caseSensitive) {
if (caseSensitive) {
return entry.getValue().equals(value);
} else {
return entry.getValue().equalsIgnoreCase(value);
}
}
private boolean filterByOriginType(EntityLogEntryResponse entry, Set<String> originTypes) {
return originTypes == null || originTypes.isEmpty() || originTypes.contains(entry.getType());
}
private boolean filterByLegalBases(EntityLogEntryResponse entry, Set<String> originLegalBases) {
return originLegalBases == null || originLegalBases.isEmpty() || originLegalBases.contains(entry.getLegalBasis());
}
private boolean filterByPageNumbers(EntityLogEntryResponse entry, Set<Integer> pageNumbers) {
if (pageNumbers == null || pageNumbers.isEmpty()) {
return true;
}
return entry.getPositions()
.stream()
.anyMatch(pos -> pageNumbers.contains(pos.getPageNumber()));
}
private boolean filterByRectangle(EntityLogEntryResponse entry, float[] rectangle) {
if (rectangle == null || rectangle.length == 0) {
return true;
}
return entry.getPositions()
.stream()
.anyMatch(pos -> Arrays.equals(pos.getRectangle(), rectangle));
}
private void verifyRequest(boolean isRectangle, Position position, String value) {
if (isRectangle && position == null) {
throw new BadRequestException("Position must be set for rectangle annotations.");
}
if (!isRectangle && value == null) {
throw new BadRequestException("Value must be set.");
}
}
private void verifyAccessForDossier(String dossierId, String fileId, boolean allDossiersAffected) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
if (allDossiersAffected) {
accessControlService.verifyUserIsApprover(dossierId);
} else {
accessControlService.verifyUserIsMemberOrApprover(dossierId);
}
}
private void verifyAccess(String dossierId, String fileId) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
}
private void verifyAccessAndDossierExistence(String dossierId, String fileId) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
}
}

View File

@ -1,111 +0,0 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.migration.SaasMigrationService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.SaasMigrationStatusPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.MigrationStatusResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.SaasMigrationStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.saas.migration.MigrationStatusResponse;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import static com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.SaasMigrationStatus.*;
@RestController
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@RequiredArgsConstructor
public class MigrationStatusController implements MigrationStatusResource {
SaasMigrationService saasMigrationService;
SaasMigrationStatusPersistenceService saasMigrationStatusPersistenceService;
FileStatusService fileStatusService;
public MigrationStatusResponse migrationStatus() {
int numberOfFilesToMigrate = saasMigrationStatusPersistenceService.countAll();
Map<SaasMigrationStatus, Integer> filesInStatus = new HashMap<>();
filesInStatus.put(MIGRATION_REQUIRED, saasMigrationStatusPersistenceService.countByStatus(MIGRATION_REQUIRED));
filesInStatus.put(DOCUMENT_FILES_MIGRATED, saasMigrationStatusPersistenceService.countByStatus(DOCUMENT_FILES_MIGRATED));
filesInStatus.put(REDACTION_LOGS_MIGRATED, saasMigrationStatusPersistenceService.countByStatus(REDACTION_LOGS_MIGRATED));
filesInStatus.put(ANNOTATION_IDS_MIGRATED, saasMigrationStatusPersistenceService.countByStatus(ANNOTATION_IDS_MIGRATED));
filesInStatus.put(FINISHED, saasMigrationStatusPersistenceService.countByStatus(FINISHED));
filesInStatus.put(ERROR, saasMigrationStatusPersistenceService.countByStatus(ERROR));
var filesInErrorState = saasMigrationStatusPersistenceService.findAllByStatus(ERROR);
Map<String, String> errorCauses = new HashMap<>();
filesInErrorState.forEach(errorFile -> {
errorCauses.put(errorFile.getFileId(), errorFile.getErrorCause());
});
return MigrationStatusResponse.builder().numberOfFilesToMigrate(numberOfFilesToMigrate).filesInStatus(filesInStatus).errorCauses(errorCauses).build();
}
@Override
public ResponseEntity<?> startMigrationForFile(String dossierId, String fileId) {
if (!fileStatusService.fileExists(fileId)) {
throw new NotFoundException(String.format("File with id %s does not exist", fileId));
}
saasMigrationService.startMigrationForFile(dossierId, fileId);
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<?> revertMigrationForFile(String dossierId, String fileId) {
if (!fileStatusService.fileExists(fileId)) {
throw new NotFoundException(String.format("File with id %s does not exist", fileId));
}
if (!saasMigrationStatusPersistenceService.findById(fileId).getStatus().equals(FINISHED)) {
throw new BadRequestException(String.format("File with id %s is not migrated yet, can't revert.", fileId));
}
saasMigrationService.revertMigrationForFile(dossierId, fileId);
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<?> requeueErrorFiles() {
MigrationStatusResponse migrationStatus = migrationStatus();
if (!migrationIsFinished(migrationStatus)) {
throw new BadRequestException("There are still files processing, please wait until migration has finished to retry!");
}
saasMigrationService.requeueErrorFiles();
return ResponseEntity.ok().build();
}
private static boolean migrationIsFinished(MigrationStatusResponse migrationStatus) {
return migrationStatus.getFilesInStatus().entrySet()
.stream()
.filter(e -> e.getValue() > 0)
.allMatch(e -> e.getKey().equals(FINISHED) || e.getKey().equals(ERROR));
}
}

View File

@ -1,287 +0,0 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_RSS;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentOverrideService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.RSSResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntityReference;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentsOverrides;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.rss.RSSFileResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.rss.RSSResponse;
import com.iqser.red.service.redaction.report.v1.api.model.rss.DetailedRSSFileResponse;
import com.iqser.red.service.redaction.report.v1.api.model.rss.DetailedRSSResponse;
import com.iqser.red.service.redaction.report.v1.api.model.rss.SCMComponent;
import com.iqser.red.service.redaction.report.v1.api.model.rss.ScmAnnotation;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.RequiredArgsConstructor;
@Deprecated(forRemoval = true)
@RestController
@RequiredArgsConstructor
@ConditionalOnProperty(name = "application.rss.component-log.enabled", havingValue = "true")
public class RSSComponentLogController implements RSSResource {
private final ComponentOverrideService componentOverrideService;
private final AuditPersistenceService auditPersistenceService;
private final ComponentLogService componentLogService;
private final StatusController statusController;
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public RSSResponse getRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId) {
List<FileStatus> dossierFiles;
if (StringUtils.isBlank(fileId)) {
dossierFiles = statusController.getDossierStatus(dossierId);
} else {
dossierFiles = List.of(statusController.getFileStatus(dossierId, fileId));
}
List<RSSFileResponse> fileResponses = dossierFiles.stream()
.map(this::getRssResponse)
.toList();
return new RSSResponse(fileResponses);
}
private RSSFileResponse getRssResponse(FileStatus file) {
var componentLog = componentLogService.getComponentLog(file.getDossierId(), file.getId(), true);
Map<String, String> results = new LinkedHashMap<>();
componentLog.getComponentLogEntries()
.forEach(entry -> {
if (entry.getComponentValues().size() <= 1) {
results.put(entry.getName(),
entry.getComponentValues()
.get(0).getValue());
return;
}
List<ComponentLogEntryValue> componentValues = entry.getComponentValues();
for (int i = 0, componentValuesSize = componentValues.size(); i < componentValuesSize; i++) {
ComponentLogEntryValue v = componentValues.get(i);
results.put(entry.getName() + "_" + (i + 1), v.getValue());
}
});
return RSSFileResponse.builder().filename(file.getFilename()).result(results).build();
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public DetailedRSSResponse getDetailedRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId) {
List<FileStatus> dossierFiles;
if (StringUtils.isBlank(fileId)) {
dossierFiles = statusController.getDossierStatus(dossierId);
} else {
dossierFiles = List.of(statusController.getFileStatus(dossierId, fileId));
}
List<DetailedRSSFileResponse> fileResponses = dossierFiles.stream()
.map(this::getDetailedRssResponse)
.toList();
return new DetailedRSSResponse(fileResponses);
}
private DetailedRSSFileResponse getDetailedRssResponse(FileStatus file) {
var componentLog = componentLogService.getComponentLog(file.getDossierId(), file.getId(), true);
Map<String, SCMComponent> results = new LinkedHashMap<>();
componentLog.getComponentLogEntries()
.forEach(entry -> {
if (entry.getComponentValues().size() <= 1) {
results.put(entry.getName(),
toSCMComponent(entry.getComponentValues()
.get(0)));
return;
}
List<ComponentLogEntryValue> componentValues = entry.getComponentValues();
for (int i = 0, componentValuesSize = componentValues.size(); i < componentValuesSize; i++) {
ComponentLogEntryValue v = componentValues.get(i);
results.put(entry.getName() + "_" + (i + 1), toSCMComponent(v));
}
});
return DetailedRSSFileResponse.builder().filename(file.getFilename()).result(results).build();
}
private SCMComponent toSCMComponent(ComponentLogEntryValue v) {
return SCMComponent.builder()
.value(v.getValue().equals(v.getOriginalValue()) ? null : v.getValue())
.originalValue(v.getOriginalValue())
.transformation(v.getValueDescription())
.scmAnnotations(v.getComponentLogEntityReferences()
.stream()
.map(this::toScmAnnotation)
.toList())
.build();
}
private ScmAnnotation toScmAnnotation(ComponentLogEntityReference er) {
return ScmAnnotation.builder().type(er.getType()).pages(Set.of(er.getPage())).ruleIdentifier(er.getEntityRuleId()).reason(formatType(er.getType())).build();
}
private static String formatType(String type) {
return type.substring(0, 1).toUpperCase(Locale.ENGLISH) + type.substring(1).toLowerCase(Locale.ENGLISH).replaceAll("_", " ");
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void addOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ComponentsOverrides componentsOverrides) {
var componentLog = componentLogService.getComponentLog(dossierId, fileId);
var allComponents = componentLog.getComponentLogEntries();
componentOverrideService.addOverrides(dossierId, fileId, componentsOverrides);
componentsOverrides.getComponentOverrides()
.forEach((componentName, overrideValue) -> auditOverride(dossierId, fileId, componentName, overrideValue, allComponents));
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
return componentOverrideService.getOverrides(dossierId, fileId);
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void revertOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RevertOverrideRequest revertOverrideRequest) {
var componentLog = componentLogService.getComponentLog(dossierId, fileId);
var allComponents = componentLog.getComponentLogEntries();
componentOverrideService.revertOverrides(dossierId, fileId, revertOverrideRequest);
revertOverrideRequest.getComponents()
.forEach(componentNameToRevert -> auditOverrideRevert(dossierId, fileId, componentNameToRevert, allComponents));
}
private void auditOverride(String dossierId, String fileId, String componentName, String overrideValue, List<ComponentLogEntry> allComponentLogEntries) {
Optional<ComponentLogEntry> component = allComponentLogEntries.stream()
.filter(c -> c.getName().equals(componentName))
.findFirst();
String originalValue = getOriginalValue(component);
String value = getValue(component);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component is overwritten with value")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
"ComponentName",
componentName,
"Action",
"MODIFY",
"OriginalValue",
originalValue,
"OldValue",
value,
"NewValue",
overrideValue))
.build());
}
private void auditOverrideRevert(String dossierId, String fileId, String componentNameToRevert, List<ComponentLogEntry> allComponentLogEntries) {
Optional<ComponentLogEntry> component = allComponentLogEntries.stream()
.filter(c -> c.getName().equals(componentNameToRevert))
.findFirst();
String originalValue = getOriginalValue(component);
String value = getValue(component);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component override for was reverted")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
"ComponentName",
componentNameToRevert,
"Action",
"REVERT",
"OriginalValue",
originalValue,
"OldValue",
value,
"NewValue",
originalValue))
.build());
}
private String getValue(Optional<ComponentLogEntry> component) {
return component.map(ComponentLogEntry::getComponentValues)
.stream()
.map(a -> a.stream()
.map(ComponentLogEntryValue::getValue)
.collect(Collectors.joining(", ")))
.findFirst()
.orElse("");
}
private static String getOriginalValue(Optional<ComponentLogEntry> component) {
return component.map(ComponentLogEntry::getComponentValues)
.stream()
.map(a -> a.stream()
.map(ComponentLogEntryValue::getOriginalValue)
.collect(Collectors.joining(", ")))
.findFirst()
.orElse("");
}
}

View File

@ -1,148 +0,0 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_RSS;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.client.redactionreportservice.RssReportClient;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentOverrideService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.RSSResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentsOverrides;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.rss.RSSFileResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.rss.RSSResponse;
import com.iqser.red.service.redaction.report.v1.api.model.rss.DetailedRSSResponse;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.RequiredArgsConstructor;
@Deprecated(forRemoval = true)
@RestController
@RequiredArgsConstructor
@ConditionalOnProperty(name = "application.rss.component-log.enabled", havingValue = "false")
public class RSSController implements RSSResource {
private final RssReportClient rssReportClient;
private final ComponentOverrideService componentOverrideService;
private final AuditPersistenceService auditPersistenceService;
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public RSSResponse getRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId) {
return convert(rssReportClient.getRSS(dossierId, fileId));
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
private RSSResponse convert(com.iqser.red.service.redaction.report.v1.api.model.rss.RSSResponse rssResponse) {
return new RSSResponse(rssResponse.getFiles()
.stream()
.map(this::convert)
.collect(Collectors.toList()));
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
private RSSFileResponse convert(com.iqser.red.service.redaction.report.v1.api.model.rss.RSSFileResponse rssFileResponse) {
return new RSSFileResponse(rssFileResponse.getFilename(), rssFileResponse.getResult());
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public DetailedRSSResponse getDetailedRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId) {
return rssReportClient.getDetailedRSS(dossierId, fileId);
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void addOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ComponentsOverrides componentsOverrides) {
var rssReport = rssReportClient.getDetailedRSS(dossierId, fileId);
var components = rssReport.getFiles()
.get(0).getResult();
componentOverrideService.addOverrides(dossierId, fileId, componentsOverrides);
componentsOverrides.getComponentOverrides()
.forEach((key, value) -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component is overwritten with value")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
"ComponentName",
key,
"Action",
"MODIFY",
"OriginalValue",
components.get(key).getOriginalValue(),
"OldValue",
components.get(key).getValue() != null ? components.get(key)
.getValue() : components.get(key).getOriginalValue(),
"NewValue",
value))
.build()));
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
return componentOverrideService.getOverrides(dossierId, fileId);
}
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void revertOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RevertOverrideRequest revertOverrideRequest) {
var rssReport = rssReportClient.getDetailedRSS(dossierId, fileId);
var components = rssReport.getFiles()
.get(0).getResult();
componentOverrideService.revertOverrides(dossierId, fileId, revertOverrideRequest);
revertOverrideRequest.getComponents()
.forEach(component -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("The component override for was reverted")
.details(Map.of(DOSSIER_ID,
dossierId,
FILE_ID,
fileId,
"ComponentName",
component,
"Action",
"REVERT",
"OriginalValue",
components.get(component).getOriginalValue(),
"OldValue",
components.get(component).getValue() != null ? components.get(component)
.getValue() : components.get(component).getOriginalValue(),
"NewValue",
components.get(component).getOriginalValue()))
.build()));
}
}

View File

@ -46,6 +46,7 @@ public class ReanalysisController implements ReanalysisResource {
public void reanalyzeDossier(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
reanalysisService.reanalyzeDossier(dossierId, force);
auditPersistenceService.audit(AuditRequest.builder()
@ -117,11 +118,12 @@ public class ReanalysisController implements ReanalysisResource {
@PreAuthorize("hasAuthority('" + REANALYZE_FILE + "')")
public void ocrFile(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force) {
@RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force,
@RequestParam(value = ALL_PAGES, required = false, defaultValue = FALSE) boolean allPages) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
validateOCR(dossierId, fileId);
reanalysisService.ocrFile(dossierId, fileId, force);
reanalysisService.ocrFile(dossierId, fileId, force, allPages);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
@ -139,7 +141,7 @@ public class ReanalysisController implements ReanalysisResource {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
fileIds.forEach(fileId -> validateOCR(dossierId, fileId));
reanalysisService.ocrFiles(dossierId, fileIds);
reanalysisService.ocrFiles(dossierId, fileIds, false);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)

View File

@ -1,156 +0,0 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_REDACTION_LOG;
import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
import com.iqser.red.service.persistence.management.v1.processor.service.RedactionLogService;
import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils;
import com.iqser.red.service.persistence.service.v1.api.external.resource.RedactionLogResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.FilteredRedactionLogRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.section.SectionGrid;
import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist;
import com.knecon.fforesight.tenantcommons.TenantContext;
import feign.FeignException;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
@RestController
@RequiredArgsConstructor
@Deprecated(forRemoval = true)
public class RedactionLogController implements RedactionLogResource {
private final RedactionLogService redactionLogService;
private final FileStatusService fileStatusService;
private final FileManagementStorageService fileManagementStorageService;
@Deprecated(forRemoval = true)
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public RedactionLog getRedactionLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "excludedType", required = false) List<String> excludedTypes,
@RequestParam(value = "withManualRedactions", required = false, defaultValue = "true") boolean withManualRedactions,
@RequestParam(value = "includeFalsePositives", required = false, defaultValue = "false") boolean includeFalsePositives) {
try {
return redactionLogService.getRedactionLog(dossierId, fileId, excludedTypes);
} catch (FeignException e) {
throw processFeignException(e);
}
}
@Deprecated(forRemoval = true)
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public SectionGrid getSectionGrid(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
try {
return redactionLogService.getSectionGrid(dossierId, fileId);
} catch (FeignException e) {
throw processFeignException(e);
}
}
@Deprecated(forRemoval = true)
@SneakyThrows
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public ResponseEntity<?> getSectionText(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
try {
return buildZipFileResponseEntity(fileId, dossierId, FileType.TEXT);
} catch (FeignException e) {
throw processFeignException(e);
}
}
private ResponseEntity<byte[]> buildZipFileResponseEntity(String fileId, String dossierId, FileType fileType) throws IOException {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.parseMediaType("application/zip"));
var fileStatus = fileStatusService.getStatus(fileId);
String filename = fileStatus.getFilename();
if (filename != null) {
var index = filename.lastIndexOf(".");
String prefix = filename.substring(0, index);
filename = prefix + ".json";
httpHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=utf-8''" + StringEncodingUtils.urlEncode(prefix) + ".zip");
}
byte[] zipBytes = getZippedBytes(dossierId, fileId, filename, fileType);
httpHeaders.setContentLength(zipBytes.length);
return new ResponseEntity<>(zipBytes, httpHeaders, HttpStatus.OK);
}
@Deprecated(forRemoval = true)
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public RedactionLog getFilteredRedactionLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody FilteredRedactionLogRequest filteredRedactionLogRequest) {
try {
return redactionLogService.getFilteredRedactionLog(dossierId, fileId, filteredRedactionLogRequest);
} catch (FeignException e) {
throw processFeignException(e);
}
}
private byte[] getZippedBytes(String dossierId, String fileId, String filename, FileType fileType) throws IOException {
try {
String objectId = dossierId + "/" + fileId + "." + fileType.name() + fileType.getExtension();
try (var inputStream = fileManagementStorageService.getObject(TenantContext.getTenantId(), objectId)) {
byte[] input = inputStream.readAllBytes();
inputStream.close();
return zipBytes(filename, input);
}
} catch (StorageObjectDoesNotExist e) {
throw new RuntimeException(String.format("%s is not available", fileType.name()), e);
}
}
public static byte[] zipBytes(String filename, byte[] input) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
ZipEntry entry = new ZipEntry(filename);
entry.setSize(input.length);
zos.putNextEntry(entry);
zos.write(input);
zos.closeEntry();
zos.close();
return baos.toByteArray();
}
}

View File

@ -13,8 +13,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.core.io.InputStreamResource;
@ -30,18 +28,17 @@ import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.iqser.red.service.persistence.management.v1.processor.client.redactionreportservice.PlaceholderClient;
import com.iqser.red.service.persistence.management.v1.processor.client.redactionreportservice.ReportTemplatePlaceholderClient;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.ReportTemplateService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ReportTemplatePersistenceService;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils;
import com.iqser.red.service.persistence.service.v1.api.external.resource.ReportTemplateResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
@ -53,6 +50,8 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemp
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.ReportTemplateUploadRequest;
import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.knecon.fforesight.tenantcommons.TenantContext;
import feign.FeignException;
@ -77,6 +76,7 @@ public class ReportTemplateController implements ReportTemplateResource {
private final FileManagementStorageService fileManagementStorageService;
@Override
@PreAuthorize("hasAuthority('" + GET_REPORT_TEMPLATES + "')")
public List<ReportTemplate> getReportTemplatesByPlaceholder(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody JSONPrimitive<String> placeholder) {
@ -174,6 +174,7 @@ public class ReportTemplateController implements ReportTemplateResource {
var storageId = reportTemplatePersistenceService.find(templateId).getStorageId();
storageService.deleteObject(TenantContext.getTenantId(), storageId);
reportTemplatePersistenceService.delete(templateId);
reportTemplatePlaceholderClient.evictReportTemplateCache();
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(templateId)

View File

@ -7,7 +7,6 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
@ -24,20 +23,20 @@ import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.FileUploadException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.RulesValidationService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.utils.RulesValidationMapper;
import com.iqser.red.service.persistence.service.v1.api.external.resource.RulesResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsSyntaxValidationResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RuleSyntaxErrorMessage;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RuleSyntaxWarningMessage;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsValidationResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUploadRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUploadRequestModel;
import com.iqser.red.service.redaction.v1.model.DroolsSyntaxValidation;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import feign.FeignException;
@ -54,49 +53,32 @@ public class RulesController implements RulesResource {
private final RulesPersistenceService rulesPersistenceService;
private final RulesValidationService rulesValidationService;
private final AuditPersistenceService auditPersistenceService;
private final FileStatusPersistenceService fileStatusPersistenceService;
@Override
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
public ResponseEntity<DroolsSyntaxValidationResponse> upload(@RequestBody RulesUploadRequestModel rules) {
public ResponseEntity<DroolsValidationResponse> upload(@RequestBody RulesUploadRequestModel rules) {
RulesUploadRequest rulesUploadRequest = RulesUploadRequest.fromModel(rules);
DroolsSyntaxValidationResponse droolsSyntaxValidationResponse = DroolsSyntaxValidationResponse.builder().build();
try {
DroolsSyntaxValidation droolsSyntaxValidation = rulesValidationService.validateRules(rulesUploadRequest.getRuleFileType(), rulesUploadRequest.getRules());
var rulesSyntaxWarningMessages = droolsSyntaxValidation.getDroolsSyntaxDeprecatedWarnings()
.stream()
.map(warningMessage -> RuleSyntaxWarningMessage.builder()
.line(warningMessage.getLine())
.column(warningMessage.getColumn())
.message(warningMessage.getMessage())
.build())
.collect(Collectors.toList());
droolsSyntaxValidationResponse.setRulesSyntaxWarningMessages(rulesSyntaxWarningMessages);
if (!droolsSyntaxValidation.isCompiled()) {
var rulesSyntaxErrorMessages = droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
.stream()
.map(errorMessage -> RuleSyntaxErrorMessage.builder()
.line(errorMessage.getLine())
.column(errorMessage.getColumn())
.message(errorMessage.getMessage())
.build())
.toList();
droolsSyntaxValidationResponse.setRulesSyntaxErrorMessages(rulesSyntaxErrorMessages);
if (!rules.isDryRun()) {
return new ResponseEntity<>(droolsSyntaxValidationResponse, HttpStatus.BAD_REQUEST);
}
}
DroolsValidationResponse droolsValidationResponse = new DroolsValidationResponse();
if (rules.isDryRun()) {
return ResponseEntity.ok(droolsSyntaxValidationResponse);
try {
var droolsValidation = rulesValidationService.validateRules(rulesUploadRequest.getRuleFileType(), rulesUploadRequest.getRules());
droolsValidationResponse = RulesValidationMapper.createFromDroolsValidation(droolsValidation);
if (!droolsValidation.isCompiled()) {
return new ResponseEntity<>(droolsValidationResponse, !rules.isDryRun() ? HttpStatus.UNPROCESSABLE_ENTITY : HttpStatus.OK);
}
} catch (FeignException e) {
if (e.status() == HttpStatus.BAD_REQUEST.value()) {
throw new BadRequestException("The provided rule string is not a valid drools rule file!");
}
}
rulesPersistenceService.setRules(rulesUploadRequest.getRules(), rulesUploadRequest.getDossierTemplateId(), rulesUploadRequest.getRuleFileType());
if (!rules.isDryRun()) {
rulesPersistenceService.setRules(rulesUploadRequest.getRules(), rulesUploadRequest.getDossierTemplateId(), rulesUploadRequest.getRuleFileType());
fileStatusPersistenceService.resetErrorCounter(rules.getDossierTemplateId());
}
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
@ -105,7 +87,7 @@ public class RulesController implements RulesResource {
.message(String.format("%s Rules have been updated", rulesUploadRequest.getRuleFileType()))
.build());
return ResponseEntity.ok(droolsSyntaxValidationResponse);
return new ResponseEntity<>(droolsValidationResponse, HttpStatus.OK);
}
@ -121,17 +103,20 @@ public class RulesController implements RulesResource {
@PreAuthorize("hasAuthority('" + READ_RULES + "')")
public RulesResponse download(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType) {
var ruleEntity = rulesPersistenceService.getRules(dossierTemplateId, ruleFileType);
return new RulesResponse(ruleEntity.getValue(), dossierTemplateId, ruleEntity.isTimeoutDetected());
var ruleEntityOptional = rulesPersistenceService.getRules(dossierTemplateId, ruleFileType);
if (ruleEntityOptional.isEmpty()) {
throw new NotFoundException(String.format("No rule file of type %s found for dossierTemplateId %s", ruleFileType, dossierTemplateId));
}
return new RulesResponse(ruleEntityOptional.get().getValue(), dossierTemplateId, ruleEntityOptional.get().isTimeoutDetected());
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
public ResponseEntity<DroolsSyntaxValidationResponse> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = DRY_RUN_PARAMETER) boolean dryRun,
@RequestPart(name = "file") MultipartFile file) {
public ResponseEntity<DroolsValidationResponse> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = DRY_RUN_PARAMETER) boolean dryRun,
@RequestPart(name = "file") MultipartFile file) {
return uploadFile(dossierTemplateId, RuleFileType.ENTITY, dryRun, file);
}
@ -139,10 +124,10 @@ public class RulesController implements RulesResource {
@Override
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
public ResponseEntity<DroolsSyntaxValidationResponse> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType,
@RequestParam(value = DRY_RUN_PARAMETER) boolean dryRun,
@RequestPart(name = "file") MultipartFile file) {
public ResponseEntity<DroolsValidationResponse> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType,
@RequestParam(value = DRY_RUN_PARAMETER) boolean dryRun,
@RequestPart(name = "file") MultipartFile file) {
try {
return upload(new RulesUploadRequestModel(new String(file.getBytes(), StandardCharsets.UTF_8), dossierTemplateId, ruleFileType, dryRun));
@ -175,4 +160,12 @@ public class RulesController implements RulesResource {
return new ResponseEntity<>(new InputStreamResource(is), httpHeaders, HttpStatus.OK);
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
public void unlockRules(String dossierTemplateId, RuleFileType ruleFileType) {
rulesPersistenceService.resetTimeoutDetected(dossierTemplateId, ruleFileType);
}
}

View File

@ -14,21 +14,29 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.acl.custom.dossier.DossierACLService;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
import com.iqser.red.service.persistence.management.v1.processor.roles.ApplicationRoles;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.ApprovalVerificationService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusMapper;
import com.iqser.red.service.persistence.management.v1.processor.service.FilterByPermissionsService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.users.UserService;
@ -38,12 +46,17 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AddNotificationRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.ProcessingStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.WorkflowStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.notification.NotificationType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.warning.ApproveResponse;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -64,6 +77,8 @@ public class StatusController implements StatusResource {
private final AccessControlService accessControlService;
private final NotificationPersistenceService notificationPersistenceService;
private final DossierACLService dossierACLService;
private final ApprovalVerificationService approvalVerificationService;
private final FilterByPermissionsService filterByPermissionsService;
@Override
@ -79,6 +94,28 @@ public class StatusController implements StatusResource {
}
@Override
@PreAuthorize("hasAuthority('" + READ_FILE_STATUS + "')")
public JSONPrimitive<Map<String, List<FileStatus>>> getFilesByIds(@RequestBody JSONPrimitive<Map<String, Set<String>>> filesByDossier) {
// filter dossiers by view
var accessibleDossierIds = filterByPermissionsService.onlyViewableDossierIds(new ArrayList<>(filesByDossier.getValue().keySet()));
var response = new HashMap<String, List<FileStatus>>();
for (var dossierId : accessibleDossierIds) {
var allFoundFiles = fileStatusManagementService.findAllDossierIdAndIds(dossierId,
filesByDossier.getValue()
.get(dossierId));
response.put(dossierId,
allFoundFiles.stream()
.map(FileStatusMapper::toFileStatus)
.collect(Collectors.toList()));
}
return new JSONPrimitive<>(response);
}
@Override
@PreAuthorize("hasAuthority('" + READ_FILE_STATUS + "')")
public Map<String, List<FileStatus>> getDossierStatus(@RequestBody List<String> dossierIds) {
@ -299,31 +336,44 @@ public class StatusController implements StatusResource {
@PreAuthorize("hasAuthority('" + SET_STATUS_APPROVED + "')")
public void setStatusApproved(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
public ApproveResponse setStatusApproved(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = FORCE_REQUEST_PARAM, required = false, defaultValue = "false") boolean force) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserIsApprover(dossierId);
setStatusApprovedForFile(dossierId, fileId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Document status was changed to Approved")
.details(Map.of(DOSSIER_ID, dossierId))
.build());
var dossier = dossierACLService.enhanceDossierWithACLData(dossierManagementService.getDossierById(dossierId, false, false));
if (!dossier.getOwnerId().equals(KeycloakSecurity.getUserId())) {
var fileStatus = fileStatusManagementService.getFileStatus(fileId);
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(dossier.getOwnerId())
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.DOCUMENT_APPROVED.name())
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
.build());
ApproveResponse approveResponse = new ApproveResponse(fileId, false, new ArrayList<>());
if (!force) {
approveResponse = approvalVerificationService.verifyApprovalOfFile(dossierId, fileId);
}
if (!approveResponse.isHasWarnings()) {
setStatusApprovedForFile(dossierId, fileId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Document status was changed to Approved")
.details(Map.of(DOSSIER_ID, dossierId))
.build());
var dossier = dossierACLService.enhanceDossierWithACLData(dossierManagementService.getDossierById(dossierId, false, false));
if (dossier.getOwnerId() == null) {
throw new ConflictException("Dossier has no owner!");
}
if (!dossier.getOwnerId().equals(KeycloakSecurity.getUserId())) {
var fileStatus = fileStatusManagementService.getFileStatus(fileId);
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(dossier.getOwnerId())
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.DOCUMENT_APPROVED.name())
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
.build());
}
}
return approveResponse;
}
@ -357,8 +407,10 @@ public class StatusController implements StatusResource {
private void generatePossibleUnassignedFromFileNotification(String dossierId, String fileId, FileModel oldFileStatus, String newAssigneeId) {
if (oldFileStatus.getAssignee() == null || newAssigneeId == null || oldFileStatus.getAssignee().equals(newAssigneeId) || KeycloakSecurity.getUserId()
.equals(oldFileStatus.getAssignee())) {
if (oldFileStatus.getAssignee() == null
|| newAssigneeId == null && oldFileStatus.getAssignee() == null
|| oldFileStatus.getAssignee().equals(newAssigneeId)
|| KeycloakSecurity.getUserId().equals(oldFileStatus.getAssignee())) {
return;
}
@ -398,14 +450,22 @@ public class StatusController implements StatusResource {
@Override
@PreAuthorize("hasAuthority('" + SET_STATUS_APPROVED + "')")
public void setStatusApprovedForList(String dossierId, List<String> fileIds) {
public List<ApproveResponse> setStatusApprovedForList(String dossierId,
List<String> fileIds,
@RequestParam(value = FORCE_REQUEST_PARAM, required = false, defaultValue = "false") boolean force) {
List<ApproveResponse> approveResponses = new ArrayList<>();
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserIsApprover(dossierId);
dossierManagementService.getDossierById(dossierId, false, false);
fileIds.forEach(fileId -> setStatusApproved(dossierId, fileId));
if (fileIds.size() > 50) {
throw new BadRequestException("Maximum amount of files that can be approved at once is 50.");
}
fileIds.forEach(fileId -> approveResponses.add(setStatusApproved(dossierId, fileId, force)));
return approveResponses;
}

View File

@ -0,0 +1,176 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.IMPORT_FILES;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.USE_SUPPORT_CONTROLLER;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.DatasetExchangeService;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.FileExchangeImportService;
import com.iqser.red.service.persistence.management.v1.processor.migration.MigrationController;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusMapper;
import com.iqser.red.service.persistence.management.v1.processor.service.ReanalysisService;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.FileExchangeExportService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ImportResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ReanalysisSettings;
import com.iqser.red.service.persistence.service.v1.api.external.resource.SupportResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DownloadResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatusFilter;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileExchangeExportRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ReanalyzeFilesResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
@PreAuthorize("hasAuthority('" + USE_SUPPORT_CONTROLLER + "')")
public class SupportController implements SupportResource {
private final ReanalysisService reanalysisService;
private final FileStatusManagementService fileStatusManagementService;
private final FileExchangeExportService fileExchangeExportService;
private final FileExchangeImportService fileExchangeImportService;
private final DatasetExchangeService datasetExchangeService;
@Override
public ReanalyzeFilesResponse reanalyzeFiles(String dossierTemplateId, ReanalysisSettings reanalysisSettings) {
return new ReanalyzeFilesResponse(reanalysisService.reanalyzeTemplate(dossierTemplateId, reanalysisSettings));
}
@Override
public void reanalyzeAllRelevantErrorFiles(@RequestParam(value = FULL_REANALYSIS_PARAM, required = false, defaultValue = FALSE) boolean repeatStructureAnalysis,
@RequestParam(value = RUN_OCR_PARAM, required = false, defaultValue = FALSE) boolean runOcr) {
reanalysisService.reanalyzeAllRelevantErrorFiles(repeatStructureAnalysis, runOcr);
}
@Override
public void reanalyzeErrorFilesBulkForDossier(@PathVariable(DOSSIER_ID) String dossierId,
@RequestBody List<String> fileIds,
@RequestParam(value = FULL_REANALYSIS_PARAM, required = false, defaultValue = FALSE) boolean repeatStructureAnalysis,
@RequestParam(value = RUN_OCR_PARAM, required = false, defaultValue = FALSE) boolean runOcr) {
reanalysisService.reanalyzeGivenErrorFilesInDossier(dossierId, new HashSet<>(fileIds), repeatStructureAnalysis, runOcr);
}
private static Predicate<FileModel> matchesStatusFilters(FileStatusFilter fileStatusFilter) {
FileStatusFilter filter = Optional.ofNullable(fileStatusFilter)
.orElseGet(FileStatusFilter::new);
if (filter.getProcessingStatusList() == null) {
filter.setProcessingStatusList(new ArrayList<>());
}
if (filter.getWorkflowStatusList() == null) {
filter.setWorkflowStatusList(new ArrayList<>());
}
return fileStatus -> (filter.getProcessingStatusList().isEmpty() || filter.getProcessingStatusList().contains(fileStatus.getProcessingStatus()))
&& (filter.getWorkflowStatusList().isEmpty() || filter.getWorkflowStatusList().contains(fileStatus.getWorkflowStatus()))
&& (filter.isIncludeSoftDeletedFiles() || fileStatus.getDeleted() == null)
&& (filter.isIncludeHardDeletedFiles() || fileStatus.getHardDeletedTime() == null);
}
@Override
public List<FileStatus> getFileStatus(@RequestBody FileStatusFilter fileStatusFilter) {
return fileStatusManagementService.getAllFileStatuses()
.stream()
.filter(matchesStatusFilters(fileStatusFilter))
.map(FileStatusMapper::toFileStatus)
.collect(Collectors.toList());
}
@Override
public List<FileStatus> getFileStatusForDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody FileStatusFilter fileStatusFilter) {
return fileStatusManagementService.getAllDossierTemplateStatus(dossierTemplateId)
.stream()
.filter(matchesStatusFilters(fileStatusFilter))
.map(FileStatusMapper::toFileStatus)
.collect(Collectors.toList());
}
@Override
public List<FileStatus> exportFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody FileStatusFilter fileStatusFilter) {
return fileStatusManagementService.getAllDossierStatus(dossierId)
.stream()
.filter(matchesStatusFilters(fileStatusFilter))
.map(FileStatusMapper::toFileStatus)
.collect(Collectors.toList());
}
@Override
public DownloadResponse exportDataset(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {
return datasetExchangeService.prepareExport(dossierTemplateId);
}
@Override
public DownloadResponse exportFiles(String dossierTemplateId, FileExchangeExportRequest exportRequest) {
return fileExchangeExportService.prepareExportDownload(dossierTemplateId, exportRequest);
}
@Override
@PreAuthorize("hasAuthority('" + IMPORT_FILES + "')")
public ImportResponse importFiles(MultipartFile file) {
byte[] bytes;
try {
bytes = file.getBytes();
} catch (IOException e) {
throw new BadRequestException("File could not be read and is likely corrupted.", e);
}
return fileExchangeImportService.importFileExchangeArchive(KeycloakSecurity.getUserId(), bytes);
}
@Override
@PreAuthorize("hasAuthority('" + IMPORT_FILES + "')")
public ImportResponse importDataset(MultipartFile file) {
byte[] bytes;
try {
bytes = file.getBytes();
} catch (IOException e) {
throw new BadRequestException("File could not be read and is likely corrupted.", e);
}
return datasetExchangeService.importDataset(KeycloakSecurity.getUserId(), bytes);
}
}

View File

@ -23,12 +23,11 @@ import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.service.persistence.management.v1.processor.service.FileFormatValidationService;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.iqser.red.service.pdftron.redaction.v1.api.model.ByteContentDocument;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileFormatValidationService;
import com.iqser.red.service.persistence.management.v1.processor.service.ReanalysisService;
import com.iqser.red.service.persistence.management.v1.processor.service.UploadService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
@ -37,10 +36,12 @@ import com.iqser.red.service.persistence.service.v1.api.external.resource.Upload
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileUploadResult;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.knecon.fforesight.tenantcommons.TenantContext;
import feign.FeignException;
import io.micrometer.core.annotation.Timed;
import io.swagger.v3.oas.annotations.Parameter;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
@ -52,9 +53,9 @@ import lombok.extern.slf4j.Slf4j;
@SuppressWarnings("PMD")
public class UploadController implements UploadResource {
private static final int THRESHOLD_ENTRIES = 10000;
private static final int THRESHOLD_SIZE = 1000000000; // 1 GB
private static final double THRESHOLD_RATIO = 10;
private static final int THRESHOLD_ENTRIES = 10000; // Maximum number of files allowed
private static final int THRESHOLD_SIZE = 1000000000; // 1 GB total unzipped data
private static final double THRESHOLD_RATIO = 10; // Max allowed compression ratio
private final UploadService uploadService;
private final ReanalysisService reanalysisService;
@ -67,34 +68,29 @@ public class UploadController implements UploadResource {
@Override
public FileUploadResult upload(@RequestPart(name = "file") MultipartFile file,
@PathVariable(DOSSIER_ID) String dossierId,
@RequestParam(value = "keepManualRedactions", required = false, defaultValue = "false") boolean keepManualRedactions) {
@RequestParam(value = "keepManualRedactions", required = false, defaultValue = "false") boolean keepManualRedactions,
@Parameter(name = DISABLE_AUTOMATIC_ANALYSIS_PARAM, description = "Disables automatic redaction for the uploaded file, imports only imported redactions") @RequestParam(value = DISABLE_AUTOMATIC_ANALYSIS_PARAM, required = false, defaultValue = "false") boolean disableAutomaticAnalysis) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
if (file.getOriginalFilename() == null) {
String originalFilename = file.getOriginalFilename();
if (originalFilename == null) {
throw new BadRequestException("Could not upload file, no filename provided.");
}
var extension = getExtension(file.getOriginalFilename());
String extension = getExtension(originalFilename);
try {
switch (extension) {
case "zip":
return handleZip(dossierId, file.getBytes(), keepManualRedactions);
case "csv":
return uploadService.importCsv(dossierId, file.getBytes());
default:
if (!fileFormatValidationService.getAllFileFormats().contains(extension)) {
throw new BadRequestException("Invalid file uploaded");
}
if (!fileFormatValidationService.getValidFileFormatsForTenant(TenantContext.getTenantId()).contains(extension)) {
throw new NotAllowedException("Insufficient permissions");
}
return uploadService.processSingleFile(dossierId, file.getOriginalFilename(), file.getBytes(), keepManualRedactions);
}
return switch (extension) {
case "zip" -> handleZip(dossierId, file.getBytes(), keepManualRedactions, disableAutomaticAnalysis);
case "csv" -> uploadService.importCsv(dossierId, file.getBytes());
default -> {
validateExtensionOrThrow(extension);
yield uploadService.processSingleFile(dossierId, originalFilename, file.getBytes(), keepManualRedactions, disableAutomaticAnalysis);
}
};
} catch (IOException e) {
throw new BadRequestException(e.getMessage(), e);
throw new BadRequestException("Failed to process file: " + e.getMessage(), e);
}
}
@ -109,7 +105,6 @@ public class UploadController implements UploadResource {
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
try {
reanalysisService.importRedactions(ByteContentDocument.builder().dossierId(dossierId).fileId(fileId).document(file.getBytes()).pages(pageInclusionRequest).build());
auditPersistenceService.audit(AuditRequest.builder()
@ -120,84 +115,116 @@ public class UploadController implements UploadResource {
.details(Map.of("dossierId", dossierId))
.build());
} catch (IOException e) {
throw new BadRequestException(e.getMessage(), e);
throw new BadRequestException("Failed to import redactions: " + e.getMessage(), e);
} catch (FeignException e) {
throw processFeignException(e);
}
}
private String getExtension(String fileName) {
private void validateExtensionOrThrow(String extension) {
return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase(Locale.ROOT);
if (!fileFormatValidationService.getAllFileFormats().contains(extension)) {
throw new BadRequestException("Invalid file uploaded (unrecognized extension).");
}
if (!fileFormatValidationService.getValidFileFormatsForTenant(TenantContext.getTenantId()).contains(extension)) {
throw new NotAllowedException("Insufficient permissions for this file type.");
}
}
private FileUploadResult handleZip(String dossierId, byte[] fileContent, boolean keepManualRedactions) throws IOException {
/**
* 1. Write the uploaded content to a temp ZIP file
* 2. Check the number of entries and reject if too big or if symlinks found
* 3. Unzip and process each file, while checking size and ratio.
*/
private FileUploadResult handleZip(String dossierId, byte[] fileContent, boolean keepManualRedactions, boolean disableAutomaticAnalysis) throws IOException {
File tempFile = FileUtils.createTempFile(UUID.randomUUID().toString(), ".zip");
try (var fileOutputStream = new FileOutputStream(tempFile)) {
IOUtils.write(fileContent, fileOutputStream);
File tempZip = FileUtils.createTempFile(UUID.randomUUID().toString(), ".zip");
try (FileOutputStream fos = new FileOutputStream(tempZip)) {
IOUtils.write(fileContent, fos);
}
try {
checkForSymlinks(tempFile);
validateZipEntries(tempZip);
var zipData = unzip(tempFile, dossierId, keepManualRedactions);
try {
ZipData zipData = processZipContents(tempZip, dossierId, keepManualRedactions, disableAutomaticAnalysis);
if (zipData.csvBytes != null) {
try {
var importResult = uploadService.importCsv(dossierId, zipData.csvBytes);
zipData.fileUploadResult.getProcessedAttributes().addAll(importResult.getProcessedAttributes());
zipData.fileUploadResult.getProcessedFileIds().addAll(importResult.getProcessedFileIds());
FileUploadResult csvResult = uploadService.importCsv(dossierId, zipData.csvBytes);
zipData.fileUploadResult.getProcessedAttributes().addAll(csvResult.getProcessedAttributes());
zipData.fileUploadResult.getProcessedFileIds().addAll(csvResult.getProcessedFileIds());
} catch (Exception e) {
log.debug("CSV file inside ZIP failed", e);
// TODO return un-processed files to client
log.debug("CSV file inside ZIP failed to import", e);
}
} else if (zipData.fileUploadResult.getFileIds().isEmpty()) {
if (zipData.containedUnpermittedFiles) {
throw new NotAllowedException("Zip file contains unpermitted files");
throw new NotAllowedException("Zip file contains unpermitted files.");
} else {
throw new BadRequestException("Only unsupported files in zip file");
throw new BadRequestException("Only unsupported files in the ZIP.");
}
}
return zipData.fileUploadResult;
} finally {
boolean isDeleted = tempFile.delete();
if (!isDeleted) {
log.warn("tempFile could not be deleted");
if (!tempZip.delete()) {
log.warn("Could not delete temporary ZIP file: {}", tempZip);
}
}
}
private void checkForSymlinks(File tempFile) throws IOException {
private void validateZipEntries(File tempZip) throws IOException {
try (FileInputStream fis = new FileInputStream(tempZip); ZipFile zipFile = new ZipFile(fis.getChannel())) {
int count = 0;
var entries = zipFile.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry ze = entries.nextElement();
try (var fis = new FileInputStream(tempFile); var zipFile = new ZipFile(fis.getChannel())) {
for (var entryEnum = zipFile.getEntries(); entryEnum.hasMoreElements(); ) {
var ze = entryEnum.nextElement();
if (ze.isUnixSymlink()) {
throw new BadRequestException("ZIP-files with symlinks are not allowed");
throw new BadRequestException("ZIP-files with symlinks are not allowed.");
}
if (!ze.isDirectory() && !ze.getName().startsWith(".")) {
count++;
if (count > THRESHOLD_ENTRIES) {
throw new BadRequestException("ZIP-Bomb detected: too many entries.");
}
}
}
}
}
private ZipData unzip(File tempFile, String dossierId, boolean keepManualRedactions) throws IOException {
private ZipData processZipContents(File tempZip, String dossierId, boolean keepManualRedactions, boolean disableAutomaticAnalysis) throws IOException {
var zipData = new ZipData();
ZipData zipData = new ZipData();
try (var fis = new FileInputStream(tempFile); var zipFile = new ZipFile(fis.getChannel())) {
try (FileInputStream fis = new FileInputStream(tempZip); ZipFile zipFile = new ZipFile(fis.getChannel())) {
for (var entryEnum = zipFile.getEntries(); entryEnum.hasMoreElements(); ) {
var ze = entryEnum.nextElement();
zipData.totalEntryArchive++;
var entries = zipFile.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
if (!ze.isDirectory()) {
processFileZipEntry(ze, zipFile, dossierId, keepManualRedactions, zipData);
if (entry.isDirectory() || entry.getName().startsWith(".")) {
continue;
}
byte[] entryBytes = readEntryWithRatioCheck(entry, zipFile);
zipData.totalSizeArchive += entryBytes.length;
if (zipData.totalSizeArchive > THRESHOLD_SIZE) {
throw new BadRequestException("ZIP-Bomb detected (exceeds total size limit).");
}
String extension = getExtension(entry.getName());
if ("csv".equalsIgnoreCase(extension)) {
zipData.csvBytes = entryBytes;
} else {
handleRegularFile(dossierId, entryBytes, extension, extractFileName(entry.getName()), zipData, keepManualRedactions, disableAutomaticAnalysis);
}
}
}
@ -205,73 +232,70 @@ public class UploadController implements UploadResource {
}
private void processFileZipEntry(ZipArchiveEntry ze, ZipFile zipFile, String dossierId, boolean keepManualRedactions, ZipData zipData) throws IOException {
private byte[] readEntryWithRatioCheck(ZipArchiveEntry entry, ZipFile zipFile) throws IOException {
var extension = getExtension(ze.getName());
long compressedSize = entry.getCompressedSize() > 0 ? entry.getCompressedSize() : 1;
try (var is = zipFile.getInputStream(entry); var bos = new ByteArrayOutputStream()) {
final String fileName;
if (ze.getName().lastIndexOf("/") >= 0) {
fileName = ze.getName().substring(ze.getName().lastIndexOf("/") + 1);
} else {
fileName = ze.getName();
}
byte[] buffer = new byte[4096];
int bytesRead;
int totalUncompressed = 0;
if (fileName.startsWith(".")) {
return;
}
while ((bytesRead = is.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
totalUncompressed += bytesRead;
var entryAsBytes = readCurrentZipEntry(ze, zipFile);
zipData.totalSizeArchive += entryAsBytes.length;
// 1. the uncompressed data size is too much for the application resource capacity
// 2. too many entries in the archive can lead to inode exhaustion of the file-system
if (zipData.totalSizeArchive > THRESHOLD_SIZE || zipData.totalEntryArchive > THRESHOLD_ENTRIES) {
throw new BadRequestException("ZIP-Bomb detected.");
}
if ("csv".equals(extension)) {
zipData.csvBytes = entryAsBytes;
} else if (fileFormatValidationService.getAllFileFormats().contains(extension)) {
if (!fileFormatValidationService.getValidFileFormatsForTenant(TenantContext.getTenantId()).contains(extension)) {
zipData.containedUnpermittedFiles = true;
return;
}
zipData.containedUnpermittedFiles = false;
try {
var result = uploadService.processSingleFile(dossierId, fileName, entryAsBytes, keepManualRedactions);
zipData.fileUploadResult.getFileIds().addAll(result.getFileIds());
} catch (Exception e) {
log.debug("PDF File inside ZIP failed", e);
// TODO return un-processed files to client
double ratio = (double) totalUncompressed / compressedSize;
if (ratio > THRESHOLD_RATIO) {
throw new BadRequestException("ZIP-Bomb detected (compression ratio too high).");
}
}
return bos.toByteArray();
}
}
private byte[] readCurrentZipEntry(ZipArchiveEntry ze, ZipFile zipFile) throws IOException {
private void handleRegularFile(String dossierId,
byte[] fileBytes,
String extension,
String fileName,
ZipData zipData,
boolean keepManualRedactions,
boolean disableAutomaticAnalysis) {
var bos = new ByteArrayOutputStream();
try (var entryStream = zipFile.getInputStream(ze)) {
var buffer = new byte[2048];
var nBytes = 0;
int totalSizeEntry = 0;
while ((nBytes = entryStream.read(buffer)) > 0) {
bos.write(buffer, 0, nBytes);
totalSizeEntry += nBytes;
double compressionRatio = (float) totalSizeEntry / ze.getCompressedSize();
if (compressionRatio > THRESHOLD_RATIO) {
// ratio between compressed and uncompressed data is highly suspicious, looks like a Zip Bomb Attack
throw new BadRequestException("ZIP-Bomb detected.");
}
}
if (!fileFormatValidationService.getAllFileFormats().contains(extension)) {
zipData.containedUnpermittedFiles = false;
return;
}
return bos.toByteArray();
if (!fileFormatValidationService.getValidFileFormatsForTenant(TenantContext.getTenantId()).contains(extension)) {
zipData.containedUnpermittedFiles = true;
return;
}
try {
FileUploadResult result = uploadService.processSingleFile(dossierId, fileName, fileBytes, keepManualRedactions, disableAutomaticAnalysis);
zipData.fileUploadResult.getFileIds().addAll(result.getFileIds());
} catch (Exception e) {
log.debug("Failed to process file '{}' in ZIP: {}", fileName, e.getMessage(), e);
}
}
private String extractFileName(String path) {
int idx = path.lastIndexOf('/');
return (idx >= 0) ? path.substring(idx + 1) : path;
}
private String getExtension(String fileName) {
int idx = fileName.lastIndexOf('.');
if (idx < 0) {
return "";
}
return fileName.substring(idx + 1).toLowerCase(Locale.ROOT);
}
@ -280,7 +304,6 @@ public class UploadController implements UploadResource {
byte[] csvBytes;
int totalSizeArchive;
int totalEntryArchive;
FileUploadResult fileUploadResult = new FileUploadResult();
boolean containedUnpermittedFiles;

View File

@ -0,0 +1,60 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_USER_STATS;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.acl.custom.dossier.DossierACLService;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.users.UserService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.UserStatsResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.UserStats;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
public class UserStatsController implements UserStatsResource {
private final UserService userService;
private final DossierService dossierService;
private final FileStatusPersistenceService fileStatusPersistenceService;
private final DossierACLService dossierACLService;
@Override
@PreAuthorize("hasAuthority('" + READ_USER_STATS + "')")
public ResponseEntity<UserStats> getUserStats(String userId) {
if (userService.getUserById(userId).isEmpty()) {
throw new NotFoundException(String.format("The user with id %s is not found.", userId));
}
List<String> dossierMemberships = new ArrayList<>();
List<String> dossierOwnerships = new ArrayList<>();
dossierService.getAllDossiers()
.stream()
.filter(dossierEntity -> dossierEntity.getHardDeletedTime() == null)
.forEach(d -> {
if (dossierACLService.getMembers(d.getId()).contains(userId)) {
dossierMemberships.add(d.getId());
}
if (dossierACLService.getOwners(d.getId()).contains(userId)) {
dossierOwnerships.add(d.getId());
}
});
return new ResponseEntity<>(new UserStats(dossierMemberships.size(), dossierOwnerships.size(), this.fileStatusPersistenceService.getNumberOfAssignedFiles(userId)),
HttpStatus.OK);
}
}

View File

@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.LegalBasisMappingPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.VersionsResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
@ -27,6 +28,7 @@ public class VersionsController implements VersionsResource {
private final DictionaryPersistenceService dictionaryPersistenceService;
private final RulesPersistenceService rulesPersistenceService;
private final AccessControlService accessControlService;
private final LegalBasisMappingPersistenceService legalBasisMappingPersistenceService;
@Override
@ -35,7 +37,9 @@ public class VersionsController implements VersionsResource {
var result = new HashMap<String, VersionsResponse>();
dossierTemplateIds.forEach(rsId -> {
VersionsResponse response = new VersionsResponse(dictionaryPersistenceService.getVersion(rsId), rulesPersistenceService.getVersion(rsId, RuleFileType.ENTITY));
VersionsResponse response = new VersionsResponse(dictionaryPersistenceService.getVersion(rsId),
rulesPersistenceService.getVersion(rsId, RuleFileType.ENTITY),
legalBasisMappingPersistenceService.getVersion(rsId));
result.put(rsId, response);
});

View File

@ -1,12 +1,15 @@
plugins {
id("com.iqser.red.service.java-conventions")
id("io.freefair.lombok") version "8.4"
id("io.freefair.lombok") version "8.6"
}
dependencies {
api(project(":persistence-service-processor-v1"))
api(project(":persistence-service-external-api-v2"))
api(project(":persistence-service-external-api-impl-v1"))
implementation("org.mapstruct:mapstruct:1.5.5.Final")
annotationProcessor("org.mapstruct:mapstruct-processor:1.5.5.Final")
}
description = "persistence-service-external-api-impl-v2"

View File

@ -1,31 +1,49 @@
package com.iqser.red.persistence.service.v2.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_RSS;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierResource.DOSSIER_ID_PARAM;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.DOSSIER_TEMPLATE_ID_PARAM;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.FileResource.FILE_ID_PARAM;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierTemplateController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.StatusController;
import com.iqser.red.persistence.service.v2.external.api.impl.mapper.ComponentMapper;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
import com.iqser.red.service.persistence.management.v1.processor.roles.ApplicationRoles;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentLogService;
import com.iqser.red.service.persistence.management.v1.processor.service.CurrentApplicationTypeProvider;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntityReference;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.users.UserService;
import com.iqser.red.service.persistence.management.v1.processor.service.users.model.User;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.iqser.red.service.persistence.service.v2.api.external.model.BulkComponentsRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.Component;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentValue;
import com.iqser.red.service.persistence.service.v2.api.external.model.EntityReference;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentOverrideList;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponents;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponentsList;
import com.iqser.red.service.persistence.service.v2.api.external.resource.ComponentResource;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.knecon.fforesight.tenantcommons.TenantProvider;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
@ -34,13 +52,21 @@ import lombok.experimental.FieldDefaults;
@RestController
@RequiredArgsConstructor
@Tag(name = "4. Component endpoints", description = "Provides operations related to components")
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class ComponentControllerV2 implements ComponentResource {
DossierTemplateController dossierTemplateController;
ComponentLogService componentLogService;
StatusController statusController;
FileStatusService fileStatusService;
private final AccessControlService accessControlService;
private final ComponentLogService componentLogService;
private final UserService userService;
private final StatusController statusController;
private final FileStatusService fileStatusService;
private final DossierController dossierController;
private final DossierTemplatePersistenceService dossierTemplatePersistenceService;
private final CurrentApplicationTypeProvider currentApplicationTypeProvider;
private final ComponentMapper componentMapper = ComponentMapper.INSTANCE;
@Value("${documine.components.filesLimit:100}")
private int documineComponentsFilesLimit = 100;
@Override
@ -49,68 +75,25 @@ public class ComponentControllerV2 implements ComponentResource {
@PathVariable(FILE_ID_PARAM) String fileId,
@RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
var componentLog = componentLogService.getComponentLog(dossierId, fileId, true);
checkApplicationType();
validateUserRoles(KeycloakSecurity.getUserId());
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
var componentLog = componentLogService.getComponentLog(dossierId, fileId);
Map<String, List<String>> basicComponent = new LinkedHashMap<>();
for (ComponentLogEntry componentLogEntry : componentLog.getComponentLogEntries()) {
basicComponent.put(componentLogEntry.getName(),
componentLogEntry.getComponentValues()
.stream()
.map(ComponentLogEntryValue::getValue)
.toList());
}
return componentMapper.toFileComponents(componentLog, dossierTemplateId, dossierId, fileId, fileStatusService.getFileName(fileId), includeDetails);
}
Map<String, Component> componentsDetails = new LinkedHashMap<>();
if (includeDetails) {
for (ComponentLogEntry entry : componentLog.getComponentLogEntries()) {
componentsDetails.put(entry.getName(), Component.builder().name(entry.getName()).componentValues(toComponentList(entry)).build());
private void validateUserRoles(String userId) {
Optional<User> userOptional = userService.getUserById(userId);
if (userOptional.isPresent()) {
if (userOptional.get().getRoles()
.stream()
.noneMatch(ApplicationRoles.VALID_MEMBER_ROLES::contains)) {
throw new NotAllowedException("User doesn't have appropriate roles");
}
}
return FileComponents.builder()
.dossierTemplateId(dossierTemplateId)
.dossierId(dossierId)
.filename(fileStatusService.getFileName(fileId))
.fileId(fileId)
.components(basicComponent)
.componentDetails(componentsDetails)
.build();
}
private List<ComponentValue> toComponentList(ComponentLogEntry componentLogEntry) {
return componentLogEntry.getComponentValues()
.stream()
.map(this::convert)
.toList();
}
private ComponentValue convert(ComponentLogEntryValue componentValue) {
return ComponentValue.builder()
.valueDescription(componentValue.getValueDescription())
.componentRuleId(componentValue.getComponentRuleId())
.entityReferences(componentValue.getComponentLogEntityReferences()
.stream()
.map(this::convertComponentEntityReference)
.toList())
.originalValue(componentValue.getOriginalValue())
.value(componentValue.getValue())
.build();
}
private EntityReference convertComponentEntityReference(ComponentLogEntityReference componentLogEntityReference) {
return EntityReference.builder()
.id(componentLogEntityReference.getId())
.entityRuleId(componentLogEntityReference.getEntityRuleId())
.type(componentLogEntityReference.getType())
.page(componentLogEntityReference.getPage())
.build();
}
@ -119,11 +102,101 @@ public class ComponentControllerV2 implements ComponentResource {
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
checkApplicationType();
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
var dossierFiles = statusController.getDossierStatus(dossierId);
if(dossierFiles.size() > documineComponentsFilesLimit) {
throw new BadRequestException(String.format("The dossier you requested components for contains %s files this is above the limit of %s files for this endpoint, please use the POST %s", dossierFiles.size(), documineComponentsFilesLimit, FILE_PATH + BULK_COMPONENTS_PATH));
}
return new FileComponentsList(dossierFiles.stream()
.map(file -> getComponents(dossierTemplateId, dossierId, file.getFileId(), includeDetails))
.toList());
}
@Override
public FileComponentsList getComponentsForFiles(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails,
@RequestBody BulkComponentsRequest bulkComponentsRequest){
if(bulkComponentsRequest.getFileIds().size() > documineComponentsFilesLimit) {
throw new BadRequestException(String.format("You requested components for %s files this is above the limit of %s files for this endpoint, lower the fileIds in the request", bulkComponentsRequest.getFileIds().size(), documineComponentsFilesLimit));
}
checkApplicationType();
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
dossierController.getDossier(dossierId, false, false);
return new FileComponentsList(bulkComponentsRequest.getFileIds().stream()
.map(fileId -> getComponents(dossierTemplateId, dossierId, fileId, includeDetails))
.toList());
}
@Override
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void addOverride(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@PathVariable(FILE_ID_PARAM) String fileId,
@RequestBody Component override) {
checkApplicationType();
accessControlService.verifyUserIsReviewer(dossierId, fileId);
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
componentLogService.overrideComponent(dossierId, fileId, componentMapper.toComponentLogEntry(override));
}
@Override
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public ComponentOverrideList getOverrides(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@PathVariable(FILE_ID_PARAM) String fileId) {
checkApplicationType();
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
FileModel status = fileStatusService.getStatus(fileId);
var overrides = componentLogService.getComponentLog(dossierId, fileId).getComponentLogEntries()
.stream()
.filter(ComponentLogEntry::isOverridden)
.collect(Collectors.toList());
var componentOverrides = componentMapper.toComponents(overrides);
return ComponentOverrideList.builder()
.dossierTemplateId(dossierTemplateId)
.dossierId(dossierId)
.fileId(fileId)
.componentOverrides(status.isSoftOrHardDeleted() ? new ArrayList<>() : componentOverrides)
.build();
}
@Override
@PreAuthorize("hasAuthority('" + GET_RSS + "')")
public void revertOverrides(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@PathVariable(FILE_ID_PARAM) String fileId,
@RequestBody RevertOverrideRequest revertOverrideRequest) {
checkApplicationType();
accessControlService.verifyUserIsReviewer(dossierId, fileId);
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
componentLogService.revertOverrides(dossierId, fileId, revertOverrideRequest);
}
private void checkApplicationType() {
if (!currentApplicationTypeProvider.isDocuMine()) {
throw new NotAllowedException("Components can only be accessed in DocuMine");
}
}
}

View File

@ -1,35 +1,67 @@
package com.iqser.red.persistence.service.v2.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_DOSSIER_ATTRIBUTES;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.DOSSIER_TEMPLATE_ID_PARAM;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.google.common.collect.Sets;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierTemplateController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DownloadController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.StatusController;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.CurrentApplicationTypeProvider;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierAttributesManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.PrepareDownloadWithOptionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttribute;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttributes;
import com.iqser.red.service.persistence.service.v2.api.external.model.DocuMineDossierRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierList;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatus;
import com.iqser.red.service.persistence.service.v2.api.external.resource.DossierResource;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
@RestController
@RequiredArgsConstructor
@Tag(name = "2. Dossier endpoints", description = "Provides operations related to dossiers")
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class DossierControllerV2 implements DossierResource {
private final DossierTemplateController dossierTemplateController;
private final DossierController dossierController;
DossierTemplateController dossierTemplateController;
DossierController dossierController;
AccessControlService accessControlService;
DossierAttributesManagementService dossierAttributesManagementService;
AuditPersistenceService auditPersistenceService;
DownloadController downloadController;
StatusController statusController;
DownloadStatusPersistenceService downloadStatusPersistenceService;
CurrentApplicationTypeProvider currentApplicationTypeProvider;
public DossierList getDossiers(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@ -71,15 +103,88 @@ public class DossierControllerV2 implements DossierResource {
public void deleteDossier(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier to retrieve.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId) {
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier to retrieve.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestParam(name = DELETE_PERMANENTLY_PARAM, defaultValue = "false", required = false) boolean deletePermanently) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
dossierController.deleteDossier(dossierId);
if (deletePermanently) {
dossierController.hardDeleteDossiers(Sets.newHashSet(dossierId));
} else {
dossierController.deleteDossier(dossierId);
}
}
private static DossierRequest mapToDossierRequest(String dossierTemplateId, DocuMineDossierRequest dossier) {
@PreAuthorize("hasAuthority('" + WRITE_DOSSIER_ATTRIBUTES + "')")
public void setDossierAttributes(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestBody DossierAttributes dossierAttributes) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserIsDossierOwner(dossierId);
var dossierAttributeList = dossierAttributes.getAttributeIdToValue().entrySet()
.stream()
.map(entry -> new DossierAttribute(dossierId, entry.getKey(), entry.getValue()))
.toList();
dossierAttributesManagementService.setDossierAttributes(dossierId, dossierAttributeList);
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Changed dossier attributes.")
.build());
}
public DownloadStatus prepareDossierDownload(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestBody DownloadRequest downloadRequest) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
var storageId = downloadController.prepareDownload(PrepareDownloadWithOptionRequest.builder()
.dossierId(dossierId)
.fileIds(statusController.getDossierStatus(dossierId)
.stream()
.map(FileStatus::getId)
.toList())
.reportTemplateIds(downloadRequest.getReportTemplateIds())
.downloadFileTypes(downloadRequest.getDownloadFileTypes())
.redactionPreviewColor(downloadRequest.getRedactionPreviewColor())
.includeUnprocessed(false)
.build()).getStorageId();
var status = downloadStatusPersistenceService.getStatus(storageId);
return DownloadStatus.builder()
.id(status.getUuid()) // This is a workaround the real is the storageId.
.userId(status.getUserId())
.filename(status.getFilename())
.mimeType(status.getMimeType())
.errorCause(status.getErrorCause())
.status(status.getStatus())
.creationDate(status.getCreationDate())
.lastDownload(status.getLastDownload())
.fileSize(status.getFileSize())
.dossierId(status.getDossier().getId())
.fileIds(status.getFiles()
.stream()
.map(FileEntity::getId)
.toList())
.downloadFileTypes(status.getDownloadFileTypes()
.stream()
.toList())
.reportTemplateIds(downloadRequest.getReportTemplateIds())
.build();
}
private DossierRequest mapToDossierRequest(String dossierTemplateId, DocuMineDossierRequest dossier) {
return DossierRequest.builder()
.dossierId(dossier.getId())
@ -88,7 +193,7 @@ public class DossierControllerV2 implements DossierResource {
.description(dossier.getDescription())
.ownerId(dossier.getOwnerId())
.memberIds(dossier.getMemberIds())
.approverIds(dossier.getMemberIds()) // for DocuMine, the members are always set as approvers
.approverIds(currentApplicationTypeProvider.isDocuMine() ? dossier.getMemberIds() : dossier.getApproverIds()) // for DocuMine, the members are always set as approvers
.downloadFileTypes(Set.of(DownloadFileType.ORIGINAL))
.reportTemplateIds(dossier.getReportTemplateIds())
.watermarkId(null)

View File

@ -1,15 +1,26 @@
package com.iqser.red.persistence.service.v2.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.EXPERIMENTAL;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_REPORT_TEMPLATES;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_DATA_FORMATS;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_DOSSIER_ATTRIBUTES_CONFIG;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_DOSSIER_STATUS;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_FILE_ATTRIBUTES_CONFIG;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_RULES;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_DATA_FORMATS;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_RULES;
import static java.lang.String.format;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
@ -23,45 +34,85 @@ import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.google.common.base.Strings;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierTemplateController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.FileAttributesController;
import com.iqser.red.persistence.service.v2.external.api.impl.mapper.ComponentMappingMapper;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.DateFormatsEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.RuleSetEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ComponentDefinitionEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.model.ComponentMappingDownloadModel;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentDefinitionService;
import com.iqser.red.service.persistence.management.v1.processor.service.ComponentMappingService;
import com.iqser.red.service.persistence.management.v1.processor.service.DateFormatsValidationService;
import com.iqser.red.service.persistence.management.v1.processor.service.RulesValidationService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DateFormatsPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ReportTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.utils.RulesValidationMapper;
import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinition;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinitionAddRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinitionUpdateRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DateFormatPatternErrorMessage;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsValidationResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUploadRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingMetadataModel;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary;
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierAttributeDefinition;
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierAttributeDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierStatusDefinition;
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierStatusDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinition;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.RulesValidationMessage;
import com.iqser.red.service.persistence.service.v2.api.external.model.RulesValidationResponse;
import com.iqser.red.service.persistence.service.v2.api.external.model.ReportTemplate;
import com.iqser.red.service.persistence.service.v2.api.external.model.ReportTemplateList;
import com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource;
import com.iqser.red.service.redaction.v1.model.DroolsSyntaxValidation;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import feign.FeignException;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;
@RestController
@RequiredArgsConstructor
@Tag(name = "1. Dossier templates endpoints", description = "Provides operations related to dossier templates")
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class DossierTemplateControllerV2 implements DossierTemplateResource {
private static final String RULES_DOWNLOAD_FILE_NAME_SUFFIX = "-rules.drl";
private static final String DATE_FORMAT_FILE_NAME = "date_formats.txt";
private final DossierTemplateController dossierTemplateController;
private final RulesPersistenceService rulesPersistenceService;
private final RulesValidationService rulesValidationService;
private final AuditPersistenceService auditPersistenceService;
private final FileAttributesController fileAttributesController;
DossierTemplateController dossierTemplateController;
RulesPersistenceService rulesPersistenceService;
DateFormatsPersistenceService dateFormatsPersistenceService;
RulesValidationService rulesValidationService;
DateFormatsValidationService dateFormatsValidationService;
AuditPersistenceService auditPersistenceService;
FileAttributesController fileAttributesController;
ComponentMappingService componentMappingService;
ComponentMappingMapper componentMappingMapper = ComponentMappingMapper.INSTANCE;
DossierTemplatePersistenceService dossierTemplatePersistenceService;
DossierStatusPersistenceService dossierStatusPersistenceService;
DossierAttributeConfigPersistenceService dossierAttributeConfigPersistenceService;
ComponentDefinitionService componentDefinitionService;
ReportTemplatePersistenceService reportTemplatePersistenceService;
public List<DossierTemplateModel> getAllDossierTemplates() {
@ -76,13 +127,12 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource {
}
@PreAuthorize("hasAuthority('" + WRITE_RULES + "') and hasAuthority('" + EXPERIMENTAL + "')")
public ResponseEntity<RulesValidationResponse> uploadEntityRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun) {
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
public ResponseEntity<DroolsValidationResponse> uploadEntityRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun) {
// Try to get dossier template to return 404 if it does not exist
getDossierTemplate(dossierTemplateId);
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
return uploadRules(dossierTemplateId, RuleFileType.ENTITY, file, dryRun);
}
@ -91,20 +141,18 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource {
@PreAuthorize("hasAuthority('" + READ_RULES + "')")
public ResponseEntity<?> downloadEntityRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) {
// Try to get dossier template to return 404 if it does not exist
getDossierTemplate(dossierTemplateId);
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
return downloadRules(dossierTemplateId, RuleFileType.ENTITY);
}
@PreAuthorize("hasAuthority('" + WRITE_RULES + "') and hasAuthority('" + EXPERIMENTAL + "')")
public ResponseEntity<RulesValidationResponse> uploadComponentRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun) {
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
public ResponseEntity<DroolsValidationResponse> uploadComponentRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun) {
// Try to get dossier template to return 404 if it does not exist
getDossierTemplate(dossierTemplateId);
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
return uploadRules(dossierTemplateId, RuleFileType.COMPONENT, file, dryRun);
}
@ -113,18 +161,69 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource {
@PreAuthorize("hasAuthority('" + READ_RULES + "')")
public ResponseEntity<?> downloadComponentRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) {
// Try to get dossier template to return 404 if it does not exist
getDossierTemplate(dossierTemplateId);
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
return downloadRules(dossierTemplateId, RuleFileType.COMPONENT);
}
@SneakyThrows
@PreAuthorize("hasAuthority('" + WRITE_DATA_FORMATS + "')")
public ResponseEntity<?> uploadDateFormats(String dossierTemplateId, MultipartFile file) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
String originalFilename = file.getOriginalFilename();
if (originalFilename == null || !originalFilename.endsWith(".txt")) {
throw new BadRequestException("Only .txt files can be parsed.");
}
String dateFormats = new String(file.getBytes(), StandardCharsets.UTF_8);
List<DateFormatPatternErrorMessage> dateFormatPatternErrorMessages = dateFormatsValidationService.validateDateFormats(dateFormats);
if (!dateFormatPatternErrorMessages.isEmpty()) {
return new ResponseEntity<>(dateFormatPatternErrorMessages, HttpStatus.UNPROCESSABLE_ENTITY);
}
dateFormatsPersistenceService.setDateFormats(dateFormats, dossierTemplateId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("date formats have been updated")
.build());
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@SneakyThrows
@PreAuthorize("hasAuthority('" + READ_DATA_FORMATS + "')")
public ResponseEntity<?> downloadDateFormats(String dossierTemplateId) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
Optional<DateFormatsEntity> dateFormatsOptional = dateFormatsPersistenceService.getDateFormats(dossierTemplateId);
if (dateFormatsOptional.isEmpty()) {
throw new NotFoundException(String.format("No date formats found for dossierTemplateId %s", dossierTemplateId));
}
var data = dateFormatsOptional.get().getValue().getBytes(StandardCharsets.UTF_8);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
httpHeaders.add("Content-Disposition", "attachment" + "; filename*=utf-8''" + StringEncodingUtils.urlEncode(DATE_FORMAT_FILE_NAME));
InputStream is = new ByteArrayInputStream(data);
return new ResponseEntity<>(new InputStreamResource(is), httpHeaders, HttpStatus.OK);
}
@PreAuthorize("hasAuthority('" + READ_FILE_ATTRIBUTES_CONFIG + "')")
public FileAttributeDefinitionList getFileAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) {
// Try to get dossier template to return 404 if it does not exist
getDossierTemplate(dossierTemplateId);
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
var fileAttributeConfigs = fileAttributesController.getFileAttributesConfiguration(dossierTemplateId);
@ -148,6 +247,7 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource {
.filterable(fileAttributeConfig.isFilterable())
.displayedInFileList(fileAttributeConfig.isDisplayedInFileList())
.build())
.includeInCsvExport(fileAttributeConfig.isIncludeInCsvExport())
.build())
.toList();
@ -155,28 +255,383 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource {
}
@Override
@PreAuthorize("hasAuthority('" + READ_RULES + "')")
public ComponentMappingSummary getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
List<com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata> summaries = componentMappingService.getMetaDataByDossierTemplateId(
dossierTemplateId);
List<ComponentMappingMetadataModel> componentMappingMetadataModelList = componentMappingMapper.toModelList(summaries);
return new ComponentMappingSummary(dossierTemplateId, componentMappingMetadataModelList);
}
@Override
@SneakyThrows
private ResponseEntity<RulesValidationResponse> uploadRules(String dossierTemplateId, RuleFileType ruleFileType, MultipartFile file, boolean dryRun) {
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
public ComponentMappingMetadataModel uploadMapping(String dossierTemplateId, MultipartFile file, String name, String encoding, String delimiter, String quoteChar) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
if (Strings.isNullOrEmpty(file.getOriginalFilename()) || !file.getOriginalFilename().endsWith(".csv")) {
throw new BadRequestException(format("File name \"%s\" does not end with .csv", file.getOriginalFilename()));
}
String fileName = file.getOriginalFilename();
String nameToUse = Strings.isNullOrEmpty(name) ? fileName.replaceAll(".csv$", "") : name;
if (Strings.isNullOrEmpty(nameToUse)) {
throw new BadRequestException(format("The provided file name \"%s\" is not valid!", nameToUse));
}
char cleanDelimiter = getDelimiter(delimiter);
char cleanQuoteChar = getQuoteChar(quoteChar);
Path mappingFile = saveToFile(file);
try {
ComponentMappingMetadata metaData = componentMappingService.create(dossierTemplateId,
nameToUse,
fileName,
cleanDelimiter,
encoding,
mappingFile.toFile(),
cleanQuoteChar);
return componentMappingMapper.toModel(metaData);
} finally {
Files.deleteIfExists(mappingFile);
}
}
@Override
@SneakyThrows
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
public ComponentMappingMetadataModel updateMapping(String dossierTemplateId,
String componentMappingId,
MultipartFile file,
String name,
String encoding,
String delimiter,
String quoteChar) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
String nameToUse = validateFileName(file, name);
char cleanDelimiter = getDelimiter(delimiter);
char cleanQuoteChar = getQuoteChar(quoteChar);
Path mappingFile = saveToFile(file);
try {
ComponentMappingMetadata resultMetaData = componentMappingService.update(dossierTemplateId,
componentMappingId,
nameToUse,
encoding,
cleanDelimiter,
mappingFile.toFile(),
file.getOriginalFilename(),
cleanQuoteChar);
return componentMappingMapper.toModel(resultMetaData);
} finally {
Files.deleteIfExists(mappingFile);
}
}
private static char getDelimiter(String delimiter) {
if (Strings.isNullOrEmpty(delimiter)) {
throw new BadRequestException("The provided delimiter is not valid! Can't be null or empty.");
} else if (delimiter.length() != 1) {
throw new BadRequestException(format("The provided delimiter %s is not valid! Only a single character is allowed.", delimiter));
}
return delimiter.charAt(0);
}
private static char getQuoteChar(String quoteChar) {
if (Strings.isNullOrEmpty(quoteChar)) {
throw new BadRequestException("The provided quoteChar is not valid! Can't be null or empty.");
} else if (quoteChar.length() != 1) {
throw new BadRequestException(format("The provided quoteChar %s is not valid! Only a single character is allowed.", quoteChar));
}
return quoteChar.charAt(0);
}
private static String validateFileName(MultipartFile file, String name) {
if (Strings.isNullOrEmpty(file.getOriginalFilename()) || !file.getOriginalFilename().endsWith(".csv")) {
throw new BadRequestException(format("File name \"%s\" does not end with .csv", file.getOriginalFilename()));
}
String fileName = file.getOriginalFilename();
String nameToUse = Strings.isNullOrEmpty(name) ? fileName.replaceAll(".csv$", "") : name;
if (Strings.isNullOrEmpty(nameToUse)) {
throw new BadRequestException(format("The provided file name \"%s\" is not valid!", nameToUse));
}
return nameToUse;
}
@Override
@PreAuthorize("hasAuthority('" + READ_RULES + "')")
public ResponseEntity<?> downloadMapping(String dossierTemplateId, String componentMappingId) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
ComponentMappingDownloadModel mappingDownloadModel = componentMappingService.getMappingForDownload(dossierTemplateId, componentMappingId);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
httpHeaders.add("Content-Disposition",
"attachment"
+ "; filename*="
+ mappingDownloadModel.encoding().toLowerCase(Locale.US)
+ "''"
+ StringEncodingUtils.urlEncode(mappingDownloadModel.fileName()));
return new ResponseEntity<>(mappingDownloadModel.mappingFileResource(), httpHeaders, HttpStatus.OK);
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
public ResponseEntity<?> deleteMapping(String dossierTemplateId, String componentMappingId) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
try {
componentMappingService.delete(dossierTemplateId, componentMappingId);
} catch (Exception ignored) {
}
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@PreAuthorize("hasAuthority('" + READ_DOSSIER_STATUS + "')")
public DossierStatusDefinitionList getDossierStatusDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) {
getDossierTemplate(dossierTemplateId);
return new DossierStatusDefinitionList(dossierStatusPersistenceService.getAllDossierStatusForTemplate(dossierTemplateId)
.stream()
.map(dossierStatusInfo -> DossierStatusDefinition.builder()
.id(dossierStatusInfo.getId())
.name(dossierStatusInfo.getName())
.description(dossierStatusInfo.getDescription())
.rank(dossierStatusInfo.getRank())
.color(dossierStatusInfo.getColor())
.dossierCount(dossierStatusInfo.getDossierCount() != null ? dossierStatusInfo.getDossierCount() : 0)
.build())
.toList());
}
@PreAuthorize("hasAuthority('" + READ_DOSSIER_ATTRIBUTES_CONFIG + "')")
public DossierAttributeDefinitionList getDossierAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) {
getDossierTemplate(dossierTemplateId);
return new DossierAttributeDefinitionList(dossierAttributeConfigPersistenceService.getDossierAttributes(dossierTemplateId)
.stream()
.map(config -> DossierAttributeDefinition.builder()
.id(config.getId())
.name(config.getLabel())
.type(config.getType())
.reportingPlaceholder(config.getPlaceholder())
.displaySettings(DossierAttributeDefinition.DossierDisplaySettings.builder()
.editable(config.isEditable())
.filterable(config.isFilterable())
.displayedInDossierList(config.isDisplayedInDossierList())
.build())
.build())
.toList());
}
@PreAuthorize("hasAuthority('" + GET_REPORT_TEMPLATES + "')")
public ReportTemplateList getReportTemplates(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) {
getDossierTemplate(dossierTemplateId);
var templates = reportTemplatePersistenceService.findByDossierTemplateId(dossierTemplateId);
return new ReportTemplateList(templates.stream()
.map(t -> ReportTemplate.builder()
.id(t.getTemplateId())
.name(t.getFileName())
.createdOn(t.getUploadDate())
.multiFile(t.isMultiFileReport())
.preSelect(t.isActiveByDefault())
.build())
.toList());
}
@Override
public List<ComponentDefinition> createComponents(String dossierTemplateId, List<ComponentDefinitionAddRequest> componentDefinitionAddRequests) {
if (componentDefinitionAddRequests.isEmpty()) {
return Collections.emptyList();
}
List<ComponentDefinition> componentDefinitions = componentDefinitionService.createComponents(dossierTemplateId, componentDefinitionAddRequests);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Components added.")
.details(Map.of("Number of added components", componentDefinitions.size()))
.build());
return componentDefinitions;
}
@Override
public List<ComponentDefinition> getComponents(String dossierTemplateId, boolean includeSoftDeleted) {
return componentDefinitionService.getComponentsByDossierTemplateId(dossierTemplateId, includeSoftDeleted);
}
@Override
public ComponentDefinition getComponent(String dossierTemplateId, String componentId) {
return componentDefinitionService.getComponentByDossierTemplateIdAndComponentId(dossierTemplateId, componentId);
}
@Override
public List<ComponentDefinition> updateComponents(String dossierTemplateId, List<ComponentDefinitionUpdateRequest> componentDefinitionUpdateRequests) {
if (componentDefinitionUpdateRequests.isEmpty()) {
return Collections.emptyList();
}
List<ComponentDefinition> componentDefinitions = componentDefinitionService.updateComponents(dossierTemplateId, componentDefinitionUpdateRequests);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Components updated.")
.details(Map.of("Number of updated components", componentDefinitions.size()))
.build());
return componentDefinitions;
}
@Override
public void deleteComponents(String dossierTemplateId, List<String> componentIds) {
List<ComponentDefinitionEntity> components = componentDefinitionService.deleteComponents(dossierTemplateId, componentIds);
if (!components.isEmpty()) {
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Components deleted.")
.details(Map.of("Number of deleted components", components.size()))
.build());
}
}
@Override
public List<ComponentDefinition> restoreComponents(String dossierTemplateId, List<String> componentIds) {
List<ComponentDefinition> components = componentDefinitionService.restoreComponents(dossierTemplateId, componentIds);
if (!components.isEmpty()) {
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Components restored.")
.details(Map.of("Number of restored components", components.size()))
.build());
}
return components;
}
@Override
public List<ComponentDefinition> reorderComponents(String dossierTemplateId, List<String> componentIds) {
List<ComponentDefinition> orderedComponents = componentDefinitionService.reorderComponents(dossierTemplateId, componentIds);
if (!orderedComponents.isEmpty()) {
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Components reordered.")
.details(Map.of("Number of reordered components", orderedComponents.size()))
.build());
}
return orderedComponents;
}
@SneakyThrows
private static Path saveToFile(MultipartFile file) {
Path mappingFile = Files.createTempFile(file.getName(), ".csv");
try (var out = new FileOutputStream(mappingFile.toFile())) {
out.write(file.getBytes());
}
return mappingFile;
}
private ResponseEntity<?> downloadRules(String dossierTemplateId, RuleFileType ruleFileType) {
Optional<RuleSetEntity> ruleEntityOptional = rulesPersistenceService.getRules(dossierTemplateId, ruleFileType);
if (ruleEntityOptional.isEmpty()) {
throw new NotFoundException(String.format("No rule file of type %s found for dossierTemplateId %s", ruleFileType, dossierTemplateId));
}
var data = ruleEntityOptional.get().getValue().getBytes(StandardCharsets.UTF_8);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
httpHeaders.add("Content-Disposition",
"attachment" + "; filename*=utf-8''" + StringEncodingUtils.urlEncode(ruleFileType.name().toLowerCase(Locale.ROOT) + RULES_DOWNLOAD_FILE_NAME_SUFFIX));
InputStream is = new ByteArrayInputStream(data);
return new ResponseEntity<>(new InputStreamResource(is), httpHeaders, HttpStatus.OK);
}
@SneakyThrows
private ResponseEntity<DroolsValidationResponse> uploadRules(String dossierTemplateId, RuleFileType ruleFileType, MultipartFile file, boolean dryRun) {
var rulesUploadRequest = RulesUploadRequest.builder()
.rules(new String(file.getBytes(), StandardCharsets.UTF_8))
.dossierTemplateId(dossierTemplateId)
.ruleFileType(ruleFileType)
.build();
try {
DroolsSyntaxValidation droolsSyntaxValidation = rulesValidationService.validateRules(rulesUploadRequest.getRuleFileType(), rulesUploadRequest.getRules());
if (!droolsSyntaxValidation.isCompiled()) {
var rulesSyntaxErrorMessages = droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
.stream()
.map(errorMessage -> RulesValidationMessage.builder()
.line(errorMessage.getLine())
.column(errorMessage.getColumn())
.message(errorMessage.getMessage())
.build())
.toList();
// TODO Add warning and deprecations to response
return new ResponseEntity<>(RulesValidationResponse.builder().errors(rulesSyntaxErrorMessages).build(), HttpStatus.UNPROCESSABLE_ENTITY);
DroolsValidationResponse rulesValidationResponse = new DroolsValidationResponse();
try {
var droolsValidation = rulesValidationService.validateRules(rulesUploadRequest.getRuleFileType(), rulesUploadRequest.getRules());
rulesValidationResponse = RulesValidationMapper.createFromDroolsValidation(droolsValidation);
if (!droolsValidation.isCompiled()) {
return new ResponseEntity<>(rulesValidationResponse, !dryRun ? HttpStatus.UNPROCESSABLE_ENTITY : HttpStatus.OK);
}
} catch (FeignException e) {
if (e.status() == HttpStatus.BAD_REQUEST.value()) {
@ -192,29 +647,10 @@ public class DossierTemplateControllerV2 implements DossierTemplateResource {
.userId(KeycloakSecurity.getUserId())
.objectId(rulesUploadRequest.getDossierTemplateId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message(String.format("%s rules have been %s", rulesUploadRequest.getRuleFileType(), dryRun ? "validated" : "updated"))
.message(format("%s rules have been %s", rulesUploadRequest.getRuleFileType(), dryRun ? "validated" : "updated"))
.build());
// TODO Add warning and deprecations to response
return new ResponseEntity<>(RulesValidationResponse.builder().build(), HttpStatus.OK);
}
private ResponseEntity<?> downloadRules(String dossierTemplateId, RuleFileType ruleFileType) {
var ruleEntity = rulesPersistenceService.getRules(dossierTemplateId, ruleFileType);
var data = ruleEntity.getValue().getBytes(StandardCharsets.UTF_8);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
httpHeaders.add("Content-Disposition",
"attachment" + "; filename*=utf-8''" + StringEncodingUtils.urlEncode(ruleFileType.name().toLowerCase(Locale.ROOT) + RULES_DOWNLOAD_FILE_NAME_SUFFIX));
InputStream is = new ByteArrayInputStream(data);
return new ResponseEntity<>(new InputStreamResource(is), httpHeaders, HttpStatus.OK);
return new ResponseEntity<>(rulesValidationResponse, HttpStatus.OK);
}
}

View File

@ -0,0 +1,126 @@
package com.iqser.red.persistence.service.v2.external.api.impl.controller;
import java.util.List;
import java.util.Optional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DownloadController;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ReportTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
import com.iqser.red.service.persistence.management.v1.processor.roles.ApplicationRoles;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.users.UserService;
import com.iqser.red.service.persistence.management.v1.processor.service.users.model.User;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RemoveDownloadRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatus;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatusList;
import com.iqser.red.service.persistence.service.v2.api.external.resource.DownloadResource;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
@Tag(name = "5. Downloads", description = "Operations related to download packages.")
public class DownloadControllerV2 implements DownloadResource {
private final DownloadController downloadController;
private final DownloadStatusPersistenceService downloadStatusPersistenceService;
private final UserService userService;
@Transactional
public DownloadStatusList getDownloadStatusList() {
validateUserRoles(KeycloakSecurity.getUserId());
var downloads = downloadStatusPersistenceService.getStatusesByUser(KeycloakSecurity.getUserId());
return new DownloadStatusList(downloads.stream().map(
status ->
DownloadStatus.builder()
.id(status.getUuid()) // This is a workaround the real id is the storageId.
.userId(status.getUserId())
.filename(status.getFilename())
.mimeType(status.getMimeType())
.errorCause(status.getErrorCause())
.status(status.getStatus())
.creationDate(status.getCreationDate())
.lastDownload(status.getLastDownload())
.fileSize(status.getFileSize())
.dossierId(status.getDossier() != null ? status.getDossier().getId() : null)
.fileIds(status.getFiles()
.stream()
.map(FileEntity::getId)
.toList())
.downloadFileTypes(status.getDownloadFileTypes()
.stream()
.toList())
.reportTemplateIds(status.getReports().stream().map(ReportTemplateEntity::getTemplateId).toList())
.build()).toList()
);
}
@Transactional
public DownloadStatus getDownloadStatus(@PathVariable(DOWNLOAD_ID_PARAM) String downloadId) {
validateUserRoles(KeycloakSecurity.getUserId());
var status = downloadStatusPersistenceService.getStatusesByUuid(downloadId);
return DownloadStatus.builder()
.id(status.getUuid()) // This is a workaround the real id is the storageId.
.userId(status.getUserId())
.filename(status.getFilename())
.mimeType(status.getMimeType())
.errorCause(status.getErrorCause())
.status(status.getStatus())
.creationDate(status.getCreationDate())
.lastDownload(status.getLastDownload())
.fileSize(status.getFileSize())
.dossierId(status.getDossier().getId())
.fileIds(status.getFiles()
.stream()
.map(FileEntity::getId)
.toList())
.downloadFileTypes(status.getDownloadFileTypes()
.stream()
.toList())
.reportTemplateIds(status.getReports().stream().map(ReportTemplateEntity::getTemplateId).toList())
.build();
}
public void deleteDownload(@PathVariable(DOWNLOAD_ID_PARAM) String downloadId) {
validateUserRoles(KeycloakSecurity.getUserId());
var status = downloadStatusPersistenceService.getStatusesByUuid(downloadId);
downloadController.deleteDownloadStatus(new RemoveDownloadRequest(List.of(status.getStorageId())));
}
public void download(@PathVariable(DOWNLOAD_ID_PARAM) String downloadId) {
validateUserRoles(KeycloakSecurity.getUserId());
var status = downloadStatusPersistenceService.getStatusesByUuid(downloadId);
downloadController.downloadFile(status.getStorageId());
}
private void validateUserRoles(String userId) {
Optional<User> userOptional = userService.getUserById(userId);
if (userOptional.isPresent()) {
if (userOptional.get().getRoles()
.stream()
.noneMatch(ApplicationRoles.VALID_MEMBER_ROLES::contains)) {
throw new NotAllowedException("User doesn't have appropriate roles");
}
}
}
}

View File

@ -0,0 +1,205 @@
package com.iqser.red.persistence.service.v2.external.api.impl.controller;
import java.time.OffsetDateTime;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.iqser.red.commons.spring.ErrorMessage;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.mchange.rmi.NotAuthorizedException;
import io.swagger.v3.oas.annotations.Hidden;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestControllerAdvice
@RequiredArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ExternalControllerAdviceV2 {
private final Scheduler scheduler;
@Hidden
@ResponseBody
@ResponseStatus(value = HttpStatus.NOT_FOUND)
@ExceptionHandler(value = NotFoundException.class)
public ErrorMessage handleContentNotFoundException(NotFoundException e) {
return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
}
/* error handling */
@Hidden
@ResponseBody
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = BadRequestException.class)
public ErrorMessage handleBadRequestException(BadRequestException e) {
return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
}
@Hidden
@ResponseBody
@ResponseStatus(value = HttpStatus.CONFLICT)
@ExceptionHandler(value = {ConflictException.class})
protected ErrorMessage handleConflictException(ConflictException e) {
return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
}
@ResponseBody
@ResponseStatus(value = HttpStatus.FORBIDDEN)
@ExceptionHandler({AccessDeniedException.class})
public ErrorMessage handleAccessDeniedException(AccessDeniedException e) {
return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
}
@ResponseBody
@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
@ExceptionHandler({NotAuthorizedException.class})
public ErrorMessage handleNotAuthorizedException(NotAuthorizedException e) {
return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
}
@ResponseBody
@ResponseStatus(value = HttpStatus.FORBIDDEN)
@ExceptionHandler({NotAllowedException.class})
public ErrorMessage handleNotAllowedException(NotAllowedException e) {
return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
}
@Hidden
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler({org.springframework.security.acls.model.NotFoundException.class})
public ErrorMessage handleACLNotFound(org.springframework.security.acls.model.NotFoundException e) {
// in case this error occurs on a rest request / force trigger the sync job
try {
scheduler.triggerJob(new JobKey("SyncUserPermissionsJob"), new JobDataMap(Map.of("tenantId", TenantContext.getTenantId())));
} catch (SchedulerException ex) {
log.debug("Failed to force trigger SyncUserPermissionsJob", ex);
}
return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
}
@Hidden
@ResponseBody
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ExceptionHandler({MethodArgumentNotValidException.class})
public ErrorMessage handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
var errorList = e.getFieldErrors();
String errorListAsString = errorList.stream()
.map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
.collect(Collectors.joining(", "));
return new ErrorMessage(OffsetDateTime.now(), String.format("You have empty/wrong formatted parameters: %s", errorListAsString));
}
@Hidden
@ResponseBody
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ExceptionHandler({MissingServletRequestPartException.class})
public ErrorMessage handleMissingServletRequestPartException(MissingServletRequestPartException e) {
return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
}
@Hidden
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({HttpMessageNotReadableException.class})
public ErrorMessage handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
var cause = e.getCause();
if (cause instanceof InvalidFormatException) {
InvalidFormatException invalidFormatException = (InvalidFormatException) cause;
Class<?> targetType = invalidFormatException.getTargetType();
if (targetType != null && targetType.isEnum()) {
return new ErrorMessage(OffsetDateTime.now(), String.format("Unsupported value for %s", targetType.getSimpleName()));
}
return new ErrorMessage(OffsetDateTime.now(), cause.getMessage());
}
return new ErrorMessage(OffsetDateTime.now(), e.getMessage());
}
@Hidden
@ResponseBody
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = DataIntegrityViolationException.class)
public ErrorMessage handleDataIntegrityViolationException(DataIntegrityViolationException exception) {
String message = Objects.requireNonNull(exception.getRootCause()).getMessage();
if (message.contains("uq_component_definition_technical_name_template")) {
message = "A component with the same technical name already exists in the given dossier template.";
} else if (message.contains("value too long for type character varying")) {
message = "Value is too long. Please use a shorter value.";
} else {
message = "Database error occurred.";
}
return new ErrorMessage(OffsetDateTime.now(), message);
}
@Order(10000)
public static class BinderControllerAdvice {
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
// This code protects Spring Core from a "Remote Code Execution" attack (dubbed "Spring4Shell").
// By applying this mitigation, you prevent the "Class Loader Manipulation" attack vector from firing.
// For more details, see this post: https://www.lunasec.io/docs/blog/spring-rce-vulnerabilities/
String[] denylist = new String[]{"class.*", "Class.*", "*.class.*", "*.Class.*"};
dataBinder.setDisallowedFields(denylist);
}
}
}

View File

@ -7,9 +7,7 @@ import static com.iqser.red.service.persistence.service.v2.api.external.resource
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.DOSSIER_TEMPLATE_ID_PARAM;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
@ -20,15 +18,22 @@ import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DossierTemplateController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.DownloadController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.FileAttributesController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.FileManagementController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.StatusController;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.UploadController;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusMapper;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttributes;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileUploadResult;
import com.iqser.red.service.persistence.service.v1.api.shared.model.PrepareDownloadWithOptionRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.BulkDownloadRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatus;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileDeleteRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileStatusList;
import com.iqser.red.service.persistence.service.v2.api.external.resource.FileResource;
@ -48,16 +53,19 @@ public class FileControllerV2 implements FileResource {
private final FileStatusManagementService fileStatusManagementService;
private final FileAttributesController fileAttributesController;
private final DossierTemplateController dossierTemplateController;
private final DownloadController downloadController;
private final DownloadStatusPersistenceService downloadStatusPersistenceService;
public FileUploadResult upload(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestPart(name = FILE_PARAM) MultipartFile file,
@RequestParam(value = KEEP_MANUAL_CHANGES_PARAM, required = false, defaultValue = "false") boolean keepManualChanges) {
@RequestParam(value = KEEP_MANUAL_CHANGES_PARAM, required = false, defaultValue = "false") boolean keepManualChanges,
@RequestParam(value = DISABLE_AUTOMATIC_ANALYSIS_PARAM, required = false, defaultValue = "false") boolean disableAutomaticAnalysis) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
return uploadController.upload(file, dossierId, keepManualChanges);
return uploadController.upload(file, dossierId, keepManualChanges, disableAutomaticAnalysis);
}
@ -68,10 +76,11 @@ public class FileControllerV2 implements FileResource {
@RequestParam(name = INCLUDE_SOFT_DELETED_PARAM, defaultValue = "false", required = false) boolean includeSoftDeleted) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
dossierController.getDossier(dossierId, includeArchived, includeSoftDeleted);
List<FileStatus> fileStatusList = new ArrayList<>();
if (!includeArchived && dossierController.getDossier(dossierId, includeArchived, includeSoftDeleted).getArchivedTime() != null) {
if (!includeArchived && dossierController.getDossier(dossierId, false, includeSoftDeleted).getArchivedTime() != null) {
return new FileStatusList(fileStatusList);
}
@ -141,4 +150,60 @@ public class FileControllerV2 implements FileResource {
fileAttributesController.setFileAttributes(dossierId, fileId, fileAttributes);
}
public DownloadStatus prepareFileDownload(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@PathVariable(FILE_ID_PARAM) String fileId,
@RequestBody DownloadRequest downloadRequest) {
return prepareBulkDownload(dossierTemplateId,
dossierId,
BulkDownloadRequest.builder()
.reportTemplateIds(downloadRequest.getReportTemplateIds())
.downloadFileTypes(downloadRequest.getDownloadFileTypes())
.redactionPreviewColor(downloadRequest.getRedactionPreviewColor())
.fileIds(List.of(fileId))
.build());
}
public DownloadStatus prepareBulkDownload(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestBody BulkDownloadRequest bulkDownloadRequest) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
var storageId = downloadController.prepareDownload(PrepareDownloadWithOptionRequest.builder()
.dossierId(dossierId)
.fileIds(bulkDownloadRequest.getFileIds())
.reportTemplateIds(bulkDownloadRequest.getReportTemplateIds())
.downloadFileTypes(bulkDownloadRequest.getDownloadFileTypes())
.redactionPreviewColor(bulkDownloadRequest.getRedactionPreviewColor())
.includeUnprocessed(false)
.build()).getStorageId();
var status = downloadStatusPersistenceService.getStatus(storageId);
return DownloadStatus.builder()
.id(status.getUuid()) // This is a workaround the real is the storageId.
.userId(status.getUserId())
.filename(status.getFilename())
.mimeType(status.getMimeType())
.errorCause(status.getErrorCause())
.status(status.getStatus())
.creationDate(status.getCreationDate())
.lastDownload(status.getLastDownload())
.fileSize(status.getFileSize())
.dossierId(status.getDossier().getId())
.fileIds(status.getFiles()
.stream()
.map(FileEntity::getId)
.toList())
.downloadFileTypes(status.getDownloadFileTypes()
.stream()
.toList())
.reportTemplateIds(bulkDownloadRequest.getReportTemplateIds())
.build();
}
}

View File

@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@Tag(name = "5. License endpoints", description = "Provides operations related to the license")
@Tag(name = "7. License", description = "Operations related to license information and usage metrics.")
public class LicenseControllerV2 implements LicenseResource {
private final LicenseReportController licenseReportController;

View File

@ -0,0 +1,75 @@
package com.iqser.red.persistence.service.v2.external.api.impl.mapper;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLogEntryValue;
import com.iqser.red.service.persistence.service.v2.api.external.model.Component;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentValue;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponents;
@Mapper
public interface ComponentMapper {
ComponentMapper INSTANCE = Mappers.getMapper(ComponentMapper.class);
@Mapping(target = "entityReferences", source = "componentLogEntityReferences")
ComponentValue toComponentValue(ComponentLogEntryValue entry);
@Mapping(target = "componentLogEntityReferences", source = "entityReferences")
ComponentLogEntryValue toComponentLogEntry(ComponentValue value);
List<ComponentValue> toComponentValues(List<ComponentLogEntryValue> entries);
List<ComponentLogEntryValue> toComponentLogEntries(List<ComponentValue> values);
@Mapping(target = "componentValues", source = "values")
Component toComponent(ComponentLogEntry entry);
List<Component> toComponents(List<ComponentLogEntry> entries);
@Mapping(target = "values", source = "componentValues")
ComponentLogEntry toComponentLogEntry(Component component);
default FileComponents toFileComponents(ComponentLog componentLog, String dossierTemplateId, String dossierId, String fileId, String fileName, boolean includeDetails) {
Map<String, List<String>> basicComponent = new LinkedHashMap<>();
for (ComponentLogEntry componentLogEntry : componentLog.getComponentLogEntries()) {
basicComponent.put(componentLogEntry.getName(),
componentLogEntry.getValues()
.stream()
.map(ComponentLogEntryValue::getValue)
.toList());
}
Map<String, Component> componentsDetails = new LinkedHashMap<>();
if (includeDetails) {
for (ComponentLogEntry entry : componentLog.getComponentLogEntries()) {
componentsDetails.put(entry.getName(), toComponent(entry));
}
}
return FileComponents.builder()
.dossierTemplateId(dossierTemplateId)
.dossierId(dossierId)
.filename(fileName)
.fileId(fileId)
.components(basicComponent)
.componentDetails(componentsDetails)
.build();
}
}

View File

@ -0,0 +1,28 @@
package com.iqser.red.persistence.service.v2.external.api.impl.mapper;
import java.util.List;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingMetadataModel;
@Mapper
public interface ComponentMappingMapper {
ComponentMappingMapper INSTANCE = Mappers.getMapper(ComponentMappingMapper.class);
ComponentMappingMetadataModel toModel(ComponentMappingMetadata componentMappingMetadata);
List<ComponentMappingMetadataModel> toModelList(List<ComponentMappingMetadata> componentMappingMetadata);
ComponentMappingMetadata toDto(ComponentMappingMetadataModel componentMappingMetadataModel);
List<ComponentMappingMetadata> toDtoList(List<ComponentMappingMetadataModel> componentMappingMetadataModels);
}

View File

@ -20,7 +20,7 @@ dependencies {
exclude(group = "com.iqser.red.service", module = "persistence-service-internal-api-v1")
exclude(group = "com.iqser.red.service", module = "persistence-service-shared-api-v1")
}
api("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.13.4")
api("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.17.2")
api("com.google.guava:guava:31.1-jre")
api("org.springframework.boot:spring-boot-starter-security:3.1.3")
api("org.springframework.boot:spring-boot-starter-validation:3.1.3")

View File

@ -0,0 +1,7 @@
package com.iqser.red.service.persistence.service.v1.api.external.model;
import java.util.List;
public record UpdateEntries(List<String> entriesToAdd, List<String> entriesToDelete) {
}

View File

@ -1,67 +0,0 @@
package com.iqser.red.service.persistence.service.v1.api.external.resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentsOverrides;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ResponseStatus(value = HttpStatus.OK)
public interface ComponentLogResource {
String COMPONENT_LOG_PATH = ExternalApi.BASE_PATH + "/componentLog";
String OVERRIDE_PATH = "/override";
String DOSSIER_ID = "dossierId";
String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID + "}";
String FILE_ID = "fileId";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
@GetMapping(value = COMPONENT_LOG_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets the component log for a fileId", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The component log is not found.")})
ComponentLog getComponentLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(name = "includeOverrides", defaultValue = "true") boolean includeOverrides);
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = COMPONENT_LOG_PATH + OVERRIDE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Adds overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void addOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ComponentsOverrides componentsOverrides);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@GetMapping(value = COMPONENT_LOG_PATH + OVERRIDE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found")})
ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = COMPONENT_LOG_PATH + OVERRIDE_PATH + "/revert" + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Reverts overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void revertOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RevertOverrideRequest revertOverrideRequest);
}

View File

@ -1,6 +1,7 @@
package com.iqser.red.service.persistence.service.v1.api.external.resource;
import java.util.List;
import java.util.Set;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@ -16,11 +17,13 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.service.persistence.service.v1.api.external.model.UpdateEntries;
import com.iqser.red.service.persistence.service.v1.api.shared.model.CreateTypeValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.Dictionary;
import com.iqser.red.service.persistence.service.v1.api.shared.model.TypeResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.TypeValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.UpdateTypeValue;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dictionary.Dictionary;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dictionary.DictionaryDifferenceResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.Colors;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.type.DictionaryEntryType;
@ -44,16 +47,21 @@ public interface DictionaryResource {
String ENTRY_PATH_VARIABLE = "/{" + ENTRY_PARAMETER_NAME + "}";
String DOSSIER_TEMPLATE_PARAMETER_NAME = "dossierTemplateId";
String DOSSIER_TEMPLATE_PATH_VARIABLE = "/{dossierTemplateId}";
String DOSSIER_TEMPLATE_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_PARAMETER_NAME + "}";
String UPLOAD = "/upload";
String DOWNLOAD = "/download";
String MERGED = "/merged";
String DELETE = "/delete";
String DIFFERENCE = "/difference";
String UPDATE = "/update";
String UPDATE_FLAG = "/updateFlag";
String COLOR_REST_PATH = ExternalApi.BASE_PATH + "/color";
String DOSSIER_ID_PARAMETER_NAME = "dossierId";
String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID_PARAMETER_NAME + "}";
String INCLUDE_DELETED_PARAMETER_NAME = "includeDeleted";
@ -95,6 +103,17 @@ public interface DictionaryResource {
@RequestParam(value = DICTIONARY_ENTRY_TYPE_PARAM, required = false, defaultValue = DEFAULT_DICTIONARY_ENTRY_TYPE) DictionaryEntryType dictionaryEntryType);
@ResponseStatus(HttpStatus.NO_CONTENT)
@PostMapping(value = DICTIONARY_REST_PATH + UPDATE + TYPE_PATH_VARIABLE + DOSSIER_TEMPLATE_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Update dictionary entries with type.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully updated the dictionary entries."), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The dossier or the entry type is not found."), @ApiResponse(responseCode = "403", description = "Forbidden")})
void updateEntries(@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestBody UpdateEntries updateEntries,
@RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId,
@RequestParam(value = DICTIONARY_ENTRY_TYPE_PARAM, required = false, defaultValue = DEFAULT_DICTIONARY_ENTRY_TYPE) DictionaryEntryType dictionaryEntryType);
@ResponseStatus(HttpStatus.NO_CONTENT)
@PostMapping(value = TYPE_PATH + TYPE_PATH_VARIABLE + DOSSIER_TEMPLATE_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Updates colors, hint and caseInsensitive of an entry type. Only label, colors and description are updatable for system managed entry types", description = "None")
@ -196,4 +215,20 @@ public interface DictionaryResource {
@GetMapping(value = COLOR_REST_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
Colors getColors(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId);
}
@ResponseStatus(HttpStatus.NO_CONTENT)
@PostMapping(value = DICTIONARY_REST_PATH + UPDATE_FLAG + TYPE_PATH_VARIABLE + DOSSIER_TEMPLATE_PATH_VARIABLE + DOSSIER_ID_PATH_VARIABLE)
@Operation(summary = "Updates flags regarding to selected type, dossier and dossier template.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully updated the flags of the type."), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The entry type is not found.")})
void changeFlags(@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAMETER_NAME) String dossierId,
@RequestParam(value = "addToDictionaryAction") boolean addToDictionaryAction);
@PostMapping(value = DICTIONARY_REST_PATH + DIFFERENCE + DOSSIER_TEMPLATE_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns the difference between the dictionaries of the dossier template and all the dossiers inside the template for a list of given types.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully returned DictionaryDifferenceResponse."), @ApiResponse(responseCode = "400", description = "The request is not valid.")})
DictionaryDifferenceResponse getDictionaryDifference(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestBody Set<String> types);
}

View File

@ -2,6 +2,7 @@ package com.iqser.red.service.persistence.service.v1.api.external.resource;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.http.HttpStatus;
@ -17,6 +18,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierChangeEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierChangeResponseV2;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierInformation;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
@ -29,10 +31,12 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
public interface DossierResource {
String DOSSIER_REST_PATH = ExternalApi.BASE_PATH + "/dossier";
String BY_ID_PATH = "/by-id";
String DOSSIER_TEMPLATE_PATH = "/dossier-template";
String DOSSIER_INFO_PATH = "/info";
String DELETED_DOSSIERS_PATH = ExternalApi.BASE_PATH + "/deleted-dossiers";
String CHANGES_DETAILS_PATH = "/changes/details";
String CHANGES_DETAILS_V2_PATH = "/changes/details/v2";
String HARD_DELETE_PATH = "/hard-delete";
String UNDELETE_PATH = "/restore";
@ -62,6 +66,12 @@ public interface DossierResource {
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Success")})
List<DossierChangeEntry> changesSince(@RequestBody JSONPrimitive<OffsetDateTime> since);
@ResponseBody
@PostMapping(value = DOSSIER_REST_PATH + CHANGES_DETAILS_V2_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "See if there are changes to dossiers since param", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Success")})
DossierChangeResponseV2 changesSinceV2(@RequestBody JSONPrimitive<OffsetDateTime> since);
@ResponseBody
@PostMapping(value = DOSSIER_REST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ -95,6 +105,13 @@ public interface DossierResource {
List<Dossier> getDossiers(@RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived,
@RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted);
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@PostMapping(value = DOSSIER_REST_PATH+BY_ID_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets dossiers by ids.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
JSONPrimitive<Map<String,Dossier>> getDossiersByIds(@RequestBody JSONPrimitive<Set<String>> dossierIds);
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody

View File

@ -95,8 +95,36 @@ public interface DossierTemplateResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = DOSSIER_TEMPLATE_PATH + IMPORT_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Receives an archive to import", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Archive have successfully imported"), @ApiResponse(responseCode = "400", description = "Validation failed during import"), @ApiResponse(responseCode = "404", description = "The dossier template to update does not exist")})
@Operation(summary = "Receives an archive to import", description = """
Import process stuck in one of the steps:"
0 - Reading the archive content
1 - store information about the dossier template
2 - store the colors
3 - store the watermarks
4 - store the dossier status
5 - store the dossier attributes
6 - store the file attributes
7 - store the report templates
8 - store the legal basis
9 - store the file attribute configuration
10 - store the component definitions
11 - store the component mappings
12 - store the types and entities
13 - store the rules and component rules""")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Archive have successfully imported"), @ApiResponse(responseCode = "400"), @ApiResponse(responseCode = "404", description = "The dossier template to update does not exist")})
DossierTemplateModel importDossierTemplate(@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@RequestParam(value = DOSSIER_TEMPLATE_ID, required = false) String dossierTemplateId,
@RequestParam(value = "updateExistingDossierTemplate", required = false, defaultValue = "false") boolean updateExistingDossierTemplate);

View File

@ -6,12 +6,11 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLogResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.FilteredEntityLogRequest;
import io.swagger.v3.oas.annotations.Operation;
@ -28,6 +27,8 @@ public interface EntityLogResource {
String DOSSIER_ID = "dossierId";
String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID + "}";
String ANALYSIS_NUMBER = "analysisNumber";
String ANALYSIS_NUMBER_PATH_VARIABLE = "/{" + ANALYSIS_NUMBER + "}";
String FALSE = "false";
@ -37,17 +38,34 @@ public interface EntityLogResource {
"Gets the entity log for a given file. The flag includeUnprocessed will merge into the entity log all the unprocessed changes if it's set to true."
+ "Default value for the flag is false.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The dossier / file / entity log is not found.")})
EntityLog getEntityLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "excludedType", required = false) List<String> excludedTypes,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed);
@PostMapping(value = ENTITY_LOG_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE + "/filtered", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets the entity log for a fileId grater than the specified date", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The dossier / file / entity log is not found.")})
EntityLog getFilteredEntityLog(@PathVariable(DOSSIER_ID) String dossierId,
EntityLogResponse getEntityLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody FilteredEntityLogRequest filteredEntityLogRequest);
@RequestParam(value = "excludedTypes", required = false, defaultValue = "") List<String> excludedTypes,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed);
@GetMapping(value = ENTITY_LOG_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE + "/filtered", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets the entity log for a fileId greater than the specified date", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The dossier / file / entity log is not found.")})
EntityLogResponse getFilteredEntityLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody FilteredEntityLogRequest filteredEntityLogRequest);
@GetMapping(value = ENTITY_LOG_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE + "/pages", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets the entity log for a fileId with all entities found on the given page numbers", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The dossier / file / entity log is not found.")})
EntityLogResponse getEntityLogWithEntriesOnPages(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "pageNumbers", defaultValue = "") List<Integer> pageNumbers);
@GetMapping(value = ENTITY_LOG_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE + ANALYSIS_NUMBER_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets the entity log for a fileId with all entities being analysed in or after iteration with given number", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The dossier / file / entity log is not found.")})
EntityLogResponse getEntityLogDeltaUpdate(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@PathVariable(ANALYSIS_NUMBER) Integer analysisNumber,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed);
}

View File

@ -44,7 +44,11 @@ public interface FileAttributesResource {
String FILE_ID = "fileId";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
Set<String> encodingList = Sets.newHashSet("ISO", "ASCII", "UTF-8");
String UTF_ENCODING = "UTF-8";
String ASCII_ENCODING = "ASCII";
String ISO_ENCODING = "ISO-8859-1";
Set<String> encodingList = Sets.newHashSet(ISO_ENCODING, ASCII_ENCODING, UTF_ENCODING);
@ResponseBody
@ -72,7 +76,7 @@ public interface FileAttributesResource {
@ResponseBody
@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(summary = "Delete a specific file attribute.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "NO_CONTENT")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "NO_CONTENT")})
@DeleteMapping(value = FILE_ATTRIBUTES_PATH + CONFIG_PATH + FILE_ATTRIBUTE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + FILE_ATTRIBUTE_ID_PATH_VARIABLE)
void deleteFileAttribute(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @PathVariable(FILE_ATTRIBUTE_ID) String fileAttributeId);
@ -81,7 +85,7 @@ public interface FileAttributesResource {
@ResponseBody
@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(summary = "Bulk delete file attributes.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "NO_CONTENT")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "NO_CONTENT")})
@PostMapping(value = FILE_ATTRIBUTES_PATH + CONFIG_PATH + FILE_ATTRIBUTE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + "/delete")
void deleteFileAttributes(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody List<String> fileAttributeIds);
@ -96,7 +100,7 @@ public interface FileAttributesResource {
@Operation(summary = "Set file attributes to an existing file", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@PostMapping(value = FILE_ATTRIBUTES_PATH + SET_PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
void setFileAttributes(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody FileAttributes fileAttributes);

View File

@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.pdftron.redaction.v1.api.model.highlights.Highlights;
import com.iqser.red.service.pdftron.redaction.v1.api.model.highlights.TextHighlights;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AnnotationIds;
import io.swagger.v3.oas.annotations.Operation;
@ -36,7 +36,7 @@ public interface HighlightsResource {
@Operation(summary = "Gets available highlights for the file", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found")})
@GetMapping(value = DOSSIERS_PATH + DOSSIER_ID_PATH_VARIABLE + FILES_PATH + FILE_ID_PATH_VARIABLE + HIGHLIGHTS_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
Highlights getHighlights(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
TextHighlights getHighlights(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@ResponseStatus(value = HttpStatus.NO_CONTENT)

View File

@ -0,0 +1,28 @@
package com.iqser.red.service.persistence.service.v1.api.external.resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.image.ImageSimilaritySearchRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.image.ImageSimilaritySearchResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
public interface ImageSimilaritySearchResource {
String IMAGE_SIMILARITY_SEARCH_PATH = ExternalApi.BASE_PATH + "/imageSimilaritySearch";
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Gets similiar images to given image", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "400", description = "Bad request: missing parameter")})
@PostMapping(value = IMAGE_SIMILARITY_SEARCH_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<ImageSimilaritySearchResponse> getSimilarImages(@RequestBody ImageSimilaritySearchRequest imageSimilaritySearchRequest);
}

View File

@ -28,15 +28,15 @@ public interface LegalBasisMappingResource {
@ResponseStatus(HttpStatus.NO_CONTENT)
@PostMapping(value = LEGAL_BASIS_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE + DELETE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "delete some legal basis by their names.", description = "None")
@Operation(summary = "delete some legal basis by their technical names.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
void deleteLegalBasis(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestBody List<String> legalBasisNames);
void deleteLegalBasis(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestBody List<String> legalBasisTechnicalNames);
@ResponseStatus(HttpStatus.NO_CONTENT)
@PutMapping(value = LEGAL_BASIS_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Add or update one legalBasis.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "400", description = "Missing required parameter")})
void addOrUpdateLegalBasis(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestBody LegalBasis legalBasis);
@ -50,7 +50,7 @@ public interface LegalBasisMappingResource {
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@GetMapping(value = LEGAL_BASIS_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Set the mapping between legal basis and redaction reason.", description = "None")
@Operation(summary = "Get all legal basis mapping in dossier template.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
List<LegalBasis> getLegalBasisMapping(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId);

View File

@ -1,6 +1,5 @@
package com.iqser.red.service.persistence.service.v1.api.external.resource;
import java.util.List;
import java.util.Set;
import org.springframework.http.HttpStatus;
@ -15,13 +14,16 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.CommentResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AnnotationComments;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualAddResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactionResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionBulkLocalRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddCommentRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ForceRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.LegalBasisChangeRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RecategorizationBulkLocalRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RecategorizationRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RemoveRedactionBulkLocalRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RemoveRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ResizeRedactionRequestModel;
@ -56,7 +58,8 @@ public interface ManualRedactionResource {
void undo(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<String> annotationIds,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed);
@RequestParam(value = "includeOnlyUnprocessed", required = false, defaultValue = FALSE) boolean includeOnlyUnprocessed,
@RequestParam(value = "includeOnlyLocal", required = false, defaultValue = FALSE) boolean includeOnlyLocal);
@ResponseStatus(value = HttpStatus.OK)
@ -90,22 +93,45 @@ public interface ManualRedactionResource {
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Adds a manual redaction", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
List<ManualAddResponse> addRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
ManualRedactionResponse addRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<AddRedactionRequestModel> addRedactionRequest);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = MANUAL_REDACTION_REST_PATH
+ "/bulk-local/add"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Adds a bulk of local redactions", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
ManualRedactionResponse addRedactionBulkLocal(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody AddRedactionBulkLocalRequestModel addRedactionRequest);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = MANUAL_REDACTION_REST_PATH
+ "/bulk-local/remove"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Removes a bulk of local redactions", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
ManualRedactionResponse removeRedactionBulkLocal(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody RemoveRedactionBulkLocalRequestModel removeRedactionRequest);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = MANUAL_REDACTION_REST_PATH
+ "/bulk/redaction/remove"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Removes the redactions list", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
List<ManualAddResponse> removeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
ManualRedactionResponse removeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<RemoveRedactionRequestModel> removeRedactionRequests,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed);
@RequestBody Set<RemoveRedactionRequestModel> removeRedactionRequests);
@ResponseStatus(value = HttpStatus.OK)
@ -115,11 +141,12 @@ public interface ManualRedactionResource {
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Forces the redactions list", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
List<ManualAddResponse> forceRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
ManualRedactionResponse forceRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<ForceRedactionRequestModel> forceRedactionRequests);
@Deprecated(forRemoval = true)
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = MANUAL_REDACTION_REST_PATH
+ "/bulk/redaction/legalBasisChange"
@ -127,7 +154,7 @@ public interface ManualRedactionResource {
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Changes the legal basis reasons list", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
List<ManualAddResponse> legalBasisChangeBulk(@PathVariable(DOSSIER_ID) String dossierId,
ManualRedactionResponse legalBasisChangeBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<LegalBasisChangeRequestModel> legalBasisChangeRequests);
@ -138,11 +165,22 @@ public interface ManualRedactionResource {
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Recategorizes the list of redaction log entries", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
List<ManualAddResponse> recategorizeBulk(@PathVariable(DOSSIER_ID) String dossierId,
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
ManualRedactionResponse recategorizeBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<RecategorizationRequestModel> recategorizationRequests,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed);
@RequestBody Set<RecategorizationRequestModel> recategorizationRequests);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = MANUAL_REDACTION_REST_PATH
+ "/bulk-local/recategorize"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Recategorizes the list of redaction log entries", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
ManualRedactionResponse recategorizeBulkLocal(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody RecategorizationBulkLocalRequestModel recategorizationRequest);
@ResponseStatus(value = HttpStatus.OK)
@ -152,10 +190,9 @@ public interface ManualRedactionResource {
+ FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Resizes the redactions list", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
List<ManualAddResponse> resizeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
ManualRedactionResponse resizeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<ResizeRedactionRequestModel> resizeRedactionRequests,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed);
@RequestBody Set<ResizeRedactionRequestModel> resizeRedactionRequests);
@ResponseStatus(value = HttpStatus.OK)

View File

@ -1,56 +0,0 @@
package com.iqser.red.service.persistence.service.v1.api.external.resource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.saas.migration.MigrationStatusResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
public interface MigrationStatusResource {
String MIGRATION_STATUS_REST_PATH = ExternalApi.BASE_PATH + "/migration-status";
String START_MIGRATION_REST_PATH = ExternalApi.BASE_PATH + "/start_migration";
String REVERT_MIGRATION_REST_PATH = ExternalApi.BASE_PATH + "/revert_migration";
String RETRY_MIGRATION_REST_PATH = ExternalApi.BASE_PATH + "/retry_migration";
String FILE_ID = "fileId";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
String DOSSIER_ID = "dossierId";
String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID + "}";
@ResponseBody
@PostMapping(value = MIGRATION_STATUS_REST_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Show the status of the migration", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Success.")})
MigrationStatusResponse migrationStatus();
@ResponseBody
@PostMapping(value = START_MIGRATION_REST_PATH + FILE_ID_PATH_VARIABLE + DOSSIER_ID_PATH_VARIABLE)
@Operation(summary = "Start SAAS migration for specific file", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Success.")})
ResponseEntity<?> startMigrationForFile(@RequestParam(value = DOSSIER_ID) String dossierId, @RequestParam(value = FILE_ID) String fileId);
@ResponseBody
@PostMapping(value = REVERT_MIGRATION_REST_PATH + FILE_ID_PATH_VARIABLE + DOSSIER_ID_PATH_VARIABLE)
@Operation(summary = "Start SAAS migration for specific file", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Success.")})
ResponseEntity<?> revertMigrationForFile(@RequestParam(value = DOSSIER_ID) String dossierId, @RequestParam(value = FILE_ID) String fileId);
@ResponseBody
@PostMapping(value = RETRY_MIGRATION_REST_PATH)
@Operation(summary = "Restart SAAS migration for all files in error state", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Success.")})
ResponseEntity<?> requeueErrorFiles();
}

View File

@ -1,76 +0,0 @@
package com.iqser.red.service.persistence.service.v1.api.external.resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentsOverrides;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.rss.RSSResponse;
import com.iqser.red.service.redaction.report.v1.api.model.rss.DetailedRSSResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@Deprecated(forRemoval = true)
@ResponseStatus(value = HttpStatus.OK)
public interface RSSResource {
String RSS_PATH = ExternalApi.BASE_PATH + "/rss";
String OVERRIDE_PATH = "/override";
String DOSSIER_ID = "dossierId";
String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID + "}";
String FILE_ID = "fileId";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
@Deprecated(forRemoval = true)
@GetMapping(value = RSS_PATH + DOSSIER_ID_PATH_VARIABLE, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@Operation(summary = "Returns the RSS response for a dossier", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
RSSResponse getRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId);
@Deprecated(forRemoval = true)
@GetMapping(value = RSS_PATH + "/detailed" + DOSSIER_ID_PATH_VARIABLE, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@Operation(summary = "Returns the RSS response with more details for a dossier", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
DetailedRSSResponse getDetailedRSS(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = "fileId", required = false) String fileId);
@Deprecated(forRemoval = true)
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = RSS_PATH + OVERRIDE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Adds overrides for RSS components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
void addOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ComponentsOverrides componentsOverrides);
@Deprecated(forRemoval = true)
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@GetMapping(value = RSS_PATH + OVERRIDE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets overrides for RSS components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
ComponentsOverrides getOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@Deprecated(forRemoval = true)
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = RSS_PATH + OVERRIDE_PATH + "/revert" + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Reverts overrides for RSS components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
void revertOverrides(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RevertOverrideRequest revertOverrideRequest);
}

View File

@ -38,6 +38,7 @@ public interface ReanalysisResource {
String EXCLUDED_STATUS_PARAM = "excluded";
String FORCE_PARAM = "force";
String ALL_PAGES = "allPages";
@PostMapping(value = REANALYSIS_REST_PATH + DOSSIER_ID_PATH_VARIABLE)
@ -73,7 +74,8 @@ public interface ReanalysisResource {
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "409", description = "Conflict"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden"), @ApiResponse(responseCode = "400", description = "Cannot OCR approved file")})
void ocrFile(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force);
@RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force,
@RequestParam(value = ALL_PAGES, required = false, defaultValue = FALSE) boolean allPages);
@Operation(summary = "Ocr and reanalyze multiple files for a dossier", description = "None")

View File

@ -1,71 +0,0 @@
package com.iqser.red.service.persistence.service.v1.api.external.resource;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.FilteredRedactionLogRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.section.SectionGrid;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@Deprecated(forRemoval = true)
@ResponseStatus(value = HttpStatus.OK)
public interface RedactionLogResource {
String REDACTION_LOG_PATH = ExternalApi.BASE_PATH + "/redactionLog";
String SECTION_GRID_PATH = ExternalApi.BASE_PATH + "/sectionGrid";
String SECTION_TEXT_PATH = ExternalApi.BASE_PATH + "/sectionText";
String FILE_ID = "fileId";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
String DOSSIER_ID = "dossierId";
String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID + "}";
@Deprecated(forRemoval = true)
@GetMapping(value = REDACTION_LOG_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets the redaction log for a fileId", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The redaction log is not found.")})
RedactionLog getRedactionLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "excludedType", required = false) List<String> excludedTypes,
@RequestParam(value = "withManualRedactions", required = false, defaultValue = "true") boolean withManualRedactions,
@RequestParam(value = "includeFalsePositives", required = false, defaultValue = "false") boolean includeFalsePositives);
@Deprecated(forRemoval = true)
@GetMapping(value = SECTION_GRID_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets the section grid for a fileId", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The section grid is not found.")})
SectionGrid getSectionGrid(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@Deprecated(forRemoval = true)
@GetMapping(value = SECTION_TEXT_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Gets the text blocks of a document for a fileId", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The section text is not found.")})
ResponseEntity<?> getSectionText(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@Deprecated(forRemoval = true)
@PostMapping(value = REDACTION_LOG_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE + "/filtered", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets the redaction log for a fileId grater than the specified date", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The redaction log is not found.")})
RedactionLog getFilteredRedactionLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody FilteredRedactionLogRequest filteredRedactionLogRequest);
}

View File

@ -6,6 +6,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
@ -14,7 +15,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsSyntaxValidationResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsValidationResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUploadRequestModel;
@ -28,6 +29,7 @@ public interface RulesResource {
String RULES_PATH = ExternalApi.BASE_PATH + "/rules";
String UPLOAD_PATH = "/upload";
String DOWNLOAD_PATH = "/download";
String RESET_PATH = "/reset";
String DOSSIER_TEMPLATE_PARAMETER_NAME = "dossierTemplateId";
String DOSSIER_TEMPLATE_PATH_VARIABLE = "/{dossierTemplateId}";
@ -46,8 +48,8 @@ public interface RulesResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = RULES_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Takes object containing string or rules as argument, which will be used by the redaction service.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful or rules validation done."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified.")})
ResponseEntity<DroolsSyntaxValidationResponse> upload(@RequestBody RulesUploadRequestModel rules);
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful or rules validation done."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified."), @ApiResponse(responseCode = "422", description = "Uploaded rules could not be compiled.")})
ResponseEntity<DroolsValidationResponse> upload(@RequestBody RulesUploadRequestModel rules);
@ResponseBody
@ -74,20 +76,20 @@ public interface RulesResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = RULES_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE + UPLOAD_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "Takes object containing string or rules as argument, which will be used by the redaction service.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful or rules validation done."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified.")})
ResponseEntity<DroolsSyntaxValidationResponse> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = DRY_RUN_PARAMETER) boolean dryRun,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file);
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful or rules validation done."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified."), @ApiResponse(responseCode = "422", description = "Uploaded rules could not be compiled.")})
ResponseEntity<DroolsValidationResponse> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = DRY_RUN_PARAMETER) boolean dryRun,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = RULES_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE + RULE_FILE_TYPE_PATH_VARIABLE + UPLOAD_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "Takes object containing string or rules as argument, which will be used by the redaction service.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful or rules validation done"), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified.")})
ResponseEntity<DroolsSyntaxValidationResponse> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType,
@RequestParam(value = DRY_RUN_PARAMETER) boolean dryRun,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file);
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful or rules validation done"), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified."), @ApiResponse(responseCode = "422", description = "Uploaded rules could not be compiled.")})
ResponseEntity<DroolsValidationResponse> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType,
@RequestParam(value = DRY_RUN_PARAMETER) boolean dryRun,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file);
@ResponseBody
@ -105,4 +107,10 @@ public interface RulesResource {
@GetMapping(value = RULES_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE + RULE_FILE_TYPE_PATH_VARIABLE + DOWNLOAD_PATH)
ResponseEntity<?> downloadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Resets the timeout detected flag in a Rule file.")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "No content")})
@PutMapping(value = RULES_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE + RULE_FILE_TYPE_PATH_VARIABLE + RESET_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
void unlockRules(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType);
}

View File

@ -3,6 +3,7 @@ package com.iqser.red.service.persistence.service.v1.api.external.resource;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@ -16,6 +17,8 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import com.iqser.red.service.persistence.service.v1.api.shared.model.warning.ApproveResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
@ -24,6 +27,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
public interface StatusResource {
String STATUS_REST_PATH = ExternalApi.BASE_PATH + "/status";
String BY_ID_PATH = "/by-id";
String CHANGES_SINCE_PATH = "/changes";
String BULK_REST_PATH = "/bulk";
String ASSIGNEE_REST_PATH = "/set-assignee";
@ -35,6 +39,7 @@ public interface StatusResource {
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
String ASSIGNEE_ID_REQUEST_PARAM = "assigneeId";
String FORCE_REQUEST_PARAM = "force";
String DELETED_PATH = "/softdeleted";
@ -54,6 +59,14 @@ public interface StatusResource {
Map<String, List<FileStatus>> getDossierStatus(@RequestBody List<String> dossierIds);
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@PostMapping(value = STATUS_REST_PATH + BY_ID_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets the status for files by dossierId and fileIds.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
JSONPrimitive<Map<String, List<FileStatus>>> getFilesByIds(@RequestBody JSONPrimitive<Map<String, Set<String>>> filesByDossier);
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@PostMapping(value = STATUS_REST_PATH + DELETED_PATH, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
@ -105,11 +118,13 @@ public interface StatusResource {
@RequestParam(value = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = STATUS_REST_PATH + "/approved" + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Sets the status APPROVED for a file.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void setStatusApproved(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Forbidden")})
ApproveResponse setStatusApproved(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = FORCE_REQUEST_PARAM, required = false, defaultValue = "false") boolean force);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@ -139,11 +154,13 @@ public interface StatusResource {
@RequestParam(value = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = STATUS_REST_PATH + "/approved" + DOSSIER_ID_PATH_VARIABLE + BULK_REST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Sets the status APPROVED for a list of files.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void setStatusApprovedForList(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody List<String> fileIds);
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
List<ApproveResponse> setStatusApprovedForList(@PathVariable(DOSSIER_ID) String dossierId,
@RequestBody List<String> fileIds,
@RequestParam(value = FORCE_REQUEST_PARAM, required = false, defaultValue = "false") boolean force);
@ResponseStatus(value = HttpStatus.NO_CONTENT)

View File

@ -0,0 +1,178 @@
package com.iqser.red.service.persistence.service.v1.api.external.resource;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DownloadResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileExchangeExportRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatusFilter;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ImportResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ReanalysisSettings;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ReanalyzeFilesResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public interface SupportResource {
String REANALYSIS_REST_PATH = ExternalApi.BASE_PATH + "/reanalyze";
String ERROR_REANALYSIS_REST_PATH = REANALYSIS_REST_PATH + "/error";
String STATUS_REST_PATH = ExternalApi.BASE_PATH + "/status/filter";
String FILE_EXCHANGE_REST_PATH = ExternalApi.BASE_PATH + "/file-exchange";
String DATASET_EXCHANGE = ExternalApi.BASE_PATH + "/dataset-exchange";
String BULK_REST_PATH = "/bulk";
String DOSSIER_TEMPLATE_ID = "dossierTemplateId";
String DOSSIER_ID = "dossierId";
String FILE_ID = "fileId";
String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID + "}";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
String DOSSIER_TEMPLATE_DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/dossierTemplateId/{" + DOSSIER_TEMPLATE_ID + "}";
String DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_ID + "}";
String DOSSIER_DOSSIER_ID_PATH_VARIABLE = "/dossierId/{" + DOSSIER_ID + "}";
String FALSE = "false";
String FULL_REANALYSIS_PARAM = "fullReanalysis";
String RUN_OCR_PARAM = "runOcr";
String EXPORT = "/export";
String IMPORT = "/import";
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = REANALYSIS_REST_PATH + DOSSIER_TEMPLATE_DOSSIER_TEMPLATE_ID_PATH_VARIABLE)
@Operation(summary = "Reanalyze all files in dossier template", description = """
## Reanalyze Files Endpoint
Use this endpoint to reanalyze all files in a specified Dossier Template. The reanalysis process can be tailored using various filtering options provided in the request body.
### Parameters
- **DossierTemplateId**: Specifies the Dossier Template whose files need to be reanalyzed.
### Request Body Configuration Options
- **dossierIds**: List of dossier IDs to filter. If empty, all dossiers are selected for reanalysis.
- **fileIds**: List of file IDs to filter. If empty, all files are selected for reanalysis.
- **repeatStructureAnalysis**: Boolean. If true, layout parsing and named entity recognition will be repeated.
- **fileStatusFilter**: Use this to create a filter for files to reanalyze. Matches any file if set to null.
""")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "403", description = "Forbidden")})
ReanalyzeFilesResponse reanalyzeFiles(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody ReanalysisSettings reanalysisSettings);
@PostMapping(value = ERROR_REANALYSIS_REST_PATH)
@Operation(summary = "Reanalyze all files in error state.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void reanalyzeAllRelevantErrorFiles(@RequestParam(value = FULL_REANALYSIS_PARAM, required = false, defaultValue = FALSE) boolean repeatStructureAnalysis,
@RequestParam(value = RUN_OCR_PARAM, required = false, defaultValue = FALSE) boolean runOcr);
@PostMapping(value = ERROR_REANALYSIS_REST_PATH + DOSSIER_ID_PATH_VARIABLE + BULK_REST_PATH)
@Operation(summary = "Reanalyze multiple files in error state for a dossier.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void reanalyzeErrorFilesBulkForDossier(@PathVariable(DOSSIER_ID) String dossierId,
@RequestBody List<String> fileIds,
@RequestParam(value = FULL_REANALYSIS_PARAM, required = false, defaultValue = FALSE) boolean repeatStructureAnalysis,
@RequestParam(value = RUN_OCR_PARAM, required = false, defaultValue = FALSE) boolean runOcr);
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@PostMapping(value = STATUS_REST_PATH, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Get the filtered status for all files in the workspace.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
List<FileStatus> getFileStatus(@RequestBody FileStatusFilter fileStatusFilter);
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@PostMapping(value = STATUS_REST_PATH
+ DOSSIER_TEMPLATE_DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Get the filtered status for all files in a dossier template.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
List<FileStatus> getFileStatusForDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody FileStatusFilter fileStatusFilter);
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@PostMapping(value = STATUS_REST_PATH + DOSSIER_DOSSIER_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Get the filtered status for all files in a dossier.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
List<FileStatus> exportFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody FileStatusFilter fileStatusFilter);
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@PostMapping(value = FILE_EXCHANGE_REST_PATH
+ EXPORT
+ DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Exports all dossiers and files from a given Dossier Template.", description = """
## Export Files Endpoint
Use this endpoint to export a full Dossier Template, including all configurations, dossiers, and files.
The endpoint returns a String storageId, which is used to query the DownloadController for the export zip archive's status and to download the archive.
### Parameters
- **DossierTemplateId**: Specifies the Dossier Template to be exported.
### Request Body Configuration Options
- **dossierIds**: List of dossier IDs to filter. If empty, all dossiers are selected.
- **fileIds**: List of file IDs to filter. If empty, all files are selected.
- **excludeLayoutFiles**: Boolean. If true, excludes DOCUMENT_STRUCTURE/_PAGES/_TEXT/_POSITIONS, SIMPLIFIED_TEXT, and NER_ENTITIES files.
- **excludeManualRedactions**: Boolean. If true, excludes MANUAL_REDACTIONS files.
- **excludeAnalysisLogs**: Boolean. If true, excludes ENTITY_LOG and COMPONENT_LOG (with overrides) files.
- **excludeFileAttributes**: Boolean. If true, excludes file attributes.
- **originFileOnly**: Boolean. If true, exports only the origin file, ignoring untouched and viewer documents.""")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
DownloadResponse exportFiles(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody FileExchangeExportRequest exportRequest);
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@PostMapping(value = DATASET_EXCHANGE + EXPORT + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Exports all dossiers and files from a given Dossier Template.", description = """
## Export Preview Files Endpoint
Use this endpoint to export all files of a DossierTemplate as a Preview file containing all applied annotations as redaction annotations with additional data
The endpoint returns a String storageId, which is used to query the DownloadController for the export zip archive's status and to download the archive.
""")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
DownloadResponse exportDataset(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = FILE_EXCHANGE_REST_PATH + IMPORT, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Imports a file exchange export zip.", description = "Use this endpoint to import a full export of a given Dossier Template including all its configurations, dossiers, and files. Returns the resulting dossierTemplateId.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
ImportResponse importFiles(@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = DATASET_EXCHANGE + IMPORT, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Imports a file exchange export zip.", description = "Use this endpoint to import a full export of a given Dossier Template including all its configurations, dossiers, and files. Returns the resulting dossierTemplateId.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
ImportResponse importDataset(@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file);
}

View File

@ -15,6 +15,7 @@ import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileUploadResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ -27,6 +28,7 @@ public interface UploadResource {
String IMPORT_REDACTIONS_PATH = ExternalApi.BASE_PATH + "/import-redactions";
String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID + "}";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
String DISABLE_AUTOMATIC_ANALYSIS_PARAM = "disableAutomaticAnalysis";
@ResponseBody
@ -37,7 +39,8 @@ public interface UploadResource {
+ "uploaded file."), @ApiResponse(responseCode = "404", description = "Dossier not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
FileUploadResult upload(@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@PathVariable(DOSSIER_ID) String dossierId,
@RequestParam(value = "keepManualRedactions", required = false, defaultValue = "false") boolean keepManualRedactions);
@RequestParam(value = "keepManualRedactions", required = false, defaultValue = "false") boolean keepManualRedactions,
@Parameter(name = DISABLE_AUTOMATIC_ANALYSIS_PARAM, description = "Disables automatic analysis for the uploaded file, imports only imported redactions") @RequestParam(value = DISABLE_AUTOMATIC_ANALYSIS_PARAM, required = false, defaultValue = "false") boolean disableAutomaticAnalysis);
@ResponseBody

View File

@ -0,0 +1,37 @@
package com.iqser.red.service.persistence.service.v1.api.external.resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.UserStats;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ApiResponses(value = {@ApiResponse(responseCode = "429", description = "Too many requests.")})
public interface UserStatsResource {
String USER_ID_PARAM = "userId";
String USER_ID_PATH_VARIABLE = "/{" + USER_ID_PARAM + "}";
String STATS_PATH = "/user-stats";
String PATH = ExternalApi.BASE_PATH + STATS_PATH;
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@GetMapping(value = PATH + USER_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets user stats for user specified by id.", description = "")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found")})
ResponseEntity<UserStats> getUserStats(@Parameter(name = USER_ID_PARAM, description = "The unique identifier of the user whose statistics we want to retrieve.", required = true) @PathVariable(USER_ID_PARAM) String userId);
}

View File

@ -1,6 +1,6 @@
plugins {
id("com.iqser.red.service.java-conventions")
id("io.freefair.lombok") version "8.4"
id("io.freefair.lombok") version "8.6"
}
dependencies {
@ -22,7 +22,7 @@ dependencies {
exclude(group = "com.iqser.red.service", module = "persistence-service-internal-api-v1")
exclude(group = "com.iqser.red.service", module = "persistence-service-shared-api-v1")
}
api("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.16.0")
api("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.17.2")
api("com.google.guava:guava:31.1-jre")
api("org.springframework.boot:spring-boot-starter-security:3.1.3")
api("org.springframework.boot:spring-boot-starter-validation:3.1.3")

View File

@ -0,0 +1,18 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BulkComponentsRequest {
private List<String> fileIds = new ArrayList<>();
}

View File

@ -0,0 +1,25 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BulkDownloadRequest {
private List<String> reportTemplateIds = new ArrayList<>();
private Set<DownloadFileType> downloadFileTypes = new HashSet<>();
private String redactionPreviewColor;
private List<String> fileIds = new ArrayList<>();
}

View File

@ -8,6 +8,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
@Data
@Builder
@ -16,8 +17,12 @@ import lombok.NoArgsConstructor;
public class Component {
@JacksonXmlCData
@NonNull
private String name;
@JacksonXmlCData
@NonNull
private List<ComponentValue> componentValues;
@JacksonXmlCData
private boolean overridden;
}

View File

@ -0,0 +1,28 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ComponentMappingMetadataModel {
String id;
String name;
String fileName;
Integer version;
List<String> columnLabels;
Integer numberOfLines;
String encoding;
char delimiter;
}

View File

@ -0,0 +1,22 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ComponentMappingSummary {
String dossierTemplateId;
List<ComponentMappingMetadataModel> componentMappingList;
}

View File

@ -0,0 +1,22 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ComponentOverrideList {
String dossierTemplateId;
String dossierId;
String fileId;
List<Component> componentOverrides = new ArrayList<>();
}

View File

@ -8,6 +8,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
@Data
@Builder
@ -16,10 +17,9 @@ import lombok.NoArgsConstructor;
public class ComponentValue {
@JacksonXmlCData
@NonNull
String value;
@JacksonXmlCData
String originalValue;
@JacksonXmlCData
String valueDescription;
@JacksonXmlCData
String componentRuleId;

View File

@ -41,6 +41,10 @@ public class DocuMineDossierRequest {
@Schema(description = "The id(s) of members associated to this dossier.")
private Set<String> memberIds = new HashSet<>();
@Builder.Default
@Schema(description = "The id(s) of approvers associated to this dossier.")
private Set<String> approverIds = new HashSet<>();
@Builder.Default
@Schema(description = "Id(s) of the word report templates used to generate downloads")
private Set<String> reportTemplateIds = new HashSet<>();

View File

@ -0,0 +1,34 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttributeType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DossierAttributeDefinition {
private String id;
private String name;
private DossierAttributeType type;
private String reportingPlaceholder;
private DossierDisplaySettings displaySettings;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class DossierDisplaySettings {
private boolean editable;
private boolean filterable;
private boolean displayedInDossierList;
}
}

View File

@ -0,0 +1,18 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DossierAttributeDefinitionList {
private List<DossierAttributeDefinition> dossierAttributeDefinitions= new ArrayList<>();
}

View File

@ -0,0 +1,21 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DossierStatusDefinition {
private String id;
private String name;
private String description;
private int rank;
private String color;
private long dossierCount;
}

View File

@ -0,0 +1,18 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DossierStatusDefinitionList {
private List<DossierStatusDefinition> dossierStatusDefinitions = new ArrayList<>();
}

View File

@ -0,0 +1,25 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DownloadRequest {
private List<String> reportTemplateIds = new ArrayList<>();
private Set<DownloadFileType> downloadFileTypes = new HashSet<>();
private String redactionPreviewColor;
}

View File

@ -0,0 +1,35 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadStatusValue;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DownloadStatus {
private String id;
private String userId;
private String filename;
private String mimeType;
private String errorCause;
private DownloadStatusValue status;
private OffsetDateTime creationDate;
private OffsetDateTime lastDownload;
private long fileSize;
private String dossierId;
private List<String> fileIds = new ArrayList<>();
private List<DownloadFileType> downloadFileTypes = new ArrayList<>();
private List<String> reportTemplateIds = new ArrayList<>();
}

View File

@ -0,0 +1,19 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DownloadStatusList {
List<DownloadStatus> downloadStatus = new ArrayList<>();
}

View File

@ -20,6 +20,7 @@ public class FileAttributeDefinition {
private String mappedCsvColumnHeader;
private String reportingPlaceholder;
private DisplaySettings displaySettings;
private boolean includeInCsvExport;
@Data
@NoArgsConstructor

View File

@ -0,0 +1,22 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.time.OffsetDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ReportTemplate {
private String id;
private String name;
private OffsetDateTime createdOn;
private boolean multiFile;
private boolean preSelect;
}

View File

@ -0,0 +1,20 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ReportTemplateList {
@Builder.Default
private List<ReportTemplate> reportTemplates = new ArrayList<>();
}

View File

@ -13,14 +13,14 @@ import lombok.experimental.FieldDefaults;
@AllArgsConstructor
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
@Schema(description = "Shows where to find problems in the uploaded rules file")
public class RulesValidationMessage {
@Schema(description = "Show where blacklisted keywords are used in the rules file.")
public class RulesBlacklistMessage {
@Schema(description = "The Line where it occurred.")
@Schema(description = "The Line where the blacklisted keywords are used.")
Integer line;
@Schema(description = "The Column where it occurred.")
@Schema(description = "The Column where the blacklisted keywords are used.")
Integer column;
@Schema(description = "The error or warning message.")
@Schema(description = "The message containing all blacklisted keywords that were used.")
String message;
}

View File

@ -1,28 +0,0 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
@Schema(description = "Show where deprecated methods are used in the rules file.")
public class RulesDeprecationMessage {
@Schema(description = "The Line where the deprecated method is used.")
Integer line;
@Schema(description = "The Column where the deprecated method is used.")
Integer column;
@Schema(description = "The deprecated message, should point to method that should be used instead.")
String message;
@Schema(description = "The name of the deprecated method.")
String methodName;
}

View File

@ -8,15 +8,21 @@ import static com.iqser.red.service.persistence.service.v2.api.external.resource
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.DOSSIER_TEMPLATE_PATH;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.FileResource.FILE_ID_PARAM;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.FileResource.FILE_ID_PATH_VARIABLE;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.FileResource.FILE_PATH;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.RevertOverrideRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.BulkComponentsRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.Component;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentOverrideList;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponents;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileComponentsList;
@ -29,7 +35,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ResponseStatus(value = HttpStatus.OK)
public interface ComponentResource {
String PATH = ExternalApiConstants.BASE_PATH + DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DOSSIER_PATH + DOSSIER_ID_PATH_PARAM + FILE_PATH;
String PATH = ExternalApiConstants.BASE_PATH + DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE;
String FILE_PATH = PATH + DOSSIER_PATH + DOSSIER_ID_PATH_PARAM + FileResource.FILE_PATH;
String COMPONENTS_PATH = "/components";
@ -44,8 +51,14 @@ public interface ComponentResource {
- false (default): The component object does not contain a field componentDetails.
""";
String OVERRIDES_PATH = "/overrides";
String REVERT_PATH = "/revert";
@GetMapping(value = PATH + FILE_ID_PATH_VARIABLE + COMPONENTS_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
String COMPONENT_OVERRIDE_PARAM = "componentOverride";
String REVERT_OVERRIDE_PARAM = "revertOverride";
@GetMapping(value = FILE_PATH + FILE_ID_PATH_VARIABLE + COMPONENTS_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@Operation(summary = "Returns the components for a file", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
FileComponents getComponents(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@ -54,11 +67,53 @@ public interface ComponentResource {
@Parameter(name = INCLUDE_DETAILS_PARAM, description = INCLUDE_DETAILS_DESCRIPTION) @RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails);
@GetMapping(value = PATH + BULK_COMPONENTS_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@GetMapping(value = FILE_PATH + BULK_COMPONENTS_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@Operation(summary = "Returns the components for all files of a dossier", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
FileComponentsList getComponentsOfDossier(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier that contains the file.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@Parameter(name = INCLUDE_DETAILS_PARAM, description = INCLUDE_DETAILS_DESCRIPTION) @RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails);
@PostMapping(value = FILE_PATH
+ BULK_COMPONENTS_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns the components for all files of a dossier", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
FileComponentsList getComponentsForFiles(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier that contains the file.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@Parameter(name = INCLUDE_DETAILS_PARAM, description = INCLUDE_DETAILS_DESCRIPTION) @RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails,
@RequestBody BulkComponentsRequest bulkComponentsRequest);
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = FILE_PATH + FILE_ID_PATH_VARIABLE + OVERRIDES_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Adds overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void addOverride(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier that contains the file.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@Parameter(name = FILE_ID_PARAM, description = "The identifier of the file that the components are requested for.", required = true) @PathVariable(FILE_ID_PARAM) String fileId,
@Parameter(name = COMPONENT_OVERRIDE_PARAM, description = "The object to override the component.", required = true) @RequestBody Component componentOverrideModel);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@GetMapping(value = FILE_PATH + FILE_ID_PATH_VARIABLE + OVERRIDES_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@Operation(summary = "Gets overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found")})
ComponentOverrideList getOverrides(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier that contains the file.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@Parameter(name = FILE_ID_PARAM, description = "The identifier of the file that the components are requested for.", required = true) @PathVariable(FILE_ID_PARAM) String fileId);
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = FILE_PATH + FILE_ID_PATH_VARIABLE + OVERRIDES_PATH + REVERT_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Reverts overrides for components", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void revertOverrides(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier that contains the file.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@Parameter(name = FILE_ID_PARAM, description = "The identifier of the file that the components are requested for.", required = true) @PathVariable(FILE_ID_PARAM) String fileId,
@Parameter(name = REVERT_OVERRIDE_PARAM) @RequestBody RevertOverrideRequest revertOverrideRequest);
}

View File

@ -18,7 +18,10 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import com.iqser.red.service.persistence.service.v2.api.external.model.DocuMineDossierRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttributes;
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierList;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatus;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@ -32,11 +35,15 @@ public interface DossierResource {
String PATH = ExternalApiConstants.BASE_PATH + DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DOSSIER_PATH;
String DOSSIER_ID_PARAM = "dossierId";
String DOSSIER_ID_PATH_PARAM = "/{" + DOSSIER_ID_PARAM + "}";
String ATTRIBUTES_PATH = "/attributes";
String CREATE_DOWNLOAD_PATH = "/create-download";
String INCLUDE_ACTIVE_PARAM = "includeActive";
String INCLUDE_ARCHIVED_PARAM = "includeArchived";
String INCLUDE_SOFT_DELETED_PARAM = "includeSoftDeleted";
String DELETE_PERMANENTLY_PARAM = "deletePermanently";
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
@ -72,6 +79,27 @@ public interface DossierResource {
@Operation(summary = "Deletes an existing dossier.", description = "Dossiers get soft-deleted unless specified other. A soft-deleted dossier can be restored during a retention period that is configured in the application settings. The default retention period is 96h.")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully deleted the dossier."), @ApiResponse(responseCode = "404", description = "Not found")})
void deleteDossier(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier to retrieve.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId);
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier to retrieve.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@Parameter(name = DELETE_PERMANENTLY_PARAM, description = "A toggle to determine the deletion mode for a dossier: `true`: The dossier will be permanently deleted and can't be restored. `false` (default): Soft-delete, allowing restoration within the application-configured retention period.") @RequestParam(name = DELETE_PERMANENTLY_PARAM, defaultValue = "false", required = false) boolean deletePermanently);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@ResponseBody
@PostMapping(value = PATH + DOSSIER_ID_PATH_PARAM + ATTRIBUTES_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Update or set attributes for a specific dossier.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Dossier attributes successfully updated."), @ApiResponse(responseCode = "404", description = "Not found")})
void setDossierAttributes(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of a dossier template", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of a dossier", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestBody DossierAttributes dossierAttributes);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = PATH + DOSSIER_ID_PATH_PARAM + CREATE_DOWNLOAD_PATH, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Initiate the creation of a download package for all files of a dossier.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully started the creation of the download package for all files of the requested dossier."), @ApiResponse(responseCode = "404", description = "Not found")})
DownloadStatus prepareDossierDownload(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of a dossier template", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of a dossier", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestBody DownloadRequest downloadRequest);
}

View File

@ -1,31 +1,41 @@
package com.iqser.red.service.persistence.service.v2.api.external.resource;
import java.util.List;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierAttributeDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.DossierStatusDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileAttributeDefinitionList;
import com.iqser.red.service.persistence.service.v2.api.external.model.RulesValidationResponse;
import com.iqser.red.service.persistence.service.v2.api.external.model.ReportTemplateList;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinition;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinitionAddRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinitionUpdateRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsValidationResponse;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingMetadataModel;
import com.iqser.red.service.persistence.service.v2.api.external.model.ComponentMappingSummary;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
@ApiResponses(value = {@ApiResponse(responseCode = "429", description = "Too many requests.")})
@ResponseStatus(HttpStatus.OK)
@ -35,15 +45,33 @@ public interface DossierTemplateResource {
String PATH = ExternalApiConstants.BASE_PATH + DOSSIER_TEMPLATE_PATH;
String ENTITY_RULES_PATH = "/entity-rules";
String DATE_FORMATS_PATH = "/date-formats";
String COMPONENT_RULES_PATH = "/component-rules";
String COMPONENT_MAPPINGS_PATH = "/component-mappings";
String DOSSIER_STATUS_DEFINITIONS_PATH = "/dossier-status-definitions";
String DOSSIER_ATTRIBUTE_DEFINITION_PATH = "/dossier-attribute-definitions";
String FILE_ATTRIBUTE_DEFINITIONS_PATH = "/file-attribute-definitions";
String DOSSIER_TEMPLATE_ID_PARAM = "dossierTemplateId";
String DOSSIER_TEMPLATE_ID_PATH_VARIABLE = "/{" + DOSSIER_TEMPLATE_ID_PARAM + "}";
String REPORT_TEMPLATES_PATH = "/report-templates";
String RULE_FILE_TYPE_PARAMETER_NAME = "ruleFileType";
String COMPONENT_MAPPING_ID_PARAM = "componentMappingId";
String COMPONENT_MAPPING_ID_PATH_VARIABLE = "/{" + COMPONENT_MAPPING_ID_PARAM + "}";
String DRY_RUN_PARAM = "dryRun";
String ENCODING_PARAM = "encoding";
String DELIMITER_PARAM = "delimiter";
String QUOTE_CHAR_PARAM = "quoteChar";
String MAPPING_NAME_PARAM = "name";
String INCLUDE_SOFT_DELETED = "includeSoftDeleted";
String COMPONENT_ID_PARAM = "componentId";
String COMPONENT_ID_PATH = "/{" + COMPONENT_ID_PARAM + "}";
String COMPONENT_IDS = "componentIds";
String COMPONENT_PATH = "/component-definitions";
String REORDER_PATH = "/reorder";
String RESTORE_PATH = "/restore";
@GetMapping(value = PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@ -61,10 +89,10 @@ public interface DossierTemplateResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + ENTITY_RULES_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Upload a component or entity rules file in drools format for a specific DossierTemplate.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found."), @ApiResponse(responseCode = "422", description = "Uploaded rules could not be verified.")})
ResponseEntity<RulesValidationResponse> uploadEntityRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun);
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified."), @ApiResponse(responseCode = "422", description = "Uploaded rules could not be compiled.")})
ResponseEntity<DroolsValidationResponse> uploadEntityRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun);
@ResponseBody
@ -80,10 +108,10 @@ public interface DossierTemplateResource {
+ DOSSIER_TEMPLATE_ID_PATH_VARIABLE
+ COMPONENT_RULES_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Upload a component or entity rules file in drools format for a specific DossierTemplate.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found."), @ApiResponse(responseCode = "422", description = "Uploaded rules could not be verified.")})
ResponseEntity<RulesValidationResponse> uploadComponentRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun);
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Rules upload successful."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified."), @ApiResponse(responseCode = "422", description = "Uploaded rules could not be compiled.")})
ResponseEntity<DroolsValidationResponse> uploadComponentRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@Parameter(name = DRY_RUN_PARAM, description = "If true rules will be only validated not stored.") @RequestParam(value = DRY_RUN_PARAM, required = false, defaultValue = "false") boolean dryRun);
@ResponseBody
@ -94,9 +122,152 @@ public interface DossierTemplateResource {
ResponseEntity<?> downloadComponentRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DATE_FORMATS_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Upload a date formats file for a specific DossierTemplate.")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Date formats upload successful."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found."), @ApiResponse(responseCode = "400", description = "Uploaded date formats could not be verified."), @ApiResponse(responseCode = "422", description = "Uploaded date formats file could not be parsed.")})
ResponseEntity<?> uploadDateFormats(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file);
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@Operation(summary = "Returns file containing the currently used date formats.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
@GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DATE_FORMATS_PATH)
ResponseEntity<?> downloadDateFormats(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);
@Operation(summary = "Get the file attribute definitions of a DossierTemplate.", description = "None")
@GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + FILE_ATTRIBUTE_DEFINITIONS_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "File attribute definitions returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
FileAttributeDefinitionList getFileAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);
@Operation(summary = "Get the component mapping summaries of a DossierTemplate.", description = "None")
@GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping views returned successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
ComponentMappingSummary getComponentMappingSummaries(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);
@Operation(summary = "Upload a new component mapping to a DossierTemplate.", description = "None")
@PostMapping(value = PATH
+ DOSSIER_TEMPLATE_ID_PATH_VARIABLE
+ COMPONENT_MAPPINGS_PATH, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping uploaded successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")})
ComponentMappingMetadataModel uploadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@Parameter(name = MAPPING_NAME_PARAM, description = "String of what the mapping should be accessible under. If left empty, the name of the file without the ending will be used as name.") @RequestParam(value = MAPPING_NAME_PARAM, required = false, defaultValue = "") String name,
@Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is UTF-8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding,
@Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") String delimiter,
@Parameter(name = QUOTE_CHAR_PARAM, description = "The quote char used in the file. Default is '\"'") @RequestParam(value = QUOTE_CHAR_PARAM, required = false, defaultValue = "\"") String quoteChar);
@Operation(summary = "Update an existing component mapping of a DossierTemplate.", description = "None")
@PutMapping(value = PATH
+ DOSSIER_TEMPLATE_ID_PATH_VARIABLE
+ COMPONENT_MAPPINGS_PATH
+ COMPONENT_MAPPING_ID_PATH_VARIABLE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Component mapping updated successfully."), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")})
ComponentMappingMetadataModel updateMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId,
@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@Parameter(name = MAPPING_NAME_PARAM, description = "String of what the mapping should be accessible under. If left empty, the name of the file without the ending will be used as name.") @RequestParam(value = MAPPING_NAME_PARAM, required = false, defaultValue = "") String name,
@Parameter(name = ENCODING_PARAM, description = "The encoding of the file. Default is UTF-8.") @RequestParam(value = ENCODING_PARAM, required = false, defaultValue = "UTF-8") String encoding,
@Parameter(name = DELIMITER_PARAM, description = "The delimiter used in the file. Default is ','") @RequestParam(value = DELIMITER_PARAM, required = false, defaultValue = ",") String delimiter,
@Parameter(name = QUOTE_CHAR_PARAM, description = "The quote char used in the file. Default is '\"'") @RequestParam(value = QUOTE_CHAR_PARAM, required = false, defaultValue = "\"") String quoteChar);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Returns file containing the specified mapping as a file.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")})
@GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE)
ResponseEntity<?> downloadMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Deletes a specified mapping.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified mapping is not found.")})
@DeleteMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_MAPPINGS_PATH + COMPONENT_MAPPING_ID_PATH_VARIABLE)
ResponseEntity<?> deleteMapping(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_MAPPING_ID_PARAM) String componentMappingId);
@Operation(summary = "Returns the list of all existing dossier status definitions.", description = "None")
@GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DOSSIER_STATUS_DEFINITIONS_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully returned the dossier status definitions for the specified dossier template."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
DossierStatusDefinitionList getDossierStatusDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);
@Operation(summary = "Returns the list of all existing dossier attribute definitions.", description = "None")
@GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DOSSIER_ATTRIBUTE_DEFINITION_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully returned the dossier attribute definitions for the specified dossier template."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
DossierAttributeDefinitionList getDossierAttributeDefinitions(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);
@ResponseBody
@ResponseStatus(HttpStatus.CREATED)
@Operation(summary = "Create new components", description = "Create new components for a given dossier template.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Created")})
@PostMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
List<ComponentDefinition> createComponents(@Valid @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@RequestBody List<ComponentDefinitionAddRequest> componentDefinitionUpdateRequests);
@ResponseBody
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Get all components", description = "Get all components for a given dossier template.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
List<ComponentDefinition> getComponents(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = INCLUDE_SOFT_DELETED, description = "Include files which are soft deleted. Default is false.") @RequestParam(value = INCLUDE_SOFT_DELETED, defaultValue = "false") boolean includeSoftDeleted);
@ResponseBody
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Get a component by ID", description = "Get specific details for a component by ID.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The DossierTemplate or the specified component is not found.")})
@GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH + COMPONENT_ID_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
ComponentDefinition getComponent(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @PathVariable(COMPONENT_ID_PARAM) String componentId);
@ResponseBody
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Update components", description = "Update existing components.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@PutMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
List<ComponentDefinition> updateComponents(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Valid @RequestBody List<ComponentDefinitionUpdateRequest> componentDefinitionUpdateRequests);
@ResponseBody
@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(summary = "Delete components", description = "Delete existing components by their IDs.")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "No Content")})
@DeleteMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH)
void deleteComponents(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @RequestParam(name = COMPONENT_IDS) List<String> componentIds);
@ResponseBody
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Restore components", description = "Restore previously soft deleted components based on their IDs.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@PutMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH + RESTORE_PATH)
List<ComponentDefinition> restoreComponents(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @RequestParam(name = COMPONENT_IDS) List<String> componentIds);
@ResponseBody
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Reorder components", description = "Reorder components based on their rank.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + COMPONENT_PATH + REORDER_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
List<ComponentDefinition> reorderComponents(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId, @RequestParam(name = COMPONENT_IDS) List<String> componentIds);
@GetMapping(value = PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + REPORT_TEMPLATES_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns the list of all available report templates", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully returned the report templates for the specified dossier template."), @ApiResponse(responseCode = "404", description = "The DossierTemplate is not found.")})
ReportTemplateList getReportTemplates(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId);
}

View File

@ -0,0 +1,56 @@
package com.iqser.red.service.persistence.service.v2.api.external.resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatus;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatusList;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ApiResponses(value = {@ApiResponse(responseCode = "429", description = "Too many requests.")})
@ResponseStatus(HttpStatus.OK)
public interface DownloadResource {
String DOWNLOADS_PATH = "/downloads";
String PATH = ExternalApiConstants.BASE_PATH + DOWNLOADS_PATH;
String DOWNLOAD_ID_PARAM = "downloadId";
String DOWNLOAD_ID_PATH_PARAM = "/{" + DOWNLOAD_ID_PARAM + "}";
String DOWNLOAD_PATH = "/download";
@GetMapping(value = PATH, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Get the list of downloads for the current user", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "List of all downloads of the current users successfully retrieved.")})
DownloadStatusList getDownloadStatusList();
@GetMapping(value = PATH + DOWNLOAD_ID_PATH_PARAM, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Get the status for a specific download of the current user", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Status of the download successfully retrieved.")})
DownloadStatus getDownloadStatus(@Parameter(name = DOWNLOAD_ID_PARAM, description = "The identifier for the file to download.", required = true) @PathVariable(DOWNLOAD_ID_PARAM) String downloadId);
@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping(value = PATH + DOWNLOAD_ID_PATH_PARAM)
@Operation(summary = "Deletes a specific download.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Download deletion successful. This confirms the absence of the specified download, irrespective of its previous existence.")})
void deleteDownload(@Parameter(name = DOWNLOAD_ID_PARAM, description = "The identifier for the file to download.", required = true) @PathVariable(DOWNLOAD_ID_PARAM) String downloadId);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Download the download package.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully downloaded the requested file."), @ApiResponse(responseCode = "404", description = "Download not found. This happens if the requested download does not exist for the current user.")})
@GetMapping(value = PATH + DOWNLOAD_ID_PATH_PARAM + DOWNLOAD_PATH, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_OCTET_STREAM_VALUE})
void download(@Parameter(name = DOWNLOAD_ID_PARAM, description = "The identifier for the file to download.", required = true) @PathVariable(DOWNLOAD_ID_PARAM) String downloadId);
}

View File

@ -26,6 +26,9 @@ import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttributes;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileUploadResult;
import com.iqser.red.service.persistence.service.v2.api.external.model.BulkDownloadRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.DownloadStatus;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileDeleteRequest;
import com.iqser.red.service.persistence.service.v2.api.external.model.FileStatusList;
@ -42,7 +45,10 @@ public interface FileResource {
String PATH = ExternalApiConstants.BASE_PATH + DOSSIER_TEMPLATE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE + DOSSIER_PATH + DOSSIER_ID_PATH_PARAM + FILE_PATH;
String BULK_DELETE_PATH = "/bulk/delete";
String FILE_ATTRIBUTES_PATH = "/attributes";
String CREATE_DOWNLOAD_PATH = "/create-download";
String BULK_CREATE_DOWNLOAD_PATH = "/bulk/create-download";
String KEEP_MANUAL_CHANGES_PARAM = "keepManualChanges";
String DISABLE_AUTOMATIC_ANALYSIS_PARAM = "disableAutomaticAnalysis";
String DELETE_PERMANENTLY_PARAM = "deletePermanently";
String FILE_PARAM = "file";
String FILE_ID_PARAM = "fileId";
@ -57,7 +63,8 @@ public interface FileResource {
FileUploadResult upload(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier to where the file is to be uploaded.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@Parameter(name = FILE_PARAM, description = "The file to be uploaded.", required = true) @Schema(type = "string", format = "binary", name = "file") @RequestPart(name = FILE_PARAM) MultipartFile file,
@Parameter(name = KEEP_MANUAL_CHANGES_PARAM, description = "A Toggle to keep manual changes: Manual changes are manually added annotations or manipulations on existing ones. If set to `true` the system keeps the manual changes on re-uploading (overwriting) the file.") @RequestParam(value = KEEP_MANUAL_CHANGES_PARAM, required = false, defaultValue = "false") boolean keepManualChanges);
@Parameter(name = KEEP_MANUAL_CHANGES_PARAM, description = "A Toggle to keep manual changes: Manual changes are manually added annotations or manipulations on existing ones. If set to `true` the system keeps the manual changes on re-uploading (overwriting) the file.") @RequestParam(value = KEEP_MANUAL_CHANGES_PARAM, required = false, defaultValue = "false") boolean keepManualChanges,
@Parameter(name = DISABLE_AUTOMATIC_ANALYSIS_PARAM, description = "Disables automatic redaction for the uploaded file, imports only imported redactions") @RequestParam(value = DISABLE_AUTOMATIC_ANALYSIS_PARAM, required = false, defaultValue = "false") boolean disableAutomaticAnalysis);
@ResponseStatus(value = HttpStatus.OK)
@ -103,12 +110,34 @@ public interface FileResource {
@Parameter(name = DELETE_PERMANENTLY_PARAM, description = "A Toggle to permanently delete the file: If `true` the file will be deleted permanently, i.e. in contrast to a soft-delete (value is `false`) the file cannot be restored anymore. This can be applied also on previously soft-deleted files.") @RequestParam(name = DELETE_PERMANENTLY_PARAM, defaultValue = "false", required = false) boolean deletePermanently);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@Operation(summary = "Set file attributes to an existing file", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@PostMapping(value = PATH + FILE_ID_PATH_VARIABLE + FILE_ATTRIBUTES_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
void setFileAttributes(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of the dossier template that is used for the dossier.", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of the dossier that contains the file.", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@Parameter(name = FILE_ID_PARAM, description = "The identifier of the file of which the file attributes are set.", required = true) @PathVariable(FILE_ID_PARAM) String fileId,
@RequestBody FileAttributes fileAttributes);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = PATH + FILE_ID_PATH_VARIABLE + CREATE_DOWNLOAD_PATH, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Initiate the creation of a download package for a single file of a dossier.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully started the creation of the download package for the requested file."), @ApiResponse(responseCode = "404", description = "Not found")})
DownloadStatus prepareFileDownload(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of a dossier template", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of a dossier", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@Parameter(name = FILE_ID_PARAM, description = "The identifier of a file", required = true) @PathVariable(FILE_ID_PARAM) String fileId,
@RequestBody DownloadRequest downloadRequest);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = PATH + BULK_CREATE_DOWNLOAD_PATH, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Initiate the creation of a download package for specific files within a dossier.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully started the creation of the download package for the requested files."), @ApiResponse(responseCode = "404", description = "Not found")})
DownloadStatus prepareBulkDownload(@Parameter(name = DOSSIER_TEMPLATE_ID_PARAM, description = "The identifier of a dossier template", required = true) @PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@Parameter(name = DOSSIER_ID_PARAM, description = "The identifier of a dossier", required = true) @PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestBody BulkDownloadRequest bulkDownloadRequest);
}

View File

@ -1,6 +1,6 @@
plugins {
id("com.iqser.red.service.java-conventions")
id("io.freefair.lombok") version "8.4"
id("io.freefair.lombok") version "8.6"
}
dependencies {

View File

@ -0,0 +1,46 @@
package com.iqser.red.service.persistence.v1.internal.api.controller;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.DateFormatsEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.exception.RulesTimeoutDetectedException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DateFormatsPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.internal.resources.DateFormatsResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class DateFormatsInternalController implements DateFormatsResource {
private final DateFormatsPersistenceService dateFormatsPersistenceService;
@Override
public JSONPrimitive<String> getDateFormats(String dossierTemplateId) {
DateFormatsEntity dateFormats = getDateFormatsForDossierTemplate(dossierTemplateId);
return new JSONPrimitive<>(dateFormats.getValue());
}
@Override
public long getVersion(String dossierTemplateId) {
DateFormatsEntity dateFormats = getDateFormatsForDossierTemplate(dossierTemplateId);
return dateFormats.getVersion();
}
private DateFormatsEntity getDateFormatsForDossierTemplate(String dossierTemplateId) {
var dateFormatsOptional = dateFormatsPersistenceService.getDateFormats(dossierTemplateId);
if (dateFormatsOptional.isEmpty()) {
throw new NotFoundException(String.format("No date formats file found for dossierTemplateId %s", dossierTemplateId));
}
return dateFormatsOptional.get();
}
}

View File

@ -32,17 +32,39 @@ public class DictionaryInternalController implements DictionaryResource {
@Override
public List<Type> getAllTypesForDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = FROM_VERSION_PARAM, required = false) Long fromVersion,
@RequestParam(value = INCLUDE_DELETED_PARAMETER_NAME, required = false, defaultValue = "false") boolean includeDeleted) {
return convert(dictionaryPersistenceService.getAllTypesForDossierTemplate(dossierTemplateId, includeDeleted), Type.class);
var targets = convert(dictionaryPersistenceService.getAllTypesForDossierTemplate(dossierTemplateId, includeDeleted), Type.class);
setEntriesForTypes(fromVersion, targets);
return targets;
}
@Override
public List<Type> getAllTypesForDossier(@PathVariable(DOSSIER_ID_PARAMETER_NAME) String dossierId,
@RequestParam(value = FROM_VERSION_PARAM, required = false) Long fromVersion,
@RequestParam(value = INCLUDE_DELETED_PARAMETER_NAME, required = false, defaultValue = "false") boolean includeDeleted) {
return convert(dictionaryPersistenceService.getAllTypesForDossier(dossierId, includeDeleted), Type.class);
var targets = convert(dictionaryPersistenceService.getAllTypesForDossier(dossierId, includeDeleted), Type.class);
setEntriesForTypes(fromVersion, targets);
return targets;
}
private void setEntriesForTypes(Long fromVersion, List<Type> targets) {
targets.forEach(target -> {
setEntriesForType(target.getTypeId(), target, fromVersion != null ? fromVersion : -1);
});
}
private void setEntriesForType(String typeId, Type target, Long fromVersion) {
target.setEntries(convert(entryPersistenceService.getEntries(typeId, DictionaryEntryType.ENTRY, fromVersion), DictionaryEntry.class));
target.setFalsePositiveEntries(convert(entryPersistenceService.getEntries(typeId, DictionaryEntryType.FALSE_POSITIVE, fromVersion), DictionaryEntry.class));
target.setFalseRecommendationEntries(convert(entryPersistenceService.getEntries(typeId, DictionaryEntryType.FALSE_RECOMMENDATION, fromVersion), DictionaryEntry.class));
}
@ -51,9 +73,7 @@ public class DictionaryInternalController implements DictionaryResource {
var entity = dictionaryPersistenceService.getType(typeId, true);
var target = convert(entity, Type.class);
target.setEntries(convert(entryPersistenceService.getEntries(typeId, DictionaryEntryType.ENTRY, fromVersion), DictionaryEntry.class));
target.setFalsePositiveEntries(convert(entryPersistenceService.getEntries(typeId, DictionaryEntryType.FALSE_POSITIVE, fromVersion), DictionaryEntry.class));
target.setFalseRecommendationEntries(convert(entryPersistenceService.getEntries(typeId, DictionaryEntryType.FALSE_RECOMMENDATION, fromVersion), DictionaryEntry.class));
setEntriesForType(typeId, target, fromVersion);
return target;
}

View File

@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierAttributeConfigEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.utils.DossierAttributeConfigMapper;
import com.iqser.red.service.persistence.service.v1.api.internal.resources.DossierAttributesConfigResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierAttributeConfig;
@ -27,7 +28,7 @@ public class DossierAttributesConfigInternalController implements DossierAttribu
@Override
public List<DossierAttributeConfig> getDossierAttributes(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {
return convert(dossierAttributeConfigPersistenceService.getDossierAttributes(dossierTemplateId), DossierAttributeConfig.class);
return convert(dossierAttributeConfigPersistenceService.getDossierAttributes(dossierTemplateId), DossierAttributeConfig.class, new DossierAttributeConfigMapper());
}
}

View File

@ -5,7 +5,7 @@ import static com.knecon.fforesight.databasetenantcommons.providers.utils.MagicC
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierTemplateImportService;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.DossierTemplateImportService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
import com.iqser.red.service.persistence.service.v1.api.internal.resources.DossierTemplateResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplate;

View File

@ -22,7 +22,7 @@ public class EntityLogInternalController implements EntityLogResource {
@Override
public EntityLog getEntityLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "excludedType", required = false) List<String> excludedTypes,
@RequestParam(value = "excludedType", required = false, defaultValue = "") List<String> excludedTypes,
@RequestParam(value = "includeUnprocessed", required = false, defaultValue = FALSE) boolean includeUnprocessed) {
return entityLogService.getEntityLog(dossierId, fileId, excludedTypes, includeUnprocessed);

View File

@ -8,6 +8,7 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.utils.FileAttributeConfigMapper;
import com.iqser.red.service.persistence.service.v1.api.internal.resources.FileAttributesConfigResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileAttributeConfig;
@ -25,7 +26,7 @@ public class FileAttributesConfigInternalController implements FileAttributesCon
@Override
public List<FileAttributeConfig> getFileAttributeConfigs(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {
return convert(fileAttributeConfigPersistenceService.getFileAttributes(dossierTemplateId), FileAttributeConfig.class);
return convert(fileAttributeConfigPersistenceService.getFileAttributes(dossierTemplateId), FileAttributeConfig.class, new FileAttributeConfigMapper());
}
}

View File

@ -5,7 +5,6 @@ import java.time.temporal.ChronoUnit;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.configuration.MessagingConfiguration;
@ -39,7 +38,7 @@ public class FileStatusProcessingUpdateInternalController implements FileStatusP
fileStatusProcessingUpdateService.preprocessingFailed(dossierId,
fileId,
new FileErrorInfo("preprocessing failed",
MessagingConfiguration.PRE_PROCESSING_DLQ,
MessagingConfiguration.PREPROCESSING_DLQ,
"pdftron-service",
OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS)));
}
@ -57,6 +56,7 @@ public class FileStatusProcessingUpdateInternalController implements FileStatusP
}
@Deprecated
public void ocrSuccessful(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId) {
fileStatusProcessingUpdateService.ocrSuccessful(dossierId, fileId);
@ -81,12 +81,14 @@ public class FileStatusProcessingUpdateInternalController implements FileStatusP
}
@Deprecated
public void ocrProcessing(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId) {
fileStatusProcessingUpdateService.ocrProcessing(dossierId, fileId);
fileStatusProcessingUpdateService.ocrProcessing(fileId);
}
@Deprecated
public void ocrFailed(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId) {
fileStatusProcessingUpdateService.ocrFailed(dossierId,
@ -98,6 +100,7 @@ public class FileStatusProcessingUpdateInternalController implements FileStatusP
}
@Deprecated
public void ocrFailed(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody FileErrorInfo fileErrorInfo) {
fileStatusProcessingUpdateService.ocrFailed(dossierId, fileId, fileErrorInfo);
@ -121,7 +124,7 @@ public class FileStatusProcessingUpdateInternalController implements FileStatusP
fileStatusProcessingUpdateService.indexingFailed(dossierId,
fileId,
new FileErrorInfo("indexing failed",
MessagingConfiguration.INDEXING_DQL,
MessagingConfiguration.INDEXING_DLQ,
"search-service",
OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS)));
}

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