Compare commits

..

1523 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
Corina Olariu
bc334b9760 Merge branch 'RED-7049' into 'master'
RED-7049 - Wrong error code when adding entity with already existing rank

Closes RED-7049

See merge request redactmanager/persistence-service!371
2024-03-04 13:34:04 +01: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
Corina Olariu
98f743052d RED-7049 - Wrong error code when adding entity with already existing rank
- update junit so the rank of the types differs

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-03-04 11:00:51 +02:00
Corina Olariu
4d107b2475 RED-7049 - Wrong error code when adding entity with already existing rank
- correct the query to get the type with same rank.
- changed to get the list of types instead of a one result for the case with several types with the same rank
- added junit tests

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-03-04 10:30:54 +02:00
Dominique Eifländer
3c4c93a634 hotfix: blobstorage migration gz fix 2024-02-29 12:14:03 +01:00
Kilian Schüttler
2fa7b4337a Merge branch 'reformat' into 'master'
reformat

See merge request redactmanager/persistence-service!369
2024-02-28 15:27:29 +01:00
Kilian Schuettler
4aa5b0ee34 reformat 2024-02-28 14:57:21 +01:00
Kilian Schüttler
23b80ca615 Merge branch 'RED-8615' into 'master'
RED-8615: change ManualChange with dict behavior

Closes RED-8615

See merge request redactmanager/persistence-service!366
2024-02-28 11:24:45 +01:00
Kilian Schuettler
b196544131 RED-8615: Revert and change the current behaviour of manualChanges 2024-02-28 09:32:45 +01:00
Kilian Schüttler
9c33404490 Merge branch 'RED-7384' into 'master'
RED-7384: migration fixes

Closes RED-7384

See merge request redactmanager/persistence-service!363
2024-02-26 11:39:29 +01:00
Kilian Schuettler
0b4e51907c RED-7384: migration fixes
* added functionality to add manualRedactionEntries during the migration
2024-02-23 14:37:50 +01:00
Corina Olariu
ea3d47f435 Merge branch 'RED-8589' into 'master'
RED-8589 - Add "MANUAL" engine to all annotations that has entries in manualChanges

Closes RED-8589

See merge request redactmanager/persistence-service!361
2024-02-23 10:50:18 +01:00
Corina Olariu
6375cedaee Merge branch 'RED-7886-dryrun' into 'master'
RED-7886 - Endpoint for rule validation

Closes RED-7886

See merge request redactmanager/persistence-service!360
2024-02-23 10:21:53 +01:00
Corina Olariu
17667d067d RED-8589 - Add "MANUAL" engine to all annotations that has entries in manualChanges
- add Engine.Manual for manual changes in the entity log entries in case the analysis is stopped

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-23 09:10:35 +02:00
Corina Olariu
e8a942d764 RED-7886 - Endpoint for rule validation
- renamed from runDry to dryRun

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-22 11:31:00 +02:00
Corina Olariu
80c7c867ae RED-7886 - Endpoint for rule validation
- renamed from dryRunParameter to dryRun

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-22 11:19:30 +02:00
Ali Oezyetimoglu
f86e4ea993 Merge branch 'RED-8480' into 'master'
RED-8480: removed Nonnull annotation for legal basis when recategorizing;...

Closes RED-8480

See merge request redactmanager/persistence-service!359
2024-02-21 16:40:59 +01:00
Ali Oezyetimoglu
d5244846a9 RED-8480: removed Nonnull annotation for legal basis when recategorizing; changed to empty String, if null is passed 2024-02-21 15:03:37 +01:00
Corina Olariu
4091f21076 RED-7886 - Endpoint for rule validation
- add dry-run parameter, based on this save the rules or not and include warnings in the responses.
- added junit tests

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-21 15:40:10 +02:00
Corina Olariu
307ae65080 Merge branch 'RED-7886' into 'master'
RED-7886 - Endpoint for rule validation

Closes RED-7886

See merge request redactmanager/persistence-service!358
2024-02-20 15:03:54 +01:00
Corina Olariu
5d4badce06 RED-7886 - Endpoint for rule validation
- Include the warning messages for deprecated imports rules in response

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-20 12:49:16 +02:00
Corina Olariu
574f05b800 Merge branch 'RED-5716' into 'master'
RED-5716 - Deprecate old prepare endpoints

Closes RED-5716

See merge request redactmanager/persistence-service!356
2024-02-20 07:54:05 +01:00
Andrei Isvoran
11fa795977 Merge branch 'RED-8489' into 'master'
RED-8494 - Don't allow creation of File Attributes with empty label

Closes RED-8489

See merge request redactmanager/persistence-service!357
2024-02-19 17:33:44 +01:00
Andrei Isvoran
50d25ead38 RED-8494 - Don't allow creation of File Attributes with empty label 2024-02-19 17:33:44 +01:00
Andrei Isvoran
d7c1d233bb Merge branch 'RED-6940' into 'master'
RED-6940 - Sanitize /r in dictionary entries

Closes RED-6940

See merge request redactmanager/persistence-service!355
2024-02-19 15:06:30 +01:00
Andrei Isvoran
4eb9240bf7 RED-6940 - Sanitize /r in dictionary entries 2024-02-19 15:06:30 +01:00
Corina Olariu
318d15f758 RED-5716 - Deprecate old prepare endpoints
- deprecate the old endpoint, update test unit with new endpoint

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-19 15:39:36 +02:00
Ali Oezyetimoglu
caba58ec19 Merge branch 'RED-8480-2' into 'master'
Resolve RED-8480 "2"

Closes RED-8480

See merge request redactmanager/persistence-service!354
2024-02-16 12:57:57 +01:00
Ali Oezyetimoglu
e8c3d3dc65 RED-8480: small fixes 2024-02-16 11:49:03 +01:00
Ali Oezyetimoglu
179d66a796 RED-8480: small validation change 2024-02-16 10:23:34 +01:00
Yannik Hampe
57b046d821 Merge branch 'RED-8481' into 'master'
RED-8481: Use visual layout parsing to detect signatures

Closes RED-8481

See merge request redactmanager/persistence-service!351
2024-02-16 08:46:36 +01:00
Ali Oezyetimoglu
e681651420 RED-8480: added some checks and tests 2024-02-15 22:19:51 +01:00
Ali Oezyetimoglu
d2b6971f04 RED-8480: added legalBasis to recategorize endpoint and migrated it to the DB 2024-02-15 15:41:55 +01:00
yhampe
c9c75fe9e1 RED-8481: Use visual layout parsing to detect signatures
fixing checkstyle
2024-02-15 15:20:58 +01:00
yhampe
09c451d2ed RED-8481: Use visual layout parsing to detect signatures
updated layout parsing version
2024-02-15 15:01:09 +01:00
Andrei Isvoran
d54b33607b Merge branch 'RED-7086' into 'master'
RED-7086 - Add ResponseStatus to hard delete files endpoint and adjust description

Closes RED-7086

See merge request redactmanager/persistence-service!352
2024-02-15 08:23:36 +01:00
Andrei Isvoran
cbef7c87b5 RED-7086 - Add ResponseStatus to hard delete files endpoint and adjust description 2024-02-15 08:23:36 +01:00
Andrei Isvoran
f3bd2b0120 Merge branch 'RED-7086-fixes' into 'master'
RED-7086 - Enable async file deletion and fix response code

Closes RED-7086

See merge request redactmanager/persistence-service!349
2024-02-14 08:52:02 +01:00
Andrei Isvoran
39000eed5a RED-7086 - Enable async file deletion and fix response code 2024-02-14 08:52:02 +01:00
Kilian Schüttler
fb58362012 Merge branch 'RED-7384' into 'master'
RED-7384: reindex during migration to avoid update error in approved files

Closes RED-7384

See merge request redactmanager/persistence-service!350
2024-02-13 13:35:15 +01:00
Kilian Schuettler
97b2e5f732 RED-7384: reindex during migration to avoid update error in approved files 2024-02-13 13:27:56 +01:00
Kilian Schüttler
5cc699043c Merge branch 'RED-8385' into 'master'
RED-8385: delete viewer doc, when rotating origin file, such that it is recreated with the rotation

Closes RED-8385

See merge request redactmanager/persistence-service!348
2024-02-12 16:27:50 +01:00
Kilian Schüttler
500d215069 Merge branch 'RED-7384' into 'master'
RED-7384: fix db fields for manual changes and tenant details

Closes RED-7384

See merge request redactmanager/persistence-service!347
2024-02-12 16:20:40 +01:00
Kilian Schuettler
768384a84c RED-8385: delete viewer doc, when rotating origin file, such that it is recreated with the rotation 2024-02-12 16:19:15 +01:00
Kilian Schuettler
ce39968508 RED-7384: fix db fields for manual changes and tenant details 2024-02-12 15:47:54 +01:00
Maverick Studer
59b29ccd38 Merge branch 'RED-8432' into 'master'
RED-8432: Return 400 if invalid date for file attributes is set

Closes RED-8432

See merge request redactmanager/persistence-service!346
2024-02-12 13:58:49 +01:00
maverickstuder
51f28963ff RED-8432: Return 400 if invalid date for file attributes is set 2024-02-12 09:14:49 +01:00
yhampe
3664f6800a RED-8481: Use visual layout parsing to detect signatures
integrated service
2024-02-12 09:12:59 +01:00
Corina Olariu
99944c9fef Merge branch 'RED-8361-update' into 'master'
RED-8361 - Returned error status codes should be checked

Closes RED-8361

See merge request redactmanager/persistence-service!344
2024-02-09 18:27:01 +01:00
Corina Olariu
8db9bd1be0 RED-8361 - Returned error status codes should be checked
- change description to include also the case that the dossier / file is not found to the error message

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-09 18:23:16 +02:00
Corina Olariu
a4f3edd144 RED-8361 - Returned error status codes should be checked
- change description to include also the case that the file is not found

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-09 17:49:31 +02:00
Andrei Isvoran
8f5d3ef9af Merge branch 'RED-7086' into 'master'
RED-7086 - Deprecate hard-delete files and add new endpoint with POST request

Closes RED-7086

See merge request redactmanager/persistence-service!345
2024-02-09 15:32:31 +01:00
Andrei Isvoran
34c7e51ae8 RED-7086 - Deprecate hard-delete files and add new endpoint with POST request 2024-02-09 15:32:31 +01:00
Corina Olariu
3e67dd8055 RED-8361 - Returned error status codes should be checked
- add authorities to the endpoints that don't have them

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-09 12:13:51 +02:00
Corina Olariu
a7c4bf866c RED-8361 - Returned error status codes should be checked
- add some view/access permissions for some endpoints
- modify messages so in case dossier is not found to not include the id

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-08 18:21:36 +02:00
Maverick Studer
bf66fdf4b7 Merge branch 'RED-8432' into 'master'
RED-8432: Return 400 if invalid input for attributes is given

Closes RED-8432

See merge request redactmanager/persistence-service!343
2024-02-07 14:01:07 +01:00
Maverick Studer
97cc851de1 RED-8432: Return 400 if invalid input for attributes is given 2024-02-07 14:01:06 +01:00
Maverick Studer
5931058a6d Merge branch 'RED-8454' into 'master'
RED-8454: Remove red-read-dossiers for RED_ADMIN and RED_USERS_ADMIN role

Closes RED-8454

See merge request redactmanager/persistence-service!342
2024-02-06 16:25:36 +01:00
maverickstuder
d317457e05 RED-8454: Remove red-read-dossiers for RED_ADMIN and RED_USERS_ADMIN role 2024-02-06 16:14:49 +01:00
Ali Oezyetimoglu
3d0697003b Merge branch 'RED-8380-2' into 'master'
RED-8380: do not filter if dossiers are empty in DossierStatusFilteringMapper

Closes RED-8380

See merge request redactmanager/persistence-service!341
2024-02-06 16:11:04 +01:00
Maverick Studer
2daefe0868 Merge branch 'RED-7368' into 'master'
RED-7368: Placeholder not updated after update/rename file/dossier attribute

Closes RED-7368

See merge request redactmanager/persistence-service!340
2024-02-06 16:10:09 +01:00
Ali Oezyetimoglu
2a57468e34 RED-8380: do not filter if dossiers are empty in DossierStatusFilteringMapper 2024-02-06 15:49:30 +01:00
maverickstuder
6b665066f3 RED-7368: Placeholder not updated after update/rename file/dossier attribute 2024-02-06 15:31:59 +01:00
Dominique Eifländer
cd171e22f0 Merge branch 'RED-8415' into 'master'
RED-8415: Fixed 500 when fileAttribute label length is exacly 255 chars

Closes RED-8415

See merge request redactmanager/persistence-service!339
2024-02-06 12:50:06 +01:00
Dominique Eifländer
52551609e7 RED-8415: Fixed 500 when fileAttribute label length is exacly 255 chars 2024-02-06 12:42:01 +01:00
Andrei Isvoran
737d2ee36b Merge branch 'RED-8428-dm' into 'master'
RED-8428 - Keep RESIZE for backwards compatibility for documine. RESIZE will...

Closes RED-8428

See merge request redactmanager/persistence-service!338
2024-02-05 14:05:20 +01:00
Andrei Isvoran
bda907d283 RED-8428 - Keep RESIZE for backwards compatibility for documine. RESIZE will... 2024-02-05 14:05:20 +01:00
Andrei Isvoran
8540d5e865 Merge branch 'RED-8428-resize' into 'master'
RED-8428 - Keep only RESIZE for ManualRedactionType in redaction log

Closes RED-8428

See merge request redactmanager/persistence-service!337
2024-02-05 10:30:49 +01:00
Andrei Isvoran
42d1bc8848 RED-8428 - Keep only RESIZE for ManualRedactionType in redaction log 2024-02-05 11:21:27 +02:00
Corina Olariu
4bd7a50f97 Merge branch 'RED-8361-fix' into 'master'
RED-8361 - Returned error status codes should be checked

Closes RED-8361

See merge request redactmanager/persistence-service!335
2024-02-05 09:33:54 +01:00
Andrei Isvoran
0ad6de9fb7 Merge branch 'RED-8428' into 'master'
RED-8428 - Add RESIZE_LOCALLY and RESIZE_IN_DICTIONARY in ManualRedactionType to differentiate between the two operations

Closes RED-8428

See merge request redactmanager/persistence-service!336
2024-02-05 09:20:18 +01:00
Andrei Isvoran
85c4f77828 RED-8428 - Add RESIZE_LOCALLY and RESIZE_IN_DICTIONARY in ManualRedactionType to differentiate between the two operations 2024-02-05 09:20:18 +01:00
Corina Olariu
17d9b13042 RED-8361 - Returned error status codes should be checked
- rework for DictionaryController and VersionsController to return empty result in case user has no view permissions

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-02 17:24:13 +02:00
Maverick Studer
8cf94c2cd7 Merge branch 'RED-8372' into 'master'
RED-8372: Include additional info about redactions in redaction annotations

Closes RED-8372

See merge request redactmanager/persistence-service!334
2024-02-02 13:27:56 +01:00
Maverick Studer
add3eea2f3 RED-8372: Include additional info about redactions in redaction annotations 2024-02-02 13:27:56 +01:00
Dominique Eifländer
61fd030dc2 Merge branch 'RED-8171' into 'master'
RED-8171: Traces do not stop at @Async

Closes RED-8171

See merge request redactmanager/persistence-service!333
2024-02-02 13:23:30 +01:00
Dominique Eifländer
2b019542bd RED-8171: Traces do not stop at @Async 2024-02-02 13:14:12 +01:00
Corina Olariu
bb58667bb2 Merge branch 'RED-8427' into 'master'
RED-8427 - Last manual change for Force Hint action should be FORCE_HINT

Closes RED-8427

See merge request redactmanager/persistence-service!332
2024-02-02 11:12:02 +01:00
Corina Olariu
d5f8a5c6f4 RED-8427 - Last manual change for Force Hint action should be FORCE_HINT
- set the correct manual redaction type depending if it was a hint or not

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-02 11:44:05 +02:00
Corina Olariu
998a0710cd Merge branch 'RED-8219' into 'master'
RED-8219 - Wrong notification type for approvers

Closes RED-8219

See merge request redactmanager/persistence-service!331
2024-02-02 10:17:07 +01:00
Kilian Schüttler
f7b80f2e61 Merge branch 'RED-7384' into 'master'
RED-7384:dont mgirate non-changing annotation Ids

Closes RED-7384

See merge request redactmanager/persistence-service!330
2024-02-02 10:10:02 +01:00
Kilian Schüttler
498cfecf88 RED-7384:dont mgirate non-changing annotation Ids 2024-02-02 10:10:02 +01:00
Corina Olariu
c843484ff0 RED-8219 - Wrong notification type for approvers
- set the correct notification type depending on the approved status of the file

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-01 20:21:08 +02:00
Corina Olariu
f6e38550bb Merge branch 'RED-7143' into 'master'
RED-7143 - Error 500 generated when entering very long text in editable fields of a file

Closes RED-7143

See merge request redactmanager/persistence-service!329
2024-02-01 18:33:50 +01:00
Corina Olariu
c00ac6ebe1 RED-7143 - Error 500 generated when entering very long text in editable fields of a file
- validate the value length

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-01 15:17:34 +02:00
Dominique Eifländer
76ee5b0c94 Merge branch 'RED-8415' into 'master'
RED-8415: Reject file attribute configs with lable > 255 chars

Closes RED-8415

See merge request redactmanager/persistence-service!328
2024-02-01 13:04:53 +01:00
Dominique Eifländer
a710c5b549 RED-8415: Reject file attribute configs with lable > 255 chars 2024-02-01 12:56:38 +01:00
Corina Olariu
a6e323ade5 Merge branch 'RED-8361' into 'master'
RED-8361 - Returned error status codes should be checked

Closes RED-8361

See merge request redactmanager/persistence-service!326
2024-02-01 12:09:32 +01:00
Corina Olariu
80e783d46b RED-8361 - Returned error status codes should be checked
- renamed and rework the access permission validations

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-02-01 12:36:57 +02:00
Maverick Studer
57cc7c7c54 Merge branch 'RED-8372' into 'master'
RED-8372: Include additional info about redactions in redaction annotations

Closes RED-8372

See merge request redactmanager/persistence-service!327
2024-01-31 10:23:21 +01:00
Maverick Studer
caac9f48ee RED-8372: Include additional info about redactions in redaction annotations 2024-01-31 10:23:21 +01:00
Corina Olariu
95978b85af RED-8361 - Returned error status codes should be checked
- adding validation to endpoints considering the dossier resources
- if changes are done to dossier, access permissions are checked
- in case information is requested regarding dossier, the view permissions are checked

Signed-off-by: Corina Olariu <corina.olariu.ext@knecon.com>
2024-01-30 15:00:38 +02:00
Timo Bejan
5394faa065 Merge branch 'RED-8085' into 'master'
Resolve RED-8085

Closes RED-8085

See merge request redactmanager/persistence-service!325
2024-01-29 10:32:13 +01:00
Timo Bejan
dab280cef5 Resolve RED-8085 2024-01-29 10:32:13 +01:00
Kilian Schüttler
99a71a6036 Merge branch 'RED-7384' into 'master'
RED-7384: deprecate AnnotationStatus

Closes RED-7384

See merge request redactmanager/persistence-service!324
2024-01-29 10:04:15 +01:00
Kilian Schüttler
f3b2620df7 RED-7384: deprecate AnnotationStatus 2024-01-29 10:04:15 +01:00
Maverick Studer
4cfe7d9c95 Merge branch 'RED-7176' into 'master'
RED-7176: Soft-/Hard-/Restore Dossier without Dossier Owner not possible

Closes RED-7176

See merge request redactmanager/persistence-service!323
2024-01-25 15:50:48 +01:00
Maverick Studer
cfb81e9326 RED-7176: Soft-/Hard-/Restore Dossier without Dossier Owner not possible 2024-01-25 15:50:47 +01:00
Kilian Schüttler
c2d4b0fd28 Merge branch 'RED-8371' into 'master'
RED-8371: after initial file upload flag calculation does not work

Closes RED-8371

See merge request redactmanager/persistence-service!322
2024-01-25 09:29:58 +01:00
Kilian Schüttler
d9497ef45d RED-8371: after initial file upload flag calculation does not work 2024-01-25 09:29:58 +01:00
Maverick Studer
9329023ff6 Merge branch 'RED-8248' into 'master'
RED-8248: Sent comments are not sorted by order of arrival

Closes RED-8248

See merge request redactmanager/persistence-service!321
2024-01-24 12:15:06 +01:00
maverickstuder
6321295d02 RED-8248: Sent comments are not sorted by order of arrival 2024-01-24 12:06:53 +01:00
maverickstuder
42b410f6f7 RED-8248: Sent comments are not sorted by order of arrival
* added sorting for getComments called from controller
2024-01-24 10:32:17 +01:00
Maverick Studer
7455628790 Merge branch 'RED-5449' into 'master'
RED-5449: Wrong behaviour of imported redactions only from PREVIEW-File

Closes RED-5449

See merge request redactmanager/persistence-service!320
2024-01-24 09:21:23 +01:00
Ali Oezyetimoglu
6690fba43d Merge branch 'RED-7252' into 'master'
RED-7252: Not possible anymore to ocr deleted or approved file

Closes RED-7252

See merge request redactmanager/persistence-service!319
2024-01-23 17:25:26 +01:00
Kilian Schüttler
a6eda4841a Merge branch 'resize_hotfix' into 'master'
hotfix: resizing a rule-based entity with dict does not work

See merge request redactmanager/persistence-service!318
2024-01-23 16:54:11 +01:00
Ali Oezyetimoglu
9162bb7edc RED-7252: Not possible anymore to ocr deleted or approved file 2024-01-23 16:45:41 +01:00
Kilian Schuettler
b67cf7eb99 hotfix: resizing a rule-based entity with dict does not work 2024-01-23 14:32:03 +01:00
maverickstuder
c674cccf43 RED-5449: Wrong behaviour of imported redactions only from PREVIEW-File 2024-01-23 13:17:41 +01:00
Maverick Studer
06ea42c410 Merge branch 'RED-7376' into 'master'
RED-7376: Flag in deployment to indicate supported file formats

Closes RED-7376

See merge request redactmanager/persistence-service!317
2024-01-23 12:56:34 +01:00
Maverick Studer
835e3568f6 RED-7376: Flag in deployment to indicate supported file formats 2024-01-23 12:56:34 +01:00
Maverick Studer
ef13d8ace2 Merge branch 'RED-5369' into 'master'
RED-5369: View dossier & access permissions are not working for dossier attributes controller

Closes RED-5369

See merge request redactmanager/persistence-service!316
2024-01-22 12:56:53 +01:00
maverickstuder
56dcd421a9 RED-5369: View dossier & access permissions are not working for dossier attributes controller
* now when no permissions are given to view dossiers the endpoint to get dossier attributes returns an empty list instead of a 403 access denied
2024-01-22 12:34:02 +01:00
maverickstuder
9a3898a377 RED-5369: View dossier & access permissions are not working for dossier attributes controller
* now when no permissions are given to view dossiers the endpoint to get dossier attributes returns an empty list instead of a 403 access denied
2024-01-22 11:23:07 +01:00
Corina Olariu
e17f577064 Merge branch 'RED-8306' into 'master'
RED-8306 - An unprocessed remove from dict should not lead to a local remove

Closes RED-8306

See merge request redactmanager/persistence-service!315
2024-01-19 13:04:09 +01:00
Corina Olariu
e8a5ae9da9 RED-8306 - An unprocessed remove from dict should not lead to a local remove
- when merging unprocessed removal check for remove from dictionary in order to set the manual redaction type accordingly
2024-01-19 13:14:40 +02:00
Yannik Hampe
f9ab3a2607 Merge branch 'RED-6805' into 'master'
RED-6805: As Operation I want to see why downloads are in an ERROR state

Closes RED-6805

See merge request redactmanager/persistence-service!306
2024-01-18 17:10:14 +01:00
yhampe
6a2260af8f Merge remote-tracking branch 'origin/RED-6805' into RED-6805
# Conflicts:
#	persistence-service-v1/persistence-service-processor-v1/src/main/resources/db/changelog/db.changelog-tenant.yaml
2024-01-18 17:02:21 +01:00
yhampe
15611d438c RED-6805: As Operation I want to see why downloads are in an ERROR state
added errorcause to all download dlqs containing queue name
2024-01-18 17:01:58 +01:00
yhampe
45cae62b01 RED-6805: As Operation I want to see why downloads are in an ERROR state
reset errorcause when downloadstatus is ready
ensuring lenghth of errorcause will not exceed field length
2024-01-18 17:01:58 +01:00
yhampe
f6c21faf0c RED-6805: As Operation I want to see why downloads are in an ERROR state
included file in changelog
2024-01-18 17:01:53 +01:00
yhampe
9de4dfc5af RED-6805: As Operation I want to see why downloads are in an ERROR state
added default method back
2024-01-18 17:01:42 +01:00
yhampe
421fe57e38 RED-6805: As Operation I want to see why downloads are in an ERROR state
checkstyle
2024-01-18 17:01:42 +01:00
yhampe
2b782fdd4c RED-6805: As Operation I want to see why downloads are in an ERROR state
checkstyle
2024-01-18 17:01:42 +01:00
yhampe
4ccf355dcf RED-6805: As Operation I want to see why downloads are in an ERROR state
added errorcause to repository
2024-01-18 17:01:42 +01:00
yhampe
0cfbdb3083 RED-6805: As Operation I want to see why downloads are in an ERROR state
added errorCause to downloadStatus
2024-01-18 17:01:42 +01:00
yhampe
bc348215a6 RED-6805: As Operation I want to see why downloads are in an ERROR state
added errorCause to dlq message receiver
added column with errorcause to downloadstatus table
2024-01-18 17:01:42 +01:00
yhampe
03303ece8e RED-6805: As Operation I want to see why downloads are in an ERROR state
added errorcause to all download dlqs containing queue name
2024-01-18 16:20:48 +01:00
Kilian Schüttler
0d9f5276a5 Merge branch 'RED-8168' into 'master'
RED-8168: Calculation of analysis flags should be async

Closes RED-8168

See merge request redactmanager/persistence-service!297
2024-01-18 16:12:36 +01:00
Kilian Schüttler
caecbb483a RED-8168: Calculation of analysis flags should be async 2024-01-18 16:12:36 +01:00
Maverick Studer
15ddc1afeb Merge branch 'RED-5368' into 'master'
RED-5368: View dossier permissions are not working for dossier status controller

Closes RED-5368

See merge request redactmanager/persistence-service!313
2024-01-18 14:13:18 +01:00
Maverick Studer
2505cb70d2 RED-5368: View dossier permissions are not working for dossier status controller 2024-01-18 14:13:18 +01:00
Maverick Studer
5d13696cec Merge branch 'RED-5369' into 'master'
RED-5369: View dossier & access permissions are not working for dossier attributes controller

Closes RED-5369

See merge request redactmanager/persistence-service!314
2024-01-18 13:51:49 +01:00
maverickstuder
804a078403 RED-5369: View dossier & access permissions are not working for dossier attributes controller
* added hasPermission checks on all dossier specific endpoints in the DossierAttributesController
2024-01-18 13:38:59 +01:00
Dominique Eifländer
f608aa1043 Merge branch 'RED-7928' into 'master'
RED-7928: Reset ocr status values if ocr fails

Closes RED-7928

See merge request redactmanager/persistence-service!312
2024-01-17 15:45:39 +01:00
Dominique Eifländer
da98b4cc4e RED-7928: Reset ocr status values if ocr fails 2024-01-17 15:33:21 +01:00
Timo Bejan
7ff2b9f11f Merge branch 'RED-8175' into 'master'
RED-8175 - fixed path traversal issue

Closes RED-8175

See merge request redactmanager/persistence-service!310
2024-01-16 16:07:10 +01:00
Timo Bejan
8c7b991ecb RED-8175 - fixed path traversal issue 2024-01-16 14:36:54 +02:00
yhampe
5333db97ea RED-6805: As Operation I want to see why downloads are in an ERROR state
reset errorcause when downloadstatus is ready
ensuring lenghth of errorcause will not exceed field length
2024-01-16 09:55:48 +01:00
yhampe
3f6c493e54 RED-6805: As Operation I want to see why downloads are in an ERROR state
included file in changelog
2024-01-15 14:41:18 +01:00
yhampe
a04b00c794 RED-6805: As Operation I want to see why downloads are in an ERROR state
added default method back
2024-01-15 14:31:45 +01:00
yhampe
9907f389b1 RED-6805: As Operation I want to see why downloads are in an ERROR state
checkstyle
2024-01-15 14:25:35 +01:00
Maverick Studer
15a4189b3e Merge branch 'RED-7232' into 'master'
RED-7232: Error 500 occurs when entering long text in Dossier States - Status Name Field

Closes RED-7232

See merge request redactmanager/persistence-service!308
2024-01-15 14:12:23 +01:00
maverickstuder
0aaafa41ea RED-7232: Error 500 occurs when entering long text in Dossier States - Status Name Field
* validate status name field on dossier status persistence
2024-01-15 14:02:59 +01:00
Maverick Studer
16d182a513 Merge branch 'RED-5949' into 'master'
RED-5949: Wrong notificationType for specific endpoint

Closes RED-5949

See merge request redactmanager/persistence-service!307
2024-01-15 12:29:40 +01:00
yhampe
9c4ee66d02 RED-6805: As Operation I want to see why downloads are in an ERROR state
checkstyle
2024-01-15 12:27:18 +01:00
yhampe
c1d46ddace RED-6805: As Operation I want to see why downloads are in an ERROR state
added errorcause to repository
2024-01-15 12:21:00 +01:00
maverickstuder
9ac8fcbf38 RED-5949: Wrong notificationType for specific endpoint
* Do not send notification if assigneeId is null
2024-01-15 12:20:54 +01:00
yhampe
5f72b0e2c4 RED-6805: As Operation I want to see why downloads are in an ERROR state
added errorCause to downloadStatus
2024-01-15 11:55:17 +01:00
yhampe
4ea6996bdd RED-6805: As Operation I want to see why downloads are in an ERROR state
added errorCause to dlq message receiver
added column with errorcause to downloadstatus table
2024-01-15 11:52:57 +01:00
Andrei Isvoran
f148c6fa6d Merge branch 'RED-8242' into 'master'
RED-8242 - Set AREA entry type for unprocessed rectangle redactions

Closes RED-8242

See merge request redactmanager/persistence-service!305
2024-01-11 15:27:18 +01:00
Andrei Isvoran
06570a33e8 RED-8242 - Set AREA entry type for unprocessed rectangle redactions 2024-01-11 15:27:18 +01:00
Dominique Eifländer
e2b64b566b Merge branch 'RED-8175' into 'master'
RED-8175: Use only one thread for @Async deletion of files that are already...

Closes RED-8175

See merge request redactmanager/persistence-service!304
2024-01-11 12:47:50 +01:00
Dominique Eifländer
ef6c268c2b RED-8175: Use only one thread for @Async deletion of files that are already added to download package 2024-01-11 12:40:16 +01:00
Andrei Isvoran
5a95dfbc1c Merge branch 'RED-8043-broken-section' into 'master'
RED-8043 - Fix section

Closes RED-8043

See merge request redactmanager/persistence-service!303
2024-01-11 12:29:47 +01:00
Andrei Isvoran
41d25ff924 RED-8043 - Fix section 2024-01-11 12:29:47 +01:00
Andrei Isvoran
1f10939e32 Merge branch 'RED-8043-fix-section' into 'master'
RED-8043 - Check for null AND empty before updating the section

Closes RED-8043

See merge request redactmanager/persistence-service!301
2024-01-10 16:42:03 +01:00
Andrei Isvoran
369e944255 RED-8043 - Check for null AND empty before updating the section 2024-01-10 16:42:03 +01:00
Kilian Schüttler
2da3b3d30c Merge branch 'RED-7384' into 'master'
RED-7384: delete NER_ENTITIES file

Closes RED-7384

See merge request redactmanager/persistence-service!300
2024-01-10 13:37:56 +01:00
Kilian Schuettler
0312656ad6 RED-7384: delete NER_ENTITIES file 2024-01-10 12:41:19 +01:00
Andrei Isvoran
3d1d7bcbf2 Merge branch 'RED-8043-section' into 'master'
RED-8043 - Save section for manual redactions and unprocessed manual redactions

Closes RED-8043

See merge request redactmanager/persistence-service!299
2024-01-09 16:02:24 +01:00
Andrei Isvoran
c7c22bfd06 RED-8043 - Save section for manual redactions and unprocessed manual redactions 2024-01-09 16:02:24 +01:00
Dominique Eifländer
a299f99575 Merge branch 'RED-8174' into 'master'
RED-8174: Fixed transaction handling for notification preferences

Closes RED-8174

See merge request redactmanager/persistence-service!298
2024-01-09 10:58:30 +01:00
Dominique Eifländer
cf4a650fb5 RED-8174: Fixed transaction handling for notification preferences 2024-01-09 10:38:09 +01:00
Timo Bejan
be705a5406 Merge branch 'RED-8175' into 'master'
Fixed transitive dependencies and concurrent download issue RED-8175

Closes RED-8175

See merge request redactmanager/persistence-service!295
2024-01-08 11:23:38 +01:00
Timo Bejan
d0571a436c Fixed transitive dependencies and concurrent download issue RED-8175 2024-01-08 10:20:10 +02:00
Andrei Isvoran
ea9af28633 Merge branch 'RED-8043-convert' into 'master'
RED-8043 - Correctly convert resize redactions

Closes RED-8043

See merge request redactmanager/persistence-service!294
2024-01-05 09:12:50 +01:00
Andrei Isvoran
08b4883e5e RED-8043 - Correctly convert resize redactions 2024-01-05 09:51:16 +02:00
Ali Oezyetimoglu
41791c7e07 Merge branch 'RED-5246' into 'master'
RED-5246: changed permission check for getDossier(...)

Closes RED-5246

See merge request redactmanager/persistence-service!293
2024-01-04 16:41:16 +01:00
Ali Oezyetimoglu
f5f1bde988 RED-5246: changed permission check for getDossier(...) 2024-01-04 14:23:55 +01:00
Dominique Eifländer
7dfd46e887 Merge branch 'RED-8043-trigger-reprocess-correctly' into 'master'
RED-8043 - Only send local manual redactions/resizes to surrounding text...

Closes RED-8043

See merge request redactmanager/persistence-service!292
2024-01-04 13:45:28 +01:00
Andrei Isvoran
c4a354560d RED-8043 - Only send local manual redactions/resizes to surrounding text... 2024-01-04 13:45:28 +01:00
Andrei Isvoran
af2a16e540 Merge branch 'RED-8043-fixes' into 'master'
RED-8043 - General fixes

Closes RED-8043

See merge request redactmanager/persistence-service!290
2024-01-03 14:13:47 +01:00
Andrei Isvoran
a1a8e14419 RED-8043 - General fixes 2024-01-03 14:13:47 +01:00
Andrei Isvoran
267e8b3abc Merge branch 'RED-8114' into 'master'
RED-8114  Copy new value for unprocessed resize & add fetch type for IdRemoval collection

Closes RED-8114

See merge request redactmanager/persistence-service!289
2023-12-22 15:12:04 +01:00
Andrei Isvoran
1761528c23 RED-8114 - Add fetchtype 2023-12-22 16:02:43 +02:00
Andrei Isvoran
3335c54000 RED-8114 - Also copy new value for resize 2023-12-22 16:01:47 +02:00
Andrei Isvoran
150df5e721 Merge branch 'RED-7662' into 'master'
RED-7662 - Don't display hasHints flag if there are no hints

Closes RED-7662

See merge request redactmanager/persistence-service!288
2023-12-22 11:46:16 +01:00
Andrei Isvoran
568979a4e1 RED-7662 - Don't display hasHints flag if there are no hints 2023-12-22 12:21:28 +02:00
Dominique Eifländer
e4f1a04220 Merge branch 'connectionHotfix' into 'master'
hotfix: Fixed 'idle in transaction' connections when loading entityLog

See merge request redactmanager/persistence-service!287
2023-12-22 11:09:42 +01:00
Dominique Eifländer
e10681a0c0 hotfix: Fixed 'idle in transaction' connections when loading entityLog 2023-12-22 10:51:45 +01:00
Dominique Eifländer
cfc0bba3dc Merge branch 'RED-8034' into 'master'
RED-8034: Filter out entityLog entries for excluded pages

Closes RED-8034

See merge request redactmanager/persistence-service!286
2023-12-22 10:35:29 +01:00
Dominique Eifländer
701e173b99 RED-8034: Filter out entityLog entries for excluded pages 2023-12-22 10:19:40 +01:00
Andrei Isvoran
b7c4ec2d75 Merge branch 'RED-7784-force-hint' into 'master'
RED-7784 - Correctly set state for forced hints

Closes RED-7784

See merge request redactmanager/persistence-service!285
2023-12-22 08:53:35 +01:00
Andrei Isvoran
bd5756a06b RED-7784 - Correctly set state for forced hints 2023-12-21 15:49:17 +02:00
Andrei Isvoran
b2dbbbb405 Merge branch 'RED-8043-update-text' into 'master'
RED-8043 - Fix surrounding text not updating when adding manual redaction

Closes RED-8043

See merge request redactmanager/persistence-service!284
2023-12-21 14:05:00 +01:00
Andrei Isvoran
d190291b5b RED-8043 - Fix surrounding text not updating when adding manual redaction 2023-12-21 14:05:00 +01:00
Ali Oezyetimoglu
ca1db849d7 Merge branch 'RED-5246-revert' into 'master'
Revert "RED-5246: updated ACLBeanConfiguration to use...

Closes RED-5246

See merge request redactmanager/persistence-service!283
2023-12-21 12:33:07 +01:00
Ali Oezyetimoglu
0d705f53ca Revert "RED-5246: updated ACLBeanConfiguration to use CustomAclPermissionEvaluator and also changed to custom NotFoundException"
This reverts commit 7172ae55d661374f4bbdfbc6398636909ebc3c52.
2023-12-21 11:40:27 +01:00
Andrei Isvoran
92bfe44d03 Merge branch 'RED-8114' into 'master'
RED-8114 - Allow editing of unprocessed manual changes

Closes RED-8114

See merge request redactmanager/persistence-service!282
2023-12-21 09:45:13 +01:00
Andrei Isvoran
4deadfec0b RED-8114 - Allow editing of unprocessed manual changes 2023-12-21 09:45:13 +01:00
Kilian Schüttler
e9c139211f Merge branch 'RED-7784-refactor' into 'master'
RED-7784 - Refactor merge logic to not loop multiple times through the entire entity log entries

Closes RED-7784

See merge request redactmanager/persistence-service!279
2023-12-20 13:43:16 +01:00
Andrei Isvoran
6ba76e66ec RED-7784 - Refactor merge logic to not loop multiple times through the entire entity log entries 2023-12-20 13:43:16 +01:00
Kilian Schüttler
85fc1a570b Merge branch 'RED-1137' into 'master'
RED-1137: Do not observe actuator endpoints

Closes RED-1137

