Compare commits
1523 Commits
test-conta
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a441909408 | ||
|
|
a7bdbb5495 | ||
|
|
3c0b9d36d4 | ||
|
|
9ff3035730 | ||
|
|
c933793721 | ||
|
|
4746d50627 | ||
|
|
fdfdba550e | ||
|
|
09d53b7743 | ||
|
|
d691164af6 | ||
|
|
43f9db59d4 | ||
|
|
7669d27e7b | ||
|
|
d7d46d5429 | ||
|
|
205f9c678e | ||
|
|
cf5e235fc9 | ||
|
|
25eb72ee05 | ||
|
|
770a489f7f | ||
|
|
18c7875844 | ||
|
|
0838f30a24 | ||
|
|
15e7417766 | ||
|
|
ab112e85c1 | ||
|
|
2e3833f55d | ||
|
|
16364671d0 | ||
|
|
8054806cd3 | ||
|
|
d4396fe6d5 | ||
|
|
6d0354946a | ||
|
|
00b4ad800d | ||
|
|
34eb724170 | ||
|
|
1919b1b306 | ||
|
|
7dae6d81c5 | ||
|
|
2b27e39234 | ||
|
|
9718f8d3fd | ||
|
|
1a5ae41001 | ||
|
|
8ed5f3388b | ||
|
|
77483b6bd0 | ||
|
|
c32c2cdab0 | ||
|
|
fbb8a7b519 | ||
|
|
61e557712b | ||
|
|
c28076df68 | ||
|
|
032f6f87c9 | ||
|
|
cfac2bcc6b | ||
|
|
c420bda820 | ||
|
|
9fc3aef669 | ||
|
|
f55ebd9ecc | ||
|
|
a3a1ee67fc | ||
|
|
945e402639 | ||
|
|
3624a6e49a | ||
|
|
850e85ffdb | ||
|
|
6b885f3212 | ||
|
|
87ba79905c | ||
|
|
ae8aecc005 | ||
|
|
45c0c3d902 | ||
|
|
3610e6c76f | ||
|
|
7d07b1c882 | ||
|
|
abfc9eed95 | ||
|
|
d3e0d8f52c | ||
|
|
1967468fec | ||
|
|
569c24924a | ||
|
|
091a648a82 | ||
|
|
7a087764c1 | ||
|
|
5e60074d2f | ||
|
|
635925b3fc | ||
|
|
fad8fb3af2 | ||
|
|
b3b547914b | ||
|
|
556e6a4f6b | ||
|
|
14143c9356 | ||
|
|
34680a3972 | ||
|
|
6068c39c33 | ||
|
|
e0717e3466 | ||
|
|
bde5c88471 | ||
|
|
84c1d037c6 | ||
|
|
dba47d0f0f | ||
|
|
298ccff842 | ||
|
|
ab7f6a1470 | ||
|
|
bc1b6b9e6d | ||
|
|
5425e06399 | ||
|
|
5d0b26aca6 | ||
|
|
1d595eb1f0 | ||
|
|
a7db55cb13 | ||
|
|
d2fdba5658 | ||
|
|
d1883fc5b6 | ||
|
|
a875f94ca4 | ||
|
|
df65dac4cb | ||
|
|
9667144c9b | ||
|
|
8548dcaf66 | ||
|
|
52c5dbea1f | ||
|
|
65ccf32346 | ||
|
|
fac80bbc5c | ||
|
|
ad20597434 | ||
|
|
66809bb136 | ||
|
|
c7a74fed78 | ||
|
|
8a5e97b9ce | ||
|
|
08671583fe | ||
|
|
e7f3b47bb6 | ||
|
|
00b47d21dc | ||
|
|
ef86d1909d | ||
|
|
793444b96e | ||
|
|
11d97683d0 | ||
|
|
e3eff19de4 | ||
|
|
c65a62e9ac | ||
|
|
867f0a9c02 | ||
|
|
d55a72e57a | ||
|
|
f3fc2e2ce2 | ||
|
|
3507130e64 | ||
|
|
6a5792adf6 | ||
|
|
d0c79c87cf | ||
|
|
c95a5f027c | ||
|
|
4f289c359f | ||
|
|
4efc1b897a | ||
|
|
0584172bbd | ||
|
|
295839c048 | ||
|
|
5767e3465e | ||
|
|
6f6095990f | ||
|
|
f3032becf4 | ||
|
|
96a8575cb6 | ||
|
|
86ff048c6b | ||
|
|
0d3e3051ab | ||
|
|
0d7b57dd6a | ||
|
|
15f05624ca | ||
|
|
47052745c7 | ||
|
|
83668117da | ||
|
|
63c72bc613 | ||
|
|
4453eab3bf | ||
|
|
e700f8b785 | ||
|
|
4adebda2ab | ||
|
|
bdaac65afe | ||
|
|
9956348a06 | ||
|
|
0e08794271 | ||
|
|
10f69631b0 | ||
|
|
d3f0d1bc87 | ||
|
|
f2c41d5191 | ||
|
|
28c97b446c | ||
|
|
b461f95638 | ||
|
|
969004b542 | ||
|
|
ce27ac8d17 | ||
|
|
b04bad6057 | ||
|
|
5f98b16bc1 | ||
|
|
71f4a78a16 | ||
|
|
f9a5b5aa01 | ||
|
|
efbfd26363 | ||
|
|
46cab2786a | ||
|
|
17b90b1b67 | ||
|
|
59933f4a88 | ||
|
|
d36cf3c7f2 | ||
|
|
861f1e559f | ||
|
|
9b74db96ba | ||
|
|
684dc3418d | ||
|
|
b3bc7bb0ac | ||
|
|
03d4f04b15 | ||
|
|
95f1ea4a00 | ||
|
|
5bbcfdffc0 | ||
|
|
5409b432d6 | ||
|
|
292c92c827 | ||
|
|
9ce067bd80 | ||
|
|
5210b8ec40 | ||
|
|
15ca9ade53 | ||
|
|
8b1f63bf45 | ||
|
|
c9b8be9405 | ||
|
|
af234311e6 | ||
|
|
e5ea667ea1 | ||
|
|
7ff7222072 | ||
|
|
d8b1a32783 | ||
|
|
3315a679a8 | ||
|
|
013d61b0d0 | ||
|
|
afe793a523 | ||
|
|
23078c0b66 | ||
|
|
c1fafaee6e | ||
|
|
fa0e29095f | ||
|
|
c7a9c2ff11 | ||
|
|
3be9566a2e | ||
|
|
b62a9b9ae6 | ||
|
|
f08a2d512a | ||
|
|
95cd4edebb | ||
|
|
21b184d97a | ||
|
|
e393d70186 | ||
|
|
511f392203 | ||
|
|
072c965593 | ||
|
|
65041c2b7a | ||
|
|
a5004c6d26 | ||
|
|
58d11fc7b9 | ||
|
|
8e40b77c70 | ||
|
|
4a32f55b61 | ||
|
|
12136e0fdc | ||
|
|
2bb20ef7fa | ||
|
|
2e83a72f29 | ||
|
|
19d7670ada | ||
|
|
8c7e64ffad | ||
|
|
edd6b87566 | ||
|
|
b21e34ec84 | ||
|
|
215afe0834 | ||
|
|
70d55ac6c3 | ||
|
|
39e78cb033 | ||
|
|
d4da6befb4 | ||
|
|
d4448fa798 | ||
|
|
0f72b876d0 | ||
|
|
ff603599b9 | ||
|
|
2aa354dd3a | ||
|
|
a1a7f4f568 | ||
|
|
bf69b806de | ||
|
|
9cabeef5d5 | ||
|
|
4eff4803c6 | ||
|
|
81cee51661 | ||
|
|
357743e1d6 | ||
|
|
0afb6ca4f2 | ||
|
|
0fc9d40155 | ||
|
|
d61eee0a93 | ||
|
|
5563c19dca | ||
|
|
491c57667b | ||
|
|
a1de121109 | ||
|
|
894be159f5 | ||
|
|
7e78063a0b | ||
|
|
6ea68ccef4 | ||
|
|
632cfdb5f6 | ||
|
|
e37bdaabdb | ||
|
|
aa5ff0daf1 | ||
|
|
ee687e42b5 | ||
|
|
a599021b31 | ||
|
|
4d36a3d813 | ||
|
|
b8710d57b3 | ||
|
|
bb5e7f5be7 | ||
|
|
01ab05b52e | ||
|
|
0761ef6676 | ||
|
|
f9183054f3 | ||
|
|
31c3ce45f0 | ||
|
|
939ebe426b | ||
|
|
f01ea9f6da | ||
|
|
25cc17bf09 | ||
|
|
1f60256117 | ||
|
|
584473565f | ||
|
|
508ac2d677 | ||
|
|
bb9bdabbae | ||
|
|
f6caa94136 | ||
|
|
43d37b67b3 | ||
|
|
92aa781a8f | ||
|
|
f23548adcf | ||
|
|
c21a97420d | ||
|
|
48a7d05ba1 | ||
|
|
1b8b828f3d | ||
|
|
1e552d22c0 | ||
|
|
227dd092b4 | ||
|
|
a36fcff7b6 | ||
|
|
b30531f552 | ||
|
|
c99f90546c | ||
|
|
0a8ed5d2c8 | ||
|
|
2d68469d8d | ||
|
|
ade8e7ed9d | ||
|
|
51b5201677 | ||
|
|
1664820a82 | ||
|
|
f2df7fe783 | ||
|
|
48a0637fba | ||
|
|
fa83069ff0 | ||
|
|
22a7f2c3af | ||
|
|
3df86ade4a | ||
|
|
a23aa12527 | ||
|
|
dddc608c34 | ||
|
|
b174ca57b1 | ||
|
|
1fe8170ec0 | ||
|
|
bbd8d5be9b | ||
|
|
258d26ede0 | ||
|
|
437941fc6c | ||
|
|
bb40b3fa20 | ||
|
|
34346b149b | ||
|
|
0f8e78a1aa | ||
|
|
4c5f164f2e | ||
|
|
a2e1b4367a | ||
|
|
b574df51bf | ||
|
|
bc057e73de | ||
|
|
13fd797148 | ||
|
|
ca75ee9607 | ||
|
|
0f651d6a67 | ||
|
|
f69921fe59 | ||
|
|
88c5e43e51 | ||
|
|
34c1c4f51c | ||
|
|
de0d4294ea | ||
|
|
3ccbac4b0e | ||
|
|
cdde8a7da6 | ||
|
|
ccbf977e01 | ||
|
|
9a99398961 | ||
|
|
f86c925835 | ||
|
|
d26d24271a | ||
|
|
0ad3973d23 | ||
|
|
3fe009512f | ||
|
|
f3823a9acd | ||
|
|
e3e3d5bc58 | ||
|
|
60e042587e | ||
|
|
9c4804d239 | ||
|
|
f5f9247834 | ||
|
|
9203993d6e | ||
|
|
260851661b | ||
|
|
0ce2b1644c | ||
|
|
5e215c2649 | ||
|
|
53bb752002 | ||
|
|
ffca299cb4 | ||
|
|
a65e9e1086 | ||
|
|
abda71fca3 | ||
|
|
c52dfbdf5a | ||
|
|
fef66f92b2 | ||
|
|
cdbf69abbf | ||
|
|
e5bac8f964 | ||
|
|
06f5f4f241 | ||
|
|
82661dcaf2 | ||
|
|
0ed4759266 | ||
|
|
15be976352 | ||
|
|
9b07def91d | ||
|
|
ec10bbdf8b | ||
|
|
b184217099 | ||
|
|
71a895f3a2 | ||
|
|
ac41a902a1 | ||
|
|
4397cddcc1 | ||
|
|
a557f0ab25 | ||
|
|
7d52b4b0a0 | ||
|
|
d794245409 | ||
|
|
dbe7949a1c | ||
|
|
4c79b8c2f4 | ||
|
|
2ac82ab8e7 | ||
|
|
44419f2c6c | ||
|
|
7a13b6adc2 | ||
|
|
45e67b8796 | ||
|
|
87eeddded3 | ||
|
|
e213127b25 | ||
|
|
adab452380 | ||
|
|
f7612ac89f | ||
|
|
8d33f19453 | ||
|
|
5d704e08f9 | ||
|
|
7ea1a87567 | ||
|
|
1c56c4665e | ||
|
|
50831dabfa | ||
|
|
dc42fcad40 | ||
|
|
0bc8bac5d3 | ||
|
|
0a09725ebe | ||
|
|
bea853cbf2 | ||
|
|
88ae52dd52 | ||
|
|
77c2f9b9a7 | ||
|
|
876ee17469 | ||
|
|
36eed8d0a1 | ||
|
|
0f935fcba3 | ||
|
|
a57aedf52c | ||
|
|
63129d7727 | ||
|
|
82da9860ce | ||
|
|
25a8bb0610 | ||
|
|
47ce4fab61 | ||
|
|
09a060e1a7 | ||
|
|
68937f298b | ||
|
|
c6110a6d65 | ||
|
|
62719aec6f | ||
|
|
a96372e542 | ||
|
|
3a113207d8 | ||
|
|
cbce87430f | ||
|
|
7ce90c1986 | ||
|
|
318883981e | ||
|
|
e08690f7ee | ||
|
|
1abcd56e97 | ||
|
|
1de9d75883 | ||
|
|
80cbda22bf | ||
|
|
256a50775d | ||
|
|
635dc05bde | ||
|
|
979e6a6650 | ||
|
|
361f899c70 | ||
|
|
04def902cd | ||
|
|
156337b82c | ||
|
|
a5dea2acf1 | ||
|
|
366b737e11 | ||
|
|
874fdc85ee | ||
|
|
d144b31747 | ||
|
|
5e7627862e | ||
|
|
e379e9b636 | ||
|
|
44f5a45887 | ||
|
|
9143292e0f | ||
|
|
7dc6a56902 | ||
|
|
48eadc410d | ||
|
|
bf5a2dd6f6 | ||
|
|
d7ea4214ed | ||
|
|
38a6ca6a82 | ||
|
|
d7694b37a2 | ||
|
|
32e0436f9e | ||
|
|
84d2a6fac0 | ||
|
|
c03b489650 | ||
|
|
05676c7fa2 | ||
|
|
8831a9b24e | ||
|
|
e37947fb7a | ||
|
|
99b6310d60 | ||
|
|
60371fb34b | ||
|
|
0d4c005862 | ||
|
|
149ce5144d | ||
|
|
9c0c442eb6 | ||
|
|
e5271c928e | ||
|
|
dd0096a04c | ||
|
|
52bd1dea3c | ||
|
|
11088ddfef | ||
|
|
342257a9c6 | ||
|
|
8e2bf84a9d | ||
|
|
4349fdc152 | ||
|
|
6957857b42 | ||
|
|
3d9b7fc03f | ||
|
|
ff04d2a5ef | ||
|
|
4789a89257 | ||
|
|
c506aefdb4 | ||
|
|
3f9afa49e8 | ||
|
|
cfcff1f069 | ||
|
|
5188ab416e | ||
|
|
a42e24d7f4 | ||
|
|
65c6cdb3c9 | ||
|
|
78e0859c02 | ||
|
|
0cc48f8dfd | ||
|
|
f1b1f270d6 | ||
|
|
5fdb7d3773 | ||
|
|
a8af78c501 | ||
|
|
84ebccfe79 | ||
|
|
2ad5232e41 | ||
|
|
daf6f835c1 | ||
|
|
1377394d6a | ||
|
|
98bd00a89c | ||
|
|
06f7e57a74 | ||
|
|
8683480c40 | ||
|
|
33cec84890 | ||
|
|
6feb0d2a58 | ||
|
|
7a209664cb | ||
|
|
22bf27dd73 | ||
|
|
bb4af4c1ce | ||
|
|
a99b2e8165 | ||
|
|
2908477ce8 | ||
|
|
b977a2e46e | ||
|
|
9eb3ef0181 | ||
|
|
a377270141 | ||
|
|
5e8d8ea6f6 | ||
|
|
5494d92f39 | ||
|
|
1edbf8ae56 | ||
|
|
3adb35bec4 | ||
|
|
2697f014e6 | ||
|
|
1ac9f7ebcf | ||
|
|
7a9bffa3c5 | ||
|
|
fd910bf189 | ||
|
|
963cf0137b | ||
|
|
1037178711 | ||
|
|
a3d15f59c1 | ||
|
|
555dd3330a | ||
|
|
9caa1f3068 | ||
|
|
1f5c9f12ff | ||
|
|
833b391f47 | ||
|
|
630cde1f8f | ||
|
|
b6b6fad242 | ||
|
|
1253d7d0b4 | ||
|
|
c57e4c507e | ||
|
|
7eea40cc6c | ||
|
|
c2ed3eeff9 | ||
|
|
4beb6e0647 | ||
|
|
ef98112871 | ||
|
|
6d1b1ca31b | ||
|
|
4eeee75f33 | ||
|
|
2a0d9ce3f9 | ||
|
|
9bcf2d177e | ||
|
|
53c624c2c9 | ||
|
|
b8f64a1c86 | ||
|
|
2c2347f5f0 | ||
|
|
91e317057d | ||
|
|
399ad6e4a1 | ||
|
|
829dc2f4b3 | ||
|
|
804ad3cf88 | ||
|
|
2b7be87985 | ||
|
|
9c539ffd21 | ||
|
|
e4ec187c2f | ||
|
|
8dd9a73694 | ||
|
|
495ed75fc3 | ||
|
|
dc8abbba58 | ||
|
|
13b92135ae | ||
|
|
fd8da20e72 | ||
|
|
6d428cd15c | ||
|
|
3c32201e94 | ||
|
|
48ce264967 | ||
|
|
c6faa877a1 | ||
|
|
b1549ba775 | ||
|
|
9be0fad408 | ||
|
|
b24dcdaa86 | ||
|
|
00f1b8e495 | ||
|
|
96eef1a7a2 | ||
|
|
79fec2e9c9 | ||
|
|
8c96f75d3a | ||
|
|
675e9e0c42 | ||
|
|
cff6826424 | ||
|
|
535f9ce8da | ||
|
|
034c880e4f | ||
|
|
8198d6cfaa | ||
|
|
c80008cbb3 | ||
|
|
4016a26c2c | ||
|
|
3af71f2760 | ||
|
|
2361d483f0 | ||
|
|
ff4b3369c9 | ||
|
|
043d9ca299 | ||
|
|
4351f67d16 | ||
|
|
5f2b851efb | ||
|
|
f4a7a9a25e | ||
|
|
514cae7fe5 | ||
|
|
39ac84566a | ||
|
|
f1058caeed | ||
|
|
dfc1f6016c | ||
|
|
81099adf64 | ||
|
|
47e8bee9db | ||
|
|
c9e3a9d086 | ||
|
|
801dc52cd2 | ||
|
|
cc969350f8 | ||
|
|
a94e4a6caf | ||
|
|
b72e432741 | ||
|
|
b1fb547b0e | ||
|
|
fdf3fe5b4f | ||
|
|
f5b26deb4c | ||
|
|
b7e88e4ec1 | ||
|
|
e7ae91af47 | ||
|
|
f760598d8f | ||
|
|
13dbd04084 | ||
|
|
6a918bf939 | ||
|
|
0f15e68623 | ||
|
|
66f1a86c77 | ||
|
|
09749e8344 | ||
|
|
2916c19cb1 | ||
|
|
3a463e1ed1 | ||
|
|
cd899b2774 | ||
|
|
e73a4cb8a3 | ||
|
|
b7c8c2b668 | ||
|
|
844ceb66c1 | ||
|
|
32aa88f6b4 | ||
|
|
1fcd3ecd6b | ||
|
|
6e2c2dfbdc | ||
|
|
8b746a5db5 | ||
|
|
193dfaca67 | ||
|
|
784b67ee5a | ||
|
|
f72ca45689 | ||
|
|
0c4ebbd6b9 | ||
|
|
bbaaa6f952 | ||
|
|
7245f71acb | ||
|
|
b6c8ec2f7c | ||
|
|
fe3b437b80 | ||
|
|
2f35d2b218 | ||
|
|
2af5e9fea7 | ||
|
|
e120ff589b | ||
|
|
aa1f3e0a8b | ||
|
|
8ef1c7187d | ||
|
|
15ab5d4215 | ||
|
|
20f760b37c | ||
|
|
8006a4c374 | ||
|
|
3430288026 | ||
|
|
5cf4c034eb | ||
|
|
8d340057a4 | ||
|
|
f266582f48 | ||
|
|
a8ead5bea4 | ||
|
|
5f0e4b381c | ||
|
|
31c2b596be | ||
|
|
5cfb45d3d2 | ||
|
|
14da82e2b7 | ||
|
|
552d760ead | ||
|
|
55c6b7419d | ||
|
|
b83207f715 | ||
|
|
99c7ef2529 | ||
|
|
89446913b1 | ||
|
|
e44646234e | ||
|
|
30c982f2c8 | ||
|
|
7539aa4510 | ||
|
|
c9389e04fa | ||
|
|
6f5174cc4b | ||
|
|
1a156e0b76 | ||
|
|
0f3434bca1 | ||
|
|
b5405370f4 | ||
|
|
e644afb8e6 | ||
|
|
cb93273528 | ||
|
|
67e42dc4e0 | ||
|
|
84ef6e6f4c | ||
|
|
4a56bbc07f | ||
|
|
562e9d0761 | ||
|
|
3fefb8efab | ||
|
|
57871d11c9 | ||
|
|
26cba53508 | ||
|
|
49fe599c35 | ||
|
|
71ffb03dfc | ||
|
|
bc760a6870 | ||
|
|
0bf85842c1 | ||
|
|
4fbea2e3e7 | ||
|
|
fd021041f7 | ||
|
|
a40962085c | ||
|
|
3d73c6f496 | ||
|
|
6cdd888967 | ||
|
|
e631281156 | ||
|
|
a0eee59ced | ||
|
|
531524be1e | ||
|
|
369cc6340a | ||
|
|
539da8d80e | ||
|
|
3ad5e0300c | ||
|
|
9d4a942a48 | ||
|
|
84fb04fb30 | ||
|
|
dc20d2c2c8 | ||
|
|
52c564b5e3 | ||
|
|
62852d48df | ||
|
|
d8c5668a38 | ||
|
|
6157154378 | ||
|
|
1c896f72e1 | ||
|
|
db09744506 | ||
|
|
7326a387ca | ||
|
|
887e56b98e | ||
|
|
a8a9232c4c | ||
|
|
a8b7a835c0 | ||
|
|
bab96ba58d | ||
|
|
9f4edce48a | ||
|
|
6c857290b8 | ||
|
|
f12b5d8e98 | ||
|
|
740e88fd1b | ||
|
|
c4e682254b | ||
|
|
bbb2a1b464 | ||
|
|
cc9daf9180 | ||
|
|
41a8a3c32a | ||
|
|
361b11ca2b | ||
|
|
c3aaba45cc | ||
|
|
44bce39a2a | ||
|
|
7a24702c47 | ||
|
|
7a86b37c13 | ||
|
|
e0d00dcc48 | ||
|
|
c34af58aa0 | ||
|
|
c55aa9d206 | ||
|
|
6956e49f4a | ||
|
|
767f77f261 | ||
|
|
80be82e361 | ||
|
|
fed0caa8d2 | ||
|
|
c2c6438187 | ||
|
|
7e65396ffe | ||
|
|
18a8f5215f | ||
|
|
0cdd10e985 | ||
|
|
87cd0b06e1 | ||
|
|
23b437e60a | ||
|
|
9c48547c36 | ||
|
|
a4426e87ce | ||
|
|
94ecfbd1b4 | ||
|
|
71c81631f9 | ||
|
|
a05bbc9c7f | ||
|
|
649b8b6a46 | ||
|
|
4af3ada659 | ||
|
|
aafe9fa31a | ||
|
|
808ce8cbaf | ||
|
|
436368b5e6 | ||
|
|
07a0bacf55 | ||
|
|
a1b224adbd | ||
|
|
23929d2772 | ||
|
|
1a585e9fe3 | ||
|
|
5dd27f682b | ||
|
|
e382e4e833 | ||
|
|
4b61a19a95 | ||
|
|
fbf9320f6c | ||
|
|
46f9523b2c | ||
|
|
cdece93037 | ||
|
|
999ecafdf8 | ||
|
|
f77c08aac8 | ||
|
|
27d2208a72 | ||
|
|
856f09583a | ||
|
|
596a8dba31 | ||
|
|
5e5b8e54ca | ||
|
|
d6b95dad3e | ||
|
|
2b574b502e | ||
|
|
e5b23b2df7 | ||
|
|
dc96ff8439 | ||
|
|
111f79a1d8 | ||
|
|
08c8d65f3c | ||
|
|
81c21b1cbe | ||
|
|
ce6ec13b4c | ||
|
|
0d5d6a1c7a | ||
|
|
c28dcbfe40 | ||
|
|
11d050a92e | ||
|
|
0f782c802d | ||
|
|
e00e5043c2 | ||
|
|
e94ff93c7d | ||
|
|
a58bfa2f54 | ||
|
|
00052b7a43 | ||
|
|
e8202a5dae | ||
|
|
0dd9d0ed07 | ||
|
|
057b6d805a | ||
|
|
c90606f4c0 | ||
|
|
63253581a3 | ||
|
|
a586ec0eeb | ||
|
|
9e1189726d | ||
|
|
65ca13089b | ||
|
|
4451c07f02 | ||
|
|
d0faf8a33d | ||
|
|
78e82020ab | ||
|
|
5baaa71933 | ||
|
|
16c3388ffe | ||
|
|
2ef9800eae | ||
|
|
4843f79466 | ||
|
|
a6c6a4111c | ||
|
|
abccf8ca82 | ||
|
|
26d03e6b6f | ||
|
|
c67fa5926b | ||
|
|
0f203d39a4 | ||
|
|
dae360a7ac | ||
|
|
64f519f100 | ||
|
|
e377df12a0 | ||
|
|
62c81c3654 | ||
|
|
a4683a4eed | ||
|
|
f9090cfa3f | ||
|
|
398ec390b7 | ||
|
|
b52ac441e9 | ||
|
|
b08a666022 | ||
|
|
088a4ac21b | ||
|
|
06b3d236c8 | ||
|
|
a18f9d11b8 | ||
|
|
fb133a4770 | ||
|
|
bb9e977e78 | ||
|
|
581206bd16 | ||
|
|
46123e179d | ||
|
|
6a4047effb | ||
|
|
01747eab4f | ||
|
|
4b6cfbb66d | ||
|
|
e4a5e6efb7 | ||
|
|
cf124d1326 | ||
|
|
fe777da0f5 | ||
|
|
b66e436058 | ||
|
|
1622228613 | ||
|
|
a5866b36c8 | ||
|
|
efd777acd8 | ||
|
|
05faad96bc | ||
|
|
cfda6a8217 | ||
|
|
f4bdff1901 | ||
|
|
55922a85e8 | ||
|
|
f3758fb003 | ||
|
|
00a1cadb83 | ||
|
|
fac9544864 | ||
|
|
5d480887a2 | ||
|
|
3e205d0b1b | ||
|
|
adb6332bf5 | ||
|
|
23cf4cf5e9 | ||
|
|
2ae40e6403 | ||
|
|
1a4d97a14f | ||
|
|
d921c3f6a0 | ||
|
|
9079ae18af | ||
|
|
9e70d9e199 | ||
|
|
6412f58d58 | ||
|
|
c2d02b3faa | ||
|
|
aabfd5d8b5 | ||
|
|
1387065784 | ||
|
|
afa61062e4 | ||
|
|
efdf6a8868 | ||
|
|
a71ee404ed | ||
|
|
8485a6b48a | ||
|
|
f0160df8d1 | ||
|
|
f962037aaa | ||
|
|
2a4c8c2b19 | ||
|
|
d24ab46616 | ||
|
|
e723550b53 | ||
|
|
8f4f95b166 | ||
|
|
e9ba3c8da1 | ||
|
|
84d033ff54 | ||
|
|
1f6339f71d | ||
|
|
64a7d24639 | ||
|
|
2c604b1aa3 | ||
|
|
7d875ee7eb | ||
|
|
edb333aa29 | ||
|
|
38d3832732 | ||
|
|
0408e3986e | ||
|
|
1a0cea1332 | ||
|
|
05153504d3 | ||
|
|
5da98461f0 | ||
|
|
f5c2a115f4 | ||
|
|
08c6f11e88 | ||
|
|
03dc5f2d50 | ||
|
|
32411a668f | ||
|
|
20ca0cd798 | ||
|
|
4f1c926a17 | ||
|
|
6110dd1497 | ||
|
|
63dbc073c7 | ||
|
|
926e8b6b6b | ||
|
|
31165bd8f6 | ||
|
|
6b2fa6b9b6 | ||
|
|
141bf035ad | ||
|
|
8c3d2266ba | ||
|
|
c1dda3cd26 | ||
|
|
a300f61609 | ||
|
|
4935d81b0c | ||
|
|
a655100ec4 | ||
|
|
1e64903352 | ||
|
|
16a31bfb65 | ||
|
|
24e3771b08 | ||
|
|
8d2091edc4 | ||
|
|
7998b81bb6 | ||
|
|
4763e6db89 | ||
|
|
6280e342eb | ||
|
|
83d6ef71ef | ||
|
|
9ac1545fe8 | ||
|
|
59c1401053 | ||
|
|
033493aaee | ||
|
|
7acc0889b7 | ||
|
|
01ca31fbbf | ||
|
|
4a6777be71 | ||
|
|
7c37c16be7 | ||
|
|
d5494660bc | ||
|
|
2aff32f33e | ||
|
|
3a93c1f715 | ||
|
|
1adf46179e | ||
|
|
bf10c3baf0 | ||
|
|
1d47744b30 | ||
|
|
dcb5c0e03e | ||
|
|
b11c95dd39 | ||
|
|
6b82815b7a | ||
|
|
ef18d68102 | ||
|
|
7f4a53ba5f | ||
|
|
5a526484d6 | ||
|
|
fac8a1ac85 | ||
|
|
11dcb2fb93 | ||
|
|
c8244781b4 | ||
|
|
a086a71833 | ||
|
|
e510f07be9 | ||
|
|
e00ea01803 | ||
|
|
a86be93177 | ||
|
|
f23f66f0ed | ||
|
|
04759967e2 | ||
|
|
8756eea0e0 | ||
|
|
77db059a2e | ||
|
|
b002f7d544 | ||
|
|
92cc2895ad | ||
|
|
cfeb370299 | ||
|
|
6d1d51098b | ||
|
|
ef7c470618 | ||
|
|
57cb10ba1d | ||
|
|
9a93c0b18b | ||
|
|
623068e1a1 | ||
|
|
f870759747 | ||
|
|
07b29d9c50 | ||
|
|
c9cda89e17 | ||
|
|
54bfdfafe2 | ||
|
|
c31a29cea2 | ||
|
|
6770d03ae1 | ||
|
|
1c60bc6586 | ||
|
|
ddcff217e5 | ||
|
|
c60c79ef06 | ||
|
|
a37ac83339 | ||
|
|
d7e5e16351 | ||
|
|
5bfeec1b21 | ||
|
|
7df02d588a | ||
|
|
29a274fdb0 | ||
|
|
6525e0c2bc | ||
|
|
88d63b46b9 | ||
|
|
9c8e584e5a | ||
|
|
147247cfe6 | ||
|
|
71f0756a3a | ||
|
|
910948bd2d | ||
|
|
d4baa5d8b7 | ||
|
|
1fc74f2a0c | ||
|
|
06e0bba76c | ||
|
|
72b148376e | ||
|
|
873daa2736 | ||
|
|
8e8aa645d0 | ||
|
|
f0c6b868e8 | ||
|
|
c9af40ddb6 | ||
|
|
729923fdea | ||
|
|
efc2b200ad | ||
|
|
75676c33b1 | ||
|
|
5f1731444e | ||
|
|
9ddcd56a67 | ||
|
|
3ab607ccbf | ||
|
|
d46ae2834a | ||
|
|
6601ee0188 | ||
|
|
45a0ca1800 | ||
|
|
8c4b177ef2 | ||
|
|
dec81549e0 | ||
|
|
ba8cf68779 | ||
|
|
7490402bb7 | ||
|
|
a5e3c0c98a | ||
|
|
ed052fa741 | ||
|
|
8a4c754250 | ||
|
|
da6856f31d | ||
|
|
b2cf55b5cc | ||
|
|
3fde378ade | ||
|
|
8f4a03dd08 | ||
|
|
857f2383d2 | ||
|
|
f3ef80df06 | ||
|
|
06360c5cbd | ||
|
|
9110e68266 | ||
|
|
36e8a2bfdb | ||
|
|
f2a6a90529 | ||
|
|
5b4f194cad | ||
|
|
43e85af0e9 | ||
|
|
6654dc9546 | ||
|
|
7a9d90a76c | ||
|
|
c78a4c1186 | ||
|
|
9eb0e5e8df | ||
|
|
eab0b3de39 | ||
|
|
8ae5ea89c6 | ||
|
|
71f760c1e4 | ||
|
|
93d2b77ca5 | ||
|
|
06970a1477 | ||
|
|
8c8a088cc8 | ||
|
|
f8645ef50c | ||
|
|
f5f33978ed | ||
|
|
d845b4cef2 | ||
|
|
baeadd145e | ||
|
|
82ed6b0b05 | ||
|
|
c2110cd965 | ||
|
|
dff30d5f7d | ||
|
|
042f3a079c | ||
|
|
c42983aa91 | ||
|
|
333fd40449 | ||
|
|
a98cc0a6f2 | ||
|
|
6bb3c93b84 | ||
|
|
01f857fcef | ||
|
|
96be1ba52e | ||
|
|
81b66236f9 | ||
|
|
7b6af5bf3b | ||
|
|
a37b2dbc32 | ||
|
|
47a9bc0343 | ||
|
|
bc334b9760 | ||
|
|
d849859def | ||
|
|
98f743052d | ||
|
|
4d107b2475 | ||
|
|
3c4c93a634 | ||
|
|
2fa7b4337a | ||
|
|
4aa5b0ee34 | ||
|
|
23b80ca615 | ||
|
|
b196544131 | ||
|
|
9c33404490 | ||
|
|
0b4e51907c | ||
|
|
ea3d47f435 | ||
|
|
6375cedaee | ||
|
|
17667d067d | ||
|
|
e8a942d764 | ||
|
|
80c7c867ae | ||
|
|
f86e4ea993 | ||
|
|
d5244846a9 | ||
|
|
4091f21076 | ||
|
|
307ae65080 | ||
|
|
5d4badce06 | ||
|
|
574f05b800 | ||
|
|
11fa795977 | ||
|
|
50d25ead38 | ||
|
|
d7c1d233bb | ||
|
|
4eb9240bf7 | ||
|
|
318d15f758 | ||
|
|
caba58ec19 | ||
|
|
e8c3d3dc65 | ||
|
|
179d66a796 | ||
|
|
57b046d821 | ||
|
|
e681651420 | ||
|
|
d2b6971f04 | ||
|
|
c9c75fe9e1 | ||
|
|
09c451d2ed | ||
|
|
d54b33607b | ||
|
|
cbef7c87b5 | ||
|
|
f3bd2b0120 | ||
|
|
39000eed5a | ||
|
|
fb58362012 | ||
|
|
97b2e5f732 | ||
|
|
5cc699043c | ||
|
|
500d215069 | ||
|
|
768384a84c | ||
|
|
ce39968508 | ||
|
|
59b29ccd38 | ||
|
|
51f28963ff | ||
|
|
3664f6800a | ||
|
|
99944c9fef | ||
|
|
8db9bd1be0 | ||
|
|
a4f3edd144 | ||
|
|
8f5d3ef9af | ||
|
|
34c7e51ae8 | ||
|
|
3e67dd8055 | ||
|
|
a7c4bf866c | ||
|
|
bf66fdf4b7 | ||
|
|
97cc851de1 | ||
|
|
5931058a6d | ||
|
|
d317457e05 | ||
|
|
3d0697003b | ||
|
|
2daefe0868 | ||
|
|
2a57468e34 | ||
|
|
6b665066f3 | ||
|
|
cd171e22f0 | ||
|
|
52551609e7 | ||
|
|
737d2ee36b | ||
|
|
bda907d283 | ||
|
|
8540d5e865 | ||
|
|
42d1bc8848 | ||
|
|
4bd7a50f97 | ||
|
|
0ad6de9fb7 | ||
|
|
85c4f77828 | ||
|
|
17d9b13042 | ||
|
|
8cf94c2cd7 | ||
|
|
add3eea2f3 | ||
|
|
61fd030dc2 | ||
|
|
2b019542bd | ||
|
|
bb58667bb2 | ||
|
|
d5f8a5c6f4 | ||
|
|
998a0710cd | ||
|
|
f7b80f2e61 | ||
|
|
498cfecf88 | ||
|
|
c843484ff0 | ||
|
|
f6e38550bb | ||
|
|
c00ac6ebe1 | ||
|
|
76ee5b0c94 | ||
|
|
a710c5b549 | ||
|
|
a6e323ade5 | ||
|
|
80e783d46b | ||
|
|
57cc7c7c54 | ||
|
|
caac9f48ee | ||
|
|
95978b85af | ||
|
|
5394faa065 | ||
|
|
dab280cef5 | ||
|
|
99a71a6036 | ||
|
|
f3b2620df7 | ||
|
|
4cfe7d9c95 | ||
|
|
cfb81e9326 | ||
|
|
c2d4b0fd28 | ||
|
|
d9497ef45d | ||
|
|
9329023ff6 | ||
|
|
6321295d02 | ||
|
|
42b410f6f7 | ||
|
|
7455628790 | ||
|
|
6690fba43d | ||
|
|
a6eda4841a | ||
|
|
9162bb7edc | ||
|
|
b67cf7eb99 | ||
|
|
c674cccf43 | ||
|
|
06ea42c410 | ||
|
|
835e3568f6 | ||
|
|
ef13d8ace2 | ||
|
|
56dcd421a9 | ||
|
|
9a3898a377 | ||
|
|
e17f577064 | ||
|
|
e8a5ae9da9 | ||
|
|
f9ab3a2607 | ||
|
|
6a2260af8f | ||
|
|
15611d438c | ||
|
|
45cae62b01 | ||
|
|
f6c21faf0c | ||
|
|
9de4dfc5af | ||
|
|
421fe57e38 | ||
|
|
2b782fdd4c | ||
|
|
4ccf355dcf | ||
|
|
0cfbdb3083 | ||
|
|
bc348215a6 | ||
|
|
03303ece8e | ||
|
|
0d9f5276a5 | ||
|
|
caecbb483a | ||
|
|
15ddc1afeb | ||
|
|
2505cb70d2 | ||
|
|
5d13696cec | ||
|
|
804a078403 | ||
|
|
f608aa1043 | ||
|
|
da98b4cc4e | ||
|
|
7ff2b9f11f | ||
|
|
8c7b991ecb | ||
|
|
5333db97ea | ||
|
|
3f6c493e54 | ||
|
|
a04b00c794 | ||
|
|
9907f389b1 | ||
|
|
15a4189b3e | ||
|
|
0aaafa41ea | ||
|
|
16d182a513 | ||
|
|
9c4ee66d02 | ||
|
|
c1d46ddace | ||
|
|
9ac8fcbf38 | ||
|
|
5f72b0e2c4 | ||
|
|
4ea6996bdd | ||
|
|
f148c6fa6d | ||
|
|
06570a33e8 | ||
|
|
e2b64b566b | ||
|
|
ef6c268c2b | ||
|
|
5a95dfbc1c | ||
|
|
41d25ff924 | ||
|
|
1f10939e32 | ||
|
|
369e944255 | ||
|
|
2da3b3d30c | ||
|
|
0312656ad6 | ||
|
|
3d1d7bcbf2 | ||
|
|
c7c22bfd06 | ||
|
|
a299f99575 | ||
|
|
cf4a650fb5 | ||
|
|
be705a5406 | ||
|
|
d0571a436c | ||
|
|
ea9af28633 | ||
|
|
08b4883e5e | ||
|
|
41791c7e07 | ||
|
|
f5f1bde988 | ||
|
|
7dfd46e887 | ||
|
|
c4a354560d | ||
|
|
af2a16e540 | ||
|
|
a1a8e14419 | ||
|
|
267e8b3abc | ||
|
|
1761528c23 | ||
|
|
3335c54000 | ||
|
|
150df5e721 | ||
|
|
568979a4e1 | ||
|
|
e4f1a04220 | ||
|
|
e10681a0c0 | ||
|
|
cfc0bba3dc | ||
|
|
701e173b99 | ||
|
|
b7c4ec2d75 | ||
|
|
bd5756a06b | ||
|
|
b2dbbbb405 | ||
|
|
d190291b5b | ||
|
|
ca1db849d7 | ||
|
|
0d705f53ca | ||
|
|
92bfe44d03 | ||
|
|
4deadfec0b | ||
|
|
e9c139211f | ||
|
|
6ba76e66ec | ||
|
|
85fc1a570b | ||
|
|
a71e75da90 | ||
|
|
187a882e0b | ||
|
|
1ea65a256c | ||
|
|
9b972984cf | ||
|
|
85d12f9355 | ||
|
|
2948ae1c10 | ||
|
|
f2352413e1 | ||
|
|
f568cf2eec | ||
|
|
822a55a8f8 | ||
|
|
b5794f7fdf | ||
|
|
4c9ffd5f64 | ||
|
|
f71976e595 | ||
|
|
d62746484e | ||
|
|
995ff6b725 | ||
|
|
6e7dadd0cf | ||
|
|
705dd42333 | ||
|
|
85737653f7 | ||
|
|
7257bdb159 | ||
|
|
7172ae55d6 | ||
|
|
36755e5958 | ||
|
|
b0f40a080c | ||
|
|
8c854900cc | ||
|
|
464d5e2ef3 | ||
|
|
31893ba442 | ||
|
|
56038f13b9 | ||
|
|
95afc6cd14 | ||
|
|
c6d330f1df | ||
|
|
9c0db2a5fe | ||
|
|
36e76b0273 | ||
|
|
6a61033441 | ||
|
|
a62f642c28 | ||
|
|
cf44dbff03 | ||
|
|
56db92bf39 | ||
|
|
8f629914bd | ||
|
|
9e3191dabd | ||
|
|
7d3a7452a9 | ||
|
|
cdf37fd486 | ||
|
|
3b8b397fe5 | ||
|
|
87f841d9dd | ||
|
|
4653cba097 | ||
|
|
9b8e6a0e84 | ||
|
|
ea81708227 | ||
|
|
c1540943a4 | ||
|
|
6f5f148731 | ||
|
|
0a4096401c | ||
|
|
430f118206 | ||
|
|
77d1e6ed4c | ||
|
|
f8157e4448 | ||
|
|
05db55adca | ||
|
|
7508e7667e | ||
|
|
5c215f3a47 | ||
|
|
ba21282b1c | ||
|
|
170cc61000 | ||
|
|
f77ec53282 | ||
|
|
37c2eef2f9 | ||
|
|
5c89099ac0 | ||
|
|
cf0d85a42d | ||
|
|
22a5be7021 | ||
|
|
e52c60c669 | ||
|
|
d8ee40bf66 | ||
|
|
853fa8365e | ||
|
|
91884f450a | ||
|
|
838c4e604c | ||
|
|
391365b2fb | ||
|
|
8dad6c44df | ||
|
|
47e268287e | ||
|
|
db38a97f8a | ||
|
|
b5cd72c7bf | ||
|
|
815de4d184 | ||
|
|
1de6210993 | ||
|
|
b9d473677c | ||
|
|
9d84085d46 | ||
|
|
4c4e1ced91 | ||
|
|
93ff601b44 | ||
|
|
9aff9e2d71 | ||
|
|
fa8a5cf61f | ||
|
|
d4a9afb8fe | ||
|
|
9985e9b0af | ||
|
|
32e4b9a1fd | ||
|
|
e55cd716cc | ||
|
|
9e04a47e90 | ||
|
|
88736855b4 | ||
|
|
c036acc81d | ||
|
|
00dff315b7 | ||
|
|
22d837a35f | ||
|
|
06d3b282cc | ||
|
|
0013b5d116 | ||
|
|
97960d8ed7 | ||
|
|
67bd3ed4bc | ||
|
|
882576dadf | ||
|
|
5eb4d86808 | ||
|
|
93167265d1 | ||
|
|
cec1c1dae9 | ||
|
|
1c2d3fe4e2 | ||
|
|
e73b6bd69b | ||
|
|
5d6ed6bba8 | ||
|
|
288bbf9f17 | ||
|
|
83c02409e9 | ||
|
|
8c090d14ba | ||
|
|
e5636452a1 | ||
|
|
19605b4185 | ||
|
|
2344af108c | ||
|
|
eae93223fa | ||
|
|
0a08c57f44 | ||
|
|
8379af0a79 | ||
|
|
8ff9fe41e1 | ||
|
|
ff8c6d2463 | ||
|
|
a254250ac5 | ||
|
|
c455bdf5ce | ||
|
|
c78722c0ee | ||
|
|
59d45a309b | ||
|
|
641dfcb0dd | ||
|
|
d8c746b48d | ||
|
|
a8e0f4b67c | ||
|
|
ba19477c25 | ||
|
|
477113b6ae | ||
|
|
5d977d191a | ||
|
|
175bc4e4cc | ||
|
|
67afcfa6b0 | ||
|
|
8845c406b2 | ||
|
|
f0a4772c33 | ||
|
|
d11a1178b3 | ||
|
|
8ad1938b1c | ||
|
|
0d475e2839 | ||
|
|
819b2384b2 | ||
|
|
05981913b3 | ||
|
|
5b0c35b04f | ||
|
|
6adfdb2991 | ||
|
|
c5dc459ad6 | ||
|
|
61cc1297fd | ||
|
|
377cc98542 | ||
|
|
e81e1eb000 | ||
|
|
84fa3acc0a | ||
|
|
93bbdd768d | ||
|
|
15995a1518 | ||
|
|
c1418d30dc | ||
|
|
cb0db9e6f5 | ||
|
|
75aa8aa467 | ||
|
|
3c374baf94 | ||
|
|
0f3cc14409 | ||
|
|
284d0bff50 | ||
|
|
79287d435d | ||
|
|
e1007dc5c4 | ||
|
|
0a8347d326 | ||
|
|
9b5b8195dc | ||
|
|
f60ef37a08 | ||
|
|
b966ac516b | ||
|
|
517763786b | ||
|
|
5c28072103 | ||
|
|
afa75b850c | ||
|
|
fcaf76eb48 | ||
|
|
c2d558636c | ||
|
|
efe6dd5930 | ||
|
|
13e47c6453 | ||
|
|
8a50f6c0a2 | ||
|
|
f250c6b961 | ||
|
|
a279a76659 | ||
|
|
d91bbc6d0e | ||
|
|
407e1be1a4 | ||
|
|
18d98ab4e6 | ||
|
|
3bdfd4c96b | ||
|
|
7a0de45add | ||
|
|
da43634b19 | ||
|
|
729881fcd4 | ||
|
|
75847a43d5 | ||
|
|
087935dddd | ||
|
|
9ebf22f52f | ||
|
|
66f740b44a | ||
|
|
2c385d9a92 | ||
|
|
c12abd0280 | ||
|
|
15b24ac879 | ||
|
|
947bd41e1b | ||
|
|
8eac48206b | ||
|
|
5a3432e6c6 | ||
|
|
6d245ae7b6 | ||
|
|
bffae1292e | ||
|
|
48d432024a | ||
|
|
59f3e99fea | ||
|
|
9f0d262f1b | ||
|
|
c831eb7025 | ||
|
|
58e66f920c | ||
|
|
c65a93bf1b | ||
|
|
50c731d6f4 | ||
|
|
a470fc0a2a | ||
|
|
9aee9627d8 | ||
|
|
39d800dd2a | ||
|
|
70bb450870 | ||
|
|
6221a7c5d7 | ||
|
|
9a5b192144 | ||
|
|
c0d2d1f9d0 | ||
|
|
b872effffd | ||
|
|
541da5d3d3 | ||
|
|
46083bbbf7 | ||
|
|
ac7b05049d | ||
|
|
66a188d25a | ||
|
|
7df20ee8bb | ||
|
|
840a216265 | ||
|
|
f4950020a7 | ||
|
|
b0db743019 | ||
|
|
12cdc83ee3 | ||
|
|
8d0cafd0c8 | ||
|
|
3611c9e33f | ||
|
|
374998449a | ||
|
|
5fb3e6b103 | ||
|
|
0d150c3aa6 | ||
|
|
f317ff9f90 | ||
|
|
cd6166315c | ||
|
|
647b91193a | ||
|
|
1b20061c72 | ||
|
|
6f87c0bcec | ||
|
|
656183d33c | ||
|
|
80af534ce6 | ||
|
|
b8b9395654 | ||
|
|
4ae0a7e646 | ||
|
|
0b44b5abe7 | ||
|
|
07c5530631 | ||
|
|
77c6348e35 | ||
|
|
c9ce96ee67 | ||
|
|
56b4746a3e | ||
|
|
cd2d97616d | ||
|
|
c4485ad26b | ||
|
|
209373d3fa | ||
|
|
c0ac625bbc | ||
|
|
99bb0228c4 | ||
|
|
1aa17ea79e | ||
|
|
95ffc7c4af | ||
|
|
5a17678d34 | ||
|
|
f7285fb821 | ||
|
|
781b761aad | ||
|
|
bc8ca76858 | ||
|
|
6ec370ad01 | ||
|
|
50005ad83a | ||
|
|
f048efd42c | ||
|
|
e9833ae71a | ||
|
|
c79c653382 | ||
|
|
97ab8a0783 | ||
|
|
15ffcf683f | ||
|
|
0e74e88f21 | ||
|
|
a0c62274ef | ||
|
|
1a92b3db26 | ||
|
|
8778031869 | ||
|
|
dd5bcfd4f9 | ||
|
|
f706619140 | ||
|
|
670b60041c | ||
|
|
24af307fb7 | ||
|
|
1f9a0e6326 | ||
|
|
02d87d7c14 | ||
|
|
f6b3df9c8f | ||
|
|
7d3da44a97 | ||
|
|
75b5db712a | ||
|
|
fc198aa34f | ||
|
|
3a1908b8ff | ||
|
|
79bc6d2b39 | ||
|
|
d694a7fec1 | ||
|
|
8fa082f7a0 | ||
|
|
ea24d26fb8 | ||
|
|
ad20a1f9f5 | ||
|
|
b706a71ec1 | ||
|
|
97d2af3eb5 | ||
|
|
d3f3817ea1 | ||
|
|
99fae8cd52 | ||
|
|
941990787c | ||
|
|
b6eb08d722 | ||
|
|
fb63bf65b0 | ||
|
|
0c91fff875 | ||
|
|
7f7ca627d6 | ||
|
|
65d62a09a1 | ||
|
|
3e43fe0382 | ||
|
|
cfe5446255 | ||
|
|
f9f5dfd1c9 | ||
|
|
d49300659b | ||
|
|
f8231a6499 | ||
|
|
bfa88fd082 | ||
|
|
5fe11f9da9 | ||
|
|
c1ea8bbb4f | ||
|
|
6f8e319abf | ||
|
|
87e27050dd | ||
|
|
ddc3af3a41 | ||
|
|
fbae5ff3a0 | ||
|
|
91ffe50781 | ||
|
|
353e0a91e7 | ||
|
|
e94d3c6146 | ||
|
|
919825774d | ||
|
|
6cdd2178f1 | ||
|
|
93d40366c7 | ||
|
|
a2bd4564b4 | ||
|
|
4a63c48ad9 | ||
|
|
f006af37e4 | ||
|
|
3f64fa6c40 | ||
|
|
54ce629a1c | ||
|
|
277f0538be | ||
|
|
3c9d247432 | ||
|
|
45b879982b | ||
|
|
e651c8225f | ||
|
|
10315356f5 | ||
|
|
55153d0a09 | ||
|
|
0b7a925757 | ||
|
|
51da8f5d58 | ||
|
|
0a5c531c72 | ||
|
|
b5c832c8a3 | ||
|
|
707631f2c7 | ||
|
|
1b0323839e | ||
|
|
5beaa3ab2b | ||
|
|
f4b2ec88d6 | ||
|
|
f8dec7f671 | ||
|
|
ad9ef6351f | ||
|
|
80d707a374 | ||
|
|
837822b7dd | ||
|
|
59474eb52e | ||
|
|
dd359282cf | ||
|
|
4465c282d6 | ||
|
|
af526cc7e7 | ||
|
|
04f3bd1c1b | ||
|
|
02cc8b54e3 | ||
|
|
81de9adcba | ||
|
|
cb269b8e86 | ||
|
|
7058cb3035 | ||
|
|
e9edfa12f7 | ||
|
|
f3d148a631 | ||
|
|
78d3a772da | ||
|
|
2fc912d414 | ||
|
|
62065e89ca | ||
|
|
0e04331285 | ||
|
|
70b7a258e9 | ||
|
|
d5af5a2132 | ||
|
|
3d8a33dfd1 | ||
|
|
d3db8c7b16 | ||
|
|
31890e6434 | ||
|
|
9bca6b406a | ||
|
|
7466a61583 | ||
|
|
e2c7238c67 | ||
|
|
662b52e00a | ||
|
|
368cf13586 | ||
|
|
32361f7f2f | ||
|
|
7d4548e40c | ||
|
|
56baa7db07 | ||
|
|
322f605004 | ||
|
|
808dc37d21 | ||
|
|
cb6c81b9e9 | ||
|
|
0f7d648044 | ||
|
|
2e142f12e9 | ||
|
|
1e53942178 | ||
|
|
db40f5a1a9 | ||
|
|
91b13c41ce | ||
|
|
8fc94de4b7 | ||
|
|
b4527e4bfe | ||
|
|
a621ff1484 | ||
|
|
701ee0ff43 | ||
|
|
78bb3b72ca | ||
|
|
82a5d6a1cd | ||
|
|
edd0c747cf | ||
|
|
be3d717079 | ||
|
|
230e674c6f | ||
|
|
261d69fa52 | ||
|
|
c4d8f16c2e | ||
|
|
23455c2468 | ||
|
|
ae7a3273a1 | ||
|
|
03cc83d751 | ||
|
|
fbba154295 | ||
|
|
b55cfefe6e | ||
|
|
8eb0ae2b06 | ||
|
|
bc63acee33 | ||
|
|
39172c2da2 | ||
|
|
067d3e49ba | ||
|
|
73afa0f349 | ||
|
|
b63fc5adb2 | ||
|
|
5302e09b4d | ||
|
|
98bf385c20 | ||
|
|
6e63a1e7f4 | ||
|
|
701c65eac7 | ||
|
|
7fcb52c90f | ||
|
|
be915430b1 | ||
|
|
a189ae4382 | ||
|
|
d40ef63116 | ||
|
|
80e602fb07 | ||
|
|
d25b8c3934 | ||
|
|
ea5f79d67c | ||
|
|
aab2971d6e | ||
|
|
d1d5331202 | ||
|
|
0b173ec930 | ||
|
|
9f250058bb | ||
|
|
6b22622e7e | ||
|
|
e12db2e0c2 | ||
|
|
8c7435f8b4 | ||
|
|
aeaf0483d3 | ||
|
|
af4dff23dc | ||
|
|
633ddec803 | ||
|
|
515edb1c1c | ||
|
|
0fb7ccf7a2 | ||
|
|
0162017e61 | ||
|
|
f5b925d961 | ||
|
|
698fcbe2b7 | ||
|
|
79756640da | ||
|
|
28f9401719 | ||
|
|
d6be241717 | ||
|
|
9d084c57c0 | ||
|
|
e9eb0a5171 | ||
|
|
d28e485dff | ||
|
|
6a544df044 | ||
|
|
7f7dd7a182 | ||
|
|
96d4407f53 | ||
|
|
b491d2df5e | ||
|
|
6f7327aa0d | ||
|
|
ef4eb85481 | ||
|
|
caa017bb24 | ||
|
|
66629b09cc | ||
|
|
2f39e6f956 | ||
|
|
8b1d26e4e5 | ||
|
|
b1b544faf6 | ||
|
|
1ac0a184b7 | ||
|
|
ccc2411a92 | ||
|
|
e3411dba42 | ||
|
|
313eebf90b | ||
|
|
9b37fceb98 | ||
|
|
ed90b6a9cc | ||
|
|
ccade4b5fd | ||
|
|
2c3ce2de97 | ||
|
|
896f0a6e5d | ||
|
|
16e84b85b1 | ||
|
|
11958a4099 | ||
|
|
3ae67b77e4 | ||
|
|
04212cdd4e | ||
|
|
7f7e1d2699 | ||
|
|
4164de2a5c | ||
|
|
13742a12cd | ||
|
|
02af9810df | ||
|
|
3384e91067 | ||
|
|
2db7128b3a |
0
.dev/docker-compose.yaml
Normal file → Executable file
0
.dev/docker-compose.yaml
Normal file → Executable file
8
.gitignore
vendored
8
.gitignore
vendored
@ -26,3 +26,11 @@
|
||||
**/.DS_Store
|
||||
**/classpath-data.json
|
||||
**/dependencies-and-licenses-overview.txt
|
||||
|
||||
gradle.properties
|
||||
gradlew
|
||||
gradlew.bat
|
||||
gradle/
|
||||
|
||||
**/.gradle
|
||||
**/build
|
||||
|
||||
@ -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
57
README.md
Normal file
@ -0,0 +1,57 @@
|
||||