See merge request redactmanager/persistence-service!278
2023-12-20 13:07:41 +01:00
Dominique Eifländer
a71e75da90 RED-1137: Do not observe actuator endpoints 2023-12-20 12:58:09 +01:00
Dominique Eifländer
187a882e0b Merge branch 'RED-7384' into 'master'
RED-7834: fix migration issues

Closes RED-7384

See merge request redactmanager/persistence-service!276
2023-12-20 12:28:46 +01:00
Kilian Schüttler
1ea65a256c RED-7834: fix migration issues 2023-12-20 12:28:46 +01:00
Corina Olariu
9b972984cf Merge branch 'RED-7256-npe' into 'master'
RED-7256 - Overlapping annotation color from imported redaction and resized redaction

Closes RED-7256

See merge request redactmanager/persistence-service!270
2023-12-20 11:48:51 +01:00
Corina Olariu
85d12f9355 RED-7256 - Overlapping annotation color from imported redaction and resized redaction
- fix merge from master
2023-12-20 11:14:30 +02:00
Corina Olariu
2948ae1c10 Merge branch 'master' into RED-7256-npe
# Conflicts:
#	persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EntityLogMergeService.java
2023-12-20 11:07:11 +02:00
Dominique Eifländer
f2352413e1 Merge branch 'RED-8034' into 'master'
RED-8034: Always load entityLog from persistence-service because excluded...

Closes RED-8034

See merge request redactmanager/persistence-service!275
2023-12-19 16:48:07 +01:00
Dominique Eifländer
f568cf2eec RED-8034: Always load entityLog from persistence-service because excluded pages are calculated there 2023-12-19 16:34:09 +01:00
Dominique Eifländer
822a55a8f8 Merge branch 'RED-7784-changes' into 'master'
RED-7784 - Don't update changes dateTime on every merge.

Closes RED-7784

See merge request redactmanager/persistence-service!274
2023-12-19 16:22:32 +01:00
Andrei Isvoran
b5794f7fdf RED-7784 - Don't update changes dateTime on every merge. 2023-12-19 17:14:49 +02:00
Andrei Isvoran
4c9ffd5f64 Merge branch 'RED-8043' into 'master'
RED-8043 - Refactor entity log merge & don't send surrounding analysis request for dictionary adds

Closes RED-8043

See merge request redactmanager/persistence-service!271
2023-12-19 16:06:39 +01:00
Andrei Isvoran
f71976e595 RED-8043 - Refactor entity log merge & don't send surrounding analysis request for dictionary adds 2023-12-19 16:06:39 +01:00
Dominique Eifländer
d62746484e Merge branch 'RED-8034' into 'master'
RED-8034: Exclude entries from entitylog for exluded pages without analyse

Closes RED-8034

See merge request redactmanager/persistence-service!273
2023-12-19 13:52:11 +01:00
Dominique Eifländer
995ff6b725 RED-8034: Exclude entries from entitylog for exluded pages without analyse 2023-12-19 13:40:32 +01:00
Corina Olariu
6e7dadd0cf RED-7256 - Overlapping annotation color from imported redaction and resized redaction
- fix immutable map
2023-12-19 14:17:15 +02:00
Corina Olariu
705dd42333 RED-7256 - Overlapping annotation color from imported redaction and resized redaction
- fix the NPE aldo for legalBasis changes
2023-12-19 13:50:38 +02:00
Corina Olariu
85737653f7 RED-7256 - Overlapping annotation color from imported redaction and resized redaction
- use Strings from guave for empty or null and set empty value in case of null value
2023-12-19 12:50:21 +02:00
Dominique Eifländer
7257bdb159 Merge branch 'RED-5246' into 'master'
RED-5246: updated ACLBeanConfiguration to use CustomAclPermissionEvaluator and...

Closes RED-5246

See merge request redactmanager/persistence-service!272
2023-12-19 11:29:24 +01:00
Ali Oezyetimoglu
7172ae55d6 RED-5246: updated ACLBeanConfiguration to use CustomAclPermissionEvaluator and also changed to custom NotFoundException 2023-12-19 11:22:11 +01:00
Corina Olariu
36755e5958 RED-7256 - Overlapping annotation color from imported redaction and resized redaction
- fix NPE which is caused by the null value in case of resize imported redaction
2023-12-15 11:17:29 +02:00
Andrei Isvoran
b0f40a080c Merge branch 'RED-8043' into 'master'
RED-8043 - Refactor EntityLog merge mechanism

Closes RED-8043

See merge request redactmanager/persistence-service!269
2023-12-14 15:06:32 +01:00
Andrei Isvoran
8c854900cc RED-8043 - Refactor EntityLog merge mechanism 2023-12-14 15:06:32 +01:00
Yannik Hampe
464d5e2ef3 Merge branch 'RED-8078' into 'master'
RED-8078: Removing an entity type should trigger complete reanalysis

Closes RED-8078

See merge request redactmanager/persistence-service!267
2023-12-14 14:07:35 +01:00
Dominique Eifländer
31893ba442 Merge branch 'cycleHotfix' into 'master'
Cycle hotfix

See merge request redactmanager/persistence-service!268
2023-12-14 14:04:39 +01:00
Dominique Eifländer
56038f13b9 hotfix: Fixed old versions in project, because of cycling dependencies 2023-12-14 13:50:50 +01:00
yhampe
95afc6cd14 Merge remote-tracking branch 'origin/RED-8078' into RED-8078 2023-12-14 13:48:10 +01:00
yhampe
c6d330f1df RED-8078: Removing an entity type should trigger complete reanalysis
added parameter to include deleted dictionary types
2023-12-14 13:48:00 +01:00
yhampe
9c0db2a5fe RED-8078: Removing an entity type should trigger complete reanalysis
added parameter to include deleted dictionary types
2023-12-14 11:49:40 +01:00
Dominique Eifländer
36e76b0273 RED-5223: Added tracing for scheduled analysis, and add fileId on upload to trace 2023-12-14 11:28:14 +01:00
Dominique Eifländer
6a61033441 Merge branch 'RED-6661-fixes' into 'master'
migration for uncompressed azure blobs

Closes RED-6661

See merge request redactmanager/persistence-service!264
2023-12-14 09:28:26 +01:00
Timo Bejan
a62f642c28 migration for uncompressed azure blobs 2023-12-13 16:21:38 +02:00
Dominique Eifländer
cf44dbff03 Merge branch 'RED-5223' into 'master'
RED-5223: Use tracing-commons from fforesight

Closes RED-5223

See merge request redactmanager/persistence-service!265
2023-12-13 14:59:49 +01:00
Dominique Eifländer
56db92bf39 RED-5223: Use tracing-commons from fforesight 2023-12-13 14:50:37 +01:00
Dominique Eifländer
8f629914bd Merge branch 'RED-5223' into 'master'
RED-5223: Fixed not working tracing when comes from queue, upgraded ocr service

Closes RED-5223

See merge request redactmanager/persistence-service!263
2023-12-12 15:13:19 +01:00
Dominique Eifländer
9e3191dabd RED-5223: Fixed not working tracing when comes from queue, upgraded ocr service 2023-12-12 15:05:18 +01:00
Dominique Eifländer
7d3a7452a9 Merge branch 'RED-7834' into 'master'
RED-7834: fix migration issues

Closes RED-7834

See merge request redactmanager/persistence-service!262
2023-12-12 13:58:06 +01:00
Kilian Schüttler
cdf37fd486 RED-7834: fix migration issues 2023-12-12 13:58:05 +01:00
Timo Bejan
3b8b397fe5 Merge branch 'RED-7834' into 'master'
RED-7834: fix migration issues

Closes RED-7834

See merge request redactmanager/persistence-service!261
2023-12-11 15:46:32 +01:00
Kilian Schüttler
87f841d9dd RED-7834: fix migration issues 2023-12-11 15:46:32 +01:00
Andrei Isvoran
4653cba097 Merge branch 'RED-8057' into 'master'
RED-8057 - Fix issue with adding dict based manual redactions with no position specified

Closes RED-8057

See merge request redactmanager/persistence-service!259
2023-12-08 16:21:02 +01:00
Andrei Isvoran
9b8e6a0e84 RED-8057 - Fix issue with adding dict based manual redactions with no position specified 2023-12-08 16:21:02 +01:00
Dominique Eifländer
ea81708227 Merge branch 'RED-1137' into 'master'
RED-1137: Enabled tracing, enabled traceId/spanId in Json logging, send custom...

Closes RED-1137

See merge request redactmanager/persistence-service!256
2023-12-08 13:46:41 +01:00
Dominique Eifländer
c1540943a4 RED-1137: Enabled tracing, enabled traceId/spanId in Json logging, send custom attributes for namespace and version 2023-12-08 13:40:11 +01:00
Andrei Isvoran
6f5f148731 Merge branch 'RED-7404-deploy' into 'master'
RED-7404 - Add missing deploy stage

Closes RED-7404

See merge request redactmanager/persistence-service!257
2023-12-08 12:22:12 +01:00
Andrei Isvoran
0a4096401c RED-7404 - Add missing deploy stage 2023-12-08 13:19:30 +02:00
Andrei Isvoran
430f118206 Merge branch 'RED-7404' into 'master'
RED-7404 - Add publishing for all modules

Closes RED-7404

See merge request redactmanager/persistence-service!254
2023-12-08 11:27:47 +01:00
Andrei Isvoran
77d1e6ed4c RED-7404 - Add publishing for all modules 2023-12-08 11:27:46 +01:00
Timo Bejan
f8157e4448 Merge branch 'RED-6661' into 'master'
Por change for local dev

Closes RED-6661

See merge request redactmanager/persistence-service!255
2023-12-07 00:24:42 +01:00
Timo Bejan
05db55adca Por change for local dev 2023-12-07 01:13:45 +02:00
Andrei Isvoran
7508e7667e Merge branch 'RED-8043' into 'master'
RED-8043 - Add new MessageType and queue

Closes RED-8043

See merge request redactmanager/persistence-service!253
2023-12-06 14:49:58 +01:00
Andrei Isvoran
5c215f3a47 RED-8043 - Add new MessageType and queue 2023-12-06 14:49:57 +01:00
Andrei Isvoran
ba21282b1c Merge branch 'RED-7715' into 'master'
RED-7715 - Modify log4j config to enable switching between console and json based on env var

Closes RED-7715

See merge request redactmanager/persistence-service!251
2023-12-06 09:30:37 +01:00
Andrei Isvoran
170cc61000 RED-7715 - Modify log4j config to enable switching between console and json based on env var 2023-12-06 09:30:37 +01:00
Andrei Isvoran
f77ec53282 Merge branch 'RED-7404-fix-jsonprimitive' into 'master'
RED-7404 - Add lombok.config

Closes RED-7404

See merge request redactmanager/persistence-service!243
2023-12-04 17:50:39 +01:00
Andrei Isvoran
37c2eef2f9 RED-7404 - Add lombok.config 2023-12-04 17:50:39 +01:00
Kevin Tumma
5c89099ac0 hotfix version 2023-12-04 14:31:39 +01:00
Andrei Isvoran
cf0d85a42d Merge branch 'RED-7404-gitlab-ci' into 'master'
RED-7404 - Fix gitlab-ci.yml

Closes RED-7404

See merge request redactmanager/persistence-service!242
2023-12-04 14:28:44 +01:00
Andrei Isvoran
22a5be7021 RED-7404 - Fix gitlab-ci.yml 2023-12-04 15:20:26 +02:00
Ali Oezyetimoglu
e52c60c669 Merge branch 'RED-8014' into 'master'
RED-8014: Remove spaces as suffix and prefix on dossier creation

Closes RED-8014

See merge request redactmanager/persistence-service!238
2023-12-04 14:10:23 +01:00
Andrei Isvoran
d8ee40bf66 Merge branch 'RED-7404-update' into 'master'
RED-7404 - Fix gitlab-ci

Closes RED-7404

See merge request redactmanager/persistence-service!240
2023-12-04 13:48:49 +01:00
Andrei Isvoran
853fa8365e RED-7404 - Fix gitlab-ci 2023-12-04 14:40:31 +02:00
Andrei Isvoran
91884f450a Merge branch 'RED-7404-update' into 'master'
RED-7404 - Remove clean cache

Closes RED-7404

See merge request redactmanager/persistence-service!239
2023-12-04 13:05:41 +01:00
Andrei Isvoran
838c4e604c RED-7404 - Remove clean cache 2023-12-04 13:56:13 +02:00
Andrei Isvoran
391365b2fb Merge branch 'RED-7404' into 'master'
RED-7404 - Migrate to gradle

Closes RED-7404

See merge request redactmanager/persistence-service!231
2023-12-04 12:31:47 +01:00
Andrei Isvoran
8dad6c44df RED-7404 - Migrate to gradle 2023-12-04 12:31:47 +01:00
Ali Oezyetimoglu
47e268287e RED-8014: Remove spaces as suffix and prefix on dossier creation 2023-12-04 09:54:10 +01:00
Timo Bejan
db38a97f8a Merge branch 'RED-8031' into 'master'
RED-8031 - Migration for non gzipped files

Closes RED-8031

See merge request redactmanager/persistence-service!237
2023-12-03 21:18:24 +01:00
Timo Bejan
b5cd72c7bf RED-8031 - Migration for non gzipped files 2023-12-03 21:45:50 +02:00
Dominique Eifländer
815de4d184 Merge branch 'RED-7962' into 'master'
RED-7962 Changed response type to "text/plain; charset=utf-8"

Closes RED-7962

See merge request redactmanager/persistence-service!236
2023-11-30 12:40:17 +01:00
Kresnadi Budisantoso
1de6210993 RED-962 Fixup response type & error 500 2023-11-29 23:58:40 +01:00
Kresnadi Budisantoso
b9d473677c Hotfix: Download file name not properly recognized by the browser as the response header is malformed 2023-11-29 23:25:46 +01:00
Kresnadi Budisantoso
9d84085d46 Hotfix: Download rule file endpoint (old version) ignores the file type 2023-11-29 23:16:31 +01:00
Kresnadi Budisantoso
4c4e1ced91 RED-7962 Changed response type to "text/plain; charset=utf-8"
- Fixup of previous commit
- Use "text/plain; charset=utf-8" instead of "application/octet-stream" as response type for rule files
  as they are actually text files.
2023-11-29 23:07:14 +01:00
Kresnadi Budisantoso
93ff601b44 Merge branch 'RED-7962' into 'master'
RED-7962 Fix error 500 for rule download endpoints.

Closes RED-7962

See merge request redactmanager/persistence-service!235
2023-11-29 17:32:20 +01:00
Kresnadi Budisantoso
9aff9e2d71 RED-7962 Fix error 500 for rule download endpoints.
The previous fix just corrected missing 404 response for the upload endpoints.
In case of a 404 error, the response entity is not a InputStreamResource anymore but an object containing the error message.
2023-11-29 17:23:45 +01:00
Dominique Eifländer
fa8a5cf61f Merge branch 'RED-7962' into 'master'
RED-7962 Fix wrong CSV mapping active flag and error 500 for rule endpoints

Closes RED-7962

See merge request redactmanager/persistence-service!234
2023-11-29 16:34:01 +01:00
Kresnadi Budisantoso
d4a9afb8fe RED-7962 Fix error 500 for rule endpoints if dossier template does not exist
Also return 404 for file attribute definition endpoint if dossier template does not exist.
2023-11-29 16:18:46 +01:00
Kresnadi Budisantoso
9985e9b0af RED-7962 Fix wrong CSV mapping active flag 2023-11-29 16:18:46 +01:00
Timo Bejan
32e4b9a1fd Merge branch 'DM-602' into 'master'
DM-602: Do not always set addToDictionary for recategorizations

Closes DM-602

See merge request redactmanager/persistence-service!233
2023-11-29 11:36:13 +01:00
Dominique Eifländer
e55cd716cc DM-602: Do not always set addToDictionary for recategorizations 2023-11-29 11:28:28 +01:00
Andrei Isvoran
9e04a47e90 Merge branch 'RED-7784-additional-fixes' into 'master'
RED-7785 - Fix false positives not getting removed for unprocessed changes & refactor merging unprocessed changes into entitylog logic.

Closes RED-7784

See merge request redactmanager/persistence-service!228
2023-11-29 11:03:52 +01:00
Andrei Isvoran
88736855b4 RED-7785 - Fix false positives not getting removed for unprocessed changes & refactor merging unprocessed changes into entitylog logic. 2023-11-29 11:03:51 +01:00
Corina Olariu
c036acc81d Merge branch 'RED-7979' into 'master'
RED-7979 - Force Hint leads to 500 BE Error

Closes RED-7979

See merge request redactmanager/persistence-service!232
2023-11-29 10:24:36 +01:00
Dominique Eifländer
00dff315b7 Merge branch 'RED-7962' into 'master'
RED-7962 Fixed error 500 and improved response of endpoint to get file attributes ...

Closes RED-7962

See merge request redactmanager/persistence-service!230
2023-11-29 09:52:18 +01:00
Corina Olariu
22d837a35f RED-7979 - Force Hint leads to 500 BE Error
- in case of force hint, there is no legalBasis so do not add property changes for legalBasis
2023-11-29 10:40:07 +02:00
Kresnadi Budisantoso
06d3b282cc RED-7962 Updated platform dependency to 2.20.0 2023-11-29 00:05:22 +01:00
Kresnadi Budisantoso
0013b5d116 RED-7962 Improved response of endpoint to get file attributes
Added CSV settings.
2023-11-29 00:05:22 +01:00
Kresnadi Budisantoso
97960d8ed7 RED-7962 Fixes error 500 that occurred because of the rule file type parameter that was not removed by mistake
Also adjusted the audit log message if 'dryRun' was set to `true`.
2023-11-29 00:05:22 +01:00
Timo Bejan
67bd3ed4bc Merge branch 'RED-7967' into 'master'
RED-7967 - liquibase fix

Closes RED-7967

See merge request redactmanager/persistence-service!229
2023-11-28 21:02:43 +01:00
Timo Bejan
882576dadf RED-7967 - liquibase fix 2023-11-28 17:39:13 +02:00
Kilian Schüttler
5eb4d86808 Merge branch 'RED-7962' into 'master'
RED-7962 Added 'red-experimental' role to restrict usage of experimental...

Closes RED-7962

See merge request redactmanager/persistence-service!227
2023-11-27 18:40:10 +01:00
Kresnadi Budisantoso
93167265d1 RED-7962 Added 'red-experimental' role to restrict usage of experimental... 2023-11-27 18:40:09 +01:00
Timo Bejan
cec1c1dae9 Merge branch 'migration-fixes' into 'master'
Migration fixes

See merge request redactmanager/persistence-service!226
2023-11-23 20:58:36 +01:00
Timo Bejan
1c2d3fe4e2 skipping bugs since this was disabled up to now but got enabled due to upgrade of dependencies 2023-11-23 21:46:40 +02:00
Timo Bejan
e73b6bd69b Migration local testing 2023-11-23 21:38:11 +02:00
Timo Bejan
5d6ed6bba8 cleanup 2023-11-23 21:38:11 +02:00
Timo Bejan
288bbf9f17 Integrate sync service 2023-11-23 21:38:11 +02:00
Timo Bejan
83c02409e9 Integration of sync service 2023-11-23 21:38:11 +02:00
Dominique Eifländer
8c090d14ba Merge branch 'RED-7962' into 'master'
RED-7962 Added rule upload and download

Closes RED-7962

See merge request redactmanager/persistence-service!225
2023-11-23 16:01:15 +01:00
Kresnadi Budisantoso
e5636452a1 RED-7962 Added rule upload and download specification to openapi.yaml
Other changes:
* minor renamings
* fixed query parameter name to camelCase
* changed response code for invalid rules to 422 (unprocessable entity)
* changed filename of rules to lower case
2023-11-23 15:33:17 +01:00
Dominique Eifländer
19605b4185 RED-7962: Implemented public rules endpoints 2023-11-23 13:37:33 +01:00
Andrei Isvoran
2344af108c Merge branch 'RED-7784-fixes' into 'master'
RED-7784 - Fixes for unprocessed redactions

Closes RED-7784

See merge request redactmanager/persistence-service!221
2023-11-21 13:25:23 +01:00
Andrei Isvoran
eae93223fa RED-7784 - Fixes for unprocessed redactions 2023-11-21 13:25:23 +01:00
Christoph Schabert
0a08c57f44 Merge branch 'RED-7934' into 'master'
RED-7934: Fixed pmd violation

Closes RED-7934

See merge request redactmanager/persistence-service!218
2023-11-16 15:35:09 +01:00
Dominique Eifländer
8379af0a79 RED-7934: Fixed pmd violation 2023-11-16 15:29:03 +01:00
Dominique Eifländer
8ff9fe41e1 Merge branch 'RED-7934' into 'master'
RED-7934: Do not delete manual redactions at remove redaction

Closes RED-7934

See merge request redactmanager/persistence-service!217
2023-11-16 14:51:42 +01:00
Dominique Eifländer
ff8c6d2463 RED-7934: Do not delete manual redactions at remove redaction 2023-11-16 14:45:30 +01:00
Corina Olariu
a254250ac5 Merge branch 'RED-7918' into 'master'
RED-7918 - RM-62: "Last Modified" date not updated after change

Closes RED-7918

See merge request redactmanager/persistence-service!215
2023-11-16 14:18:22 +01:00
Corina Olariu
c455bdf5ce Merge branch 'master' into RED-7918 2023-11-16 15:08:29 +02:00
Dominique Eifländer
c78722c0ee Merge branch 'RED-7934' into 'master'
RED-7934: Do not delete manual redactions at remove redaction if not updateDictionary

Closes RED-7934

See merge request redactmanager/persistence-service!216
2023-11-16 14:04:17 +01:00
Dominique Eifländer
59d45a309b RED-7934: Do not delete manual redactions at remove redaction if not updateDictionary 2023-11-16 13:46:18 +01:00
Andrei Isvoran
641dfcb0dd Merge branch 'RED-7784-default-boolean-flag' into 'master'
RED-7784 - Include unprocessed redactions in reports by default

Closes RED-7784

See merge request redactmanager/persistence-service!214
2023-11-16 09:04:14 +01:00
Andrei Isvoran
d8c746b48d RED-7784 - Include unprocessed redactions in reports by default 2023-11-16 09:04:14 +01:00
Corina Olariu
a8e0f4b67c RED-7918 - RM-62: "Last Modified" date not updated after change
- update tests
2023-11-16 09:53:03 +02:00
Corina Olariu
ba19477c25 RED-7918 - RM-62: "Last Modified" date not updated after change
- in calculate flags get the entity log with the unprocessed changes
2023-11-16 09:32:24 +02:00
Andrei Isvoran
477113b6ae Merge branch 'RED-7784-flag-default' into 'master'
RED-7784 - Include unprocessed redactions in reports by default

Closes RED-7784

See merge request redactmanager/persistence-service!213
2023-11-14 14:40:48 +01:00
Andrei Isvoran
5d977d191a RED-7784 - Include unprocessed redactions in reports by default 2023-11-14 15:18:16 +02:00
Ali Oezyetimoglu
175bc4e4cc Merge branch 'RED-7883' into 'master'
Resolve RED-7883

Closes RED-7883

See merge request redactmanager/persistence-service!211
2023-11-14 12:19:32 +01:00
Ali Oezyetimoglu
67afcfa6b0 RED-7883: updated zips of dossier templates to have the right encoded type dictionary lists 2023-11-14 12:12:39 +01:00
Ali Oezyetimoglu
8845c406b2 RED-7883: updated methods to import and export with UTF-8 instead of modified UTF-8 2023-11-14 11:26:34 +01:00
Ali Oezyetimoglu
f0a4772c33 RED-7883: changed encoding of type dictionaries from modified UTF-8 to UTF-8 2023-11-14 10:44:21 +01:00
Ali Oezyetimoglu
d11a1178b3 RED-7883: changed encoding from modified UTF-8 to UTF-8 for dossier template import and export 2023-11-13 16:51:14 +01:00
Corina Olariu
8ad1938b1c Merge branch 'RED-7876' into 'master'
RED-7876 - Diagrams for future license are empty

Closes RED-7876

See merge request redactmanager/persistence-service!209
2023-11-13 09:45:59 +01:00
Corina Olariu
0d475e2839 RED-7876 - Diagrams for future license are empty
- remove the check for start date in the future
- remove the setting to set current date for end date in case date is in the future
2023-11-13 09:50:03 +02:00
Kilian Schüttler
819b2384b2 Merge branch 'DM-557' into 'master'
DM-557: fix component sorting

Closes DM-557

See merge request redactmanager/persistence-service!208
2023-11-10 12:37:10 +01:00
Kilian Schuettler
05981913b3 DM-557: fix component sorting 2023-11-10 12:15:07 +01:00
Dominique Eifländer
5b0c35b04f Merge branch 'DM-576' into 'master'
DM-576: Fixed connections idle in transaction

Closes DM-576

See merge request redactmanager/persistence-service!207
2023-11-10 09:32:54 +01:00
Dominique Eifländer
6adfdb2991 DM-576: Fixed connections idle in transaction 2023-11-09 16:16:04 +01:00
Andrei Isvoran
c5dc459ad6 Merge branch 'RED-7784' into 'master'
RED-7784 - Report with unprocessed redactions

Closes RED-7784

See merge request redactmanager/persistence-service!205
2023-11-08 09:42:23 +01:00
Andrei Isvoran
61cc1297fd RED-7784 - Report with unprocessed redactions 2023-11-08 09:42:23 +01:00
Andrei Isvoran
377cc98542 Merge branch 'RED-7784' into 'master'
RED-7784 - Report with unprocessed redactions

Closes RED-7784

See merge request redactmanager/persistence-service!204
2023-11-07 12:44:57 +01:00
Andrei Isvoran
e81e1eb000 RED-7784 - Report with unprocessed redactions 2023-11-07 12:44:57 +01:00
Andrei Isvoran
84fa3acc0a Merge branch 'RED-7784' into 'master'
RED-7784 - Report with unprocessed redactions

Closes RED-7784

See merge request redactmanager/persistence-service!203
2023-11-07 11:50:33 +01:00
Andrei Isvoran
93bbdd768d RED-7784 - Report with unprocessed redactions 2023-11-07 11:50:32 +01:00
Kilian Schüttler
15995a1518 Merge branch 'sorting_hotfix' into 'master'
hotfix: fix component sorting

See merge request redactmanager/persistence-service!212
2023-11-06 17:48:13 +01:00
Kilian Schuettler
c1418d30dc hotfix: fix component sorting 2023-11-06 15:36:32 +01:00
Dominique Eifländer
cb0db9e6f5 Merge branch 'RED-7382' into 'master'
RED-7382: Implemented Saas migration

Closes RED-7382

See merge request redactmanager/persistence-service!211
2023-11-06 14:37:19 +01:00
Dominique Eifländer
75aa8aa467 RED-7382: Implemented Saas migration 2023-11-06 13:06:49 +01:00
Andrei Isvoran
3c374baf94 Merge branch 'RED-7784' into 'master'
RED-7784 - Add state for unprocessed redactions

Closes RED-7784

See merge request redactmanager/persistence-service!210
2023-11-02 09:34:17 +01:00
Andrei Isvoran
0f3cc14409 RED-7784 - Add state for unprocessed redactions 2023-11-02 09:34:17 +01:00
Dominique Eifländer
284d0bff50 Merge branch 'RED-7517' into 'master'
Resolve RED-7517

Closes RED-7517

See merge request redactmanager/persistence-service!203
2023-11-02 08:56:13 +01:00
Andrei Isvoran
79287d435d Merge branch 'RED-7677' into 'master'
RED-7677 - dossier-template endpoint should return a 400 if validFrom > validTo

Closes RED-7677

See merge request redactmanager/persistence-service!206
2023-11-01 10:16:14 +01:00
Andrei Isvoran
e1007dc5c4 RED-7677 - dossier-template endpoint should return a 400 if validFrom > validTo 2023-11-01 10:16:14 +01:00
Corina Olariu
0a8347d326 Merge branch 'RED-7854' into 'master'
RED-7854 - UI stuck when selecting a future license

Closes RED-7854

See merge request redactmanager/persistence-service!205
2023-11-01 09:40:59 +01:00
Corina Olariu
9b5b8195dc RED-7854 - UI stuck when selecting a future license
- no need to specify 0 for int values, will be initialized with 0 by default
2023-11-01 10:21:36 +02:00
Corina Olariu
f60ef37a08 Merge branch 'RED-7854' into 'master'
RED-7854 - UI stuck when selecting a future license

Closes RED-7854

See merge request redactmanager/persistence-service!204
2023-10-31 15:42:50 +01:00
Dominique Eifländer
b966ac516b RED-7517: Save dossier and acls in same transaction 2023-10-31 15:03:38 +01:00
Dominique Eifländer
517763786b Merge branch 'RED-7821' into 'master'
RED-7821: Added migration to fix wrong dossier dictionary entries in redaction logs

Closes RED-7821

See merge request redactmanager/persistence-service!197
2023-10-31 15:03:26 +01:00
Corina Olariu
5c28072103 RED-7854 - UI stuck when selecting a future license
- return 0 values in all invalid cases
2023-10-31 15:58:12 +02:00
Corina Olariu
afa75b850c RED-7854 - UI stuck when selecting a future license
- in case start date is in the future send 0 values for license report
2023-10-31 15:39:06 +02:00
Corina Olariu
fcaf76eb48 Merge branch 'RED-7848' into 'master'
RED-7848 - Sorting in merged dictionary differs from template dictionary in terms of capitalization

Closes RED-7848

See merge request redactmanager/persistence-service!202
2023-10-31 08:36:04 +01:00
Corina Olariu
c2d558636c RED-7848 - Sorting in merged dictionary differs from template dictionary in terms of capitalization
- fic cheskstyle error
2023-10-30 15:19:28 +02:00
Corina Olariu
efe6dd5930 RED-7848 - Sorting in merged dictionary differs from template dictionary in terms of capitalization
- correct junit assertions
2023-10-30 14:58:48 +02:00
Corina Olariu
13e47c6453 RED-7848 - Sorting in merged dictionary differs from template dictionary in terms of capitalization
- update the sorting of entries with a custom comparator
- update junit tests
2023-10-30 14:51:23 +02:00
Corina Olariu
8a50f6c0a2 RED-7848 - Sorting in merged dictionary differs from template dictionary in terms of capitalization
- update the sorting to natural order
- update junit tests
2023-10-30 11:20:02 +02:00
Kilian Schüttler
f250c6b961 Merge branch 'RED-7384' into 'master'
RED-7384: create shared models in persistence service

Closes RED-7384

See merge request redactmanager/persistence-service!201
2023-10-27 12:19:44 +02:00
Kilian Schuettler
a279a76659 RED-7384: create shared models in persistence service 2023-10-27 12:13:47 +02:00
Kilian Schuettler
d91bbc6d0e RED-7384: create shared models in persistence service 2023-10-27 12:09:48 +02:00
Andrei Isvoran
407e1be1a4 Merge branch 'RED-7784-unprocessed-redactions' into 'master'
RED-7782 - Merge unprocessed redactions into entity log without re-analysis.

Closes RED-7784

See merge request redactmanager/persistence-service!200
2023-10-27 12:03:10 +02:00
Kilian Schuettler
18d98ab4e6 RED-7384: create shared models in persistence service 2023-10-27 11:55:38 +02:00
Andrei Isvoran
3bdfd4c96b RED-7782 - Add property changes 2023-10-27 12:16:04 +03:00
Andrei Isvoran
7a0de45add RED-7782 - Address PR comments 2023-10-27 12:14:22 +03:00
Andrei Isvoran
da43634b19 RED-7782 - Address PR comments 2023-10-27 12:09:20 +03:00
Andrei Isvoran
729881fcd4 RED-7782 - Fix tests 2023-10-26 17:07:42 +03:00
Andrei Isvoran
75847a43d5 RED-7782 - Merge unprocessed redactions into entity log without re-analysis. 2023-10-26 15:37:33 +03:00
Kilian Schüttler
087935dddd Merge branch 'RED-7631' into 'master'
RED-7631: add EntryType IMAGE_HINT

Closes RED-7631

See merge request redactmanager/persistence-service!199
2023-10-26 12:09:26 +02:00
Kilian Schuettler
9ebf22f52f RED-7631: add EntryType IMAGE_HINT
* also fix BaseAnnotation builder
2023-10-26 12:02:29 +02:00
Kilian Schuettler
66f740b44a RED-7631: add EntryType IMAGE_HINT
* also fix ManualResizeRedaction builder
2023-10-26 11:58:11 +02:00
Dominique Eifländer
2c385d9a92 Merge branch 'RED-7838' into 'master'
RED-7838: merge numberOfComments into EntityLog

Closes RED-7838

See merge request redactmanager/persistence-service!198
2023-10-26 09:09:23 +02:00
Kilian Schuettler
c12abd0280 RED-7838: merge numberOfComments into EntityLog
* also change comment endpoint to return comments per annotation instead of file
2023-10-25 17:12:11 +02:00
Dominique Eifländer
15b24ac879 RED-7821: Added migration to fix wrong dossier dictionary entries in redaction logs 2023-10-25 13:36:40 +02:00
Corina Olariu
947bd41e1b Merge branch 'RED-7812' into 'master'
RED-7812 - Dossier template will be renamed every time after each import

Closes RED-7812

See merge request redactmanager/persistence-service!195
2023-10-25 10:31:11 +02:00
Corina Olariu
8eac48206b RED-7812 - Dossier template will be renamed every time after each import
- remove unnecessary logs
2023-10-24 15:24:08 +03:00
Corina Olariu
5a3432e6c6 RED-7812 - Dossier template will be renamed every time after each import
- put back the checking by name for the other cases (when cloning, creating, updating a dossier template)
2023-10-24 15:15:27 +03:00
Corina Olariu
6d245ae7b6 RED-7812 - Dossier template will be renamed every time after each import
- when checking for the existence of the dossier template name ignore the dossier template id of the one that will be replaced
2023-10-24 13:32:07 +03:00
Andrei Isvoran
bffae1292e Merge branch 'RED-7782-comments' into 'master'
RED-7782 - Remove comments

Closes RED-7782

See merge request redactmanager/persistence-service!194
2023-10-24 11:22:44 +02:00
Andrei Isvoran
48d432024a RED-7782 - Remove comments 2023-10-24 11:22:44 +02:00
Corina Olariu
59f3e99fea Merge branch 'RED-7434' into 'master'
RED-7434 - Remove Section Grid entirely

Closes RED-7434

See merge request redactmanager/persistence-service!192
2023-10-23 14:28:54 +02:00
Kilian Schüttler
9f0d262f1b Merge branch 'DM-285' into 'master'
DM-285: flush add redaction removal

Closes DM-285

See merge request redactmanager/persistence-service!193
2023-10-23 14:17:13 +02:00
Kilian Schuettler
c831eb7025 DM-285: resolve circular dependecies 2023-10-23 14:10:46 +02:00
Kilian Schuettler
58e66f920c DM-285: change fields of comments 2023-10-23 13:54:26 +02:00
Kilian Schuettler
c65a93bf1b DM-285: flush add redaction removal
* only remove add redactions
2023-10-23 13:35:36 +02:00
Andrei Isvoran
50c731d6f4 Merge branch 'RED-7784' into 'master'
RED-7784 - Merge unprocessed changes into entity log

Closes RED-7784

See merge request redactmanager/persistence-service!191
2023-10-23 11:10:59 +02:00
Andrei Isvoran
a470fc0a2a RED-7784 - Merge unprocessed changes into entity log 2023-10-23 11:10:59 +02:00
Corina Olariu
9aee9627d8 RED-7434 - Remove Section Grid entirely
- update the layout parser version to remove the section grid from layou parser request
2023-10-23 11:20:12 +03:00
Yannik Hampe
39d800dd2a Merge branch 'RED-7654' into 'master'
RED-7654: Import and export of rules in non-escaped form

Closes RED-7654

See merge request redactmanager/persistence-service!190
2023-10-23 10:06:48 +02:00
yhampe
70bb450870 RED-7654: Import and export of rules in non-escaped form
* added back deleted code

* added id to ignoreproperties
2023-10-23 09:59:41 +02:00
yhampe
6221a7c5d7 RED-7654: Import and export of rules in non-escaped form
* removed debug logging
2023-10-23 08:22:42 +02:00
yhampe
9a5b192144 RED-7654: Import and export of rules in non-escaped form
* removed debug logging
2023-10-23 08:20:37 +02:00
yhampe
c0d2d1f9d0 Merge remote-tracking branch 'origin/RED-7654' into RED-7654 2023-10-23 08:19:22 +02:00
yhampe
b872effffd RED-7654: Import and export of rules in non-escaped form
* removed method call, as we do not care about the names when updating an existing template
2023-10-23 08:19:07 +02:00
yhampe
541da5d3d3 RED-7654: Import and export of rules in non-escaped form
* removed method call, as we do not care about the names when updating an existing template
2023-10-23 08:17:56 +02:00
Kilian Schüttler
46083bbbf7 Merge branch 'RED-7782' into 'master'
RED-7782 - Endpoint for unprocessed changes

Closes RED-7782

See merge request redactmanager/persistence-service!189
2023-10-20 15:56:46 +02:00
Andrei Isvoran
ac7b05049d RED-7782 - Endpoint for unprocessed changes 2023-10-20 15:56:46 +02:00
Dominique Eifländer
66a188d25a Merge branch 'RED-7760' into 'master'
RED-7760: Fixed accidently renamed id that leads to not working liquibase migration

Closes RED-7760

See merge request redactmanager/persistence-service!188
2023-10-19 14:24:07 +02:00
Dominique Eifländer
7df20ee8bb RED-7760: Fixed accidently renamed id that leads to not working liquibase migration 2023-10-19 14:16:34 +02:00
Corina Olariu
840a216265 Merge branch 'RED-7763' into 'master'
RED-7763 - Use EntityLog in tests in persistence-service

Closes RED-7763

See merge request redactmanager/persistence-service!186
2023-10-19 08:53:11 +02:00
Dominique Eifländer
f4950020a7 Merge branch 'RED-7775' into 'master'
RED-7775: Fixed too many connection to database by scheduler, name connections in pg_stat_activity

Closes RED-7775

See merge request redactmanager/persistence-service!187
2023-10-18 15:11:46 +02:00
Dominique Eifländer
b0db743019 RED-7775: Fixed too many connection to database by scheduler, name connections in pg_stat_activity 2023-10-18 15:00:38 +02:00
Corina Olariu
12cdc83ee3 RED-7763 - Use EntityLog in tests in persistence-service
- remove unneeded migrations
2023-10-17 14:52:10 +03:00
Corina Olariu
8d0cafd0c8 RED-7763 - Use EntityLog in tests in persistence-service
- updates junit tests
2023-10-17 14:14:23 +03:00
Corina Olariu
3611c9e33f RED-7763 - Use EntityLog in tests in persistence-service
- replaced redaction log with entity log
- updates junit tests
2023-10-17 13:22:38 +03:00
Dominique Eifländer
374998449a Merge branch 'RED-7768' into 'master'
RED-7768: Fixed double counting of deleted documents in license

Closes RED-7768

See merge request redactmanager/persistence-service!184
2023-10-17 09:32:13 +02:00
Andrei Isvoran
5fb3e6b103 Merge branch 'RED-7745-controller' into 'master'
RED-7745 - Refactor to EntityLog

See merge request redactmanager/persistence-service!183
2023-10-17 09:22:21 +02:00
Andrei Isvoran
0d150c3aa6 RED-7745 - Refactor to EntityLog 2023-10-17 09:22:21 +02:00
Dominique Eifländer
f317ff9f90 RED-7768: Fixed double counting of deleted documents in license 2023-10-17 09:21:42 +02:00
Dominique Eifländer
cd6166315c Merge branch 'RED-7767' into 'master'
RED-7767: Added migration for missing filesizes

Closes RED-7767

See merge request redactmanager/persistence-service!180
2023-10-16 13:31:45 +02:00
Dominique Eifländer
647b91193a RED-7767: Added migration for missing filesizes 2023-10-16 12:57:39 +02:00
Dominique Eifländer
1b20061c72 Merge branch 'RED-7682' into 'master'
RED-7682: Fixed Joining of parallel processed redaction of files when service...

Closes RED-7682

See merge request redactmanager/persistence-service!177
2023-10-16 10:42:33 +02:00
Dominique Eifländer
6f87c0bcec RED-7682: Fixed Joining of parallel processed redaction of files when service is scaled to more than 1 2023-10-16 10:34:15 +02:00
Andrei Isvoran
656183d33c Merge branch 'RED-7745' into 'master'
RED-7745 - Add EntityLog Feign Client

Closes RED-7745

See merge request redactmanager/persistence-service!178
2023-10-16 09:41:15 +02:00
Andrei Isvoran
80af534ce6 RED-7745 - Add EntityLog Feign Client 2023-10-16 09:41:15 +02:00
Kilian Schüttler
b8b9395654 Merge branch 'DM-285' into 'master'
DM-285: add filename to component result

Closes DM-285

See merge request redactmanager/persistence-service!176
2023-10-13 11:25:32 +02:00
Kilian Schuettler
4ae0a7e646 DM-285: add fileName to component endpoint 2023-10-13 11:01:10 +02:00
Kilian Schuettler
0b44b5abe7 DM-285: add fileName to component endpoint 2023-10-13 10:43:50 +02:00
Kilian Schüttler
07c5530631 Merge branch 'DM-504' into 'master'
DM-504: adjust error messages

Closes DM-504

See merge request redactmanager/persistence-service!175
2023-10-11 10:57:22 +02:00
Kilian Schuettler
77c6348e35 DM-504: adjust error messages
* also check for hard deleted date in dossier
2023-10-11 10:49:28 +02:00
Yannik Hampe
c9ce96ee67 Merge branch 'RED-7631' into 'master'
RED-7631: unescaped rule files

Closes RED-7631

See merge request redactmanager/persistence-service!174
2023-10-11 10:48:29 +02:00
Kilian Schuettler
56b4746a3e RED-7631: unescaped rule files 2023-10-11 10:41:50 +02:00
Kilian Schuettler
cd2d97616d RED-7631: unescaped rule files 2023-10-11 10:01:43 +02:00
Kilian Schüttler
c4485ad26b Merge branch 'RED-5945' into 'master'
RED-5945: 500 when importing faulty archive

Closes RED-5945

See merge request redactmanager/persistence-service!169
2023-10-10 16:02:55 +02:00
yhampe
209373d3fa RED-5945: 500 when importing faulty archive
* now throwing a BadRequestException when archive is faulty
2023-10-10 14:07:22 +02:00
Kilian Schüttler
c0ac625bbc Merge branch 'DM-504' into 'master'
DM-504: always exclude hard deleted files

Closes DM-504

See merge request redactmanager/persistence-service!171
2023-10-10 12:32:21 +02:00
Kilian Schuettler
99bb0228c4 DM-504: always exclude hard deleted files
* fix pmd
2023-10-10 12:16:30 +02:00
Kilian Schüttler
1aa17ea79e Merge branch 'DM-285' into 'master'
DM-285: add description to Component Endpoint

Closes DM-285

See merge request redactmanager/persistence-service!162
2023-10-10 12:09:18 +02:00
Kilian Schüttler
95ffc7c4af Merge branch 'DM-504' into 'master'
DM-504: always exclude hard deleted files

Closes DM-504

See merge request redactmanager/persistence-service!170
2023-10-10 11:32:14 +02:00
Kilian Schuettler
5a17678d34 DM-504: always exclude hard deleted files
* adjusted error message
* also removed annotationStatus since it is always APPROVED anyway
* removed inactive options from FilteredEntityLogRequest
* validated fileId and dossierId when getting comments
2023-10-10 11:25:43 +02:00
Kilian Schuettler
f7285fb821 DM-504: always exclude hard deleted files
* adjusted error message
* also removed annotationStatus since it is always APPROVED anyway
* removed inactive options from FilteredEntityLogRequest
* validated fileId and dossierId when getting comments
2023-10-10 10:51:40 +02:00
Kilian Schuettler
781b761aad DM-504: always exclude hard deleted files
* adjusted error message
* also removed annotationStatus since it is always APPROVED anyway
* removed inactive options from FilteredEntityLogRequest
2023-10-10 10:27:10 +02:00
yhampe
bc8ca76858 RED-5945: 500 when importing faulty archive
* now throwing a BadRequestException when archive is faulty
2023-10-10 09:51:48 +02:00
Dominique Eifländer
6ec370ad01 Merge branch 'RED-7715' into 'master'
RED-7715: Fixed messed up logging

Closes RED-7715

See merge request redactmanager/persistence-service!168
2023-10-09 15:15:07 +02:00
Dominique Eifländer
50005ad83a RED-7715: Fixed messed up logging 2023-10-09 15:03:09 +02:00
Yannik Hampe
f048efd42c Merge branch 'RED-7653' into 'master'
RED-7653: removed unnecessary test file, updated database-tenants-commons...

Closes RED-7653

See merge request redactmanager/persistence-service!167
2023-10-09 14:48:15 +02:00
yhampe
e9833ae71a RED-7653: removed unnecessary test file, updated database-tenants-commons version, fixed publish-custom-docker image script for windows 2023-10-09 14:26:57 +02:00
Corina Olariu
c79c653382 Merge branch 'RED-7185-clone' into 'master'
RED-7185 - RM-46 - Error message when adjusting the Justification

Closes RED-7185

See merge request redactmanager/persistence-service!165
2023-10-09 12:15:28 +02:00
Corina Olariu
97ab8a0783 RED-7185 - RM-46 - Error message when adjusting the Justification
- check for null for dossier template's name
- update junit test
2023-10-09 12:01:25 +03:00
Kilian Schüttler
15ffcf683f Merge branch 'windows-hotfix' into 'master'
hotfix: update database-tenant-commons

See merge request redactmanager/persistence-service!164
2023-10-09 10:59:49 +02:00
Kilian Schuettler
0e74e88f21 hotfix: update database-tenant-commons 2023-10-09 10:44:02 +02:00
Corina Olariu
a0c62274ef RED-7185 - RM-46 - Error message when adjusting the Justification
- adding the validation of dossier template's name and description when requesting a clone dossier template
2023-10-09 10:57:09 +03:00
yhampe
1a92b3db26 RED-7653: added cases for importing escaped and unescaped rule files 2023-10-09 09:28:13 +02:00
Kilian Schüttler
8778031869 Merge branch 'DM-504' into 'master'
DM-504: always exclude hard deleted files

Closes DM-504

See merge request redactmanager/persistence-service!163
2023-10-06 16:25:54 +02:00
Kilian Schuettler
dd5bcfd4f9 DM-504: always exclude hard deleted files 2023-10-06 15:19:00 +02:00
Kilian Schuettler
f706619140 DM-504: always exclude hard deleted files 2023-10-06 15:11:09 +02:00
Yannik Hampe
670b60041c Merge branch 'windows-hotfix' into 'master'
fixed errors on windows because of not closed input stream

See merge request redactmanager/persistence-service!161
2023-10-06 12:04:52 +02:00
yhampe
24af307fb7 added dependency to server pom 2023-10-06 11:56:50 +02:00
Kilian Schuettler
1f9a0e6326 DM-285: add description to Component Endpoint
* also remove field value from EntityReference
2023-10-06 11:46:10 +02:00
yhampe
02d87d7c14 fixed errors on windows because of not closed input stream
removed FileSystemBackedStorageService.java and started using import from storage-commons
2023-10-06 11:44:02 +02:00
Dominique Eifländer
f6b3df9c8f Merge branch 'RED-7715' into 'master'
RED-7715: Changed logging output to json

Closes RED-7715

See merge request redactmanager/persistence-service!160
2023-10-05 16:33:33 +02:00
Dominique Eifländer
7d3da44a97 RED-7715: Changed logging output to json 2023-10-05 16:26:30 +02:00
Kilian Schüttler
75b5db712a Merge branch 'DM-285' into 'master'
DM-285: added componentLog sorting in ComponentLogService

Closes DM-285

See merge request redactmanager/persistence-service!158
2023-10-05 14:37:01 +02:00
Kilian Schuettler
fc198aa34f DM-285: added componentLog sorting in ComponentLogService 2023-10-05 14:25:40 +02:00
Andrei Isvoran
3a1908b8ff Merge branch 'RED-7694' into 'master'
RED-7694 - Throw BadRequest if startDate is null or in the future for license report

Closes RED-7694

See merge request redactmanager/persistence-service!156
2023-10-05 11:37:49 +02:00
Andrei Isvoran
79bc6d2b39 RED-7694 - Throw BadRequest if startDate is null or in the future for license report 2023-10-05 12:28:56 +03:00
Kilian Schüttler
d694a7fec1 Merge branch 'DM-285' into 'master'
DM-285: add internal component controller

Closes DM-285

See merge request redactmanager/persistence-service!155
2023-10-04 16:25:11 +02:00
Kilian Schuettler
8fa082f7a0 DM-285: add internal component controller 2023-10-04 16:18:41 +02:00
Andrei Isvoran
ea24d26fb8 Merge branch 'RED-7687' into 'master'
RED-7694 - Check if startDate is before endDate for license before overwriting...

Closes RED-7694

See merge request redactmanager/persistence-service!152
2023-10-03 09:50:13 +02:00
Andrei Isvoran
ad20a1f9f5 RED-7687 - Check if startDate is before endDate for license before overwriting the endDate if it's in the future. 2023-10-02 16:18:52 +03:00
Corina Olariu
b706a71ec1 Merge branch 'RED-7185-adjustLimit' into 'master'
RED-7185 - RM-46 - Error message when adjusting the Justification

Closes RED-7185

See merge request redactmanager/persistence-service!150
2023-10-02 11:44:29 +02:00
Andrei Isvoran
97d2af3eb5 Merge branch 'RED-7676' into 'master'
RED-7676 - Copy ocrByDefault and removeWatermark flags when cloning dossier template

Closes RED-7676

See merge request redactmanager/persistence-service!151
2023-10-02 11:39:14 +02:00
Andrei Isvoran
d3f3817ea1 RED-7676 - Copy ocrByDefault and removeWatermark flags when cloning dossier template 2023-10-02 12:32:39 +03:00
Kresnadi Budisantoso
99fae8cd52 Merge branch 'DM-504' into 'master'
DM-504: re-add includeSoftDeleted flag

See merge request redactmanager/persistence-service!149
2023-09-29 22:14:50 +02:00
Kilian Schuettler
941990787c DM-504: re-add includeSoftDeleted flag
* minor renames of ComponentLog fields
2023-09-29 17:56:54 +02:00
Kresnadi Budisantoso
b6eb08d722 Merge branch 'DM-285' into 'master'
Resolve DM-285

Closes DM-285

See merge request redactmanager/persistence-service!148
2023-09-29 17:30:41 +02:00
Kilian Schuettler
fb63bf65b0 DM-285: fix order of Components 2023-09-29 17:22:03 +02:00
Kilian Schuettler
0c91fff875 DM-285: fix order of Components 2023-09-29 12:47:58 +02:00
Corina Olariu
7f7ca627d6 RED-7185 - RM-46 - Error message when adjusting the Justification
- permit only comments with length <= 4000 characters
2023-09-29 13:23:52 +03:00
Timo Bejan
65d62a09a1 Merge branch 'dossier-template-swagger-import-fix' into 'master'
Fixed binary file upload for swagger

See merge request redactmanager/persistence-service!147
2023-09-28 13:06:47 +02:00
Timo Bejan
3e43fe0382 Fixed binary file upload for swagger 2023-09-28 13:09:14 +03:00
Kilian Schüttler
cfe5446255 Merge branch 'DM-502' into 'master'
DM-502, DM-503, DM-504

Closes DM-502

See merge request redactmanager/persistence-service!146
2023-09-27 11:27:28 +02:00
Kilian Schuettler
f9f5dfd1c9 DM-504: Remove unnecessary flag for requesting specific file or dossier 2023-09-27 11:18:36 +02:00
Kilian Schuettler
d49300659b DM-503: Wrong query parameter for "keepManualChanges" 2023-09-27 11:11:49 +02:00
Kilian Schuettler
f8231a6499 DM-502: Dossier Endpoint returns all dossiers regardless of the set dossierTemplateId 2023-09-27 11:08:29 +02:00
Corina Olariu
bfa88fd082 Merge branch 'RED-7185-checkComment' into 'master'
RED-7185 - RM-46 - Error message when adjusting the Justification

Closes RED-7185

See merge request redactmanager/persistence-service!145
2023-09-27 08:15:26 +02:00
Kilian Schüttler
5fe11f9da9 Merge branch 'RED-7416-apidoc' into 'master'
Put back the parameters in the main spec file but moved them to the components...

Closes RED-7416

See merge request redactmanager/persistence-service!144
2023-09-26 18:11:03 +02:00
Kilian Schuettler
c1ea8bbb4f RED-7416: only write value if not equal to originalValue 2023-09-26 18:04:20 +02:00
Kilian Schüttler
6f8e319abf Merge branch 'RED-7416-apidoc' into 'master'
OpenAPI Specification for DocuMine

Closes RED-7416

See merge request redactmanager/persistence-service!142
2023-09-26 17:52:16 +02:00
Kresnadi Budisantoso
87e27050dd OpenAPI Specification for DocuMine 2023-09-26 17:52:16 +02:00
Kilian Schüttler
ddc3af3a41 Merge branch 'DM-285' into 'master'
DM-285: ensure request bodies are not empty

Closes DM-285

See merge request redactmanager/persistence-service!143
2023-09-26 17:41:26 +02:00
Kilian Schuettler
fbae5ff3a0 DM-285: ensure request bodies are not empty 2023-09-26 14:47:26 +02:00
Corina Olariu
91ffe50781 RED-7185 - RM-46 - Error message when adjusting the Justification
- add checkComment in addComment when making changes to a redaction
2023-09-26 15:01:39 +03:00
Kresnadi Budisantoso
353e0a91e7 Merge branch 'DM-285' into 'master'
Resolve DM-285

Closes DM-285

See merge request redactmanager/persistence-service!141
2023-09-25 17:27:01 +02:00
Kilian Schuettler
e94d3c6146 DM-285: polish component log and component endpoints 2023-09-25 17:10:02 +02:00
Kilian Schuettler
919825774d DM-285: add containing Node to EntityLogEntry 2023-09-25 16:39:12 +02:00
Corina Olariu
6cdd2178f1 Merge branch 'RED-7185' into 'master'
RED-7185 - Fix comment too long

Closes RED-7185

See merge request redactmanager/persistence-service!138
2023-09-25 10:27:15 +02:00
Kilian Schüttler
93d40366c7 Merge branch 'DM-285' into 'master'
DM-285: Change component log

Closes DM-285

See merge request redactmanager/persistence-service!137
2023-09-25 09:57:50 +02:00
Christoph Schabert
a2bd4564b4 Merge branch 'staging-fix' into 'master'
Fixed wrong execution of liquibase on staging envs

See merge request redactmanager/persistence-service!140
2023-09-25 08:03:31 +02:00
Timo Bejan
4a63c48ad9 Fixed wrong execution of liquibase on staging envs 2023-09-22 17:32:33 +03:00
Andrei Isvoran
f006af37e4 RED-7185 - Fix comment too long 2023-09-22 17:10:10 +03:00
Kilian Schuettler
3f64fa6c40 DM-285: Change component log
* split AnalysisLogController into EntityLogController and ComponentLogController
* fully deprecate RSSController
2023-09-22 15:19:57 +02:00
Kilian Schuettler
54ce629a1c DM-285: Change component log
* apply overrides in ComponentLogService
* added a test for manual type change
* include overrides in getComponentLog
* split AnalysisLogController into EntityLogController and ComponentLogController
* fully deprecate RSSController
2023-09-22 15:10:18 +02:00
Kilian Schüttler
277f0538be Merge branch 'DM-285' into 'master'
DM-285: Change component log

Closes DM-285

See merge request redactmanager/persistence-service!135
2023-09-22 14:22:42 +02:00
Kilian Schuettler
3c9d247432 DM-285: Change component log
* apply overrides in ComponentLogService
* added a test for manual type change
* include overrides in getComponentLog
2023-09-22 14:16:03 +02:00
Kilian Schüttler
45b879982b Merge branch 'DM-285' into 'master'
DM-285: Change component log

Closes DM-285

See merge request redactmanager/persistence-service!134
2023-09-22 10:57:59 +02:00
Kilian Schuettler
e651c8225f DM-285: Change component log 2023-09-22 10:32:09 +02:00
Kilian Schüttler
10315356f5 Merge branch 'RED-6825' into 'master'
RED-6835: clean incoming strings

Closes RED-6825

See merge request redactmanager/persistence-service!132
2023-09-21 15:24:01 +02:00
Kilian Schuettler
55153d0a09 RED-6835: clean incoming strings 2023-09-21 14:55:21 +02:00
Andrei Isvoran
0b7a925757 Merge branch 'DM-498' into 'master'
DM-498 - Fix 500 internal server error when overrides do not exist

Closes DM-498

See merge request redactmanager/persistence-service!131
2023-09-21 14:10:45 +02:00
Andrei Isvoran
51da8f5d58 DM-498 - Fix 500 internal server error when overrides do not exist 2023-09-21 14:10:45 +02:00
Andrei Isvoran
0a5c531c72 Merge branch 'RED-7185-fix' into 'master'
RED-7185 - Error message when adjusting the Justification

Closes RED-7185

See merge request redactmanager/persistence-service!130
2023-09-20 12:02:16 +02:00
Andrei Isvoran
b5c832c8a3 RED-7185 - Error message when adjusting the Justification 2023-09-20 12:31:39 +03:00
Christoph Schabert
707631f2c7 Merge branch 'DM-285' into 'master'
DM-285: validate rules on dossier template import

Closes DM-285

See merge request redactmanager/persistence-service!128
2023-09-19 08:22:07 +02:00
Kilian Schuettler
1b0323839e DM-285: validate rules on dossier template import 2023-09-18 16:16:14 +02:00
Andrei Isvoran
5beaa3ab2b Merge branch 'RED-5205-fix' into 'master'
RED-5205 - fix getmapping path for download

Closes RED-5205

See merge request redactmanager/persistence-service!127
2023-09-15 11:01:36 +02:00
Dominique Eifländer
f4b2ec88d6 Merge branch 'RED-7578' into 'master'
RED-7578: Implemented logic of components endpoint for api v2

Closes RED-7578

See merge request redactmanager/persistence-service!126
2023-09-15 10:56:14 +02:00
Andrei Isvoran
f8dec7f671 RED-5205 - fix getmapping path for download 2023-09-15 11:55:17 +03:00
deiflaender
ad9ef6351f RED-7578: Implemented logic of components endpoint for api v2 2023-09-15 10:36:13 +02:00
Andrei Isvoran
80d707a374 Merge branch 'RED-7185' into 'master'
RED-7185 - RM-46 - Error message when adjusting the Justification

Closes RED-7185

See merge request redactmanager/persistence-service!123
2023-09-15 09:35:57 +02:00
Andrei Isvoran
837822b7dd RED-7185 - RM-46 - Error message when adjusting the Justification 2023-09-15 09:35:57 +02:00
Dominique Eifländer
59474eb52e Merge branch 'RED-7599' into 'master'
RED-7599: schedule reanalysis for any manual redaction

Closes RED-7599

See merge request redactmanager/persistence-service!125
2023-09-15 09:01:06 +02:00
Kilian Schuettler
dd359282cf RED-7599: schedule reanalysis for any manual redaction 2023-09-14 19:25:19 +02:00
Dominique Eifländer
4465c282d6 Merge branch 'DM-285' into 'master'
DM-285: move originalValue to EntityReference

Closes DM-285

See merge request redactmanager/persistence-service!124
2023-09-14 16:27:55 +02:00
Kilian Schuettler
af526cc7e7 DM-285: move originalValue to EntityReference 2023-09-14 15:44:25 +02:00
Timo Bejan
04f3bd1c1b Merge branch 'RED-5202-direct-download' into 'master'
RED-5205 - direct stream download implementation

Closes RED-5202

See merge request redactmanager/persistence-service!122
2023-09-14 14:46:55 +02:00
Dominique Eifländer
02cc8b54e3 Merge branch 'RED-7578' into 'master'
RED-7578: Fixed order of endpoints, set swagger endpoint to /api, fixed not...

Closes RED-7578

See merge request redactmanager/persistence-service!121
2023-09-14 14:27:30 +02:00
Andrei Isvoran
81de9adcba RED-5205 - fix compile error 2023-09-14 15:18:37 +03:00
deiflaender
cb269b8e86 RED-7578: Fixed order of endpoints, set swagger endpoint to /api, fixed not used Error codes in spec 2023-09-14 14:11:40 +02:00
Timo Bejan
7058cb3035 RED-5205 - direct stream download implementation 2023-09-14 15:10:17 +03:00
Dominique Eifländer
e9edfa12f7 Merge branch 'DM-285' into 'master'
DM-285: make component rules version default to -1

Closes DM-285

See merge request redactmanager/persistence-service!120
2023-09-14 13:39:16 +02:00
Kilian Schuettler
f3d148a631 DM-285: make component rules version default to -1
* fix 500 when invalid rules file
* fix AnalysisLogResource documentation
2023-09-14 13:32:25 +02:00
Dominique Eifländer
78d3a772da Merge branch 'RED-7578' into 'master'
RED-7578: Implemented first endpoints of external-api-v2

Closes RED-7578

See merge request redactmanager/persistence-service!119
2023-09-14 09:09:25 +02:00
Kilian Schüttler
2fc912d414 Merge branch 'DM-285' into 'master'
DM-285: minor fixes to previous version

See merge request redactmanager/persistence-service!117
2023-09-13 17:30:39 +02:00
Kilian Schuettler
62065e89ca DM-285: get component version without transaction 2023-09-13 17:05:13 +02:00
deiflaender
0e04331285 RED-7578: Implemented first endpoints of external-api-v2 2023-09-13 16:44:51 +02:00
Kilian Schuettler
70b7a258e9 DM-285: update component rules version with AnalyzeResult 2023-09-13 16:04:17 +02:00
Andrei Isvoran
d5af5a2132 Merge branch 'RED-7599' into 'master'
RED-7599 - Always trigger reanalysis when removing manual redaction

Closes RED-7599

See merge request redactmanager/persistence-service!118
2023-09-13 15:43:53 +02:00
Andrei Isvoran
3d8a33dfd1 RED-7599 - Always trigger reanalysis when removing manual redaction 2023-09-13 16:31:27 +03:00
Kilian Schuettler
d3db8c7b16 DM-285: update component rules version with AnalyzeResult 2023-09-13 15:14:41 +02:00
Kilian Schuettler
31890e6434 DM-285: update component rules version with AnalyzeResult 2023-09-13 14:46:11 +02:00
Kilian Schuettler
9bca6b406a DM-285: make rule value non nullable 2023-09-13 13:59:31 +02:00
Kilian Schuettler
7466a61583 DM-285: don't import empty component rules 2023-09-12 17:22:19 +02:00
Kilian Schuettler
e2c7238c67 DM-285: make RuleFileType.ENTITY default 2023-09-12 15:41:45 +02:00
Kilian Schüttler
662b52e00a Merge branch 'DM-285' into 'master'
DM-285: add pathvariable annotation to internal rules controller

Closes DM-285

See merge request redactmanager/persistence-service!116
2023-09-12 08:20:02 +02:00
Kilian Schuettler
368cf13586 DM-285: add pathvariable annotation to internal rules controller 2023-09-11 17:36:44 +02:00
Kilian Schüttler
32361f7f2f Merge branch 'DM-285' into 'master'
DM-285: add component log and entity log, deprecate redaction log

Closes DM-285

See merge request redactmanager/persistence-service!110
2023-09-11 17:14:00 +02:00
Kilian Schuettler
7d4548e40c DM-285: fully deprecated RedactionLog, added component rule file
* return 404, when component rule file hasn't been created yet* deprecate RSSResponse
* enter clean redaction-service version
* fixed some messages
* used correct RuleFileType
* updated default rules
2023-09-11 17:02:17 +02:00
Kilian Schuettler
56baa7db07 DM-285: fully deprecated RedactionLog, added component rule file
* return 404, when component rule file hasn't been created yet* deprecate RSSResponse
* enter clean redaction-service version
* fixed some messages
* used correct RuleFileType
2023-09-11 16:23:08 +02:00
Ali Oezyetimoglu
322f605004 Merge branch 'RED-7572' into 'master'
RED-7572 - Remove unused method

Closes RED-7572

See merge request redactmanager/persistence-service!115
2023-09-11 16:00:24 +02:00
Andrei Isvoran
808dc37d21 RED-7572 - Remove unused method 2023-09-11 16:53:03 +03:00
Andrei Isvoran
cb6c81b9e9 Merge branch 'RED-7572' into 'master'
RED-7572 - Use MagicConverter instead of manually converting

Closes RED-7572

See merge request redactmanager/persistence-service!114
2023-09-11 15:49:19 +02:00
Andrei Isvoran
0f7d648044 RED-7572 - Use MagicConverter instead of manually converting 2023-09-11 16:27:26 +03:00
Kilian Schuettler
2e142f12e9 DM-285: fully deprecated RedactionLog, added component rule file
* return 404, when component rule file hasn't been created yet* deprecate RSSResponse
* enter clean redaction-service version
2023-09-11 15:27:06 +02:00
Kilian Schuettler
1e53942178 DM-285: fully deprecated RedactionLog, added component rule file
* return 404, when component rule file hasn't been created yet* deprecate RSSResponse
2023-09-11 13:46:46 +02:00
Kilian Schuettler
db40f5a1a9 DM-285: fully deprecated RedactionLog, added component rule file
* return 404, when component rule file hasn't been created yet
2023-09-11 13:35:02 +02:00
Kilian Schuettler
91b13c41ce DM-285: fully deprecated RedactionLog, added component rule file
* add ComponentLogCategory
2023-09-11 13:35:02 +02:00
Kilian Schuettler
8fc94de4b7 DM-285: fully deprecated RedactionLog, added component rule file
* return 404, when component rule file hasn't been created yet
2023-09-11 13:35:02 +02:00
Kilian Schuettler
b4527e4bfe DM-285: fully deprecated RedactionLog, added component rule file 2023-09-11 13:35:02 +02:00
Kilian Schuettler
a621ff1484 DM-285: add component log and entity log, deprecate redaction log
* also refactor RedactionLogController
2023-09-11 13:35:02 +02:00
Andrei Isvoran
701ee0ff43 Merge branch 'RED-7572' into 'master'
RED-7572 - Fix privateKey for digital signature

Closes RED-7572

See merge request redactmanager/persistence-service!113
2023-09-11 13:04:56 +02:00
Andrei Isvoran
78bb3b72ca RED-7572 - Fix privateKey for digital signature 2023-09-11 13:49:24 +03:00
Andrei Isvoran
82a5d6a1cd Merge branch 'RED-7572' into 'master'
RED-7572 - Change privateKey to base64EncodedPrivateKey for DigitalSignature...

Closes RED-7572

See merge request redactmanager/persistence-service!112
2023-09-08 13:47:24 +02:00
Andrei Isvoran
edd0c747cf RED-7572 - Change privateKey to base64EncodedPrivateKey for DigitalSignature... 2023-09-08 13:47:24 +02:00
Andrei Isvoran
be3d717079 Merge branch 'DM-402' into 'master'
DM-402 - Don't add double notifications when user is added to a dossier in documine

Closes DM-402

See merge request redactmanager/persistence-service!111
2023-09-08 08:17:37 +02:00
Andrei Isvoran
230e674c6f DM-402 - Don't add double notifications when user is added to a dossier in documine 2023-09-07 17:04:40 +03:00
Dominique Eifländer
261d69fa52 Merge branch 'DM-410' into 'master'
DM-410: Added endpoint to set that a timeout was detected during rule execution

Closes DM-410

See merge request redactmanager/persistence-service!109
2023-09-07 11:17:11 +02:00
deiflaender
c4d8f16c2e DM-410: Added endpoint to set that a timeout was detected during rule execution 2023-09-07 10:54:25 +02:00
Timo Bejan
23455c2468 Merge branch 'RED-7500' into 'master'
RED-7500 - Remove extension that gets generated by storage-commons

Closes RED-7500

See merge request redactmanager/persistence-service!107
2023-09-06 13:35:49 +02:00
Andrei Isvoran
ae7a3273a1 RED-7500 - Remove extension that gets generated by storage-commons 2023-09-06 13:35:49 +02:00
Kilian Schüttler
03cc83d751 Merge branch 'RED-7317' into 'master'
RED-7317: fix addredaction with addToDict

Closes RED-7317

See merge request redactmanager/persistence-service!108
2023-09-05 16:59:36 +02:00
Kilian Schuettler
fbba154295 RED-7317: fix addredaction with addToDict
* remove latest tag
* rename publish-custom-image.sh, so mvn doesn't try to accidentally run it :)
2023-09-05 16:44:39 +02:00
Kilian Schüttler
b55cfefe6e Merge branch 'RED-7317' into 'master'
RED-7317: fix addredaction with addToDict

Closes RED-7317

See merge request redactmanager/persistence-service!106
2023-09-05 16:26:27 +02:00
Kilian Schuettler
8eb0ae2b06 RED-7317: fix addredaction with addToDict
* added publish-custom-image.sh
2023-09-05 14:54:41 +02:00
Kilian Schüttler
bc63acee33 Merge branch 'RED-7317' into 'master'
RED-7317: set DictionaryEntryType to ENTRY by default

Closes RED-7317

See merge request redactmanager/persistence-service!105
2023-09-04 17:09:46 +02:00
Kilian Schuettler
39172c2da2 RED-7317: reprocess when legalBasisChange 2023-09-04 14:46:21 +02:00
Kilian Schuettler
067d3e49ba RED-7317: change endpoint descriptions 2023-09-04 13:47:59 +02:00
Kilian Schuettler
73afa0f349 RED-7317: set DictionaryEntryType to ENTRY by default 2023-09-04 12:06:27 +02:00
Kilian Schüttler
b63fc5adb2 Merge branch 'RED-7317' into 'master'
RED-7317: mark LegalBasisChange as processed in DB after analysis

Closes RED-7317

See merge request redactmanager/persistence-service!104
2023-09-01 16:00:23 +02:00
Kilian Schuettler
5302e09b4d RED-7317: rename image-recategorization to recategorization 2023-09-01 15:50:12 +02:00
Kilian Schuettler
98bf385c20 RED-7317: mark LegalBasisChange as processed in DB after analysis 2023-09-01 13:56:33 +02:00
Kilian Schüttler
6e63a1e7f4 Merge branch 'RED-7317' into 'master'
RED-7317: Endpoint to change entity types of dict-based annotations

Closes RED-7317

See merge request redactmanager/persistence-service!102
2023-08-30 17:07:46 +02:00
Kilian Schuettler
701c65eac7 RED-7317: Endpoint to change entity types of dict-based annotations
* fix test
* make it clear, which field does what
2023-08-30 17:01:53 +02:00
Kilian Schuettler
7fcb52c90f RED-7317: Endpoint to change entity types of dict-based annotations
* fix test
2023-08-30 16:57:34 +02:00
Kilian Schuettler
be915430b1 RED-7317: Endpoint to change entity types of dict-based annotations
* fix pmd
* add mapper to remove even more code from controller
2023-08-30 16:41:26 +02:00
Kilian Schüttler
a189ae4382 Merge branch 'RED-7317' into 'master'
RED-7317: Keep user-edited paragraph/location when recategorizing entity

Closes RED-7317

See merge request redactmanager/persistence-service!101
2023-08-30 15:53:47 +02:00
Kilian Schuettler
d40ef63116 RED-7317: Endpoint to change entity types of dict-based annotations
* fix tests
2023-08-30 15:46:49 +02:00
Kilian Schuettler
80e602fb07 RED-7317: Endpoint to change entity types of dict-based annotations
* rebased
2023-08-30 15:33:09 +02:00
Kilian Schuettler
d25b8c3934 RED-7317: Endpoint to change entity types of dict-based annotations
* fixed tests
2023-08-30 15:28:35 +02:00
Kilian Schuettler
ea5f79d67c RED-7317: Endpoint to change entity types of dict-based annotations
* added undo functionality
* fixed tests
* added recategorize with dictionary tests
2023-08-30 15:28:34 +02:00
Kilian Schuettler
aab2971d6e RED-7317: Endpoint to change entity types of dict-based annotations
* major refactor of manual redaction service
2023-08-30 15:28:20 +02:00
Kilian Schuettler
d1d5331202 RED-7317: add getComment Endpoint
* moved manual redaction undo code
2023-08-30 15:28:20 +02:00
Kilian Schuettler
0b173ec930 RED-7317: Keep user-edited paragraph/location when recategorizing entity
* refactor of ManualRedactionService, still WIP
2023-08-30 15:28:17 +02:00
Dominique Eifländer
9f250058bb Merge branch 'RED-7508' into 'master'
RED-7508: Fixed wrong response type for false positive entity types

Closes RED-7508

See merge request redactmanager/persistence-service!100
2023-08-30 12:56:06 +02:00
deiflaender
6b22622e7e RED-7508: Fixed wrong response type for false positive entity types 2023-08-30 12:47:52 +02:00
Dominique Eifländer
e12db2e0c2 Merge branch 'RED-7508' into 'master'
RED-7508: Fixed undelete of false positive entries at manual redaction at...

Closes RED-7508

See merge request redactmanager/persistence-service!99
2023-08-30 12:32:25 +02:00
deiflaender
8c7435f8b4 RED-7508: Fixed undelete of false positive entries at manual redaction at false positive to all dossiers 2023-08-30 12:26:06 +02:00
Andrei Isvoran
aeaf0483d3 Merge branch 'RED-7500' into 'master'
RED-7500 - Only delete the metadata for the specific storage

Closes RED-7500

See merge request redactmanager/persistence-service!98
2023-08-29 15:30:19 +02:00
Andrei Isvoran
af4dff23dc RED-7500 - Only delete the metadata for the specific storage 2023-08-29 15:30:19 +02:00
Andrei Isvoran
633ddec803 Merge branch 'RED-7500' into 'master'
RED-7500 Fix report storage id generation

Closes RED-7500

See merge request redactmanager/persistence-service!97
2023-08-29 14:08:10 +02:00
Andrei Isvoran
515edb1c1c RED-7500 Fix report storage id generation 2023-08-29 14:08:10 +02:00
Andrei Isvoran
0fb7ccf7a2 Merge branch 'RED-7500' into 'master'
RED-7500 - Delete Report json metadata from azure/s3 storage

Closes RED-7500

See merge request redactmanager/persistence-service!96
2023-08-29 12:57:35 +02:00
Andrei Isvoran
0162017e61 RED-7500 - Delete Report json metadata from azure/s3 storage 2023-08-29 12:57:34 +02:00
Corina Olariu
f5b925d961 Merge branch 'RED-6860-addGetObject' into 'master'
RED-6860 - Transaction timeout while preparing download

Closes RED-6860

See merge request redactmanager/persistence-service!95
2023-08-29 09:28:22 +02:00
Corina Olariu
698fcbe2b7 RED-6860 - Transaction timeout while preparing download
- add getObject for the download of archives
2023-08-28 18:29:56 +03:00
Andrei Isvoran
79756640da Merge branch 'RED-7500' into 'master'
RED-7500 - Delete file from storage when deleting download status

Closes RED-7500

See merge request redactmanager/persistence-service!94
2023-08-28 12:44:25 +02:00
Andrei Isvoran
28f9401719 RED-7500 - Also delete Report json metadata when deleting archive 2023-08-28 12:44:25 +02:00
Timo Bejan
d6be241717 Merge branch 'RED-7439' into 'master'
RED-7439 - single transaction write for ace entries of a dossier

Closes RED-7439

See merge request redactmanager/persistence-service!93
2023-08-24 20:20:04 +02:00
Timo Bejan
9d084c57c0 RED-7439 - single transaction write for ace entries of a dossier 2023-08-24 21:10:03 +03:00
Kilian Schüttler
e9eb0a5171 Merge branch 'RED-7158' into 'master'
RED-7158: even more specific errors

Closes RED-7158

See merge request redactmanager/persistence-service!92
2023-08-24 14:20:10 +02:00
Kilian Schuettler
d28e485dff RED-7158: even more specific errors 2023-08-24 14:13:00 +02:00
Andrei Isvoran
6a544df044 Merge branch 'RED-7500' into 'master'
RED-7500 - Delete file from storage when deleting download status

Closes RED-7500

See merge request redactmanager/persistence-service!91
2023-08-24 09:53:10 +02:00
Andrei Isvoran
7f7dd7a182 RED-7500 - Delete file from storage when deleting download status 2023-08-24 10:07:03 +03:00
Timo Bejan
96d4407f53 Merge branch 'RED-7400-ps2' into 'master'
Resolve RED-7400

Closes RED-7400

See merge request redactmanager/persistence-service!79
2023-08-23 18:20:51 +02:00
Kilian Schüttler
b491d2df5e Merge branch 'RED-7158' into 'master'
RED-7158: Rosario wants more verbose error messages

Closes RED-7158

See merge request redactmanager/persistence-service!90
2023-08-23 18:00:08 +02:00
Kilian Schuettler
6f7327aa0d RED-7158: Rosario wants more verbose error messages 2023-08-23 17:52:24 +02:00
Ali Oezyetimoglu
ef4eb85481 Merge branch 'RED-7439' into 'master'
RED-7439: throw JobExecutionError to prevent 500 because violating constraint...

Closes RED-7439

See merge request redactmanager/persistence-service!89
2023-08-23 14:05:07 +02:00
Ali Oezyetimoglu
caa017bb24 RED-7439: throw JobExecutionError to prevent 500 because violating constraint acl_entry_unique leads to
DuplicateKeyException
2023-08-23 12:30:09 +02:00
Kilian Schüttler
66629b09cc Update redaction-service version 2023-08-23 09:46:16 +02:00
Kilian Schüttler
2f39e6f956 Merge branch 'RED-7493' into 'master'
RED-7493: provide error messages in upload endpoint