|
||||
## Description
|
||||
*TODO*
|
||||
|
||||
## Table of Contents
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [License](#license)
|
||||
- [Tests](#tests)
|
||||
- [API](#api)
|
||||
## Installation
|
||||
*TODO*
|
||||
|
||||
## Usage
|
||||
*TODO*
|
||||
|
||||
## License
|
||||
<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
|
||||
```
|
||||
7
buildSrc/build.gradle.kts
Normal file
7
buildSrc/build.gradle.kts
Normal file
@ -0,0 +1,7 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
39
config/checkstyle/checkstyle.xml
Normal file
39
config/checkstyle/checkstyle.xml
Normal 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
20
config/pmd/pmd.xml
Normal 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
22
config/pmd/test_pmd.xml
Normal 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
1
gradle.properties.kts
Normal file
@ -0,0 +1 @@
|
||||
version = 2.0-SNAPSHOT
|
||||
@ -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>
|
||||
@ -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}", "/"]
|
||||
@ -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"
|
||||
@ -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>
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -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());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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})
|
||||
|
||||
@ -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());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
@ -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 {
|
||||
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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"
|
||||
|
||||
@ -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>
|
||||
@ -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) {
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -5,4 +5,5 @@ public interface ExternalApi {
|
||||
// String BASE_PATH = "/api";
|
||||
|
||||
String BASE_PATH = "/redaction-gateway-v1";
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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"
|
||||
@ -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<>();
|
||||
}
|
||||
@ -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<>();
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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
Loading…
x
Reference in New Issue
Block a user