Closes RED-7493

See merge request redactmanager/persistence-service!86
2023-08-22 15:52:49 +02:00
Kilian Schuettler
8b1d26e4e5 RED-7493: fix tests 2023-08-22 15:43:33 +02:00
Kilian Schuettler
b1b544faf6 RED-7493: fix versions in parent pom 2023-08-22 15:30:34 +02:00
Kilian Schuettler
1ac0a184b7 RED-7493: provide error messages in upload endpoint 2023-08-22 15:08:45 +02:00
Kilian Schüttler
ccc2411a92 Merge branch 'RED-7158' into 'master'
RED-7158: fix status code, when dossierId not found

Closes RED-7158

See merge request redactmanager/persistence-service!85
2023-08-22 09:52:10 +02:00
Kilian Schuettler
e3411dba42 RED-7158: fix status code, when dossierId not found 2023-08-22 09:33:04 +02:00
Kevin Tumma
313eebf90b hotfix 2023-08-18 12:22:52 +02:00
Kilian Schüttler
9b37fceb98 Merge branch 'DM-357' into 'master'
DM-357: fix idRemoval mark as processed logic

Closes DM-357

See merge request redactmanager/persistence-service!84
2023-08-18 12:11:20 +02:00
Kilian Schuettler
ed90b6a9cc DM-357: fix idRemoval mark as processed logic 2023-08-18 11:22:34 +02:00
Corina Olariu
ccade4b5fd Merge branch 'RED-6864-dlq' into 'master'
RED-6864 - Optimization of Upload and Download for Large Files in Azure Blob...

Closes RED-6864

See merge request redactmanager/persistence-service!82
2023-08-18 09:43:57 +02:00
Corina Olariu
2c3ce2de97 Merge remote-tracking branch 'origin' into RED-6864-dlq 2023-08-18 10:28:56 +03:00
Corina Olariu
896f0a6e5d RED-6864 - Optimization of Upload and Download for Large Files in Azure Blob Storage and AWS S3/MinIO
- remove the entry from map when download finished
- check for null in case the download process has finished
2023-08-18 10:26:23 +03:00
Kevin Tumma
16e84b85b1 hotfix 2023-08-17 16:40:14 +02:00
Corina Olariu
11958a4099 RED-6864 - Optimization of Upload and Download for Large Files in Azure Blob Storage and AWS S3/MinIO
- add @RabbitListener to the method
2023-08-17 16:50:09 +03:00
deiflaender
3ae67b77e4 hotfix: load test-containers docker images from docker-dev.knecon.com/tests/ 2023-08-17 15:15:24 +02:00
Andrei Isvoran
04212cdd4e RED-6864 - Remove fileProxyStreamForDownload as it closes the stream before downloading the file 2023-08-17 14:37:17 +03:00
Ali Oezyetimoglu
7f7e1d2699 Merge branch 'RED-7400-ps3' into 'master'
RED-7400: changed place of isLocalManualRedaction as it is not needed in RedactionLogEntry anymore

Closes RED-7400

See merge request redactmanager/persistence-service!80
2023-08-17 08:22:16 +02:00
Ali Oezyetimoglu
4164de2a5c RED-7400: changed place of isLocalManualRedaction as it is not needed in RedactionLogEntry anymore 2023-08-17 08:15:02 +02:00
Timo Bejan
13742a12cd Merge branch 'RED-6864' into 'master'
RED-6864 - Optimization of Upload and Download for Large Files in Azure Blob...

Closes RED-6864

See merge request redactmanager/persistence-service!78
2023-08-16 21:05:41 +02:00
Andrei Isvoran
02af9810df RED-6864 - Optimization of Upload and Download for Large Files in Azure Blob... 2023-08-16 21:05:40 +02:00
Ali Oezyetimoglu
3384e91067 RED-7400: added @Builder.Default for engines and reference 2023-08-16 19:21:20 +02:00
Ali Oezyetimoglu
2db7128b3a RED-7400: added @Builder.Default for engines and reference 2023-08-16 10:37:41 +02:00
942 changed files with 566038 additions and 11703 deletions

0
.dev/docker-compose.yaml Normal file → Executable file
View File

8
.gitignore vendored
View File

@ -26,3 +26,11 @@
**/.DS_Store
**/classpath-data.json
**/dependencies-and-licenses-overview.txt
gradle.properties
gradlew
gradlew.bat
gradle/
**/.gradle
**/build

View File

@ -2,5 +2,23 @@ variables:
SONAR_PROJECT_KEY: 'RED_persistence-service'
include:
- project: 'gitlab/gitlab'
ref: 'nexustest'
file: 'ci-templates/maven_java.yml'
ref: 'main'
file: 'ci-templates/gradle_java.yml'
deploy:
stage: deploy
tags:
- dind
script:
- echo "Building with gradle version ${BUILDVERSION}"
- gradle -Pversion=${BUILDVERSION} publish
- gradle bootBuildImage --publishImage -PbuildbootDockerHostNetwork=true -Pversion=${BUILDVERSION}
- echo "BUILDVERSION=$BUILDVERSION" >> version.env
artifacts:
reports:
dotenv: version.env
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

@ -0,0 +1,7 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
}

View File

@ -0,0 +1,79 @@
plugins {
`java-library`
`maven-publish`
pmd
checkstyle
jacoco
}
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()
mavenCentral()
maven {
url = uri("https://nexus.knecon.com/repository/gindev/");
credentials {
username = providers.gradleProperty("mavenUser").getOrNull();
password = providers.gradleProperty("mavenPassword").getOrNull();
}
}
}
group = "com.iqser.red.service"
java.sourceCompatibility = JavaVersion.VERSION_17
java.targetCompatibility = JavaVersion.VERSION_17
pmd {
isConsoleOutput = true
}
tasks.pmdMain {
pmd.ruleSetFiles = files("${rootDir}/config/pmd/pmd.xml")
}
tasks.pmdTest {
pmd.ruleSetFiles = files("${rootDir}/config/pmd/test_pmd.xml")
}
tasks.named<Test>("test") {
useJUnitPlatform()
reports {
junitXml.outputLocation.set(layout.buildDirectory.dir("reports/junit"))
}
minHeapSize = "512m"
maxHeapSize = "2048m"
}
tasks.test {
finalizedBy(tasks.jacocoTestReport) // report is always generated after tests run
}
tasks.jacocoTestReport {
dependsOn(tasks.test) // tests are required to run before generating the report
reports {
xml.required.set(true)
csv.required.set(false)
html.outputLocation.set(layout.buildDirectory.dir("jacocoHtml"))
}
}
java {
withJavadocJar()
}
allprojects {
tasks.withType<Javadoc> {
options {
this as StandardJavadocDocletOptions
addBooleanOption("Xdoclint:none", true)
addStringOption("Xmaxwarns", "1")
}
}
}

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
<property
name="severity"
value="error"/>
<module name="TreeWalker">
<module name="SuppressWarningsHolder"/>
<module name="MissingDeprecated"/>
<module name="MissingOverride"/>
<module name="AnnotationLocation"/>
<module name="JavadocStyle"/>
<module name="NonEmptyAtclauseDescription"/>
<module name="IllegalImport"/>
<module name="RedundantImport"/>
<module name="RedundantModifier"/>
<module name="EmptyBlock"/>
<module name="DefaultComesLast"/>
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="ExplicitInitialization"/>
<module name="IllegalInstantiation"/>
<module name="ModifiedControlVariable"/>
<module name="MultipleVariableDeclarations"/>
<module name="PackageDeclaration"/>
<module name="ParameterAssignment"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="StringLiteralEquality"/>
<module name="OneStatementPerLine"/>
<module name="FinalClass"/>
<module name="ArrayTypeStyle"/>
<module name="UpperEll"/>
<module name="OuterTypeFilename"/>
</module>
<module name="FileTabCharacter"/>
<module name="SuppressWarningsFilter"/>
</module>

20
config/pmd/pmd.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0"?>
<ruleset name="Custom ruleset"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
<description>
Knecon ruleset checks the code for bad stuff
</description>
<rule ref="category/java/errorprone.xml">
<exclude name="MissingSerialVersionUID"/>
<exclude name="AvoidLiteralsInIfCondition"/>
<exclude name="AvoidDuplicateLiterals"/>
<exclude name="NullAssignment"/>
<exclude name="AssignmentInOperand"/>
<exclude name="BeanMembersShouldSerialize"/>
</rule>
</ruleset>

22
config/pmd/test_pmd.xml Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0"?>
<ruleset name="Custom ruleset"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
<description>
Knecon test ruleset checks the code for bad stuff
</description>
<rule ref="category/java/errorprone.xml">
<exclude name="MissingSerialVersionUID"/>
<exclude name="AvoidLiteralsInIfCondition"/>
<exclude name="AvoidDuplicateLiterals"/>
<exclude name="NullAssignment"/>
<exclude name="AssignmentInOperand"/>
<exclude name="TestClassWithoutTestCases"/>
<exclude name="BeanMembersShouldSerialize"/>
</rule>
</ruleset>

1
gradle.properties.kts Normal file
View File

@ -0,0 +1 @@
version = 2.0-SNAPSHOT

View File

@ -1,97 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.knecon.fforesight</groupId>
<artifactId>platform-docker-dependency</artifactId>
<version>0.1.0</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>persistence-service-image-v1</artifactId>
<groupId>com.iqser.red.service</groupId>
<version>2.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<service.server>persistence-service-server-v1</service.server>
<platform.jar>${service.server}.jar</platform.jar>
<docker.skip.push>false</docker.skip.push>
<docker.image.name>${docker.image.prefix}/${service.server}</docker.image.name>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>download-platform-jar</id>
<phase>prepare-package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>${service.server}</artifactId>
<version>${project.version}</version>
<type>jar</type>
<overWrite>true</overWrite>
<destFileName>${platform.jar}</destFileName>
</dependency>
</artifactItems>
<outputDirectory>${docker.build.directory}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<images>
<image>
<name>${docker.image.name}</name>
<build>
<dockerFileDir>${docker.build.directory}</dockerFileDir>
<args>
<PLATFORM_JAR>${platform.jar}</PLATFORM_JAR>
</args>
<tags>
<tag>${docker.image.version}</tag>
<tag>latest</tag>
</tags>
</build>
</image>
</images>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View File

@ -1,9 +0,0 @@
FROM red/base-image:2.0.2
ARG PLATFORM_JAR
ENV PLATFORM_JAR ${PLATFORM_JAR}
ENV USES_ELASTICSEARCH false
COPY ["${PLATFORM_JAR}", "/"]

View File

@ -0,0 +1,12 @@
plugins {
id("com.iqser.red.service.java-conventions")
id("io.spring.dependency-management") version "1.1.6"
id("org.sonarqube") version "4.4.1.3373"
id("io.freefair.lombok") version "8.6"
}
dependencies {
api(project(":persistence-service-processor-v1"))
}
description = "persistence-service-external-api-impl-v1"

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>persistence-service-v1</artifactId>
<groupId>com.iqser.red.service</groupId>
<version>2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>persistence-service-external-api-impl-v1</artifactId>
<properties>
<slf4j.version>1.7.30</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.iqser.red.service</groupId>
<artifactId>persistence-service-processor-v1</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -3,7 +3,6 @@ package com.iqser.red.persistence.service.v1.external.api.impl;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class PersistenceServiceExternalApiConfiguration {

View File

@ -3,18 +3,23 @@ package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_APP_CONFIG;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_APP_CONFIG;
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.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;
@ -24,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)),
ApplicationConfig.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;
}
@ -42,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

@ -36,11 +36,11 @@ public class AuditController implements AuditResource {
var auditModels = convert(auditPersistenceService.search(auditSearchRequest), AuditModel.class);
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(auditSearchRequest.getObjectId())
.category(AuditCategory.AUDIT.name())
.message("Audit Log has been viewed.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(auditSearchRequest.getObjectId())
.category(AuditCategory.AUDIT.name())
.message("Audit Log has been viewed.")
.build());
return new AuditResponse(auditModels.getElements(), auditModels.getTotalHits(), auditModels.getPage(), auditModels.getPageSize());
}

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

@ -1,4 +1,4 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -8,9 +8,9 @@ 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;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@ -23,24 +23,28 @@ import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
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;
@ -54,6 +58,8 @@ public class DictionaryController implements DictionaryResource {
private final DictionaryService dictionaryService;
private final AuditPersistenceService auditClient;
private final AccessControlService accessControlService;
private final DictionaryDifferenceService dictionaryDifferenceService;
@Override
@ -66,12 +72,12 @@ public class DictionaryController implements DictionaryResource {
addEntries(type, dossierTemplateId, entries, removeCurrent, dossierId, dictionaryEntryType);
auditClient.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary entries were added.")
.details(Map.of("Type", type, "Number", entries.size()))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary entries were added.")
.details(Map.of("Type", type, "Number", entries.size()))
.build());
}
@ -80,6 +86,8 @@ public class DictionaryController implements DictionaryResource {
if (dossierId == null) {
dictionaryService.addGlobalEntries(type, dossierTemplateId, entries, removeCurrent, dictionaryEntryType);
} else {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
dictionaryService.addDossierEntries(type, dossierTemplateId, entries, removeCurrent, dossierId, dictionaryEntryType);
}
}
@ -94,12 +102,37 @@ public class DictionaryController implements DictionaryResource {
deleteEntries(type, dossierTemplateId, Arrays.asList(entry), dossierId, dictionaryEntryType);
auditClient.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary entry was deleted.")
.details(Map.of("Type", type, "Value", entry))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary entry was deleted.")
.details(Map.of("Type", type, "Value", entry))
.build());
}
@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());
}
@ -113,16 +146,17 @@ public class DictionaryController implements DictionaryResource {
if (dossierId == null) {
dictionaryService.deleteGlobalEntries(type, dossierTemplateId, entries, dictionaryEntryType);
} else {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
dictionaryService.deleteDossierEntries(type, dossierTemplateId, entries, dossierId, dictionaryEntryType);
}
auditClient.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary entries were deleted.")
.details(Map.of("Type", type, "Number", entries.size()))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary entries were deleted.")
.details(Map.of("Type", type, "Number", entries.size()))
.build());
}
@ -134,12 +168,12 @@ public class DictionaryController implements DictionaryResource {
dictionaryService.updateGlobalType(type, dossierTemplateId, typeValue);
auditClient.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary type was updated.")
.details(Map.of("Type", type))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary type was updated.")
.details(Map.of("Type", type))
.build());
}
@ -149,36 +183,34 @@ public class DictionaryController implements DictionaryResource {
Type result = dictionaryService.addGlobalType(typeValue);
auditClient.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(typeValue.getDossierTemplateId())
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary type was added.")
.details(Map.of("Type", typeValue.getType()))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(typeValue.getDossierTemplateId())
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary type was added.")
.details(Map.of("Type", typeValue.getType()))
.build());
return MagicConverter.convert(result, TypeValue.class, new TypeValueMapper());
}
@Override
public void deleteType(@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {
public void deleteType(@PathVariable(TYPE_PARAMETER_NAME) String type, @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {
dictionaryService.deleteGlobalType(type, dossierTemplateId);
auditClient.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary type was deleted.")
.details(Map.of("Type", type))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary type was deleted.")
.details(Map.of("Type", type))
.build());
}
@Override
public void deleteTypes(@RequestBody List<String> types,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {
public void deleteTypes(@RequestBody List<String> types, @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {
List<String> errorIds = new ArrayList<>();
@ -187,12 +219,12 @@ public class DictionaryController implements DictionaryResource {
dictionaryService.deleteGlobalType(type, dossierTemplateId);
auditClient.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary type was deleted.")
.details(Map.of("Type", type))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary type was deleted.")
.details(Map.of("Type", type))
.build());
} catch (FeignException e) {
errorIds.add(type);
}
@ -230,19 +262,25 @@ public class DictionaryController implements DictionaryResource {
validateFile(file);
try {
addEntries(type, dossierTemplateId, new String(file.getBytes(), StandardCharsets.UTF_8).lines().collect(Collectors.toList()), true, dossierId, dictionaryEntryType);
addEntries(type,
dossierTemplateId,
new String(file.getBytes(), StandardCharsets.UTF_8).lines()
.collect(Collectors.toList()),
true,
dossierId,
dictionaryEntryType);
} catch (IOException e) {
log.debug(e.getMessage(), e);
throw new BadRequestException("Could not upload file.", e);
}
auditClient.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary has been uploaded.")
.details(Map.of("Type", type))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DICTIONARY.name())
.message("Dictionary has been uploaded.")
.details(Map.of("Type", type))
.build());
}
@ -291,26 +329,52 @@ public class DictionaryController implements DictionaryResource {
return dictionaryService.getDictionaryForType(type, dossierTemplateId, dossierId);
}
@Override
public Dictionary getMergedDictionaries(@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = DOSSIER_ID_PARAMETER_NAME) String dossierId) {
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = DOSSIER_ID_PARAMETER_NAME) String dossierId) {
return dictionaryService.getMergedDictionaryForType(type, dossierTemplateId, dossierId);
}
@Override
public void setColors(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestBody Colors colors) {
dictionaryService.setColors(dossierTemplateId, colors);
auditClient.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Colors have been changed.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Colors have been changed.")
.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

@ -3,14 +3,12 @@ package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_DIGITAL_SIGNATURE;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_DIGITAL_SIGNATURE;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.Base64Utils;
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.entity.configuration.DigitalSignatureEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.DigitalSignatureKmsEntity;
import com.iqser.red.service.persistence.management.v1.processor.service.DigitalSignatureKmsService;
@ -25,6 +23,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.DigitalSign
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.configuration.DigitalSignature;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.DigitalSignatureType;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import lombok.RequiredArgsConstructor;
@ -53,11 +52,11 @@ public class DigitalSignatureController implements DigitalSignatureResource {
digitalSignatureTypeService.setActiveDigitalSignatureType(digitalSignatureType);
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(DIGITAL_SIGNATURE_AUDIT_ID)
.category(AuditCategory.SETTINGS.name())
.message("Digital signature type has been updated.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(DIGITAL_SIGNATURE_AUDIT_ID)
.category(AuditCategory.SETTINGS.name())
.message("Digital signature type has been updated.")
.build());
}
@ -67,11 +66,11 @@ public class DigitalSignatureController implements DigitalSignatureResource {
DigitalSignatureViewModel digitalSignatureViewModel = convertToView(digitalSignatureService.saveDigitalSignature(convert(digitalSignatureModel)));
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(DIGITAL_SIGNATURE_AUDIT_ID)
.category(AuditCategory.SETTINGS.name())
.message("Digital signature has been saved.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(DIGITAL_SIGNATURE_AUDIT_ID)
.category(AuditCategory.SETTINGS.name())
.message("Digital signature has been saved.")
.build());
return digitalSignatureViewModel;
}
@ -83,11 +82,11 @@ public class DigitalSignatureController implements DigitalSignatureResource {
digitalSignatureService.updateDigitalSignature(convert(digitalSignatureModel));
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(DIGITAL_SIGNATURE_AUDIT_ID)
.category(AuditCategory.SETTINGS.name())
.message("Digital signature has been updated.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(DIGITAL_SIGNATURE_AUDIT_ID)
.category(AuditCategory.SETTINGS.name())
.message("Digital signature has been updated.")
.build());
}
@ -105,11 +104,11 @@ public class DigitalSignatureController implements DigitalSignatureResource {
digitalSignatureService.deleteDigitalSignature();
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(DIGITAL_SIGNATURE_AUDIT_ID)
.category(AuditCategory.SETTINGS.name())
.message("Digital signature has been deleted.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(DIGITAL_SIGNATURE_AUDIT_ID)
.category(AuditCategory.SETTINGS.name())
.message("Digital signature has been deleted.")
.build());
}
@ -119,11 +118,11 @@ public class DigitalSignatureController implements DigitalSignatureResource {
DigitalSignatureKmsViewModel result = convert(digitalSignatureKmsService.saveDigitalSignature(digitalSignature));
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(DIGITAL_SIGNATURE_AUDIT_ID)
.category(AuditCategory.SETTINGS.name())
.message("Digital KMS signature has been saved.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(DIGITAL_SIGNATURE_AUDIT_ID)
.category(AuditCategory.SETTINGS.name())
.message("Digital KMS signature has been saved.")
.build());
return result;
}
@ -142,11 +141,11 @@ public class DigitalSignatureController implements DigitalSignatureResource {
digitalSignatureKmsService.deleteDigitalSignature();
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(DIGITAL_SIGNATURE_AUDIT_ID)
.category(AuditCategory.SETTINGS.name())
.message("Digital KMS signature has been deleted.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(DIGITAL_SIGNATURE_AUDIT_ID)
.category(AuditCategory.SETTINGS.name())
.message("Digital KMS signature has been deleted.")
.build());
}
@ -192,7 +191,7 @@ public class DigitalSignatureController implements DigitalSignatureResource {
.location(digitalSignature.getLocation())
.password(digitalSignature.getPassword())
.reason(digitalSignature.getReason())
.privateKey(Base64Utils.decodeFromString(digitalSignature.getPrivateKey()))
.privateKey(Base64.getDecoder().decode(digitalSignature.getBase64EncodedPrivateKey()))
.build();
}

View File

@ -5,7 +5,6 @@ import static com.iqser.red.service.persistence.management.v1.processor.service.
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@ -15,19 +14,14 @@ 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.AccessControlService;
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.external.resource.DocumentResource;
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;
@ -37,75 +31,21 @@ import lombok.SneakyThrows;
@RestController
@RequiredArgsConstructor
public class RedactionLogController implements RedactionLogResource {
public class DocumentController implements DocumentResource {
private final RedactionLogService redactionLogService;
private final FileStatusService fileStatusService;
private final FileManagementStorageService fileManagementStorageService;
@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, withManualRedactions, includeFalsePositives);
} catch (FeignException e) {
throw processFeignException(e);
}
}
@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);
}
}
@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);
}
private final AccessControlService accessControlService;
@SneakyThrows
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public ResponseEntity<?> getDocumentText(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
// check access to resources and check for deletion
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
try {
return buildZipFileResponseEntity(fileId, dossierId, FileType.DOCUMENT_TEXT);
} catch (FeignException e) {
@ -118,6 +58,10 @@ public class RedactionLogController implements RedactionLogResource {
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public ResponseEntity<?> getDocumentPositions(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
// check access to resources and check for deletion
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
try {
return buildZipFileResponseEntity(fileId, dossierId, FileType.DOCUMENT_POSITION);
} catch (FeignException e) {
@ -130,6 +74,9 @@ public class RedactionLogController implements RedactionLogResource {
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public ResponseEntity<?> getDocumentStructure(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
// check access to resources and check for deletion
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
try {
return buildZipFileResponseEntity(fileId, dossierId, FileType.DOCUMENT_STRUCTURE);
} catch (FeignException e) {
@ -142,6 +89,9 @@ public class RedactionLogController implements RedactionLogResource {
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public ResponseEntity<?> getDocumentPages(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
// check access to resources and check for deletion
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
try {
return buildZipFileResponseEntity(fileId, dossierId, FileType.DOCUMENT_PAGES);
} catch (FeignException e) {
@ -154,6 +104,10 @@ public class RedactionLogController implements RedactionLogResource {
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public ResponseEntity<?> getSimplifiedSectionText(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
// check access to resources and check for deletion
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
try {
HttpHeaders httpHeaders = new HttpHeaders();
@ -179,19 +133,23 @@ public class RedactionLogController implements RedactionLogResource {
}
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public RedactionLog getFilteredRedactionLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody FilteredRedactionLogRequest filteredRedactionLogRequest) {
private ResponseEntity<byte[]> buildZipFileResponseEntity(String fileId, String dossierId, FileType fileType) throws IOException {
try {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.parseMediaType("application/zip"));
return redactionLogService.getFilteredRedactionLog(dossierId, fileId, filteredRedactionLogRequest);
} catch (FeignException e) {
throw processFeignException(e);
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);
}

View File

@ -6,6 +6,7 @@ import static com.iqser.red.service.persistence.management.v1.processor.roles.Ac
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_DOSSIER_ATTRIBUTES_CONFIG;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_FILE_ATTRIBUTES;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@ -15,6 +16,8 @@ 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.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;
@ -36,6 +39,7 @@ import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class DossierAttributesController implements DossierAttributesResource {
private final DossierPersistenceService dossierPersistenceService;
private final DossierAttributeConfigPersistenceService dossierAttributeConfigPersistenceService;
private final AuditPersistenceService auditPersistenceService;
private final DossierAttributesManagementService dossierAttributesManagementService;
@ -47,13 +51,15 @@ public class DossierAttributesController implements DossierAttributesResource {
public DossierAttributesConfig setDossierAttributesConfig(String dossierTemplateId, DossierAttributesConfig dossierAttributesConfig) {
var result = MagicConverter.convert(dossierAttributeConfigPersistenceService.setDossierAttributesConfig(dossierTemplateId,
MagicConverter.convert(dossierAttributesConfig.getDossierAttributeConfigs(), DossierAttributeConfigEntity.class)), DossierAttributeConfig.class);
MagicConverter.convert(dossierAttributesConfig.getDossierAttributeConfigs(),
DossierAttributeConfigEntity.class)),
DossierAttributeConfig.class, new DossierAttributeConfigMapper());
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Changed dossier attributes base configuration.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Changed dossier attributes base configuration.")
.build());
return new DossierAttributesConfig(result);
}
@ -65,13 +71,15 @@ public class DossierAttributesController implements DossierAttributesResource {
@RequestBody DossierAttributeConfig dossierAttribute) {
var result = MagicConverter.convert(dossierAttributeConfigPersistenceService.addOrUpdateDossierAttribute(dossierTemplateId,
MagicConverter.convert(dossierAttribute, DossierAttributeConfigEntity.class)), DossierAttributeConfig.class);
MagicConverter.convert(dossierAttribute,
DossierAttributeConfigEntity.class)),
DossierAttributeConfig.class, new DossierAttributeConfigMapper());
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier attributes added/updated")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier attributes added/updated")
.build());
return result;
}
@ -83,12 +91,12 @@ public class DossierAttributesController implements DossierAttributesResource {
dossierAttributeConfigPersistenceService.deleteDossierAttribute(dossierAttributeId);
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier attributes removed")
.details(Map.of("DossierAttributeId", dossierAttributeId))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier attributes removed")
.details(Map.of("DossierAttributeId", dossierAttributeId))
.build());
}
@ -98,12 +106,12 @@ public class DossierAttributesController implements DossierAttributesResource {
dossierAttributeConfigPersistenceService.deleteDossierAttributes(dossierAttributeIds);
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier attributes removed")
.details(Map.of("DossierAttributeId", dossierAttributeIds))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier attributes removed")
.details(Map.of("DossierAttributeId", dossierAttributeIds))
.build());
}
@ -111,21 +119,22 @@ 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()));
}
@PreAuthorize("hasAuthority('" + WRITE_FILE_ATTRIBUTES + "')")
public DossierAttributes setDossierAttributes(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody DossierAttributes dossierAttributes) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserIsDossierOwner(dossierId);
var result = dossierAttributesManagementService.setDossierAttributes(dossierId, dossierAttributes.getDossierAttributeList());
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Changed dossier attributes.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Changed dossier attributes.")
.build());
return new DossierAttributes(result);
}
@ -134,14 +143,15 @@ public class DossierAttributesController implements DossierAttributesResource {
@PreAuthorize("hasAuthority('" + WRITE_DOSSIER_ATTRIBUTES + "')")
public DossierAttributes addOrUpdateDossierAttribute(String dossierId, DossierAttribute dossierAttribute) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserIsDossierOwner(dossierId);
DossierAttribute result = dossierAttributesManagementService.addOrUpdateDossierAttribute(dossierId, dossierAttribute);
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Added or updated dossier attributes.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Added or updated dossier attributes.")
.build());
return new DossierAttributes(List.of(result)); // TODO should be single Object???
}
@ -149,13 +159,19 @@ public class DossierAttributesController implements DossierAttributesResource {
@PreAuthorize("hasAuthority('" + READ_DOSSIER_ATTRIBUTES + "')")
public DossierAttributes getDossierAttributes(String dossierId) {
var result = dossierAttributesManagementService.getDossierAttributes(dossierId);
//check if dossier exists before verifying permissions
dossierPersistenceService.findByDossierId(dossierId);
List<DossierAttribute> result = Collections.emptyList();
if (accessControlService.hasUserViewPermissionsForDossier(dossierId)) {
result = dossierAttributesManagementService.getDossierAttributes(dossierId);
}
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Got dossier attributes.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Got dossier attributes.")
.build());
return new DossierAttributes(result);
}
@ -164,14 +180,15 @@ public class DossierAttributesController implements DossierAttributesResource {
@PreAuthorize("hasAuthority('" + WRITE_DOSSIER_ATTRIBUTES + "')")
public void deleteDossierAttribute(String dossierId, String dossierAttributeId) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserIsDossierOwner(dossierId);
dossierAttributesManagementService.deleteDossierAttribute(dossierId, dossierAttributeId);
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Changed dossier attributes.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Changed dossier attributes.")
.build());
}

View File

@ -10,20 +10,30 @@ 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;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
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;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;
@ -33,17 +43,18 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.google.common.collect.Lists;
import com.iqser.red.service.persistence.management.v1.processor.service.users.model.User;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.iqser.red.service.persistence.management.v1.processor.roles.ApplicationRoles;
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.roles.ApplicationRoles;
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.users.UserService;
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;
import com.iqser.red.service.persistence.service.v1.api.external.resource.DossierResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierChangeEntry;
@ -54,7 +65,9 @@ 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;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -68,11 +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
@ -95,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) {
@ -109,8 +140,8 @@ public class DossierController implements DossierResource {
Set<String> approvers = getAndValidateMembers(ownerId, dossierRequest.getApproverIds());
members.addAll(approvers);
if ((dossierRequest.getDownloadFileTypes() == null || dossierRequest.getDownloadFileTypes()
.isEmpty()) && (dossierRequest.getReportTemplateIds() == null || dossierRequest.getReportTemplateIds().isEmpty())) {
if ((dossierRequest.getDownloadFileTypes() == null || dossierRequest.getDownloadFileTypes().isEmpty()) && (dossierRequest.getReportTemplateIds() == null
|| dossierRequest.getReportTemplateIds().isEmpty())) {
throw new BadRequestException("Download and report types cannot both be empty");
}
@ -125,16 +156,16 @@ public class DossierController implements DossierResource {
// update using data from request and computed owner/members/approvers
Dossier updatedDossier = dossierManagementService.updateDossier(CreateOrUpdateDossierRequest.builder()
.dossierName(dossierRequest.getDossierName())
.description(dossierRequest.getDescription())
.dossierTemplateId(dossierRequest.getDossierTemplateId())
.downloadFileTypes(dossierRequest.getDownloadFileTypes())
.dueDate(dossierRequest.getDueDate())
.reportTemplateIds(new ArrayList<>(dossierRequest.getReportTemplateIds()))
.watermarkId(dossierRequest.getWatermarkId())
.previewWatermarkId(dossierRequest.getPreviewWatermarkId())
.dossierStatusId(dossierRequest.getDossierStatusId())
.build(), existingDossier.getId());
.dossierName(dossierRequest.getDossierName())
.description(dossierRequest.getDescription())
.dossierTemplateId(dossierRequest.getDossierTemplateId())
.downloadFileTypes(dossierRequest.getDownloadFileTypes())
.dueDate(dossierRequest.getDueDate())
.reportTemplateIds(new ArrayList<>(dossierRequest.getReportTemplateIds()))
.watermarkId(dossierRequest.getWatermarkId())
.previewWatermarkId(dossierRequest.getPreviewWatermarkId())
.dossierStatusId(dossierRequest.getDossierStatusId())
.build(), existingDossier.getId());
dossierACLService.updateDossierACL(members, approvers, ownerId, updatedDossier.getId());
dossierACLService.enhanceDossierWithACLData(updatedDossier);
@ -142,72 +173,81 @@ public class DossierController implements DossierResource {
updateFileStatusForDossierFiles(updatedDossier.getId(), members);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(updatedDossier.getId())
.category(AuditCategory.DOSSIER.name())
.message("Dossier has been updated.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(updatedDossier.getId())
.category(AuditCategory.DOSSIER.name())
.message("Dossier has been updated.")
.build());
if (existingDossier.getOwnerId() == null || !existingDossier.getOwnerId().equals(ownerId)) {
if (ownerId != null && !ownerId.equals(KeycloakSecurity.getUserId())) {
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(ownerId)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.DOSSIER_OWNER_SET.name())
.target(Map.of("dossierId", dossierRequest.getDossierId()))
.build());
.userId(ownerId)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.DOSSIER_OWNER_SET.name())
.target(Map.of("dossierId", dossierRequest.getDossierId()))
.build());
}
if (existingDossier.getOwnerId() != null && !existingDossier.getOwnerId().equals(KeycloakSecurity.getUserId())) {
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(existingDossier.getOwnerId())
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.DOSSIER_OWNER_REMOVED.name())
.target(Map.of("dossierId", dossierRequest.getDossierId()))
.build());
.userId(existingDossier.getOwnerId())
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.DOSSIER_OWNER_REMOVED.name())
.target(Map.of("dossierId", dossierRequest.getDossierId()))
.build());
}
}
Stream.concat(members.stream(), approvers.stream())
.filter(member -> !member.equals(ownerId) && !member.equals(KeycloakSecurity.getUserId()) && (existingDossier.getMemberIds() == null || !existingDossier.getMemberIds()
.contains(member)))
Set<String> uniqueMembers = new LinkedHashSet<>(members);
uniqueMembers.addAll(approvers);
uniqueMembers.stream()
.filter(member -> !member.equals(ownerId) && !member.equals(KeycloakSecurity.getUserId()) && (existingDossier.getMemberIds() == null
|| !existingDossier.getMemberIds().contains(member)))
.forEach(member -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(member)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.USER_BECOMES_DOSSIER_MEMBER.name())
.target(Map.of("dossierId", dossierRequest.getDossierId()))
.build()));
.userId(member)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.USER_BECOMES_DOSSIER_MEMBER.name())
.target(Map.of("dossierId", dossierRequest.getDossierId()))
.build()));
if (existingDossier.getMemberIds() != null) {
existingDossier.getMemberIds()
.stream()
.filter(member -> !members.contains(member) && !approvers.contains(member) && !member.equals(KeycloakSecurity.getUserId()))
.forEach(member -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(member)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.USER_REMOVED_AS_DOSSIER_MEMBER.name())
.target(Map.of("dossierId", dossierRequest.getDossierId()))
.build()));
.userId(member)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.USER_REMOVED_AS_DOSSIER_MEMBER.name())
.target(Map.of("dossierId", dossierRequest.getDossierId()))
.build()));
}
approvers.stream()
.filter(approver -> !KeycloakSecurity.getUserId().equals(approver) && existingDossier.getMemberIds() != null && existingDossier.getMemberIds()
.contains(approver) && (existingDossier.getApproverIds() == null || !existingDossier.getApproverIds().contains(approver)))
.forEach(approver -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(approver)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.USER_PROMOTED_TO_APPROVER.name())
.target(Map.of("dossierId", dossierRequest.getDossierId()))
.build()));
.userId(approver)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.USER_PROMOTED_TO_APPROVER.name())
.target(Map.of("dossierId", dossierRequest.getDossierId()))
.build()));
members.stream()
.filter(member -> !member.equals(KeycloakSecurity.getUserId()) && existingDossier.getApproverIds() != null && existingDossier.getApproverIds()
.contains(member) && !approvers.contains(member))
.filter(member -> !member.equals(KeycloakSecurity.getUserId())
&& existingDossier.getApproverIds() != null
&& existingDossier.getApproverIds().contains(member)
&& !approvers.contains(member))
.forEach(member -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(member)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.USER_DEGRADED_TO_REVIEWER.name())
.target(Map.of("dossierId", dossierRequest.getDossierId()))
.build()));
.userId(member)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.USER_DEGRADED_TO_REVIEWER.name())
.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);
@ -217,12 +257,13 @@ public class DossierController implements DossierResource {
Dossier created = createNewDossier(dossierRequest, ownerId, members, approvers);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(created.getId())
.category(AuditCategory.DOSSIER.name())
.message("Dossier has been created.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(created.getId())
.category(AuditCategory.DOSSIER.name())
.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);
@ -248,7 +289,9 @@ public class DossierController implements DossierResource {
}
// check he has a manager role, thus he can be the owner
if (user.isPresent() && user.get().getRoles().stream().noneMatch(ApplicationRoles.RED_MANAGER_ROLE::equals)) {
if (user.isPresent() && user.get().getRoles()
.stream()
.noneMatch(ApplicationRoles.RED_MANAGER_ROLE::equals)) {
throw new BadRequestException("Make sure provided user id has the manager role.");
}
@ -260,7 +303,8 @@ public class DossierController implements DossierResource {
private Set<String> getAndValidateMembers(String ownerId, Set<String> memberIds) {
Set<String> actualMemberIds = memberIds == null ? new TreeSet<>() : memberIds;
if (actualMemberIds.stream().anyMatch(Objects::isNull)) {
if (actualMemberIds.stream()
.anyMatch(Objects::isNull)) {
throw new BadRequestException("Member IDs cannot be null");
}
@ -274,7 +318,10 @@ public class DossierController implements DossierResource {
Set<String> deletedUserIds = userService.removeDeletedUsers(actualMemberIds);
actualMemberIds.removeAll(deletedUserIds);
}
if (users.stream().anyMatch(u -> u.getRoles().stream().noneMatch(VALID_MEMBER_ROLES::contains))) {
if (users.stream()
.anyMatch(u -> u.getRoles()
.stream()
.noneMatch(VALID_MEMBER_ROLES::contains))) {
throw new BadRequestException("Make sure each provided member id has the permission to be a member of a dossier.");
}
return actualMemberIds;
@ -293,32 +340,36 @@ public class DossierController implements DossierResource {
private void updateFileStatusForDossierFiles(String dossierId, Collection<String> members) {
fileStatusManagementService.getDossierStatus(dossierId).stream().filter(fileStatus -> !fileStatus.isSoftOrHardDeleted()).forEach(f -> {
fileStatusManagementService.getDossierStatus(dossierId)
.stream()
.filter(fileStatus -> !fileStatus.isSoftOrHardDeleted())
.forEach(f -> {
if (f.getAssignee() != null && !members.contains(f.getAssignee())) {
fileStatusManagementService.setCurrentFileAssignee(dossierId, f.getId(), null);
}
});
if (f.getAssignee() != null && !members.contains(f.getAssignee())) {
fileStatusManagementService.setCurrentFileAssignee(dossierId, f.getId(), null);
}
});
}
private Dossier createNewDossier(DossierRequest dossier, String ownerId, Set<String> members, Set<String> approvers) {
Dossier newDossier = dossierManagementService.addDossier(CreateOrUpdateDossierRequest.builder()
.dossierName(dossier.getDossierName())
.description(dossier.getDescription())
.dossierTemplateId(dossier.getDossierTemplateId())
.downloadFileTypes(dossier.getDownloadFileTypes())
.dueDate(dossier.getDueDate())
.reportTemplateIds(dossier.getReportTemplateIds() != null ? new ArrayList<>(dossier.getReportTemplateIds()) : Lists.newArrayList())
.watermarkId(dossier.getWatermarkId())
.previewWatermarkId(dossier.getPreviewWatermarkId())
.dossierStatusId(dossier.getDossierStatusId())
.build());
Dossier newDossier = dossierCreatorService.addDossier(CreateOrUpdateDossierRequest.builder()
.dossierName(dossier.getDossierName().trim())
.description(dossier.getDescription())
.dossierTemplateId(dossier.getDossierTemplateId())
.downloadFileTypes(dossier.getDownloadFileTypes())
.dueDate(dossier.getDueDate())
.reportTemplateIds(dossier.getReportTemplateIds()
!= null ? new ArrayList<>(dossier.getReportTemplateIds()) : Lists.newArrayList())
.watermarkId(dossier.getWatermarkId())
.previewWatermarkId(dossier.getPreviewWatermarkId())
.dossierStatusId(dossier.getDossierStatusId())
.build(), members, approvers, ownerId);
dossierACLService.updateDossierACL(members, approvers, ownerId, newDossier.getId());
dossierACLService.enhanceDossierWithACLData(newDossier);
newDossier.setDossierAttributes(new DossierAttributes());
return newDossier;
@ -340,92 +391,114 @@ public class DossierController implements DossierResource {
}
@PreAuthorize("hasAuthority('" + DELETE_DOSSIER + "') && hasPermission(#dossierId, 'Dossier', 'ACCESS_OBJECT')")
@PreAuthorize("hasAuthority('" + DELETE_DOSSIER + "')")
public void deleteDossier(@PathVariable(DOSSIER_ID_PARAM) String dossierId) {
var dossierToBeDeleted = dossierACLService.enhanceDossierWithACLData(dossierManagementService.getDossierById(dossierId, true, false));
Dossier dossier = dossierACLService.enhanceDossierWithACLData(dossierManagementService.getDossierById(dossierId, true, false));
accessControlService.checkAccessPermissionsToDossier(dossierId);
dossierManagementService.delete(dossierId);
if (dossier.getOwnerId() != null && !dossier.getOwnerId().equals(KeycloakSecurity.getUserId())) {
throw new AccessDeniedException("Can not delete dossier that is owned by a different user");
}
dossierManagementService.softDeleteDossier(dossierId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Dossier moved to trash.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Dossier moved to trash.")
.build());
dossierToBeDeleted.getMemberIds()
dossier.getMemberIds()
.stream()
.filter(m -> !KeycloakSecurity.getUserId().equals(m))
.forEach(member -> notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(member)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.DOSSIER_DELETED.name())
.target(Map.of("dossierId", dossierId, "dossierName", dossierToBeDeleted.getDossierName()))
.build()));
.userId(member)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.DOSSIER_DELETED.name())
.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);
}
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
@PostAuthorize("hasPermission(#dossierId, 'Dossier', 'VIEW_OBJECT')")
public Dossier getDossier(@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived,
@RequestParam(name = INCLUDE_DELETED_PARAM, defaultValue = "false", required = false) boolean includeDeleted) {
return dossierACLService.enhanceDossierWithACLData(dossierManagementService.getDossierById(dossierId, includeArchived, includeDeleted));
accessControlService.checkViewPermissionsToDossier(dossierId);
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);
}
@ -440,27 +513,30 @@ public class DossierController implements DossierResource {
for (String dossierId : dossierIds) {
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Dossier archived.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Dossier archived.")
.build());
websocketService.sendDossierEvent(dossierId, DossierEventType.ARCHIVE);
}
}
@PreAuthorize("hasAuthority('" + UNARCHIVE_DOSSIER + "')")
@PreFilter("hasPermission(filterObject, 'Dossier', 'ACCESS_OBJECT')")
public void unarchiveDossiers(@RequestBody Set<String> dossierIds) {
dossierManagementService.unarchiveDossiers(dossierIds);
for (String dossierId : dossierIds) {
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Dossier restored from archive.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Dossier restored from archive.")
.build());
websocketService.sendDossierEvent(dossierId, DossierEventType.UNARCHIVE);
}
}
@ -469,20 +545,19 @@ public class DossierController implements DossierResource {
@PreFilter("hasPermission(filterObject, 'Dossier', 'ACCESS_OBJECT')")
public void hardDeleteDossiers(@RequestParam(DOSSIER_ID_PARAM) Set<String> dossierIds) {
for (String dossierId : dossierIds) {
accessControlService.verifyUserIsDossierOwner(dossierId);
}
dossierManagementService.hardDeleteDossiers(dossierIds);
var filteredDossierIds = filterDossierIdsByOwnedKeepUnowned(dossierIds);
for (String dossierId : dossierIds) {
dossierManagementService.hardDeleteDossiers(filteredDossierIds);
for (String dossierId : filteredDossierIds) {
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Dossier permanently deleted.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Dossier permanently deleted.")
.build());
websocketService.sendDossierEvent(dossierId, DossierEventType.HARD_DELETE);
}
}
@ -491,17 +566,68 @@ public class DossierController implements DossierResource {
@PreFilter("hasPermission(filterObject, 'Dossier', 'ACCESS_OBJECT')")
public void undeleteDossiers(@RequestBody Set<String> dossierIds) {
dossierManagementService.undeleteDossiers(dossierIds);
for (String dossierId : dossierIds) {
var filteredDossierIds = filterDossierIdsByOwnedKeepUnowned(dossierIds);
dossierManagementService.undeleteDossiers(filteredDossierIds);
for (String dossierId : filteredDossierIds) {
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Dossier restored from trash.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Dossier restored from trash.")
.build());
websocketService.sendDossierEvent(dossierId, DossierEventType.UNDELETE);
}
}
private Set<String> filterDossierIdsByOwnedKeepUnowned(Set<String> dossierIds) {
return dossierIds.stream()
.map(id -> dossierManagementService.getDossierById(id, true, true))
.map(dossierACLService::enhanceDossierWithACLData)
.filter(dossier -> dossier.getOwnerId() == null || dossier.getOwnerId().equals(KeycloakSecurity.getUserId()))
.map(Dossier::getId)
.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

@ -12,6 +12,7 @@ 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.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierStatsService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.DossierStatsResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierStats;
@ -26,12 +27,14 @@ import lombok.extern.slf4j.Slf4j;
public class DossierStatsController implements DossierStatsResource {
private final DossierStatsService dossierStatsService;
private final AccessControlService accessControlService;
@Override
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "') && hasPermission(#dossierId, 'Dossier', 'VIEW_OBJECT')")
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
public DossierStats getDossierStats(@PathVariable(DOSSIER_ID_PARAM) String dossierId) {
accessControlService.checkViewPermissionsToDossier(dossierId);
return dossierStatsService.getDossierStats(dossierId);
}
@ -41,7 +44,9 @@ public class DossierStatsController implements DossierStatsResource {
@PreFilter("hasPermission(filterObject, 'Dossier', 'VIEW_OBJECT')")
public List<DossierStats> getDossierStats(@RequestBody Set<String> dossierIds) {
return dossierIds.stream().map(dossierStatsService::getDossierStats).collect(Collectors.toList());
return dossierIds.stream()
.map(dossierStatsService::getDossierStats)
.collect(Collectors.toList());
}
}

View File

@ -4,7 +4,6 @@ import static com.iqser.red.service.persistence.management.v1.processor.roles.Ac
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_DOSSIER_STATUS;
import java.util.List;
import java.util.stream.Collectors;
import jakarta.transaction.Transactional;
@ -15,14 +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.management.v1.processor.utils.DossierStatusMapper;
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;
@ -33,6 +35,7 @@ import lombok.extern.slf4j.Slf4j;
public class DossierStatusController implements DossierStatusResource {
private final DossierStatusPersistenceService dossierStatusPersistenceService;
private final AuditPersistenceService auditPersistenceService;
@Override
@ -52,13 +55,22 @@ public class DossierStatusController implements DossierStatusResource {
throw new BadRequestException("The rank must not be negative");
}
var response = dossierStatusPersistenceService.createOrUpdateDossierStatus(CreateOrUpdateDossierStatusRequest.builder()
.dossierStatusId(dossierStatusRequest.getDossierStatusId())
.name(dossierStatusRequest.getName())
.description(dossierStatusRequest.getDescription())
.dossierTemplateId(dossierStatusRequest.getDossierTemplateId())
.rank(dossierStatusRequest.getRank())
.color(dossierStatusRequest.getColor())
.build());
.dossierStatusId(dossierStatusRequest.getDossierStatusId())
.name(dossierStatusRequest.getName())
.description(dossierStatusRequest.getDescription())
.dossierTemplateId(dossierStatusRequest.getDossierTemplateId())
.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);
}
@ -67,10 +79,7 @@ public class DossierStatusController implements DossierStatusResource {
@PreAuthorize("hasAuthority('" + READ_DOSSIER_STATUS + "')")
public List<DossierStatusInfo> getAllDossierStatusForTemplate(@PathVariable("dossierTemplateId") String dossierTemplateId) {
return dossierStatusPersistenceService.getAllDossierStatusForTemplate(dossierTemplateId)
.stream()
.map(d -> MagicConverter.convert(d, DossierStatusInfo.class))
.collect(Collectors.toList());
return dossierStatusPersistenceService.getAllDossierStatusForTemplate(dossierTemplateId);
}
@ -78,10 +87,7 @@ public class DossierStatusController implements DossierStatusResource {
@PreAuthorize("hasAuthority('" + READ_DOSSIER_STATUS + "')")
public List<DossierStatusInfo> getAllDossierStatuses(@RequestBody List<String> dossierTemplateIds) {
return dossierStatusPersistenceService.getAllDossierStatuses(dossierTemplateIds)
.stream()
.map(d -> MagicConverter.convert(d, DossierStatusInfo.class))
.collect(Collectors.toList());
return dossierStatusPersistenceService.getAllDossierStatuses(dossierTemplateIds);
}
@ -90,7 +96,7 @@ public class DossierStatusController implements DossierStatusResource {
@PreAuthorize("hasAuthority('" + READ_DOSSIER_STATUS + "')")
public DossierStatusInfo getDossierStatus(@PathVariable("dossierStatusId") String dossierStatusId) {
return MagicConverter.convert(dossierStatusPersistenceService.getDossierStatus(dossierStatusId), DossierStatusInfo.class, new DossierStatusMapper());
return dossierStatusPersistenceService.getDossierStatusInfo(dossierStatusId);
}
@ -99,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

@ -8,6 +8,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@ -41,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;
@ -76,11 +77,11 @@ public class DossierTemplateController implements DossierTemplateResource {
DossierTemplateModel response = convert(dossierTemplateManagementService.createOrUpdateDossierTemplate(dossierTemplate));
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(response.getDossierTemplateId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier Template has been added or updated")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(response.getDossierTemplateId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier Template has been added or updated")
.build());
return response;
} catch (FeignException e) {
throw processFeignException(e);
@ -93,7 +94,10 @@ public class DossierTemplateController implements DossierTemplateResource {
@PreAuthorize("hasAuthority('" + READ_DOSSIER_TEMPLATES + "')")
public List<DossierTemplateModel> getAllDossierTemplates() {
return dossierTemplateManagementService.getAllDossierTemplates().stream().map(this::convert).collect(Collectors.toList());
return dossierTemplateManagementService.getAllDossierTemplates()
.stream()
.map(this::convert)
.collect(Collectors.toList());
}
@ -116,22 +120,23 @@ public class DossierTemplateController implements DossierTemplateResource {
String userId = KeycloakSecurity.getUserId();
List<Dossier> dossiers = dossierManagementService.getAllDossiers(true, false);
if (dossiers != null && dossiers.stream().anyMatch(dossier -> dossier.getDossierTemplateId().equals(dossierTemplateId))) {
if (dossiers != null && dossiers.stream()
.anyMatch(dossier -> dossier.getDossierTemplateId().equals(dossierTemplateId))) {
throw new ConflictException("Can not delete dossier template because there are dossiers based on it");
}
dossierTemplateManagementService.deleteDossierTemplate(dossierTemplateId, userId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier Template has been deleted")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier Template has been deleted")
.build());
}
@Override
@PreAuthorize("hasAuthority('" + READ_DOSSIER_TEMPLATES + "')")
@PreAuthorize("hasAuthority('" + WRITE_DOSSIER_TEMPLATES + "')")
public void deleteDossierTemplates(@RequestBody List<String> dossierTemplateIds) {
String userId = KeycloakSecurity.getUserId();
@ -140,17 +145,18 @@ public class DossierTemplateController implements DossierTemplateResource {
for (String dossierTemplateId : dossierTemplateIds) {
try {
List<Dossier> dossiers = dossierManagementService.getAllDossiers(true, false);
if (dossiers != null && dossiers.stream().anyMatch(dossier -> dossier.getDossierTemplateId().equals(dossierTemplateId))) {
if (dossiers != null && dossiers.stream()
.anyMatch(dossier -> dossier.getDossierTemplateId().equals(dossierTemplateId))) {
throw new ConflictException("Can not delete dossier template because there are dossiers based on it");
}
dossierTemplateManagementService.deleteDossierTemplate(dossierTemplateId, userId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier template has been deleted")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier template has been deleted")
.build());
} catch (FeignException e) {
errorIds.add(dossierTemplateId);
}
@ -173,11 +179,11 @@ public class DossierTemplateController implements DossierTemplateResource {
DossierTemplateModel response = convert(dossierTemplateManagementService.cloneDossierTemplate(dossierTemplateId, cloneDossierTemplateRequest));
auditPersistenceService.audit(AuditRequest.builder()
.userId(userId)
.objectId(response.getDossierTemplateId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier Template has been cloned")
.build());
.userId(userId)
.objectId(response.getDossierTemplateId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier Template has been cloned")
.build());
return response;
} catch (FeignException e) {
throw processFeignException(e);
@ -220,12 +226,12 @@ public class DossierTemplateController implements DossierTemplateResource {
ExportDownloadRequest request = ExportDownloadRequest.builder().dossierTemplateId(dossierTemplateId).userId(KeycloakSecurity.getUserId()).build();
var response = dossierTemplateManagementService.prepareExportDownload(request);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(response.getValue())
.category(AuditCategory.DOWNLOAD.name())
.message("Export Download was prepared")
.details(Map.of("dossierTemplateId", request.getDossierTemplateId()))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(response.getValue())
.category(AuditCategory.DOWNLOAD.name())
.message("Export Download was prepared")
.details(Map.of("dossierTemplateId", request.getDossierTemplateId()))
.build());
return new DownloadResponse(response.getValue());
} catch (FeignException e) {
throw processFeignException(e);
@ -243,7 +249,7 @@ public class DossierTemplateController implements DossierTemplateResource {
if (originalFileName == null || originalFileName.isEmpty()) {
throw new BadRequestException("Could not upload file, no filename provided.");
}
var extension = originalFileName.substring(originalFileName.lastIndexOf(".") + 1).toLowerCase();
var extension = originalFileName.substring(originalFileName.lastIndexOf(".") + 1).toLowerCase(Locale.ROOT);
if ("zip".equalsIgnoreCase(extension)) {
if (StringUtils.isEmpty(dossierTemplateId) && updateExistingDossierTemplate) {
throw new BadRequestException("Could not update with dossier template empty");
@ -261,12 +267,12 @@ public class DossierTemplateController implements DossierTemplateResource {
.build();
DossierTemplate loadedDossierTemplate = dossierTemplateManagementService.importDossierTemplate(request);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(loadedDossierTemplate.getId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier template was imported")
.details(Map.of("dossierTemplateId", loadedDossierTemplate.getId()))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(loadedDossierTemplate.getId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Dossier template was imported")
.details(Map.of("dossierTemplateId", loadedDossierTemplate.getId()))
.build());
return convert(loadedDossierTemplate);
} catch (IOException e) {
throw new BadRequestException(e.getMessage(), e);
@ -282,7 +288,8 @@ public class DossierTemplateController implements DossierTemplateResource {
private void enhanceDossierTemplateStatsWithACLMemberDetails(DossierTemplateStats stats) {
Set<String> members = new HashSet<>();
stats.getDossiersInTemplate().forEach(d -> members.addAll(dossierACLService.getMembers(d)));
stats.getDossiersInTemplate()
.forEach(d -> members.addAll(dossierACLService.getMembers(d)));
stats.setNumberOfPeople(members.size());
}
@ -290,7 +297,7 @@ public class DossierTemplateController implements DossierTemplateResource {
private DossierTemplateModel convert(DossierTemplate dossierTemplate) {
return DossierTemplateModel.builder()
.dossierTemplateId(dossierTemplate.getId())
.id(dossierTemplate.getId())
.name(dossierTemplate.getName())
.description(dossierTemplate.getDescription())
.dateAdded(dossierTemplate.getDateAdded())
@ -300,7 +307,7 @@ public class DossierTemplateController implements DossierTemplateResource {
.validFrom(dossierTemplate.getValidFrom())
.validTo(dossierTemplate.getValidTo())
.downloadFileTypes(dossierTemplate.getDownloadFileTypes())
.dossierTemplateStatus(DossierTemplateStatus.valueOf(dossierTemplate.getDossierTemplateStatus().name()))
.status(DossierTemplateStatus.valueOf(dossierTemplate.getDossierTemplateStatus().name()))
.keepImageMetadata(dossierTemplate.isKeepImageMetadata())
.keepHiddenText(dossierTemplate.isKeepHiddenText())
.keepOverlappingObjects(dossierTemplate.isKeepOverlappingObjects())

View File

@ -2,38 +2,33 @@ package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.PROCESS_DOWNLOAD;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_DOWNLOAD_STATUS;
import static com.iqser.red.service.persistence.management.v1.processor.utils.DownloadBufferUtils.fileProxyStreamForDownload;
import java.io.InputStream;
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.FileSystemResource;
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.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.iqser.red.persistence.service.v1.external.api.impl.service.OneTimeTokenService;
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.AccessControlService;
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;
@ -53,8 +48,10 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.download.Do
import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadWithOptionRequest;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.knecon.fforesight.tenantcommons.TenantContext;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@ -74,24 +71,32 @@ public class DownloadController implements DownloadResource {
private final OneTimeTokenService oneTimeTokenDownloadService;
private final AccessControlService accessControlService;
private final FileManagementStorageService fileManagementStorageService;
private final FileStatusManagementService fileStatusManagementService;
@Value("${storage.backend:s3}")
private final String REPORT_INFO = "/REPORT_INFO.json";
@Value("${storage.backend}")
private String storageBackend;
@Deprecated(forRemoval = true)
@PreAuthorize("hasAuthority('" + PROCESS_DOWNLOAD + "')")
public DownloadResponse prepareDownload(@RequestBody PrepareDownloadRequest request) {
if (request.getIncludeUnprocessed() == null) {
request.setIncludeUnprocessed(true);
}
// check the user is non-member or reviewer
accessControlService.verifyUserIsDossierOwnerOrApprover(request.getDossierId());
var response = downloadService.prepareDownload(convert(request));
auditPersistenceService.insertRecord(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(response.getValue())
.category(AuditCategory.DOWNLOAD.name())
.message("Download was prepared")
.details(Map.of("dossierId", request.getDossierId()))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(response.getValue())
.category(AuditCategory.DOWNLOAD.name())
.message("Download was prepared")
.details(Map.of("dossierId", request.getDossierId()))
.build());
return new DownloadResponse(response.getValue());
}
@ -99,6 +104,10 @@ public class DownloadController implements DownloadResource {
@PreAuthorize("hasAuthority('" + PROCESS_DOWNLOAD + "')")
public DownloadResponse prepareDownload(@RequestBody PrepareDownloadWithOptionRequest request) {
if (request.getIncludeUnprocessed() == null) {
request.setIncludeUnprocessed(true);
}
validateDossierId(request.getDossierId());
validateAndFilterFileIds(request);
@ -114,12 +123,12 @@ public class DownloadController implements DownloadResource {
var response = downloadService.prepareDownload(convert(request));
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(response.getValue())
.category(AuditCategory.DOWNLOAD.name())
.message("Download was prepared")
.details(Map.of("dossierId", request.getDossierId()))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(response.getValue())
.category(AuditCategory.DOWNLOAD.name())
.message("Download was prepared")
.details(Map.of("dossierId", request.getDossierId()))
.build());
return new DownloadResponse(response.getValue());
}
@ -129,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);
}
@ -139,23 +148,36 @@ 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
validFiles = validFiles.stream().filter(f -> fileIds.contains(f.getId())).collect(Collectors.toList());
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)).collect(Collectors.toList());
var validFilesAndNotProcessed = validFiles.stream()
.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");
}
request.setFileIds(validFiles.stream().map(FileModel::getId).collect(Collectors.toList()));
var approvedFiles = validFiles.stream().filter(f -> f.getWorkflowStatus().equals(WorkflowStatus.APPROVED)).toList();
request.setFileIds(validFiles.stream()
.map(FileModel::getId)
.collect(Collectors.toList()));
var approvedFiles = validFiles.stream()
.filter(f -> f.getWorkflowStatus().equals(WorkflowStatus.APPROVED))
.toList();
// special corner case: unapproved files, no reports and only REDACTED type selected
if (approvedFiles.isEmpty() && (request.getReportTemplateIds() == null || request.getReportTemplateIds()
.isEmpty()) && request.getDownloadFileTypes() != null && request.getDownloadFileTypes().size() == 1 && request.getDownloadFileTypes()
.contains(DownloadFileType.REDACTED)) {
if (approvedFiles.isEmpty()
&& (request.getReportTemplateIds() == null || request.getReportTemplateIds().isEmpty())
&& request.getDownloadFileTypes() != null
&& request.getDownloadFileTypes().size() == 1
&& request.getDownloadFileTypes().contains(DownloadFileType.REDACTED)) {
throw new BadRequestException("Unapproved files in redacted state with no reports cannot be included");
}
}
@ -170,6 +192,7 @@ public class DownloadController implements DownloadResource {
.downloadFileTypes(request.getDownloadFileTypes())
.reportTemplateIds(request.getReportTemplateIds())
.redactionPreviewColor(request.getRedactionPreviewColor())
.includeUnprocessed(request.getIncludeUnprocessed())
.build();
}
@ -178,15 +201,29 @@ public class DownloadController implements DownloadResource {
@PreAuthorize("hasAuthority('" + PROCESS_DOWNLOAD + "')")
public void deleteDownloadStatus(@RequestBody RemoveDownloadRequest removeDownloadRequest) {
removeDownloadRequest.getStorageIds().forEach(storageId -> {
downloadService.deleteDownloadStatus(JSONPrimitive.of(storageId));
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(storageId)
.category(AuditCategory.DOWNLOAD.name())
.message("Remove Prepared Download")
.build());
});
removeDownloadRequest.getStorageIds()
.forEach(storageId -> {
downloadService.deleteDownloadStatus(JSONPrimitive.of(storageId));
fileManagementStorageService.deleteObject(storageId);
if (storageBackend.equals("s3")) {
var storageIdForS3 = generateReportJsonStorageIdForS3(storageId);
log.info("Deleting Report Json from S3 Storage {}", storageIdForS3);
fileManagementStorageService.deleteObject(storageIdForS3);
} else {
var storageIdForAzure = generateReportJsonStorageIdForAzure(storageId);
log.info("Deleting Report Json from Azure Storage {}", storageIdForAzure);
fileManagementStorageService.deleteObject(storageIdForAzure);
}
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(storageId)
.category(AuditCategory.DOWNLOAD.name())
.message("Remove Prepared Download")
.build());
});
}
@ -199,68 +236,57 @@ public class DownloadController implements DownloadResource {
}
@SneakyThrows
@PreAuthorize("hasAuthority('" + PROCESS_DOWNLOAD + "')")
public CompletableFuture<ResponseEntity<InputStreamResource>> downloadFile(@RequestParam(STORAGE_ID) String storageId,
@RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline) {
public void downloadFile(@RequestParam(STORAGE_ID) String storageId) {
var requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletResponse response = ((ServletRequestAttributes) requestAttributes).getResponse();
var userId = KeycloakSecurity.getUserId();
var tenantId = TenantContext.getTenantId();
var downloadStatus = getDownloadStatus(storageId, userId);
var fileDownloadStream = getFileForDownload(storageId, userId);
return CompletableFuture.supplyAsync(() -> {
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment" + "; filename*=utf-8''" + StringEncodingUtils.urlEncode(downloadStatus.getFilename()));
response.setHeader("Content-Length", String.valueOf(downloadStatus.getFileSize()));
TenantContext.setTenantId(tenantId);
var downloadStatus = getDownloadStatus(storageId, userId);
var fileDownloadStream = getFileForDownload(storageId, userId);
fileProxyStreamForDownload(fileDownloadStream);
return getResponseEntity(inline, new InputStreamResource(fileDownloadStream), downloadStatus.getFilename(), MediaType.parseMediaType("application/zip"), downloadStatus.getFileSize());
});
org.apache.commons.io.IOUtils.copyLarge(fileDownloadStream.getInputStream(), response.getOutputStream());
response.flushBuffer();
}
private DownloadStatus getDownloadStatus(String storageId, String userId) {
// TODO Add endpoint to get single download status for userId and storageId.
var downloadStatusResponse = downloadService.getDownloadStatus(userId);
Optional<DownloadStatus> downloadStatusOptional = downloadStatusResponse.stream().filter(ds -> ds.getStorageId().equals(storageId)).findFirst();
Optional<DownloadStatus> downloadStatusOptional = downloadStatusResponse.stream()
.filter(ds -> ds.getStorageId().equals(storageId))
.findFirst();
return downloadStatusOptional.orElseThrow(() -> new NotFoundException("Download status not found for this user"));
}
private InputStream getFileForDownload(String storageId, String userId) {
private InputStreamResource getFileForDownload(String storageId, String userId) {
try {
var response = fileManagementStorageService.getObject(TenantContext.getTenantId(), storageId);
var response = storageService.getObject(TenantContext.getTenantId(), storageId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(userId)
.objectId(storageId)
.category(AuditCategory.DOWNLOAD.name())
.message("File was downloaded.")
.build());
.userId(userId)
.objectId(storageId)
.category(AuditCategory.DOWNLOAD.name())
.message("File was downloaded.")
.build());
downloadService.setDownloaded(JSONPrimitive.of(storageId));
return response;
return new InputStreamResource(new BufferedInputStream(response.getInputStream()));
} catch (Exception e) {
throw new NotFoundException(e.getMessage(), e);
}
}
@SneakyThrows
private ResponseEntity<InputStreamResource> getResponseEntity(boolean inline, InputStreamResource resource, String filename, MediaType mediaType, long fileSize) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(mediaType);
httpHeaders.setContentLength(fileSize);
if (filename != null) {
httpHeaders.add("Content-Disposition", inline ? "inline" : "attachment" + "; filename*=utf-8''" + StringEncodingUtils.urlEncode(filename));
}
return new ResponseEntity<>(resource, httpHeaders, HttpStatus.OK);
}
@Override
@PreAuthorize("hasAuthority('" + PROCESS_DOWNLOAD + "')")
public JSONPrimitive<String> generateOneTimeToken(@RequestBody JSONPrimitive<String> storageIdWrapper) {
@ -271,28 +297,46 @@ public class DownloadController implements DownloadResource {
@Override
public CompletableFuture<ResponseEntity<InputStreamResource>> downloadFileUsingOTT(@PathVariable(OTT) String oneTimeToken,
@RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline,
@RequestParam(value = "tenantId") String tenantId) {
return CompletableFuture.supplyAsync(() -> {
TenantContext.setTenantId(tenantId);
@SneakyThrows
public void downloadFileUsingOTT(@PathVariable(OTT) String oneTimeToken, @RequestParam(value = "tenantId") String tenantId) {
log.debug("downloadFileUsingOTT '{}'", oneTimeToken);
var token = oneTimeTokenDownloadService.getToken(oneTimeToken);
var downloadStatus = getDownloadStatus(token.getStorageId(), token.getUserId());
var fileDownloadStream = getFileForDownload(token.getStorageId(), token.getUserId());
fileProxyStreamForDownload(fileDownloadStream);
TenantContext.setTenantId(tenantId);
var token = oneTimeTokenDownloadService.getToken(oneTimeToken);
var requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletResponse response = ((ServletRequestAttributes) requestAttributes).getResponse();
TenantContext.clear();
var downloadStatus = getDownloadStatus(token.getStorageId(), token.getUserId());
var fileDownloadStream = getFileForDownload(token.getStorageId(), token.getUserId());
return getResponseEntity(inline, new InputStreamResource(fileDownloadStream), downloadStatus.getFilename(), MediaType.parseMediaType("application/zip"), downloadStatus.getFileSize());
});
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment" + "; filename*=utf-8''" + StringEncodingUtils.urlEncode(downloadStatus.getFilename()));
response.setHeader("Content-Length", String.valueOf(downloadStatus.getFileSize()));
org.apache.commons.io.IOUtils.copyLarge(fileDownloadStream.getInputStream(), response.getOutputStream());
response.flushBuffer();
}
private DownloadRequest convert(PrepareDownloadRequest request) {
return DownloadRequest.builder().dossierId(request.getDossierId()).userId(KeycloakSecurity.getUserId()).fileIds(request.getFileIds()).build();
return DownloadRequest.builder()
.dossierId(request.getDossierId())
.userId(KeycloakSecurity.getUserId())
.fileIds(request.getFileIds())
.includeUnprocessed(request.getIncludeUnprocessed())
.build();
}
private String generateReportJsonStorageIdForS3(String storageId) {
return storageId.substring(0, storageId.length() - 3) + REPORT_INFO;
}
private String generateReportJsonStorageIdForAzure(String storageId) {
return storageId.substring(0, storageId.length() - 4) + REPORT_INFO;
}
}

View File

@ -0,0 +1,80 @@
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;
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.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.EntityLogResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.FilteredEntityLogRequest;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
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 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 entityLogResponseMapper.toLogResponse(entityLogService.getEntityLog(dossierId,
fileId,
excludedTypes == null ? new ArrayList<>() : excludedTypes,
includeUnprocessed));
}
@PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
public EntityLogResponse getFilteredEntityLog(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody FilteredEntityLogRequest filteredEntityLogRequest) {
accessControlService.checkViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
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

@ -1,29 +1,5 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import java.time.OffsetDateTime;
import java.util.Map;
import java.util.stream.Collectors;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
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;
@ -31,10 +7,30 @@ import com.iqser.red.service.persistence.management.v1.processor.exception.Confl
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;
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.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.*;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import java.time.OffsetDateTime;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@RequiredArgsConstructor
@RestControllerAdvice
@ -44,6 +40,7 @@ public class ExternalControllerAdvice {
private final Scheduler scheduler;
@Hidden
@ResponseBody
@ResponseStatus(value = HttpStatus.NOT_FOUND)
@ExceptionHandler(value = NotFoundException.class)
@ -56,6 +53,7 @@ public class ExternalControllerAdvice {
/* error handling */
@Hidden
@ResponseBody
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = BadRequestException.class)
@ -65,6 +63,7 @@ public class ExternalControllerAdvice {
}
@Hidden
@ResponseBody
@ResponseStatus(value = HttpStatus.CONFLICT)
@ExceptionHandler(value = {ConflictException.class})
@ -83,6 +82,15 @@ public class ExternalControllerAdvice {
}
@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})
@ -92,6 +100,7 @@ public class ExternalControllerAdvice {
}
@Hidden
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler({org.springframework.security.acls.model.NotFoundException.class})
@ -108,17 +117,21 @@ public class ExternalControllerAdvice {
}
@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(", "));
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})
@ -128,6 +141,7 @@ public class ExternalControllerAdvice {
}
@Hidden
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({HttpMessageNotReadableException.class})

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;
@ -33,6 +36,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.dossier.file.FileAttributeConfig;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.WorkflowStatus;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -46,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
@ -56,20 +61,23 @@ public class FileAttributesController implements FileAttributesResource {
throw new BadRequestException("Invalid encoding setting");
}
fileAttributeConfigPersistenceService.setFileAttributesGeneralConfig(dossierTemplateId,
MagicConverter.convert(fileAttributesConfig, FileAttributesGeneralConfigurationEntity.class));
var result = fileAttributeConfigPersistenceService.setFileAttributesConfig(dossierTemplateId,
MagicConverter.convert(fileAttributesConfig.getFileAttributeConfigs(), FileAttributeConfigEntity.class));
MagicConverter.convert(fileAttributesConfig, FileAttributesGeneralConfigurationEntity.class));
fileAttributeConfigPersistenceService.setFileAttributesConfig(dossierTemplateId,
MagicConverter.convert(fileAttributesConfig.getFileAttributeConfigs(), FileAttributeConfigEntity.class));
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Changed file attributes base configuration & attribute configuration ( CSV Import )")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Changed file attributes base configuration & attribute configuration ( CSV Import )")
.build());
return FileAttributesConfig.builder()
.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();
}
@ -77,18 +85,21 @@ public class FileAttributesController implements FileAttributesResource {
@Override
@PreAuthorize("hasAuthority('" + WRITE_FILE_ATTRIBUTES_CONFIG + "')")
public FileAttributeConfig addOrUpdateFileAttribute(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody FileAttributeConfig fileAttribute) {
public FileAttributeConfig addOrUpdateFileAttribute(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @Valid @RequestBody FileAttributeConfig fileAttribute) {
var result = fileAttributeConfigPersistenceService.addOrUpdateFileAttribute(dossierTemplateId, MagicConverter.convert(fileAttribute, FileAttributeConfigEntity.class));
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("File attributes added/updated")
.details(Map.of("FileAttributeName", fileAttribute.getLabel() != null ? fileAttribute.getLabel() : "", "dossierTemplateId", dossierTemplateId))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("File attributes added/updated")
.details(Map.of("FileAttributeName",
fileAttribute.getLabel() != null ? fileAttribute.getLabel() : "",
"dossierTemplateId",
dossierTemplateId))
.build());
return MagicConverter.convert(result, FileAttributeConfig.class);
return MagicConverter.convert(result, FileAttributeConfig.class, new FileAttributeConfigMapper());
}
@ -98,12 +109,12 @@ public class FileAttributesController implements FileAttributesResource {
fileAttributeConfigPersistenceService.deleteFileAttribute(fileAttributeId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("File attributes removed")
.details(Map.of("FileAttributeId", fileAttributeId))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("File attributes removed")
.details(Map.of("FileAttributeId", fileAttributeId))
.build());
}
@ -113,12 +124,12 @@ public class FileAttributesController implements FileAttributesResource {
fileAttributeConfigPersistenceService.deleteFileAttributes(fileAttributeIds);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("File attributes removed")
.details(Map.of("FileAttributeId", fileAttributeIds))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("File attributes removed")
.details(Map.of("FileAttributeId", fileAttributeIds))
.build());
}
@ -129,7 +140,7 @@ public class FileAttributesController implements FileAttributesResource {
FileAttributesGeneralConfiguration generalConfig = new FileAttributesGeneralConfiguration();
try {
generalConfig = MagicConverter.convert(fileAttributeConfigPersistenceService.getFileAttributesGeneralConfiguration(dossierTemplateId),
FileAttributesGeneralConfiguration.class);
FileAttributesGeneralConfiguration.class);
} catch (Exception e) {
log.debug("No general config defined", e);
}
@ -137,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();
}
@ -145,6 +156,7 @@ public class FileAttributesController implements FileAttributesResource {
@PreAuthorize("hasAuthority('" + WRITE_FILE_ATTRIBUTES + "')")
public void setFileAttributes(@PathVariable(DOSSIER_ID_PARAM) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody FileAttributes fileAttributes) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
var file = fileStatusService.getStatus(fileId);
if (file.getWorkflowStatus().equals(WorkflowStatus.APPROVED)) {
@ -155,12 +167,13 @@ 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)
.category(AuditCategory.DOCUMENT.name())
.message("File attributes has been edited for a document.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("File attributes has been edited for a document.")
.build());
}

View File

@ -21,11 +21,14 @@ 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.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.client.pdftronredactionservice.PDFTronClient;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.InternalServerErrorException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotAllowedException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
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.FileService;
@ -41,6 +44,8 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.RotatePages
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.dossiertemplate.dossier.file.FileType;
import com.iqser.red.storage.commons.exception.StorageException;
import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.knecon.fforesight.tenantcommons.TenantContext;
@ -74,14 +79,15 @@ public class FileManagementController implements FileManagementResource {
@PreAuthorize("hasAuthority('" + DELETE_FILE + "')")
public void deleteFile(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
fileService.deleteFile(dossierId, fileId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("File has been deleted.")
.details(Map.of("fileId", fileId))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("File has been deleted.")
.details(Map.of("fileId", fileId))
.build());
}
@ -90,17 +96,18 @@ public class FileManagementController implements FileManagementResource {
@PreAuthorize("hasAuthority('" + DELETE_FILE + "')")
public void deleteFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody List<String> fileIds) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
List<String> errorIds = new ArrayList<>();
for (String fileId : fileIds) {
try {
fileService.deleteFile(dossierId, fileId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Files have been deleted.")
.details(Map.of("Size", fileIds.size()))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Files have been deleted.")
.details(Map.of("Size", fileIds.size()))
.build());
} catch (Exception e) {
errorIds.add(fileId);
}
@ -118,6 +125,7 @@ public class FileManagementController implements FileManagementResource {
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline) {
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
return getResponseEntityForPDFDocument(fileId, dossierId, FileType.ORIGIN, inline);
}
@ -129,6 +137,7 @@ public class FileManagementController implements FileManagementResource {
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline) {
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
// Viewer Document Returns
if (storageService.objectExists(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, FileType.VIEWER_DOCUMENT))) {
return getResponseEntityForPDFDocument(fileId, dossierId, FileType.VIEWER_DOCUMENT, inline);
@ -138,18 +147,18 @@ public class FileManagementController implements FileManagementResource {
}
private ResponseEntity<?> getResponseEntityForPDFDocument(String fileId, String dossierId, FileType viewerDocument, boolean inline) {
private ResponseEntity<?> getResponseEntityForPDFDocument(String fileId, String dossierId, FileType fileType, boolean inline) {
String storageId = StorageIdUtils.getStorageId(dossierId, fileId, fileType);
try {
var file = fileStatusManagementService.getFileStatus(fileId);
var viewerDocumentFileStream = fileManagementStorageService.getObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, viewerDocument));
return getResponseEntity(inline, viewerDocumentFileStream, file.getFilename());
} catch (FeignException e) {
if (e.status() == HttpStatus.NOT_FOUND.value()) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
var pdfFileStream = fileManagementStorageService.getObject(TenantContext.getTenantId(), storageId);
return getResponseEntity(inline, pdfFileStream, file.getFilename());
} catch (StorageObjectDoesNotExist e) {
throw new NotFoundException(String.format("File \"%s\" not found!, the dossier with ID \"%s\" probably does not exist.", storageId, dossierId));
} catch (StorageException e) {
log.debug(e.getMessage(), e);
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
throw new InternalServerErrorException(e.getMessage());
}
}
@ -171,21 +180,50 @@ public class FileManagementController implements FileManagementResource {
@Override
@PreAuthorize("hasAuthority('" + DELETE_FILE + "')")
@Deprecated(forRemoval = true)
public void hardDeleteFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(FILE_IDS) Set<String> fileIds) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
for (String fileId : fileIds) {
if (fileStatusManagementService.getFileStatus(fileId).getAssignee() != null) {
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
}
}
fileService.hardDeleteFiles(dossierId, new ArrayList<>(fileIds));
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Files has been hard deleted.")
.details(Map.of("FileIds", fileIds))
.build());
}
@Override
@PreAuthorize("hasAuthority('" + DELETE_FILE + "')")
@ResponseStatus(HttpStatus.ACCEPTED)
public void hardDeleteFiles(String dossierId, @RequestBody List<String> fileIds) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
fileIds.forEach(fileId -> {
try {
if (fileStatusManagementService.getFileStatus(fileId).getAssignee() != null) {
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
}
} catch (NotFoundException e) {
log.warn("File {} to be deleted was not found.", fileId);
}
});
fileService.hardDeleteFiles(dossierId, fileIds);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Files has been hard deleted.")
.details(Map.of("FileIds", fileIds))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Files have been hard deleted.")
.details(Map.of("FileIds", fileIds))
.build());
}
@ -193,15 +231,16 @@ public class FileManagementController implements FileManagementResource {
@PreAuthorize("hasAuthority('" + DELETE_FILE + "')")
public void restoreFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody Set<String> fileIds) {
verifyUserIsDossierOwnerOrApproverOrAssignedReviewer(dossierId, fileIds);
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserIsDossierOwnerOrApproverOrAssignedReviewer(dossierId, fileIds);
fileService.undeleteFiles(dossierId, fileIds);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Files has been restored.")
.details(Map.of("FileIds", fileIds))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Files has been restored.")
.details(Map.of("FileIds", fileIds))
.build());
}
@ -209,55 +248,45 @@ public class FileManagementController implements FileManagementResource {
@PreAuthorize("hasAuthority('" + ROTATE_PAGE + "')")
public void rotatePages(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RotatePagesRequest rotatePagesRequest) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsReviewer(dossierId, fileId);
try {
pdfTronClient.rotate(com.iqser.red.service.pdftron.redaction.v1.api.model.RotatePagesRequest.builder()
.dossierId(dossierId)
.fileId(fileId)
.pages(rotatePagesRequest.getPages())
.build());
.dossierId(dossierId)
.fileId(fileId)
.pages(rotatePagesRequest.getPages())
.build());
fileService.deleteViewerDocument(dossierId, fileId);
fileStatusManagementService.updateFileModificationDate(fileId);
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()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOCUMENT.name())
.message("Pages have been rotated.")
.details(Map.of("Pages", rotatePagesRequest.getPages().keySet()))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOCUMENT.name())
.message("Pages have been rotated.")
.details(Map.of("Pages", rotatePagesRequest.getPages().keySet()))
.build());
} catch (FeignException e) {
throw processFeignException(e);
}
}
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,18 +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))) {
InputStream stream = fileManagementStorageService.getObject(TenantContext.getTenantId(), getStorageId(dossierId, fileId, FileType.TEXT_HIGHLIGHTS));
Highlights highlights = objectMapper.readValue(stream, Highlights.class);
stream.close();
return highlights;
try (InputStream stream = fileManagementStorageService.getObject(TenantContext.getTenantId(), getStorageId(dossierId, fileId, FileType.TEXT_HIGHLIGHTS))) {
TextHighlights highlights = objectMapper.readValue(stream, TextHighlights.class);
stream.close();
return highlights;
}
}
return new Highlights();
return new TextHighlights();
}
@ -66,6 +68,7 @@ public class HighlightsController implements HighlightsResource {
public void convertHighlights(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody AnnotationIds annotationIds) {
try {
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
@ -80,6 +83,7 @@ public class HighlightsController implements HighlightsResource {
public void deleteHighlights(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody AnnotationIds annotationIds) {
try {
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
@ -95,6 +99,7 @@ public class HighlightsController implements HighlightsResource {
public void deleteImportedRedactions(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody AnnotationIds annotationIds) {
try {
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);

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,15 +32,15 @@ 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)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Legal basis mapping has been changed.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Legal basis mapping has been changed.")
.build());
}
@ -51,11 +50,11 @@ public class LegalBasisMappingController implements LegalBasisMappingResource {
legalBasisMappingPersistenceService.addOrUpdateLegalBasis(dossierTemplateId, legalBasis);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Legal basis mapping has been changed.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Legal basis mapping has been changed.")
.build());
}
@ -64,13 +63,14 @@ public class LegalBasisMappingController implements LegalBasisMappingResource {
legalBasisMappingPersistenceService.setLegalBasisMapping(dossierTemplateId, legalBasisMapping);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Legal basis mapping has been changed.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Legal basis mapping has been changed.")
.build());
}
@Transactional
@PreAuthorize("hasAuthority('" + READ_LEGAL_BASIS + "')")
public List<LegalBasis> getLegalBasisMapping(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {

View File

@ -2,19 +2,14 @@ package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_LICENSE;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.UPDATE_LICENSE;
import static com.iqser.red.service.persistence.management.v1.processor.service.LicenseUtilityService.*;
import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.Environment;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.service.FileFormatValidationService;
import com.iqser.red.service.persistence.management.v1.processor.service.LicenseUtilityService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.LicenseResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.license.Feature;
import com.iqser.red.service.persistence.service.v1.api.shared.model.license.FeatureType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.license.License;
import com.iqser.red.service.persistence.service.v1.api.shared.model.license.RedactionLicenseModel;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.tenantcommons.TenantContext;
@ -28,18 +23,9 @@ import lombok.extern.slf4j.Slf4j;
@RequiredArgsConstructor
public class LicenseController implements LicenseResource {
private static final String LICENSE_OBJECT_ID = "license/redact-manager-license.json";
private static final String DEMO_PDFTRON_LICENSE = "demo:1650351709282:7bd235e003000000004ec28a6743e1163a085e2115de2536ab6e2cfe5a";
private static final String LICENSE_CUSTOMER = "LICENSE_CUSTOMER";
private static final String LICENSE_START = "LICENSE_START";
private static final String LICENSE_END = "LICENSE_END";
private static final String LICENSE_PAGE_COUNT = "LICENSE_PAGE_COUNT";
private static final String PDFTRON_FRONTEND_LICENSE = "PDFTRON_FRONTEND_LICENSE";
private static final String PDFTRON_FEATURE = "pdftron";
private static final String PROCESSING_PAGES_FEATURE = "processingPages";
private static final String DEFAULT_PROCESSING_PAGES = "200000";
private final StorageService storageService;
private final Environment environment;
private final LicenseUtilityService licenseUtilityService;
private final FileFormatValidationService fileFormatValidationService;
@Override
@ -48,6 +34,7 @@ public class LicenseController implements LicenseResource {
public void updateLicense(RedactionLicenseModel licenseModel) {
storageService.storeJSONObject(TenantContext.getTenantId(), LICENSE_OBJECT_ID, licenseModel);
fileFormatValidationService.evictValidFileFormatsForTenant(TenantContext.getTenantId());
}
@ -55,80 +42,16 @@ public class LicenseController implements LicenseResource {
@PreAuthorize("hasAuthority('" + READ_LICENSE + "')")
public RedactionLicenseModel getLicense() {
if (storageService.objectExists(TenantContext.getTenantId(), LICENSE_OBJECT_ID)) {
String tenantId = TenantContext.getTenantId();
if (storageService.objectExists(tenantId, LICENSE_OBJECT_ID)) {
try {
return storageService.readJSONObject(TenantContext.getTenantId(), LICENSE_OBJECT_ID, RedactionLicenseModel.class);
return storageService.readJSONObject(tenantId, LICENSE_OBJECT_ID, RedactionLicenseModel.class);
} catch (Exception e) {
return generateDemoLicense();
return licenseUtilityService.generateDemoLicense();
}
} else {
return generateDemoLicense();
return licenseUtilityService.generateDemoLicense();
}
}
private RedactionLicenseModel generateDemoLicense() {
License demo = new License();
demo.setId("RedactManager");
demo.setName("RedactManager License");
demo.setProduct("RedactManager");
var licensedTo = environment.getProperty(LICENSE_CUSTOMER, "RedactManager Demo License");
demo.setLicensedTo(licensedTo);
var licenseStartDate = parseDate(environment.getProperty(LICENSE_START));
var start = licenseStartDate != null ? licenseStartDate : OffsetDateTime.now().withDayOfYear(1).withHour(0).withMinute(0).withSecond(0).truncatedTo(ChronoUnit.SECONDS);
demo.setValidFrom(start);
var licenseEndDate = parseDate(environment.getProperty(LICENSE_END));
var end = licenseEndDate != null ? licenseEndDate : OffsetDateTime.now()
.withMonth(12)
.withDayOfMonth(31)
.withHour(0)
.withMinute(0)
.withSecond(0)
.truncatedTo(ChronoUnit.SECONDS);
demo.setValidUntil(end);
var pdftronLicense = environment.getProperty(PDFTRON_FRONTEND_LICENSE, DEMO_PDFTRON_LICENSE);
demo.getFeatures().add(Feature.builder().name(PDFTRON_FEATURE).type(FeatureType.STRING).value(pdftronLicense).build());
var pageCount = Long.parseLong(environment.getProperty(LICENSE_PAGE_COUNT, DEFAULT_PROCESSING_PAGES));
demo.getFeatures().add(Feature.builder().name(PROCESSING_PAGES_FEATURE).type(FeatureType.NUMBER).value(pageCount).build());
var demoLicense = new RedactionLicenseModel();
demoLicense.setActiveLicense("RedactManager");
demoLicense.getLicenses().add(demo);
return demoLicense;
}
private OffsetDateTime parseDate(String date) {
if (StringUtils.isEmpty(date)) {
return null;
}
var parts = date.split("-");
if (parts.length != 3) {
return null;
}
try {
return OffsetDateTime.now()
.withYear(Integer.parseInt(parts[2]))
.withMonth(Integer.parseInt(parts[1]))
.withDayOfMonth(Integer.parseInt(parts[0]))
.withHour(0)
.withMinute(0)
.withSecond(0)
.truncatedTo(ChronoUnit.SECONDS);
} catch (Exception e) {
log.debug("Failed to parse date: {}", date);
return null;
}
}
}

View File

@ -33,11 +33,11 @@ public class LicenseReportController implements LicenseReportResource {
LicenseReport licenseReport = licenseReportService.getLicenseReport(reportRequest);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(LICENSE_AUDIT_KEY)
.category(AuditCategory.LICENSE.name())
.message("License report has been viewed.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(LICENSE_AUDIT_KEY)
.category(AuditCategory.LICENSE.name())
.message("License report has been viewed.")
.build());
return licenseReport;
}

View File

@ -5,11 +5,10 @@ import static com.iqser.red.service.persistence.management.v1.processor.roles.Ac
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.DELETE_MANUAL_REDACTION;
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 static com.iqser.red.service.persistence.management.v1.processor.utils.TypeIdUtils.toTypeId;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -18,507 +17,601 @@ 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 com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
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.ManualRedactionService;
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.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.AnnotationStatus;
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.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.entitymapped.IdRemoval;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualForceRedaction;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualImageRecategorization;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualLegalBasisChange;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualRedactionEntry;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualResizeRedaction;
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.manual.AddCommentRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ForceRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ImageRecategorizationRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.LegalBasisChangeRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ManualRedactionWrapper;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RemoveRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ResizeRedactionRequest;
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;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class ManualRedactionController implements ManualRedactionResource {
private static final String FILE_ID = "fileId";
private static final String DOSSIER_ID = "dossierId";
private static final String ANNOTATION_ID = "annotationId";
private final ManualRedactionService manualRedactionService;
private final DossierManagementService dossierManagementService;
private final AuditPersistenceService auditPersistenceService;
private final AccessControlService accessControlService;
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;
CommentService commentService;
FileStatusManagementService fileStatusManagementService;
FileStatusService fileStatusService;
EntityLogMongoService entityLogMongoService;
PendingEntryFactory pendingEntryFactory;
DictionaryPersistenceService dictionaryPersistenceService;
EntityLogController entityLogController;
EntityLogResponseMapper mapper = EntityLogResponseMapper.INSTANCE;
private ManualRedactionWrapper getLatestManualRedactionForAnnotationId(ManualRedactions manualRedactions, String annotationId) {
final List<ManualRedactionWrapper> manualRedactionWrappers = new ArrayList<>();
manualRedactions.getEntriesToAdd()
.stream()
.filter(item -> item.getSoftDeletedTime() == null && item.getAnnotationId().equals(annotationId))
.forEach(item -> manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item)));
manualRedactions.getImageRecategorization()
.stream()
.filter(item -> item.getSoftDeletedTime() == null && item.getAnnotationId().equals(annotationId))
.forEach(item -> manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item)));
manualRedactions.getIdsToRemove()
.stream()
.filter(item -> item.getSoftDeletedTime() == null && item.getAnnotationId().equals(annotationId))
.forEach(item -> manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item)));
manualRedactions.getForceRedactions()
.stream()
.filter(item -> item.getSoftDeletedTime() == null && item.getAnnotationId().equals(annotationId))
.forEach(item -> manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item)));
manualRedactions.getLegalBasisChanges()
.stream()
.filter(item -> item.getSoftDeletedTime() == null && item.getAnnotationId().equals(annotationId))
.forEach(item -> manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item)));
manualRedactions.getResizeRedactions()
.stream()
.filter(item -> item.getSoftDeletedTime() == null && item.getAnnotationId().equals(annotationId))
.forEach(item -> manualRedactionWrappers.add(new ManualRedactionWrapper(item.getAnnotationId(), item.getRequestDate(), item)));
var sortedManualRedactionWrappers = manualRedactionWrappers.stream()
.sorted(Comparator.comparing(ManualRedactionWrapper::getDate, Comparator.nullsLast(Comparator.reverseOrder())))
.collect(Collectors.toList());
return sortedManualRedactionWrappers.isEmpty() ? null : sortedManualRedactionWrappers.get(0);
}
@Override
@PreAuthorize("hasAuthority('" + DELETE_MANUAL_REDACTION + "')")
public void undo(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody Set<String> annotationIds) {
public void undo(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<String> annotationIds,
@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, includeOnlyUnprocessed, includeOnlyLocal);
ManualRedactions manualRedactions = manualRedactionService.getManualRedactions(fileId);
Map<String, ManualRedactionWrapper> manualRedactionWrappers = getLatestManualRedactionsForAnnotationIds(manualRedactions, annotationIds);
if (manualRedactionWrappers.isEmpty()) {
throw new NotFoundException(String.format("ManualRedaction with annotationIds %s could not be found.", annotationIds));
}
List<String> manualRedactionEntries = manualRedactionWrappers.values()
.stream()
.filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualRedactionEntry)
.map(ManualRedactionWrapper::getId)
.collect(Collectors.toList());
if (!manualRedactionEntries.isEmpty()) {
manualRedactionService.deleteAddRedaction(dossierId, fileId, manualRedactionEntries);
manualRedactionEntries.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Undo of manual add redaction was done.")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
.build()));
}
List<String> idRemovals = manualRedactionWrappers.values()
.stream()
.filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof IdRemoval)
.map(ManualRedactionWrapper::getId)
.collect(Collectors.toList());
if (!idRemovals.isEmpty()) {
manualRedactionService.deleteRemoveRedaction(dossierId, fileId, idRemovals);
idRemovals.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Undo of manual remove redaction was done.")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
.build()));
}
List<String> manualForceRedactions = manualRedactionWrappers.values()
.stream()
.filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualForceRedaction)
.map(ManualRedactionWrapper::getId)
.collect(Collectors.toList());
if (!manualForceRedactions.isEmpty()) {
manualRedactionService.deleteForceRedaction(dossierId, fileId, manualForceRedactions);
manualForceRedactions.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Undo of manual force redaction was done.")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
.build()));
}
List<String> manualImageRecategorizations = manualRedactionWrappers.values()
.stream()
.filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualImageRecategorization)
.map(ManualRedactionWrapper::getId)
.collect(Collectors.toList());
if (!manualImageRecategorizations.isEmpty()) {
manualRedactionService.deleteImageRecategorization(dossierId, fileId, manualImageRecategorizations);
manualImageRecategorizations.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Undo of manual image recategorization was done.")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
.build()));
}
List<String> manualLegalBasisChanges = manualRedactionWrappers.values()
.stream()
.filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualLegalBasisChange)
.map(ManualRedactionWrapper::getId)
.collect(Collectors.toList());
if (!manualLegalBasisChanges.isEmpty()) {
manualRedactionService.deleteLegalBasisChange(dossierId, fileId, manualLegalBasisChanges);
manualLegalBasisChanges.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Undo of legal basis change was done.")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
.build()));
}
List<String> manualResizeRedactions = manualRedactionWrappers.values()
.stream()
.filter(manualRedactionWrapper -> manualRedactionWrapper.getItem() instanceof ManualResizeRedaction)
.map(ManualRedactionWrapper::getId)
.collect(Collectors.toList());
if (!manualResizeRedactions.isEmpty()) {
manualRedactionService.deleteResizeRedaction(dossierId, fileId, manualResizeRedactions);
manualResizeRedactions.forEach(annotationId -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Undo of manual resize redaction was done.")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
.build()));
}
}
private Map<String, ManualRedactionWrapper> getLatestManualRedactionsForAnnotationIds(ManualRedactions manualRedactions, Set<String> annotationIds) {
Map<String, ManualRedactionWrapper> result = new HashMap<>();
annotationIds.forEach(annotationId -> {
var last = getLatestManualRedactionForAnnotationId(manualRedactions, annotationId);
if (last != null) {
result.put(annotationId, last);
}
});
return result;
}
@Override
@PreAuthorize("hasAuthority('" + DELETE_COMMENT + "')")
public void undoComment(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@PathVariable(ANNOTATION_ID) String annotationId,
@PathVariable(COMMENT_ID) String commentId) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Comment was removed.")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
.build());
manualRedactionService.deleteComment(fileId, List.of(Long.valueOf(commentId)));
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Comment was removed.")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
.build());
commentService.deleteComment(fileId, List.of(Long.valueOf(commentId)));
}
@Override
@PreAuthorize("hasAuthority('" + READ_MANUAL_REDACTIONS + "')")
public ManualRedactions getManualRedactions(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
public ManualRedactions getManualRedactions(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "unprocessed", required = false, defaultValue = FALSE) boolean unprocessed,
@RequestParam(value = "includeDictChanges", required = false, defaultValue = TRUE) boolean includeDictChanges) {
return manualRedactionService.getManualRedactions(fileId);
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
accessControlService.validateFileResourceExistence(fileId);
return manualRedactionService.getManualRedactions(fileId,
ManualChangesQueryOptions.builder().includeOnlyUnprocessed(unprocessed).includeDictChanges(includeDictChanges).build());
}
@PreAuthorize("hasAuthority('" + READ_MANUAL_REDACTIONS + "')")
public AnnotationComments getComments(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @PathVariable(ANNOTATION_ID) String annotationId) {
dossierManagementService.getDossierById(dossierId, false, false);
accessControlService.checkViewPermissionsToDossier(dossierId);
fileStatusManagementService.getFileStatus(fileId, false);
List<Comment> comments = commentService.getComments(fileId, annotationId);
return new AnnotationComments(comments);
}
@Override
@PreAuthorize("hasAuthority('" + ADD_COMMENT + "')")
public CommentResponse addComment(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@PathVariable(ANNOTATION_ID) String annotationId,
@RequestBody AddCommentRequest addCommentRequest) {
@RequestBody AddCommentRequestModel addCommentRequest) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
var response = manualRedactionService.addComment(fileId,
annotationId,
CommentRequest.builder().user(KeycloakSecurity.getUserId()).text(addCommentRequest.getText()).build());
var response = commentService.addComment(fileId, annotationId, CommentRequest.builder().user(KeycloakSecurity.getUserId()).text(addCommentRequest.getText()).build());
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Comment was added.")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Comment was added.")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
.build());
return new CommentResponse(String.valueOf(response.getId()));
}
@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<AddRedactionRequest> addRedactionRequests) {
@RequestBody Set<AddRedactionRequestModel> addRedactionRequests) {
var dossier = dossierManagementService.getDossierById(dossierId, false, false);
verifyAccessForDossier(dossierId,
fileId,
addRedactionRequests.stream()
.anyMatch(AddRedactionRequestModel::isAddToAllDossiers));
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
if(addRedactionRequests.stream().anyMatch(AddRedactionRequest::isAddToAllDossiers)){
accessControlService.verifyUserIsApprover(dossierId);
} else {
accessControlService.verifyUserIsMemberOrApprover(dossierId);
}
List<com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AddRedactionRequest> requests = new ArrayList<>();
for (var addRedactionRequest : addRedactionRequests) {
var addRedactionRequestBuilder = com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AddRedactionRequest.builder()
.value(addRedactionRequest.getValue())
.legalBasis(addRedactionRequest.getLegalBasis())
.user(KeycloakSecurity.getUserId())
.dossierTemplateTypeId(toTypeId(addRedactionRequest.getType(), dossier.getDossierTemplateId()))
.reason(addRedactionRequest.getReason())
.addToDictionary(addRedactionRequest.isAddToDictionary())
.status(AnnotationStatus.APPROVED)
.comment(addRedactionRequest.getComment() != null ? addRedactionRequest.getComment().getText() : null)
.section(addRedactionRequest.getSection())
.rectangle(addRedactionRequest.isRectangle())
.addToAllDossiers(addRedactionRequest.isAddToAllDossiers())
.forceAddToDictionary(addRedactionRequest.isForceAddToDictionary())
.positions(addRedactionRequest.getPositions())
.sourceId(addRedactionRequest.getSourceId())
.dossierId(dossierId)
.dictionaryEntryType(addRedactionRequest.getDictionaryEntryType());
requests.add(addRedactionRequestBuilder.build());
}
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.")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
return responseList;
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Manual annotation was added.")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
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<RemoveRedactionRequest> removeRedactionRequests) {
@RequestBody Set<RemoveRedactionRequestModel> removeRedactionRequests) {
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
if(removeRedactionRequests.stream().anyMatch(RemoveRedactionRequest::isRemoveFromAllDossiers)){
accessControlService.verifyUserIsApprover(dossierId);
} else {
accessControlService.verifyUserIsMemberOrApprover(dossierId);
}
var dossier = dossierManagementService.getDossierById(dossierId, false, false);
verifyAccessForDossier(dossierId,
fileId,
removeRedactionRequests.stream()
.anyMatch(RemoveRedactionRequestModel::isRemoveFromAllDossiers));
List<com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.RemoveRedactionRequest> requests = new ArrayList<>();
for (var removeRedactionRequest : removeRedactionRequests) {
var removeRedactionRequestBuilder = com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.RemoveRedactionRequest.builder()
.annotationId(removeRedactionRequest.getAnnotationId())
.user(KeycloakSecurity.getUserId())
.status(AnnotationStatus.APPROVED)
.removeFromDictionary(removeRedactionRequest.isRemoveFromDictionary())
.removeFromAllDossiers(removeRedactionRequest.isRemoveFromAllDossiers());
if (removeRedactionRequest.getComment() != null) {
removeRedactionRequestBuilder.comment(removeRedactionRequest.getComment());
}
requests.add(removeRedactionRequestBuilder.build());
}
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")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.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<ForceRedactionRequest> forceRedactionRequests) {
@RequestBody Set<ForceRedactionRequestModel> forceRedactionRequests) {
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
verifyAccessAndDossierExistence(dossierId, fileId);
List<com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ForceRedactionRequest> requests = forceRedactionRequests.stream()
.map(forceRedactionRequest -> com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ForceRedactionRequest.builder()
.annotationId(forceRedactionRequest.getAnnotationId())
.user(KeycloakSecurity.getUserId())
.status(AnnotationStatus.APPROVED)
.legalBasis(forceRedactionRequest.getLegalBasis())
.comment(forceRedactionRequest.getComment())
.build())
.collect(Collectors.toList());
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")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.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<LegalBasisChangeRequest> legalBasisChangeRequests) {
@RequestBody Set<LegalBasisChangeRequestModel> legalBasisChangeRequests) {
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
verifyAccessAndDossierExistence(dossierId, fileId);
List<com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.LegalBasisChangeRequest> requests = legalBasisChangeRequests.stream()
.map(legalBasisChangeRequest -> com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.LegalBasisChangeRequest.builder()
.annotationId(legalBasisChangeRequest.getAnnotationId())
.user(KeycloakSecurity.getUserId())
.status(AnnotationStatus.APPROVED)
.section(legalBasisChangeRequest.getSection())
.legalBasis(legalBasisChangeRequest.getLegalBasis())
.comment(legalBasisChangeRequest.getComment())
.value(legalBasisChangeRequest.getValue())
.build())
.collect(Collectors.toList());
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())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Legal basis reason was changed")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Legal basis reason was changed")
.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> recategorizeImageBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<ImageRecategorizationRequest> imageRecategorizationRequests) {
public ManualRedactionResponse recategorizeBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<RecategorizationRequestModel> recategorizationRequests) {
var dossier = dossierManagementService.getDossierById(dossierId, false, false);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
verifyAccess(dossierId, fileId);
List<com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ImageRecategorizationRequest> requests = imageRecategorizationRequests.stream()
.map(imageRecategorizationRequest -> com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ImageRecategorizationRequest.builder()
.annotationId(imageRecategorizationRequest.getAnnotationId())
.user(KeycloakSecurity.getUserId())
.status(AnnotationStatus.APPROVED)
.typeId(toTypeId(imageRecategorizationRequest.getType(), dossier.getDossierTemplateId()))
.comment(imageRecategorizationRequest.getComment())
.build())
.collect(Collectors.toList());
List<ManualAddResponse> responseList = manualRedactionService.addImageRecategorization(dossierId, fileId, requests);
List<ManualAnnotationResponse> responseList = manualRedactionService.addRecategorization(dossierId, fileId, dossier, recategorizationRequests, true);
responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Image was recategorized")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Entity was recategorized.")
.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,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<ResizeRedactionRequest> resizeRedactionRequests) {
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
public ManualRedactionResponse recategorizeBulkLocal(String dossierId, String fileId, RecategorizationBulkLocalRequestModel recategorizationRequest) {
List<com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ResizeRedactionRequest> requests = resizeRedactionRequests.stream()
.map(resizeRedactionRequest -> com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ResizeRedactionRequest.builder()
.annotationId(resizeRedactionRequest.getAnnotationId())
.user(KeycloakSecurity.getUserId())
.status(AnnotationStatus.APPROVED)
.positions(resizeRedactionRequest.getPositions())
.value(resizeRedactionRequest.getValue())
.comment(resizeRedactionRequest.getComment())
.updateDictionary(resizeRedactionRequest.getUpdateDictionary())
.addToAllDossiers(resizeRedactionRequest.isAddToAllDossiers())
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.toList());
.collect(Collectors.toSet());
List<ManualAddResponse> responseList = manualRedactionService.addResizeRedaction(dossierId, fileId, requests);
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) {
verifyAccessAndDossierExistence(dossierId, fileId);
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")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
.build()));
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.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

@ -35,38 +35,48 @@ public class NotificationController implements NotificationResource {
return JSONPrimitive.of(notificationPersistenceService.hasNewNotificationsSince(KeycloakSecurity.getUserId(), since.getValue()));
}
@PreAuthorize("hasAuthority('" + UPDATE_NOTIFICATIONS + "')")
public void toggleNotificationSeen(@RequestBody List<String> notificationIds, @RequestParam(SET_SEEN_PARAM) boolean setSeen) {
notificationIds.stream().map(Long::valueOf).forEach(notificationId -> {
if (setSeen) {
notificationPersistenceService.setSeenDate(KeycloakSecurity.getUserId(), notificationId, OffsetDateTime.now());
} else {
notificationPersistenceService.setSeenDate(KeycloakSecurity.getUserId(), notificationId, null);
}
});
notificationIds.stream()
.map(Long::valueOf)
.forEach(notificationId -> {
if (setSeen) {
notificationPersistenceService.setSeenDate(KeycloakSecurity.getUserId(), notificationId, OffsetDateTime.now());
} else {
notificationPersistenceService.setSeenDate(KeycloakSecurity.getUserId(), notificationId, null);
}
});
}
@PreAuthorize("hasAuthority('" + UPDATE_NOTIFICATIONS + "')")
public void toggleNotificationRead(@RequestBody List<String> notificationIds, @RequestParam(SET_READ_PARAM) boolean setRead) {
notificationIds.stream().map(Long::valueOf).forEach(notificationId -> {
if (setRead) {
notificationPersistenceService.setReadDate(KeycloakSecurity.getUserId(), notificationId, OffsetDateTime.now());
} else {
notificationPersistenceService.setReadDate(KeycloakSecurity.getUserId(), notificationId, null);
}
});
notificationIds.stream()
.map(Long::valueOf)
.forEach(notificationId -> {
if (setRead) {
notificationPersistenceService.setReadDate(KeycloakSecurity.getUserId(), notificationId, OffsetDateTime.now());
} else {
notificationPersistenceService.setReadDate(KeycloakSecurity.getUserId(), notificationId, null);
}
});
}
@PreAuthorize("hasAuthority('" + UPDATE_NOTIFICATIONS + "')")
public void delete(@RequestBody List<String> notificationIds) {
notificationIds.stream().map(Long::valueOf).forEach(notificationId -> {
notificationPersistenceService.softDelete(KeycloakSecurity.getUserId(), notificationId);
});
notificationIds.stream()
.map(Long::valueOf)
.forEach(notificationId -> {
notificationPersistenceService.softDelete(KeycloakSecurity.getUserId(), notificationId);
});
}
@PreAuthorize("hasAuthority('" + READ_NOTIFICATIONS + "')")
public NotificationResponse getNotifications(@RequestParam(INCLUDE_SEEN_PARAM) boolean includeSeen) {

View File

@ -3,12 +3,12 @@ package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_NOTIFICATIONS;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.UPDATE_NOTIFICATIONS;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.service.NotificationPreferencesService;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.NotificationPreferencesPersistenceService;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
@ -21,13 +21,15 @@ import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class NotificationPreferencesController implements NotificationPreferencesResource {
private final NotificationPreferencesService notificationPreferencesService;
private final NotificationPreferencesPersistenceService notificationPreferencesPersistenceService;
@Override
@PreAuthorize("hasAuthority('" + READ_NOTIFICATIONS + "')")
public NotificationPreferences getNotificationPreferences() {
return notificationPreferencesPersistenceService.getOrCreateNotificationPreferences(KeycloakSecurity.getUserId());
return notificationPreferencesService.getOrCreateNotificationPreferences(KeycloakSecurity.getUserId());
}

View File

@ -1,138 +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.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.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
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 lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
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

@ -8,7 +8,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
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;
@ -42,40 +41,38 @@ public class ReanalysisController implements ReanalysisResource {
private final AuditPersistenceService auditPersistenceService;
private final AccessControlService accessControlService;
@PreAuthorize("hasAuthority('" + REANALYZE_DOSSIER + "')")
public void reanalyzeDossier(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force) {
try {
accessControlService.verifyUserHasViewPermissions(dossierId);
} catch (AccessDeniedException e) {
throw new NotFoundException("Object not found");
}
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserHasAccessPermissions(dossierId);
reanalysisService.reanalyzeDossier(dossierId, force);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Reanalyse dossier was triggered")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("Reanalyse dossier was triggered")
.build());
}
@PreAuthorize("hasAuthority('" + REANALYZE_FILE + "')")
public void reanalyzeFile(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
reanalysisService.reanalyzeFiles(dossierId, Sets.newHashSet(fileId), force);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOCUMENT.name())
.message("Reanalyse file was triggered")
.details(Map.of(DOSSIER_ID, dossierId))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOCUMENT.name())
.message("Reanalyse file was triggered")
.details(Map.of(DOSSIER_ID, dossierId))
.build());
}
@ -85,15 +82,16 @@ public class ReanalysisController implements ReanalysisResource {
@RequestBody List<String> fileIds,
@RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
reanalysisService.reanalyzeFiles(dossierId, new HashSet<>(fileIds), force);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOCUMENT.name())
.message("Reanalyse files was triggered")
.details(Map.of(DOSSIER_ID, dossierId, "number", fileIds.size()))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOCUMENT.name())
.message("Reanalyse files was triggered")
.details(Map.of(DOSSIER_ID, dossierId, "number", fileIds.size()))
.build());
}
@ -102,21 +100,16 @@ public class ReanalysisController implements ReanalysisResource {
@PreAuthorize("hasAuthority('" + REANALYZE_DOSSIER + "')")
public void ocrDossier(@PathVariable(DOSSIER_ID) String dossierId) {
try {
accessControlService.verifyUserHasViewPermissions(dossierId);
} catch (AccessDeniedException e) {
throw new NotFoundException("Object not found");
}
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserHasAccessPermissions(dossierId);
reanalysisService.ocrDossier(dossierId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("OCR and reanalyse dossier was triggered")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOSSIER.name())
.message("OCR and reanalyse dossier was triggered")
.build());
}
@ -125,17 +118,19 @@ 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)
.category(AuditCategory.DOCUMENT.name())
.message("OCR and reanalyse file was triggered")
.details(Map.of(DOSSIER_ID, dossierId))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOCUMENT.name())
.message("OCR and reanalyse file was triggered")
.details(Map.of(DOSSIER_ID, dossierId))
.build());
}
@ -144,32 +139,35 @@ public class ReanalysisController implements ReanalysisResource {
@PreAuthorize("hasAuthority('" + REANALYZE_FILE + "')")
public void ocrFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody Set<String> fileIds) {
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)
.category(AuditCategory.DOCUMENT.name())
.message("OCR and reanalyse was triggered")
.details(Map.of(DOSSIER_ID, dossierId, "number", fileIds.size()))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierId)
.category(AuditCategory.DOCUMENT.name())
.message("OCR and reanalyse was triggered")
.details(Map.of(DOSSIER_ID, dossierId, "number", fileIds.size()))
.build());
}
@PreAuthorize("hasAuthority('" + EXCLUDE_INCLUDE_FILE + "')")
public void toggleAutomaticAnalysis(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(EXCLUDED_STATUS_PARAM) boolean excludedFromAutomaticAnalysis) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserIsReviewer(dossierId, fileId);
fileStatusManagementService.toggleAutomaticAnalysis(dossierId, fileId, excludedFromAutomaticAnalysis);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Toggle Exclusion status: File excluded from automatic analysis: " + excludedFromAutomaticAnalysis)
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Toggle Exclusion status: File excluded from automatic analysis: " + excludedFromAutomaticAnalysis)
.build());
}
@ -180,6 +178,7 @@ public class ReanalysisController implements ReanalysisResource {
@PathVariable(FILE_ID) String fileId,
@RequestParam(name = EXCLUDED_STATUS_PARAM, required = false, defaultValue = "false") boolean excluded) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
var status = fileStatusManagementService.getFileStatus(fileId);
if (!(status.getAssignee() == null && status.isExcluded())) { // Needed to include documents after 3.0 migration.
accessControlService.verifyUserIsReviewer(dossierId, fileId);
@ -187,14 +186,15 @@ public class ReanalysisController implements ReanalysisResource {
fileStatusManagementService.toggleExclusion(dossierId, fileId, excluded);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Toggle Exclusion status: File excluded from analysis: " + excluded)
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Toggle Exclusion status: File excluded from analysis: " + excluded)
.build());
}
@PreAuthorize("hasAuthority('" + EXCLUDE_INCLUDE_FILE + "')")
public void toggleAutomaticAnalysisForList(@PathVariable(DOSSIER_ID) String dossierId,
@RequestBody Set<String> fileIds,
@ -213,6 +213,7 @@ public class ReanalysisController implements ReanalysisResource {
}
}
@PreAuthorize("hasAuthority('" + EXCLUDE_INCLUDE_FILE + "')")
public void toggleExclusionForList(@PathVariable(DOSSIER_ID) String dossierId,
@RequestBody Set<String> fileIds,
@ -247,11 +248,11 @@ public class ReanalysisController implements ReanalysisResource {
fileStatusManagementService.excludePages(dossierId, fileId, excludedPages);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Page exclusions added for file")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Page exclusions added for file")
.build());
}
@ -271,11 +272,11 @@ public class ReanalysisController implements ReanalysisResource {
fileStatusManagementService.includePages(dossierId, fileId, includePages);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Page inclusions added for file")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Page inclusions added for file")
.build());
}
@ -284,14 +285,15 @@ public class ReanalysisController implements ReanalysisResource {
@RequestParam(value = "dropIndex", required = false, defaultValue = FALSE) boolean dropIndex,
@RequestBody List<String> fileIds) {
accessControlService.checkDossierExistenceAndAccessPermissionsToDossier(dossierId);
reanalysisService.reindex(dossierId, dropIndex, new HashSet<>(fileIds));
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId("redaction")
.category(AuditCategory.INDEX.name())
.message("Reindexing has been triggered" + (dropIndex ? " (with drop index)." : "."))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId("redaction")
.category(AuditCategory.INDEX.name())
.message("Reindexing has been triggered" + (dropIndex ? " (with drop index)." : "."))
.build());
}
@ -299,6 +301,9 @@ public class ReanalysisController implements ReanalysisResource {
private void validateOCR(String dossierId, String fileId) {
var status = fileStatusManagementService.getFileStatus(fileId);
if (status.isSoftOrHardDeleted()) {
throw new NotFoundException("File does not exist");
}
if (status.getWorkflowStatus() == WorkflowStatus.APPROVED) {
throw new BadRequestException("Cannot OCR approved file");
}

View File

@ -13,6 +13,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
@ -27,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;
@ -50,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;
@ -59,12 +61,14 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
@SuppressWarnings("PMD")
public class ReportTemplateController implements ReportTemplateResource {
private final ReportTemplatePlaceholderClient reportTemplatePlaceholderClient;
private final PlaceholderClient placeholderClient;
private final AuditPersistenceService auditPersistenceService;
private final DossierAttributeConfigPersistenceService dossierAttributeConfigPersistenceService;
private final DossierTemplatePersistenceService dossierTemplatePersistenceService;
private final FileAttributeConfigPersistenceService fileAttributeConfigPersistenceService;
private final StorageService storageService;
private final ReportTemplatePersistenceService reportTemplatePersistenceService;
@ -95,21 +99,24 @@ public class ReportTemplateController implements ReportTemplateResource {
try {
if (file.getOriginalFilename() != null) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
var cleanupName = FilenameUtils.getName(file.getOriginalFilename());
ReportTemplateUploadRequest reportTemplateUploadRequest = ReportTemplateUploadRequest.builder()
.template(file.getBytes())
.fileName(file.getOriginalFilename())
.fileName(cleanupName)
.dossierTemplateId(dossierTemplateId)
.activeByDefault(activeByDefault)
.multiFileReport(multiFileReport)
.build();
var reportTemplate = reportTemplateService.uploadTemplate(reportTemplateUploadRequest);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(reportTemplate.getTemplateId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Report template was uploaded.")
.details(Map.of("DossierTemplateId", dossierTemplateId))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(reportTemplate.getTemplateId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Report template was uploaded.")
.details(Map.of("DossierTemplateId", dossierTemplateId))
.build());
return reportTemplate;
} else {
throw new BadRequestException("Could not upload file, no filename provided.");
@ -167,13 +174,14 @@ 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)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Report template was deleted.")
.details(Map.of("DossierTemplateId", dossierTemplateId))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(templateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Report template was deleted.")
.details(Map.of("DossierTemplateId", dossierTemplateId))
.build());
}

View File

@ -2,7 +2,6 @@ package com.iqser.red.persistence.service.v1.external.api.impl.controller;
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_RULES;
import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -17,19 +16,27 @@ 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.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.iqser.red.service.persistence.management.v1.processor.client.redactionservice.RedactionClient;
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.InvalidRulesException;
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.Rules;
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.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.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import feign.FeignException;
@ -44,52 +51,89 @@ public class RulesController implements RulesResource {
private static final String DOWNLOAD_FILE_NAME = "rules.drl";
private final RulesPersistenceService rulesPersistenceService;
private final RedactionClient redactionServiceClient;
private final RulesValidationService rulesValidationService;
private final AuditPersistenceService auditPersistenceService;
private final FileStatusPersistenceService fileStatusPersistenceService;
@Override
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
public void upload(@RequestBody Rules rules) {
public ResponseEntity<DroolsValidationResponse> upload(@RequestBody RulesUploadRequestModel rules) {
RulesUploadRequest rulesUploadRequest = RulesUploadRequest.fromModel(rules);
DroolsValidationResponse droolsValidationResponse = new DroolsValidationResponse();
try {
redactionServiceClient.testRules(rules.getRules());
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 InvalidRulesException("Rules could not be updated, validation check failed: " + e.getMessage());
throw new BadRequestException("The provided rule string is not a valid drools rule file!");
}
throw processFeignException(e);
}
rulesPersistenceService.setRules(rules.getRules(), rules.getDossierTemplateId());
if (!rules.isDryRun()) {
rulesPersistenceService.setRules(rulesUploadRequest.getRules(), rulesUploadRequest.getDossierTemplateId(), rulesUploadRequest.getRuleFileType());
fileStatusPersistenceService.resetErrorCounter(rules.getDossierTemplateId());
}
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(rules.getDossierTemplateId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Rules have been updated")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(rulesUploadRequest.getDossierTemplateId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message(String.format("%s Rules have been updated", rulesUploadRequest.getRuleFileType()))
.build());
return new ResponseEntity<>(droolsValidationResponse, HttpStatus.OK);
}
@Override
@PreAuthorize("hasAuthority('" + READ_RULES + "')")
public Rules download(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {
public RulesResponse download(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {
return new Rules(rulesPersistenceService.getRules(dossierTemplateId).getValue(), dossierTemplateId);
return download(dossierTemplateId, RuleFileType.ENTITY);
}
@Override
@PreAuthorize("hasAuthority('" + READ_RULES + "')")
public RulesResponse download(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType) {
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 void uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @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);
}
@Override
@PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
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 {
upload(new Rules(new String(file.getBytes(), StandardCharsets.UTF_8), dossierTemplateId));
return upload(new RulesUploadRequestModel(new String(file.getBytes(), StandardCharsets.UTF_8), dossierTemplateId, ruleFileType, dryRun));
} catch (IOException e) {
throw new FileUploadException("Could not upload file.", e);
}
}
@ -97,15 +141,31 @@ public class RulesController implements RulesResource {
@PreAuthorize("hasAuthority('" + READ_RULES + "')")
public ResponseEntity<?> downloadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {
byte[] data = download(dossierTemplateId).getRules().getBytes(StandardCharsets.UTF_8);
return downloadFile(dossierTemplateId, RuleFileType.ENTITY);
}
@Override
@PreAuthorize("hasAuthority('" + READ_RULES + "')")
public ResponseEntity<?> downloadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType) {
byte[] data = download(dossierTemplateId, ruleFileType).getRules().getBytes(StandardCharsets.UTF_8);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.TEXT_PLAIN);
httpHeaders.add("Content-Disposition", "attachment; filename*=utf-8\"" + DOWNLOAD_FILE_NAME + "\"");
httpHeaders.add("Content-Disposition", "attachment; filename*=utf-8''" + ruleFileType.name() + "_" + DOWNLOAD_FILE_NAME);
InputStream is = new ByteArrayInputStream(data);
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

@ -1,6 +1,9 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.*;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_FILE_STATUS;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.SET_REVIEWER;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.SET_STATUS_APPROVED;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.SET_STATUS_UNDER_APPROVAL;
import java.time.OffsetDateTime;
import java.util.ArrayList;
@ -11,38 +14,50 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import jakarta.transaction.Transactional;
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.roles.ApplicationRoles;
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.users.UserService;
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;
import com.iqser.red.service.persistence.service.v1.api.external.resource.StatusResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
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.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;
@ -62,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
@ -77,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) {
@ -88,7 +127,7 @@ public class StatusController implements StatusResource {
List<FileStatus> statusList = fileStatusManagementService.getDossierStatus(dossierId)
.stream()
.filter(fileStatus -> !fileStatus.isSoftOrHardDeleted())
.map(this::convert)
.map(FileStatusMapper::toFileStatus)
.collect(Collectors.toList());
response.put(dossierId, statusList);
} catch (AccessDeniedException e) {
@ -105,7 +144,6 @@ public class StatusController implements StatusResource {
@Transactional
public Map<String, List<FileStatus>> getSoftDeletedFilesForDossiers(@RequestBody List<String> dossierIds) {
List<FileStatus> statusList;
List<String> dossiersWithViewPermissions = new ArrayList<>();
for (var dossierId : dossierIds) {
try {
@ -118,9 +156,11 @@ public class StatusController implements StatusResource {
if (dossiersWithViewPermissions.isEmpty()) {
return new HashMap<>();
}
statusList = fileStatusManagementService.getSoftDeletedForDossierList(dossiersWithViewPermissions).stream().map(this::convert).collect(Collectors.toList());
return fileStatusManagementService.getSoftDeletedForDossierList(dossiersWithViewPermissions)
.stream()
.map(FileStatusMapper::toFileStatus)
.collect(Collectors.groupingBy(FileStatus::getDossierId, Collectors.toList()));
return statusList.stream().collect(Collectors.groupingBy(FileStatus::getDossierId, Collectors.toList()));
}
@ -133,7 +173,7 @@ public class StatusController implements StatusResource {
return fileStatusManagementService.getDossierStatus(dossierId)
.stream()
.filter(fileStatus -> !fileStatus.isSoftOrHardDeleted())
.map(this::convert)
.map(FileStatusMapper::toFileStatus)
.collect(Collectors.toList());
} catch (AccessDeniedException e) {
@ -142,10 +182,12 @@ public class StatusController implements StatusResource {
}
@PreAuthorize("hasAuthority('" + READ_FILE_STATUS + "')")
public FileStatus getFileStatus(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
return convert(fileStatusManagementService.getFileStatus(fileId));
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
return FileStatusMapper.toFileStatus(fileStatusManagementService.getFileStatus(fileId));
}
@ -155,6 +197,7 @@ public class StatusController implements StatusResource {
@PathVariable(FILE_ID) String fileId,
@RequestParam(name = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyUserIsMemberOrApprover(dossierId);
log.debug("Requested [setFileReviewer] for dossier: {} / file: {} / reviewer: {}", dossierId, fileId, assigneeId);
@ -168,39 +211,39 @@ public class StatusController implements StatusResource {
fileStatusManagementService.setCurrentFileAssignee(dossierId, fileId, assigneeId);
if (assigneeId == null) {
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Reviewer was unassigned from document")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Reviewer was unassigned from document")
.details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
.build());
} else {
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Reviewer was assigned to document")
.details(Map.of(DOSSIER_ID, dossierId, "reviewer", assigneeId))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Reviewer was assigned to document")
.details(Map.of(DOSSIER_ID, dossierId, "reviewer", assigneeId))
.build());
}
if (assigneeId != null && !assigneeId.equals(KeycloakSecurity.getUserId())) {
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(assigneeId)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.ASSIGN_REVIEWER.name())
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
.build());
.userId(assigneeId)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(WorkflowStatus.APPROVED.equals(fileStatus.getWorkflowStatus()) ? NotificationType.ASSIGN_APPROVER.name() : NotificationType.ASSIGN_REVIEWER.name())
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
.build());
}
if (assigneeId == null || fileStatus.getAssignee() != null && !fileStatus.getAssignee().equals(assigneeId) && !KeycloakSecurity.getUserId()
.equals(fileStatus.getAssignee())) {
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(fileStatus.getAssignee())
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.UNASSIGNED_FROM_FILE.name())
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
.build());
.userId(fileStatus.getAssignee())
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.UNASSIGNED_FROM_FILE.name())
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
.build());
}
}
@ -215,7 +258,9 @@ public class StatusController implements StatusResource {
throw new BadRequestException("Unknown user=" + assigneeId);
}
// check he has a manager role, thus he can be the owner
if (user.get().getRoles().stream().noneMatch(VALID_MEMBER_ROLES::contains)) {
if (user.get().getRoles()
.stream()
.noneMatch(VALID_MEMBER_ROLES::contains)) {
throw new BadRequestException("Make sure each provided member id has the permission to be a member of a dossier.");
}
// check assignee is a member
@ -234,83 +279,101 @@ public class StatusController implements StatusResource {
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
var fileStatus = fileStatusManagementService.getFileStatus(fileId);
setStatusUnderReviewForFile(dossierId, fileId, assigneeId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Document status was changed to Under Review")
.details(Map.of(DOSSIER_ID, dossierId))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Document status was changed to Under Review")
.details(Map.of(DOSSIER_ID, dossierId))
.build());
if (assigneeId != null && !assigneeId.equals(KeycloakSecurity.getUserId())) {
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(assigneeId)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.ASSIGN_REVIEWER.name())
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
.build());
.userId(assigneeId)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.ASSIGN_REVIEWER.name())
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
.build());
}
generatePossibleUnassignedFromFileNotification(dossierId, fileId, fileStatus, assigneeId);
}
@PreAuthorize("hasAuthority('" + SET_STATUS_UNDER_APPROVAL + "')")
public void setStatusUnderApproval(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(name = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
var fileStatus = fileStatusManagementService.getFileStatus(fileId);
setStatusUnderApprovalForFile(dossierId, fileId, assigneeId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Document status was changed to Under Approval")
.details(Map.of(DOSSIER_ID, dossierId))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Document status was changed to Under Approval")
.details(Map.of(DOSSIER_ID, dossierId))
.build());
if (assigneeId != null && !assigneeId.equals(KeycloakSecurity.getUserId())) {
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(assigneeId)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.ASSIGN_APPROVER.name())
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
.build());
.userId(assigneeId)
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.ASSIGN_APPROVER.name())
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, fileStatus.getFilename()))
.build());
}
generatePossibleUnassignedFromFileNotification(dossierId, fileId, fileStatus, assigneeId);
}
@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;
}
@ -344,14 +407,19 @@ public class StatusController implements StatusResource {
private void generatePossibleUnassignedFromFileNotification(String dossierId, String fileId, FileModel oldFileStatus, String newAssigneeId) {
if (oldFileStatus.getAssignee() != null && !oldFileStatus.getAssignee().equals(newAssigneeId) && !KeycloakSecurity.getUserId().equals(oldFileStatus.getAssignee())) {
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(oldFileStatus.getAssignee())
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.UNASSIGNED_FROM_FILE.name())
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, oldFileStatus.getFilename()))
.build());
if (oldFileStatus.getAssignee() == null
|| newAssigneeId == null && oldFileStatus.getAssignee() == null
|| oldFileStatus.getAssignee().equals(newAssigneeId)
|| KeycloakSecurity.getUserId().equals(oldFileStatus.getAssignee())) {
return;
}
notificationPersistenceService.insertNotification(AddNotificationRequest.builder()
.userId(oldFileStatus.getAssignee())
.issuerId(KeycloakSecurity.getUserId())
.notificationType(NotificationType.UNASSIGNED_FROM_FILE.name())
.target(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, FILE_NAME, oldFileStatus.getFilename()))
.build());
}
@ -382,18 +450,29 @@ 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;
}
@PreAuthorize("hasAuthority('" + SET_REVIEWER + "')")
public void setStatusNewForList(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody List<String> fileIds) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
for (var fileId : fileIds) {
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
var fileStatus = fileStatusManagementService.getFileStatus(fileId);
@ -409,12 +488,16 @@ public class StatusController implements StatusResource {
}
@PreAuthorize("hasAuthority('" + READ_FILE_STATUS + "')")
public List<FileStatus> getSoftDeletedDossierStatus(@PathVariable(DOSSIER_ID) String dossierId) {
try {
accessControlService.verifyUserHasViewPermissions(dossierId);
return fileStatusManagementService.getSoftDeletedDossierStatus(dossierId).stream().map(this::convert).collect(Collectors.toList());
return fileStatusManagementService.getSoftDeletedDossierStatus(dossierId)
.stream()
.map(FileStatusMapper::toFileStatus)
.collect(Collectors.toList());
} catch (AccessDeniedException e) {
return new ArrayList<>();
@ -434,62 +517,4 @@ public class StatusController implements StatusResource {
fileStatusManagementService.setStatusApproved(dossierId, fileId, KeycloakSecurity.getUserId());
}
private FileStatus convert(FileModel status) {
return FileStatus.builder()
.dossierId(status.getDossierId())
.dossierArchived(status.isDossierArchived())
.dossierStatusId(status.getDossierStatusId())
.dossierTemplateId(status.getDossierTemplateId())
.fileId(status.getId())
.filename(status.getFilename())
.processingStatus(ProcessingStatus.valueOf(status.getProcessingStatus().name()))
.workflowStatus(WorkflowStatus.valueOf(status.getWorkflowStatus().name()))
.numberOfPages(status.getNumberOfPages())
.added(status.getAdded())
.lastUpdated(status.getLastUpdated())
.numberOfAnalyses(status.getNumberOfAnalyses())
.assignee(status.getAssignee())
.lastReviewer(status.getLastReviewer())
.lastApprover(status.getLastApprover())
.hasUpdates(status.isHasUpdates())
.hasImages(status.isHasImages())
.hasRequests(status.isHasSuggestions())
.hasSuggestions(status.isHasSuggestions())
.excludedFromAutomaticAnalysis(status.isExcludedFromAutomaticAnalysis())
.hasHints(status.isHasHints())
.hasRedactions(status.isHasRedactions())
.ocrEndTime(status.getOcrEndTime())
.ocrStartTime(status.getOcrStartTime())
.numberOfOCRedPages(status.getNumberOfOCRedPages() != null ? status.getNumberOfOCRedPages() : 0)
.numberOfPagesToOCR(status.getNumberOfPagesToOCR() != null ? status.getNumberOfPagesToOCR() : 0)
.hasAnnotationComments(status.isHasAnnotationComments())
.uploader(status.getUploader())
.dictionaryVersion(status.getDictionaryVersion())
.rulesVersion(status.getRulesVersion())
.legalBasisVersion(status.getLegalBasisVersion())
.lastProcessed(status.getLastProcessed())
.lastLayoutProcessed(status.getLastLayoutProcessed())
.approvalDate(status.getApprovalDate())
.lastUploaded(status.getLastUploaded())
.analysisDuration(status.getAnalysisDuration())
.fileAttributes(new FileAttributes(status.getFileAttributes()))
.dossierDictionaryVersion(status.getDossierDictionaryVersion())
.excluded(status.isExcluded())
.excludedPages(status.getExcludedPages())
.softDeletedTime(status.getDeleted())
.analysisRequired(status.isAnalysisRequired())
.lastFileAttributeChange(status.getLastFileAttributeChange())
.redactionModificationDate(status.getRedactionModificationDate())
.fileManipulationDate(status.getFileManipulationDate())
.lastManualChangeDate(status.getLastManualChangeDate())
.hasHighlights(status.isHasHighlights())
.lastIndexed(status.getLastIndexed())
.fileSize(status.getFileSize())
.fileErrorInfo(status.getFileErrorInfo())
.build();
}
}

View File

@ -1,6 +1,5 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_DOSSIER;
import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;
@ -41,11 +40,7 @@ public class StatusReportController implements StatusReportResource {
@PreAuthorize("hasAuthority('" + READ_DOSSIER + "')")
public ResponseEntity<?> generateStatusReport(@PathVariable(DOSSIER_ID) String dossierId) {
try {
accessControlService.verifyUserHasViewPermissions(dossierId);
} catch (AccessDeniedException e) {
throw new NotFoundException("Object not found");
}
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
try {
StatusReportResponse statusReportResponse = statusReportClient.generateStatusReport(dossierId);

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

@ -1,5 +1,6 @@
package com.iqser.red.persistence.service.v1.external.api.impl.controller;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.UPLOAD_FILE;
import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;
import java.io.ByteArrayOutputStream;
@ -7,7 +8,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@ -15,16 +16,18 @@ import java.util.UUID;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.io.IOUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
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;
@ -33,9 +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;
@ -44,144 +50,181 @@ import lombok.extern.slf4j.Slf4j;
@RestController
@RequiredArgsConstructor
@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 List<String> VALID_FILE_EXTENSIONS = List.of("pdf", "docx", "doc", "xls", "xlsx", "ppt", "pptx");
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;
private final AccessControlService accessControlService;
private final AuditPersistenceService auditPersistenceService;
private final FileFormatValidationService fileFormatValidationService;
@Timed
@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) {
if (file.getOriginalFilename() == null) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
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 (VALID_FILE_EXTENSIONS.contains(extension)) {
return uploadService.processSingleFile(dossierId, file.getOriginalFilename(), file.getBytes(), keepManualRedactions);
} else {
throw new BadRequestException("Invalid file uploaded");
}
}
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);
}
}
@PreAuthorize("hasAuthority('" + UPLOAD_FILE + "')")
public void importRedactions(@RequestPart(name = "file") MultipartFile file,
@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "pageInclusionRequest", required = false) Set<Integer> pageInclusionRequest) {
accessControlService.checkAccessPermissionsToDossier(dossierId);
accessControlService.verifyFileIsNotApproved(dossierId, fileId);
accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);
try {
reanalysisService.importRedactions(ByteContentDocument.builder().dossierId(dossierId).fileId(fileId).document(file.getBytes()).pages(pageInclusionRequest).build());
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Redactions were imported")
.details(Map.of("dossierId", dossierId))
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(fileId)
.category(AuditCategory.DOCUMENT.name())
.message("Redactions were imported")
.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();
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.");
} else {
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);
}
}
}
@ -189,65 +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());
final String fileName;
if (ze.getName().lastIndexOf("/") >= 0) {
fileName = ze.getName().substring(ze.getName().lastIndexOf("/") + 1);
} else {
fileName = ze.getName();
}
long compressedSize = entry.getCompressedSize() > 0 ? entry.getCompressedSize() : 1;
try (var is = zipFile.getInputStream(entry); var bos = new ByteArrayOutputStream()) {
if (fileName.startsWith(".")) {
return;
}
byte[] buffer = new byte[4096];
int bytesRead;
int totalUncompressed = 0;
var entryAsBytes = readCurrentZipEntry(ze, zipFile);
zipData.totalSizeArchive += entryAsBytes.length;
while ((bytesRead = is.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
totalUncompressed += bytesRead;
// 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 (VALID_FILE_EXTENSIONS.contains(extension)) {
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);
}
@ -256,8 +304,8 @@ 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

@ -11,10 +11,12 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.management.v1.processor.service.DictionaryManagementService;
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;
import com.iqser.red.service.persistence.service.v1.api.shared.model.VersionsResponse;
import lombok.RequiredArgsConstructor;
@ -25,6 +27,8 @@ public class VersionsController implements VersionsResource {
private final DictionaryPersistenceService dictionaryPersistenceService;
private final RulesPersistenceService rulesPersistenceService;
private final AccessControlService accessControlService;
private final LegalBasisMappingPersistenceService legalBasisMappingPersistenceService;
@Override
@ -33,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.getRules(rsId).getVersion());
VersionsResponse response = new VersionsResponse(dictionaryPersistenceService.getVersion(rsId),
rulesPersistenceService.getVersion(rsId, RuleFileType.ENTITY),
legalBasisMappingPersistenceService.getVersion(rsId));
result.put(rsId, response);
});
@ -45,7 +51,10 @@ public class VersionsController implements VersionsResource {
@PreAuthorize("hasAuthority('" + READ_VERSIONS + "')")
public Long getDossierDictionaryVersion(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @PathVariable(DOSSIER_ID_PARAM) String dossierId) {
return dictionaryPersistenceService.getVersionForDossier(dossierId);
if (accessControlService.hasUserViewPermissionsForDossier(dossierId)) {
return dictionaryPersistenceService.getVersionForDossier(dossierId);
}
return 0L;
}
}

View File

@ -31,34 +31,30 @@ public class ViewedPagesController implements ViewedPagesResource {
private final AccessControlService accessControlService;
private final ViewedPagesPersistenceService viewedPagesPersistenceService;
private final FileStatusService fileStatusService;
private final AnalysisFlagsCalculationService analysisFlagsCalculationService;
@PreAuthorize("hasAuthority('" + MANAGE_VIEWED_PAGES + "')")
public void addPage(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody ViewedPagesRequest viewedPagesRequest) {
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
accessControlService.verifyUserIsReviewer(dossierId, fileId);
viewedPagesPersistenceService.insertPage(fileId, KeycloakSecurity.getUserId(), viewedPagesRequest.getPage());
var file = fileStatusService.getStatus(fileId);
analysisFlagsCalculationService.calculateFlags(file.getDossierId(), fileId);
}
@PreAuthorize("hasAuthority('" + MANAGE_VIEWED_PAGES + "')")
public void removePage(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @PathVariable(PAGE) int page) {
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
accessControlService.verifyUserIsReviewer(dossierId, fileId);
viewedPagesPersistenceService.removePage(fileId, KeycloakSecurity.getUserId(), page);
var file = fileStatusService.getStatus(fileId);
analysisFlagsCalculationService.calculateFlags(file.getDossierId(), fileId);
}
@PreAuthorize("hasAuthority('" + MANAGE_VIEWED_PAGES + "')")
public ViewedPages getViewedPages(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {
accessControlService.checkDossierExistenceAndViewPermissionsToDossier(dossierId);
accessControlService.verifyUserIsReviewer(dossierId, fileId);
try {
var pages = MagicConverter.convert(viewedPagesPersistenceService.findViewedPages(fileId, KeycloakSecurity.getUserId()), ViewedPage.class);

View File

@ -40,11 +40,11 @@ public class WatermarkController implements WatermarkResource {
watermark.setCreatedBy(userId);
WatermarkModel result = MagicConverter.convert(watermarkService.createOrUpdateWatermark(watermark), WatermarkModel.class);
auditPersistenceService.audit(AuditRequest.builder()
.userId(userId)
.objectId(result.getDossierTemplateId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Watermark has been changed.")
.build());
.userId(userId)
.objectId(result.getDossierTemplateId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Watermark has been changed.")
.build());
return result;
}
@ -72,11 +72,11 @@ public class WatermarkController implements WatermarkResource {
String dossierTemplateId = watermarkService.getWatermark(watermarkId).getDossierTemplateId();
watermarkService.deleteWatermark(watermarkId);
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Watermark has been deleted.")
.build());
.userId(KeycloakSecurity.getUserId())
.objectId(dossierTemplateId)
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message("Watermark has been deleted.")
.build());
}

View File

@ -11,7 +11,6 @@ import com.iqser.red.persistence.service.v1.external.api.impl.model.OneTimeToken
@Service
public class OneTimeTokenCacheService {
@Cacheable(value = OTT_CACHE, key = "#tokenId")
public OneTimeToken cacheOTT(String tokenId, OneTimeToken token) {
// empty

View File

@ -0,0 +1,15 @@
plugins {
id("com.iqser.red.service.java-conventions")
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

@ -0,0 +1,10 @@
package com.iqser.red.persistence.service.v2.external.api.impl;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class PersistenceServiceExternalApiConfigurationV2 {
}

View File

@ -0,0 +1,202 @@
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.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.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.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.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.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;
import lombok.experimental.FieldDefaults;
@RestController
@RequiredArgsConstructor
@Tag(name = "4. Component endpoints", description = "Provides operations related to components")
public class ComponentControllerV2 implements ComponentResource {
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
public FileComponents getComponents(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@PathVariable(FILE_ID_PARAM) String fileId,
@RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails) {
checkApplicationType();
validateUserRoles(KeycloakSecurity.getUserId());
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
var componentLog = componentLogService.getComponentLog(dossierId, fileId);
return componentMapper.toFileComponents(componentLog, dossierTemplateId, dossierId, fileId, fileStatusService.getFileName(fileId), includeDetails);
}
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");
}
}
}
@Override
public FileComponentsList getComponentsOfDossier(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestParam(name = INCLUDE_DETAILS_PARAM, defaultValue = "false", required = false) boolean includeDetails) {
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

@ -0,0 +1,205 @@
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 {
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,
@RequestParam(name = INCLUDE_ACTIVE_PARAM, defaultValue = "true", required = false) boolean includeActive,
@RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived,
@RequestParam(name = INCLUDE_SOFT_DELETED_PARAM, defaultValue = "false", required = false) boolean includeSoftDeleted) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
var dossiers = dossierController.getDossiersForDossierTemplate(dossierTemplateId, includeArchived, includeSoftDeleted);
if (!includeActive) {
return new DossierList(dossiers.stream()
.filter(dossier -> dossier.getSoftDeletedTime() != null || dossier.getArchivedTime() != null)
.toList());
}
return new DossierList(dossiers);
}
public Dossier getDossier(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestParam(name = INCLUDE_SOFT_DELETED_PARAM, defaultValue = "false", required = false) boolean includeSoftDeleted) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
return dossierController.getDossier(dossierId, true, includeSoftDeleted);
}
public ResponseEntity<Dossier> createDossierOrUpdateDossier(@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,
@RequestBody DocuMineDossierRequest dossier) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
return dossierController.createDossierOrUpdateDossier(mapToDossierRequest(dossierTemplateId, dossier));
}
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,
@RequestParam(name = DELETE_PERMANENTLY_PARAM, defaultValue = "false", required = false) boolean deletePermanently) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
if (deletePermanently) {
dossierController.hardDeleteDossiers(Sets.newHashSet(dossierId));
} else {
dossierController.deleteDossier(dossierId);
}
}
@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())
.dossierName(dossier.getName())
.dossierTemplateId(dossierTemplateId)
.description(dossier.getDescription())
.ownerId(dossier.getOwnerId())
.memberIds(dossier.getMemberIds())
.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)
.previewWatermarkId(null)
.dossierStatusId(dossier.getDossierStatusId())
.build();
}
}

View File

@ -0,0 +1,656 @@
package com.iqser.red.persistence.service.v2.external.api.impl.controller;
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;
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.RequestParam;
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.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.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";
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() {
return dossierTemplateController.getAllDossierTemplates();
}
public DossierTemplateModel getDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) {
return dossierTemplateController.getDossierTemplate(dossierTemplateId);
}
@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) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
return uploadRules(dossierTemplateId, RuleFileType.ENTITY, file, dryRun);
}
@PreAuthorize("hasAuthority('" + READ_RULES + "')")
public ResponseEntity<?> downloadEntityRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
return downloadRules(dossierTemplateId, RuleFileType.ENTITY);
}
@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) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
return uploadRules(dossierTemplateId, RuleFileType.COMPONENT, file, dryRun);
}
@PreAuthorize("hasAuthority('" + READ_RULES + "')")
public ResponseEntity<?> downloadComponentRules(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String 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) {
dossierTemplatePersistenceService.checkDossierTemplateExistsOrElseThrow404(dossierTemplateId);
var fileAttributeConfigs = fileAttributesController.getFileAttributesConfiguration(dossierTemplateId);
var csvImportSettings = FileAttributeDefinitionList.CsvImportSettings.builder()
.encoding(fileAttributeConfigs.getEncoding())
.delimiter(fileAttributeConfigs.getDelimiter())
.filenameMappingCsvColumnHeader(fileAttributeConfigs.getFilenameMappingColumnHeaderName())
.build();
var fileAttributeDefinitions = fileAttributeConfigs.getFileAttributeConfigs()
.stream()
.map(fileAttributeConfig -> FileAttributeDefinition.builder()
.id(fileAttributeConfig.getId())
.name(fileAttributeConfig.getLabel())
.type(fileAttributeConfig.getType())
.mappedCsvColumnHeader(fileAttributeConfig.getCsvColumnHeader())
.reportingPlaceholder(fileAttributeConfig.getPlaceholder())
.displaySettings(FileAttributeDefinition.DisplaySettings.builder()
.primaryAttribute(fileAttributeConfig.isPrimaryAttribute())
.editable(fileAttributeConfig.isEditable())
.filterable(fileAttributeConfig.isFilterable())
.displayedInFileList(fileAttributeConfig.isDisplayedInFileList())
.build())
.includeInCsvExport(fileAttributeConfig.isIncludeInCsvExport())
.build())
.toList();
return new FileAttributeDefinitionList(csvImportSettings, fileAttributeDefinitions);
}
@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
@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();
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()) {
throw new BadRequestException("The provided rule string is not a valid drools rule file!");
}
}
if (!dryRun) {
rulesPersistenceService.setRules(rulesUploadRequest.getRules(), rulesUploadRequest.getDossierTemplateId(), rulesUploadRequest.getRuleFileType());
}
auditPersistenceService.audit(AuditRequest.builder()
.userId(KeycloakSecurity.getUserId())
.objectId(rulesUploadRequest.getDossierTemplateId())
.category(AuditCategory.DOSSIER_TEMPLATE.name())
.message(format("%s rules have been %s", rulesUploadRequest.getRuleFileType(), dryRun ? "validated" : "updated"))
.build());
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

@ -0,0 +1,209 @@
package com.iqser.red.persistence.service.v2.external.api.impl.controller;
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.DossierResource.INCLUDE_ACTIVE_PARAM;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierResource.INCLUDE_ARCHIVED_PARAM;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierResource.INCLUDE_SOFT_DELETED_PARAM;
import static com.iqser.red.service.persistence.service.v2.api.external.resource.DossierTemplateResource.DOSSIER_TEMPLATE_ID_PARAM;
import java.util.ArrayList;
import java.util.List;
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.RequestPart;
import org.springframework.web.bind.annotation.RestController;
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;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
@Tag(name = "3. File endpoints", description = "Provides operations related to files")
public class FileControllerV2 implements FileResource {
private final UploadController uploadController;
private final StatusController statusController;
private final DossierController dossierController;
private final FileManagementController fileManagementController;
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 = DISABLE_AUTOMATIC_ANALYSIS_PARAM, required = false, defaultValue = "false") boolean disableAutomaticAnalysis) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
return uploadController.upload(file, dossierId, keepManualChanges, disableAutomaticAnalysis);
}
public FileStatusList getDossierStatus(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestParam(name = INCLUDE_ACTIVE_PARAM, defaultValue = "true", required = false) boolean includeActive,
@RequestParam(name = INCLUDE_ARCHIVED_PARAM, defaultValue = "false", required = false) boolean includeArchived,
@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, false, includeSoftDeleted).getArchivedTime() != null) {
return new FileStatusList(fileStatusList);
}
if (includeActive) {
fileStatusList.addAll(statusController.getDossierStatus(dossierId));
}
if (includeSoftDeleted) {
fileStatusList.addAll(statusController.getSoftDeletedDossierStatus(dossierId));
}
return new FileStatusList(fileStatusList);
}
public FileStatus getFileStatus(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@PathVariable(FILE_ID_PARAM) String fileId,
@RequestParam(name = INCLUDE_SOFT_DELETED_PARAM, defaultValue = "false", required = false) boolean includeSoftDeleted) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
dossierController.getDossier(dossierId, true, includeSoftDeleted);
return FileStatusMapper.toFileStatus(fileStatusManagementService.getFileStatus(fileId, includeSoftDeleted));
}
public void deleteFile(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@PathVariable(FILE_ID_PARAM) String fileId,
@RequestParam(name = DELETE_PERMANENTLY_PARAM, defaultValue = "false", required = false) boolean deletePermanently) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
if (deletePermanently) {
fileManagementController.hardDeleteFiles(dossierId, List.of(fileId));
} else {
fileManagementController.deleteFile(dossierId, fileId);
}
}
public void deleteFiles(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@RequestBody FileDeleteRequest fileDeleteRequest,
@RequestParam(name = DELETE_PERMANENTLY_PARAM, defaultValue = "false", required = false) boolean deletePermanently) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
if (deletePermanently) {
fileManagementController.hardDeleteFiles(dossierId, new ArrayList<>(fileDeleteRequest.getFileIds()));
} else {
fileManagementController.deleteFiles(dossierId, fileDeleteRequest.getFileIds());
}
}
public void setFileAttributes(@PathVariable(DOSSIER_TEMPLATE_ID_PARAM) String dossierTemplateId,
@PathVariable(DOSSIER_ID_PARAM) String dossierId,
@PathVariable(FILE_ID_PARAM) String fileId,
@RequestBody FileAttributes fileAttributes) {
dossierTemplateController.getDossierTemplate(dossierTemplateId);
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

@ -0,0 +1,27 @@
package com.iqser.red.persistence.service.v2.external.api.impl.controller;
import com.iqser.red.persistence.service.v1.external.api.impl.controller.LicenseReportController;
import com.iqser.red.service.persistence.service.v1.api.shared.model.license.LicenseReport;
import com.iqser.red.service.persistence.service.v1.api.shared.model.license.LicenseReportRequest;
import com.iqser.red.service.persistence.service.v2.api.external.resource.LicenseResource;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@Tag(name = "7. License", description = "Operations related to license information and usage metrics.")
public class LicenseControllerV2 implements LicenseResource {
private final LicenseReportController licenseReportController;
public LicenseReport getReport(@RequestBody LicenseReportRequest reportRequest) {
return licenseReportController.getReport(reportRequest);
}
}

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

@ -0,0 +1,16 @@
package com.iqser.red.persistence.service.v2.external.api.impl;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class IdentityTest {
@Test
public void mockTest() {
int i = 1;
assertThat(i).isEqualTo(1);
}
}

View File

@ -0,0 +1,37 @@
plugins {
id("com.iqser.red.service.java-conventions")
}
dependencies {
api(project(":persistence-service-internal-api-v1"))
api("com.iqser.red.service:pdftron-redaction-service-api-v1:${rootProject.extra.get("pdftronRedactionServiceVersion")}") {
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.iqser.red.service:redaction-service-api-v1:${rootProject.extra.get("redactionServiceVersion")}") {
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.iqser.red.service:redaction-report-service-api-v1:${rootProject.extra.get("redactionReportServiceVersion")}") {
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.iqser.red.service:search-service-api-v1:${rootProject.extra.get("searchServiceVersion")}") {
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.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")
api("com.iqser.red.commons:jackson-commons:2.1.0")
api(project(":persistence-service-shared-api-v1"))
testImplementation("com.iqser.red.commons:test-commons:2.1.0")
testImplementation("org.springframework.boot:spring-boot-starter-test:3.0.4")
compileOnly("org.springdoc:springdoc-openapi-ui:1.6.13")
api("io.github.openfeign:feign-core:12.2")
compileOnly("org.springframework:spring-web:6.0.6")
}
description = "persistence-service-external-api-v1"

View File

@ -1,141 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>persistence-service-v1</artifactId>
<groupId>com.iqser.red.service</groupId>
<version>2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>persistence-service-external-api-v1</artifactId>
<dependencies>
<dependency>
<groupId>com.iqser.red.service</groupId>
<artifactId>persistence-service-internal-api-v1</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>com.iqser.red.service</groupId>
<artifactId>redaction-service-api-v1</artifactId>
</exclusion>
<exclusion>
<groupId>com.iqser.red.service</groupId>
<artifactId>pdftron-redaction-service-api-v1</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.iqser.red.service</groupId>
<artifactId>pdftron-redaction-service-api-v1</artifactId>
<exclusions>
<exclusion>
<groupId>com.iqser.red.service</groupId>
<artifactId>redaction-service-api-v1</artifactId>
</exclusion>
<exclusion>
<groupId>com.iqser.red.service</groupId>
<artifactId>persistence-service-api-v1</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.iqser.red.service</groupId>
<artifactId>redaction-service-api-v1</artifactId>
<exclusions>
<exclusion>
<groupId>com.iqser.red.service</groupId>
<artifactId>pdftron-redaction-service-api-v1</artifactId>
</exclusion>
<exclusion>
<groupId>com.iqser.red.service</groupId>
<artifactId>persistence-service-api-v1</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.iqser.red.service</groupId>
<artifactId>redaction-report-service-api-v1</artifactId>
<exclusions>
<exclusion>
<groupId>com.iqser.red.service</groupId>
<artifactId>persistence-service-api-v1</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.iqser.red.service</groupId>
<artifactId>search-service-api-v1</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.13.4</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!-- spring -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.iqser.red.commons</groupId>
<artifactId>jackson-commons</artifactId>
</dependency>
<!-- test -->
<dependency>
<groupId>com.iqser.red.commons</groupId>
<artifactId>test-commons</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.iqser.red.service</groupId>
<artifactId>persistence-service-shared-api-v1</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

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,6 +1,5 @@
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;

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,15 +17,18 @@ 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;
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;
@ -43,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";
@ -63,7 +72,7 @@ public interface DictionaryResource {
@ResponseStatus(HttpStatus.NO_CONTENT)
@PostMapping(value = DICTIONARY_REST_PATH + TYPE_PATH_VARIABLE + DOSSIER_TEMPLATE_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Add dictionary entries with entry type.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully added the dictionary entries."), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The " + "entry type is not found.")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully added 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 addEntry(@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestBody List<String> entries,
@ -75,7 +84,7 @@ public interface DictionaryResource {
@ResponseStatus(HttpStatus.NO_CONTENT)
@PostMapping(value = DICTIONARY_REST_PATH + DELETE + TYPE_PATH_VARIABLE + DOSSIER_TEMPLATE_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Delete dictionary entries with entry type.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully deleted the dictionary entries."), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The " + "entry type is not found.")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully deleted 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 deleteEntries(@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestBody List<String> entries,
@ -86,7 +95,7 @@ public interface DictionaryResource {
@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping(value = DICTIONARY_REST_PATH + TYPE_PATH_VARIABLE + DOSSIER_TEMPLATE_PATH_VARIABLE + ENTRY_PATH_VARIABLE)
@Operation(summary = "Delete dictionary entry with entry type.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully deleted the dictionary entry."), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The " + "entry type is not found.")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully deleted the dictionary entry."), @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 deleteEntry(@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@PathVariable(ENTRY_PARAMETER_NAME) String entry,
@ -94,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")
@ -106,7 +126,8 @@ public interface DictionaryResource {
@ResponseStatus(HttpStatus.OK)
@PostMapping(value = TYPE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Creates entry type with colors, hint and caseInsensitive", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully created the entry type with colors, hint " + "and caseInsensitive"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "409", description = "The entry type already exists, could not be added again.")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully created the entry type with colors, hint "
+ "and caseInsensitive"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "409", description = "The entry type already exists, could not be added again.")})
TypeValue addType(@RequestBody CreateTypeValue typeValue);
@ -114,21 +135,19 @@ public interface DictionaryResource {
@DeleteMapping(value = TYPE_PATH + TYPE_PATH_VARIABLE + DOSSIER_TEMPLATE_PATH_VARIABLE)
@Operation(summary = "Deletes entry type", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully deleted the entry type with value and all its entries"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The entry type is not found.")})
void deleteType(@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId);
void deleteType(@PathVariable(TYPE_PARAMETER_NAME) String type, @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId);
@ResponseStatus(HttpStatus.NO_CONTENT)
@PostMapping(value = TYPE_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE + DELETE)
@Operation(summary = "Deletes entry types", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully deleted the entry types with value and all their entries"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The entry type is not found.")})
void deleteTypes(@RequestBody List<String> types,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId);
void deleteTypes(@RequestBody List<String> types, @PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId);
@GetMapping(value = TYPE_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Retrieve all entry types", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully retrieved all the entry types")})
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully retrieved all the entry types"), @ApiResponse(responseCode = "404", description = "Not found")})
TypeResponse getAllTypes(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId,
@RequestParam(value = INCLUDE_DELETED_PARAMETER_NAME, required = false, defaultValue = "false") boolean includeDeleted);
@ -136,7 +155,8 @@ public interface DictionaryResource {
@GetMapping(value = DICTIONARY_REST_PATH + TYPE_PATH_VARIABLE + DOSSIER_TEMPLATE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Retrieves all dictionary entries of an entry type", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully retrieved all the dictionary entries of " + "the entry type."), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The entry type is not found.")})
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully retrieved all the dictionary entries of "
+ "the entry type."), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The dossier or the entry type is not found.")})
Dictionary getDictionaryForType(@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId);
@ -151,8 +171,8 @@ public interface DictionaryResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = DICTIONARY_REST_PATH + UPLOAD + TYPE_PATH_VARIABLE + DOSSIER_TEMPLATE_PATH_VARIABLE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "Upload a text-file with 1 entry per line and add each line as an entry to a dictionary for a specific type")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Dictionary upload successful."), @ApiResponse(responseCode = "400", description = "Dictionary could not be uploaded.")})
void uploadDictionary(@RequestPart(name = "file", required = false) MultipartFile file,
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Dictionary upload successful."), @ApiResponse(responseCode = "400", description = "Dictionary could not be uploaded."), @ApiResponse(responseCode = "404", description = "The dossier is not found."), @ApiResponse(responseCode = "403", description = "Forbidden")})
void uploadDictionary(@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file", required = false) MultipartFile file,
@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = DOSSIER_ID_PARAMETER_NAME, required = false) String dossierId,
@ -166,19 +186,21 @@ public interface DictionaryResource {
*/
@ResponseBody
@Operation(summary = "Returns file containing the the dictionary entries for given type..")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "The dossier is not found.")})
@GetMapping(value = DICTIONARY_REST_PATH + DOWNLOAD + TYPE_PATH_VARIABLE + DOSSIER_TEMPLATE_PATH_VARIABLE)
ResponseEntity<?> downloadDictionary(@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@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);
@GetMapping(value = DICTIONARY_REST_PATH + MERGED + TYPE_PATH_VARIABLE+ DOSSIER_TEMPLATE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@GetMapping(value = DICTIONARY_REST_PATH + MERGED + TYPE_PATH_VARIABLE + DOSSIER_TEMPLATE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Retrieves the merged dictionary for the given type, dossier template and dossier", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully retrieved all the dictionary entries"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The entry type is not found.")})
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully retrieved all the dictionary entries"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The dossier or the entry type is not found.")})
Dictionary getMergedDictionaries(@PathVariable(TYPE_PARAMETER_NAME) String type,
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = DOSSIER_ID_PARAMETER_NAME) String dossierId);
@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
@RequestParam(value = DOSSIER_ID_PARAMETER_NAME) String dossierId);
@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(summary = "Set system colors for redaction")
@ -193,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

@ -0,0 +1,58 @@
package com.iqser.red.service.persistence.service.v1.api.external.resource;
import org.springframework.http.HttpStatus;
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.ResponseStatus;
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 DocumentResource {
String DOCUMENT_TEXT_PATH = ExternalApi.BASE_PATH + "/documentText";
String DOCUMENT_POSITIONS_PATH = ExternalApi.BASE_PATH + "/documentPositions";
String DOCUMENT_PAGES_PATH = ExternalApi.BASE_PATH + "/documentPages";
String DOCUMENT_STRUCTURE_PATH = ExternalApi.BASE_PATH + "/documentStructure";
String SIMPLIFIED_SECTION_TEXT_PATH = ExternalApi.BASE_PATH + "/simplifiedSectionText";
String FILE_ID = "fileId";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
String DOSSIER_ID = "dossierId";
String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID + "}";
@GetMapping(value = DOCUMENT_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 dossier / file / document text is not found.")})
ResponseEntity<?> getDocumentText(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@GetMapping(value = DOCUMENT_POSITIONS_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Gets the positions of 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 dossier /file / document positions is not found.")})
ResponseEntity<?> getDocumentPositions(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@GetMapping(value = DOCUMENT_STRUCTURE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Gets the document structure for a fileId", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The dossier / file / document structure is not found.")})
ResponseEntity<?> getDocumentStructure(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@GetMapping(value = DOCUMENT_PAGES_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Gets the page information 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 dossier / file / page information is not found.")})
ResponseEntity<?> getDocumentPages(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@GetMapping(value = SIMPLIFIED_SECTION_TEXT_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Gets the simplified section text for a fileId", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The dossier / file / simplified section text is not found.")})
ResponseEntity<?> getSimplifiedSectionText(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
}

View File

@ -48,7 +48,9 @@ public interface DossierAttributesResource {
@Operation(summary = "Set dossier attributes base configuration.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@PutMapping(value = DOSSIER_ATTRIBUTES_PATH + CONFIG_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PutMapping(value = DOSSIER_ATTRIBUTES_PATH
+ CONFIG_PATH
+ DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
DossierAttributesConfig setDossierAttributesConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody DossierAttributesConfig dossierAttributesConfig);
@ -56,7 +58,9 @@ public interface DossierAttributesResource {
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Add or update a dossier attribute in base configuration.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@PostMapping(value = DOSSIER_ATTRIBUTES_PATH + CONFIG_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(value = DOSSIER_ATTRIBUTES_PATH
+ CONFIG_PATH
+ DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
DossierAttributeConfig addOrUpdateDossierAttributeConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody DossierAttributeConfig dossierAttributes);
@ -85,7 +89,7 @@ public interface DossierAttributesResource {
@Operation(summary = "Set dossier attributes to an existing dossier", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
@PostMapping(value = DOSSIER_ATTRIBUTES_PATH + SET_PATH + DOSSIER_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
DossierAttributes setDossierAttributes(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody DossierAttributes dossierAttributes);
@ -94,7 +98,7 @@ public interface DossierAttributesResource {
@ResponseBody
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Add or update a dossier attribute in existing dossier.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
@PostMapping(value = DOSSIER_ATTRIBUTES_PATH + UPDATE_PATH + DOSSIER_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
DossierAttributes addOrUpdateDossierAttribute(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody DossierAttribute dossierAttribute);
@ -102,7 +106,7 @@ public interface DossierAttributesResource {
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Get the dossier attributes.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found")})
@GetMapping(value = DOSSIER_ATTRIBUTES_PATH + DOSSIER_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
DossierAttributes getDossierAttributes(@PathVariable(DOSSIER_ID) String dossierId);
@ -111,7 +115,7 @@ public interface DossierAttributesResource {
@ResponseBody
@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(summary = "Delete a specific dossier attribute.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "NO_CONTENT")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "NO_CONTENT"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
@DeleteMapping(value = DOSSIER_ATTRIBUTES_PATH + SET_PATH + DOSSIER_ID_PATH_VARIABLE + DOSSIER_ATTRIBUTE_ID_PATH)
void deleteDossierAttribute(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(DOSSIER_ATTRIBUTE_ID) String dossierAttributeId);

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,18 +66,24 @@ 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)
@Operation(summary = "Creates or updates a dossier.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Successfully saved the dossier."), @ApiResponse(responseCode = "400", description = "Incorrect dossier ID provided or attempted to change dossier-template for a dossier with files."), @ApiResponse(responseCode = "409", description = "Duplicate")})
@ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Successfully saved the dossier."), @ApiResponse(responseCode = "400", description = "Incorrect dossier ID provided or attempted to change dossier-template for a dossier with files."), @ApiResponse(responseCode = "409", description = "Duplicate"), @ApiResponse(responseCode = "403", description = "Forbidden")})
ResponseEntity<Dossier> createDossierOrUpdateDossier(@RequestBody DossierRequest dossier);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@DeleteMapping(value = DOSSIER_REST_PATH + DOSSIER_ID_PATH_PARAM)
@Operation(summary = "Deletes an existing dossier.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully deleted the dossier."), @ApiResponse(responseCode = "404", description = "Not found")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully deleted the dossier."), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void deleteDossier(@PathVariable(DOSSIER_ID_PARAM) String dossierId);
@ -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
@ -133,7 +150,7 @@ public interface DossierResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = ARCHIVE_DOSSIERS_PATH + ARCHIVE_PATH)
@Operation(summary = "Archives an existing dossier.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully archived the dossier."), @ApiResponse(responseCode = "400", description = "Incorrect dossier ID entered to archive dossier."), @ApiResponse(responseCode = "403", description = "Forbidden operation while archiving."), @ApiResponse(responseCode = "404", description = "Dossier not found")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully archived the dossiers."), @ApiResponse(responseCode = "400", description = "Incorrect dossier ID entered to archive dossier."), @ApiResponse(responseCode = "403", description = "Forbidden operation while archiving."), @ApiResponse(responseCode = "404", description = "Dossier not found")})
void archiveDossiers(@RequestBody Set<String> dossierIds);
@ -147,14 +164,14 @@ public interface DossierResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@DeleteMapping(value = DELETED_DOSSIERS_PATH + HARD_DELETE_PATH)
@Operation(summary = "Hard deletes existing dossiers.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully hard deleted the dossier."), @ApiResponse(responseCode = "404", description = "Not found")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully hard deleted the dossiers."), @ApiResponse(responseCode = "404", description = "Not found")})
void hardDeleteDossiers(@RequestParam(DOSSIER_ID_PARAM) Set<String> dossierIds);
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = DELETED_DOSSIERS_PATH + UNDELETE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Restores dossiers.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Successfully restored the dossiers."), @ApiResponse(responseCode = "400", description = "Incorrect dossier ID entered to restore dossier."), @ApiResponse(responseCode = "403", description = "Forbidden operation while restoring."), @ApiResponse(responseCode = "409", description = "Conflict occurred while restoring.")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully restored the dossiers."), @ApiResponse(responseCode = "400", description = "Incorrect dossier ID entered to restore dossier."), @ApiResponse(responseCode = "403", description = "Forbidden operation while restoring."), @ApiResponse(responseCode = "409", description = "Conflict occurred while restoring.")})
void undeleteDossiers(@RequestBody Set<String> dossierIds);
}

View File

@ -20,6 +20,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 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;
@ -94,9 +95,37 @@ 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")})
DossierTemplateModel importDossierTemplate(@RequestPart(name = "file") MultipartFile file,
@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

@ -1,12 +1,7 @@
package com.iqser.red.service.persistence.service.v1.api.external.resource;
import java.util.concurrent.CompletableFuture;
import org.springframework.core.io.FileSystemResource;
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.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
@ -40,6 +35,7 @@ public interface DownloadResource {
String OTT_PATH = "/with-ott";
@Deprecated(forRemoval = true)
@Operation(summary = "Prepares a download for given fileIds and types", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Success."), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "Dossier or file not found.")})
@PostMapping(value = REST_PATH + "/prepare", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ -66,16 +62,16 @@ public interface DownloadResource {
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Returns a downloadable byte stream of the requested file", description = "Use the optional \"inline\" request parameter " + "to select, if this report will be opened in the browser.")
@Operation(summary = "Returns a downloadable byte stream of the requested file")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Download with this Id is no longer available")})
@GetMapping(value = REST_PATH)
CompletableFuture<ResponseEntity<InputStreamResource>> downloadFile(@RequestParam(STORAGE_ID) String storageId,
@RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline);
void downloadFile(@RequestParam(STORAGE_ID) String storageId);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Returns a oneTimeToken for a requested download", description = "Use the optional \"inline\" request parameter " + "to select, if this report will be opened in the browser.")
@Operation(summary = "Returns a oneTimeToken for a requested download", description = "Use the optional \"inline\" request parameter "
+ "to select, if this report will be opened in the browser.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@PostMapping(value = REST_PATH + GENERATE_OTT_PATH)
JSONPrimitive<String> generateOneTimeToken(@RequestBody JSONPrimitive<String> storageIdWrapper);
@ -83,11 +79,9 @@ public interface DownloadResource {
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Returns a downloadable byte stream of the requested file using a valid oneTimeToken", description = "Use the optional \"inline\" request parameter " + "to select, if this report will be opened in the browser.")
@Operation(summary = "Returns a downloadable byte stream of the requested file using a valid oneTimeToken")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Download with this Id is no longer available"), @ApiResponse(responseCode = "400", description = "OTT is not valid")})
@GetMapping(value = REST_PATH + OTT_PATH + OTT_PATH_VARIABLE)
CompletableFuture<ResponseEntity<InputStreamResource>> downloadFileUsingOTT(@PathVariable(OTT) String oneTimeToken,
@RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline,
@RequestParam(value = "tenantId") String tenantId);
void downloadFileUsingOTT(@PathVariable(OTT) String oneTimeToken, @RequestParam(value = "tenantId") String tenantId);
}

View File

@ -0,0 +1,71 @@
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.GetMapping;
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.ResponseStatus;
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;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ResponseStatus(value = HttpStatus.OK)
public interface EntityLogResource {
String ENTITY_LOG_PATH = ExternalApi.BASE_PATH + "/entityLog";
String FILE_ID = "fileId";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
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";
@GetMapping(value = ENTITY_LOG_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets the entity log for a fileId", description =
"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.")})
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);
@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

@ -5,4 +5,5 @@ public interface ExternalApi {
// String BASE_PATH = "/api";
String BASE_PATH = "/redaction-gateway-v1";
}

View File

@ -22,6 +22,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemp
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.validation.Valid;
public interface FileAttributesResource {
@ -43,14 +44,21 @@ 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
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Set file attributes base configuration and a list of file attributes, ", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@PutMapping(value = FILE_ATTRIBUTES_PATH + CONFIG_PATH + BASE_CONFIG_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PutMapping(value = FILE_ATTRIBUTES_PATH
+ CONFIG_PATH
+ BASE_CONFIG_PATH
+ DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
FileAttributesConfig setFileAttributesConfig(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody FileAttributesConfig fileAttributesConfig);
@ -58,14 +66,17 @@ public interface FileAttributesResource {
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Add or update a file attribute that can be used at importing csv.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@PostMapping(value = FILE_ATTRIBUTES_PATH + CONFIG_PATH + FILE_ATTRIBUTE_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
FileAttributeConfig addOrUpdateFileAttribute(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody FileAttributeConfig fileAttributes);
@PostMapping(value = FILE_ATTRIBUTES_PATH
+ CONFIG_PATH
+ FILE_ATTRIBUTE_PATH
+ DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
FileAttributeConfig addOrUpdateFileAttribute(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @Valid @RequestBody FileAttributeConfig fileAttributes);
@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);
@ -74,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);
@ -89,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

@ -43,21 +43,21 @@ public interface FileManagementResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@DeleteMapping(value = DELETE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Deletes a file for a given dossierId and FileId", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found")})
void deleteFile(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = DELETE_PATH + DOSSIER_ID_PATH_VARIABLE)
@Operation(summary = "Deletes a a list of files for a given dossierId", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void deleteFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody List<String> fileIds);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Returns a downloadable byte stream of the original file with the specified fileId", description = "Use the optional \"inline\" request parameter to select, if this downloadAnnotated will be opened in the browser.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Could not prepare file download.")})
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Could not prepare file download."), @ApiResponse(responseCode = "404", description = "Not found")})
@GetMapping(value = DOWNLOAD_ORIGINAL_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
ResponseEntity<?> downloadOriginal(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@ -67,32 +67,40 @@ public interface FileManagementResource {
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Returns a downloadable byte stream of the viewer document file with the specified fileId", description = "Use the optional \"inline\" request parameter to select, if this downloadAnnotated will be opened in the browser.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Could not prepare file download.")})
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Could not prepare file download."), @ApiResponse(responseCode = "404", description = "Not found")})
@GetMapping(value = DOWNLOAD_VIEWER_DOCUMENT_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
ResponseEntity<?> downloadViewerDocument(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline);
@Deprecated(forRemoval = true)
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@DeleteMapping(value = HARD_DELETE_PATH + DOSSIER_ID_PATH_VARIABLE)
@Operation(summary = "Hard deletes an uploaded file.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully hard deleted the file."), @ApiResponse(responseCode = "404", description = "Not found")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Successfully hard deleted the file."), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void hardDeleteFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(FILE_IDS) Set<String> fileIds);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = HARD_DELETE_PATH + DOSSIER_ID_PATH_VARIABLE)
@Operation(summary = "Hard deletes a list of files", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "202", description = "Asynchronously hard deletes files.")})
void hardDeleteFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody List<String> files);
@ResponseBody
@ResponseStatus(value = HttpStatus.CREATED)
@PostMapping(value = UNDELETE_PATH + DOSSIER_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Restores an deleted file.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "201", description = "File successfully restored."), @ApiResponse(responseCode = "400", description = "Incorrect dossier ID or file ID entered to restore file."), @ApiResponse(responseCode = "403", description = "Forbidden operation while restoring."), @ApiResponse(responseCode = "409", description = "Conflict occurred while restoring.")})
@ApiResponses(value = {@ApiResponse(responseCode = "201", description = "File successfully restored."), @ApiResponse(responseCode = "400", description = "Incorrect dossier ID or file ID entered to restore file."), @ApiResponse(responseCode = "403", description = "Forbidden operation while restoring."), @ApiResponse(responseCode = "409", description = "Conflict occurred while restoring."), @ApiResponse(responseCode = "404", description = "Not found")})
void restoreFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody Set<String> fileIds);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = ROTATION_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Rotates one or more pages for one file.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Pages successfully rotated."), @ApiResponse(responseCode = "400", description = "Incorrect dossier ID, file ID, pages or rotation entered."), @ApiResponse(responseCode = "403", description = "Forbidden operation while rotating.")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Pages successfully rotated."), @ApiResponse(responseCode = "400", description = "Incorrect dossier ID, file ID, pages or rotation entered."), @ApiResponse(responseCode = "403", description = "Forbidden operation while rotating."), @ApiResponse(responseCode = "404", description = "Not found")})
void rotatePages(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody RotatePagesRequest rotatePagesRequest);
}

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;
@ -34,29 +34,39 @@ public interface HighlightsResource {
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Gets available highlights for the file", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@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)
@Operation(summary = "Converts highlights to imported redactions", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@PostMapping(value = DOSSIERS_PATH + DOSSIER_ID_PATH_VARIABLE + FILES_PATH + FILE_ID_PATH_VARIABLE + HIGHLIGHTS_PATH + CONVERT_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
@PostMapping(value = DOSSIERS_PATH
+ DOSSIER_ID_PATH_VARIABLE
+ FILES_PATH
+ FILE_ID_PATH_VARIABLE
+ HIGHLIGHTS_PATH
+ CONVERT_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
void convertHighlights(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody AnnotationIds annotationIds);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@Operation(summary = "Removed highlights from the file", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
@PostMapping(value = DOSSIERS_PATH + DOSSIER_ID_PATH_VARIABLE + FILES_PATH + FILE_ID_PATH_VARIABLE + HIGHLIGHTS_PATH + DELETE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
void deleteHighlights(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody AnnotationIds annotationIds);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@Operation(summary = "Deletes wrong imported redactions for a file", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@PostMapping(value = DOSSIERS_PATH + DOSSIER_ID_PATH_VARIABLE + FILES_PATH + FILE_ID_PATH_VARIABLE + IMPORTED_REDACTIONS_PATH + DELETE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
@PostMapping(value = DOSSIERS_PATH
+ DOSSIER_ID_PATH_VARIABLE
+ FILES_PATH
+ FILE_ID_PATH_VARIABLE
+ IMPORTED_REDACTIONS_PATH
+ DELETE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
void deleteImportedRedactions(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody AnnotationIds annotationIds);
}

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;
@ -10,18 +9,23 @@ 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.CommentResponse;
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.AnnotationComments;
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.AddCommentRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ForceRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ImageRecategorizationRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.LegalBasisChangeRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RemoveRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ResizeRedactionRequest;
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;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
@ -43,28 +47,39 @@ public interface ManualRedactionResource {
String COMMENT_ID = "commentId";
String COMMENT_ID_PATH_VARIABLE = "/{" + COMMENT_ID + "}";
String FALSE = "false";
String TRUE = "true";
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@DeleteMapping(MANUAL_REDACTION_REST_PATH + "/bulk/undo" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Undo a list of manual requests or redactions", description = "Can only be done be the " + "user who added the request/redaction.")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
void undo(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody Set<String> annotationIds);
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void undo(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<String> annotationIds,
@RequestParam(value = "includeOnlyUnprocessed", required = false, defaultValue = FALSE) boolean includeOnlyUnprocessed,
@RequestParam(value = "includeOnlyLocal", required = false, defaultValue = FALSE) boolean includeOnlyLocal);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = MANUAL_REDACTION_REST_PATH + "/comment/add" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE + ANNOTATION_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(value = MANUAL_REDACTION_REST_PATH
+ "/comment/add"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE
+ ANNOTATION_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Adds a comment to a redaction/redaction request", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
CommentResponse addComment(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@PathVariable(ANNOTATION_ID) String annotationId,
@RequestBody AddCommentRequest addCommentRequest);
@RequestBody AddCommentRequestModel addCommentRequest);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@DeleteMapping(value = MANUAL_REDACTION_REST_PATH + "/comment/undo" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE + ANNOTATION_ID_PATH_VARIABLE + COMMENT_ID_PATH_VARIABLE)
@Operation(summary = "Undo a comment", description = "Can only be done be the user who added" + " the comment.")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void undoComment(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@PathVariable(ANNOTATION_ID) String annotationId,
@ -72,64 +87,136 @@ public interface ManualRedactionResource {
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = MANUAL_REDACTION_REST_PATH + "/bulk/redaction/add" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(value = MANUAL_REDACTION_REST_PATH
+ "/bulk/redaction/add"
+ DOSSIER_ID_PATH_PARAM
+ 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")})
List<ManualAddResponse> addRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
ManualRedactionResponse addRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<AddRedactionRequest> addRedactionRequest);
@RequestBody Set<AddRedactionRequestModel> addRedactionRequest);
@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")})
List<ManualAddResponse> removeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<RemoveRedactionRequest> removeRedactionRequests);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = MANUAL_REDACTION_REST_PATH + "/bulk/redaction/force" + DOSSIER_ID_PATH_PARAM + 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")})
List<ManualAddResponse> forceRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<ForceRedactionRequest> forceRedactionRequests);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = MANUAL_REDACTION_REST_PATH + "/bulk/redaction/legalBasisChange" + DOSSIER_ID_PATH_PARAM + 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")})
List<ManualAddResponse> legalBasisChangeBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<LegalBasisChangeRequest> legalBasisChangeRequests);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = MANUAL_REDACTION_REST_PATH + "/bulk/redaction/recategorize" + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Recategorizes the images list", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
List<ManualAddResponse> recategorizeImageBulk(@PathVariable(DOSSIER_ID) String dossierId,
@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 Set<ImageRecategorizationRequest> imageRecategorizationRequests);
@RequestBody AddRedactionBulkLocalRequestModel addRedactionRequest);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = MANUAL_REDACTION_REST_PATH + "/bulk/redaction/resize" + DOSSIER_ID_PATH_PARAM + 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")})
List<ManualAddResponse> resizeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<ResizeRedactionRequest> resizeRedactionRequests);
@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 = "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);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = MANUAL_REDACTION_REST_PATH
+ "/bulk/redaction/force"
+ DOSSIER_ID_PATH_PARAM
+ 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")})
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"
+ DOSSIER_ID_PATH_PARAM
+ 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")})
ManualRedactionResponse legalBasisChangeBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<LegalBasisChangeRequestModel> legalBasisChangeRequests);
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = MANUAL_REDACTION_REST_PATH
+ "/bulk/redaction/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 recategorizeBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@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)
@PostMapping(value = MANUAL_REDACTION_REST_PATH
+ "/bulk/redaction/resize"
+ DOSSIER_ID_PATH_PARAM
+ 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")})
ManualRedactionResponse resizeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestBody Set<ResizeRedactionRequestModel> resizeRedactionRequests);
@ResponseStatus(value = HttpStatus.OK)
@GetMapping(value = MANUAL_REDACTION_REST_PATH + DOSSIER_ID_PATH_PARAM + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns the manual redactions", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
ManualRedactions getManualRedactions(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@Operation(summary = "Returns the manual redactions", description = """
If the unprocessed flag is true then only the unprocessed manual redactions are returned. If the flag is false\
all manual redactions are returned. Default value for the flag is false.\
If the includeDictChanges flag is false, only local manual redactions will be returned, otherwise all are returned. Default value for the flag is true.
""")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found")})
ManualRedactions getManualRedactions(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "unprocessed", required = false, defaultValue = FALSE) boolean unprocessed,
@RequestParam(value = "includeDictChanges", required = false, defaultValue = TRUE) boolean includeDictChanges);
@ResponseStatus(value = HttpStatus.OK)
@GetMapping(value = MANUAL_REDACTION_REST_PATH
+ "/comments"
+ DOSSIER_ID_PATH_PARAM
+ FILE_ID_PATH_VARIABLE
+ ANNOTATION_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Returns the comments for a specific annotation in a specific file", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier or file not found")})
AnnotationComments getComments(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @PathVariable(ANNOTATION_ID) String annotationId);
}

View File

@ -42,7 +42,6 @@ public interface NotificationResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@Operation(summary = "Mark a notifications as seen or unseen", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@PostMapping(value = NOTIFICATION_PATH + TOGGLE_SEEN_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
void toggleNotificationSeen(@RequestBody List<String> notificationIds, @RequestParam(SET_SEEN_PARAM) boolean setSeen);

View File

@ -1,70 +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;
@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 + "}";
@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);
@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);
@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);
@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);
@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,17 +38,18 @@ 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)
@Operation(summary = "Reanalyze all files of the dossier.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void reanalyzeDossier(@PathVariable(DOSSIER_ID) String dossierId, @RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force);
@PostMapping(value = REANALYSIS_REST_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Reanalyze a file", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void reanalyzeFile(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force);
@ -56,7 +57,7 @@ public interface ReanalysisResource {
@PostMapping(value = REANALYSIS_REST_PATH + DOSSIER_ID_PATH_VARIABLE + BULK_REST_PATH)
@Operation(summary = "Reanalyze multiple files for a dossier", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void reanalyzeFilesForDossier(@PathVariable(DOSSIER_ID) String dossierId,
@RequestBody List<String> fileIds,
@RequestParam(value = FORCE_PARAM, required = false, defaultValue = FALSE) boolean force);
@ -64,26 +65,27 @@ public interface ReanalysisResource {
@Operation(summary = "Ocr and reanalyze a dossier", description = "None")
@PostMapping(value = OCR_REANALYSIS_REST_PATH + DOSSIER_ID_PATH_VARIABLE)
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void ocrDossier(@PathVariable(DOSSIER_ID) String dossierId);
@Operation(summary = "Ocr and reanalyze a file", description = "None")
@PostMapping(value = OCR_REANALYSIS_REST_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "409", description = "Conflict")})
@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")
@PostMapping(value = OCR_REANALYSIS_REST_PATH + DOSSIER_ID_PATH_VARIABLE + BULK_REST_PATH)
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void ocrFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody Set<String> fileIds);
@Operation(summary = "Exclude or re-include a file to the automatic analysis", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
@PostMapping(value = TOGGLE_AUTOMATIC_ANALYSIS_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
void toggleAutomaticAnalysis(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@ -132,7 +134,7 @@ public interface ReanalysisResource {
@PostMapping(value = REINDEX_REST_PATH)
@Operation(summary = "Reindex a dossier, files of a dossier or all", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void reindex(@RequestParam(value = "dossierId", required = false) String dossierId,
@RequestParam(value = "dropIndex", required = false, defaultValue = FALSE) boolean dropIndex,
@RequestBody List<String> fileIds);

View File

@ -1,103 +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;
@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 DOCUMENT_TEXT_PATH = ExternalApi.BASE_PATH + "/documentText";
String DOCUMENT_POSITIONS_PATH = ExternalApi.BASE_PATH + "/documentPositions";
String DOCUMENT_PAGES_PATH = ExternalApi.BASE_PATH + "/documentPages";
String DOCUMENT_STRUCTURE_PATH = ExternalApi.BASE_PATH + "/documentStructure";
String SIMPLIFIED_SECTION_TEXT_PATH = ExternalApi.BASE_PATH + "/simplifiedSectionText";
String FILE_ID = "fileId";
String FILE_ID_PATH_VARIABLE = "/{" + FILE_ID + "}";
String DOSSIER_ID = "dossierId";
String DOSSIER_ID_PATH_VARIABLE = "/{" + DOSSIER_ID + "}";
@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
@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
@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);
@GetMapping(value = DOCUMENT_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 document text is not found.")})
ResponseEntity<?> getDocumentText(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@GetMapping(value = DOCUMENT_POSITIONS_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Gets the positions of 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 document positions is not found.")})
ResponseEntity<?> getDocumentPositions(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@GetMapping(value = DOCUMENT_STRUCTURE_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Gets the document structure for a fileId", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The document structure is not found.")})
ResponseEntity<?> getDocumentStructure(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@GetMapping(value = DOCUMENT_PAGES_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Gets the page information 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 page information is not found.")})
ResponseEntity<?> getDocumentPages(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@GetMapping(value = SIMPLIFIED_SECTION_TEXT_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Gets the simplified section text for a fileId", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "400", description = "Request contains error."), @ApiResponse(responseCode = "404", description = "The simplified section text is not found.")})
ResponseEntity<?> getSimplifiedSectionText(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@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

@ -23,6 +23,7 @@ import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSON
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.ReportTemplate;
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;
@ -47,10 +48,11 @@ public interface ReportTemplateResource {
@ResponseBody
@ResponseStatus(value = HttpStatus.CREATED)
@PostMapping(value = REPORT_TEMPLATE_UPLOAD_PATH + DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(value = REPORT_TEMPLATE_UPLOAD_PATH
+ DOSSIER_TEMPLATE_ID_PATH_VARIABLE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Upload template file for redaction-report", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Report template upload succeeded.")})
ReportTemplate uploadTemplate(@RequestPart(name = "file") MultipartFile file,
ReportTemplate uploadTemplate(@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId,
@RequestParam(value = MULTI_FILE_REPORT, required = false, defaultValue = "false") boolean multiFileReport,
@RequestParam(value = ACTIVE_BY_DEFAULT, required = false, defaultValue = "false") boolean activeByDefault);

View File

@ -6,15 +6,21 @@ 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;
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.Rules;
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.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;
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;
@ -23,10 +29,16 @@ 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}";
String RULE_FILE_TYPE_PARAMETER_NAME = "ruleFileType";
String RULE_FILE_TYPE_PATH_VARIABLE = "/{ruleFileType}";
String DRY_RUN_PARAMETER = "dryRun";
/**
* Upload rules to be used by redaction service.
@ -36,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 = "204", description = "Rules upload successful."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified.")})
void upload(@RequestBody Rules 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
@ -45,7 +57,15 @@ public interface RulesResource {
@Operation(summary = "Returns object containing the currently used Drools rules.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@GetMapping(value = RULES_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
Rules download(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId);
RulesResponse download(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Returns object containing the currently used Drools rules.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@GetMapping(value = RULES_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE + RULE_FILE_TYPE_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
RulesResponse download(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType);
/**
@ -56,8 +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 = "204", description = "Rules upload successful."), @ApiResponse(responseCode = "400", description = "Uploaded rules could not be verified.")})
void uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @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."), @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
@ -67,4 +99,18 @@ public interface RulesResource {
@GetMapping(value = RULES_PATH + DOSSIER_TEMPLATE_PATH_VARIABLE + DOWNLOAD_PATH)
ResponseEntity<?> downloadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId);
@ResponseBody
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Returns file containing the currently used Drools rules.")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
@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

@ -18,7 +18,7 @@ public interface StatusReportResource {
@GetMapping(value = STATUS_REPORT + DOSSIER_ID_PATH_VARIABLE)
@Operation(summary = "Generate status report for dossier", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Status report was generated.")})
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Status report was generated."), @ApiResponse(responseCode = "404", description = "Not found")})
ResponseEntity<?> generateStatusReport(@PathVariable(DOSSIER_ID) String dossierId);
}

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)
@ -74,14 +87,14 @@ public interface StatusResource {
@ResponseBody
@GetMapping(value = STATUS_REST_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Gets the status for a file.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found")})
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
FileStatus getFileStatus(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = STATUS_REST_PATH + ASSIGNEE_REST_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Assigns a user to a a file.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully assigned new owner to dossier."), @ApiResponse(responseCode = "404", description = "Not found")})
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully assigned new owner to dossier."), @ApiResponse(responseCode = "404", description = "Not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void setCurrentFileAssignee(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId);
@ -90,7 +103,7 @@ public interface StatusResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = STATUS_REST_PATH + "/under-review" + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Sets the status UNDER_REVIEW for a file.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void setStatusUnderReview(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId);
@ -99,22 +112,24 @@ public interface StatusResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = STATUS_REST_PATH + "/under-approval" + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE)
@Operation(summary = "Sets the status UNDER_APPROVAL for a file.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void setStatusUnderApproval(@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@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")})
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)
@PostMapping(value = STATUS_REST_PATH + "/set-assignee" + DOSSIER_ID_PATH_VARIABLE + BULK_REST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Assign a a user for a list of files.", description = "None")
@Operation(summary = "Assigns a user for a list of files.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Successfully assigned new owner to dossier."), @ApiResponse(responseCode = "404", description = "Not found")})
void setAssigneeForList(@PathVariable(DOSSIER_ID) String dossierId,
@RequestParam(value = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId,
@ -124,7 +139,7 @@ public interface StatusResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = STATUS_REST_PATH + "/under-review" + DOSSIER_ID_PATH_VARIABLE + BULK_REST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Sets the status UNDER_REVIEW for a list of files.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void setStatusUnderReviewForList(@PathVariable(DOSSIER_ID) String dossierId,
@RequestBody List<String> fileIds,
@RequestParam(value = ASSIGNEE_ID_REQUEST_PARAM, required = false) String assigneeId);
@ -133,23 +148,25 @@ public interface StatusResource {
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PostMapping(value = STATUS_REST_PATH + "/under-approval" + DOSSIER_ID_PATH_VARIABLE + BULK_REST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Sets the status UNDER_APPROVAL for a list of files.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void setStatusUnderApprovalForList(@PathVariable(DOSSIER_ID) String dossierId,
@RequestBody List<String> fileIds,
@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")})
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)
@PostMapping(value = STATUS_REST_PATH + "/new" + DOSSIER_ID_PATH_VARIABLE + BULK_REST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Sets the status NEW for a list of files.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK")})
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "404", description = "Dossier not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void setStatusNewForList(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody List<String> fileIds);

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,8 @@ 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;
@ -26,24 +28,27 @@ 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
@ResponseStatus(value = HttpStatus.CREATED)
@PostMapping(value = UPLOAD_PATH + DOSSIER_ID_PATH_VARIABLE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Receives an uploaded file and returns its fileId.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "201", description = "File upload succeeded. Return the fileId of the " + "uploaded file.")})
FileUploadResult upload(@RequestPart(name = "file") MultipartFile file,
@ApiResponses(value = {@ApiResponse(responseCode = "201", description = "File upload succeeded. Return the fileId of the "
+ "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
@ResponseStatus(value = HttpStatus.OK)
@PostMapping(value = IMPORT_REDACTIONS_PATH + DOSSIER_ID_PATH_VARIABLE + FILE_ID_PATH_VARIABLE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "Imports redactions from a redacted file to a existing file", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Ok")})
void importRedactions(@RequestPart(name = "file") MultipartFile file,
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Ok"), @ApiResponse(responseCode = "404", description = "Dossier not found"), @ApiResponse(responseCode = "403", description = "Forbidden")})
void importRedactions(@Schema(type = "string", format = "binary", name = "file") @RequestPart(name = "file") MultipartFile file,
@PathVariable(DOSSIER_ID) String dossierId,
@PathVariable(FILE_ID) String fileId,
@RequestParam(value = "pageInclusionRequest", required = false) Set<Integer> pageInclusionRequest);

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

@ -0,0 +1,38 @@
plugins {
id("com.iqser.red.service.java-conventions")
id("io.freefair.lombok") version "8.6"
}
dependencies {
api(project(":persistence-service-external-api-v1"))
api(project(":persistence-service-internal-api-v1"))
api("com.iqser.red.service:pdftron-redaction-service-api-v1:${rootProject.extra.get("pdftronRedactionServiceVersion")}") {
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.iqser.red.service:redaction-service-api-v1:${rootProject.extra.get("redactionServiceVersion")}") {
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.iqser.red.service:redaction-report-service-api-v1:${rootProject.extra.get("redactionReportServiceVersion")}") {
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.iqser.red.service:search-service-api-v1:${rootProject.extra.get("searchServiceVersion")}") {
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.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")
api("com.iqser.red.commons:jackson-commons:2.1.0")
api(project(":persistence-service-shared-api-v1"))
testImplementation("com.iqser.red.commons:test-commons:2.1.0")
testImplementation("org.springframework.boot:spring-boot-starter-test:3.0.4")
compileOnly("org.springdoc:springdoc-openapi-ui:1.6.13")
api("io.github.openfeign:feign-core:12.2")
compileOnly("org.springframework:spring-web:6.0.6")
}
description = "persistence-service-external-api-v2"

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

@ -0,0 +1,28 @@
package com.iqser.red.service.persistence.service.v2.api.external.model;
import java.util.List;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlCData;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
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;
}

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