mirror of
https://github.com/Alfresco/acs-community-packaging.git
synced 2026-09-16 18:13:21 +00:00
Compare commits
865
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2227c522d1 | ||
|
|
c280cb76ec | ||
|
|
6c1b00a11d | ||
|
|
06a79d7fe8 | ||
|
|
b269c4ff11 | ||
|
|
f0f85dd031 | ||
|
|
db9ae0a800 | ||
|
|
81df71adcb | ||
|
|
75d57e4806 | ||
|
|
92b39d478f | ||
|
|
5c84333cc2 | ||
|
|
29338fd53e | ||
|
|
0e5c7c9465 | ||
|
|
52832f1100 | ||
|
|
b7afbb57d6 | ||
|
|
4e1f805bff | ||
|
|
6183411b72 | ||
|
|
691fee053f | ||
|
|
2658664376 | ||
|
|
a4d1b7c37f | ||
|
|
5f1ba33c6b | ||
|
|
7c0662abe7 | ||
|
|
0ade3eb669 | ||
|
|
f1bae17140 | ||
|
|
b64c65f000 | ||
|
|
035fd9ca7c | ||
|
|
3dc624a652 | ||
|
|
60bf0b5b14 | ||
|
|
6fbab4c900 | ||
|
|
d6dd22a2a0 | ||
|
|
f9d8167d35 | ||
|
|
bf2e0cc91a | ||
|
|
5d1ee8c4c0 | ||
|
|
60a470da91 | ||
|
|
9ef8d49f80 | ||
|
|
2ed27931a5 | ||
|
|
5af1ad824e | ||
|
|
975646f422 | ||
|
|
32d42e8d5f | ||
|
|
c644f08501 | ||
|
|
fd4c548e4b | ||
|
|
f01f4636ba | ||
|
|
274278dbeb | ||
|
|
fa2868c092 | ||
|
|
2d4a251226 | ||
|
|
157e5a2553 | ||
|
|
2b6cc0d8b2 | ||
|
|
1f36d3eeb5 | ||
|
|
ddcfbf0510 | ||
|
|
1175ea568b | ||
|
|
c638d28cba | ||
|
|
56db329fc4 | ||
|
|
e1c2d88d2b | ||
|
|
4e2eec974d | ||
|
|
a651c90dd4 | ||
|
|
04b717ba95 | ||
|
|
72338d4243 | ||
|
|
9ed1edf334 | ||
|
|
240071c9be | ||
|
|
0b715d5596 | ||
|
|
5e8864c4ba | ||
|
|
a72ac09968 | ||
|
|
ce7197cc08 | ||
|
|
0a92a00e4f | ||
|
|
12774a7d22 | ||
|
|
db91c6c4d1 | ||
|
|
9a9839bf4d | ||
|
|
e595960521 | ||
|
|
e4f5ce6ee6 | ||
|
|
b93a4418cb | ||
|
|
e8b295f679 | ||
|
|
ed6dd74864 | ||
|
|
b556a55117 | ||
|
|
aeb1e994d2 | ||
|
|
f14138bbec | ||
|
|
99102b597e | ||
|
|
4d7c1964ec | ||
|
|
19bcf20933 | ||
|
|
35f280eae5 | ||
|
|
dc0e49706e | ||
|
|
dff2e08dd2 | ||
|
|
7b18ea984a | ||
|
|
5b208509a0 | ||
|
|
a2061e0731 | ||
|
|
267f9f988e | ||
|
|
f00e8f014b | ||
|
|
6ae6140e28 | ||
|
|
a7b2090893 | ||
|
|
c58dd8bd50 | ||
|
|
09eacc4294 | ||
|
|
9bd65dbc7e | ||
|
|
555c3db905 | ||
|
|
350b5fb159 | ||
|
|
5c45fe3faa | ||
|
|
b1ee5b8014 | ||
|
|
1ce3f6c150 | ||
|
|
ae5b3fa58a | ||
|
|
853e1a63bf | ||
|
|
bb0854c36e | ||
|
|
0b295bd3f7 | ||
|
|
08d8eb19d5 | ||
|
|
ab6bcf90d1 | ||
|
|
9fb8af9192 | ||
|
|
0e05048b79 | ||
|
|
ef6ae66a60 | ||
|
|
b37e4405a3 | ||
|
|
1451d7ef7a | ||
|
|
bda880ca42 | ||
|
|
55193a915b | ||
|
|
4e564a942a | ||
|
|
f80bfa17b6 | ||
|
|
776e79135a | ||
|
|
20cb9e856b | ||
|
|
5f8717652c | ||
|
|
ee689aba90 | ||
|
|
46eac6d5b9 | ||
|
|
351816775d | ||
|
|
a0f498e8d2 | ||
|
|
c8489fe6c7 | ||
|
|
d3aeac4a9d | ||
|
|
2b61e37be3 | ||
|
|
262573c6b7 | ||
|
|
969683c33b | ||
|
|
ba3fb7eec4 | ||
|
|
011e41a20d | ||
|
|
8736aea464 | ||
|
|
c79250581c | ||
|
|
d4d2a5b15b | ||
|
|
ed0008c5cd | ||
|
|
9cce32fc1f | ||
|
|
751b1d4c14 | ||
|
|
3e404f7a3b | ||
|
|
023968ca5e | ||
|
|
673330dc5f | ||
|
|
0ad1e5b653 | ||
|
|
1196038009 | ||
|
|
16589bfa17 | ||
|
|
be0264f5a2 | ||
|
|
b36f5032e9 | ||
|
|
d72064db50 | ||
|
|
df9d489142 | ||
|
|
5b0c9f3996 | ||
|
|
7731a4aa1a | ||
|
|
f082e00736 | ||
|
|
2928545f0b | ||
|
|
de15826ca2 | ||
|
|
a93f7e5dee | ||
|
|
0e2388ce8a | ||
|
|
37cdf77375 | ||
|
|
4b4a30a154 | ||
|
|
782454e3fd | ||
|
|
29fa03aba0 | ||
|
|
a3c4af776f | ||
|
|
a33ef36bd6 | ||
|
|
ea3b050ed6 | ||
|
|
e17e78a5d6 | ||
|
|
7f3a6133bc | ||
|
|
5240a04a68 | ||
|
|
f13ca82499 | ||
|
|
8133e5975c | ||
|
|
8cbc850861 | ||
|
|
8b3d450452 | ||
|
|
f7725392d9 | ||
|
|
e44ce19320 | ||
|
|
71ff372afc | ||
|
|
caea7b4efb | ||
|
|
ac323556b5 | ||
|
|
421022a576 | ||
|
|
2a0b98e423 | ||
|
|
4cc4adf61f | ||
|
|
bc308f3615 | ||
|
|
c5bf783bca | ||
|
|
a0531edf72 | ||
|
|
b85ebfa4c1 | ||
|
|
56ea91fa5b | ||
|
|
0ec0dc3043 | ||
|
|
73acd243e8 | ||
|
|
5f339944a3 | ||
|
|
8872f9634a | ||
|
|
7db6d945d6 | ||
|
|
630b48ffdf | ||
|
|
36a99c4587 | ||
|
|
485270a3b2 | ||
|
|
962e79f052 | ||
|
|
af7ef7286b | ||
|
|
e54a70ff32 | ||
|
|
7f949924c3 | ||
|
|
0045df452f | ||
|
|
89fdb0acaf | ||
|
|
50e3f88d97 | ||
|
|
68d49a81f8 | ||
|
|
8ec63574a3 | ||
|
|
1031230f1f | ||
|
|
6b3b16a9a4 | ||
|
|
9892d3705a | ||
|
|
06ead549e3 | ||
|
|
360c584f64 | ||
|
|
3bb5a988cf | ||
|
|
3442fce59a | ||
|
|
f83f55060a | ||
|
|
1e8dd2817a | ||
|
|
17a38d51d0 | ||
|
|
25af309ea9 | ||
|
|
c37e9526c6 | ||
|
|
29720e6e64 | ||
|
|
26fbca754f | ||
|
|
a04fbabee9 | ||
|
|
a142b96bdf | ||
|
|
a3fb8d3637 | ||
|
|
c19bcdb4fc | ||
|
|
b54d674d5b | ||
|
|
a60bde4b45 | ||
|
|
82323f0a95 | ||
|
|
d44d995a10 | ||
|
|
87cb633a4b | ||
|
|
d7bc35f807 | ||
|
|
2a93669740 | ||
|
|
174ab3349f | ||
|
|
999bfa4f46 | ||
|
|
a719ab4fa9 | ||
|
|
dfe9b7a59e | ||
|
|
e77852afea | ||
|
|
f97f9a0a91 | ||
|
|
b9d9ae55d3 | ||
|
|
8dc54096e3 | ||
|
|
5b6c11b730 | ||
|
|
c6b699eb64 | ||
|
|
b9a034a276 | ||
|
|
ca4f8c68be | ||
|
|
140157277f | ||
|
|
734a476ece | ||
|
|
6252de4707 | ||
|
|
3c34c532d0 | ||
|
|
b1b5c9f660 | ||
|
|
ba43305169 | ||
|
|
bd8d35fbb0 | ||
|
|
e4ac865301 | ||
|
|
ce91803b7a | ||
|
|
adeab43b5a | ||
|
|
c8d47f10f6 | ||
|
|
ff9c75bc55 | ||
|
|
24db2ecd16 | ||
|
|
bea8523556 | ||
|
|
9c863ba2a0 | ||
|
|
63de82aea2 | ||
|
|
5d512ed857 | ||
|
|
bc9ec03949 | ||
|
|
495266b3d7 | ||
|
|
fbb9ef7e16 | ||
|
|
4a8d7cf5b2 | ||
|
|
9345081327 | ||
|
|
9d810abaa2 | ||
|
|
e07f6f2f60 | ||
|
|
be5b82fc1c | ||
|
|
8487068b03 | ||
|
|
9764e292a9 | ||
|
|
79ca33f16b | ||
|
|
e61f0e6acd | ||
|
|
62c11b4ba3 | ||
|
|
14eb024001 | ||
|
|
d7880064d5 | ||
|
|
10c9b11f21 | ||
|
|
8c096b7203 | ||
|
|
ccc1ff32b0 | ||
|
|
e85aa18487 | ||
|
|
bd41276881 | ||
|
|
34a495a878 | ||
|
|
a08cf71ca7 | ||
|
|
e2d23cda41 | ||
|
|
d9a26c4f04 | ||
|
|
42449efc05 | ||
|
|
3c72804cb2 | ||
|
|
2a1618eed1 | ||
|
|
8dae776c87 | ||
|
|
a7e3eb01fb | ||
|
|
1db23f2e7d | ||
|
|
d917a6856f | ||
|
|
aa086100c4 | ||
|
|
a28f7a2e4e | ||
|
|
f46a4e15bc | ||
|
|
ecdfd47ce7 | ||
|
|
fd4a4223df | ||
|
|
2b051a5859 | ||
|
|
72f48d5a02 | ||
|
|
a4e19d9a76 | ||
|
|
95b320258f | ||
|
|
7707fdf552 | ||
|
|
c41960fb4e | ||
|
|
ae77b1eb49 | ||
|
|
badf8fdd13 | ||
|
|
5eb021421c | ||
|
|
88ff9f0411 | ||
|
|
224a922ecb | ||
|
|
34ba3a9845 | ||
|
|
db207e34f8 | ||
|
|
723928d02a | ||
|
|
b987a3da83 | ||
|
|
54d6c589cb | ||
|
|
60af28d6f7 | ||
|
|
df073bb29e | ||
|
|
381cc7fef5 | ||
|
|
ae0f441388 | ||
|
|
db773ab6dc | ||
|
|
915f05565d | ||
|
|
eb0c971daf | ||
|
|
2346357bc1 | ||
|
|
cd5fdf55e4 | ||
|
|
a0dc27fafd | ||
|
|
527c24d597 | ||
|
|
b8ad8bf266 | ||
|
|
847a6b5a27 | ||
|
|
e728eecc33 | ||
|
|
326de0cddf | ||
|
|
6dc44e028f | ||
|
|
ec185bfefa | ||
|
|
912657dcf4 | ||
|
|
7e0c38d097 | ||
|
|
4dbe6b8c98 | ||
|
|
8df9f7bdf5 | ||
|
|
97cca8240c | ||
|
|
6aa8bcc853 | ||
|
|
7fcf8da378 | ||
|
|
4feef848b8 | ||
|
|
5321cc0ca6 | ||
|
|
a7685e6c86 | ||
|
|
a75c2f286f | ||
|
|
05349fd5f4 | ||
|
|
bbfc00c39c | ||
|
|
e2e1e8e7c1 | ||
|
|
65bd40e77e | ||
|
|
52d7524868 | ||
|
|
81023c8e99 | ||
|
|
697f2820c1 | ||
|
|
2a555e44ec | ||
|
|
6d8f528ee4 | ||
|
|
32dbf00695 | ||
|
|
e9d741b411 | ||
|
|
088c159ba4 | ||
|
|
cb185f90d1 | ||
|
|
f36db37acd | ||
|
|
75d5d39208 | ||
|
|
d0c6c8b8b4 | ||
|
|
bd0cac80ac | ||
|
|
4031b67417 | ||
|
|
f2aa80352b | ||
|
|
26624ece0b | ||
|
|
86ac11e74e | ||
|
|
62abf6c0d3 | ||
|
|
96afa81b30 | ||
|
|
726ec9715b | ||
|
|
3a6eabb2b0 | ||
|
|
b9175cda67 | ||
|
|
d8e74c9a66 | ||
|
|
0d2508cfd8 | ||
|
|
0602443410 | ||
|
|
bbf45555f9 | ||
|
|
1a4161bc4e | ||
|
|
0a8349beac | ||
|
|
600292cab8 | ||
|
|
bc6e6e6fe4 | ||
|
|
c36870e49c | ||
|
|
3a381a3425 | ||
|
|
db50d96160 | ||
|
|
ce2d1d0a3b | ||
|
|
affa2b3fd1 | ||
|
|
c1784b388f | ||
|
|
0e4b6df7af | ||
|
|
3f0a7757eb | ||
|
|
a4d0e78887 | ||
|
|
898e4e2ad7 | ||
|
|
0a6521de02 | ||
|
|
05bdb689f2 | ||
|
|
6cc270264b | ||
|
|
57b7fe7d65 | ||
|
|
fd4f1f24b0 | ||
|
|
bac86e7ca1 | ||
|
|
8c94e339dd | ||
|
|
07a5f7f88f | ||
|
|
544165e172 | ||
|
|
ee13662274 | ||
|
|
db8676b73e | ||
|
|
b363c0341c | ||
|
|
8606ff3aa2 | ||
|
|
c7625052ba | ||
|
|
eb22a74f1f | ||
|
|
a858c7046e | ||
|
|
5944ef48c4 | ||
|
|
f5aa0c93e6 | ||
|
|
90ffa23561 | ||
|
|
cca10c3bd4 | ||
|
|
3a8c21ae48 | ||
|
|
dfdfbed093 | ||
|
|
b302eb9714 | ||
|
|
dd9cb21d5c | ||
|
|
0dd61c2e0d | ||
|
|
b5d253d6db | ||
|
|
da4c4e24c5 | ||
|
|
32ae60eadf | ||
|
|
7d28684e55 | ||
|
|
7eaffbed80 | ||
|
|
ee5830808e | ||
|
|
72ca0e34a1 | ||
|
|
3106d3e7be | ||
|
|
c1a7f6ffb0 | ||
|
|
d4f0006825 | ||
|
|
5af41219a3 | ||
|
|
e5031aadb4 | ||
|
|
e8b9b64afc | ||
|
|
a9975152b7 | ||
|
|
db5a85fc43 | ||
|
|
fcedcd878b | ||
|
|
a58cc3dc72 | ||
|
|
e699b6cce5 | ||
|
|
3e82bb8707 | ||
|
|
511d5cafdb | ||
|
|
23b1718396 | ||
|
|
9a70909b6b | ||
|
|
d652dc6d66 | ||
|
|
c27b8dfeed | ||
|
|
5532c6bc57 | ||
|
|
10fbf62e00 | ||
|
|
0a3e5c9770 | ||
|
|
15bdd187c8 | ||
|
|
cb8ec03d95 | ||
|
|
718f352838 | ||
|
|
c7794747ee | ||
|
|
b7ce513320 | ||
|
|
da02d4d273 | ||
|
|
22a27773fd | ||
|
|
c94e81c198 | ||
|
|
5aa54969af | ||
|
|
92b5a9b8e4 | ||
|
|
c8ad52884a | ||
|
|
2dd77da097 | ||
|
|
dc8f9bf4ea | ||
|
|
946ad7b9bd | ||
|
|
77100a81dc | ||
|
|
71cefc3d9a | ||
|
|
5b6b23f656 | ||
|
|
1d6460398a | ||
|
|
0fcef3cca8 | ||
|
|
4fb77ddba8 | ||
|
|
e7226b42d4 | ||
|
|
8ec1a1a9b5 | ||
|
|
0e89c23b14 | ||
|
|
3213ce72c5 | ||
|
|
ce62a721e4 | ||
|
|
5600bcc5ef | ||
|
|
7d7cc3cf0a | ||
|
|
806e7cbc3a | ||
|
|
f766f25d97 | ||
|
|
a4449af635 | ||
|
|
0791f53e89 | ||
|
|
56f76fba19 | ||
|
|
f5ad17a9a0 | ||
|
|
9119a35939 | ||
|
|
8efa004e28 | ||
|
|
58751b98d8 | ||
|
|
f0a77ca965 | ||
|
|
7da64a6ac2 | ||
|
|
d7c09e862a | ||
|
|
354acdb9f5 | ||
|
|
8d4f482d75 | ||
|
|
bec4dbca62 | ||
|
|
2becded5d3 | ||
|
|
ed9e9f610e | ||
|
|
890a5bcd90 | ||
|
|
821c5fb519 | ||
|
|
8a36da0605 | ||
|
|
f8c6a806eb | ||
|
|
5e99ab660b | ||
|
|
a5c271c7b8 | ||
|
|
f907d052b3 | ||
|
|
3ed5046777 | ||
|
|
c1b243625b | ||
|
|
fa56edd3f4 | ||
|
|
0de1ae65a9 | ||
|
|
4aec7abf93 | ||
|
|
05795e517c | ||
|
|
d7d77f71dd | ||
|
|
5cc915f7f5 | ||
|
|
c2f30f0bf9 | ||
|
|
b65ae950fd | ||
|
|
9d39afd88c | ||
|
|
b80a644f65 | ||
|
|
6d92cd441c | ||
|
|
f5e366c6cf | ||
|
|
adec7629f8 | ||
|
|
287dcffa0e | ||
|
|
d233044274 | ||
|
|
2b21baa935 | ||
|
|
dcf3d00277 | ||
|
|
e4613936f5 | ||
|
|
67aacbb0f6 | ||
|
|
7833d53b2a | ||
|
|
5549a53791 | ||
|
|
4f2bc499a7 | ||
|
|
a5e44a583e | ||
|
|
6ba785704a | ||
|
|
fdd014cf11 | ||
|
|
6346c32e76 | ||
|
|
7835f5b2d2 | ||
|
|
9023c154f4 | ||
|
|
6a2c3fc023 | ||
|
|
c6f27d3b0a | ||
|
|
b8f48fb5b6 | ||
|
|
e7301f1b96 | ||
|
|
ead8bf08fa | ||
|
|
88091e8bd5 | ||
|
|
9fac786681 | ||
|
|
7d1bc6e7e7 | ||
|
|
ce52e367df | ||
|
|
fe69c1801b | ||
|
|
2bef15a06e | ||
|
|
de6b97e368 | ||
|
|
33ecddda9b | ||
|
|
02cf0aa02f | ||
|
|
c93334581c | ||
|
|
e14ebf5b92 | ||
|
|
ae888805ee | ||
|
|
13dbb1f269 | ||
|
|
d24a99f129 | ||
|
|
fa13901bd9 | ||
|
|
891e23fcaa | ||
|
|
ba716a34e3 | ||
|
|
9096bf8f2a | ||
|
|
fe71f6f426 | ||
|
|
8ec4bc84b8 | ||
|
|
ecba39dce0 | ||
|
|
d8241e10ff | ||
|
|
6a6af800fc | ||
|
|
372ac1af04 | ||
|
|
bf42081f01 | ||
|
|
de53aa17ab | ||
|
|
f28200a3a2 | ||
|
|
85cc15dc17 | ||
|
|
4478b49365 | ||
|
|
85ee3ed03c | ||
|
|
f288145889 | ||
|
|
0488add6e4 | ||
|
|
ed37a5154d | ||
|
|
97bc4cb261 | ||
|
|
56ab0be42b | ||
|
|
d97bd0ea92 | ||
|
|
a166637d0b | ||
|
|
9ca7937111 | ||
|
|
a769c80da3 | ||
|
|
ae32cf5685 | ||
|
|
b537680089 | ||
|
|
87714d2530 | ||
|
|
9eaf9c2c08 | ||
|
|
9e8bb77f40 | ||
|
|
251fde4d47 | ||
|
|
c79b4ea86b | ||
|
|
13418d82fc | ||
|
|
78208ce079 | ||
|
|
09c2d71979 | ||
|
|
905d721fe0 | ||
|
|
f831dbc917 | ||
|
|
12deda8cca | ||
|
|
b42cf0dfd8 | ||
|
|
d46be39dad | ||
|
|
2ee616830d | ||
|
|
9813140719 | ||
|
|
9308c63973 | ||
|
|
0ac4d085d5 | ||
|
|
3f32a8acab | ||
|
|
9e03d300ca | ||
|
|
87e6cdd615 | ||
|
|
4dbab06960 | ||
|
|
a462c3a402 | ||
|
|
a56fee8584 | ||
|
|
1411086762 | ||
|
|
fce23ebcbd | ||
|
|
2b89ada006 | ||
|
|
551f1e90fe | ||
|
|
982d300641 | ||
|
|
ca7e339de9 | ||
|
|
d27b0ec044 | ||
|
|
64c5292c2d | ||
|
|
d932116ceb | ||
|
|
5b0738e762 | ||
|
|
06c40dbaf4 | ||
|
|
17b709a19b | ||
|
|
79c0d8c57c | ||
|
|
6e1d0b8885 | ||
|
|
6c64e4ac46 | ||
|
|
d4fd707535 | ||
|
|
faef8d406a | ||
|
|
73bda87c3e | ||
|
|
106952d3eb | ||
|
|
b3241b1cdb | ||
|
|
b70003dfd5 | ||
|
|
0c9005dc80 | ||
|
|
ca24b1500f | ||
|
|
423b89f686 | ||
|
|
3e1ed57a8c | ||
|
|
bf1d760602 | ||
|
|
45ffdee8ec | ||
|
|
0b025df98b | ||
|
|
f074aac8df | ||
|
|
a0d711ebac | ||
|
|
37738c6bcc | ||
|
|
41b3250838 | ||
|
|
499cbf222f | ||
|
|
6f005c2123 | ||
|
|
c21035e27f | ||
|
|
c892acaa0e | ||
|
|
dc2fd4768b | ||
|
|
4275054845 | ||
|
|
e964f22a93 | ||
|
|
b804d7255d | ||
|
|
b32290c444 | ||
|
|
d1ea01c1c4 | ||
|
|
c69097a351 | ||
|
|
a8c765447e | ||
|
|
1809c3fa2a | ||
|
|
a4b6cdc064 | ||
|
|
381a5e0c08 | ||
|
|
662ff67685 | ||
|
|
fd72a77bb4 | ||
|
|
52ba2bda17 | ||
|
|
d1e245d09f | ||
|
|
70850f9c0a | ||
|
|
9e40d147fb | ||
|
|
3945b08218 | ||
|
|
3ad42e3259 | ||
|
|
5aaf16c9ea | ||
|
|
d21e33def5 | ||
|
|
3f98b66dd9 | ||
|
|
dbb6099c44 | ||
|
|
8e38cf6c7d | ||
|
|
3872f059bb | ||
|
|
d1d39af68a | ||
|
|
abbefa322e | ||
|
|
807fb8f14f | ||
|
|
e4f1dcd242 | ||
|
|
1e61d27d6c | ||
|
|
ec3112ab6d | ||
|
|
6b93b9a822 | ||
|
|
a2c66417b4 | ||
|
|
d9b1060dd0 | ||
|
|
78b5de7006 | ||
|
|
e72389f087 | ||
|
|
164a5b6e71 | ||
|
|
6e493f6fc2 | ||
|
|
ef0887366b | ||
|
|
950d064617 | ||
|
|
e2bfebd0ba | ||
|
|
0b42ed0a8b | ||
|
|
7c7189182e | ||
|
|
0b2ddbb7a0 | ||
|
|
c864faa9b3 | ||
|
|
e2479176c2 | ||
|
|
2f69f23092 | ||
|
|
31577fba21 | ||
|
|
d117b56a42 | ||
|
|
9145d1924f | ||
|
|
9949eb6ebf | ||
|
|
4cbed75399 | ||
|
|
85b7cf4cd5 | ||
|
|
38a779b421 | ||
|
|
4e3178cabd | ||
|
|
e95814acf3 | ||
|
|
ccfafef37b | ||
|
|
cd82e74b6d | ||
|
|
bc65bb6cf7 | ||
|
|
fb10cf9f8e | ||
|
|
345b7f9732 | ||
|
|
282d4ecedb | ||
|
|
31b6f612f4 | ||
|
|
8133c20355 | ||
|
|
8b41b66675 | ||
|
|
61cf3139ec | ||
|
|
31879f80e0 | ||
|
|
c3286dbcbf | ||
|
|
7559575daf | ||
|
|
ccf040d55a | ||
|
|
d532dd2448 | ||
|
|
ed31cfc681 | ||
|
|
cf2aee3bb8 | ||
|
|
105e605060 | ||
|
|
d4f0de607f | ||
|
|
2ee42e98fa | ||
|
|
77e687ced5 | ||
|
|
d1e5759949 | ||
|
|
52d988ba94 | ||
|
|
bddf020b32 | ||
|
|
2607293ad0 | ||
|
|
5501eca6a0 | ||
|
|
5e0598215b | ||
|
|
b6ee5f0b69 | ||
|
|
80451f8f0f | ||
|
|
7075152c76 | ||
|
|
7657d51662 | ||
|
|
a7d56d4a8c | ||
|
|
95108ed7c0 | ||
|
|
aab83d25f1 | ||
|
|
297605e8a8 | ||
|
|
846732c458 | ||
|
|
6baeff690f | ||
|
|
223133728a | ||
|
|
c4815a4aad | ||
|
|
f7d5854a19 | ||
|
|
4837d2bf23 | ||
|
|
44cb44db9f | ||
|
|
7afc54a81f | ||
|
|
929ae95c0f | ||
|
|
4580ed339d | ||
|
|
c92951f4d1 | ||
|
|
6e28c92f35 | ||
|
|
d6f2e803e9 | ||
|
|
dd7a6641ad | ||
|
|
5afab82762 | ||
|
|
b90809c48b | ||
|
|
29bbfa6fb7 | ||
|
|
f22d58dc74 | ||
|
|
a2a8f62cc4 | ||
|
|
0d39fe65b3 | ||
|
|
db00c0ebfb | ||
|
|
83be9672ae | ||
|
|
43b4f0e9e9 | ||
|
|
953f216bda | ||
|
|
d93cdfa871 | ||
|
|
40b10d3924 | ||
|
|
6d48c9fe41 | ||
|
|
355421f25b | ||
|
|
f72cd2d731 | ||
|
|
d8274bd04b | ||
|
|
aa6fef3c40 | ||
|
|
fb66d3ffb6 | ||
|
|
ee39c160b8 | ||
|
|
2115d2c079 | ||
|
|
25c3151722 | ||
|
|
ec09fcfd91 | ||
|
|
435d84e6a2 | ||
|
|
1d6b90d442 | ||
|
|
aa475b57b0 | ||
|
|
eb36ccc8e8 | ||
|
|
8a6c0315ed | ||
|
|
85092a63da | ||
|
|
9bf0d5ca11 | ||
|
|
fa7a57310f | ||
|
|
c2bfa12c45 | ||
|
|
59bb2d4093 | ||
|
|
151269ed6e | ||
|
|
d29290bf33 | ||
|
|
4a81764e77 | ||
|
|
1df9faf624 | ||
|
|
dee27344c1 | ||
|
|
fe797dd89b | ||
|
|
b84d48ad89 | ||
|
|
522e9a7ba3 | ||
|
|
51c51e1622 | ||
|
|
28846aa431 | ||
|
|
64eabb0f99 | ||
|
|
ac8c7dc395 | ||
|
|
a6ed8984f0 | ||
|
|
accf389a25 | ||
|
|
68c11271ec | ||
|
|
722b8cfad9 | ||
|
|
090aff5828 | ||
|
|
011e3a82b2 | ||
|
|
f24b02c211 | ||
|
|
0da9de5c98 | ||
|
|
a47c473aad | ||
|
|
fe2ad7b057 | ||
|
|
356fbe5577 | ||
|
|
5bd822ba7e | ||
|
|
90fdc9ec5a | ||
|
|
2e367804a9 | ||
|
|
95885b8edc | ||
|
|
99f0aaa907 | ||
|
|
5ca7f478a8 | ||
|
|
5887cc4e58 | ||
|
|
43da5d3ca2 | ||
|
|
6b96bb61f6 | ||
|
|
3f3bbb3ce0 | ||
|
|
b7b5d06c04 | ||
|
|
6714b55cdb | ||
|
|
6929e6f971 | ||
|
|
6832afc263 | ||
|
|
430e8d2564 | ||
|
|
c3a66e7a29 | ||
|
|
09d94b02b8 | ||
|
|
89c5c170da | ||
|
|
e96f28bf52 | ||
|
|
f419a278de | ||
|
|
ba05785465 | ||
|
|
0ecbefb478 | ||
|
|
c3866ed5e8 | ||
|
|
59ef7335c5 | ||
|
|
268d4dc27f | ||
|
|
73223e0287 | ||
|
|
e0f8742f7e | ||
|
|
f81407f5a5 | ||
|
|
48ca11a839 | ||
|
|
087fc83373 | ||
|
|
b6a037d529 | ||
|
|
d9f2694feb | ||
|
|
e9cb267b73 | ||
|
|
82f6ee42b6 | ||
|
|
cfba6fd92f | ||
|
|
afeed07245 | ||
|
|
c364ee05cf | ||
|
|
8e370bf067 | ||
|
|
4fd4eff8e6 | ||
|
|
8019bc63f8 | ||
|
|
0acda9915d | ||
|
|
de362015da | ||
|
|
3a570f58ee | ||
|
|
af906b7031 | ||
|
|
ae734570fd | ||
|
|
dd04302da0 | ||
|
|
c7d450f936 | ||
|
|
e8729ddfac | ||
|
|
2305ae7a38 | ||
|
|
42aaf72c01 | ||
|
|
f987808bc1 | ||
|
|
65d624b41e | ||
|
|
9b32310d5c | ||
|
|
e5be1f0071 | ||
|
|
20ad6948fc | ||
|
|
ca22c4e454 | ||
|
|
fa5291e120 | ||
|
|
50af2aed48 | ||
|
|
9080b69020 | ||
|
|
04d31a7369 | ||
|
|
e72a754fa4 | ||
|
|
400a8b44ca | ||
|
|
3510d794d8 | ||
|
|
b8e1f1d11d | ||
|
|
6cc0116cdf | ||
|
|
f3038c9609 | ||
|
|
0ef3819222 | ||
|
|
c06697bf8f | ||
|
|
1ab7a88af9 | ||
|
|
77ec2e3726 | ||
|
|
12daf19d5b | ||
|
|
e29bab8ffe | ||
|
|
1143cd1015 | ||
|
|
d7ce21b3b4 | ||
|
|
a94a03cb59 | ||
|
|
54fc25409e | ||
|
|
ab4bc47a07 | ||
|
|
a85d57402c | ||
|
|
c26fb7d27d | ||
|
|
170dfeeecd | ||
|
|
ebe30818e2 | ||
|
|
1d8123d735 | ||
|
|
cfa9c4886a | ||
|
|
de8511e396 | ||
|
|
55368b5254 | ||
|
|
db4f20e2f5 | ||
|
|
b19f892689 | ||
|
|
34d1221ef7 | ||
|
|
c439b51275 | ||
|
|
0041f25619 | ||
|
|
180e3a353a | ||
|
|
dbaa53d12e | ||
|
|
4e2bca8799 | ||
|
|
dfe6a27785 | ||
|
|
120b5df54e | ||
|
|
298c55e75c | ||
|
|
6f604e8b36 |
+11
-21
@@ -12,8 +12,9 @@ updates:
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "22:00"
|
||||
timezone: Africa/Abidjan
|
||||
open-pull-requests-limit: 99
|
||||
timezone: "Europe/London"
|
||||
# disabling versions updates, when set to 0 only security updates will open
|
||||
open-pull-requests-limit: 0
|
||||
ignore:
|
||||
# Upstream alfresco-community-repo artifacts
|
||||
- dependency-name: org.alfresco:alfresco-community-repo
|
||||
@@ -28,13 +29,6 @@ updates:
|
||||
- dependency-name: org.alfresco.tas:alfresco-community-repo-integration-test:tests
|
||||
- dependency-name: org.alfresco.tas:alfresco-community-repo-email-test:tests
|
||||
- dependency-name: org.alfresco.tas:alfresco-community-repo-cmis-test:tests
|
||||
# Used in dev env - Currently using 7.0.86 but have tried higher versions
|
||||
- dependency-name: org.apache.tomcat.embed
|
||||
versions:
|
||||
- "> 7.0.109"
|
||||
- dependency-name: org.apache.tomcat
|
||||
versions:
|
||||
- "> 7.0.109"
|
||||
# Upstream alfresco-enterprise-share artifacts
|
||||
- dependency-name: org.alfresco:share:classes
|
||||
- dependency-name: org.alfresco:alfresco-wcmqs-web:classes
|
||||
@@ -43,20 +37,16 @@ updates:
|
||||
- dependency-name: org.alfresco:alfresco-content-services-share-distribution
|
||||
- dependency-name: org.alfresco:alfresco-share-services
|
||||
# Others
|
||||
- dependency-name: io.fabric8:fabric8-maven-plugin
|
||||
versions:
|
||||
- "> 4.4.1"
|
||||
- dependency-name: org.alfresco:api-explorer
|
||||
versions:
|
||||
- "> 6.1.0, < 6.2"
|
||||
- dependency-name: org.alfresco.integrations:alfresco-googledocs-repo-community
|
||||
versions:
|
||||
- "> 3.1.0"
|
||||
- dependency-name: org.alfresco.integrations:alfresco-googledocs-share-community
|
||||
versions:
|
||||
- "> 3.1.0"
|
||||
- dependency-name: org.apache.maven.plugins:maven-war-plugin
|
||||
versions:
|
||||
- ">= 3.a, < 4"
|
||||
registries:
|
||||
- maven-repository-artifacts-alfresco-com-nexus-content-groups-int
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
name: ACS Community Packaging CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- release/**
|
||||
- feature/**
|
||||
- fix/**
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- release/**
|
||||
- feature/**
|
||||
- fix/**
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
JAVA_VERSION: "21"
|
||||
GIT_USERNAME: ${{ secrets.BOT_GITHUB_USERNAME }}
|
||||
GIT_EMAIL: ${{ secrets.BOT_GITHUB_EMAIL }}
|
||||
GIT_PASSWORD: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
GITHUB_ACTIONS_DEPLOY_TIMEOUT: 60
|
||||
MAVEN_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}
|
||||
MAVEN_USERNAME: ${{ secrets.NEXUS_USERNAME }}
|
||||
QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
|
||||
QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }}
|
||||
TAS_ENVIRONMENT: ./tests/environment
|
||||
TAS_SCRIPTS: ../alfresco-community-repo/packaging/tests/scripts
|
||||
# Release version has to start with real version (23.2.0-....) for the docker image to build successfully.
|
||||
RELEASE_VERSION: 26.2.0-A.30
|
||||
DEVELOPMENT_VERSION: 26.2.0-A.31-SNAPSHOT
|
||||
|
||||
jobs:
|
||||
precommit:
|
||||
name: Pre-Commit checks
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
!contains(github.event.head_commit.message, '[skip tests]')
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||
with:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/pre-commit@v8.16.0
|
||||
|
||||
tas_tests:
|
||||
name: ${{ matrix.testSuite }} TAS tests
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !contains(github.event.head_commit.message, '[skip tas]') && !contains(github.event.head_commit.message, '[skip tests]') }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- testSuite: REST API part1
|
||||
pom: tests/tas-restapi/pom.xml
|
||||
profiles: all-tas-tests,run-restapi-part1
|
||||
compose-file: docker-compose-minimal+transforms.yml
|
||||
- testSuite: REST API part2
|
||||
pom: tests/tas-restapi/pom.xml
|
||||
profiles: all-tas-tests,run-restapi-part2
|
||||
compose-file: docker-compose-minimal+transforms.yml
|
||||
- testSuite: REST API part3
|
||||
pom: tests/tas-restapi/pom.xml
|
||||
profiles: all-tas-tests,run-restapi-part3
|
||||
compose-file: docker-compose-minimal+transforms.yml
|
||||
- testSuite: CMIS (BROWSER binding)
|
||||
pom: tests/tas-cmis/pom.xml
|
||||
profiles: all-tas-tests,run-cmis-browser
|
||||
compose-file: docker-compose-minimal+transforms.yml
|
||||
- testSuite: CMIS (ATOM binding)
|
||||
pom: tests/tas-cmis/pom.xml
|
||||
profiles: all-tas-tests,run-cmis-atom
|
||||
compose-file: docker-compose-minimal+transforms.yml
|
||||
- testSuite: CMIS (WEBSERVICES binding)
|
||||
pom: tests/tas-cmis/pom.xml
|
||||
profiles: all-tas-tests,run-cmis-webservices
|
||||
compose-file: docker-compose-minimal+transforms.yml
|
||||
- testSuite: Email
|
||||
pom: tests/tas-email/pom.xml
|
||||
profiles: all-tas-tests
|
||||
compose-file: docker-compose-minimal.yml
|
||||
- testSuite: WebDAV
|
||||
pom: tests/tas-webdav/pom.xml
|
||||
profiles: all-tas-tests
|
||||
compose-file: docker-compose-minimal.yml
|
||||
- testSuite: Integration
|
||||
pom: tests/tas-integration/pom.xml
|
||||
profiles: all-tas-tests
|
||||
compose-file: docker-compose-minimal.yml
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||
with:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
- name: "Init"
|
||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||
run: |
|
||||
bash ./scripts/ci/init.sh
|
||||
bash ./scripts/ci/build.sh
|
||||
- name: "Setup the environment"
|
||||
run: |
|
||||
${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/${{ matrix.compose-file }}
|
||||
${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
|
||||
- name: "Test"
|
||||
run: mvn -B install -ntp -f ${{ matrix.pom }} -P${{ matrix.profiles }} -Denvironment=default -DrunBugs=false
|
||||
- name: "Clean Maven cache"
|
||||
run: bash ./scripts/ci/cleanup_cache.sh
|
||||
|
||||
tas_tests_search_api:
|
||||
name: ${{ matrix.testSuite }} | TAS tests (Search API)
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
(github.ref_name == 'master' || startsWith(github.ref_name, 'release/') || github.event_name == 'pull_request') &&
|
||||
!contains(github.event.head_commit.message, '[skip search]') &&
|
||||
!contains(github.event.head_commit.message, '[skip tests]')
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- testSuite: Elasticsearch postgreSQL
|
||||
profiles: all-tas-tests,elastic
|
||||
search-engine-type: elasticsearch
|
||||
db-type: postgresql
|
||||
- testSuite: Elasticsearch MySQL
|
||||
profiles: all-tas-tests,elastic
|
||||
search-engine-type: elasticsearch
|
||||
db-type: mysql
|
||||
- testSuite: Elasticsearch Maria DB 11.8
|
||||
profiles: all-tas-tests,elastic
|
||||
search-engine-type: elasticsearch
|
||||
db-type: mariadb
|
||||
- testSuite: Opensearch
|
||||
profiles: all-tas-tests,elastic
|
||||
search-engine-type: opensearch
|
||||
db-type: postgresql
|
||||
- testSuite: Elasticsearch Basic Auth postgreSQL
|
||||
profiles: all-tas-tests,elastic-basic-auth
|
||||
search-engine-type: elasticsearch
|
||||
db-type: postgresql
|
||||
- testSuite: Elasticsearch Basic Auth MySQL
|
||||
profiles: all-tas-tests,elastic-basic-auth
|
||||
search-engine-type: elasticsearch
|
||||
db-type: mysql
|
||||
- testSuite: Elasticsearch Basic Auth MariaDB 11.8
|
||||
profiles: all-tas-tests,elastic-basic-auth
|
||||
search-engine-type: elasticsearch
|
||||
db-type: mariadb
|
||||
- testSuite: Opensearch Basic Auth
|
||||
profiles: all-tas-tests,elastic-basic-auth
|
||||
search-engine-type: opensearch
|
||||
db-type: postgresql
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v12.4.3
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v12.4.3
|
||||
with:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
- name: "Build"
|
||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||
run: |
|
||||
bash ./scripts/ci/init.sh
|
||||
bash ./scripts/ci/build.sh
|
||||
- name: "Run tests"
|
||||
id: tests
|
||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||
run: |
|
||||
set -a
|
||||
source ${TAS_ENVIRONMENT}/.env
|
||||
set +a
|
||||
BATCH_INDEXING_TAG="${BATCH_INDEXING_TAG//[[:space:]]/}"
|
||||
mvn -B -ntp -q install -N -f tests/pom.xml
|
||||
mvn -B -ntp -q install -DskipTests -f tests/testcontainers-env/pom.xml
|
||||
mvn -B install -ntp -f tests/tas-elasticsearch/pom.xml -P${{ matrix.profiles }} -Denvironment=default -DrunBugs=false "-Dsearch.engine.type=${{ matrix.search-engine-type }}" "-Ddatabase.type=${{ matrix.db-type }}" -Dindeximage="alfresco/alfresco-elasticsearch-batch-indexing:${BATCH_INDEXING_TAG}" -Drepoimage="alfresco-repository-databases:latest"
|
||||
- name: "Dump all Docker containers logs"
|
||||
uses: Alfresco/alfresco-build-tools/.github/actions/docker-dump-containers-logs@v12.4.3
|
||||
if: failure() && steps.tests.outcome == 'failure'
|
||||
- name: "Clean Maven cache"
|
||||
run: bash ./scripts/ci/cleanup_cache.sh
|
||||
|
||||
|
||||
community_zip_tests:
|
||||
name: Community Distribution Zip content tests
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.event.head_commit.message, '[skip tests]')"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||
with:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
- name: "Init"
|
||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||
run: |
|
||||
bash ./scripts/ci/init.sh
|
||||
bash ./scripts/ci/build.sh
|
||||
- name: "Build"
|
||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||
run: mvn -B -ntp -V clean install -Pags -DskipTests -Dmaven.javadoc.skip=true
|
||||
- name: "Test"
|
||||
run: mvn -B install -ntp -f tests/tas-distribution-zip/pom.xml -Prun-distribution-zip-contents-check -DrunBugs=false
|
||||
- name: "Clean Maven cache"
|
||||
run: bash ./scripts/ci/cleanup_cache.sh
|
||||
|
||||
community_test_tomcat_deployment:
|
||||
name: "Test Tomcat deployment"
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
!contains(github.event.head_commit.message, '[skip tests]')
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||
with:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
- name: "Build"
|
||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||
run: |
|
||||
bash ./scripts/ci/init.sh
|
||||
bash ./scripts/ci/build.sh
|
||||
- name: "Set up the environment"
|
||||
run: |
|
||||
${TAS_SCRIPTS}/start-compose.sh ./dev/docker-compose.yml
|
||||
${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8983/solr"
|
||||
- name: "Run tomcat, verify it starts"
|
||||
id: tests
|
||||
run: |
|
||||
(mvn clean install -Prun -rf dev &)
|
||||
${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8080/alfresco/"
|
||||
echo "Test Alfresco Repository availability"
|
||||
curl -Ls -o /dev/null --fail "http://localhost:8080/alfresco"
|
||||
echo "Test Share availability"
|
||||
curl -Ls -o /dev/null --fail "http://localhost:8080/share"
|
||||
|
||||
docker_latest:
|
||||
name: Update latest Docker images
|
||||
runs-on: ubuntu-latest
|
||||
needs: [tas_tests, community_zip_tests]
|
||||
if: >
|
||||
!(failure() || cancelled()) &&
|
||||
!contains(github.event.head_commit.message, '[skip docker_latest]') &&
|
||||
(github.ref_name == 'master' || startsWith(github.ref_name, 'release/')) &&
|
||||
github.event_name != 'pull_request'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||
with:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: "Init"
|
||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||
run: |
|
||||
bash ./scripts/ci/init.sh
|
||||
bash ./scripts/ci/build.sh -m
|
||||
- name: "Push Docker images"
|
||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||
run: mvn -B -ntp -V clean install -DskipTests -Dmaven.javadoc.skip=true -Pags -Ppush-docker-images
|
||||
- name: "Clean Maven cache"
|
||||
run: bash ./scripts/ci/cleanup_cache.sh
|
||||
|
||||
release:
|
||||
name: Release and Copy to S3 Staging Bucket
|
||||
runs-on: ubuntu-latest
|
||||
needs: [docker_latest]
|
||||
if: >
|
||||
!(failure() || cancelled()) &&
|
||||
contains(github.event.head_commit.message, '[release]') &&
|
||||
(github.ref_name == 'master' || startsWith(github.ref_name, 'release/')) &&
|
||||
github.event_name != 'pull_request'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||
with:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: "Init"
|
||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||
run: |
|
||||
bash ./scripts/ci/init.sh
|
||||
bash ./scripts/ci/build.sh -m
|
||||
- name: "Release"
|
||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||
run: |
|
||||
bash scripts/ci/verify_release_tag.sh
|
||||
bash scripts/ci/maven_release.sh
|
||||
source scripts/ci/prepare_staging_deploy.sh
|
||||
- name: "Clean Maven cache"
|
||||
run: bash ./scripts/ci/cleanup_cache.sh
|
||||
- name: "Configure AWS credentials"
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_S3_STAGING_ACCESS_KEY }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_S3_STAGING_SECRET_KEY }}
|
||||
aws-region: eu-west-1
|
||||
- name: "Deploy to S3 Staging Bucket"
|
||||
run: |
|
||||
aws s3 cp --acl private --recursive ./deploy_dir s3://alfresco-artefacts-staging/alfresco-content-services-community/release/${BRANCH_NAME}/${BUILD_NUMBER}
|
||||
aws s3 cp --acl private --recursive ./deploy_dir_ags s3://alfresco-artefacts-staging/community/RM/${RELEASE_VERSION}
|
||||
echo "Finished release and deployed to https://s3.console.aws.amazon.com/s3/buckets/alfresco-artefacts-staging/alfresco-content-services-community/release/${BRANCH_NAME}/${BUILD_NUMBER}"
|
||||
|
||||
publish:
|
||||
name: Copy to S3 Release Bucket
|
||||
runs-on: ubuntu-latest
|
||||
needs: [release]
|
||||
if: >
|
||||
!(failure() || cancelled()) &&
|
||||
contains(github.event.head_commit.message, '[publish]') &&
|
||||
(github.ref_name == 'master' || startsWith(github.ref_name, 'release/')) &&
|
||||
github.event_name != 'pull_request'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||
- name: "Configure AWS credentials"
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_S3_RELEASE_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_S3_RELEASE_SECRET_ACCESS_KEY }}
|
||||
aws-region: eu-west-1
|
||||
- name: "Deploy to S3 Release Bucket"
|
||||
run: |
|
||||
bash scripts/ci/copy_to_release_bucket.sh
|
||||
bash scripts/ci/copy_ags_to_release_bucket.sh
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Pre-Commit formatter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- precommit/**
|
||||
|
||||
env:
|
||||
JAVA_VERSION: "21"
|
||||
MAVEN_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}
|
||||
MAVEN_USERNAME: ${{ secrets.NEXUS_USERNAME }}
|
||||
|
||||
jobs:
|
||||
format-code:
|
||||
name: "Reformat code"
|
||||
runs-on: ubuntu-latest
|
||||
if: contains(github.event.head_commit.message, '[reformat code]')
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v8.16.0
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v8.16.0
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v8.16.0
|
||||
with:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
- name: Set up Python ${{ inputs.python-version }}
|
||||
uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0
|
||||
with:
|
||||
python-version: "3.9"
|
||||
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
|
||||
continue-on-error: true
|
||||
with:
|
||||
extra_args: --all-files
|
||||
- name: Update secrets baseline
|
||||
run: pip install detect-secrets && detect-secrets scan --baseline .secrets.baseline
|
||||
- uses: Alfresco/alfresco-build-tools/.github/actions/git-commit-changes@v8.16.0
|
||||
with:
|
||||
username: ${{ secrets.BOT_GITHUB_USERNAME }}
|
||||
add-options: -u
|
||||
commit-message: "Apply Pre-Commit code formatting"
|
||||
skip-if-no-changes: true
|
||||
- name: Push changes
|
||||
run: git push
|
||||
@@ -0,0 +1,14 @@
|
||||
repos:
|
||||
- repo: https://github.com/Yelp/detect-secrets
|
||||
rev: v1.5.0
|
||||
hooks:
|
||||
- id: detect-secrets
|
||||
args: ["--baseline", ".secrets.baseline"]
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: check-format-and-headers
|
||||
name: Check format and headers and fix if necessary
|
||||
entry: ./scripts/hooks/check-format-and-headers.sh
|
||||
language: script
|
||||
files: ".*.java"
|
||||
pass_filenames: false
|
||||
@@ -0,0 +1,292 @@
|
||||
{
|
||||
"version": "1.5.0",
|
||||
"plugins_used": [
|
||||
{
|
||||
"name": "ArtifactoryDetector"
|
||||
},
|
||||
{
|
||||
"name": "AWSKeyDetector"
|
||||
},
|
||||
{
|
||||
"name": "AzureStorageKeyDetector"
|
||||
},
|
||||
{
|
||||
"name": "Base64HighEntropyString",
|
||||
"limit": 4.5
|
||||
},
|
||||
{
|
||||
"name": "BasicAuthDetector"
|
||||
},
|
||||
{
|
||||
"name": "CloudantDetector"
|
||||
},
|
||||
{
|
||||
"name": "DiscordBotTokenDetector"
|
||||
},
|
||||
{
|
||||
"name": "GitHubTokenDetector"
|
||||
},
|
||||
{
|
||||
"name": "GitLabTokenDetector"
|
||||
},
|
||||
{
|
||||
"name": "HexHighEntropyString",
|
||||
"limit": 3.0
|
||||
},
|
||||
{
|
||||
"name": "IbmCloudIamDetector"
|
||||
},
|
||||
{
|
||||
"name": "IbmCosHmacDetector"
|
||||
},
|
||||
{
|
||||
"name": "IPPublicDetector"
|
||||
},
|
||||
{
|
||||
"name": "JwtTokenDetector"
|
||||
},
|
||||
{
|
||||
"name": "KeywordDetector",
|
||||
"keyword_exclude": ""
|
||||
},
|
||||
{
|
||||
"name": "MailchimpDetector"
|
||||
},
|
||||
{
|
||||
"name": "NpmDetector"
|
||||
},
|
||||
{
|
||||
"name": "OpenAIDetector"
|
||||
},
|
||||
{
|
||||
"name": "PrivateKeyDetector"
|
||||
},
|
||||
{
|
||||
"name": "PypiTokenDetector"
|
||||
},
|
||||
{
|
||||
"name": "SendGridDetector"
|
||||
},
|
||||
{
|
||||
"name": "SlackDetector"
|
||||
},
|
||||
{
|
||||
"name": "SoftlayerDetector"
|
||||
},
|
||||
{
|
||||
"name": "SquareOAuthDetector"
|
||||
},
|
||||
{
|
||||
"name": "StripeDetector"
|
||||
},
|
||||
{
|
||||
"name": "TelegramBotTokenDetector"
|
||||
},
|
||||
{
|
||||
"name": "TwilioKeyDetector"
|
||||
}
|
||||
],
|
||||
"filters_used": [
|
||||
{
|
||||
"path": "detect_secrets.filters.allowlist.is_line_allowlisted"
|
||||
},
|
||||
{
|
||||
"path": "detect_secrets.filters.common.is_baseline_file",
|
||||
"filename": ".secrets.baseline"
|
||||
},
|
||||
{
|
||||
"path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies",
|
||||
"min_level": 2
|
||||
},
|
||||
{
|
||||
"path": "detect_secrets.filters.heuristic.is_indirect_reference"
|
||||
},
|
||||
{
|
||||
"path": "detect_secrets.filters.heuristic.is_likely_id_string"
|
||||
},
|
||||
{
|
||||
"path": "detect_secrets.filters.heuristic.is_lock_file"
|
||||
},
|
||||
{
|
||||
"path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string"
|
||||
},
|
||||
{
|
||||
"path": "detect_secrets.filters.heuristic.is_potential_uuid"
|
||||
},
|
||||
{
|
||||
"path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign"
|
||||
},
|
||||
{
|
||||
"path": "detect_secrets.filters.heuristic.is_sequential_string"
|
||||
},
|
||||
{
|
||||
"path": "detect_secrets.filters.heuristic.is_swagger_file"
|
||||
},
|
||||
{
|
||||
"path": "detect_secrets.filters.heuristic.is_templated_secret"
|
||||
}
|
||||
],
|
||||
"results": {
|
||||
"dev/dev-tomcat/src/main/tomcat/shared/classes/alfresco-global.properties": [
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "dev/dev-tomcat/src/main/tomcat/shared/classes/alfresco-global.properties",
|
||||
"hashed_secret": "a4a747bd4ba5e3a5049cad116881867c71fb625b",
|
||||
"is_verified": false,
|
||||
"line_number": 8
|
||||
}
|
||||
],
|
||||
"dev/dev-tomcat/src/main/tomcat/tomcat-users.xml": [
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "dev/dev-tomcat/src/main/tomcat/tomcat-users.xml",
|
||||
"hashed_secret": "2be88ca4242c76e8253ac62474851065032d6833",
|
||||
"is_verified": false,
|
||||
"line_number": 19
|
||||
}
|
||||
],
|
||||
"dev/docker-compose.yml": [
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "dev/docker-compose.yml",
|
||||
"hashed_secret": "d1ebbc78705ad98aa2c8fb357d2e30c8f001673a",
|
||||
"is_verified": false,
|
||||
"line_number": 41
|
||||
}
|
||||
],
|
||||
"tests/environment/docker-compose-minimal+transforms.yml": [
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/environment/docker-compose-minimal+transforms.yml",
|
||||
"hashed_secret": "9e3d103f7aa5f4f778cf752087dfceeba15d4fef",
|
||||
"is_verified": false,
|
||||
"line_number": 13
|
||||
},
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/environment/docker-compose-minimal+transforms.yml",
|
||||
"hashed_secret": "92cda941553ea9ba9b39716c79a46b08a740613c",
|
||||
"is_verified": false,
|
||||
"line_number": 23
|
||||
},
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/environment/docker-compose-minimal+transforms.yml",
|
||||
"hashed_secret": "d1ebbc78705ad98aa2c8fb357d2e30c8f001673a",
|
||||
"is_verified": false,
|
||||
"line_number": 76
|
||||
}
|
||||
],
|
||||
"tests/environment/docker-compose-minimal.yml": [
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/environment/docker-compose-minimal.yml",
|
||||
"hashed_secret": "9e3d103f7aa5f4f778cf752087dfceeba15d4fef",
|
||||
"is_verified": false,
|
||||
"line_number": 13
|
||||
},
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/environment/docker-compose-minimal.yml",
|
||||
"hashed_secret": "92cda941553ea9ba9b39716c79a46b08a740613c",
|
||||
"is_verified": false,
|
||||
"line_number": 23
|
||||
},
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/environment/docker-compose-minimal.yml",
|
||||
"hashed_secret": "d1ebbc78705ad98aa2c8fb357d2e30c8f001673a",
|
||||
"is_verified": false,
|
||||
"line_number": 79
|
||||
}
|
||||
],
|
||||
"tests/tas-cmis/src/test/resources/default.properties": [
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/tas-cmis/src/test/resources/default.properties",
|
||||
"hashed_secret": "d033e22ae348aeb5660fc2140aec35850c4da997",
|
||||
"is_verified": false,
|
||||
"line_number": 8
|
||||
},
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/tas-cmis/src/test/resources/default.properties",
|
||||
"hashed_secret": "a4a747bd4ba5e3a5049cad116881867c71fb625b",
|
||||
"is_verified": false,
|
||||
"line_number": 84
|
||||
}
|
||||
],
|
||||
"tests/tas-email/src/test/resources/default.properties": [
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/tas-email/src/test/resources/default.properties",
|
||||
"hashed_secret": "d033e22ae348aeb5660fc2140aec35850c4da997",
|
||||
"is_verified": false,
|
||||
"line_number": 8
|
||||
},
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/tas-email/src/test/resources/default.properties",
|
||||
"hashed_secret": "43b7ed74f99cf021109506832a48aa75435b817d",
|
||||
"is_verified": false,
|
||||
"line_number": 35
|
||||
},
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/tas-email/src/test/resources/default.properties",
|
||||
"hashed_secret": "a4a747bd4ba5e3a5049cad116881867c71fb625b",
|
||||
"is_verified": false,
|
||||
"line_number": 97
|
||||
}
|
||||
],
|
||||
"tests/tas-integration/src/test/resources/default.properties": [
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/tas-integration/src/test/resources/default.properties",
|
||||
"hashed_secret": "d033e22ae348aeb5660fc2140aec35850c4da997",
|
||||
"is_verified": false,
|
||||
"line_number": 8
|
||||
},
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/tas-integration/src/test/resources/default.properties",
|
||||
"hashed_secret": "a4a747bd4ba5e3a5049cad116881867c71fb625b",
|
||||
"is_verified": false,
|
||||
"line_number": 118
|
||||
}
|
||||
],
|
||||
"tests/tas-restapi/src/test/resources/default.properties": [
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/tas-restapi/src/test/resources/default.properties",
|
||||
"hashed_secret": "d033e22ae348aeb5660fc2140aec35850c4da997",
|
||||
"is_verified": false,
|
||||
"line_number": 21
|
||||
},
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/tas-restapi/src/test/resources/default.properties",
|
||||
"hashed_secret": "a4a747bd4ba5e3a5049cad116881867c71fb625b",
|
||||
"is_verified": false,
|
||||
"line_number": 87
|
||||
}
|
||||
],
|
||||
"tests/tas-webdav/src/test/resources/default.properties": [
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/tas-webdav/src/test/resources/default.properties",
|
||||
"hashed_secret": "d033e22ae348aeb5660fc2140aec35850c4da997",
|
||||
"is_verified": false,
|
||||
"line_number": 8
|
||||
},
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": "tests/tas-webdav/src/test/resources/default.properties",
|
||||
"hashed_secret": "a4a747bd4ba5e3a5049cad116881867c71fb625b",
|
||||
"is_verified": false,
|
||||
"line_number": 73
|
||||
}
|
||||
]
|
||||
},
|
||||
"generated_at": "2025-03-18T15:20:48Z"
|
||||
}
|
||||
-177
@@ -1,177 +0,0 @@
|
||||
---
|
||||
dist: xenial
|
||||
language: java
|
||||
jdk: openjdk11
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
git:
|
||||
depth: false
|
||||
quiet: true
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- ${HOME}/.m2/repository
|
||||
|
||||
# the cache can grow constantly
|
||||
before_cache: bash scripts/travis/cleanup_cache.sh
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /release\/.*/
|
||||
- /feature\/.*/
|
||||
- /fix\/.*/
|
||||
|
||||
env:
|
||||
global:
|
||||
- TAS_SCRIPTS=../alfresco-community-repo/packaging/tests/scripts
|
||||
- TAS_ENVIRONMENT=./tests/environment
|
||||
# Release version has to start with real version (23.1.0-....) for the docker image to build successfully.
|
||||
- RELEASE_VERSION=23.1.0-A4
|
||||
- DEVELOPMENT_VERSION=23.1.0-A5-SNAPSHOT
|
||||
|
||||
stages:
|
||||
- name: test
|
||||
if: commit_message !~ /\[skip tests\]/
|
||||
- name: docker_latest
|
||||
if: commit_message !~ /\[skip docker_latest\]/ AND (branch = master OR branch =~ /release\/.*/) AND type != pull_request AND fork = false
|
||||
- name: release
|
||||
if: commit_message =~ /\[release\]/ AND (branch = master OR branch =~ /release\/.*/) AND type != pull_request AND fork = false
|
||||
- name: publish
|
||||
if: commit_message =~ /\[publish\]/ AND (branch = master OR branch =~ /release\/.*/) AND type != pull_request AND fork = false
|
||||
|
||||
before_install: travis_retry bash scripts/travis/init.sh
|
||||
install: travis_retry travis_wait 40 bash scripts/travis/build.sh
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- name: "REST API TAS tests part1"
|
||||
if: commit_message !~ /\[skip tas\]/
|
||||
before_script:
|
||||
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal+transforms.yml
|
||||
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
|
||||
script:
|
||||
- travis_wait 60 mvn -B install -ntp -f tests/tas-restapi/pom.xml -Pall-tas-tests,run-restapi-part1 -Denvironment=default -DrunBugs=false
|
||||
|
||||
- name: "REST API TAS tests part2"
|
||||
if: commit_message !~ /\[skip tas\]/
|
||||
before_script:
|
||||
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal+transforms.yml
|
||||
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
|
||||
script:
|
||||
- travis_wait 60 mvn -B install -ntp -f tests/tas-restapi/pom.xml -Pall-tas-tests,run-restapi-part2 -Denvironment=default -DrunBugs=false
|
||||
|
||||
- name: "REST API TAS tests part3"
|
||||
if: commit_message !~ /\[skip tas\]/
|
||||
before_script:
|
||||
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal+transforms.yml
|
||||
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
|
||||
script:
|
||||
- travis_wait 60 mvn -B install -ntp -f tests/tas-restapi/pom.xml -Pall-tas-tests,run-restapi-part3 -Denvironment=default -DrunBugs=false
|
||||
|
||||
- name: "CMIS TAS tests - BROWSER binding"
|
||||
if: commit_message !~ /\[skip tas\]/
|
||||
before_script:
|
||||
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal+transforms.yml
|
||||
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
|
||||
script:
|
||||
- travis_wait 40 mvn -B install -ntp -f tests/tas-cmis/pom.xml -Pall-tas-tests,run-cmis-browser -Denvironment=default -DrunBugs=false
|
||||
|
||||
- name: "CMIS TAS tests - ATOM binding"
|
||||
if: commit_message !~ /\[skip tas\]/
|
||||
before_script:
|
||||
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal+transforms.yml
|
||||
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
|
||||
script:
|
||||
- travis_wait 40 mvn -B install -ntp -f tests/tas-cmis/pom.xml -Pall-tas-tests,run-cmis-atom -Denvironment=default -DrunBugs=false
|
||||
|
||||
- name: "CMIS TAS tests - WEBSERVICES binding"
|
||||
if: commit_message !~ /\[skip tas\]/
|
||||
before_script:
|
||||
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal+transforms.yml
|
||||
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
|
||||
script:
|
||||
- travis_wait 40 mvn -B install -ntp -f tests/tas-cmis/pom.xml -Pall-tas-tests,run-cmis-webservices -Denvironment=default -DrunBugs=false
|
||||
|
||||
- name: "Email TAS tests"
|
||||
if: commit_message !~ /\[skip tas\]/
|
||||
before_script:
|
||||
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal.yml
|
||||
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
|
||||
script:
|
||||
- travis_wait 30 mvn -B install -ntp -f tests/tas-email/pom.xml -Pall-tas-tests -Denvironment=default -DrunBugs=false
|
||||
|
||||
- name: "WebDAV TAS tests"
|
||||
if: commit_message !~ /\[skip tas\]/
|
||||
before_script:
|
||||
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal.yml
|
||||
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
|
||||
script:
|
||||
- travis_wait 20 mvn -B install -ntp -f tests/tas-webdav/pom.xml -Pall-tas-tests -Denvironment=default -DrunBugs=false
|
||||
|
||||
- name: "Integration TAS tests"
|
||||
if: commit_message !~ /\[skip tas\]/
|
||||
before_script:
|
||||
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal.yml
|
||||
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
|
||||
script:
|
||||
- travis_wait 30 mvn -B install -ntp -f tests/tas-integration/pom.xml -Pall-tas-tests -Denvironment=default -DrunBugs=false
|
||||
|
||||
- name: "Community Distribution Zip content tests"
|
||||
before_script:
|
||||
- travis_retry travis_wait 20 mvn -B -ntp -V clean install -Pags -DskipTests -Dmaven.javadoc.skip=true
|
||||
script:
|
||||
- travis_wait 20 mvn -B install -ntp -f tests/tas-distribution-zip/pom.xml -Prun-distribution-zip-contents-check -DrunBugs=false
|
||||
|
||||
- name: "Update latest images"
|
||||
stage: docker_latest
|
||||
script: travis_retry travis_wait 30 mvn -B -ntp -V clean install -DskipTests -Dmaven.javadoc.skip=true -Pags -Ppush-docker-images
|
||||
|
||||
- name: "Release and Copy to S3 Staging Bucket"
|
||||
stage: release
|
||||
before_script: bash scripts/travis/verify_release_tag.sh
|
||||
script: travis_wait 60 bash scripts/travis/maven_release.sh
|
||||
before_deploy: source scripts/travis/prepare_staging_deploy.sh
|
||||
deploy:
|
||||
- provider: s3
|
||||
access_key_id: "${AWS_STAGING_ACCESS_KEY}"
|
||||
secret_access_key: "${AWS_STAGING_SECRET_KEY}"
|
||||
bucket: "alfresco-artefacts-staging"
|
||||
region: "eu-west-1"
|
||||
skip_cleanup: true
|
||||
acl: private
|
||||
local_dir: "deploy_dir"
|
||||
upload_dir: "alfresco-content-services-community/release/${TRAVIS_BRANCH}/${TRAVIS_BUILD_NUMBER}"
|
||||
on:
|
||||
all_branches: true
|
||||
- provider: s3
|
||||
access_key_id: "${AWS_STAGING_ACCESS_KEY}"
|
||||
secret_access_key: "${AWS_STAGING_SECRET_KEY}"
|
||||
bucket: "alfresco-artefacts-staging"
|
||||
region: "eu-west-1"
|
||||
skip_cleanup: true
|
||||
acl: private
|
||||
local_dir: "deploy_dir_ags"
|
||||
upload_dir: "community/RM/${RELEASE_VERSION}"
|
||||
on:
|
||||
all_branches: true
|
||||
after_deploy:
|
||||
- echo "Finished release and deployed to https://s3.console.aws.amazon.com/s3/buckets/alfresco-artefacts-staging/alfresco-content-services/release/${TRAVIS_BRANCH}/${TRAVIS_BUILD_NUMBER}"
|
||||
|
||||
- name: "Copy to S3 Release Bucket"
|
||||
stage: publish
|
||||
# Nothing to build/install as we are just copying from S3 buckets
|
||||
install: skip
|
||||
script: skip
|
||||
before_deploy: pip install awscli
|
||||
deploy:
|
||||
- provider: script
|
||||
script: bash scripts/travis/copy_to_release_bucket.sh
|
||||
on:
|
||||
all_branches: true
|
||||
- provider: script
|
||||
script: bash scripts/travis/copy_ags_to_release_bucket.sh
|
||||
on:
|
||||
all_branches: true
|
||||
+7
-7
@@ -66,9 +66,9 @@ The repository code will need to talk to other ACS components, such as a databas
|
||||
The simplest way to create these, is to use the `docker-compose.yml` file in the `dev` directory.
|
||||
~~~
|
||||
# The `envUp` alias is the same as the following commands. Run these in a new terminal session, or add a `-d` flag to
|
||||
the `docker-compose` command.
|
||||
the `docker compose` command.
|
||||
cd acs-community-packaging
|
||||
docker-compose -f dev/docker-compose.yml up
|
||||
docker compose -f dev/docker-compose.yml up
|
||||
Creating dev_activemq_1 ... done
|
||||
Creating dev_solr6_1 ... done
|
||||
Creating dev_postgres_1 ... done
|
||||
@@ -81,7 +81,7 @@ cd ..
|
||||
Set any alfresco-global.properties or log4j properties you may need in the following files. They will be copied
|
||||
to the `dev/dev-acs-amps-overlay/target` directory. Other customisations may also be placed in the `extension` directory.
|
||||
~~~
|
||||
dev/dev-tomcat/src/main/tomcat/shared/classes/alfresco/extension/custom-log4j.properties
|
||||
dev/dev-tomcat/src/main/tomcat/shared/classes/alfresco/extension/custom-log4j2.properties
|
||||
dev/dev-tomcat/src/main/tomcat/shared/classes/alfresco-global.properties
|
||||
~~~
|
||||
|
||||
@@ -120,13 +120,13 @@ mvn install -Prun,withShare -rf dev-acs-amps-overlay
|
||||
## Clean up
|
||||
When finished, kill the tomcat instance and stop the Docker instances. You will normally also
|
||||
remove the Docker containers, as you will need a clean database if you are going to issue
|
||||
another `mvn clean install` command. If you started `docker-compose` in a separate terminal session,
|
||||
simply use `^C` or if you used a `-d` flag, use `docker-compose -f dev/docker-compose.yml stop`.
|
||||
another `mvn clean install` command. If you started `docker compose` in a separate terminal session,
|
||||
simply use `^C` or if you used a `-d` flag, use `docker compose -f dev/docker-compose.yml stop`.
|
||||
~~~
|
||||
^C
|
||||
... Stopped 'sysAdmin' subsystem, ID: [sysAdmin, default]
|
||||
|
||||
docker-compose -f dev/docker-compose.yml rm
|
||||
docker compose -f dev/docker-compose.yml rm
|
||||
Going to remove dev_transform-core-aio_1, dev_transform-router_1, dev_solr6_1, dev_postgres_1, dev_activemq_1, dev_shared-file-store_1
|
||||
Are you sure? [yN] y
|
||||
Removing dev_transform-core-aio_1 ... done
|
||||
@@ -139,6 +139,6 @@ If you have not removed the containers, it is possible to restart the tomcat ins
|
||||
a `mvn install` (no `clean`), but this may result in failures if there are incompatibilities
|
||||
between the code, database and content in `dev/dev-acs-amps-overlay/target/dev-instance/runtime/alf_data`.
|
||||
Any changes made to alfresco-global properties or log4j will not be picked up, unless you
|
||||
directly edit `dev/dev-acs-amps-overlay/target/dev-instance/tomcat/shared/classes/alfresco/extension/custom-log4j.properties`
|
||||
directly edit `dev/dev-acs-amps-overlay/target/dev-instance/tomcat/shared/classes/alfresco/extension/custom-log4j2.properties`
|
||||
and `dev/dev-acs-amps-overlay/target/dev-instance/tomcat/shared/classes/alfresco-global.properties`, but they will be thrown away
|
||||
on the next `mvn clean`.
|
||||
|
||||
+4
-4
@@ -64,7 +64,7 @@ alias comXS='comS && comP'
|
||||
# Clean up of docker images and content stores (alf_data directories)
|
||||
alias drm='docker rm -f $(docker ps -q)'
|
||||
alias arm='rm -rf `find . -name alf_data`'
|
||||
alias envUp='docker-compose -f acs-community-packaging/dev/docker-compose.yml up'
|
||||
alias envStop='docker-compose -f acs-community-packaging/dev/docker-compose.yml stop'
|
||||
alias envKill='docker-compose -f acs-community-packaging/dev/docker-compose.yml kill'
|
||||
alias envRm='docker-compose -f acs-community-packaging/dev/docker-compose.yml rm'
|
||||
alias envUp='./acs-community-packaging/scripts/dev/start-compose.sh'
|
||||
alias envStop='docker compose -f acs-community-packaging/dev/docker-compose.yml stop'
|
||||
alias envKill='docker compose -f acs-community-packaging/dev/docker-compose.yml kill'
|
||||
alias envRm='docker compose -f acs-community-packaging/dev/docker-compose.yml rm'
|
||||
|
||||
@@ -7,13 +7,12 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-dev-community-tomcat-env</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<dependency.sis.version>1.1</dependency.sis.version>
|
||||
<dependency.sis.version>1.3</dependency.sis.version>
|
||||
|
||||
<tomcat.version>7.0.86</tomcat.version>
|
||||
<tomcat.default.solr6.port>8983</tomcat.default.solr6.port>
|
||||
</properties>
|
||||
|
||||
@@ -32,180 +31,116 @@
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<!-- Provided dependencies to satisfy Cargo requirement, but not include in built WAR -->
|
||||
<dependency>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-server-root</artifactId>
|
||||
<version>${dependency.alfresco-server-root.version}</version>
|
||||
<type>war</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>api-explorer</artifactId>
|
||||
<version>${alfresco.api-explorer.version}</version>
|
||||
<type>war</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>dev-community-share-amps-overlay</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>alfresco</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<groupId>org.codehaus.cargo</groupId>
|
||||
<artifactId>cargo-maven3-plugin</artifactId>
|
||||
<configuration>
|
||||
<overlays>
|
||||
<overlay>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>dev-community-repo-amps-overlay</artifactId>
|
||||
<type>war</type>
|
||||
</overlay>
|
||||
</overlays>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepare-exploded-war</id>
|
||||
<goals>
|
||||
<goal>exploded</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.tomcat.maven</groupId>
|
||||
<artifactId>tomcat7-maven-plugin</artifactId>
|
||||
<version>2.2</version>
|
||||
<configuration>
|
||||
<systemProperties>
|
||||
<index.subsystem.name>solr6</index.subsystem.name>
|
||||
<solr.port>${tomcat.default.solr6.port}</solr.port>
|
||||
<dir.root>${runtime.data.folder}/alf_data</dir.root>
|
||||
<solr.host>localhost</solr.host>
|
||||
<solr.base.url>/solr</solr.base.url>
|
||||
<solr.secureComms>secret</solr.secureComms>
|
||||
<solr.sharedSecret>secret</solr.sharedSecret>
|
||||
<encryption.keystore.type>pkcs12</encryption.keystore.type>
|
||||
<encryption.cipherAlgorithm>AES/CBC/PKCS5Padding</encryption.cipherAlgorithm>
|
||||
<encryption.keyAlgorithm>AES</encryption.keyAlgorithm>
|
||||
<metadata-keystore.password>mp6yc0UD9e</metadata-keystore.password>
|
||||
<metadata-keystore.aliases>metadata</metadata-keystore.aliases>
|
||||
<metadata-keystore.metadata.password>mp6yc0UD9e</metadata-keystore.metadata.password>
|
||||
<metadata-keystore.metadata.algorithm>AES</metadata-keystore.metadata.algorithm>
|
||||
</systemProperties>
|
||||
<delegate>false</delegate>
|
||||
<useSeparateTomcatClassLoader>true</useSeparateTomcatClassLoader>
|
||||
<contextFile>${runtime.tomcat.conf.folder}/alfresco-context.xml</contextFile>
|
||||
<tomcatUsers>${runtime.tomcat.conf.folder}/tomcat-users.xml</tomcatUsers>
|
||||
<webapps>
|
||||
<webapp>
|
||||
<container>
|
||||
<containerId>tomcat10x</containerId>
|
||||
<type>embedded</type>
|
||||
<log>target/cargo.log</log>
|
||||
<systemProperties>
|
||||
<cargo.tomcat.ajp.port>-1</cargo.tomcat.ajp.port>
|
||||
|
||||
<index.subsystem.name>solr6</index.subsystem.name>
|
||||
<solr.port>${tomcat.default.solr6.port}</solr.port>
|
||||
<dir.root>${runtime.data.folder}/alf_data</dir.root>
|
||||
<solr.host>localhost</solr.host>
|
||||
<solr.base.url>/solr</solr.base.url>
|
||||
<solr.secureComms>secret</solr.secureComms>
|
||||
<solr.sharedSecret>secret</solr.sharedSecret>
|
||||
<encryption.keystore.type>pkcs12</encryption.keystore.type>
|
||||
<encryption.cipherAlgorithm>AES/CBC/PKCS5Padding</encryption.cipherAlgorithm>
|
||||
<encryption.keyAlgorithm>AES</encryption.keyAlgorithm>
|
||||
<metadata-keystore.password>mp6yc0UD9e</metadata-keystore.password>
|
||||
<metadata-keystore.aliases>metadata</metadata-keystore.aliases>
|
||||
<metadata-keystore.metadata.password>mp6yc0UD9e</metadata-keystore.metadata.password>
|
||||
<metadata-keystore.metadata.algorithm>AES</metadata-keystore.metadata.algorithm>
|
||||
</systemProperties>
|
||||
</container>
|
||||
<configuration>
|
||||
<configfiles>
|
||||
<configfile>
|
||||
<file>${runtime.tomcat.conf.folder}/alfresco-context.xml</file>
|
||||
<todir>conf</todir>
|
||||
<tofile>context.xml</tofile>
|
||||
</configfile>
|
||||
<configfile>
|
||||
<file>${runtime.tomcat.conf.folder}/tomcat-users.xml</file>
|
||||
<todir>conf</todir>
|
||||
<tofile>tomcat-users.xml</tofile>
|
||||
</configfile>
|
||||
</configfiles>
|
||||
<files>
|
||||
<file>
|
||||
<file>${runtime.tomcat.conf.folder}/shared</file>
|
||||
<todir>shared</todir>
|
||||
</file>
|
||||
</files>
|
||||
</configuration>
|
||||
<deployables>
|
||||
<deployable>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-server-root</artifactId>
|
||||
<version>${dependency.alfresco-server-root.version}</version>
|
||||
<contextPath>/</contextPath>
|
||||
<type>war</type>
|
||||
<asWebapp>true</asWebapp>
|
||||
<!-- Workaround for https://issues.apache.org/jira/browse/MTOMCAT-269 -->
|
||||
<!-- tomcat7-maven-plugin does not pick up the context.xml file from META-INF -->
|
||||
<contextFile>
|
||||
${project.build.directory}/tomcat/webapps/alfresco-server-root/META-INF/context.xml
|
||||
</contextFile>
|
||||
</webapp>
|
||||
<webapp>
|
||||
<properties>
|
||||
<context>/</context>
|
||||
</properties>
|
||||
</deployable>
|
||||
<deployable>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>api-explorer</artifactId>
|
||||
<version>${alfresco.api-explorer.version}</version>
|
||||
<contextPath>/api-explorer</contextPath>
|
||||
<type>war</type>
|
||||
<asWebapp>true</asWebapp>
|
||||
</webapp>
|
||||
<webapp>
|
||||
<properties>
|
||||
<context>/api-explorer</context>
|
||||
</properties>
|
||||
</deployable>
|
||||
<deployable>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>dev-community-share-amps-overlay</artifactId>
|
||||
<contextPath>/share</contextPath>
|
||||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
<asWebapp>true</asWebapp>
|
||||
</webapp>
|
||||
</webapps>
|
||||
<properties>
|
||||
<context>/share</context>
|
||||
</properties>
|
||||
</deployable>
|
||||
<deployable>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>dev-acs-community-amps-overlay</artifactId>
|
||||
<type>war</type>
|
||||
<properties>
|
||||
<context>/alfresco</context>
|
||||
</properties>
|
||||
</deployable>
|
||||
</deployables>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-util</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-coyote</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-api</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-jdbc</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-dbcp</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-servlet-api</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-jsp-api</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-jasper</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-jasper-el</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-el-api</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-catalina</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-tribes</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-catalina-ha</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-annotations-api</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>tomcat-juli</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-logging-juli</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-logging-log4j</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
<!-- Workaround required to make Share work after Tika upgrade -->
|
||||
<dependency>
|
||||
<groupId>org.apache.sis.core</groupId>
|
||||
@@ -318,8 +253,8 @@
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.tomcat.maven</groupId>
|
||||
<artifactId>tomcat7-maven-plugin</artifactId>
|
||||
<groupId>org.codehaus.cargo</groupId>
|
||||
<artifactId>cargo-maven3-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>run-exploded-webapp</id>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-dev-community-tomcat-env</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-dev-community-tomcat-env</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-dev-community-tomcat-env</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Context docBase="${project.build.directory}/${project.build.finalName}" path="/alfresco">
|
||||
<Resources className="org.apache.naming.resources.VirtualDirContext"
|
||||
extraResourcePaths="/=${project.build.directory}/${project.build.finalName}" />
|
||||
<Loader searchVirtualFirst="true"
|
||||
className="org.apache.catalina.loader.VirtualWebappLoader"
|
||||
virtualClasspath="${project.build.outputDirectory};${runtime.tomcat.conf.folder}/shared/classes" />
|
||||
<Context>
|
||||
<Resources cachingAllowed="true" cacheMaxSize="102400">
|
||||
<PostResources className="org.apache.catalina.webresources.DirResourceSet"
|
||||
base="${catalina.home}/shared/classes"
|
||||
webAppMount="/WEB-INF/classes"/>
|
||||
</Resources>
|
||||
</Context>
|
||||
@@ -20,7 +20,7 @@ db.create.command=create database ${db.name}
|
||||
#db.name=alfresco
|
||||
#db.username=alfresco
|
||||
#db.password=alfresco
|
||||
#db.driver=com.mysql.jdbc.Driver
|
||||
#db.driver=com.mysql.cj.jdbc.Driver
|
||||
#db.url=jdbc:mysql://localhost/${db.name}?useUnicode=true&characterEncoding=UTF-8
|
||||
#db.master.url=jdbc:mysql://localhost/
|
||||
#db.master.username=${db.username}
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
log4j.logger.org.alfresco.repo.content.transform.TransformerDebug=debug
|
||||
|
||||
#log4j.logger.org.alfresco.util.exec.RuntimeExecBootstrapBean=debug
|
||||
#log4j.logger.org.alfresco.util.exec.RuntimeExec=debug
|
||||
|
||||
#log4j.logger.org.alfresco.repo.rendition2=debug
|
||||
#log4j.logger.org.alfresco.repo.content.metadata=debug
|
||||
#log4j.logger.org.alfresco.repo.rendition2.LocalTransformClient=debug
|
||||
#log4j.logger.org.alfresco.repo.rendition.RenditionServiceImpl=debug
|
||||
#log4j.logger.org.alfresco.enterprise.repo.rendition2.RemoteTransformClient=debug
|
||||
|
||||
#log4j.logger.org.alfresco.repo.content.transform.LocalTransformServiceRegistry=debug
|
||||
#log4j.logger.org.alfresco.repo.rendition2.RenditionDefinitionRegistry2Impl=debug
|
||||
#log4j.logger.org.alfresco.repo.content.MimetypeMap=debug
|
||||
#log4j.logger.org.alfresco.repo.content.transform.LocalTransform=debug
|
||||
|
||||
#log4j.logger.org.alfresco.repo.rawevents=debug
|
||||
|
||||
#log4j.logger.org.alfresco.repo.jscript.RhinoScriptProcessor=debug
|
||||
#log4j.logger.RhinoScriptProcessor.calls=debug
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
|
||||
logger.alfresco-repo-content-transform-TransformerDebug.name=org.alfresco.repo.content.transform.TransformerDebug
|
||||
logger.alfresco-repo-content-transform-TransformerDebug.level=debug
|
||||
|
||||
#logger.alfresco-util-exec-RuntimeExecBootstrapBean.name=org.alfresco.util.exec.RuntimeExecBootstrapBean
|
||||
#logger.alfresco-util-exec-RuntimeExecBootstrapBean.level=debug
|
||||
#logger.alfresco-util-exec-RuntimeExec.name=org.alfresco.util.exec.RuntimeExec
|
||||
#logger.alfresco-util-exec-RuntimeExec.level=debug
|
||||
|
||||
#logger.alfresco-repo-rendition2.name=org.alfresco.repo.rendition2
|
||||
#logger.alfresco-repo-rendition2.level=debug
|
||||
#logger.alfresco-repo-content-metadata.name=org.alfresco.repo.content.metadata
|
||||
#logger.alfresco-repo-content-metadata.level=debug
|
||||
#logger.alfresco-repo-rendition2-LocalTransformClient.name=org.alfresco.repo.rendition2.LocalTransformClient
|
||||
#logger.alfresco-repo-rendition2-LocalTransformClient.level=debug
|
||||
#logger.alfresco-repo-rendition-RenditionServiceImpl.name=org.alfresco.repo.rendition.RenditionServiceImpl
|
||||
#logger.alfresco-repo-rendition-RenditionServiceImpl.level=debug
|
||||
#logger.alfresco-enterprise-repo-rendition2-RemoteTransformClient.name=org.alfresco.enterprise.repo.rendition2.RemoteTransformClient
|
||||
#logger.alfresco-enterprise-repo-rendition2-RemoteTransformClient.level=debug
|
||||
|
||||
#logger.alfresco-repo-content-transform-LocalTransformServiceRegistry.name=org.alfresco.repo.content.transform.LocalTransformServiceRegistry
|
||||
#logger.alfresco-repo-content-transform-LocalTransformServiceRegistry.level=debug
|
||||
#logger.alfresco-repo-rendition2-RenditionDefinitionRegistry2Impl.name=org.alfresco.repo.rendition2.RenditionDefinitionRegistry2Impl
|
||||
#logger.alfresco-repo-rendition2-RenditionDefinitionRegistry2Impl.level=debug
|
||||
#logger.alfresco-repo-content-MimetypeMap.name=org.alfresco.repo.content.MimetypeMap
|
||||
#logger.alfresco-repo-content-MimetypeMap.level=debug
|
||||
#logger.alfresco-repo-content-transform-LocalTransform.name=org.alfresco.repo.content.transform.LocalTransform
|
||||
#logger.alfresco-repo-content-transform-LocalTransform.level=debug
|
||||
|
||||
#logger.alfresco-repo-rawevents.name=org.alfresco.repo.rawevents
|
||||
#logger.alfresco-repo-rawevents.level=debug
|
||||
|
||||
#logger.alfresco-repo-jscript-RhinoScriptProcessor.name=org.alfresco.repo.jscript.RhinoScriptProcessor
|
||||
#logger.alfresco-repo-jscript-RhinoScriptProcessor.level=debug
|
||||
#logger.calls.name=RhinoScriptProcessor.calls
|
||||
#logger.calls.level=debug
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Context crossContext="true">
|
||||
<Environment name="solr/home" type="java.lang.String" value="${solr.solr.home}" override="true"/>
|
||||
<Environment name="solr/model/dir" type="java.lang.String" value="${solr.solr.model.dir}" override="true"/>
|
||||
<Environment name="solr/content/dir" type="java.lang.String" value="${solr.solr.content.dir}" override="true"/>
|
||||
|
||||
<Resources className="org.apache.naming.resources.VirtualDirContext"
|
||||
extraResourcePaths="/=${project.build.directory}/tomcat/webapps/alfresco-solr4" />
|
||||
<Loader searchVirtualFirst="true"
|
||||
className="org.apache.catalina.loader.VirtualWebappLoader"
|
||||
virtualClasspath="${project.build.directory}/tomcat/webapps/alfresco-solr4/WEB-INF/classes" />
|
||||
|
||||
</Context>
|
||||
@@ -6,7 +6,7 @@ version: "2"
|
||||
|
||||
services:
|
||||
transform-core-aio:
|
||||
image: alfresco/alfresco-transform-core-aio:2.6.0-A3
|
||||
image: alfresco/alfresco-transform-core-aio:${TRANSFORMERS_TAG}
|
||||
mem_limit: 1536m
|
||||
environment:
|
||||
JAVA_OPTS: " -Xms256m -Xmx1536m"
|
||||
@@ -14,7 +14,7 @@ services:
|
||||
- 8090:8090
|
||||
|
||||
postgres:
|
||||
image: postgres:13.3
|
||||
image: postgres:15.4
|
||||
mem_limit: 512m
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=alfresco
|
||||
@@ -25,14 +25,14 @@ services:
|
||||
- 5432:5432
|
||||
|
||||
solr6:
|
||||
image: alfresco/alfresco-search-services:2.0.3
|
||||
image: alfresco/alfresco-search-services:2.0.21
|
||||
mem_limit: 2g
|
||||
environment:
|
||||
#Solr needs to know how to register itself with Alfresco
|
||||
SOLR_ALFRESCO_HOST: "alfresco"
|
||||
SOLR_ALFRESCO_HOST: "host.docker.internal"
|
||||
SOLR_ALFRESCO_PORT: "8080"
|
||||
#Alfresco needs to know how to call solr
|
||||
SOLR_SOLR_HOST: "solr6"
|
||||
SOLR_SOLR_HOST: "localhost"
|
||||
SOLR_SOLR_PORT: "8983"
|
||||
#Create the default alfresco and archive cores
|
||||
SOLR_CREATE_ALFRESCO_DEFAULTS: "alfresco,archive"
|
||||
@@ -44,10 +44,10 @@ services:
|
||||
"
|
||||
SOLR_JAVA_MEM: "-Xms2g -Xmx2g"
|
||||
ports:
|
||||
- 8083:8983 #Browser port
|
||||
- 8983:8983 #Browser port
|
||||
|
||||
activemq:
|
||||
image: alfresco/alfresco-activemq:5.16.1
|
||||
image: alfresco/alfresco-activemq:5.18.3-jre17-rockylinux8
|
||||
mem_limit: 1g
|
||||
ports:
|
||||
- 8161:8161 # Web Console
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>acs-community-packaging</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>acs-community-packaging</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>acs-community-packaging</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
FROM alfresco/alfresco-community-repo-base:${repo.image.tag}
|
||||
ARG BASE_IMAGE
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
# Set default docker_context.
|
||||
ARG resource_path=target
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
ARG BASE_IMAGE
|
||||
# BUILD STAGE AGS
|
||||
FROM debian:11-slim AS AGSBUILDER
|
||||
|
||||
@@ -12,7 +13,7 @@ RUN unzip -q /build/gs-api-explorer-*.war -d /build/gs-api-explorer && \
|
||||
chmod -R g-w,o= /build
|
||||
|
||||
# ACTUAL IMAGE
|
||||
FROM alfresco/alfresco-content-repository-community:${image.tag}
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
# Alfresco user does not have permissions to modify webapps or configuration. Switch to root.
|
||||
# The access will be fixed after all operations are done.
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>content-services-community-docker</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<image.name>alfresco/alfresco-governance-repository-community</image.name>
|
||||
<base.image>alfresco/alfresco-content-repository-community:${image.tag}</base.image>
|
||||
<scripts.directory>${project.parent.parent.basedir}/scripts/dev</scripts.directory>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
|
||||
+170
-5
@@ -7,12 +7,14 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>acs-community-packaging</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<image.name>alfresco/alfresco-content-repository-community</image.name>
|
||||
<docker.quay-expires.value>NEVER</docker.quay-expires.value>
|
||||
<base.image>alfresco/alfresco-community-repo-base:${repo.image.tag}</base.image>
|
||||
<scripts.directory>${project.parent.basedir}/scripts/dev</scripts.directory>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@@ -76,6 +78,12 @@
|
||||
<images>
|
||||
<image>
|
||||
<name>${image.name}:${image.tag}</name>
|
||||
<build>
|
||||
<args>
|
||||
<BASE_IMAGE>${base.image}</BASE_IMAGE>
|
||||
</args>
|
||||
<contextDir>${project.basedir}</contextDir>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
@@ -93,6 +101,73 @@
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>build-multiarch-docker-images</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<images>
|
||||
<image>
|
||||
<name>${local.registry}/${image.name}:${image.tag}</name>
|
||||
<build>
|
||||
<buildx>
|
||||
<builderName>${builder.name}</builderName>
|
||||
<platforms>
|
||||
<platform>linux/amd64</platform>
|
||||
<platform>linux/arm64</platform>
|
||||
</platforms>
|
||||
<attestations>
|
||||
<provenance>false</provenance>
|
||||
</attestations>
|
||||
</buildx>
|
||||
<dockerFileDir>${project.basedir}</dockerFileDir>
|
||||
<args>
|
||||
<BASE_IMAGE>${local.registry}/${base.image}</BASE_IMAGE>
|
||||
</args>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build-push-image</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>build</goal>
|
||||
<goal>push</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepare-buildx</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>${scripts.directory}/prepare_buildx.sh</executable>
|
||||
<arguments>
|
||||
<argument>${builder.name}</argument>
|
||||
<argument>${image.registry}</argument>
|
||||
<argument>${image.name}</argument>
|
||||
<argument>${image.tag}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>push-docker-images</id>
|
||||
<!-- publishes "image:latest" to Quay -->
|
||||
@@ -105,8 +180,23 @@
|
||||
<images>
|
||||
<!-- Quay image -->
|
||||
<image>
|
||||
<name>${image.name}:${image.tag}</name>
|
||||
<registry>${image.registry}</registry>
|
||||
<name>${image.registry}/${image.name}:${image.tag}</name>
|
||||
<build>
|
||||
<buildx>
|
||||
<builderName>${builder.name}</builderName>
|
||||
<platforms>
|
||||
<platform>linux/amd64</platform>
|
||||
<platform>linux/arm64</platform>
|
||||
</platforms>
|
||||
<attestations>
|
||||
<provenance>false</provenance>
|
||||
</attestations>
|
||||
</buildx>
|
||||
<dockerFileDir>${project.basedir}</dockerFileDir>
|
||||
<args>
|
||||
<BASE_IMAGE>${local.registry}/${base.image}</BASE_IMAGE>
|
||||
</args>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
@@ -121,6 +211,28 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepare-buildx</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>${scripts.directory}/prepare_buildx.sh</executable>
|
||||
<arguments>
|
||||
<argument>${builder.name}</argument>
|
||||
<argument>${image.registry}</argument>
|
||||
<argument>${image.name}</argument>
|
||||
<argument>${image.tag}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
@@ -142,6 +254,22 @@
|
||||
<!-- DockerHub image -->
|
||||
<image>
|
||||
<name>${image.name}:${project.version}</name>
|
||||
<build>
|
||||
<buildx>
|
||||
<builderName>${builder.name}</builderName>
|
||||
<platforms>
|
||||
<platform>linux/amd64</platform>
|
||||
<platform>linux/arm64</platform>
|
||||
</platforms>
|
||||
<attestations>
|
||||
<provenance>false</provenance>
|
||||
</attestations>
|
||||
</buildx>
|
||||
<dockerFileDir>${project.basedir}</dockerFileDir>
|
||||
<args>
|
||||
<BASE_IMAGE>${local.registry}/${base.image}</BASE_IMAGE>
|
||||
</args>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
@@ -157,8 +285,23 @@
|
||||
<images>
|
||||
<!-- Quay image -->
|
||||
<image>
|
||||
<name>${image.name}:${project.version}</name>
|
||||
<registry>${image.registry}</registry>
|
||||
<name>${image.registry}/${image.name}:${project.version}</name>
|
||||
<build>
|
||||
<buildx>
|
||||
<builderName>${builder.name}</builderName>
|
||||
<platforms>
|
||||
<platform>linux/amd64</platform>
|
||||
<platform>linux/arm64</platform>
|
||||
</platforms>
|
||||
<attestations>
|
||||
<provenance>false</provenance>
|
||||
</attestations>
|
||||
</buildx>
|
||||
<dockerFileDir>${project.basedir}</dockerFileDir>
|
||||
<args>
|
||||
<BASE_IMAGE>${local.registry}/${base.image}</BASE_IMAGE>
|
||||
</args>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
@@ -169,6 +312,28 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepare-buildx</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>${scripts.directory}/prepare_buildx.sh</executable>
|
||||
<arguments>
|
||||
<argument>${builder.name}</argument>
|
||||
<argument>${image.registry}</argument>
|
||||
<argument>${image.name}</argument>
|
||||
<argument>${image.tag}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
### Apply AGS community share AMP to Share image
|
||||
FROM alfresco/alfresco-share-base:${share.image.tag}
|
||||
ARG BASE_IMAGE
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
LABEL quay.expires-after=${docker.quay-expires.value}
|
||||
|
||||
|
||||
+170
-5
@@ -8,12 +8,14 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>share-community-docker</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<image.name>alfresco/alfresco-governance-share-community</image.name>
|
||||
<docker.quay-expires.value>NEVER</docker.quay-expires.value>
|
||||
<base.image>alfresco/alfresco-share-base:${share.image.tag}</base.image>
|
||||
<scripts.directory>${project.parent.parent.basedir}/scripts/dev</scripts.directory>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@@ -58,6 +60,12 @@
|
||||
<images>
|
||||
<image>
|
||||
<name>${image.name}:${image.tag}</name>
|
||||
<build>
|
||||
<args>
|
||||
<BASE_IMAGE>${base.image}</BASE_IMAGE>
|
||||
</args>
|
||||
<contextDir>${project.basedir}</contextDir>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
@@ -75,6 +83,73 @@
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>build-multiarch-docker-images</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<images>
|
||||
<image>
|
||||
<name>${local.registry}/${image.name}:${image.tag}</name>
|
||||
<build>
|
||||
<buildx>
|
||||
<builderName>${builder.name}</builderName>
|
||||
<platforms>
|
||||
<platform>linux/amd64</platform>
|
||||
<platform>linux/arm64</platform>
|
||||
</platforms>
|
||||
<attestations>
|
||||
<provenance>false</provenance>
|
||||
</attestations>
|
||||
</buildx>
|
||||
<dockerFileDir>${project.basedir}</dockerFileDir>
|
||||
<args>
|
||||
<BASE_IMAGE>${local.registry}/${base.image}</BASE_IMAGE>
|
||||
</args>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>build-push-image</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>build</goal>
|
||||
<goal>push</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepare-buildx</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>${scripts.directory}/prepare_buildx.sh</executable>
|
||||
<arguments>
|
||||
<argument>${builder.name}</argument>
|
||||
<argument>${image.registry}</argument>
|
||||
<argument>${image.name}</argument>
|
||||
<argument>${image.tag}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>push-docker-images</id>
|
||||
<!-- publishes "image:latest" to Quay -->
|
||||
@@ -87,8 +162,23 @@
|
||||
<images>
|
||||
<!-- Quay image -->
|
||||
<image>
|
||||
<name>${image.name}:${image.tag}</name>
|
||||
<registry>${image.registry}</registry>
|
||||
<name>${image.registry}/${image.name}:${image.tag}</name>
|
||||
<build>
|
||||
<buildx>
|
||||
<builderName>${builder.name}</builderName>
|
||||
<platforms>
|
||||
<platform>linux/amd64</platform>
|
||||
<platform>linux/arm64</platform>
|
||||
</platforms>
|
||||
<attestations>
|
||||
<provenance>false</provenance>
|
||||
</attestations>
|
||||
</buildx>
|
||||
<dockerFileDir>${project.basedir}</dockerFileDir>
|
||||
<args>
|
||||
<BASE_IMAGE>${local.registry}/${base.image}</BASE_IMAGE>
|
||||
</args>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
@@ -103,6 +193,28 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepare-buildx</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>${scripts.directory}/prepare_buildx.sh</executable>
|
||||
<arguments>
|
||||
<argument>${builder.name}</argument>
|
||||
<argument>${image.registry}</argument>
|
||||
<argument>${image.name}</argument>
|
||||
<argument>${image.tag}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
@@ -124,6 +236,22 @@
|
||||
<!-- DockerHub image -->
|
||||
<image>
|
||||
<name>${image.name}:${project.version}</name>
|
||||
<build>
|
||||
<buildx>
|
||||
<builderName>${builder.name}</builderName>
|
||||
<platforms>
|
||||
<platform>linux/amd64</platform>
|
||||
<platform>linux/arm64</platform>
|
||||
</platforms>
|
||||
<attestations>
|
||||
<provenance>false</provenance>
|
||||
</attestations>
|
||||
</buildx>
|
||||
<dockerFileDir>${project.basedir}</dockerFileDir>
|
||||
<args>
|
||||
<BASE_IMAGE>${local.registry}/${base.image}</BASE_IMAGE>
|
||||
</args>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
@@ -139,8 +267,23 @@
|
||||
<images>
|
||||
<!-- Quay image -->
|
||||
<image>
|
||||
<name>${image.name}:${project.version}</name>
|
||||
<registry>${image.registry}</registry>
|
||||
<name>${image.registry}/${image.name}:${project.version}</name>
|
||||
<build>
|
||||
<buildx>
|
||||
<builderName>${builder.name}</builderName>
|
||||
<platforms>
|
||||
<platform>linux/amd64</platform>
|
||||
<platform>linux/arm64</platform>
|
||||
</platforms>
|
||||
<attestations>
|
||||
<provenance>false</provenance>
|
||||
</attestations>
|
||||
</buildx>
|
||||
<dockerFileDir>${project.basedir}</dockerFileDir>
|
||||
<args>
|
||||
<BASE_IMAGE>${local.registry}/${base.image}</BASE_IMAGE>
|
||||
</args>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
@@ -151,6 +294,28 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepare-buildx</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>${scripts.directory}/prepare_buildx.sh</executable>
|
||||
<arguments>
|
||||
<argument>${builder.name}</argument>
|
||||
<argument>${image.registry}</argument>
|
||||
<argument>${image.name}</argument>
|
||||
<argument>${image.tag}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>acs-community-packaging</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
</project>
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
<artifactId>acs-community-packaging</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Alfresco Content Services Community Packaging</name>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<relativePath>../alfresco-community-repo/pom.xml</relativePath>
|
||||
<version>23.1.0.17</version>
|
||||
<version>26.2.0.95</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<dependency.alfresco-community-repo.version>23.1.0.17</dependency.alfresco-community-repo.version>
|
||||
<dependency.alfresco-community-share.version>23.1.0.13</dependency.alfresco-community-share.version>
|
||||
<dependency.acs-packaging.version>23.1.0-A4</dependency.acs-packaging.version> <!-- for Share distribution zip -->
|
||||
<dependency.alfresco-community-repo.version>26.2.0.95</dependency.alfresco-community-repo.version>
|
||||
<dependency.alfresco-community-share.version>26.2.0.67</dependency.alfresco-community-share.version>
|
||||
<dependency.acs-packaging.version>26.2.0-A.30</dependency.acs-packaging.version> <!-- for Share distribution zip -->
|
||||
|
||||
<repo.image.tag>${dependency.alfresco-community-repo.version}</repo.image.tag>
|
||||
<share.image.tag>${dependency.alfresco-community-share.version}</share.image.tag>
|
||||
@@ -25,7 +25,7 @@
|
||||
<connection>scm:git:https://github.com/Alfresco/acs-community-packaging.git</connection>
|
||||
<developerConnection>scm:git:https://github.com/Alfresco/acs-community-packaging.git</developerConnection>
|
||||
<url>https://github.com/Alfresco/acs-community-packaging</url>
|
||||
<tag>23.1.0-A4</tag>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<distributionManagement>
|
||||
@@ -167,7 +167,12 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>org.alfresco.tas</groupId>
|
||||
<artifactId>alfresco-community-repo-cmis-test</artifactId>
|
||||
<artifactId>cmis</artifactId>
|
||||
<version>${dependency.alfresco-community-repo.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.alfresco.tas</groupId>
|
||||
<artifactId>cmis</artifactId>
|
||||
<version>${dependency.alfresco-community-repo.version}</version>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
@@ -188,7 +193,12 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.alfresco.tas</groupId>
|
||||
<artifactId>alfresco-community-repo-restapi-test</artifactId>
|
||||
<artifactId>restapi</artifactId>
|
||||
<version>${dependency.alfresco-community-repo.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.alfresco.tas</groupId>
|
||||
<artifactId>restapi</artifactId>
|
||||
<version>${dependency.alfresco-community-repo.version}</version>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
@@ -237,4 +247,13 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.diffplug.spotless</groupId>
|
||||
<artifactId>spotless-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>acs-community-packaging</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
|
||||
Regular → Executable
+41
-10
@@ -6,6 +6,28 @@ pushd "$(dirname "${BASH_SOURCE[0]}")/../../"
|
||||
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/build_functions.sh"
|
||||
|
||||
usage() {
|
||||
echo "Builds the upstream projects first, then the current one." 1>&2;
|
||||
echo 1>&2;
|
||||
echo "Usage: $0 [-m]" 1>&2;
|
||||
echo " -m: Flag to build Docker images with multi-architecture" 1>&2;
|
||||
echo " -h: Display the usage information" 1>&2;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
while getopts "mh" option; do
|
||||
case $option in
|
||||
m)
|
||||
DOCKER_BUILD_PROFILE=build-multiarch-docker-images
|
||||
;;
|
||||
h)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
BUILD_PROFILE=${DOCKER_BUILD_PROFILE:-build-docker-images}
|
||||
|
||||
COM_DEPENDENCY_VERSION="$(retrievePomProperty "dependency.alfresco-community-repo.version")"
|
||||
REPO_IMAGE=$([[ "${COM_DEPENDENCY_VERSION}" =~ ^.+-SNAPSHOT$ ]] && echo "-Drepo.image.tag=latest" || echo)
|
||||
|
||||
@@ -16,13 +38,13 @@ if [ "${COM_DEPENDENCY_VERSION}" != "$(retrievePomParentVersion)" ]; then
|
||||
fi
|
||||
|
||||
# Prevent merging of any SNAPSHOT dependencies into the master or the release/* branches
|
||||
if [[ $(isPullRequestBuild) && "${COM_DEPENDENCY_VERSION}" =~ ^.+-SNAPSHOT$ && "${TRAVIS_BRANCH}" =~ ^master$|^release/.+$ ]] ; then
|
||||
if [[ $(isPullRequestBuild) && "${COM_DEPENDENCY_VERSION}" =~ ^.+-SNAPSHOT$ && "${BRANCH_NAME}" =~ ^master$|^release/.+$ ]] ; then
|
||||
printf "PRs with SNAPSHOT dependencies are not allowed into master or release branches\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Prevent release jobs from starting when there are SNAPSHOT upstream dependencies
|
||||
if [[ "${COM_DEPENDENCY_VERSION}" =~ ^.+-SNAPSHOT$ ]] && [ "${TRAVIS_BUILD_STAGE_NAME,,}" = "release" ] ; then
|
||||
if [[ "${COM_DEPENDENCY_VERSION}" =~ ^.+-SNAPSHOT$ ]] && [ "${JOB_NAME,,}" = "release" ] ; then
|
||||
printf "Cannot release project with SNAPSHOT dependencies!\n"
|
||||
exit 1
|
||||
fi
|
||||
@@ -32,37 +54,46 @@ UPSTREAM_REPO="github.com/Alfresco/alfresco-community-repo.git"
|
||||
# Search, checkout and build the same branch on the upstream project in case of SNAPSHOT dependencies
|
||||
# Otherwise, checkout the upstream tag and build its Docker image (use just "mvn package", without "mvn install")
|
||||
if [[ "${COM_DEPENDENCY_VERSION}" =~ ^.+-SNAPSHOT$ ]] ; then
|
||||
pullAndBuildSameBranchOnUpstream "${UPSTREAM_REPO}" "-Pbuild-docker-images -Pags -Dlicense.failOnNotUptodateHeader=true"
|
||||
pullAndBuildSameBranchOnUpstream "${UPSTREAM_REPO}" "-P$BUILD_PROFILE -Pags -Dlicense.failOnNotUptodateHeader=true"
|
||||
else
|
||||
pullUpstreamTagAndBuildDockerImage "${UPSTREAM_REPO}" "${COM_DEPENDENCY_VERSION}" "-Pbuild-docker-images -Pags -Dlicense.failOnNotUptodateHeader=true"
|
||||
pullUpstreamTagAndBuildDockerImage "${UPSTREAM_REPO}" "${COM_DEPENDENCY_VERSION}" "-P$BUILD_PROFILE -Pags -Dlicense.failOnNotUptodateHeader=true"
|
||||
fi
|
||||
|
||||
SHARE_DEPENDENCY_VERSION="$(retrievePomProperty "dependency.alfresco-community-share.version")"
|
||||
SHARE_IMAGE=$([[ "${SHARE_DEPENDENCY_VERSION}" =~ ^.+-SNAPSHOT$ ]] && echo "-Dshare.image.tag=latest" || echo)
|
||||
|
||||
# Prevent merging of any SNAPSHOT dependencies into the master or the release/* branches
|
||||
if [[ $(isPullRequestBuild) && "${SHARE_DEPENDENCY_VERSION}" =~ ^.+-SNAPSHOT$ && "${TRAVIS_BRANCH}" =~ ^master$|^release/.+$ ]] ; then
|
||||
if [[ $(isPullRequestBuild) && "${SHARE_DEPENDENCY_VERSION}" =~ ^.+-SNAPSHOT$ && "${BRANCH_NAME}" =~ ^master$|^release/.+$ ]] ; then
|
||||
printf "PRs with SNAPSHOT dependencies are not allowed into master or release branches\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Prevent release jobs from starting when there are SNAPSHOT upstream dependencies
|
||||
if [[ "${SHARE_DEPENDENCY_VERSION}" =~ ^.+-SNAPSHOT$ ]] && [ "${TRAVIS_BUILD_STAGE_NAME,,}" = "release" ] ; then
|
||||
if [[ "${SHARE_DEPENDENCY_VERSION}" =~ ^.+-SNAPSHOT$ ]] && [ "${JOB_NAME,,}" = "release" ] ; then
|
||||
printf "Cannot release project with SNAPSHOT dependencies!\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SHARE_UPSTREAM_REPO="github.com/Alfresco/alfresco-community-share.git"
|
||||
|
||||
# Checkout the upstream share project (tag or branch; + build if the latter)
|
||||
if [[ "${SHARE_DEPENDENCY_VERSION}" =~ ^.+-SNAPSHOT$ ]] ; then
|
||||
pullAndBuildSameBranchOnUpstream "${SHARE_UPSTREAM_REPO}" "-Pbuild-docker-images -Pags -Dlicense.failOnNotUptodateHeader=true -Ddocker.quay-expires.value=NEVER ${REPO_IMAGE} -Ddependency.alfresco-community-repo.version=${COM_DEPENDENCY_VERSION}"
|
||||
pullAndBuildSameBranchOnUpstream "${SHARE_UPSTREAM_REPO}" "-P$BUILD_PROFILE -Pags -Dlicense.failOnNotUptodateHeader=true -Ddocker.quay-expires.value=NEVER ${REPO_IMAGE} -Ddependency.alfresco-community-repo.version=${COM_DEPENDENCY_VERSION}"
|
||||
else
|
||||
pullUpstreamTagAndBuildDockerImage "${SHARE_UPSTREAM_REPO}" "${SHARE_DEPENDENCY_VERSION}" "-Pbuild-docker-images -Pags -Dlicense.failOnNotUptodateHeader=true -Ddocker.quay-expires.value=NEVER -Ddependency.alfresco-community-repo.version=${COM_DEPENDENCY_VERSION}"
|
||||
pullUpstreamTagAndBuildDockerImage "${SHARE_UPSTREAM_REPO}" "${SHARE_DEPENDENCY_VERSION}" "-P$BUILD_PROFILE -Pags -Dlicense.failOnNotUptodateHeader=true -Ddocker.quay-expires.value=NEVER -Ddependency.alfresco-community-repo.version=${COM_DEPENDENCY_VERSION}"
|
||||
fi
|
||||
|
||||
# Build the current project
|
||||
mvn -B -ntp -V -q install -DskipTests -Dmaven.javadoc.skip=true -Pbuild-docker-images -Pags ${REPO_IMAGE} ${SHARE_IMAGE}
|
||||
mvn -B -ntp -V -q install -DskipTests -Dmaven.javadoc.skip=true -P$BUILD_PROFILE -Pags ${REPO_IMAGE} ${SHARE_IMAGE}
|
||||
|
||||
# Build alfresco image with jdbc drivers (used by search API tests for MariaDB/MySQL)
|
||||
MYSQL_JDBC_TAG=$(mvn help:evaluate -Dexpression=dependency.mysql.version -q -DforceStdout)
|
||||
mvn dependency:copy -Dartifact=mysql:mysql-connector-java:${MYSQL_JDBC_TAG}:jar -DoutputDirectory=tests/environment/alfresco-with-jdbc-drivers
|
||||
|
||||
MARIADB_JDBC_TAG=$(mvn help:evaluate -Dexpression=dependency.mariadb.version -q -DforceStdout)
|
||||
mvn dependency:copy -Dartifact=org.mariadb.jdbc:mariadb-java-client:${MARIADB_JDBC_TAG}:jar -DoutputDirectory=tests/environment/alfresco-with-jdbc-drivers
|
||||
|
||||
REPO_LATEST_IMAGE=$(docker images --format='{{.Repository}}:{{.Tag}}' | grep "alfresco-content-repository-community:latest")
|
||||
docker build -t alfresco-repository-databases:latest -f tests/environment/alfresco-with-jdbc-drivers/alfresco.Dockerfile . --build-arg BASE_IMAGE=${REPO_LATEST_IMAGE}
|
||||
|
||||
|
||||
popd
|
||||
Regular → Executable
+5
-11
@@ -2,11 +2,11 @@
|
||||
set +vx
|
||||
|
||||
function isPullRequestBuild() {
|
||||
test "${TRAVIS_PULL_REQUEST}" != "false"
|
||||
test "${PULL_REQUEST}" != "false"
|
||||
}
|
||||
|
||||
function isBranchBuild() {
|
||||
test "${TRAVIS_PULL_REQUEST}" = "false"
|
||||
test "${PULL_REQUEST}" = "false"
|
||||
}
|
||||
|
||||
function cloneRepo() {
|
||||
@@ -82,15 +82,9 @@ function remoteBranchExists() {
|
||||
function identifyUpstreamSourceBranch() {
|
||||
local UPSTREAM_REPO="${1}"
|
||||
|
||||
# if it's a pull request, use the source branch name (if it exists)
|
||||
if isPullRequestBuild && remoteBranchExists "${UPSTREAM_REPO}" "${TRAVIS_PULL_REQUEST_BRANCH}" ; then
|
||||
echo "${TRAVIS_PULL_REQUEST_BRANCH}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# otherwise use the current branch name (or in case of PRs, the target branch name)
|
||||
if remoteBranchExists "${UPSTREAM_REPO}" "${TRAVIS_BRANCH}" ; then
|
||||
echo "${TRAVIS_BRANCH}"
|
||||
# use the current branch name (or in case of PRs, the source branch name)
|
||||
if remoteBranchExists "${UPSTREAM_REPO}" "${BRANCH_NAME}" ; then
|
||||
echo "${BRANCH_NAME}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
Regular → Executable
Regular → Executable
+2
-2
@@ -8,7 +8,7 @@ set -vex
|
||||
#
|
||||
|
||||
if [ -z "${RELEASE_VERSION}" ]; then
|
||||
echo "Please provide a RELEASE_VERSION in the format <acs-version>-<additional-info> (23.1.0-EA or 23.1.0-SNAPSHOT)"
|
||||
echo "Please provide a RELEASE_VERSION in the format <acs-version>-<additional-info> (e.g. 7.2.0-A2)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -17,7 +17,7 @@ DESTINATION="s3://eu.dl.alfresco.com/release/community/RM/${RELEASE_VERSION}"
|
||||
|
||||
printf "\n%s\n%s\n" "${SOURCE}" "${DESTINATION}"
|
||||
|
||||
aws s3 cp --acl private --recursive "${SOURCE}" "${DESTINATION}"
|
||||
aws s3 cp --acl private --recursive --copy-props none "${SOURCE}" "${DESTINATION}"
|
||||
|
||||
set +vex
|
||||
echo "=========================== Finishing Copy to Release Bucket Script =========================="
|
||||
@@ -8,16 +8,16 @@ set -vex
|
||||
#
|
||||
|
||||
if [ -z "${RELEASE_VERSION}" ]; then
|
||||
echo "Please provide a RELEASE_VERSION in the format <acs-version>-<additional-info> (23.1.0-EA or 23.1.0-SNAPSHOT)"
|
||||
echo "Please provide a RELEASE_VERSION in the format <acs-version>-<additional-info> (e.g. 7.2.0-A2)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SOURCE="s3://alfresco-artefacts-staging/alfresco-content-services-community/release/${TRAVIS_BRANCH}/${TRAVIS_BUILD_NUMBER}"
|
||||
DESTINATION="s3://eu.dl.alfresco.com/release/community/${RELEASE_VERSION}-build-${TRAVIS_BUILD_NUMBER}"
|
||||
SOURCE="s3://alfresco-artefacts-staging/alfresco-content-services-community/release/${BRANCH_NAME}/${BUILD_NUMBER}"
|
||||
DESTINATION="s3://eu.dl.alfresco.com/release/community/${RELEASE_VERSION}-build-${BUILD_NUMBER}"
|
||||
|
||||
printf "\n%s\n%s\n" "${SOURCE}" "${DESTINATION}"
|
||||
|
||||
aws s3 cp --acl private --recursive "${SOURCE}" "${DESTINATION}"
|
||||
aws s3 cp --acl private --recursive --copy-props none "${SOURCE}" "${DESTINATION}"
|
||||
|
||||
set +vex
|
||||
echo "=========================== Finishing Copy to Release Bucket Script =========================="
|
||||
Regular → Executable
-1
@@ -5,7 +5,6 @@ set -vex
|
||||
pushd "$(dirname "${BASH_SOURCE[0]}")/../../"
|
||||
|
||||
# Maven Setup
|
||||
mkdir -p "${HOME}/.m2" && cp -f .travis.settings.xml "${HOME}/.m2/settings.xml"
|
||||
find "${HOME}/.m2/repository/" -type d -name "*-SNAPSHOT*" | xargs -r -l rm -rf
|
||||
|
||||
# Docker Logins
|
||||
@@ -6,21 +6,22 @@ pushd "$(dirname "${BASH_SOURCE[0]}")/../../"
|
||||
|
||||
|
||||
if [ -z "${RELEASE_VERSION}" ] || [ -z "${DEVELOPMENT_VERSION}" ]; then
|
||||
echo "Please provide a Release and Development version in the format <acs-version>-<additional-info> (23.1.0-EA or 23.1.0-SNAPSHOT)"
|
||||
echo "Please provide a Release and Development version in the format <acs-version>-<additional-info> (e.g. 7.2.0-A2)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Use full history for release
|
||||
git checkout -B "${TRAVIS_BRANCH}"
|
||||
# Add email to link commits to user
|
||||
git checkout -B "${BRANCH_NAME}"
|
||||
# Define git identity for commits
|
||||
git config user.email "${GIT_EMAIL}"
|
||||
git config user.name "${GIT_USERNAME}"
|
||||
|
||||
mvn -B \
|
||||
-ntp \
|
||||
-Prelease,all-tas-tests -Pags \
|
||||
-DreleaseVersion="${RELEASE_VERSION}" \
|
||||
-DdevelopmentVersion="${DEVELOPMENT_VERSION}" \
|
||||
"-Darguments=-Prelease,all-tas-tests -Pags -DskipTests -Dbuild-number=${TRAVIS_BUILD_NUMBER}" \
|
||||
"-Darguments=-Prelease,all-tas-tests -Pags -DskipTests -Dbuild-number=${BUILD_NUMBER}" \
|
||||
release:clean release:prepare release:perform \
|
||||
-DscmCommentPrefix="[maven-release-plugin][skip ci] " \
|
||||
-Dusername="${GIT_USERNAME}" \
|
||||
@@ -9,7 +9,7 @@ pushd "$(dirname "${BASH_SOURCE[0]}")/../../"
|
||||
#
|
||||
|
||||
if [ -z "${RELEASE_VERSION}" ]; then
|
||||
echo "Please provide a RELEASE_VERSION in the format <acs-version>-<additional-info> (23.1.0-EA or 23.1.0-SNAPSHOT)"
|
||||
echo "Please provide a RELEASE_VERSION in the format <acs-version>-<additional-info> (e.g. 7.2.0-A2)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
BUILDER_NAME="${1}"
|
||||
TARGET_REGISTRY="${2}"
|
||||
TARGET_IMAGE="${3}"
|
||||
IMAGE_TAG="${4}"
|
||||
|
||||
#Create a `docker-container` builder with host networking and required flags (quay.io)
|
||||
docker --config target/docker/"${TARGET_REGISTRY}"/"${TARGET_IMAGE}"/"${IMAGE_TAG}"/docker \
|
||||
buildx create --use --name "${BUILDER_NAME}" --driver-opt network=host \
|
||||
--buildkitd-flags '--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host'
|
||||
|
||||
#Create a `docker-container` builder with host networking and required flags (docker.io)
|
||||
docker --config target/docker/"${TARGET_IMAGE}"/"${IMAGE_TAG}"/docker \
|
||||
buildx create --use --name "${BUILDER_NAME}" --driver-opt network=host \
|
||||
--buildkitd-flags '--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host'
|
||||
|
||||
#Create a `docker-container` builder with host networking and required flags (local registry)
|
||||
docker --config target/docker/127.0.0.1/5000/"${TARGET_IMAGE}"/"${IMAGE_TAG}"/docker \
|
||||
buildx create --use --name "${BUILDER_NAME}" --driver-opt network=host \
|
||||
--buildkitd-flags '--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host'
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
set -x
|
||||
export TRANSFORMERS_TAG=$(mvn -f acs-community-packaging/pom.xml help:evaluate -Dexpression=dependency.alfresco-transform-core.version -q -DforceStdout)
|
||||
|
||||
# .env files are picked up from project directory correctly on docker-compose 1.23.0+
|
||||
docker compose -f acs-community-packaging/dev/docker-compose.yml up
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set +x
|
||||
|
||||
mvn spotless:apply validate -DlicenseUpdateHeaders=true -Pags,all-tas-tests > /dev/null || true
|
||||
|
||||
set -x
|
||||
@@ -1,4 +1,9 @@
|
||||
TRANSFORMERS_TAG=2.6.0-A3
|
||||
SOLR6_TAG=2.0.3
|
||||
POSTGRES_TAG=13.3
|
||||
ACTIVEMQ_TAG=5.16.1
|
||||
SOLR6_TAG=2.0.21
|
||||
ACTIVEMQ_TAG=6.2.0-jre17-rockylinux8
|
||||
ELASTICSEARCH_TAG=8.17.0
|
||||
BATCH_INDEXING_TAG=5.7.0
|
||||
POSTGRES_TAG=17.9
|
||||
MYSQL_TAG=8.0.30
|
||||
MARIADB_TAG=11.8
|
||||
OPENSEARCH_TAG=2.11.1
|
||||
OPENSEARCH_DASHBOARDS_TAG=2.11.1
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#BUILDING CONTAINER FOR TAS TESTING WITH DIFFERENT JDBC CONNECTORS
|
||||
ARG BASE_IMAGE
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
COPY tests/environment/alfresco-with-jdbc-drivers/*.jar /usr/local/tomcat/lib/
|
||||
|
||||
+3
-1
@@ -6,11 +6,13 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>acs-community-packaging</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
<module>testcontainers-env</module>
|
||||
<module>tas-restapi</module>
|
||||
<module>tas-elasticsearch</module>
|
||||
<module>tas-cmis</module>
|
||||
<module>tas-email</module>
|
||||
<module>tas-webdav</module>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>content-repository-community-tests</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
@@ -58,13 +58,13 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.alfresco.tas</groupId>
|
||||
<artifactId>alfresco-community-repo-cmis-test</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
<artifactId>cmis</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.alfresco.tas</groupId>
|
||||
<artifactId>cmis</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
# Root logger option
|
||||
log4j.rootLogger=INFO, file, stdout
|
||||
|
||||
# Direct log messages to a log file
|
||||
log4j.appender.file=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.file.File=./target/reports/alfresco-tas.log
|
||||
log4j.appender.file.MaxBackupIndex=10
|
||||
log4j.appender.file.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.file.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
|
||||
# Direct log messages to stdout
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.Target=System.out
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
|
||||
# TestRail particular log file
|
||||
# Direct log messages to a log file
|
||||
log4j.appender.testrailLog=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.testrailLog.File=./target/reports/alfresco-testrail.log
|
||||
log4j.appender.testrailLog.MaxBackupIndex=10
|
||||
log4j.appender.testrailLog.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.testrailLog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
|
||||
log4j.category.testrail=INFO, testrailLog
|
||||
log4j.additivity.testrail=false
|
||||
@@ -0,0 +1,42 @@
|
||||
# Root logger option
|
||||
rootLogger.level=info
|
||||
rootLogger.appenderRef.stdout.ref=ConsoleAppender
|
||||
rootLogger.appenderRef.rolling.ref=RollingAppender
|
||||
|
||||
###### File appender definition #######
|
||||
appender.rolling.type=RollingFile
|
||||
appender.rolling.name=RollingAppender
|
||||
appender.rolling.fileName=./target/reports/alfresco-tas.log
|
||||
appender.rolling.filePattern=./target/reports/alfresco-tas.log.%i
|
||||
appender.rolling.layout.type=PatternLayout
|
||||
appender.rolling.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
|
||||
appender.rolling.policies.type = Policies
|
||||
appender.rolling.policies.size.type=SizeBasedTriggeringPolicy
|
||||
appender.rolling.policies.size.size=10MB
|
||||
appender.rolling.strategy.type=DefaultRolloverStrategy
|
||||
appender.rolling.strategy.max=10
|
||||
|
||||
###### Console appender definition #######
|
||||
appender.console.type=Console
|
||||
appender.console.name=ConsoleAppender
|
||||
appender.console.layout.type=PatternLayout
|
||||
appender.console.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
|
||||
|
||||
# TestRail particular log file
|
||||
# Direct log messages to a log file
|
||||
logger.testrail.name=testrail
|
||||
logger.testrail.level=info
|
||||
logger.testrail.additivity=false
|
||||
logger.testrail.appenderRef.testrail.ref=TestrailAppender
|
||||
|
||||
appender.testrail.name=TestrailAppender
|
||||
appender.testrail.type=RollingFile
|
||||
appender.testrail.fileName=./target/reports/alfresco-testrail.log
|
||||
appender.testrail.filePattern=./target/reports/alfresco-testrail.log.%i
|
||||
appender.testrail.layout.type=PatternLayout
|
||||
appender.testrail.layout.pattern=%d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
|
||||
appender.testrail.policies.type=Policies
|
||||
appender.testrail.policies.size.type=SizeBasedTriggeringPolicy
|
||||
appender.testrail.policies.size.size=10MB
|
||||
appender.testrail.strategy.type=DefaultRolloverStrategy
|
||||
appender.testrail.strategy.max=10
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>content-repository-community-tests</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
@@ -21,7 +21,7 @@
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>3.22.0</version>
|
||||
<version>3.26.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -32,7 +32,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-model</artifactId>
|
||||
<version>3.8.4</version>
|
||||
<version>3.9.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
+128
-134
@@ -1,134 +1,128 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Remote API
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.distribution;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FilenameFilter;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import org.apache.maven.model.Model;
|
||||
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
|
||||
|
||||
public class CheckDistributionZipContents
|
||||
{
|
||||
public static final String ACS_PREFIX = "alfresco-content-services-community-distribution";
|
||||
public static final String ACS_DIR_NAME = "distribution";
|
||||
public static final String AGS_PREFIX = "alfresco-governance-services-community";
|
||||
public static final String AGS_DIR_NAME = "distribution-ags";
|
||||
public static final String FORMAT = ".zip";
|
||||
|
||||
@Test
|
||||
public void testAcsDistributionZipContents() throws Exception
|
||||
{
|
||||
File filesList[] = getDistributionZip(ACS_DIR_NAME, ACS_PREFIX);
|
||||
for (File file : filesList)
|
||||
{
|
||||
List<String> zipEntries = getZipEntries(file.getAbsolutePath());
|
||||
assertThat(zipEntries).
|
||||
contains(
|
||||
"keystore/metadata-keystore/keystore-passwords.properties",
|
||||
"keystore/metadata-keystore/keystore",
|
||||
"keystore/generate_keystores.bat",
|
||||
"keystore/generate_keystores.sh",
|
||||
"bin/alfresco-mmt.jar",
|
||||
"bin/apply_amps.bat",
|
||||
"bin/apply_amps.sh",
|
||||
"web-server/webapps/ROOT.war",
|
||||
"web-server/webapps/alfresco.war",
|
||||
"web-server/webapps/share.war",
|
||||
"web-server/webapps/_vti_bin.war",
|
||||
"web-server/conf/Catalina/localhost/alfresco.xml",
|
||||
"web-server/shared/classes/alfresco/web-extension/share-config-custom.xml"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAgsDistributionZipContents() throws Exception
|
||||
{
|
||||
String repoVersion = getPomValues().getProperties().getProperty("dependency.alfresco-community-repo.version");
|
||||
String shareVersion = getPomValues().getProperties().getProperty("dependency.alfresco-community-share.version");
|
||||
File filesList[] = getDistributionZip(AGS_DIR_NAME, AGS_PREFIX);
|
||||
for (File file : filesList)
|
||||
{
|
||||
List<String> zipEntries = getZipEntries(file.getAbsolutePath());
|
||||
assertThat(zipEntries).
|
||||
contains(
|
||||
"alfresco-governance-services-community-repo-" + repoVersion + ".amp",
|
||||
"alfresco-governance-services-community-rest-api-explorer-" + repoVersion + ".war",
|
||||
"alfresco-governance-services-community-share-" + shareVersion + ".amp"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private File[] getDistributionZip(String dirName, String prefix) throws Exception
|
||||
{
|
||||
String resourcePath = Paths.get("").toAbsolutePath().getParent().getParent().toString() + "/" + dirName + "/" + "target" + "/";
|
||||
File distributionZip = new File(resourcePath);
|
||||
FilenameFilter zipFileFilter = (dir, name) -> {
|
||||
if (name.startsWith(prefix) && name.endsWith(FORMAT))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return distributionZip.listFiles(zipFileFilter);
|
||||
}
|
||||
|
||||
private List<String> getZipEntries(String filePath) throws Exception
|
||||
{
|
||||
List<String> zipEntries = new ArrayList<>();
|
||||
ZipFile zipFile = new ZipFile(new File(filePath));
|
||||
Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||
while (entries.hasMoreElements())
|
||||
{
|
||||
ZipEntry entry = entries.nextElement();
|
||||
zipEntries.add(entry.toString());
|
||||
}
|
||||
return zipEntries;
|
||||
}
|
||||
|
||||
private Model getPomValues() throws Exception
|
||||
{
|
||||
String parentPom = Paths.get("").toAbsolutePath().getParent().getParent().toString() + "/pom.xml";
|
||||
MavenXpp3Reader reader = new MavenXpp3Reader();
|
||||
Model model = reader.read(new FileReader(parentPom));
|
||||
return model;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Remote API
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.distribution;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FilenameFilter;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import org.apache.maven.model.Model;
|
||||
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class CheckDistributionZipContents
|
||||
{
|
||||
public static final String ACS_PREFIX = "alfresco-content-services-community-distribution";
|
||||
public static final String ACS_DIR_NAME = "distribution";
|
||||
public static final String AGS_PREFIX = "alfresco-governance-services-community";
|
||||
public static final String AGS_DIR_NAME = "distribution-ags";
|
||||
public static final String FORMAT = ".zip";
|
||||
|
||||
@Test
|
||||
public void testAcsDistributionZipContents() throws Exception
|
||||
{
|
||||
File filesList[] = getDistributionZip(ACS_DIR_NAME, ACS_PREFIX);
|
||||
for (File file : filesList)
|
||||
{
|
||||
List<String> zipEntries = getZipEntries(file.getAbsolutePath());
|
||||
assertThat(zipEntries).contains(
|
||||
"keystore/metadata-keystore/keystore-passwords.properties",
|
||||
"keystore/metadata-keystore/keystore",
|
||||
"bin/alfresco-mmt.jar",
|
||||
"bin/apply_amps.bat",
|
||||
"bin/apply_amps.sh",
|
||||
"web-server/webapps/ROOT.war",
|
||||
"web-server/webapps/alfresco.war",
|
||||
"web-server/webapps/share.war",
|
||||
"web-server/webapps/_vti_bin.war",
|
||||
"web-server/conf/Catalina/localhost/alfresco.xml",
|
||||
"web-server/shared/classes/alfresco/web-extension/share-config-custom.xml");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAgsDistributionZipContents() throws Exception
|
||||
{
|
||||
String repoVersion = getPomValues().getProperties().getProperty("dependency.alfresco-community-repo.version");
|
||||
String shareVersion = getPomValues().getProperties().getProperty("dependency.alfresco-community-share.version");
|
||||
File filesList[] = getDistributionZip(AGS_DIR_NAME, AGS_PREFIX);
|
||||
for (File file : filesList)
|
||||
{
|
||||
List<String> zipEntries = getZipEntries(file.getAbsolutePath());
|
||||
assertThat(zipEntries).contains(
|
||||
"alfresco-governance-services-community-repo-" + repoVersion + ".amp",
|
||||
"alfresco-governance-services-community-rest-api-explorer-" + repoVersion + ".war",
|
||||
"alfresco-governance-services-community-share-" + shareVersion + ".amp");
|
||||
}
|
||||
}
|
||||
|
||||
private File[] getDistributionZip(String dirName, String prefix) throws Exception
|
||||
{
|
||||
String resourcePath = Paths.get("").toAbsolutePath().getParent().getParent().toString() + "/" + dirName + "/" + "target" + "/";
|
||||
File distributionZip = new File(resourcePath);
|
||||
FilenameFilter zipFileFilter = (dir, name) -> {
|
||||
if (name.startsWith(prefix) && name.endsWith(FORMAT))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return distributionZip.listFiles(zipFileFilter);
|
||||
}
|
||||
|
||||
private List<String> getZipEntries(String filePath) throws Exception
|
||||
{
|
||||
List<String> zipEntries = new ArrayList<>();
|
||||
ZipFile zipFile = new ZipFile(new File(filePath));
|
||||
Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||
while (entries.hasMoreElements())
|
||||
{
|
||||
ZipEntry entry = entries.nextElement();
|
||||
zipEntries.add(entry.toString());
|
||||
}
|
||||
return zipEntries;
|
||||
}
|
||||
|
||||
private Model getPomValues() throws Exception
|
||||
{
|
||||
String parentPom = Paths.get("").toAbsolutePath().getParent().getParent().toString() + "/pom.xml";
|
||||
MavenXpp3Reader reader = new MavenXpp3Reader();
|
||||
Model model = reader.read(new FileReader(parentPom));
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.alfresco.tas</groupId>
|
||||
<artifactId>content-repository-community-elasticsearch-test</artifactId>
|
||||
<name>Elasticsearch test</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>content-repository-community-tests</artifactId>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>elastic</id>
|
||||
<properties>
|
||||
<suiteXmlFile>${project.basedir}/src/test/resources/test-suites/standard-elasticsearch-suites.xml</suiteXmlFile>
|
||||
<search.engine.type />
|
||||
<database.type />
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>elastic-basic-auth</id>
|
||||
<properties>
|
||||
<suiteXmlFile>${project.basedir}/src/test/resources/test-suites/elasticsearch-basic-auth-suite.xml</suiteXmlFile>
|
||||
<search.engine.type />
|
||||
<database.type />
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.alfresco.tas</groupId>
|
||||
<artifactId>content-repository-community-testcontainers</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.alfresco.tas</groupId>
|
||||
<artifactId>restapi</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.alfresco.tas</groupId>
|
||||
<artifactId>restapi</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.parsson</groupId>
|
||||
<artifactId>parsson</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.opensearch.client</groupId>
|
||||
<artifactId>opensearch-java</artifactId>
|
||||
<version>${dependency.opensearch.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse</groupId>
|
||||
<artifactId>yasson</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<redirectTestOutputToFile>false</redirectTestOutputToFile>
|
||||
<suiteXmlFiles>
|
||||
<suiteXmlFile>${suiteXmlFile}</suiteXmlFile>
|
||||
</suiteXmlFiles>
|
||||
<!-- Keeping illegal-access=warn for Java 11 compatibility, even though it has no effect on JDK 21 -->
|
||||
<argLine>
|
||||
--illegal-access=warn
|
||||
--add-opens=java.base/java.lang=ALL-UNNAMED
|
||||
</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- Build only test jar -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
import static org.alfresco.utility.model.FileType.TEXT_PLAIN;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.elasticsearch.utility.ElasticsearchRESTHelper;
|
||||
import org.alfresco.rest.core.RestWrapper;
|
||||
import org.alfresco.rest.model.RestCategoryLinkBodyModel;
|
||||
import org.alfresco.rest.model.RestCategoryModel;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.data.RandomData;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
import org.alfresco.utility.report.log.Step;
|
||||
|
||||
/**
|
||||
* Tests to verify batch indexing of category using Elasticsearch.
|
||||
*/
|
||||
@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert"})
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
public class CategoryIndexingTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
private static final String TEST_PREFIX = RandomData.getRandomAlphanumeric() + "_";
|
||||
private static final String CATEGORY_A_NAME = TEST_PREFIX + "CategoryA";
|
||||
private static final String CATEGORY_B_NAME = TEST_PREFIX + "CategoryB";
|
||||
private static final String ROOT_CATEGORY_ID = "-root-";
|
||||
private static final RestCategoryModel ROOT_CATEGORY = RestCategoryModel.builder().id(ROOT_CATEGORY_ID).create();
|
||||
|
||||
@Autowired
|
||||
private ElasticsearchRESTHelper helper;
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
@Autowired
|
||||
private DataSite dataSite;
|
||||
@Autowired
|
||||
private DataContent dataContent;
|
||||
@Autowired
|
||||
protected SearchQueryService searchQueryService;
|
||||
@Autowired
|
||||
protected RestWrapper restClient;
|
||||
|
||||
private RestCategoryModel categoryA;
|
||||
private RestCategoryModel categoryB;
|
||||
private UserModel testUser;
|
||||
private SiteModel testSite;
|
||||
private FileModel testFile;
|
||||
private FolderModel testFolder;
|
||||
|
||||
/** Create a user, private site and two categories. Create a folder (in category B) containing a document (in category A and category B). */
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
serverHealth.isServerReachable();
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
Step.STEP("Create two categories under the root.");
|
||||
List<RestCategoryModel> categories = List.of(RestCategoryModel.builder().name(CATEGORY_A_NAME).create(),
|
||||
RestCategoryModel.builder().name(CATEGORY_B_NAME).create());
|
||||
List<RestCategoryModel> categoryList = restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI()
|
||||
.usingCategory(ROOT_CATEGORY).createCategoriesList(categories).getEntries();
|
||||
categoryA = categoryList.get(0).onModel();
|
||||
categoryB = categoryList.get(1).onModel();
|
||||
|
||||
Step.STEP("Create a test user and use them to create a private site with a folder containing a document.");
|
||||
testUser = dataUser.createRandomTestUser();
|
||||
testSite = dataSite.usingUser(testUser).createPrivateRandomSite();
|
||||
FolderModel folderModel = FolderModel.getRandomFolderModel();
|
||||
testFolder = dataContent.usingUser(testUser).usingSite(testSite).createFolder(folderModel);
|
||||
FileModel fileModel = FileModel.getRandomFileModel(TEXT_PLAIN);
|
||||
testFile = dataContent.usingUser(testUser).usingResource(testFolder).createContent(fileModel);
|
||||
|
||||
Step.STEP("Assign the document to both categories and the folder to category B.");
|
||||
RestCategoryLinkBodyModel categoryALink = RestCategoryLinkBodyModel.builder().categoryId(categoryA.getId()).create();
|
||||
RestCategoryLinkBodyModel categoryBLink = RestCategoryLinkBodyModel.builder().categoryId(categoryB.getId()).create();
|
||||
restClient.authenticateUser(testUser).withCoreAPI().usingNode(testFile).linkToCategory(categoryALink);
|
||||
restClient.authenticateUser(testUser).withCoreAPI().usingNode(testFile).linkToCategory(categoryBLink);
|
||||
restClient.authenticateUser(testUser).withCoreAPI().usingNode(testFolder).linkToCategory(categoryBLink);
|
||||
}
|
||||
|
||||
/** Check we can find the document assigned to a category. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testFindDocumentByCategory()
|
||||
{
|
||||
SearchRequest query = req("cm:categories:\"" + categoryA.getId() + "\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFile.getName());
|
||||
}
|
||||
|
||||
/** Check we can find the document assigned to a category even when the query includes a StoreRef. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testFindDocumentByCategoryWithStoreRef()
|
||||
{
|
||||
SearchRequest query = req("cm:categories:\"workspace://SpacesStore/" + categoryA.getId() + "\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFile.getName());
|
||||
}
|
||||
|
||||
/** Check we can find the folder and document assigned to the other category. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testFindFolderByCategory()
|
||||
{
|
||||
SearchRequest query = req("cm:categories:\"" + categoryB.getId() + "\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFile.getName(), testFolder.getName());
|
||||
}
|
||||
|
||||
/** Check we can find the document by the pseudo-path created for the category. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testQueryByCategoryPseudoPath()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"/cm:categoryRoot/cm:generalclassifiable/cm:" + CATEGORY_A_NAME + "/*\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFile.getName());
|
||||
}
|
||||
|
||||
/** Check we can find the document by a partial path match for the category. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testQueryByPartialCategoryPathA()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"//cm:" + CATEGORY_A_NAME + "/*\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFile.getName());
|
||||
}
|
||||
|
||||
/** Check we can find the document and folder by a partial path match for the second category. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testQueryByPartialCategoryPathB()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"//cm:" + CATEGORY_B_NAME + "/*\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFile.getName(), testFolder.getName());
|
||||
}
|
||||
|
||||
/** Check we cannot find the document by a partial path match for the category that has been applied to it and then deleted. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testQueryByPathOnDeletedCategory() throws InterruptedException
|
||||
{
|
||||
// create 2 categories
|
||||
final RestCategoryModel categoryToDelete = helper.createCategory();
|
||||
final RestCategoryModel otherCategory = helper.createCategory();
|
||||
|
||||
// assign both categories to a document
|
||||
helper.linkToCategory(testUser, testFile, categoryToDelete);
|
||||
helper.linkToCategory(testUser, testFile, otherCategory);
|
||||
|
||||
// we can find the document by partial category paths after it is linked to both categories
|
||||
SearchRequest categoryToDeleteQuery = req("PATH:\"//cm:" + categoryToDelete.getName() + "/*\"");
|
||||
searchQueryService.expectResultsFromQuery(categoryToDeleteQuery, testUser, testFile.getName());
|
||||
SearchRequest otherCategoryQuery = req("PATH:\"//cm:" + otherCategory.getName() + "/*\"");
|
||||
searchQueryService.expectResultsFromQuery(otherCategoryQuery, testUser, testFile.getName());
|
||||
|
||||
// delete one of linked categories
|
||||
restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI()
|
||||
.usingCategory(categoryToDelete).deleteCategory();
|
||||
restClient.assertStatusCodeIs(HttpStatus.NO_CONTENT);
|
||||
|
||||
// we cannot find the document by partial category path anymore after the category is deleted
|
||||
searchQueryService.expectNoResultsFromQuery(categoryToDeleteQuery, testUser);
|
||||
// we can still find the document by partial category path with a different category that has been assigned but not deleted
|
||||
searchQueryService.expectResultsFromQuery(otherCategoryQuery, testUser, testFile.getName());
|
||||
}
|
||||
}
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.EMPTY;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
import static org.alfresco.utility.data.RandomData.getRandomFile;
|
||||
import static org.alfresco.utility.data.RandomData.getRandomName;
|
||||
import static org.alfresco.utility.report.log.Step.STEP;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.rest.model.RestTagModel;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.ContentModel;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FileType;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
public class ElasticsearchBoostedSearchTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
private static final String SEARCH_TERM = "mountain";
|
||||
private static final String DIFFERENT_SEARCH_TERM = "fountain";
|
||||
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
|
||||
@Autowired
|
||||
private DataContent dataContent;
|
||||
|
||||
@Autowired
|
||||
private SearchQueryService searchQueryService;
|
||||
|
||||
private UserModel testUser;
|
||||
private ContentModel fileWithTermInName;
|
||||
private ContentModel fileWithDifferentTermInName;
|
||||
private ContentModel fileWithPhraseInContent;
|
||||
private ContentModel fileWithTermInTitle;
|
||||
private ContentModel folderWithTermInName;
|
||||
private ContentModel folderWithTermInTitle;
|
||||
private ContentModel testFolder;
|
||||
private LocalDateTime creationTime;
|
||||
private LocalDateTime afterCreationTime;
|
||||
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
STEP("Create a test user and few files and folders containing searched term in name, title and content");
|
||||
testUser = dataUser.createRandomTestUser();
|
||||
testFolder = createFolder(getRandomName("folder"));
|
||||
fileWithTermInName = createFile(testFolder, SEARCH_TERM + ".txt", "dummy content");
|
||||
fileWithDifferentTermInName = createFile(testFolder, DIFFERENT_SEARCH_TERM + ".txt", "dummy other content");
|
||||
fileWithPhraseInContent = createFile(testFolder, getRandomFile(FileType.TEXT_PLAIN), "content with " + SEARCH_TERM + " searched phrase");
|
||||
folderWithTermInName = createFolder(testFolder, SEARCH_TERM);
|
||||
creationTime = ZonedDateTime.now(Clock.system(ZoneOffset.UTC)).toLocalDateTime();
|
||||
fileWithTermInTitle = createRandomFileWithTitle(testFolder, SEARCH_TERM);
|
||||
folderWithTermInTitle = createRandomFolderWithTitle(testFolder, SEARCH_TERM);
|
||||
afterCreationTime = ZonedDateTime.now(Clock.system(ZoneOffset.UTC)).toLocalDateTime();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void dataCleanup()
|
||||
{
|
||||
STEP("Clean up created files, folders and user");
|
||||
dataContent.usingAdmin().usingResource(testFolder).deleteContent();
|
||||
dataUser.deleteUser(testUser);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_simpleTermBoost()
|
||||
{
|
||||
STEP("Search for files and folders by name with higher priority for files");
|
||||
String boostedQuery = "TYPE:('cm:content'^2 OR 'cm:folder'^0.5) AND cm:name:" + SEARCH_TERM;
|
||||
SearchRequest searchRequest = req("afts", boostedQuery);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithTermInName.getName(), folderWithTermInName.getName());
|
||||
|
||||
STEP("Search for files and folders by name with higher priority for folders");
|
||||
String invertedBoost = "TYPE:('cm:content'^0.5 OR 'cm:folder'^2) AND cm:name:" + SEARCH_TERM;
|
||||
searchRequest = req("afts", invertedBoost);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, folderWithTermInName.getName(), fileWithTermInName.getName());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_complexTermBoost()
|
||||
{
|
||||
STEP("Search for files and folders by name or title with higher priority for files by name");
|
||||
String boostedQuery1 = "TYPE:('cm:content'^4 OR 'cm:folder'^0.5)^6 AND (cm:name:" + SEARCH_TERM + "^1.5 OR cm:title:" + SEARCH_TERM + "^0.5)";
|
||||
SearchRequest searchRequest = req("afts", boostedQuery1);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithTermInName.getName(), fileWithTermInTitle.getName(), folderWithTermInName.getName(), folderWithTermInTitle.getName());
|
||||
|
||||
STEP("Search for files and folders by name or title with higher priority for folders by name");
|
||||
String boostedQuery2 = "TYPE:('cm:content'^0.5 OR 'cm:folder'^4)^6 AND (cm:name:" + SEARCH_TERM + "^1.5 OR cm:title:" + SEARCH_TERM + "^0.5)";
|
||||
searchRequest = req("afts", boostedQuery2);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, folderWithTermInName.getName(), folderWithTermInTitle.getName(), fileWithTermInName.getName(), fileWithTermInTitle.getName());
|
||||
|
||||
STEP("Search for files and folders by name or title with higher priority for files by title");
|
||||
String boostedQuery3 = "TYPE:('cm:content'^4 OR 'cm:folder'^0.5)^6 AND (cm:name:" + SEARCH_TERM + "^0.5 OR cm:title:" + SEARCH_TERM + "^1.5)";
|
||||
searchRequest = req("afts", boostedQuery3);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithTermInTitle.getName(), fileWithTermInName.getName(), folderWithTermInTitle.getName(), folderWithTermInName.getName());
|
||||
|
||||
STEP("Search for files and folders by name or title with higher priority for folders by title");
|
||||
String boostedQuery4 = "TYPE:('cm:content'^0.5 OR 'cm:folder'^4)^6 AND (cm:name:" + SEARCH_TERM + "^0.5 OR cm:title:" + SEARCH_TERM + "^1.5)";
|
||||
searchRequest = req("afts", boostedQuery4);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, folderWithTermInTitle.getName(), folderWithTermInName.getName(), fileWithTermInTitle.getName(), fileWithTermInName.getName());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_phraseBoost()
|
||||
{
|
||||
STEP("Search for files by name or TEXT with higher priority for name filter");
|
||||
String boostedQuery = "TYPE:'cm:content' AND (cm:name:" + SEARCH_TERM + "^2 OR TEXT:'" + SEARCH_TERM + " searched'^0.1)";
|
||||
SearchRequest searchRequest = req("afts", boostedQuery);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithTermInName.getName(), fileWithPhraseInContent.getName());
|
||||
|
||||
STEP("Search for files by name or TEXT with higher priority for TEXT filter");
|
||||
String invertedBoost = "TYPE:'cm:content' AND (cm:name:" + SEARCH_TERM + "^0.1 OR TEXT:'" + SEARCH_TERM + " searched'^2)";
|
||||
searchRequest = req("afts", invertedBoost);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithPhraseInContent.getName(), fileWithTermInName.getName());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_exactTermBoost()
|
||||
{
|
||||
STEP("Search for files by exact name or content with higher priority for exact name filter");
|
||||
String boostedQuery = "TYPE:'cm:content' AND (=cm:name:" + SEARCH_TERM + ".txt^2 OR =cm:content:" + SEARCH_TERM + "^0.5)";
|
||||
SearchRequest searchRequest = req("afts", boostedQuery);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithTermInName.getName(), fileWithPhraseInContent.getName());
|
||||
|
||||
STEP("Search for files by exact name or content with higher priority for exact content filter");
|
||||
String invertedBoost = "TYPE:'cm:content' AND (=cm:name:" + SEARCH_TERM + ".txt^0.1 OR =cm:content:" + SEARCH_TERM + "^3)";
|
||||
searchRequest = req("afts", invertedBoost);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithPhraseInContent.getName(), fileWithTermInName.getName());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_expandedTermBoost()
|
||||
{
|
||||
STEP("Search for files by expanded name and two different terms with higher priority for first term");
|
||||
String boostedQuery = "TYPE:'cm:content' AND (~cm:name:" + SEARCH_TERM + "^3 OR ~cm:name:" + DIFFERENT_SEARCH_TERM + "^0.1)";
|
||||
SearchRequest searchRequest = req("afts", boostedQuery);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithTermInName.getName(), fileWithDifferentTermInName.getName());
|
||||
|
||||
STEP("Search for files by expanded name and two different terms with higher priority for second term");
|
||||
String invertedBoost = "TYPE:'cm:content' AND (~cm:name:" + SEARCH_TERM + "^0.1 OR ~cm:name:" + DIFFERENT_SEARCH_TERM + "^3)";
|
||||
searchRequest = req("afts", invertedBoost);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithDifferentTermInName.getName(), fileWithTermInName.getName());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_fuzzyMatchingBoost()
|
||||
{
|
||||
STEP("Fuzzy matching search for files by name or title with higher priority for fuzzy name filter");
|
||||
String boostedQuery = "TYPE:'cm:content' AND (cm:name:" + SEARCH_TERM + "~0.7^3 OR cm:title:" + SEARCH_TERM + "^0.01)";
|
||||
SearchRequest searchRequest = req("afts", boostedQuery);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithTermInName.getName(), fileWithDifferentTermInName.getName(), fileWithTermInTitle.getName());
|
||||
|
||||
STEP("Fuzzy matching search for files by name or title with higher priority for fuzzy title filter");
|
||||
String invertedBoost = "TYPE:'cm:content' AND (cm:name:" + SEARCH_TERM + "~0.7^0.01 OR cm:title:" + SEARCH_TERM + "^3)";
|
||||
searchRequest = req("afts", invertedBoost);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithTermInTitle.getName(), fileWithTermInName.getName(), fileWithDifferentTermInName.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if boosts works fine with words proximity search. Files containing terms within specific distance from another one should be returned.
|
||||
*/
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_proximitySearchBoost()
|
||||
{
|
||||
STEP("Search for files by name or proximity TEXT with higher priority for name filter");
|
||||
String boostedQuery = "TYPE:'cm:content' AND (cm:name:" + SEARCH_TERM + "^5 OR TEXT:(" + SEARCH_TERM + " *(1) phrase)^0.1)";
|
||||
SearchRequest searchRequest = req("afts", boostedQuery);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithTermInName.getName(), fileWithPhraseInContent.getName());
|
||||
|
||||
STEP("Search for files by name or proximity TEXT with higher priority for proximity TEXT filter");
|
||||
String invertedBoost = "TYPE:'cm:content' AND (cm:name:" + SEARCH_TERM + "^0.1 OR TEXT:(" + SEARCH_TERM + " *(1) phrase)^5)";
|
||||
searchRequest = req("afts", invertedBoost);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithPhraseInContent.getName(), fileWithTermInName.getName());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_dateRangeSearchBoost()
|
||||
{
|
||||
String timeFrom = creationTime.format(DateTimeFormatter.ISO_DATE_TIME);
|
||||
String timeTo = afterCreationTime.format(DateTimeFormatter.ISO_DATE_TIME);
|
||||
|
||||
STEP("Search for files and folders by name or creation time range with higher priority for name filter");
|
||||
String boostedQuery = "TYPE:('cm:content'^16 OR 'cm:folder'^1) AND (cm:name:" + SEARCH_TERM + "^4 OR cm:created:['" + timeFrom + "' TO '" + timeTo + "']^0.1)^3";
|
||||
SearchRequest searchRequest = req("afts", boostedQuery);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, fileWithTermInName.getName(), folderWithTermInName.getName(), fileWithTermInTitle.getName(), folderWithTermInTitle.getName());
|
||||
|
||||
STEP("Search for files and folders by name or creation time range with higher priority for creation time range filter");
|
||||
String invertedBoost = "TYPE:('cm:content' OR 'cm:folder'^3) AND (cm:name:" + SEARCH_TERM + "^0.1 OR cm:created:['" + timeFrom + "' TO '" + timeTo + "']^4)^3";
|
||||
searchRequest = req("afts", invertedBoost);
|
||||
searchQueryService.expectResultsInOrder(searchRequest, testUser, folderWithTermInTitle.getName(), fileWithTermInTitle.getName(), folderWithTermInName.getName(), fileWithTermInName.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if boosts works fine with words range search. Files containing words from alphabetical range (from 'mountain' to 'phrase', this includes word 'other') should be returned.
|
||||
*/
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_wordsRangeSearchBoost()
|
||||
{
|
||||
String contentPath = "AND PATH:\"/app:company_home/cm:" + testFolder.getName() + "//*\"";
|
||||
|
||||
STEP("Search for files by name or words in content from given range with higher priority for name filter");
|
||||
String boostedQuery = "TYPE:'cm:content' AND (cm:name:" + SEARCH_TERM + "^3 OR cm:content:" + SEARCH_TERM + "..phrase^0.1) " + contentPath;
|
||||
SearchRequest searchRequest = req("afts", boostedQuery);
|
||||
searchQueryService.expectResultsStartingWithOneOf(searchRequest, testUser, fileWithTermInName.getName());
|
||||
searchQueryService.expectResultsFromQuery(searchRequest, testUser, fileWithTermInName.getName(), fileWithPhraseInContent.getName(), fileWithDifferentTermInName.getName());
|
||||
|
||||
STEP("Search for files by name or words in content from given range with higher priority for words in content from given range filter");
|
||||
String invertedBoost = "TYPE:'cm:content' AND (cm:name:" + SEARCH_TERM + "^0.1 OR cm:content:" + SEARCH_TERM + "..phrase^3) " + contentPath;
|
||||
searchRequest = req("afts", invertedBoost);
|
||||
searchQueryService.expectResultsStartingWithOneOf(searchRequest, testUser, fileWithPhraseInContent.getName(), fileWithDifferentTermInName.getName());
|
||||
searchQueryService.expectResultsFromQuery(searchRequest, testUser, fileWithTermInName.getName(), fileWithPhraseInContent.getName(), fileWithDifferentTermInName.getName());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_wildcardSearchBoost()
|
||||
{
|
||||
STEP("Search for files by wildcard name or title with higher priority for wildcard name filter");
|
||||
String wildcardTerm = SEARCH_TERM.replaceFirst("^.", "?");
|
||||
String boostedQuery = "TYPE:'cm:content' AND (cm:name:" + wildcardTerm + "^3 OR cm:title:" + SEARCH_TERM + "^0.1)";
|
||||
SearchRequest searchRequest = req("afts", boostedQuery);
|
||||
searchQueryService.expectResultsStartingWithOneOf(searchRequest, testUser, fileWithTermInName.getName(), fileWithDifferentTermInName.getName());
|
||||
searchQueryService.expectResultsFromQuery(searchRequest, testUser, fileWithTermInName.getName(), fileWithDifferentTermInName.getName(), fileWithTermInTitle.getName());
|
||||
|
||||
STEP("Search for files by wildcard name or title with higher priority for wildcard title filter");
|
||||
wildcardTerm = SEARCH_TERM.replaceFirst("^.", "*");
|
||||
String invertedBoost = "TYPE:'cm:content' AND (cm:name:" + wildcardTerm + "^0.1 OR cm:title:" + SEARCH_TERM + "^3)";
|
||||
searchRequest = req("afts", invertedBoost);
|
||||
searchQueryService.expectResultsStartingWithOneOf(searchRequest, testUser, fileWithTermInTitle.getName());
|
||||
searchQueryService.expectResultsFromQuery(searchRequest, testUser, fileWithTermInName.getName(), fileWithDifferentTermInName.getName(), fileWithTermInTitle.getName());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_invalidNegativeBoost()
|
||||
{
|
||||
STEP("Try to search for files by name using negative boost and expect 500 error response");
|
||||
String boostedQuery = "TYPE:'cm:content'^-2 AND cm:name:" + SEARCH_TERM;
|
||||
SearchRequest searchRequest = req("afts", boostedQuery);
|
||||
searchQueryService.expectErrorFromQuery(searchRequest, testUser, HttpStatus.INTERNAL_SERVER_ERROR, EMPTY);
|
||||
}
|
||||
|
||||
private ContentModel createRandomFileWithTitle(ContentModel parent, String title)
|
||||
{
|
||||
return createRandomFile(parent, title, null, null);
|
||||
}
|
||||
|
||||
private ContentModel createRandomFileWithTitle(String title)
|
||||
{
|
||||
return createRandomFile(title, null, null);
|
||||
}
|
||||
|
||||
private ContentModel createRandomFile(ContentModel parent, String title, String description, String tag)
|
||||
{
|
||||
return createFile(parent, getRandomFile(FileType.TEXT_PLAIN), "dummy content", title, description, tag);
|
||||
}
|
||||
|
||||
private ContentModel createRandomFile(String title, String description, String tag)
|
||||
{
|
||||
return createFile(getRandomFile(FileType.TEXT_PLAIN), "dummy content", title, description, tag);
|
||||
}
|
||||
|
||||
private ContentModel createFile(ContentModel parent, String filename, String content)
|
||||
{
|
||||
return createFile(parent, filename, content, null, null, null);
|
||||
}
|
||||
|
||||
private ContentModel createFile(String filename, String content)
|
||||
{
|
||||
return createFile(filename, content, null, null, null);
|
||||
}
|
||||
|
||||
private ContentModel createFile(ContentModel parent, String filename, String content, String title, String description, String tag)
|
||||
{
|
||||
FileModel fileModel = new FileModel(filename, FileType.TEXT_PLAIN, content);
|
||||
fileModel.setTitle(title);
|
||||
fileModel.setDescription(description);
|
||||
|
||||
FileModel file = dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(parent)
|
||||
.createContent(fileModel);
|
||||
|
||||
if (StringUtils.isNotBlank(tag))
|
||||
{
|
||||
dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(file)
|
||||
.addTagToContent(RestTagModel.builder().tag(tag).create());
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
private ContentModel createFile(String filename, String content, String title, String description, String tag)
|
||||
{
|
||||
ContentModel contentRoot = new ContentModel("-root-");
|
||||
contentRoot.setNodeRef(contentRoot.getName());
|
||||
return createFile(contentRoot, filename, content, title, description, tag);
|
||||
}
|
||||
|
||||
private ContentModel createRandomFolderWithTitle(ContentModel parent, String title)
|
||||
{
|
||||
return createFolder(parent, getRandomName("folder"), title, null, null);
|
||||
}
|
||||
|
||||
private ContentModel createFolder(ContentModel parent, String folderName)
|
||||
{
|
||||
return createFolder(parent, folderName, null, null, null);
|
||||
}
|
||||
|
||||
private ContentModel createFolder(String folderName)
|
||||
{
|
||||
return createFolder(folderName, null, null, null);
|
||||
}
|
||||
|
||||
private ContentModel createFolder(ContentModel parent, String folderName, String title, String description, String tag)
|
||||
{
|
||||
FolderModel folderModel = new FolderModel(folderName, title, description);
|
||||
|
||||
FolderModel folder = dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(parent)
|
||||
.createFolder(folderModel);
|
||||
|
||||
if (StringUtils.isNotBlank(tag))
|
||||
{
|
||||
dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(folder)
|
||||
.addTagToContent(RestTagModel.builder().tag(tag).create());
|
||||
}
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
private ContentModel createFolder(String folderName, String title, String description, String tag)
|
||||
{
|
||||
ContentModel contentRoot = new ContentModel("-root-");
|
||||
contentRoot.setNodeRef(contentRoot.getName());
|
||||
return createFolder(contentRoot, folderName, title, description, tag);
|
||||
}
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
import static org.alfresco.tas.TestDataUtility.getAlphabeticUUID;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FileType;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
/**
|
||||
* Tests for CMIS queries that require path indexing against Elasticsearch.
|
||||
*/
|
||||
public class ElasticsearchCMISPathTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
private static final String PREFIX = getAlphabeticUUID();
|
||||
private static final String FOLDER_0_NAME = PREFIX + "_folder0";
|
||||
private static final String FOLDER_00_NAME = PREFIX + "_folder00";
|
||||
private static final String FOLDER_000_NAME = PREFIX + "_folder000";
|
||||
private static final String FOLDER_1_NAME = PREFIX + "_folder1";
|
||||
private static final String DOC_0000_NAME = PREFIX + "_doc0000.txt";
|
||||
private static final String DOC_00_NAME = PREFIX + "_doc00.txt";
|
||||
private static final String DOC_01_NAME = PREFIX + "_doc01.txt";
|
||||
private static final String DOC_10_NAME = PREFIX + "_doc10.txt";
|
||||
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
@Autowired
|
||||
private DataContent dataContent;
|
||||
@Autowired
|
||||
private DataSite dataSite;
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
@Autowired
|
||||
protected SearchQueryService searchQueryService;
|
||||
|
||||
private UserModel user;
|
||||
private SiteModel siteModel;
|
||||
private FolderModel folder0;
|
||||
private FolderModel folder00;
|
||||
private FolderModel folder000;
|
||||
private FolderModel folder1;
|
||||
private FileModel document0000;
|
||||
private FileModel document00;
|
||||
private FileModel document01;
|
||||
private FileModel document10;
|
||||
|
||||
/**
|
||||
* Data will be prepared using the schema below:
|
||||
*
|
||||
* <pre>
|
||||
* Site
|
||||
* + Document Library
|
||||
* +-folder0
|
||||
* | +-folder00
|
||||
* | | +-folder000
|
||||
* | | +-document0000
|
||||
* | +-document00
|
||||
* | +-document01
|
||||
* +-folder1
|
||||
* +-document10
|
||||
* </pre>
|
||||
*/
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
user = dataUser.createRandomTestUser();
|
||||
|
||||
siteModel = dataSite.usingUser(user).createPrivateRandomSite();
|
||||
|
||||
folder0 = dataContent.usingUser(user).usingSite(siteModel).createFolderCmisApi(FOLDER_0_NAME);
|
||||
folder1 = dataContent.usingUser(user).usingSite(siteModel).createFolderCmisApi(FOLDER_1_NAME);
|
||||
|
||||
folder00 = dataContent.usingUser(user).usingResource(folder0).createFolderCmisApi(FOLDER_00_NAME);
|
||||
folder000 = dataContent.usingUser(user).usingResource(folder00).createFolderCmisApi(FOLDER_000_NAME);
|
||||
|
||||
document0000 = createContent(DOC_0000_NAME, "This is document 0000", folder000, user);
|
||||
document00 = createContent(DOC_00_NAME, "This is document 00", folder0, user);
|
||||
document01 = createContent(DOC_01_NAME, "This is document 01", folder0, user);
|
||||
document10 = createContent(DOC_10_NAME, "This is document 10", folder1, user);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void inTreeQuery_selectSubfolders()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:folder WHERE IN_TREE('" + folder0.getNodeRef() + "')");
|
||||
searchQueryService.expectResultsFromQuery(query, user, FOLDER_00_NAME, FOLDER_000_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void inTreeQuery_selectDocuments()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE IN_TREE('" + folder0.getNodeRef() + "')");
|
||||
searchQueryService.expectResultsFromQuery(query, user, DOC_0000_NAME, DOC_00_NAME, DOC_01_NAME);
|
||||
}
|
||||
|
||||
private FileModel createContent(String filename, String content, FolderModel folderModel, UserModel user)
|
||||
{
|
||||
FileModel fileModel = new FileModel(filename, FileType.TEXT_PLAIN, content);
|
||||
return dataContent.usingUser(user).usingResource(folderModel).createContent(fileModel);
|
||||
}
|
||||
}
|
||||
+604
@@ -0,0 +1,604 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
import static org.alfresco.tas.TestDataUtility.getAlphabeticUUID;
|
||||
import static org.alfresco.utility.report.log.Step.STEP;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.Utility;
|
||||
import org.alfresco.utility.constants.UserRole;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FileType;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
/**
|
||||
* In this test we are verifying end-to-end the indexing and CMIS queries against Elasticsearch. In order to test ACLs we created 2 sites and 3 users.
|
||||
*/
|
||||
public class ElasticsearchCMISTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
private static final String PREFIX = getAlphabeticUUID();
|
||||
private static final String SUFFIX = getAlphabeticUUID();
|
||||
private static final String UNIQUE_WORD = getAlphabeticUUID();
|
||||
private static final String FILE_0_NAME = PREFIX + "_test.txt" + SUFFIX;
|
||||
private static final String FILE_1_NAME = "internal_" + PREFIX + "_and_" + SUFFIX + ".txt";
|
||||
private static final String FILE_2_NAME = PREFIX + "_user2doc_" + SUFFIX;
|
||||
/** This is a file that user 1 doesn't have access to and so shouldn't be returned in their search results. */
|
||||
private static final String USER_2_FILE_NAME = PREFIX + "_user2only_" + SUFFIX;
|
||||
private static final String FOLDER_PREFIX = getAlphabeticUUID();
|
||||
private static final String FOLDER_0_NAME = FOLDER_PREFIX + "_folder0";
|
||||
private static final String FOLDER_1_NAME = FOLDER_PREFIX + "_folder1";
|
||||
private static final int X_DIMENSION = 48;
|
||||
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
|
||||
@Autowired
|
||||
private DataContent dataContent;
|
||||
|
||||
@Autowired
|
||||
private DataSite dataSite;
|
||||
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
|
||||
@Autowired
|
||||
protected SearchQueryService searchQueryService;
|
||||
|
||||
private UserModel user1;
|
||||
private UserModel user2;
|
||||
private UserModel userMultiSite;
|
||||
private SiteModel siteModel1;
|
||||
private SiteModel siteModel2;
|
||||
private FileModel file0;
|
||||
private List<String> fileCreationDates;
|
||||
private String file3Name;
|
||||
/**
|
||||
* Scoping predicate restricting the result universe to the 4 documents user1 owns in this class. Used to AND into negative / open-range queries so they aren't polluted by bootstrap content or by files created in other test classes that share the same Spring context.
|
||||
*/
|
||||
private String user1FilesScope;
|
||||
/**
|
||||
* Scoping predicate restricting to file3 (the only EXIF-bearing image we upload). Used to AND into negative / open-range integer queries on exif:pixelXDimension.
|
||||
*/
|
||||
private String file3Scope;
|
||||
|
||||
/**
|
||||
* Data will be prepared using the schema below:
|
||||
* <p>
|
||||
* Site1: - Users: user1, userMultiSite - Documents: FILE_0_NAME (owner: user1), FILE_1_NAME (owner: user1), FILE_2_NAME (owner: user2), file3Name (owner: user1) - Folders: FOLDER_0_NAME (owner: user1), FOLDER_1_NAME (owner: user1)
|
||||
* <p>
|
||||
* Site2: - Users: user2, userMultiSite - Documents: USER_2_FILE_NAME (owner: user2)
|
||||
*/
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation() throws IOException, InterruptedException
|
||||
{
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
user1 = dataUser.createRandomTestUser();
|
||||
user2 = dataUser.createRandomTestUser();
|
||||
userMultiSite = dataUser.createRandomTestUser();
|
||||
|
||||
siteModel1 = dataSite.usingUser(user1).createPrivateRandomSite();
|
||||
siteModel2 = dataSite.usingUser(user2).createPrivateRandomSite();
|
||||
|
||||
dataUser.addUserToSite(user2, siteModel1, UserRole.SiteContributor);
|
||||
dataUser.addUserToSite(userMultiSite, siteModel1, UserRole.SiteContributor);
|
||||
dataUser.addUserToSite(userMultiSite, siteModel2, UserRole.SiteContributor);
|
||||
|
||||
file0 = createContent(FILE_0_NAME, "This is the first test containing " + UNIQUE_WORD, siteModel1, user1);
|
||||
FileModel file1 = createContent(FILE_1_NAME, "This is another TEST file containing " + UNIQUE_WORD, siteModel1, user1);
|
||||
FileModel file2 = createContent(FILE_2_NAME, "This Test file is owned by user2 " + UNIQUE_WORD, siteModel1, user2);
|
||||
FileModel file3 = uploadDocument("test-data/alfresco-logo.png", user1, siteModel1);
|
||||
file3Name = file3.getName();
|
||||
|
||||
fileCreationDates = getCreationDates(file0, file1, file2);
|
||||
|
||||
// Remove user 2 from site, but he keeps ownership on FILE_2_NAME.
|
||||
dataUser.removeUserFromSite(user2, siteModel1);
|
||||
// Also create another file that only user 2 has access to.
|
||||
createContent(USER_2_FILE_NAME, "This is a test file that user1 does not have access to, but it still contains " + UNIQUE_WORD, siteModel2, user2);
|
||||
|
||||
dataContent.usingUser(user1).usingSite(siteModel1).createFolder(new FolderModel(FOLDER_0_NAME));
|
||||
dataContent.usingUser(user1).usingSite(siteModel1).createFolder(new FolderModel(FOLDER_1_NAME));
|
||||
|
||||
// Pre-build scoping predicates used by the negative / open-range tests below.
|
||||
user1FilesScope = "cmis:name IN ('" + FILE_0_NAME + "', '" + FILE_1_NAME + "', '" + FILE_2_NAME + "', '" + file3Name + "')";
|
||||
file3Scope = "D.cmis:name = '" + file3Name + "'";
|
||||
|
||||
STEP("Wait for batch indexer to index the last-created node (FOLDER_1_NAME)");
|
||||
SearchRequest probe = req("cmis", "SELECT * FROM cmis:folder WHERE cmis:name = '" + FOLDER_1_NAME + "'");
|
||||
Utility.sleep(500, 60000, () -> searchQueryService.expectResultsFromQuery(probe, user1, FOLDER_1_NAME));
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void basicQuery()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document");
|
||||
searchQueryService.expectResultsInclude(query, user1, FILE_0_NAME, FILE_1_NAME, FILE_2_NAME, file3Name);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void objectIdQuery()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE cmis:objectId = '" + file0.getNodeRef() + "'");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void objectTypeIdQuery()
|
||||
{
|
||||
SearchRequest query1 = req("cmis", "SELECT * FROM cmis:folder WHERE cmis:objectTypeId = 'cmis:folder'");
|
||||
searchQueryService.expectResultsInclude(query1, user1, "documentLibrary", FOLDER_0_NAME, FOLDER_1_NAME, user1.getUsername());
|
||||
|
||||
SearchRequest query2 = req("cmis", "SELECT * FROM cmis:folder WHERE cmis:objectTypeId = 'F:st:site'");
|
||||
searchQueryService.expectResultsInclude(query2, user1, siteModel1.getId());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void baseTypeIdQuery()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:folder WHERE cmis:baseTypeId = 'cmis:folder'");
|
||||
searchQueryService.expectResultsInclude(query, user1, "documentLibrary", FOLDER_0_NAME, FOLDER_1_NAME, user1.getUsername(), siteModel1.getId());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void isNullQuery()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:folder WHERE cmis:description IS NULL");
|
||||
searchQueryService.expectResultsInclude(query, user1, "documentLibrary", FOLDER_0_NAME, FOLDER_1_NAME, user1.getUsername());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void isNotNullQuery()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:folder WHERE cmis:description IS NOT NULL");
|
||||
searchQueryService.expectResultsInclude(query, user1, siteModel1.getId());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void matchNamesLikePrefix()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE cmis:name LIKE '" + PREFIX + "%'");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME, FILE_1_NAME, FILE_2_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void matchNamesLikeSuffix()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE cmis:name LIKE '%" + SUFFIX + "'");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME, FILE_1_NAME, FILE_2_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void matchContentOfFile()
|
||||
{
|
||||
// Check the query is case insensitive.
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE CONTAINS('" + UNIQUE_WORD + "')");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME, FILE_1_NAME, FILE_2_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkPermissionForUser2()
|
||||
{
|
||||
// Reuse the prefix query to check which documents user2 can access.
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE cmis:name LIKE '" + PREFIX + "%'");
|
||||
searchQueryService.expectResultsFromQuery(query, user2, FILE_2_NAME, USER_2_FILE_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void matchDocumentName()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE cmis:name = '" + FILE_0_NAME + "'");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void doesNotMatchDocumentName()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE " + user1FilesScope + " AND cmis:name <> '" + FILE_0_NAME + "'");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_1_NAME, FILE_2_NAME, file3Name);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkOrderByAscSyntax()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT cmis:name FROM cmis:document WHERE cmis:name IN('" + FILE_0_NAME + "','" + FILE_1_NAME + "','" + FILE_2_NAME + "') ORDER BY cmis:name ASC");
|
||||
searchQueryService.expectResultsInOrder(query, user1, true, FILE_0_NAME, FILE_1_NAME, FILE_2_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkOrderByDescSyntax()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT cmis:name FROM cmis:document WHERE cmis:name IN('" + FILE_0_NAME + "','" + FILE_1_NAME + "','" + FILE_2_NAME + "') ORDER BY cmis:name DESC");
|
||||
searchQueryService.expectResultsInOrder(query, user1, false, FILE_0_NAME, FILE_1_NAME, FILE_2_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkInSyntax()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE cmis:name IN ('" + FILE_0_NAME + "', '" + FILE_1_NAME + "')");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME, FILE_1_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkNotInSyntax()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE " + user1FilesScope + " AND cmis:name NOT IN ('" + FILE_0_NAME + "', '" + FILE_1_NAME + "')");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_2_NAME, file3Name);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkAfterDateSyntax()
|
||||
{
|
||||
String file0CreationDate = fileCreationDates.get(0);
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE cmis:creationDate > TIMESTAMP '" + file0CreationDate + "'");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_1_NAME, FILE_2_NAME, file3Name);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkAfterOrSameDateSyntax()
|
||||
{
|
||||
String file0CreationDate = fileCreationDates.get(0);
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE cmis:creationDate >= TIMESTAMP '" + file0CreationDate + "'");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME, FILE_1_NAME, FILE_2_NAME, file3Name);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkBeforeDateSyntax()
|
||||
{
|
||||
String file2CreationDate = fileCreationDates.get(2);
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE " + user1FilesScope + " AND cmis:creationDate < TIMESTAMP '" + file2CreationDate + "'");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME, FILE_1_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkBeforeOrSameDateSyntax()
|
||||
{
|
||||
String file2CreationDate = fileCreationDates.get(2);
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE " + user1FilesScope + " AND cmis:creationDate <= TIMESTAMP '" + file2CreationDate + "'");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME, FILE_1_NAME, FILE_2_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkMatchesDateSyntax()
|
||||
{
|
||||
String file0CreationDate = fileCreationDates.get(0);
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE cmis:creationDate = TIMESTAMP '" + file0CreationDate + "'");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkDoesNotMatchDateSyntax()
|
||||
{
|
||||
String file0CreationDate = fileCreationDates.get(0);
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE " + user1FilesScope + " AND cmis:creationDate <> TIMESTAMP '" + file0CreationDate + "'");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_1_NAME, FILE_2_NAME, file3Name);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkInDatesSyntax()
|
||||
{
|
||||
String file0CreationDate = fileCreationDates.get(0);
|
||||
String file1CreationDate = fileCreationDates.get(1);
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE cmis:creationDate IN (TIMESTAMP '" + file0CreationDate + "', TIMESTAMP '" + file1CreationDate + "')");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME, FILE_1_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkNotInDatesSyntax()
|
||||
{
|
||||
String file0CreationDate = fileCreationDates.get(0);
|
||||
String file1CreationDate = fileCreationDates.get(1);
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document WHERE " + user1FilesScope + " AND cmis:creationDate NOT IN (TIMESTAMP '" + file0CreationDate + "', TIMESTAMP '" + file1CreationDate + "')");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_2_NAME, file3Name);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkEqualIntegerSyntax()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE E.exif:pixelXDimension = " + X_DIMENSION);
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectResultsFromQuery(query, user1, file3Name);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkDifferentThanIntegerSyntax()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE " + file3Scope + " AND E.exif:pixelXDimension <> " + X_DIMENSION);
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectNoResultsFromQuery(query, user1);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkGreaterThanIntegerSyntax()
|
||||
{
|
||||
int lessThanX = X_DIMENSION - 1;
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE " + file3Scope + " AND E.exif:pixelXDimension > " + lessThanX);
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectResultsFromQuery(query, user1, file3Name);
|
||||
|
||||
query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE " + file3Scope + " AND E.exif:pixelXDimension > " + X_DIMENSION);
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectNoResultsFromQuery(query, user1);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkGreaterThanOrEqualIntegerSyntax()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE " + file3Scope + " AND E.exif:pixelXDimension >= " + X_DIMENSION);
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectResultsFromQuery(query, user1, file3Name);
|
||||
|
||||
int moreThanX = X_DIMENSION + 1;
|
||||
query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE " + file3Scope + " AND E.exif:pixelXDimension >= " + moreThanX);
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectNoResultsFromQuery(query, user1);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkLessThanIntegerSyntax()
|
||||
{
|
||||
int moreThanX = X_DIMENSION + 1;
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE E.exif:pixelXDimension < " + moreThanX);
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectResultsFromQuery(query, user1, file3Name);
|
||||
|
||||
query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE E.exif:pixelXDimension < " + X_DIMENSION);
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectNoResultsFromQuery(query, user1);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkLessThanOrEqualIntegerSyntax()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE E.exif:pixelXDimension <= " + X_DIMENSION);
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectResultsFromQuery(query, user1, file3Name);
|
||||
|
||||
int lessThanX = X_DIMENSION - 1;
|
||||
query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE E.exif:pixelXDimension <= " + lessThanX);
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectNoResultsFromQuery(query, user1);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkInIntegersSyntax()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE E.exif:pixelXDimension IN (" + X_DIMENSION + ", " + 0 + ")");
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectResultsFromQuery(query, user1, file3Name);
|
||||
|
||||
query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE E.exif:pixelXDimension IN (" + 0 + ", " + -1 + ")");
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectNoResultsFromQuery(query, user1);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void checkNotInIntegersSyntax()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE " + file3Scope + " AND E.exif:pixelXDimension NOT IN (" + X_DIMENSION + ", " + 0 + ")");
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectNoResultsFromQuery(query, user1);
|
||||
|
||||
query = req("cmis", "SELECT * FROM cmis:document AS D JOIN exif:exif AS E ON D.cmis:objectId = E.cmis:objectId WHERE " + file3Scope + " AND E.exif:pixelXDimension NOT IN (" + 0 + ", " + -1 + ")");
|
||||
query.setInclude(List.of("properties"));
|
||||
searchQueryService.expectResultsFromQuery(query, user1, file3Name);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void negative_basicCMISQuery_missingFrom()
|
||||
{
|
||||
// note: ideally 400 but currently 500 (also for Solr) :-(
|
||||
|
||||
SearchRequest query1 = req("cmis", "SELECT *");
|
||||
searchQueryService.expectErrorFromQuery(query1, user1, HttpStatus.INTERNAL_SERVER_ERROR, "expecting FROM");
|
||||
|
||||
SearchRequest query2 = req("cmis", "SELECT * FROM");
|
||||
searchQueryService.expectErrorFromQuery(query2, user1, HttpStatus.INTERNAL_SERVER_ERROR, "no viable alternative at input");
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void negative_basicCMISQuery_invalidType()
|
||||
{
|
||||
// note: ideally 400 but currently 500 (also for Solr) :-(
|
||||
SearchRequest query = req("SELECT * FROM cmis:unknown");
|
||||
searchQueryService.expectErrorFromQuery(query, user1, HttpStatus.INTERNAL_SERVER_ERROR, "Unknown property: {http://www.alfresco.org/model/content/1.0}cmis");
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void negative_objectTypeIdQuery_invalidType()
|
||||
{
|
||||
// note: ideally 400 but currently 500 (also for Solr) :-(
|
||||
SearchRequest query = req("SELECT * FROM cmis:folder WHERE cmis:objectTypeId = 'unknown:site'");
|
||||
searchQueryService.expectErrorFromQuery(query, user1, HttpStatus.INTERNAL_SERVER_ERROR, "Unknown property: {http://www.alfresco.org/model/content/1.0}cmis");
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void negative_isNullQuery_invalidField()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:folder WHERE cmis:unknown IS NULL");
|
||||
searchQueryService.expectErrorFromQuery(query, user1, HttpStatus.INTERNAL_SERVER_ERROR, "Unknown column/property cmis:unknown");
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void negative_isNotNullQuery_invalidField()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:folder WHERE cmis:unknown IS NOT NULL");
|
||||
searchQueryService.expectErrorFromQuery(query, user1, HttpStatus.INTERNAL_SERVER_ERROR, "Unknown column/property cmis:unknown");
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void negative_basicCMISQuery_invalidFieldName()
|
||||
{
|
||||
// note: ideally 400 but currently 500 (also for Solr) :-(
|
||||
|
||||
SearchRequest query1 = req("SELECT cmis:unknown FROM cmis:document");
|
||||
searchQueryService.expectErrorFromQuery(query1, user1, HttpStatus.INTERNAL_SERVER_ERROR, "Unknown property: {http://www.alfresco.org/model/content/1.0}cmis");
|
||||
|
||||
SearchRequest query2 = req("SELECT cm:unknown FROM cmis:document");
|
||||
searchQueryService.expectErrorFromQuery(query2, user1, HttpStatus.INTERNAL_SERVER_ERROR, "Unknown property: {http://www.alfresco.org/model/content/1.0}cm");
|
||||
|
||||
SearchRequest query3 = req("SELECT my:custom FROM cmis:document");
|
||||
searchQueryService.expectErrorFromQuery(query3, user1, HttpStatus.INTERNAL_SERVER_ERROR, "Unknown property: {http://www.alfresco.org/model/content/1.0}my");
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void negative_baseTypeIdQuery_invalidType()
|
||||
{
|
||||
// note: ideally 400 but currently 500 (also for Solr) :-(
|
||||
SearchRequest query = req("SELECT * FROM cmis:folder WHERE cmis:baseTypeId = 'cmis:unknown'");
|
||||
searchQueryService.expectErrorFromQuery(query, user1, HttpStatus.INTERNAL_SERVER_ERROR, "Unknown property: {http://www.alfresco.org/model/content/1.0}cmis");
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void selectFolders()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:folder");
|
||||
searchQueryService.expectResultsInclude(query, user1,
|
||||
// We expect the site and the document library, ...
|
||||
siteModel1.getId(), "documentLibrary",
|
||||
// the two folders, ...
|
||||
FOLDER_0_NAME, FOLDER_1_NAME,
|
||||
// and the user home for the user performing the query.
|
||||
user1.getUsername());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void selectItems()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cmis:item");
|
||||
// This error is consistent with the way the DB and Solr handle the request.
|
||||
searchQueryService.expectErrorFromQuery(query, user1, HttpStatus.INTERNAL_SERVER_ERROR, "Type is not queryable cmis:item");
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void selectObjects()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cm:cmobject WHERE cmis:name IN ('" + FILE_0_NAME + "', '" + FOLDER_0_NAME + "')");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME, FOLDER_0_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void selectPeople()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT * FROM cm:person");
|
||||
searchQueryService.expectNodeTypesFromQuery(query, user1, "cm:person");
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void tableAlias()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT d.cmis:name FROM cmis:document d WHERE d.cmis:name IN ('" + FILE_0_NAME + "')");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void tableAliasWithAs()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT d.cmis:name FROM cmis:document AS d WHERE d.cmis:name IN ('" + FILE_0_NAME + "')");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void tableAlias_mismatchedAliasInSelect()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT z.cmis:name FROM cmis:document d WHERE d.cmis:name IN ('" + FILE_0_NAME + "')");
|
||||
searchQueryService.expectErrorFromQuery(query, user1, HttpStatus.INTERNAL_SERVER_ERROR, "No selector for z");
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void tableAlias_mismatchedAliasInWhere()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT d.cmis:name FROM cmis:document d WHERE z.cmis:name IN ('" + FILE_0_NAME + "')");
|
||||
searchQueryService.expectErrorFromQuery(query, user1, HttpStatus.INTERNAL_SERVER_ERROR, "No selector for z");
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void tableJoin()
|
||||
{
|
||||
SearchRequest query = req("cmis", "SELECT t.*, d.* FROM cm:titled t JOIN cmis:document d ON t.cmis:objectId = d.cmis:objectId WHERE d.cmis:name = '" + FILE_0_NAME + "'");
|
||||
searchQueryService.expectResultsFromQuery(query, user1, FILE_0_NAME);
|
||||
}
|
||||
|
||||
private FileModel createContent(String filename, String content, SiteModel site, UserModel user)
|
||||
{
|
||||
FileModel fileModel = new FileModel(filename, FileType.TEXT_PLAIN, content);
|
||||
return dataContent.usingUser(user).usingSite(site)
|
||||
.createContent(fileModel);
|
||||
}
|
||||
|
||||
private List<String> getCreationDates(FileModel... files)
|
||||
{
|
||||
return getCreationDates(user1, siteModel1, files);
|
||||
}
|
||||
|
||||
private List<String> getCreationDates(UserModel user, SiteModel site, FileModel... files)
|
||||
{
|
||||
return List.of(files)
|
||||
.stream()
|
||||
.map(FileModel::getCmisLocation)
|
||||
.map(cmisLocation -> dataContent.usingUser(user).usingSite(site).getCMISDocument(cmisLocation))
|
||||
.map(cmisDocument -> cmisDocument.<GregorianCalendar> getProperty("cmis:creationDate"))
|
||||
.map(creationDateProperty -> creationDateProperty.<GregorianCalendar> getValue())
|
||||
.map(GregorianCalendar::toInstant)
|
||||
.map(Instant::toString)
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
private FileModel uploadDocument(String filePath, UserModel user, SiteModel site) throws IOException
|
||||
{
|
||||
ClassPathResource toUpload = new ClassPathResource(filePath);
|
||||
return dataContent.usingUser(user)
|
||||
.usingSite(site)
|
||||
.uploadDocument(toUpload.getFile());
|
||||
}
|
||||
}
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.springframework.http.HttpStatus.CREATED;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import static org.alfresco.utility.data.RandomData.getRandomName;
|
||||
import static org.alfresco.utility.report.log.Step.STEP;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.dataprep.CMISUtil;
|
||||
import org.alfresco.rest.core.RestWrapper;
|
||||
import org.alfresco.rest.model.RestCategoryLinkBodyModel;
|
||||
import org.alfresco.rest.model.RestCategoryModel;
|
||||
import org.alfresco.rest.model.RestCategoryModelsCollection;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.Utility;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
import org.alfresco.utility.model.RepoTestModel;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
public class ElasticsearchCategoriesCountTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
protected static final String INCLUDE_COUNT_PARAM = "count";
|
||||
protected static final String ROOT_CATEGORY_ID = "-root-";
|
||||
protected static final String CATEGORY_NAME_PREFIX = "CategoryName";
|
||||
protected static final String FIELD_NAME = "name";
|
||||
protected static final String FIELD_ID = "id";
|
||||
protected static final String FIELD_COUNT = "count";
|
||||
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
@Autowired
|
||||
private DataSite dataSite;
|
||||
@Autowired
|
||||
private DataContent dataContent;
|
||||
@Autowired
|
||||
private RestWrapper restClient;
|
||||
|
||||
private UserModel user;
|
||||
private SiteModel site;
|
||||
private RestCategoryModel categoryLinkedWithFolder;
|
||||
private RestCategoryModel categoryLinkedWithFile;
|
||||
private RestCategoryModel categoryLinkedWithBoth;
|
||||
private RestCategoryModel notLinkedCategory;
|
||||
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation() throws InterruptedException
|
||||
{
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
STEP("Create user and site");
|
||||
user = dataUser.createRandomTestUser();
|
||||
site = dataSite.usingUser(user).createPublicRandomSite();
|
||||
|
||||
STEP("Create a folder, file in it and few categories");
|
||||
final FolderModel folder = dataContent.usingUser(user).usingSite(site).createFolder();
|
||||
final FileModel file = dataContent.usingUser(user).usingResource(folder).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
|
||||
categoryLinkedWithFolder = prepareCategoryUnderRoot();
|
||||
categoryLinkedWithFile = prepareCategoryUnderRoot();
|
||||
categoryLinkedWithBoth = prepareCategoryUnder(prepareCategoryUnderRoot());
|
||||
notLinkedCategory = prepareCategoryUnderRoot();
|
||||
|
||||
STEP("Link folder and file to categories");
|
||||
linkContentToCategories(folder, categoryLinkedWithFolder, categoryLinkedWithBoth);
|
||||
linkContentToCategories(file, categoryLinkedWithFile, categoryLinkedWithBoth);
|
||||
|
||||
STEP("Wait for indexing to complete");
|
||||
Utility.sleep(500, 60000, () -> restClient.authenticateUser(user)
|
||||
.withCoreAPI()
|
||||
.usingCategory(categoryLinkedWithBoth)
|
||||
.include(INCLUDE_COUNT_PARAM)
|
||||
.getCategory()
|
||||
.assertThat()
|
||||
.field(FIELD_COUNT)
|
||||
.is(2));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void dataCleanup()
|
||||
{
|
||||
STEP("Remove site and user");
|
||||
dataSite.usingUser(user).deleteSite(site);
|
||||
dataUser.deleteUser(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify count for a category linked with file and folder.
|
||||
*/
|
||||
@Test(groups = {TestGroup.REST_API})
|
||||
public void testGetCategoryById_includeCount()
|
||||
{
|
||||
STEP("Get linked category and verify if count is higher than 0");
|
||||
final RestCategoryModel actualCategory = restClient.authenticateUser(user)
|
||||
.withCoreAPI()
|
||||
.usingCategory(categoryLinkedWithBoth)
|
||||
.include(INCLUDE_COUNT_PARAM)
|
||||
.getCategory();
|
||||
|
||||
restClient.assertStatusCodeIs(OK);
|
||||
actualCategory.assertThat().field(FIELD_ID).is(categoryLinkedWithBoth.getId());
|
||||
actualCategory.assertThat().field(FIELD_COUNT).is(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify count for a category not linked with any content.
|
||||
*/
|
||||
@Test(groups = {TestGroup.REST_API})
|
||||
public void testGetCategoryById_includeCountForNonLinkedCategory()
|
||||
{
|
||||
STEP("Get non-linked category and verify if count is 0");
|
||||
final RestCategoryModel actualCategory = restClient.authenticateUser(user)
|
||||
.withCoreAPI()
|
||||
.usingCategory(notLinkedCategory)
|
||||
.include(INCLUDE_COUNT_PARAM)
|
||||
.getCategory();
|
||||
|
||||
restClient.assertStatusCodeIs(OK);
|
||||
actualCategory.assertThat().field(FIELD_ID).is(notLinkedCategory.getId());
|
||||
actualCategory.assertThat().field(FIELD_COUNT).is(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify count for three categories: linked with file, linked with folder and third not linked to any content.
|
||||
*/
|
||||
@Test(groups = {TestGroup.REST_API})
|
||||
public void testGetCategories_includeCount()
|
||||
{
|
||||
STEP("Get few categories and verify its counts");
|
||||
final RestCategoryModel parentCategory = createCategoryModelWithId(ROOT_CATEGORY_ID);
|
||||
final RestCategoryModelsCollection actualCategories = restClient.authenticateUser(user)
|
||||
.withCoreAPI()
|
||||
.usingCategory(parentCategory)
|
||||
.include(INCLUDE_COUNT_PARAM)
|
||||
.getCategoryChildren();
|
||||
|
||||
restClient.assertStatusCodeIs(OK);
|
||||
assertTrue(actualCategories.getEntries().stream()
|
||||
.map(RestCategoryModel::onModel)
|
||||
.anyMatch(category -> category.getId().equals(categoryLinkedWithFolder.getId()) && category.getCount() == 1));
|
||||
assertTrue(actualCategories.getEntries().stream()
|
||||
.map(RestCategoryModel::onModel)
|
||||
.anyMatch(category -> category.getId().equals(categoryLinkedWithFile.getId()) && category.getCount() == 1));
|
||||
assertTrue(actualCategories.getEntries().stream()
|
||||
.map(RestCategoryModel::onModel)
|
||||
.anyMatch(category -> category.getId().equals(notLinkedCategory.getId()) && category.getCount() == 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create category and verify that its count is 0.
|
||||
*/
|
||||
@Test(groups = {TestGroup.REST_API})
|
||||
public void testCreateCategory_includingCount()
|
||||
{
|
||||
STEP("Create a category under root and verify if count is 0");
|
||||
final String categoryName = getRandomName("Category");
|
||||
final RestCategoryModel rootCategory = createCategoryModelWithId(ROOT_CATEGORY_ID);
|
||||
final RestCategoryModel aCategory = createCategoryModelWithName(categoryName);
|
||||
final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser())
|
||||
.withCoreAPI()
|
||||
.include(INCLUDE_COUNT_PARAM)
|
||||
.usingCategory(rootCategory)
|
||||
.createSingleCategory(aCategory);
|
||||
|
||||
STEP("Create a category under root category (as admin)");
|
||||
restClient.assertStatusCodeIs(CREATED);
|
||||
createdCategory.assertThat().field(FIELD_NAME).is(categoryName);
|
||||
createdCategory.assertThat().field(FIELD_COUNT).is(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update category linked to file and folder and verify that its count is 2.
|
||||
*/
|
||||
@Test(groups = {TestGroup.REST_API})
|
||||
public void testUpdateCategory_includeCount()
|
||||
{
|
||||
STEP("Update linked category and verify if count is higher than 0");
|
||||
final String categoryNewName = getRandomName("NewCategoryName");
|
||||
final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(categoryNewName);
|
||||
final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser())
|
||||
.withCoreAPI()
|
||||
.usingCategory(categoryLinkedWithBoth)
|
||||
.include(INCLUDE_COUNT_PARAM)
|
||||
.updateCategory(fixedCategoryModel);
|
||||
|
||||
restClient.assertStatusCodeIs(OK);
|
||||
updatedCategory.assertThat().field(FIELD_ID).is(categoryLinkedWithBoth.getId());
|
||||
updatedCategory.assertThat().field(FIELD_COUNT).is(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update category not linked to any content and verify that its count is 0.
|
||||
*/
|
||||
@Test(groups = {TestGroup.REST_API})
|
||||
public void testUpdateCategory_includeCountForNonLinkedCategory()
|
||||
{
|
||||
STEP("Update non-linked category and verify if count is 0");
|
||||
final String categoryNewName = getRandomName("NewCategoryName");
|
||||
final RestCategoryModel fixedCategoryModel = createCategoryModelWithName(categoryNewName);
|
||||
final RestCategoryModel updatedCategory = restClient.authenticateUser(dataUser.getAdminUser())
|
||||
.withCoreAPI()
|
||||
.usingCategory(notLinkedCategory)
|
||||
.include(INCLUDE_COUNT_PARAM)
|
||||
.updateCategory(fixedCategoryModel);
|
||||
|
||||
restClient.assertStatusCodeIs(OK);
|
||||
updatedCategory.assertThat().field(FIELD_ID).is(notLinkedCategory.getId());
|
||||
updatedCategory.assertThat().field(FIELD_COUNT).is(0);
|
||||
}
|
||||
|
||||
private RestCategoryModelsCollection linkContentToCategories(final RepoTestModel node, final RestCategoryModel... categories)
|
||||
{
|
||||
final List<RestCategoryLinkBodyModel> categoryLinkModels = Arrays.stream(categories)
|
||||
.map(RestCategoryModel::getId)
|
||||
.map(this::createCategoryLinkModelWithId)
|
||||
.collect(Collectors.toList());
|
||||
final RestCategoryModelsCollection linkedCategories = restClient.authenticateUser(user).withCoreAPI().usingNode(node).linkToCategories(categoryLinkModels);
|
||||
|
||||
restClient.assertStatusCodeIs(CREATED);
|
||||
|
||||
return linkedCategories;
|
||||
}
|
||||
|
||||
private RestCategoryModel prepareCategoryUnderRoot()
|
||||
{
|
||||
return prepareCategoryUnder(createCategoryModelWithId(ROOT_CATEGORY_ID));
|
||||
}
|
||||
|
||||
private RestCategoryModel prepareCategoryUnder(final RestCategoryModel parentCategory)
|
||||
{
|
||||
final RestCategoryModel categoryModel = createCategoryModelWithName(getRandomName(CATEGORY_NAME_PREFIX));
|
||||
final RestCategoryModel createdCategory = restClient.authenticateUser(dataUser.getAdminUser())
|
||||
.withCoreAPI()
|
||||
.usingCategory(parentCategory)
|
||||
.createSingleCategory(categoryModel);
|
||||
restClient.assertStatusCodeIs(CREATED);
|
||||
|
||||
return createdCategory;
|
||||
}
|
||||
|
||||
private RestCategoryModel createCategoryModelWithId(final String id)
|
||||
{
|
||||
return createCategoryModelWithIdAndName(id, null);
|
||||
}
|
||||
|
||||
private RestCategoryModel createCategoryModelWithName(final String name)
|
||||
{
|
||||
return createCategoryModelWithIdAndName(null, name);
|
||||
}
|
||||
|
||||
private RestCategoryModel createCategoryModelWithIdAndName(final String id, final String name)
|
||||
{
|
||||
return RestCategoryModel.builder()
|
||||
.id(id)
|
||||
.name(name)
|
||||
.create();
|
||||
}
|
||||
|
||||
private RestCategoryLinkBodyModel createCategoryLinkModelWithId(final String id)
|
||||
{
|
||||
return RestCategoryLinkBodyModel.builder()
|
||||
.categoryId(id)
|
||||
.create();
|
||||
}
|
||||
}
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.alfresco.utility.report.log.Step.STEP;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.dataprep.CMISUtil;
|
||||
import org.alfresco.rest.core.RestWrapper;
|
||||
import org.alfresco.rest.model.RestTagModel;
|
||||
import org.alfresco.rest.model.RestTagModelsCollection;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.Utility;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.data.RandomData;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
|
||||
/**
|
||||
* Tests to verify batch indexing of tags using Elasticsearch.
|
||||
*/
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
public class ElasticsearchGetTagsTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
@Autowired
|
||||
private DataSite dataSite;
|
||||
@Autowired
|
||||
private DataContent dataContent;
|
||||
@Autowired
|
||||
private RestWrapper restClient;
|
||||
|
||||
private UserModel user;
|
||||
private SiteModel site;
|
||||
private RestTagModel apple, banana, pineapple, winegrape, grapefruit, orange;
|
||||
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation() throws InterruptedException
|
||||
{
|
||||
serverHealth.isServerReachable();
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
STEP("Create user and site");
|
||||
user = dataUser.createRandomTestUser();
|
||||
site = dataSite.usingUser(user).createPublicRandomSite();
|
||||
final FileModel document = dataContent.usingAdmin().usingSite(site).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
|
||||
|
||||
STEP("Create few tags");
|
||||
apple = restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI()
|
||||
.createSingleTag(RestTagModel.builder().tag(RandomData.getRandomName("apple")).create());
|
||||
banana = restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI()
|
||||
.createSingleTag(RestTagModel.builder().tag(RandomData.getRandomName("banana")).create());
|
||||
pineapple = restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI()
|
||||
.createSingleTag(RestTagModel.builder().tag(RandomData.getRandomName("pineapple")).create());
|
||||
winegrape = restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI()
|
||||
.createSingleTag(RestTagModel.builder().tag(RandomData.getRandomName("winegrape")).create());
|
||||
grapefruit = restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI()
|
||||
.createSingleTag(RestTagModel.builder().tag(RandomData.getRandomName("grapefruit")).create());
|
||||
orange = restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI()
|
||||
.createSingleTag(RestTagModel.builder().tag(RandomData.getRandomName("orange")).create());
|
||||
|
||||
STEP("Wait for indexing to complete");
|
||||
Utility.sleep(500, 60000, () -> restClient.authenticateUser(dataUser.getAdminUser())
|
||||
.withParams("where=(tag MATCHES ('oran*'))")
|
||||
.withCoreAPI()
|
||||
.getTags()
|
||||
.assertThat()
|
||||
.entrySetContains("tag", orange.getTag().toLowerCase()));
|
||||
|
||||
// Step 2: now attach apple to document - tag node is guaranteed to be in ES
|
||||
STEP("Attach apple tag to document");
|
||||
restClient.authenticateUser(user).withCoreAPI().usingResource(document)
|
||||
.addTag(apple.getTag());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void dataCleanup()
|
||||
{
|
||||
STEP("Remove created tags");
|
||||
restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingTag(apple).deleteTag();
|
||||
restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingTag(banana).deleteTag();
|
||||
restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingTag(pineapple).deleteTag();
|
||||
restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingTag(winegrape).deleteTag();
|
||||
restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingTag(grapefruit).deleteTag();
|
||||
restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI().usingTag(orange).deleteTag();
|
||||
|
||||
STEP("Remove site and user");
|
||||
dataSite.usingUser(user).deleteSite(site);
|
||||
dataUser.deleteUser(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if exact name filter can be applied.
|
||||
*/
|
||||
@Test(groups = {TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
|
||||
public void testGetTags_withSingleNameFilter()
|
||||
{
|
||||
STEP("Get tags with names filter using EQUALS and expect one item in result");
|
||||
final RestTagModelsCollection returnedCollection = restClient.authenticateUser(user)
|
||||
.withParams("where=(tag='" + apple.getTag() + "')")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
returnedCollection.assertThat()
|
||||
.entrySetMatches("tag", Set.of(apple.getTag().toLowerCase()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if multiple names can be applied as a filter.
|
||||
*/
|
||||
@Test(groups = {TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
|
||||
public void testGetTags_withTwoNameFilters()
|
||||
{
|
||||
STEP("Get tags with names filter using IN and expect two items in result");
|
||||
final RestTagModelsCollection returnedCollection = restClient.authenticateUser(user)
|
||||
.withParams("where=(tag IN ('" + apple.getTag() + "', '" + banana.getTag() + "'))")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
returnedCollection.assertThat()
|
||||
.entrySetMatches("tag", Set.of(apple.getTag().toLowerCase(), banana.getTag().toLowerCase()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if alike name filter can be applied.
|
||||
*/
|
||||
@Test(groups = {TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
|
||||
public void testGetTags_whichNamesStartsWithOrphan()
|
||||
{
|
||||
STEP("Get tags with names filter using MATCHES and expect one item in result");
|
||||
final RestTagModelsCollection returnedCollection = restClient.authenticateUser(user)
|
||||
.withParams("where=(tag MATCHES ('*an*'))")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
returnedCollection.assertThat()
|
||||
.entrySetContains("tag", banana.getTag().toLowerCase(), orange.getTag().toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that tags can be filtered by exact name and alike name at the same time.
|
||||
*/
|
||||
@Test(groups = {TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
|
||||
public void testGetTags_withExactNameAndAlikeFilters()
|
||||
{
|
||||
STEP("Get tags with names filter using EQUALS and MATCHES and expect three items in result");
|
||||
final RestTagModelsCollection returnedCollection = restClient.authenticateUser(user)
|
||||
.withParams("where=(tag='" + orange.getTag() + "' OR tag MATCHES ('*grape*'))")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
returnedCollection.assertThat()
|
||||
.entrySetMatches("tag", Set.of(orange.getTag().toLowerCase(), grapefruit.getTag().toLowerCase(), winegrape.getTag().toLowerCase()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if multiple alike filters can be applied.
|
||||
*/
|
||||
@Test(groups = {TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
|
||||
public void testGetTags_withTwoAlikeFilters()
|
||||
{
|
||||
STEP("Get tags applying names filter using MATCHES twice and expect three items in result");
|
||||
final RestTagModelsCollection returnedCollection = restClient.authenticateUser(user)
|
||||
.withParams("where=(tag MATCHES ('*apple*') OR tag MATCHES ('grape*'))")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
returnedCollection.assertThat()
|
||||
.entrySetMatches("tag", Set.of(apple.getTag().toLowerCase(), pineapple.getTag().toLowerCase(), grapefruit.getTag().toLowerCase()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that providing incorrect field name in where query will result with 400 (Bad Request).
|
||||
*/
|
||||
@Test(groups = {TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
|
||||
public void testGetTags_withWrongWherePropertyNameAndExpect400()
|
||||
{
|
||||
STEP("Try to get tags with names filter using EQUALS and wrong property name and expect 400");
|
||||
restClient.authenticateUser(user)
|
||||
.withParams("where=(name=apple)")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
|
||||
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST)
|
||||
.assertLastError().containsSummary("Where query error: property with name: name is not expected");
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify tht AND operator is not supported in where query and expect 400 (Bad Request).
|
||||
*/
|
||||
@Test(groups = {TestGroup.REST_API, TestGroup.TAGS, TestGroup.REGRESSION})
|
||||
public void testGetTags_queryAndOperatorNotSupported()
|
||||
{
|
||||
STEP("Try to get tags applying names filter using AND operator and expect 400");
|
||||
restClient.authenticateUser(user)
|
||||
.withParams("where=(tag=apple AND tag IN ('banana', 'melon'))")
|
||||
.withCoreAPI()
|
||||
.getTags();
|
||||
|
||||
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST)
|
||||
.assertLastError().containsSummary("An invalid WHERE query was received. Unsupported Predicate");
|
||||
}
|
||||
}
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static java.util.function.Predicate.not;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static java.util.stream.Stream.concat;
|
||||
import static java.util.stream.Stream.of;
|
||||
|
||||
import static org.apache.http.entity.ContentType.APPLICATION_JSON;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.gson.Gson;
|
||||
import org.apache.http.HeaderElement;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.dataprep.AlfrescoHttpClient;
|
||||
import org.alfresco.dataprep.AlfrescoHttpClientFactory;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
public class ElasticsearchIsUnsetTest extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
private static final String CM_TITLED_ASPECT = "cm:titled";
|
||||
private static final String CM_TITLE_PROPERTY = "cm:title";
|
||||
private static final String CM_DESCRIPTION_PROPERTY = "cm:description";
|
||||
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
@Autowired
|
||||
SearchQueryService searchQueryService;
|
||||
|
||||
@Autowired
|
||||
private AlfrescoHttpClientFactory alfrescoHttpClientFactory;
|
||||
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
|
||||
private AlfrescoHttpClient alfrescoClient;
|
||||
private AlfNode testNode;
|
||||
|
||||
@BeforeClass
|
||||
public void setUp() throws IOException
|
||||
{
|
||||
alfrescoClient = alfrescoHttpClientFactory.getObject();
|
||||
testNode = createNodeWithTitleButWithoutDescription();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void tearDown()
|
||||
{
|
||||
alfrescoClient.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testElasticsearchIsUnsetQuery() throws Exception
|
||||
{
|
||||
assertNodeIsReadyForTesting();
|
||||
|
||||
// cm:title is set, we should have no result
|
||||
shouldNotFindNode("ISUNSET:\"" + CM_TITLE_PROPERTY + "\"");
|
||||
shouldFindNode("NOT ISUNSET:\"" + CM_TITLE_PROPERTY + "\"");
|
||||
|
||||
// cm:description is not set, we should have a result
|
||||
shouldFindNode("ISUNSET:\"" + CM_DESCRIPTION_PROPERTY + "\"");
|
||||
shouldNotFindNode("NOT ISUNSET:\"" + CM_DESCRIPTION_PROPERTY + "\"");
|
||||
|
||||
removeCmTitledAspect();
|
||||
|
||||
// ISUNSET is type aware, no cm:titled aspect so no result
|
||||
shouldNotFindNode("ISUNSET:\"" + CM_TITLE_PROPERTY + "\"");
|
||||
shouldNotFindNode("ISUNSET:\"" + CM_DESCRIPTION_PROPERTY + "\"");
|
||||
shouldFindNode("NOT ISUNSET:\"" + CM_TITLE_PROPERTY + "\"");
|
||||
shouldFindNode("NOT ISUNSET:\"" + CM_DESCRIPTION_PROPERTY + "\"");
|
||||
|
||||
restoreCmTitledAspect();
|
||||
|
||||
// cm:titled aspect is back but cm:title property has been "unset"
|
||||
shouldFindNode("ISUNSET:\"" + CM_TITLE_PROPERTY + "\"");
|
||||
shouldFindNode("ISUNSET:\"" + CM_DESCRIPTION_PROPERTY + "\"");
|
||||
shouldNotFindNode("NOT ISUNSET:\"" + CM_TITLE_PROPERTY + "\"");
|
||||
shouldNotFindNode("NOT ISUNSET:\"" + CM_DESCRIPTION_PROPERTY + "\"");
|
||||
}
|
||||
|
||||
private void restoreCmTitledAspect() throws IOException
|
||||
{
|
||||
setAspects(testNode.getAspects());
|
||||
}
|
||||
|
||||
private void removeCmTitledAspect() throws IOException
|
||||
{
|
||||
final List<String> noCmTitled = testNode.getAspects().stream().filter(not(CM_TITLED_ASPECT::equals)).toList();
|
||||
setAspects(noCmTitled);
|
||||
}
|
||||
|
||||
private void assertNodeIsReadyForTesting()
|
||||
{
|
||||
assertThat(testNode.getAspects()).contains(CM_TITLED_ASPECT);
|
||||
shouldFindNode();
|
||||
}
|
||||
|
||||
private void shouldFindNode(String... conjunctions)
|
||||
{
|
||||
searchQueryService.expectResultsInclude(searchByName(conjunctions), dataUser.getAdminUser(), testNode.getName());
|
||||
}
|
||||
|
||||
private void shouldNotFindNode(String... conjunctions)
|
||||
{
|
||||
searchQueryService.expectNoResultsFromQuery(searchByName(conjunctions), dataUser.getAdminUser());
|
||||
}
|
||||
|
||||
private SearchRequest searchByName(String... conjunctions)
|
||||
{
|
||||
return req(concat(of("cm:name:\"" + testNode.getName() + "\""), of(conjunctions)).collect(joining(" AND ")));
|
||||
}
|
||||
|
||||
private void setAspects(Collection<String> aspectsToSet) throws IOException
|
||||
{
|
||||
final URI nodeURI = URI.create(alfrescoClient.getApiVersionUrl()).resolve("nodes/" + testNode.getId());
|
||||
final HttpPut putRequest = new HttpPut(nodeURI);
|
||||
|
||||
final String jsonBody = toJsonString(Map.of("aspectNames", aspectsToSet));
|
||||
putRequest.setEntity(new StringEntity(jsonBody, APPLICATION_JSON));
|
||||
|
||||
nodeApiRequest(putRequest, 200);
|
||||
}
|
||||
|
||||
private AlfNode createNodeWithTitleButWithoutDescription() throws IOException
|
||||
{
|
||||
final URI parentURI = URI.create(alfrescoClient.getApiVersionUrl()).resolve("nodes/-my-/children");
|
||||
final HttpPost postRequest = new HttpPost(parentURI);
|
||||
final String jsonBody = toJsonString(
|
||||
Map.of("name", uniqueName(),
|
||||
"nodeType", "cm:content",
|
||||
"properties", Map.of("cm:title", "UnsetTestingDocument")));
|
||||
postRequest.setEntity(new StringEntity(jsonBody, APPLICATION_JSON));
|
||||
|
||||
return nodeApiRequest(postRequest, 201);
|
||||
}
|
||||
|
||||
private AlfNode nodeApiRequest(HttpRequestBase request, int expectedResponseCode) throws IOException
|
||||
{
|
||||
final HttpResponse response = alfrescoClient.executeAsAdmin(request);
|
||||
assertThat(response.getStatusLine().getStatusCode()).isEqualTo(expectedResponseCode);
|
||||
|
||||
final HttpEntity entity = response.getEntity();
|
||||
assertThat(entity).isNotNull();
|
||||
assertThat(entity.getContentType()).isNotNull();
|
||||
assertThat(entity.getContentType().getElements()).hasSize(1);
|
||||
|
||||
final HeaderElement contentTypeHeader = entity.getContentType().getElements()[0];
|
||||
assertThat(contentTypeHeader.getName()).isEqualTo(APPLICATION_JSON.getMimeType());
|
||||
|
||||
final Charset contentCharset = ofNullable(contentTypeHeader.getParameterByName("charset"))
|
||||
.map(NameValuePair::getValue)
|
||||
.map(Charset::forName)
|
||||
.orElse(StandardCharsets.UTF_8);
|
||||
|
||||
try (var reader = new InputStreamReader(entity.getContent(), contentCharset))
|
||||
{
|
||||
return asNode((Map<String, ?>) fromJsonString(CharStreams.toString(reader)).get("entry"));
|
||||
}
|
||||
}
|
||||
|
||||
private AlfNode asNode(Map<String, ?> map)
|
||||
{
|
||||
return new AlfNode(map);
|
||||
}
|
||||
|
||||
private String uniqueName()
|
||||
{
|
||||
return "Name-" + UUID.randomUUID();
|
||||
}
|
||||
|
||||
private String toJsonString(Map<String, ?> map)
|
||||
{
|
||||
return gson.toJson(map);
|
||||
}
|
||||
|
||||
private Map<String, ?> fromJsonString(String string)
|
||||
{
|
||||
return gson.fromJson(string, Map.class);
|
||||
}
|
||||
|
||||
private static class AlfNode
|
||||
{
|
||||
private final Map<String, ?> nodeMap;
|
||||
|
||||
private AlfNode(Map<String, ?> nodeMap)
|
||||
{
|
||||
this.nodeMap = requireNonNull(nodeMap);
|
||||
}
|
||||
|
||||
public String getId()
|
||||
{
|
||||
return requireNonNull((String) nodeMap.get("id"));
|
||||
}
|
||||
|
||||
public Set<String> getAspects()
|
||||
{
|
||||
return ofNullable(nodeMap.get("aspectNames"))
|
||||
.map(c -> (Collection<String>) c)
|
||||
.map(Set::copyOf)
|
||||
.orElseGet(Set::of);
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return (String) nodeMap.get("name");
|
||||
}
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
import static org.alfresco.tas.TestDataUtility.getAlphabeticUUID;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.rest.search.RestRequestLimitsModel;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.constants.UserRole;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FileType;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml", initializers = AlfrescoStackInitializer.class)
|
||||
/**
|
||||
* In this test we are verifying the Track Total Hits feature
|
||||
*/
|
||||
public class ElasticsearchLimitTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
private static final String PREFIX = getAlphabeticUUID() + "_";
|
||||
private static final int TOTAL_DOCUMENT_COUNT = 20;
|
||||
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
|
||||
@Autowired
|
||||
private DataContent dataContent;
|
||||
|
||||
@Autowired
|
||||
private DataSite dataSite;
|
||||
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
|
||||
@Autowired
|
||||
protected SearchQueryService searchQueryService;
|
||||
|
||||
private UserModel userSite1;
|
||||
private SiteModel siteModel1;
|
||||
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
userSite1 = dataUser.createRandomTestUser();
|
||||
|
||||
siteModel1 = dataSite.usingUser(userSite1).createPrivateRandomSite();
|
||||
|
||||
dataUser.addUserToSite(userSite1, siteModel1, UserRole.SiteContributor);
|
||||
|
||||
for (int i = 0; i < TOTAL_DOCUMENT_COUNT; i++)
|
||||
{
|
||||
createContent(PREFIX + i + ".txt", "Document " + i, siteModel1, userSite1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchUsingTotalHitsLimitDefaultValue()
|
||||
{
|
||||
SearchRequest request = req("cm:name:" + PREFIX + "*.txt");
|
||||
|
||||
// Without setting limits, we should get all up to 10k
|
||||
searchQueryService.expectTotalHitsFromQuery(request, userSite1, TOTAL_DOCUMENT_COUNT);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchUsingTotalHitsLimitZeroShouldDefault()
|
||||
{
|
||||
SearchRequest request = req("cm:name:" + PREFIX + "*.txt");
|
||||
|
||||
// Setting trackTotalHitsLimit to 0 should behave as not setting it, counting all up to 10k
|
||||
request.setLimits(new RestRequestLimitsModel(null, null, 0));
|
||||
searchQueryService.expectTotalHitsFromQuery(request, userSite1, TOTAL_DOCUMENT_COUNT);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchUsingTotalHitsLimit()
|
||||
{
|
||||
SearchRequest request = req("cm:name:" + PREFIX + "*.txt");
|
||||
|
||||
// Setting trackTotalHitsLimit to 10 should only count up to 10
|
||||
request.setLimits(new RestRequestLimitsModel(null, null, 10));
|
||||
searchQueryService.expectTotalHitsFromQuery(request, userSite1, 10);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchUsingTotalHitsUnlimited()
|
||||
{
|
||||
SearchRequest request = req("cm:name:" + PREFIX + "*.txt");
|
||||
|
||||
// Setting trackTotalHitsLimit to -1 should only count up to max int
|
||||
request.setLimits(new RestRequestLimitsModel(null, null, -1));
|
||||
searchQueryService.expectTotalHitsFromQuery(request, userSite1, TOTAL_DOCUMENT_COUNT);
|
||||
}
|
||||
|
||||
private FileModel createContent(String filename, String content, SiteModel site, UserModel user)
|
||||
{
|
||||
FileModel fileModel = new FileModel(filename, FileType.TEXT_PLAIN, content);
|
||||
return dataContent.usingUser(user).usingSite(site).createContent(fileModel);
|
||||
}
|
||||
}
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
import static org.alfresco.tas.TestDataUtility.getAlphabeticUUID;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.dataprep.AlfrescoHttpClient;
|
||||
import org.alfresco.dataprep.AlfrescoHttpClientFactory;
|
||||
import org.alfresco.rest.search.RestRequestQueryModel;
|
||||
import org.alfresco.rest.search.SearchNodeModel;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.constants.UserRole;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FileType;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
/**
|
||||
* In this test we are verifying end-to-end the indexing and search in Elasticsearch. In order to test ACLs we created 2 sites and 3 users.
|
||||
*/
|
||||
public class ElasticsearchLiveIndexingTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
private static final String PREFIX = getAlphabeticUUID() + "_";
|
||||
private static final String UNIQUE_WORD = getAlphabeticUUID();
|
||||
private static final String FILE_0_NAME = PREFIX + "test.txt";
|
||||
private static final String FILE_1_NAME = PREFIX + "another.txt";
|
||||
private static final String FILE_2_NAME = PREFIX + "user1.txt";
|
||||
private static final String FILE_3_NAME = PREFIX + "user1Old.txt";
|
||||
public static final String BEFORE_1970_TXT = "before1970.txt";
|
||||
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
|
||||
@Autowired
|
||||
private DataContent dataContent;
|
||||
|
||||
@Autowired
|
||||
private DataSite dataSite;
|
||||
|
||||
@Autowired
|
||||
private AlfrescoHttpClientFactory alfrescoHttpClientFactory;
|
||||
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
|
||||
@Autowired
|
||||
protected SearchQueryService searchQueryService;
|
||||
|
||||
private UserModel userSite1;
|
||||
private UserModel userSite2;
|
||||
private UserModel userMultiSite;
|
||||
private SiteModel siteModel1;
|
||||
private SiteModel siteModel2;
|
||||
|
||||
/**
|
||||
* Data will be prepared using the schema below:
|
||||
* <p>
|
||||
* Site1: - Users: userSite1, userMultiSite - Documents: FILE_0_NAME (owner: userSite1), FILE_1_NAME (owner: userSite1), FILE_3_NAME (owner: userSite2)
|
||||
* <p>
|
||||
* Site2: - Users: userSite2, userMultiSite - Documents: FILE_2_NAME (owner: userSite2)
|
||||
*/
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
userSite1 = dataUser.createRandomTestUser();
|
||||
userSite2 = dataUser.createRandomTestUser();
|
||||
userMultiSite = dataUser.createRandomTestUser();
|
||||
|
||||
siteModel1 = dataSite.usingUser(userSite1).createPrivateRandomSite();
|
||||
siteModel2 = dataSite.usingUser(userSite2).createPrivateRandomSite();
|
||||
|
||||
dataUser.addUserToSite(userSite2, siteModel1, UserRole.SiteContributor);
|
||||
dataUser.addUserToSite(userMultiSite, siteModel1, UserRole.SiteContributor);
|
||||
dataUser.addUserToSite(userMultiSite, siteModel2, UserRole.SiteContributor);
|
||||
|
||||
createContent(FILE_0_NAME, "This is the first test containing the unique word " + UNIQUE_WORD, siteModel1, userSite1);
|
||||
createContent(FILE_1_NAME, "This is another TEST file", siteModel1, userSite1);
|
||||
createContent(FILE_2_NAME, "This is another test file", siteModel2, userSite2);
|
||||
createContent(FILE_3_NAME, "This is another Test file", siteModel1, userSite2);
|
||||
// remove the user from site, but he keeps ownership on FILE_3_NAME
|
||||
dataUser.removeUserFromSite(userSite2, siteModel1);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchCanFindAFileUsingIncludeParameter()
|
||||
{
|
||||
SearchRequest queryWithoutIncludes = req(UNIQUE_WORD);
|
||||
Predicate<SearchNodeModel> allFieldsNull = searchNodeModel -> searchNodeModel.getProperties() == null
|
||||
&& searchNodeModel.getPath() == null
|
||||
&& searchNodeModel.getAspectNames() == null
|
||||
&& searchNodeModel.getAllowableOperations() == null
|
||||
&& searchNodeModel.getPermissions() == null
|
||||
&& searchNodeModel.getAssociation() == null
|
||||
&& searchNodeModel.isLocked() == null
|
||||
&& searchNodeModel.isLink() == null;
|
||||
searchQueryService.expectAllResultsFromQuery(queryWithoutIncludes, userSite1, allFieldsNull);
|
||||
|
||||
SearchRequest queryWithIncludes = new SearchRequest();
|
||||
// A full list of all fields that can be included is declared in constant:
|
||||
// org.alfresco.rest.api.search.impl.SearchMapper.PERMITTED_INCLUDES
|
||||
queryWithIncludes.setInclude(asList("properties", "path", "aspectNames", "isLocked", "allowableOperations",
|
||||
"permissions", "isLink", "association"));
|
||||
RestRequestQueryModel queryReq = new RestRequestQueryModel();
|
||||
queryReq.setQuery(UNIQUE_WORD);
|
||||
queryWithIncludes.setQuery(queryReq);
|
||||
Predicate<SearchNodeModel> noFieldsNull = searchNodeModel -> searchNodeModel.getProperties() != null
|
||||
&& searchNodeModel.getPath() != null
|
||||
&& searchNodeModel.getAspectNames() != null
|
||||
&& searchNodeModel.getAllowableOperations() != null
|
||||
&& searchNodeModel.getPermissions() != null
|
||||
&& searchNodeModel.getAssociation() != null
|
||||
&& searchNodeModel.isLocked() != null
|
||||
&& searchNodeModel.isLink() != null;
|
||||
searchQueryService.expectAllResultsFromQuery(queryWithIncludes, userSite1, noFieldsNull);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchCanFindAFile()
|
||||
{
|
||||
// this test must found only one documents, while documents in the system are four because
|
||||
// only one contains the unique word.
|
||||
searchQueryService.expectResultsFromQuery(req(UNIQUE_WORD), userSite1, FILE_0_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchCanFindFilesOnASite()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req(PREFIX), userSite1, FILE_0_NAME, FILE_1_NAME, FILE_3_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchCanFindAFileOnMultipleSitesWithOwner()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req(PREFIX), userSite2, FILE_3_NAME, FILE_2_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchCanFindAFileOnMultipleSites()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req(PREFIX), userMultiSite, FILE_0_NAME, FILE_1_NAME, FILE_3_NAME, FILE_2_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void wildcardWorksInsideQuotes()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("cm:name:\"" + PREFIX + "user1*\""), userMultiSite, FILE_2_NAME, FILE_3_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void wildcardWorksWithoutQuotes()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("cm:name:" + PREFIX + "user1*"), userMultiSite, FILE_2_NAME, FILE_3_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH, enabled = false) // Test should be re-enabled within: ACS-6068
|
||||
public void wildcardNodeRefQuery()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("ANCESTOR:\"" + siteModel2.getGuid().substring(0, 10) + "*\""), userMultiSite, "documentLibrary", FILE_2_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void findFileWithRangeQuery()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("cm:created:[NOW-1YEAR TO MAX] AND name:" + FILE_0_NAME), userSite1, FILE_0_NAME);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void omitFileWithRangeQuery()
|
||||
{
|
||||
searchQueryService.expectNoResultsFromQuery(req("cm:created:[MIN TO NOW-2YEARS] AND name:" + FILE_0_NAME), userSite1);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void indexAndSearchForDateBefore1970()
|
||||
{
|
||||
// Elasticsearch doesn't accept numbers for dates before 1970, so we create and search for a specific document in order to verify that.
|
||||
createNodeWithProperties(siteModel1, new FileModel(BEFORE_1970_TXT, FileType.TEXT_PLAIN), userSite1,
|
||||
Map.of("cm:from", -2637887000L));
|
||||
|
||||
searchQueryService.expectResultsInclude(req("cm:from:1969-12-01T11:15:13Z"), userSite1, BEFORE_1970_TXT);
|
||||
}
|
||||
|
||||
private FileModel createContent(String filename, String content, SiteModel site, UserModel user)
|
||||
{
|
||||
FileModel fileModel = new FileModel(filename, FileType.TEXT_PLAIN, content);
|
||||
return dataContent.usingUser(user).usingSite(site)
|
||||
.createContent(fileModel);
|
||||
}
|
||||
|
||||
private void createNodeWithProperties(SiteModel parentSite, FileModel fileModel, UserModel currentUser, Map<String, Object> properties)
|
||||
{
|
||||
AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject();
|
||||
String reqUrl = client.getApiVersionUrl() + "nodes/" + parentSite.getGuid() + "/children";
|
||||
String name = fileModel.getName();
|
||||
|
||||
HttpPost post = new HttpPost(reqUrl);
|
||||
JSONObject body = new JSONObject();
|
||||
body.put("name", name);
|
||||
body.put("nodeType", "cm:content");
|
||||
|
||||
JSONObject jsonProperties = new JSONObject();
|
||||
jsonProperties.putAll(properties);
|
||||
body.put("properties", jsonProperties);
|
||||
|
||||
post.setEntity(client.setMessageBody(body));
|
||||
|
||||
// Send Request
|
||||
logger.info(String.format("POST: '%s'", reqUrl));
|
||||
HttpResponse response = client.execute(currentUser.getUsername(), currentUser.getPassword(), post);
|
||||
if (org.apache.http.HttpStatus.SC_CREATED != response.getStatusLine().getStatusCode())
|
||||
{
|
||||
throw new RuntimeException("Could not create file. Request response: " + client.getParameterFromJSON(response, "briefSummary", "error"));
|
||||
}
|
||||
}
|
||||
}
|
||||
+301
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.ContentModel;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
import org.alfresco.utility.report.log.Step;
|
||||
|
||||
/**
|
||||
* Tests to verify indexing of paths using Elasticsearch.
|
||||
*/
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert", "PMD.JUnit4TestShouldUseTestAnnotation"}) // these are testng tests and use searchQueryService.expectResultsFromQuery for assertion
|
||||
public class ElasticsearchPathIndexingTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
@Autowired
|
||||
private DataSite dataSite;
|
||||
@Autowired
|
||||
private DataContent dataContent;
|
||||
@Autowired
|
||||
protected SearchQueryService searchQueryService;
|
||||
|
||||
private org.alfresco.utility.model.UserModel testUser;
|
||||
|
||||
private org.alfresco.utility.model.SiteModel testSite;
|
||||
|
||||
private List<FolderModel> testFolders;
|
||||
|
||||
private String testFileName;
|
||||
private String filenameWhichIncludesWhitespace = "TestFile " + UUID.randomUUID() + ".txt";
|
||||
private String testFileNameWithWhitespace;
|
||||
|
||||
/**
|
||||
* Create a user and a private site containing some nested folders with a document in.
|
||||
*/
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
serverHealth.isServerReachable();
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
Step.STEP("Create a test user and private site containing three nested folders and a document.");
|
||||
testUser = dataUser.createRandomTestUser();
|
||||
testSite = dataSite.usingUser(testUser).createPrivateRandomSite();
|
||||
|
||||
testFolders = createNestedFolders(3);
|
||||
|
||||
testFileName = createDocument(testFolders.get(testFolders.size() - 1));
|
||||
|
||||
testFileNameWithWhitespace = createDocument(testFolders.get(testFolders.size() - 1), filenameWhichIncludesWhitespace);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSimple()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"//cm:" + testFileName + "\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFileName);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testRelativePathQuery()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"//cm:" + testFileName + "\" ");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFileName);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testRelativePathQueryWithoutPrefixes()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"//" + testFileName + "\" AND name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFileName);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testWildcardQuery()
|
||||
{
|
||||
// The test file should be the only descendent of the last folder.
|
||||
SearchRequest query = req("PATH:\"//" + testSite.getId() + "//" + testFolders.get(testFolders.size() - 1).getName() + "/*\" AND name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFileName, testFileNameWithWhitespace);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testWildcardQueryWithNamespaces()
|
||||
{
|
||||
// The test file should be the only descendent of the last folder.
|
||||
SearchRequest query = req("PATH:\"//cm:" + testSite.getId() + "//cm:" + testFolders.get(testFolders.size() - 1).getName() + "/*\" AND name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFileName, testFileNameWithWhitespace);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testAbsolutePathQuery()
|
||||
{
|
||||
String folderPath = testFolders.stream().map(folder -> "cm:" + folder.getName()).collect(Collectors.joining("/"));
|
||||
SearchRequest query = req("PATH:\"/app:company_home/st:sites/cm:" + testSite.getId() + "/cm:documentLibrary/" + folderPath + "/cm:" + testFileName + "\" AND cm:name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFileName);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testAbsolutePathQueryWithoutPrefixes()
|
||||
{
|
||||
String folderPath = testFolders.stream().map(folder -> folder.getName()).collect(Collectors.joining("/"));
|
||||
SearchRequest query = req("PATH:\"/company_home/sites/" + testSite.getId() + "/documentLibrary/" + folderPath + "/" + testFileName + "\" AND name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFileName);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testRootNodes()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"/*\" AND name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, "categories", "Company Home");
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testRootNodesWithoutWildcard()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"/\"");
|
||||
searchQueryService.expectNodeTypesFromQuery(query, testUser, "sys:store_root");
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testPathNameMismatch()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"/*\" AND name:" + testFileName + " AND name:*");
|
||||
searchQueryService.expectNoResultsFromQuery(query, testUser);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testPathNameIntersect()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"//*\" AND name:" + testFileName + " AND name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFileName);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testAllDescendentsOfFolder()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"//" + testFolders.get(0).getName() + "//*\" AND name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFileName, testFileNameWithWhitespace, testFolders.get(1).getName(), testFolders.get(2).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSearchAllDescendentsOfFolderQueryWithNamespaces()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"//cm:" + testFolders.get(0).getName() + "//*\" AND name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFileName, testFileNameWithWhitespace, testFolders.get(1).getName(), testFolders.get(2).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testWhereFolderIsAncestor()
|
||||
{
|
||||
SearchRequest query = req("ANCESTOR:\"" + testFolders.get(2).getNodeRef() + "\" AND name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFileName, testFileNameWithWhitespace);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testAncestorWithWorkspaceReference()
|
||||
{
|
||||
SearchRequest query = req("ANCESTOR:\"workspace://SpacesStore/" + testFolders.get(0).getNodeRef() + "\" AND name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFileName, testFileNameWithWhitespace, testFolders.get(1).getName(), testFolders.get(2).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testPrimaryParent()
|
||||
{
|
||||
SearchRequest query = req("PRIMARYPARENT:\"" + testFolders.get(1).getNodeRef() + "\" AND name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFolders.get(2).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testParent()
|
||||
{
|
||||
SearchRequest query = req("PARENT:\"" + testFolders.get(1).getNodeRef() + "\" AND name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFolders.get(2).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testAllFoldersInSite()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"/*/sites/" + testSite.getId() + "/*//*\" AND TYPE:\"cm:folder\" AND name:*");
|
||||
String[] folderNames = testFolders.stream().map(ContentModel::getName).toArray(String[]::new);
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, folderNames);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSearchAllFoldersInSiteQueryWithNamespaces()
|
||||
{
|
||||
SearchRequest query = req("PATH:\"/*/st:sites/cm:" + testSite.getId() + "/*//*\" AND TYPE:\"cm:folder\" AND name:*");
|
||||
String[] folderNames = testFolders.stream().map(ContentModel::getName).toArray(String[]::new);
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, folderNames);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH, enabled = false)
|
||||
public void testUpdatePath()
|
||||
{
|
||||
// disabled test: find a way to rename or move a folder on repository
|
||||
String folderPath = testFolders.stream().map(folder -> "cm:" + folder.getName()).collect(Collectors.joining("/"));
|
||||
SearchRequest query = req("PATH:\"/app:company_home/cm:" + testFileName + "\" AND cm:name:*");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a set of nested folders in the test site using the test user.
|
||||
*
|
||||
* @return The folder objects (containing the randomly generated names) in order of depth.
|
||||
*/
|
||||
private List<FolderModel> createNestedFolders(int maxDepth)
|
||||
{
|
||||
List<FolderModel> folders = new ArrayList<>();
|
||||
dataContent.usingSite(testSite);
|
||||
for (int depth = 0; depth < maxDepth; depth++)
|
||||
{
|
||||
String folderName = "TestFolder" + depth + "_" + UUID.randomUUID();
|
||||
FolderModel folderModel = new FolderModel(folderName);
|
||||
folders.add(folderModel);
|
||||
if (depth != 0)
|
||||
{
|
||||
dataContent.usingResource(folders.get(depth - 1));
|
||||
}
|
||||
dataContent.usingUser(testUser)
|
||||
.createFolder(folderModel);
|
||||
}
|
||||
return folders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a document in the given folder using the test user and a random filename.
|
||||
*
|
||||
* @param folderModel
|
||||
* The location to create the document.
|
||||
* @return The randomly generated name of the new document.
|
||||
*/
|
||||
private String createDocument(FolderModel folderModel)
|
||||
{
|
||||
return createDocument(folderModel, "TestFile" + UUID.randomUUID() + ".txt");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a document in the given folder using the test user and the given filename.
|
||||
*
|
||||
* @param folderModel
|
||||
* The location to create the document.
|
||||
* @param filename
|
||||
* the filename.
|
||||
* @return the passed filename.
|
||||
*/
|
||||
private String createDocument(FolderModel folderModel, String filename)
|
||||
{
|
||||
dataContent.usingUser(testUser)
|
||||
.usingResource(folderModel)
|
||||
.createContent(new org.alfresco.utility.model.FileModel(filename, org.alfresco.utility.model.FileType.TEXT_PLAIN, "content"));
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
import static org.alfresco.utility.report.log.Step.STEP;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.rest.search.ResponseHighlightModel;
|
||||
import org.alfresco.rest.search.RestRequestFieldsModel;
|
||||
import org.alfresco.rest.search.RestRequestHighlightModel;
|
||||
import org.alfresco.rest.search.SearchNodeModel;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FileType;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
// These are TestNG tests and the assertions are hidden in searchQueryService.
|
||||
@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert"})
|
||||
public class ElasticsearchPlainHighlightingTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
static final Logger LOGGER = LoggerFactory.getLogger(ElasticsearchPlainHighlightingTests.class);
|
||||
static final String FILE_A = "fileA.txt";
|
||||
static final String FILE_B = "fileB.txt";
|
||||
static final String FILE_C = "fileC.txt";
|
||||
static final String CONTENT_A = "The quick brown fox jumps over the lazy dog.";
|
||||
static final String CONTENT_B = """
|
||||
The lazy dog sleeps under the quick brown fox. The middle of the document is quite long:
|
||||
Lorem ipsum dolor sit amet. In veniam tempore hic provident sunt et distinctio velit et reprehenderit
|
||||
officiis id sapiente omnis et quisquam aliquam et porro perferendis. In sequi placeat ut quaerat voluptatem
|
||||
ea consequuntur impedit aut eaque enim aut atque rerum.
|
||||
The end of the document mentions the dog again!
|
||||
""";
|
||||
static final String CONTENT_C = """
|
||||
The rabbit made a ring in the center of the field. He said to the wolf, "Now, you dance around this ring, and sing just as I do."
|
||||
Rabbit made a larger ring for himself and danced around just beyond the wolf.
|
||||
The wolf thought that this was the finest dance he had ever seen.
|
||||
He and the rabbit danced faster and faster, and sang louder and louder.
|
||||
As the rabbit danced, he moved nearer and nearer to the edge of the field.
|
||||
The wolf was dancing so fast and singing so loud that he did not notice this.
|
||||
At last, Brother Rabbit reached the edge of the field; then he jumped into the blackberry bushes and ran away.
|
||||
The wolf tried to give chase, but he was so dizzy that he could not run. And the rabbit got away without having his ears cut off.
|
||||
""";
|
||||
|
||||
@Autowired
|
||||
ServerHealth serverHealth;
|
||||
@Autowired
|
||||
DataUser dataUser;
|
||||
@Autowired
|
||||
DataContent dataContent;
|
||||
@Autowired
|
||||
DataSite dataSite;
|
||||
@Autowired
|
||||
SearchQueryService searchQueryService;
|
||||
|
||||
UserModel user;
|
||||
SiteModel site;
|
||||
FileModel fileA;
|
||||
FileModel fileB;
|
||||
FileModel fileC;
|
||||
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
STEP("Create a test user and a private site.");
|
||||
user = dataUser.createRandomTestUser();
|
||||
site = dataSite.usingUser(user).createPrivateRandomSite();
|
||||
|
||||
STEP("Create two test files with highlightable content");
|
||||
FileModel fileModelA = new FileModel(FILE_A, FileType.TEXT_PLAIN, CONTENT_A);
|
||||
fileA = dataContent.usingUser(user).usingSite(site).createContent(fileModelA);
|
||||
FileModel fileModelB = new FileModel(FILE_B, FileType.TEXT_PLAIN, CONTENT_B);
|
||||
fileB = dataContent.usingUser(user).usingSite(site).createContent(fileModelB);
|
||||
FileModel fileModelC = new FileModel(FILE_C, FileType.TEXT_PLAIN, CONTENT_C);
|
||||
fileC = dataContent.usingUser(user).usingSite(site).createContent(fileModelC);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void dataCleanup()
|
||||
{
|
||||
STEP("Remove test site and user");
|
||||
dataSite.usingAdmin().deleteSite(site);
|
||||
dataUser.usingAdmin().deleteUser(user);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsInContent()
|
||||
{
|
||||
STEP("Search for files mentioning 'dog'");
|
||||
String query = "cm:content:dog AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder().fields(List.of("cm:content")).build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_A, Map.of("cm:content", List.of("The quick brown fox jumps over the lazy <em>dog</em>.")),
|
||||
FILE_B, Map.of("cm:content", List.of("The lazy <em>dog</em> sleeps under the quick brown fox. The middle of the document is quite long:\nLorem",
|
||||
"\nea consequuntur impedit aut eaque enim aut atque rerum.\nThe end of the document mentions the <em>dog</em> again!\n"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsInTwoFields()
|
||||
{
|
||||
STEP("Search for files with 'file' in the name that mention 'middle'");
|
||||
String query = "cm:content:middle AND cm:name:file AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder().fields(List.of("cm:name", "cm:content")).build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_B, Map.of("cm:name", List.of("<em>file</em>B.txt"),
|
||||
"cm:content", List.of("The lazy dog sleeps under the quick brown fox. The <em>middle</em> of the document is quite long:\nLorem"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsWithCustomPrefixAndPostfix()
|
||||
{
|
||||
STEP("Search for files mentioning 'dog' with custom prefix and postfix");
|
||||
String query = "cm:content:dog AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.prefix("<b>")
|
||||
.postfix("</b>")
|
||||
.fields(List.of("cm:content"))
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_A, Map.of("cm:content", List.of("The quick brown fox jumps over the lazy <b>dog</b>.")),
|
||||
FILE_B, Map.of("cm:content", List.of("The lazy <b>dog</b> sleeps under the quick brown fox. The middle of the document is quite long:\nLorem",
|
||||
"\nea consequuntur impedit aut eaque enim aut atque rerum.\nThe end of the document mentions the <b>dog</b> again!\n"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsWithBlankPrefixAndPostfix()
|
||||
{
|
||||
STEP("Search for files mentioning 'middle' with blank mark as custom prefix and postfix");
|
||||
String query = "cm:content:middle AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.prefix(" ")
|
||||
.postfix(" ")
|
||||
.fields(List.of("cm:content"))
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_B, Map.of("cm:content", List.of("The lazy dog sleeps under the quick brown fox. The middle of the document is quite long:\nLorem"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsWithGeneralAndFieldSpecificPrefixesAndPostfixes()
|
||||
{
|
||||
STEP("Search for files with 'file' in the name that mention 'dog' with general and field specific prefixes or postfixes");
|
||||
String query = "cm:content:dog AND cm:name:file AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.prefix("¿")
|
||||
.postfix("?")
|
||||
.fields(
|
||||
RestRequestFieldsModel.of("cm:name"),
|
||||
RestRequestFieldsModel.of("cm:content", "(", ")"))
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_A, Map.of("cm:name", List.of("¿file?A.txt"), "cm:content", List.of("The quick brown fox jumps over the lazy (dog).")),
|
||||
FILE_B, Map.of("cm:name", List.of("¿file?B.txt"), "cm:content", List.of("The lazy (dog) sleeps under the quick brown fox. The middle of the document is quite long:\nLorem",
|
||||
"\nea consequuntur impedit aut eaque enim aut atque rerum.\nThe end of the document mentions the (dog) again!\n"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsWithOneFieldSpecificPrefixAndPostfix()
|
||||
{
|
||||
STEP("Search for files with 'file' in the name that mention 'dog' with one field specific prefix or postfix");
|
||||
String query = "cm:content:dog AND cm:name:file AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.fields(
|
||||
RestRequestFieldsModel.of("cm:name"),
|
||||
RestRequestFieldsModel.of("cm:content", "(", ")"))
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_A, Map.of("cm:name", List.of("<em>file</em>A.txt"), "cm:content", List.of("The quick brown fox jumps over the lazy (dog).")),
|
||||
FILE_B, Map.of("cm:name", List.of("<em>file</em>B.txt"), "cm:content", List.of("The lazy (dog) sleeps under the quick brown fox. The middle of the document is quite long:\nLorem",
|
||||
"\nea consequuntur impedit aut eaque enim aut atque rerum.\nThe end of the document mentions the (dog) again!\n"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsInTwoFieldsWithPrefixAndWithoutPostfix()
|
||||
{
|
||||
STEP("Search for files with 'file' in the name that mention 'middle' with custom prefix");
|
||||
String query = "cm:content:middle AND cm:name:file AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.fields(List.of("cm:name", "cm:content"))
|
||||
.prefix("(")
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_B, Map.of("cm:name", List.of("(file</em>B.txt"),
|
||||
"cm:content", List.of("The lazy dog sleeps under the quick brown fox. The (middle</em> of the document is quite long:\nLorem"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsInTwoFieldsWithoutPrefixAndWithPostfix()
|
||||
{
|
||||
STEP("Search for files with 'file' in the name that mention 'middle' with custom postfix");
|
||||
String query = "cm:content:middle AND cm:name:file AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.fields(List.of("cm:name", "cm:content"))
|
||||
.postfix(")")
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_B, Map.of("cm:name", List.of("<em>file)B.txt"),
|
||||
"cm:content", List.of("The lazy dog sleeps under the quick brown fox. The <em>middle) of the document is quite long:\nLorem"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsInContentWithGeneralSnippetCount()
|
||||
{
|
||||
STEP("Search for files mentioning 'rabbit' and expect 2 snippets in result");
|
||||
String query = "cm:content:rabbit AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.fields(RestRequestFieldsModel.of("cm:content"))
|
||||
.snippetCount(2)
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_C, Map.of("cm:content", List.of("The <em>rabbit</em> made a ring in the center of the field. He said to the wolf, \"Now, you dance around",
|
||||
" this ring, and sing just as I do.\"\n<em>Rabbit</em> made a larger ring for himself and danced around just beyond"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsInContentWithFieldSpecificSnippetCount()
|
||||
{
|
||||
STEP("Search for files mentioning 'rabbit' and expect 3 snippets in result");
|
||||
String query = "cm:content:rabbit AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.fields(RestRequestFieldsModel.builder().field("cm:content").snippetCount(3).build())
|
||||
.snippetCount(1)
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_C, Map.of("cm:content", List.of("The <em>rabbit</em> made a ring in the center of the field. He said to the wolf, \"Now, you dance around",
|
||||
" this ring, and sing just as I do.\"\n<em>Rabbit</em> made a larger ring for himself and danced around just beyond",
|
||||
" the wolf.\nThe wolf thought that this was the finest dance he had ever seen.\nHe and the <em>rabbit</em> danced"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsInContentWithZeroSnippetCount()
|
||||
{
|
||||
STEP("Search for files mentioning 'rabbit' and expect default 5 snippets in result");
|
||||
String query = "cm:content:rabbit AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.fields(RestRequestFieldsModel.of("cm:content"))
|
||||
.snippetCount(0)
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_C, Map.of("cm:content", List.of("The <em>rabbit</em> made a ring in the center of the field. He said to the wolf, \"Now, you dance around",
|
||||
" this ring, and sing just as I do.\"\n<em>Rabbit</em> made a larger ring for himself and danced around just beyond",
|
||||
" the wolf.\nThe wolf thought that this was the finest dance he had ever seen.\nHe and the <em>rabbit</em> danced",
|
||||
" faster and faster, and sang louder and louder.\nAs the <em>rabbit</em> danced, he moved nearer and nearer",
|
||||
" this.\nAt last, Brother <em>Rabbit</em> reached the edge of the field; then he jumped into the blackberry bushes"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsInContentWithNegativeSnippetCount()
|
||||
{
|
||||
STEP("Search for files mentioning 'rabbit' and expect default 5 snippets in result");
|
||||
String query = "cm:content:rabbit AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.fields(RestRequestFieldsModel.of("cm:content"))
|
||||
.snippetCount(-1)
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_C, Map.of("cm:content", List.of("The <em>rabbit</em> made a ring in the center of the field. He said to the wolf, \"Now, you dance around",
|
||||
" this ring, and sing just as I do.\"\n<em>Rabbit</em> made a larger ring for himself and danced around just beyond",
|
||||
" the wolf.\nThe wolf thought that this was the finest dance he had ever seen.\nHe and the <em>rabbit</em> danced",
|
||||
" faster and faster, and sang louder and louder.\nAs the <em>rabbit</em> danced, he moved nearer and nearer",
|
||||
" this.\nAt last, Brother <em>Rabbit</em> reached the edge of the field; then he jumped into the blackberry bushes"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsInContentWithGeneralFragmentSize()
|
||||
{
|
||||
STEP("Search for files mentioning 'dog' and expect snippets about 10 characters long");
|
||||
String query = "cm:content:dog AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.fields(RestRequestFieldsModel.of("cm:content"))
|
||||
.fragmentSize(10)
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_A, Map.of("cm:content", List.of(" over the lazy <em>dog</em>.")),
|
||||
FILE_B, Map.of("cm:content", List.of(" <em>dog</em> sleeps", " the <em>dog</em> again!\n"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsInContentWithFieldSpecificFragmentSize()
|
||||
{
|
||||
STEP("Search for files mentioning 'dog' and expect snippets about 15 characters long");
|
||||
String query = "cm:content:dog AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.fields(RestRequestFieldsModel.builder().field("cm:content").fragmentSize(15).build())
|
||||
.fragmentSize(10)
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_A, Map.of("cm:content", List.of(" over the lazy <em>dog</em>.")),
|
||||
FILE_B, Map.of("cm:content", List.of("The lazy <em>dog</em>", " the <em>dog</em> again!\n"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsInContentWithZeroFragmentSize()
|
||||
{
|
||||
STEP("Search for files mentioning 'dog' and expect snippets having default characters length");
|
||||
String query = "cm:content:dog AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.fields(RestRequestFieldsModel.of("cm:content"))
|
||||
.fragmentSize(0)
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_A, Map.of("cm:content", List.of("The quick brown fox jumps over the lazy <em>dog</em>.")),
|
||||
FILE_B, Map.of("cm:content", List.of("The lazy <em>dog</em> sleeps under the quick brown fox. The middle of the document is quite long:\nLorem",
|
||||
"\nea consequuntur impedit aut eaque enim aut atque rerum.\nThe end of the document mentions the <em>dog</em> again!\n"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testHighlightsInContentWithNegativeFragmentSize()
|
||||
{
|
||||
STEP("Search for files mentioning 'dog' and expect snippets having default characters length");
|
||||
String query = "cm:content:dog AND SITE:" + site.getId();
|
||||
SearchRequest searchRequest = req("afts", query);
|
||||
RestRequestHighlightModel highlightModel = RestRequestHighlightModel.builder()
|
||||
.fields(RestRequestFieldsModel.of("cm:content"))
|
||||
.fragmentSize(-1)
|
||||
.build();
|
||||
searchRequest.setHighlight(highlightModel);
|
||||
// Configure the expected highlights for each document.
|
||||
Predicate<SearchNodeModel> assertionMethod = highlightAssert(
|
||||
Map.of(FILE_A, Map.of("cm:content", List.of("The quick brown fox jumps over the lazy <em>dog</em>.")),
|
||||
FILE_B, Map.of("cm:content", List.of("The lazy <em>dog</em> sleeps under the quick brown fox. The middle of the document is quite long:\nLorem",
|
||||
"\nea consequuntur impedit aut eaque enim aut atque rerum.\nThe end of the document mentions the <em>dog</em> again!\n"))));
|
||||
searchQueryService.expectAllResultsFromQuery(searchRequest, user, assertionMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a predicate that returns true if the highlights for a received document match the given expectation.
|
||||
*
|
||||
* @param allExpectedHighlights
|
||||
* The expected highlights for all documents keyed by document name and then field name.
|
||||
* @return The predicate.
|
||||
*/
|
||||
private Predicate<SearchNodeModel> highlightAssert(Map<String, Map<String, List<String>>> allExpectedHighlights)
|
||||
{
|
||||
return document -> {
|
||||
if (!allExpectedHighlights.containsKey(document.getName()))
|
||||
{
|
||||
LOGGER.error("Unexpected entry in results: {}", document.getName());
|
||||
return false;
|
||||
}
|
||||
Map<String, List<String>> expectedHighlights = allExpectedHighlights.get(document.getName());
|
||||
List<ResponseHighlightModel> actualHighlights = document.getSearch().getHighlight();
|
||||
Set<String> actualFields = actualHighlights.stream()
|
||||
.map(ResponseHighlightModel::getField)
|
||||
.collect(toSet());
|
||||
if (!actualFields.equals(expectedHighlights.keySet()))
|
||||
{
|
||||
LOGGER.error("Unexpected field set for {}: {}", document.getName(), actualFields);
|
||||
return false;
|
||||
}
|
||||
Set<ResponseHighlightModel> expectedHighlightResponse = new HashSet<>();
|
||||
for (String expectedField : expectedHighlights.keySet())
|
||||
{
|
||||
ResponseHighlightModel expectedHighlight = new ResponseHighlightModel();
|
||||
List<String> expectedSnippets = expectedHighlights.get(expectedField);
|
||||
expectedHighlight.setField(expectedField);
|
||||
expectedHighlight.setSnippets(expectedSnippets);
|
||||
expectedHighlightResponse.add(expectedHighlight);
|
||||
}
|
||||
if (!new HashSet<>(actualHighlights).equals(expectedHighlightResponse))
|
||||
{
|
||||
LOGGER.error("Unexpected highlights for {}, {}", document.getName(), actualHighlights);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.ContentModel;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FileType;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
public class ElasticsearchProximitySearchTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
@Autowired
|
||||
ServerHealth serverHealth;
|
||||
|
||||
@Autowired
|
||||
DataUser dataUser;
|
||||
|
||||
@Autowired
|
||||
DataContent dataContent;
|
||||
|
||||
@Autowired
|
||||
SearchQueryService searchQueryService;
|
||||
|
||||
private UserModel testUser;
|
||||
private String contentA;
|
||||
private String contentB;
|
||||
private String contentC;
|
||||
private String contentD;
|
||||
private String nameA;
|
||||
private String nameB;
|
||||
private String nameC;
|
||||
private String nameD;
|
||||
private String file_NameA_ContentABCD;
|
||||
private String file_NameAB_ContentABC;
|
||||
private String file_NameABC_ContentAB;
|
||||
private String file_NameABCD_ContentA;
|
||||
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
nameA = uniqueString();
|
||||
nameB = uniqueString();
|
||||
nameC = uniqueString();
|
||||
nameD = uniqueString();
|
||||
|
||||
contentA = uniqueString();
|
||||
contentB = uniqueString();
|
||||
contentC = uniqueString();
|
||||
contentD = uniqueString();
|
||||
|
||||
file_NameA_ContentABCD = createContent(join(nameA), join(contentA, contentB, contentC, contentD));
|
||||
file_NameAB_ContentABC = createContent(join(nameA, nameB), join(contentA, contentB, contentC));
|
||||
file_NameABC_ContentAB = createContent(join(nameA, nameB, nameC), join(contentA, contentB));
|
||||
file_NameABCD_ContentA = createContent(join(nameA, nameB, nameC, nameD), join(contentA));
|
||||
|
||||
testUser = dataUser.createRandomTestUser();
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH, TestGroup.TAGS, TestGroup.REGRESSION})
|
||||
public void testProximitySearchUsingAFTSSyntax()
|
||||
{
|
||||
final String AFTS = "%s *%s %s";
|
||||
|
||||
assertAFTS(format(AFTS, contentA, "(0)", contentB), file_NameA_ContentABCD, file_NameAB_ContentABC, file_NameABC_ContentAB);
|
||||
assertAFTS(format(AFTS, contentA, "(1)", contentB), file_NameA_ContentABCD, file_NameAB_ContentABC, file_NameABC_ContentAB);
|
||||
assertAFTS(format(AFTS, contentA, "(2)", contentB), file_NameA_ContentABCD, file_NameAB_ContentABC, file_NameABC_ContentAB);
|
||||
assertAFTS(format(AFTS, contentA, "(3)", contentB), file_NameA_ContentABCD, file_NameAB_ContentABC, file_NameABC_ContentAB);
|
||||
assertAFTS(format(AFTS, contentA, "", contentB), file_NameA_ContentABCD, file_NameAB_ContentABC, file_NameABC_ContentAB);
|
||||
|
||||
assertAFTS(format(AFTS, contentA, "(0)", contentC));
|
||||
assertAFTS(format(AFTS, contentA, "(1)", contentC), file_NameA_ContentABCD, file_NameAB_ContentABC);
|
||||
assertAFTS(format(AFTS, contentA, "(2)", contentC), file_NameA_ContentABCD, file_NameAB_ContentABC);
|
||||
assertAFTS(format(AFTS, contentA, "(3)", contentC), file_NameA_ContentABCD, file_NameAB_ContentABC);
|
||||
assertAFTS(format(AFTS, contentA, "", contentC), file_NameA_ContentABCD, file_NameAB_ContentABC);
|
||||
|
||||
assertAFTS(format(AFTS, contentA, "(0)", contentD));
|
||||
assertAFTS(format(AFTS, contentA, "(1)", contentD));
|
||||
assertAFTS(format(AFTS, contentA, "(2)", contentD), file_NameA_ContentABCD);
|
||||
assertAFTS(format(AFTS, contentA, "(3)", contentD), file_NameA_ContentABCD);
|
||||
assertAFTS(format(AFTS, contentA, "", contentD), file_NameA_ContentABCD);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH, TestGroup.TAGS, TestGroup.REGRESSION})
|
||||
public void testProximitySearchUsingAFTSSyntaxForSpecificProperty()
|
||||
{
|
||||
final String AFTS = "cm:name:(%s *%s %s)";
|
||||
|
||||
assertAFTS(format(AFTS, nameA, "(0)", nameB), file_NameAB_ContentABC, file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
assertAFTS(format(AFTS, nameA, "(1)", nameB), file_NameAB_ContentABC, file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
assertAFTS(format(AFTS, nameA, "(2)", nameB), file_NameAB_ContentABC, file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
assertAFTS(format(AFTS, nameA, "(3)", nameB), file_NameAB_ContentABC, file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
assertAFTS(format(AFTS, nameA, "", nameB), file_NameAB_ContentABC, file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
|
||||
assertAFTS(format(AFTS, nameA, "(0)", nameC));
|
||||
assertAFTS(format(AFTS, nameA, "(1)", nameC), file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
assertAFTS(format(AFTS, nameA, "(2)", nameC), file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
assertAFTS(format(AFTS, nameA, "(3)", nameC), file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
assertAFTS(format(AFTS, nameA, "", nameC), file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
|
||||
assertAFTS(format(AFTS, nameA, "(0)", nameD));
|
||||
assertAFTS(format(AFTS, nameA, "(1)", nameD));
|
||||
assertAFTS(format(AFTS, nameA, "(2)", nameD), file_NameABCD_ContentA);
|
||||
assertAFTS(format(AFTS, nameA, "(3)", nameD), file_NameABCD_ContentA);
|
||||
assertAFTS(format(AFTS, nameA, "", nameD), file_NameABCD_ContentA);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH, TestGroup.TAGS, TestGroup.REGRESSION})
|
||||
public void testProximitySearchUsingLuceneSyntax()
|
||||
{
|
||||
final String lucene = "\"%s %s\"~%d";
|
||||
|
||||
assertLucene(format(lucene, contentA, contentB, 0), file_NameA_ContentABCD, file_NameAB_ContentABC, file_NameABC_ContentAB);
|
||||
assertLucene(format(lucene, contentA, contentB, 1), file_NameA_ContentABCD, file_NameAB_ContentABC, file_NameABC_ContentAB);
|
||||
assertLucene(format(lucene, contentA, contentB, 2), file_NameA_ContentABCD, file_NameAB_ContentABC, file_NameABC_ContentAB);
|
||||
assertLucene(format(lucene, contentA, contentB, 3), file_NameA_ContentABCD, file_NameAB_ContentABC, file_NameABC_ContentAB);
|
||||
|
||||
assertLucene(format(lucene, contentA, contentC, 0));
|
||||
assertLucene(format(lucene, contentA, contentC, 1), file_NameA_ContentABCD, file_NameAB_ContentABC);
|
||||
assertLucene(format(lucene, contentA, contentC, 2), file_NameA_ContentABCD, file_NameAB_ContentABC);
|
||||
assertLucene(format(lucene, contentA, contentC, 3), file_NameA_ContentABCD, file_NameAB_ContentABC);
|
||||
|
||||
assertLucene(format(lucene, contentA, contentD, 0));
|
||||
assertLucene(format(lucene, contentA, contentD, 1));
|
||||
assertLucene(format(lucene, contentA, contentD, 2), file_NameA_ContentABCD);
|
||||
assertLucene(format(lucene, contentA, contentD, 3), file_NameA_ContentABCD);
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH, TestGroup.TAGS, TestGroup.REGRESSION})
|
||||
public void testProximitySearchUsingLuceneSyntaxForSpecificProperty()
|
||||
{
|
||||
final String lucene = "@cm\\:name:\"%s %s\"~%d";
|
||||
|
||||
assertLucene(format(lucene, nameA, nameB, 0), file_NameAB_ContentABC, file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
assertLucene(format(lucene, nameA, nameB, 1), file_NameAB_ContentABC, file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
assertLucene(format(lucene, nameA, nameB, 2), file_NameAB_ContentABC, file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
assertLucene(format(lucene, nameA, nameB, 3), file_NameAB_ContentABC, file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
|
||||
assertLucene(format(lucene, nameA, nameC, 0));
|
||||
assertLucene(format(lucene, nameA, nameC, 1), file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
assertLucene(format(lucene, nameA, nameC, 2), file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
assertLucene(format(lucene, nameA, nameC, 3), file_NameABC_ContentAB, file_NameABCD_ContentA);
|
||||
|
||||
assertLucene(format(lucene, nameA, nameD, 0));
|
||||
assertLucene(format(lucene, nameA, nameD, 1));
|
||||
assertLucene(format(lucene, nameA, nameD, 2), file_NameABCD_ContentA);
|
||||
assertLucene(format(lucene, nameA, nameD, 3), file_NameABCD_ContentA);
|
||||
}
|
||||
|
||||
private void assertLucene(String query, String... expected)
|
||||
{
|
||||
assertQueryResult("lucene", query, expected);
|
||||
}
|
||||
|
||||
private void assertAFTS(String query, String... expected)
|
||||
{
|
||||
assertQueryResult("afts", query, expected);
|
||||
}
|
||||
|
||||
private void assertQueryResult(String language, String query, String... expected)
|
||||
{
|
||||
final SearchRequest searchRequest = req(language, query);
|
||||
if (expected.length == 0)
|
||||
{
|
||||
searchQueryService.expectNoResultsFromQuery(searchRequest, testUser);
|
||||
}
|
||||
else
|
||||
{
|
||||
searchQueryService.expectNodeRefsFromQuery(searchRequest, testUser, expected);
|
||||
}
|
||||
}
|
||||
|
||||
private String createContent(String filename, String content)
|
||||
{
|
||||
return dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(contentRoot())
|
||||
.createContent(new FileModel(filename, FileType.TEXT_PLAIN, content))
|
||||
.getNodeRef();
|
||||
}
|
||||
|
||||
private static ContentModel contentRoot()
|
||||
{
|
||||
final ContentModel root = new ContentModel("-root-");
|
||||
root.setNodeRef(root.getName());
|
||||
return root;
|
||||
}
|
||||
|
||||
private String join(String... parts)
|
||||
{
|
||||
return String.join(" ", parts);
|
||||
}
|
||||
|
||||
private static String uniqueString()
|
||||
{
|
||||
final UUID unique = UUID.randomUUID();
|
||||
final StringBuilder result = new StringBuilder(Long.SIZE / 2);
|
||||
for (long l : List.of(unique.getMostSignificantBits(), unique.getLeastSignificantBits()))
|
||||
{
|
||||
for (int i = Long.SIZE - 4; i >= 0; i -= 4)
|
||||
{
|
||||
int ch = 'a' + (byte) ((((l & (0xFL << i)) >> i)) & 0xFL);
|
||||
result.append(Character.toChars(ch));
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
+522
@@ -0,0 +1,522 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.chemistry.opencmis.client.api.CmisObject;
|
||||
import org.apache.chemistry.opencmis.client.api.Document;
|
||||
import org.apache.chemistry.opencmis.client.api.Folder;
|
||||
import org.apache.chemistry.opencmis.client.api.Session;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.dataprep.AlfrescoHttpClientFactory;
|
||||
import org.alfresco.dataprep.CMISUtil;
|
||||
import org.alfresco.dataprep.ContentActions;
|
||||
import org.alfresco.dataprep.SiteService.Visibility;
|
||||
import org.alfresco.rest.core.RestWrapper;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.ContentModel;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FileType;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
import org.alfresco.utility.report.log.Step;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
public class ElasticsearchSiteIndexingTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
private static final Iterable<String> LANGUAGES_TO_CHECK = List.of("afts", "lucene");
|
||||
private static final FileModel DOCUMENT_LIBRARY = new FileModel("documentLibrary");
|
||||
private static final String FILENAME_PREFIX = "EsSiteTest";
|
||||
private static final String FILE_CONTENT_CONDITION = " TEXT:" + FILENAME_PREFIX + "*";
|
||||
private static final String SAMPLE_SITE_ID = "swsdp";
|
||||
private static final String ALL_SITES = "_ALL_SITES_ ";
|
||||
private static final String EVERYTHING = "_EVERYTHING_ ";
|
||||
|
||||
@Autowired
|
||||
ServerHealth serverHealth;
|
||||
|
||||
@Autowired
|
||||
DataUser dataUser;
|
||||
|
||||
@Autowired
|
||||
DataContent dataContent;
|
||||
|
||||
@Autowired
|
||||
public DataSite dataSite;
|
||||
|
||||
@Autowired
|
||||
private AlfrescoHttpClientFactory alfrescoHttpClientFactory;
|
||||
|
||||
@Autowired
|
||||
SearchQueryService searchQueryService;
|
||||
|
||||
@Autowired
|
||||
protected RestWrapper restClient;
|
||||
|
||||
private UserModel testUser;
|
||||
private UserModel siteCreator;
|
||||
private FolderModel testFolder;
|
||||
private SiteModel testSite1;
|
||||
private SiteModel testSite2;
|
||||
private FileModel fileNotInSite;
|
||||
private FileModel file1;
|
||||
private FileModel file2;
|
||||
private FileModel file3;
|
||||
private FileModel file4;
|
||||
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
Step.STEP("Create test users and a folder.");
|
||||
testUser = dataUser.createRandomTestUser();
|
||||
siteCreator = dataUser.createRandomTestUser();
|
||||
testFolder = dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(contentRoot())
|
||||
.createFolder(new FolderModel(unique("FOLDER")));
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH, TestGroup.SITES, TestGroup.REGRESSION})
|
||||
public void testSiteUseCasesForCreateModifyDeleteSite()
|
||||
{
|
||||
// Remove the automatically created Sample Site
|
||||
deleteSite(SAMPLE_SITE_ID);
|
||||
|
||||
// Sometimes this test may fail, so if previous data exists it must be deleted before the next run
|
||||
Stream.of(fileNotInSite, file1, file2, file3, file4)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(this::deleteFile);
|
||||
Stream.of(testSite1, testSite2)
|
||||
.filter(Objects::nonNull)
|
||||
.map(SiteModel::getId)
|
||||
.forEach(this::deleteSite);
|
||||
|
||||
Step.STEP("Site creation use cases");
|
||||
|
||||
// Check there are no files within or without a site, that have the given filename prefix
|
||||
assertSiteQueryResult(ALL_SITES, List.of());
|
||||
assertSiteQueryResult(EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of());
|
||||
|
||||
// Create a file with the given filename prefix, outside any site
|
||||
fileNotInSite = new FileModel(unique(FILENAME_PREFIX) + ".txt", FileType.TEXT_PLAIN, "Content for fileNotInSite");
|
||||
fileNotInSite = dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(testFolder)
|
||||
.createContent(fileNotInSite);
|
||||
|
||||
// Search with condition of filename prefix - we should see the file using EVERYTHING but not from ALL_SITES
|
||||
assertSiteQueryResult(ALL_SITES, "AND", FILE_CONTENT_CONDITION, List.of());
|
||||
assertSiteQueryResult(EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite));
|
||||
|
||||
// No sites should exist - expect no results
|
||||
assertSiteQueryResult(ALL_SITES, List.of());
|
||||
assertSiteQueryResult(unique("NoSuchSite"), List.of());
|
||||
|
||||
// Create one empty public site - expect no results other than document library
|
||||
testSite1 = createPublicSite();
|
||||
assertSiteQueryResult(testSite1.getId(), List.of(DOCUMENT_LIBRARY));
|
||||
assertSiteQueryResult(ALL_SITES, List.of(DOCUMENT_LIBRARY));
|
||||
assertSiteQueryResult(EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite));
|
||||
|
||||
// Create a file in public site - expect one file plus the document library.
|
||||
file1 = createContentInSite(testSite1, FILENAME_PREFIX + "test1");
|
||||
assertSiteQueryResult(testSite1.getId(), List.of(DOCUMENT_LIBRARY, file1));
|
||||
assertSiteQueryResult(ALL_SITES, List.of(DOCUMENT_LIBRARY, file1));
|
||||
assertSiteQueryResult(EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite, file1));
|
||||
|
||||
// Create another file in public site - expect two files plus the document library.
|
||||
file2 = createContentInSite(testSite1, FILENAME_PREFIX + "test2");
|
||||
assertSiteQueryResult(testSite1.getId(), List.of(DOCUMENT_LIBRARY, file1, file2));
|
||||
assertSiteQueryResult(ALL_SITES, List.of(DOCUMENT_LIBRARY, file1, file2));
|
||||
assertSiteQueryResult(EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite, file1, file2));
|
||||
|
||||
// Create a second public site, empty - expect no results other than document library
|
||||
testSite2 = createPublicSite();
|
||||
assertSiteQueryResult(testSite2.getId(), List.of(DOCUMENT_LIBRARY));
|
||||
assertSiteQueryResult(testSite1.getId(), List.of(DOCUMENT_LIBRARY, file1, file2));
|
||||
assertSiteQueryResult(ALL_SITES, List.of(DOCUMENT_LIBRARY, file1, file2));
|
||||
assertSiteQueryResult(EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite, file1, file2));
|
||||
|
||||
// Create a file in second public site - expect one file plus the document library.
|
||||
file3 = createContentInSite(testSite2, FILENAME_PREFIX + "test3");
|
||||
assertSiteQueryResult(testSite2.getId(), List.of(DOCUMENT_LIBRARY, file3));
|
||||
assertSiteQueryResult(testSite1.getId(), List.of(DOCUMENT_LIBRARY, file1, file2));
|
||||
assertSiteQueryResult(ALL_SITES, List.of(DOCUMENT_LIBRARY, file1, file2, file3));
|
||||
assertSiteQueryResult(EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite, file1, file2, file3));
|
||||
|
||||
// Create another file in second public site - expect two files plus the document library.
|
||||
file4 = createContentInSite(testSite2, FILENAME_PREFIX + "test4");
|
||||
assertSiteQueryResult(testSite2.getId(), List.of(DOCUMENT_LIBRARY, file3, file4));
|
||||
assertSiteQueryResult(testSite1.getId(), List.of(DOCUMENT_LIBRARY, file1, file2));
|
||||
assertSiteQueryResult(ALL_SITES, List.of(DOCUMENT_LIBRARY, file1, file2, file3, file4));
|
||||
assertSiteQueryResult(EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite, file1, file2, file3, file4));
|
||||
|
||||
// Test disjunction and conjunction using SITE: and SITE:
|
||||
assertSiteQueryResult(testSite1.getId(), "OR", " SITE:" + testSite2.getId(), List.of(DOCUMENT_LIBRARY, file1, file2, file3, file4));
|
||||
assertSiteQueryResult(testSite1.getId(), "OR", " SITE:" + unique("NoSuchSite"), List.of(DOCUMENT_LIBRARY, file1, file2));
|
||||
assertSiteQueryResult(testSite1.getId(), "AND", " SITE:" + unique("NoSuchSite"), List.of());
|
||||
|
||||
// Test conjunction using SITE: and TEXT:
|
||||
assertSiteQueryResult(testSite1.getId(), "AND", " TEXT:" + FILENAME_PREFIX + "test2*", List.of(file2));
|
||||
assertSiteQueryResult(testSite1.getId(), "AND", " TEXT:" + FILENAME_PREFIX + "testX*", List.of());
|
||||
assertSiteQueryResult(EVERYTHING, "AND", " TEXT:" + FILENAME_PREFIX + "test2*", List.of(file2));
|
||||
assertSiteQueryResult(EVERYTHING, "AND", " TEXT:" + FILENAME_PREFIX + "testX*", List.of());
|
||||
assertSiteQueryResult(ALL_SITES, "AND", " TEXT:" + FILENAME_PREFIX + "test2*", List.of(file2));
|
||||
assertSiteQueryResult(ALL_SITES, "AND", " TEXT:" + FILENAME_PREFIX + "testX*", List.of());
|
||||
|
||||
// Test modify site
|
||||
Step.STEP("Site modification use cases");
|
||||
|
||||
// Verify site creating user can see all files in both public sites
|
||||
assertSiteQueryResult(siteCreator, testSite1.getId(), List.of(DOCUMENT_LIBRARY, file1, file2));
|
||||
assertSiteQueryResult(siteCreator, testSite2.getId(), List.of(DOCUMENT_LIBRARY, file3, file4));
|
||||
assertSiteQueryResult(siteCreator, ALL_SITES, List.of(DOCUMENT_LIBRARY, file1, file2, file3, file4));
|
||||
assertSiteQueryResult(siteCreator, EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite, file1, file2, file3, file4));
|
||||
|
||||
// Verify user who is not a member of any sites can see all files in both public sites
|
||||
UserModel publicUser = dataUser.createRandomTestUser();
|
||||
assertSiteQueryResult(publicUser, testSite1.getId(), List.of(DOCUMENT_LIBRARY, file1, file2));
|
||||
assertSiteQueryResult(publicUser, testSite2.getId(), List.of(DOCUMENT_LIBRARY, file3, file4));
|
||||
assertSiteQueryResult(publicUser, ALL_SITES, List.of(DOCUMENT_LIBRARY, file1, file2, file3, file4));
|
||||
assertSiteQueryResult(publicUser, EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite, file1, file2, file3, file4));
|
||||
|
||||
// Change first site's visibility to private - verify site creator can still see all files in both sites
|
||||
dataSite.usingUser(siteCreator).updateSiteVisibility(testSite1, Visibility.PRIVATE);
|
||||
assertSiteQueryResult(siteCreator, testSite1.getId(), List.of(DOCUMENT_LIBRARY, file1, file2));
|
||||
assertSiteQueryResult(siteCreator, testSite2.getId(), List.of(DOCUMENT_LIBRARY, file3, file4));
|
||||
assertSiteQueryResult(siteCreator, ALL_SITES, List.of(DOCUMENT_LIBRARY, file1, file2, file3, file4));
|
||||
assertSiteQueryResult(siteCreator, EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite, file1, file2, file3, file4));
|
||||
|
||||
// Verify user who is not a member of any sites can see files in public site but not see files in private site
|
||||
assertSiteQueryResult(publicUser, testSite1.getId(), List.of());
|
||||
assertSiteQueryResult(publicUser, testSite2.getId(), List.of(DOCUMENT_LIBRARY, file3, file4));
|
||||
assertSiteQueryResult(publicUser, ALL_SITES, List.of(DOCUMENT_LIBRARY, file3, file4));
|
||||
assertSiteQueryResult(publicUser, EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite, file3, file4));
|
||||
|
||||
// Change site visibility back to public - user who is not a member of any sites can see files in both sites again
|
||||
dataSite.usingUser(siteCreator).updateSiteVisibility(testSite1, Visibility.PUBLIC);
|
||||
assertSiteQueryResult(publicUser, testSite1.getId(), List.of(DOCUMENT_LIBRARY, file1, file2));
|
||||
assertSiteQueryResult(publicUser, testSite2.getId(), List.of(DOCUMENT_LIBRARY, file3, file4));
|
||||
assertSiteQueryResult(publicUser, ALL_SITES, List.of(DOCUMENT_LIBRARY, file1, file2, file3, file4));
|
||||
assertSiteQueryResult(publicUser, EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite, file1, file2, file3, file4));
|
||||
|
||||
// Test delete site
|
||||
Step.STEP("Site deletion use cases");
|
||||
|
||||
// Delete one site - expect no results for that site
|
||||
deleteSite(testSite1.getId());
|
||||
assertSiteQueryResult(testSite1.getId(), List.of());
|
||||
assertSiteQueryResult(testSite2.getId(), List.of(DOCUMENT_LIBRARY, file3, file4));
|
||||
assertSiteQueryResult(ALL_SITES, List.of(DOCUMENT_LIBRARY, file3, file4));
|
||||
assertSiteQueryResult(EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite, file3, file4));
|
||||
|
||||
// Delete remaining site - expect no results
|
||||
deleteSite(testSite2.getId());
|
||||
assertSiteQueryResult(testSite2.getId(), List.of());
|
||||
assertSiteQueryResult(testSite1.getId(), List.of());
|
||||
assertSiteQueryResult(ALL_SITES, List.of());
|
||||
assertSiteQueryResult(EVERYTHING, "AND", FILE_CONTENT_CONDITION, List.of(fileNotInSite));
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH, TestGroup.SITES, TestGroup.REGRESSION})
|
||||
public void manipulatingFilesAndContentBetweenSites()
|
||||
{
|
||||
Step.STEP("Moving files between sites and modifying content use cases");
|
||||
|
||||
SiteModel publicSite1 = createPublicSite(siteCreator);
|
||||
SiteModel publicSite2 = createPublicSite(siteCreator);
|
||||
FileModel file5 = createContentInSite(publicSite1, "file5");
|
||||
assertSiteQueryResult(publicSite1.getId(), List.of(DOCUMENT_LIBRARY, file5));
|
||||
|
||||
// Moving a file between sites.
|
||||
moveFile(file5, publicSite1, publicSite2);
|
||||
assertSiteQueryResult(publicSite1.getId(), List.of(DOCUMENT_LIBRARY));
|
||||
assertSiteQueryResult(publicSite2.getId(), List.of(DOCUMENT_LIBRARY, file5));
|
||||
|
||||
// Moving a file out of a site.
|
||||
moveFileOutsideOfSite(file5, publicSite2, testFolder);
|
||||
assertSiteQueryResult(publicSite2.getId(), List.of(DOCUMENT_LIBRARY));
|
||||
|
||||
// Moving a file to the site
|
||||
FileModel file6 = dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(contentRoot())
|
||||
.createContent(CMISUtil.DocumentType.TEXT_PLAIN);
|
||||
FileModel file7 = createContentInSite(publicSite1, "file7"); // Ensuring that DocumentLibrary exists in the publicSite1
|
||||
assertSiteQueryResult(publicSite1.getId(), List.of(DOCUMENT_LIBRARY, file7));
|
||||
moveFileToTheSite(file6, publicSite1);
|
||||
assertSiteQueryResult(publicSite1.getId(), List.of(DOCUMENT_LIBRARY, file6, file7));
|
||||
|
||||
// Removing file.
|
||||
FileModel file8 = createContentInSite(publicSite2, "file8");
|
||||
assertSiteQueryResult(publicSite2.getId(), List.of(DOCUMENT_LIBRARY, file8));
|
||||
deleteFile(file8);
|
||||
assertSiteQueryResult(publicSite2.getId(), List.of(DOCUMENT_LIBRARY));
|
||||
|
||||
// Document modification.
|
||||
FileModel file9 = createContentInSite(publicSite2, "file9", "Initial Content.");
|
||||
assertContentInGivenFileUnderSiteQueryResult(publicSite2.getId(), "initial", List.of(file9));
|
||||
modifyDocument(file9, "Modified Content.");
|
||||
assertContentInGivenFileUnderSiteQueryResult(publicSite2.getId(), "modified", List.of(file9));
|
||||
|
||||
// Cleanup.
|
||||
deleteFiles(file6, file7, file9);
|
||||
dataContent.usingAdmin().deleteSite(publicSite1);
|
||||
dataContent.usingAdmin().deleteSite(publicSite2);
|
||||
}
|
||||
|
||||
private void deleteFiles(FileModel... fileModels)
|
||||
{
|
||||
for (FileModel fileModel : fileModels)
|
||||
{
|
||||
deleteFile(fileModel);
|
||||
}
|
||||
}
|
||||
|
||||
private void moveFileToTheSite(FileModel file, SiteModel targetSite)
|
||||
{
|
||||
Session session = dataContent.getContentActions().getCMISSession(alfrescoHttpClientFactory.getAdminUser(), alfrescoHttpClientFactory.getAdminPassword());
|
||||
ContentActions actions = dataContent.usingAdmin().getContentActions();
|
||||
CmisObject objFrom = actions.getCmisObject(session, file.getName());
|
||||
CmisObject objTarget = session.getObjectByPath("/Sites/" + targetSite.getId() + "/documentLibrary");
|
||||
|
||||
List parents;
|
||||
CmisObject parent;
|
||||
if (objFrom instanceof Document)
|
||||
{
|
||||
Document d = (Document) objFrom;
|
||||
parents = d.getParents();
|
||||
parent = session.getObject(((Folder) parents.get(0)).getId());
|
||||
d.move(parent, objTarget);
|
||||
}
|
||||
else if (objFrom instanceof Folder)
|
||||
{
|
||||
Folder f = (Folder) objFrom;
|
||||
parents = f.getParents();
|
||||
parent = session.getObject(((Folder) parents.get(0)).getId());
|
||||
f.move(parent, objTarget);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertSiteQueryResult(String siteName, Collection<ContentModel> contentModels)
|
||||
{
|
||||
assertSiteQueryResult(testUser, siteName, contentModels);
|
||||
}
|
||||
|
||||
private void assertSiteQueryResult(UserModel user, String siteName, Collection<ContentModel> contentModels)
|
||||
{
|
||||
final List<String> contentNames = contentModels
|
||||
.stream()
|
||||
.map(ContentModel::getName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (final String language : LANGUAGES_TO_CHECK)
|
||||
{
|
||||
Step.STEP("Searching for SITE `" + siteName + "` using `" + language + "` language.");
|
||||
final SearchRequest query = req(language, "SITE:" + siteName + " ");
|
||||
if (contentNames.isEmpty())
|
||||
{
|
||||
searchQueryService.expectNoResultsFromQuery(query, user);
|
||||
}
|
||||
else
|
||||
{
|
||||
Collections.shuffle(contentNames);
|
||||
searchQueryService.expectResultsFromQuery(query, user, contentNames.toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertSiteQueryResult(String site1Name, String operator, String condition, Collection<ContentModel> contentModels)
|
||||
{
|
||||
assertSiteQueryResult(testUser, site1Name, operator, condition, contentModels);
|
||||
}
|
||||
|
||||
private void assertSiteQueryResult(UserModel user, String site1Name, String operator, String condition, Collection<ContentModel> contentModels)
|
||||
{
|
||||
final List<String> contentNames = contentModels
|
||||
.stream()
|
||||
.map(ContentModel::getName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (final String language : LANGUAGES_TO_CHECK)
|
||||
{
|
||||
Step.STEP("Searching for SITE `" + site1Name + "` " + operator + " `" + condition + "` using `" + language + "` language.");
|
||||
final SearchRequest query = req(language, "SITE:" + site1Name + " " + operator + condition);
|
||||
if (contentNames.isEmpty())
|
||||
{
|
||||
searchQueryService.expectNoResultsFromQuery(query, user);
|
||||
}
|
||||
else
|
||||
{
|
||||
Collections.shuffle(contentNames);
|
||||
searchQueryService.expectResultsFromQuery(query, user, contentNames.toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertContentInGivenFileUnderSiteQueryResult(String siteName, String content, Collection<ContentModel> contentModels)
|
||||
{
|
||||
final List<String> contentNames = contentModels
|
||||
.stream()
|
||||
.map(ContentModel::getName)
|
||||
.collect(Collectors.toList());
|
||||
for (final String language : LANGUAGES_TO_CHECK)
|
||||
{
|
||||
Step.STEP("Searching for SITE `" + siteName + "` and content:'" + content + "' using `" + language + "` language.");
|
||||
final SearchRequest query = req(language, "SITE:" + siteName + " AND TEXT:" + content + " ");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, contentNames.toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
|
||||
private void modifyDocument(FileModel file, String newContent)
|
||||
{
|
||||
dataContent.usingUser(siteCreator).usingResource(file)
|
||||
.updateContent(newContent);
|
||||
}
|
||||
|
||||
private void deleteFile(ContentModel contentModel)
|
||||
{
|
||||
dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(contentModel)
|
||||
.deleteContent();
|
||||
}
|
||||
|
||||
private void moveFile(FileModel file, SiteModel sourceSite, SiteModel targetSite)
|
||||
{
|
||||
moveFile(file, sourceSite, targetSite, null);
|
||||
}
|
||||
|
||||
private void moveFile(FileModel file, SiteModel sourceSite, SiteModel targetSite, ContentModel targetFolder)
|
||||
{
|
||||
Session session = dataContent.getContentActions().getCMISSession(siteCreator.getUsername(), siteCreator.getPassword());
|
||||
dataContent.usingUser(siteCreator).getContentActions()
|
||||
.moveTo(
|
||||
session,
|
||||
sourceSite.getId(),
|
||||
file.getName(),
|
||||
targetSite.getId(),
|
||||
targetFolder != null ? targetFolder.getName() : Strings.EMPTY // if empty then target folder is documentLibrary (which is default target location)
|
||||
);
|
||||
}
|
||||
|
||||
private void moveFileOutsideOfSite(FileModel file, SiteModel sourceSite, ContentModel targetContentModel)
|
||||
{
|
||||
Session session = dataContent.getContentActions().getCMISSession(alfrescoHttpClientFactory.getAdminUser(), alfrescoHttpClientFactory.getAdminPassword());
|
||||
ContentActions actions = dataContent.usingAdmin().getContentActions();
|
||||
CmisObject objFrom = actions.getCmisObject(session, sourceSite.getId(), file.getName());
|
||||
CmisObject objTarget = actions.getCmisObject(session, targetContentModel.getCmisLocation());
|
||||
|
||||
List parents;
|
||||
CmisObject parent;
|
||||
if (objFrom instanceof Document)
|
||||
{
|
||||
Document d = (Document) objFrom;
|
||||
parents = d.getParents();
|
||||
parent = session.getObject(((Folder) parents.get(0)).getId());
|
||||
d.move(parent, objTarget);
|
||||
}
|
||||
else if (objFrom instanceof Folder)
|
||||
{
|
||||
Folder f = (Folder) objFrom;
|
||||
parents = f.getParents();
|
||||
parent = session.getObject(((Folder) parents.get(0)).getId());
|
||||
f.move(parent, objTarget);
|
||||
}
|
||||
}
|
||||
|
||||
private FileModel createContentInSite(SiteModel site, String fileName)
|
||||
{
|
||||
return createContentInSite(site, fileName, "Content for " + fileName);
|
||||
}
|
||||
|
||||
private FileModel createContentInSite(SiteModel site, String fileName, String content)
|
||||
{
|
||||
final FileModel file = new FileModel(unique(fileName) + ".txt", FileType.TEXT_PLAIN, content);
|
||||
return dataContent.usingUser(siteCreator)
|
||||
.usingSite(site)
|
||||
.createContent(file);
|
||||
}
|
||||
|
||||
private SiteModel createPublicSite()
|
||||
{
|
||||
return createPublicSite(siteCreator);
|
||||
}
|
||||
|
||||
private SiteModel createPublicSite(UserModel user)
|
||||
{
|
||||
SiteModel createdSite = dataSite.usingUser(user).createPublicRandomSite();
|
||||
Step.STEP("Created public site '" + createdSite.getId() + "'.");
|
||||
return createdSite;
|
||||
}
|
||||
|
||||
private void deleteSite(String siteId)
|
||||
{
|
||||
Step.STEP("Deleting site '" + siteId + "', if it exists");
|
||||
SiteModel siteToDelete = new SiteModel(siteId);
|
||||
if (dataSite.usingAdmin().isSiteCreated(siteToDelete))
|
||||
{
|
||||
dataSite.usingAdmin().deleteSite(siteToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
private static ContentModel contentRoot()
|
||||
{
|
||||
final ContentModel root = new ContentModel("-root-");
|
||||
root.setNodeRef(root.getName());
|
||||
return root;
|
||||
}
|
||||
|
||||
private static String unique(String prefix)
|
||||
{
|
||||
return prefix + "-" + UUID.randomUUID();
|
||||
}
|
||||
}
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.dataprep.AlfrescoHttpClientFactory;
|
||||
import org.alfresco.rest.core.RestWrapper;
|
||||
import org.alfresco.rest.model.RestTagModel;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.*;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
import org.alfresco.utility.report.log.Step;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
public class ElasticsearchTagIndexingTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
private static final Iterable<String> LANGUAGES_TO_CHECK = List.of("afts", "lucene");
|
||||
|
||||
@Autowired
|
||||
ServerHealth serverHealth;
|
||||
|
||||
@Autowired
|
||||
DataUser dataUser;
|
||||
|
||||
@Autowired
|
||||
DataContent dataContent;
|
||||
|
||||
@Autowired
|
||||
private AlfrescoHttpClientFactory alfrescoHttpClientFactory;
|
||||
|
||||
@Autowired
|
||||
SearchQueryService searchQueryService;
|
||||
|
||||
@Autowired
|
||||
protected RestWrapper restClient;
|
||||
|
||||
private UserModel testUser;
|
||||
private FolderModel testFolder;
|
||||
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
testUser = dataUser.createRandomTestUser();
|
||||
testFolder = dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(contentRoot())
|
||||
.createFolder(new FolderModel(unique("FOLDER")));
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH, TestGroup.TAGS, TestGroup.REGRESSION})
|
||||
public void testTAGUseCases()
|
||||
{
|
||||
final String tag1 = unique("TAG1");
|
||||
|
||||
// No result for not existing tag
|
||||
assertTagQueryResult(tag1, List.of());
|
||||
|
||||
// Tag first file - tag is created - expect single result
|
||||
final FileModel file1 = givenFile("test1");
|
||||
tagContent(file1, tag1);
|
||||
assertTagQueryResult(tag1, List.of(file1));
|
||||
|
||||
// Tag another file - tag is reused - expect two items in the result
|
||||
final FileModel file2 = givenFile("test2");
|
||||
tagContent(file2, tag1);
|
||||
assertTagQueryResult(tag1, List.of(file1, file2));
|
||||
|
||||
final String tag2 = unique("TAG2");
|
||||
|
||||
// Just a sanity check - No result for the second tag
|
||||
assertTagQueryResult(tag2, List.of());
|
||||
|
||||
// Tag second file with a new tag
|
||||
final String tag2Id = tagContent(file2, tag2);
|
||||
assertTagQueryResult(tag1, List.of(file1, file2));
|
||||
assertTagQueryResult(tag2, List.of(file2));
|
||||
|
||||
// Tag new file with the second tag
|
||||
final FileModel file3 = givenFile("test3");
|
||||
tagContent(file3, tag2);
|
||||
assertTagQueryResult(tag1, List.of(file1, file2));
|
||||
assertTagQueryResult(tag2, List.of(file2, file3));
|
||||
|
||||
// test disjunction and conjunction
|
||||
assertTagQueryResult(tag1, "OR", tag2, List.of(file1, file2, file3));
|
||||
assertTagQueryResult(tag1, "AND", tag2, List.of(file2));
|
||||
final String unknownTag = unique("unknown");
|
||||
assertTagQueryResult(tag1, "OR", unknownTag, List.of(file1, file2));
|
||||
assertTagQueryResult(tag1, "AND", unknownTag, List.of());
|
||||
|
||||
// Delete file
|
||||
deleteFile(file1);
|
||||
assertTagQueryResult(tag1, List.of(file2));
|
||||
assertTagQueryResult(tag2, List.of(file2, file3));
|
||||
|
||||
// Rename tag
|
||||
final String newTag2 = unique("NEW-TAG2");
|
||||
assertTagQueryResult(newTag2, List.of());
|
||||
renameTag(tag2Id, newTag2);
|
||||
assertTagQueryResult(tag1, List.of(file2));
|
||||
assertTagQueryResult(newTag2, List.of(file2, file3));
|
||||
assertTagQueryResult(tag2, List.of());
|
||||
|
||||
// Delete tag
|
||||
deleteTag(file3, newTag2);
|
||||
assertTagQueryResult(tag1, List.of(file2));
|
||||
assertTagQueryResult(tag2, List.of());
|
||||
assertTagQueryResult(newTag2, List.of(file2));
|
||||
}
|
||||
|
||||
private void assertTagQueryResult(String tag1Name, String operator, String tag2Name, Collection<ContentModel> contentModels)
|
||||
{
|
||||
final List<String> contentNames = contentModels
|
||||
.stream()
|
||||
.map(ContentModel::getName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (final String tag1 : tagVariantsToCheck(tag1Name))
|
||||
{
|
||||
for (final String tag2 : tagVariantsToCheck(tag2Name))
|
||||
{
|
||||
for (final String language : LANGUAGES_TO_CHECK)
|
||||
{
|
||||
Step.STEP("Searching for TAG `" + tag1 + "` " + operator + " `" + tag2 + "` using `" + language + "` language.");
|
||||
final SearchRequest query = req(language, "TAG:" + tag1 + " " + operator + " TAG:" + tag2);
|
||||
if (contentNames.isEmpty())
|
||||
{
|
||||
searchQueryService.expectNoResultsFromQuery(query, testUser);
|
||||
}
|
||||
else
|
||||
{
|
||||
Collections.shuffle(contentNames);
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, contentNames.toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertTagQueryResult(String tagName, Collection<ContentModel> contentModels)
|
||||
{
|
||||
final List<String> contentNames = contentModels
|
||||
.stream()
|
||||
.map(ContentModel::getName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (final String tag : tagVariantsToCheck(tagName))
|
||||
{
|
||||
for (final String language : LANGUAGES_TO_CHECK)
|
||||
{
|
||||
Step.STEP("Searching for TAG `" + tag + "` using `" + language + "` language.");
|
||||
final SearchRequest query = req(language, "TAG:" + tag);
|
||||
if (contentNames.isEmpty())
|
||||
{
|
||||
searchQueryService.expectNoResultsFromQuery(query, testUser);
|
||||
}
|
||||
else
|
||||
{
|
||||
Collections.shuffle(contentNames);
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, contentNames.toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Iterable<String> tagVariantsToCheck(String tagName)
|
||||
{
|
||||
return List.of(
|
||||
tagName.toLowerCase(Locale.ROOT),
|
||||
tagName.toUpperCase(Locale.ROOT),
|
||||
"\"" + tagName.toLowerCase(Locale.ROOT) + "\"",
|
||||
"\"" + tagName.toUpperCase(Locale.ROOT) + "\"");
|
||||
}
|
||||
|
||||
private FileModel givenFile(final String fileName)
|
||||
{
|
||||
final FileModel file = new FileModel(unique(fileName), FileType.TEXT_PLAIN, "Content for " + fileName);
|
||||
return dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(testFolder)
|
||||
.createContent(file);
|
||||
}
|
||||
|
||||
private String tagContent(ContentModel contentModel, String tag)
|
||||
{
|
||||
var tagModel = new TagModel(tag);
|
||||
dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(contentModel)
|
||||
.addTagToContent(tagModel);
|
||||
|
||||
final String tagNodeId = dataContent.getContentActions().getTagNodeRef(
|
||||
dataContent.getCurrentUser().getUsername(), dataContent.getCurrentUser().getPassword(),
|
||||
contentModel.getCmisLocation(), tag);
|
||||
assertNotNull(tagNodeId, "Tag node ref must exist.");
|
||||
return tagNodeId;
|
||||
}
|
||||
|
||||
private void deleteFile(ContentModel contentModel)
|
||||
{
|
||||
dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(contentModel)
|
||||
.deleteContent();
|
||||
}
|
||||
|
||||
private void deleteTag(ContentModel contentModel, String tagName)
|
||||
{
|
||||
boolean deleted = dataContent
|
||||
.usingAdmin()
|
||||
.getContentActions()
|
||||
.removeTag(
|
||||
dataContent.getCurrentUser().getUsername(), dataContent.getCurrentUser().getPassword(),
|
||||
contentModel.getCmisLocation(), tagName);
|
||||
assertTrue(deleted, "Tag should be deleted.");
|
||||
}
|
||||
|
||||
private void renameTag(String tagId, String newName)
|
||||
{
|
||||
RestTagModel tag = RestTagModel.builder().id(tagId).create();
|
||||
RestTagModel update = restClient.authenticateUser(dataContent.getAdminUser()).withCoreAPI().usingTag(tag).update(newName);
|
||||
restClient.assertStatusCodeIs(OK);
|
||||
}
|
||||
|
||||
private static ContentModel contentRoot()
|
||||
{
|
||||
final ContentModel root = new ContentModel("-root-");
|
||||
root.setNodeRef(root.getName());
|
||||
return root;
|
||||
}
|
||||
|
||||
private static String unique(String prefix)
|
||||
{
|
||||
return prefix + "-" + UUID.randomUUID();
|
||||
}
|
||||
}
|
||||
+316
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
import static org.alfresco.utility.data.RandomData.getRandomFile;
|
||||
import static org.alfresco.utility.data.RandomData.getRandomName;
|
||||
import static org.alfresco.utility.report.log.Step.STEP;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.rest.model.RestTagModel;
|
||||
import org.alfresco.rest.search.RestRequestDefaultsModel;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.Utility;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.ContentModel;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FileType;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
public class ElasticsearchTemplateSearchTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
private static final String SEARCH_TERM = "sample";
|
||||
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
|
||||
@Autowired
|
||||
private DataContent dataContent;
|
||||
|
||||
@Autowired
|
||||
private SearchQueryService searchQueryService;
|
||||
|
||||
private UserModel testUser;
|
||||
private FolderModel testParentFolder;
|
||||
/** Pre-built ANCESTOR clause for the test parent folder. */
|
||||
private String ancestorClause;
|
||||
private ContentModel fileWithTermInName;
|
||||
private ContentModel fileWithPhraseInContent;
|
||||
private ContentModel fileWithTermInTitle;
|
||||
private ContentModel fileWithTermInDescription;
|
||||
private ContentModel fileWithTermInTag;
|
||||
private ContentModel fileWithDifferentTermInName;
|
||||
private ContentModel folderWithTermInName;
|
||||
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation() throws Exception
|
||||
{
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
STEP("Create a dedicated parent folder so all queries can be scoped via ANCESTOR");
|
||||
testParentFolder = createTestParentFolder();
|
||||
ancestorClause = "ANCESTOR:\"" + testParentFolder.getNodeRef() + "\"";
|
||||
|
||||
STEP("Create a test user and few files and folders containing searched term in name, title, description, content and tag");
|
||||
fileWithTermInName = createFile(SEARCH_TERM + ".txt", "some text");
|
||||
fileWithPhraseInContent = createFile(getRandomFile(FileType.TEXT_PLAIN), "Dummy " + SEARCH_TERM + " irrelevant text");
|
||||
fileWithTermInTitle = createRandomFileWithTitle(SEARCH_TERM);
|
||||
fileWithTermInDescription = createRandomFileWithDescription(SEARCH_TERM);
|
||||
fileWithTermInTag = createRandomFileWithTag(SEARCH_TERM);
|
||||
fileWithDifferentTermInName = createFile("dummy.txt", "content without phrase");
|
||||
folderWithTermInName = createFolder(SEARCH_TERM);
|
||||
|
||||
testUser = dataUser.createRandomTestUser();
|
||||
|
||||
STEP("Wait for the batch indexer to index the last-created node under the test parent folder");
|
||||
SearchRequest probe = req("afts", ancestorClause + " AND TYPE:'cm:folder'", Map.of());
|
||||
Utility.sleep(500, 60000, () -> searchQueryService.expectNodeRefsFromQuery(
|
||||
probe, dataUser.getAdminUser(), folderWithTermInName.getNodeRef()));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public void dataCleanup()
|
||||
{
|
||||
STEP("Clean up the test parent folder (cascades to all children) and the test user");
|
||||
if (testParentFolder != null)
|
||||
{
|
||||
dataContent.usingAdmin().usingResource(testParentFolder).deleteContent();
|
||||
}
|
||||
if (testUser != null)
|
||||
{
|
||||
dataUser.deleteUser(testUser);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_simpleTemplate()
|
||||
{
|
||||
STEP("Search for files by name using simple template with one property");
|
||||
Map<String, String> templates = Map.of("_NODE", "%cm:name");
|
||||
String query = ancestorClause + " AND TYPE:'cm:content' AND _NODE:" + SEARCH_TERM;
|
||||
SearchRequest request = req("afts", query, templates);
|
||||
|
||||
searchQueryService.expectNodeRefsFromQuery(request, testUser, fileWithTermInName.getNodeRef());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_simpleTemplateWithPhrase()
|
||||
{
|
||||
STEP("Search for files containing specific phrase using simple template with one property");
|
||||
Map<String, String> templates = Map.of("_NODE", "%TEXT");
|
||||
String query = ancestorClause + " AND TYPE:'cm:content' AND _NODE:\"" + SEARCH_TERM + " irrelevant\"";
|
||||
SearchRequest request = req("afts", query, templates);
|
||||
|
||||
searchQueryService.expectNodeRefsFromQuery(request, testUser, fileWithPhraseInContent.getNodeRef());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_templateWithTwoParameters()
|
||||
{
|
||||
STEP("Search for files using template containing multiple properties");
|
||||
Map<String, String> templates = Map.of("_NODE", "%(cm:name cm:title)");
|
||||
String query = ancestorClause + " AND TYPE:'cm:content' AND _NODE:" + SEARCH_TERM;
|
||||
SearchRequest request = req("afts", query, templates);
|
||||
|
||||
searchQueryService.expectNodeRefsFromQuery(request, testUser, fileWithTermInName.getNodeRef(), fileWithTermInTitle.getNodeRef());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_nestedTemplate()
|
||||
{
|
||||
Map<String, String> templates = Map.of(
|
||||
"_NODE", "%(cm:name cm:title)",
|
||||
"_NODET", "%(_NODEX TAG)",
|
||||
"_NODEX", "%(_NODE cm:description)"
|
||||
|
||||
);
|
||||
STEP("Search for files using more complex two-level nested template containing multiple properties");
|
||||
String query = ancestorClause + " AND TYPE:'cm:content' AND _NODEX:" + SEARCH_TERM;
|
||||
SearchRequest request = req("afts", query, templates);
|
||||
searchQueryService.expectNodeRefsFromQuery(request, testUser,
|
||||
fileWithTermInName.getNodeRef(), fileWithTermInDescription.getNodeRef(), fileWithTermInTitle.getNodeRef());
|
||||
|
||||
STEP("Search for files using three-level nested template containing multiple properties");
|
||||
String queryIncludingTag = ancestorClause + " AND TYPE:'cm:content' AND _NODET:" + SEARCH_TERM;
|
||||
SearchRequest requestIncludingTag = req("afts", queryIncludingTag, templates);
|
||||
searchQueryService.expectNodeRefsFromQuery(requestIncludingTag, testUser,
|
||||
fileWithTermInName.getNodeRef(), fileWithTermInDescription.getNodeRef(), fileWithTermInTitle.getNodeRef(), fileWithTermInTag.getNodeRef());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_templateNameAsQueryDefaultFieldName()
|
||||
{
|
||||
STEP("Search for files using template containing multiple properties, as a default search field");
|
||||
Map<String, String> templates = Map.of("_NODE", "%(cm:name cm:title)");
|
||||
// ANCESTOR must be its own clause; only the bare term uses the default field name.
|
||||
String query = ancestorClause + " AND TYPE:'cm:content' AND " + SEARCH_TERM;
|
||||
SearchRequest request = req("afts", query, templates);
|
||||
request.setDefaults(RestRequestDefaultsModel.builder().defaultFieldName("_NODE").create());
|
||||
|
||||
searchQueryService.expectNodeRefsFromQuery(request, testUser, fileWithTermInName.getNodeRef(), fileWithTermInTitle.getNodeRef());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_templateWithFixedValue()
|
||||
{
|
||||
STEP("Search for files and folders using template containing multiple properties, including a fixed one");
|
||||
Map<String, String> templates = Map.of("_NODE", "%cm:name AND TYPE:'cm:folder'");
|
||||
String query = ancestorClause + " AND _NODE:" + SEARCH_TERM;
|
||||
SearchRequest request = req("afts", query, templates);
|
||||
|
||||
searchQueryService.expectNodeRefsFromQuery(request, testUser, folderWithTermInName.getNodeRef());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_boostedTemplate()
|
||||
{
|
||||
STEP("Search for files using templates and boosts, where second term has higher priority ");
|
||||
Map<String, String> templates = Map.of("_NODE", "%cm:name");
|
||||
String query = ancestorClause + " AND TYPE:'cm:content' AND (_NODE:" + SEARCH_TERM + "^0.5 OR _NODE:dummy^2)";
|
||||
SearchRequest request = req("afts", query, templates);
|
||||
searchQueryService.expectResultsInOrder(request, testUser, fileWithDifferentTermInName.getName(), fileWithTermInName.getName());
|
||||
|
||||
STEP("Search for files using templates and boosts, where first term has higher priority ");
|
||||
String queryInvertedBoost = ancestorClause + " AND TYPE:'cm:content' AND (_NODE:" + SEARCH_TERM + "^2 OR _NODE:dummy^0.5)";
|
||||
SearchRequest requestInvertedBoost = req("afts", queryInvertedBoost, templates);
|
||||
searchQueryService.expectResultsInOrder(requestInvertedBoost, testUser, fileWithTermInName.getName(), fileWithDifferentTermInName.getName());
|
||||
}
|
||||
|
||||
@Test(groups = {TestGroup.SEARCH})
|
||||
public void testAftsQuery_expandedTemplate()
|
||||
{
|
||||
Map<String, String> templates = Map.of("_NODE", "%cm:name");
|
||||
String query = ancestorClause + " AND TYPE:'cm:content' AND ~_NODE:" + SEARCH_TERM;
|
||||
SearchRequest request = req("afts", query, templates);
|
||||
|
||||
searchQueryService.expectNodeRefsFromQuery(request, testUser, fileWithTermInName.getNodeRef());
|
||||
}
|
||||
|
||||
private FolderModel createTestParentFolder()
|
||||
{
|
||||
ContentModel contentRoot = new ContentModel("-root-");
|
||||
contentRoot.setNodeRef(contentRoot.getName());
|
||||
FolderModel parent = new FolderModel(getRandomName("templateSearchTests-"));
|
||||
return dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(contentRoot)
|
||||
.createFolder(parent);
|
||||
}
|
||||
|
||||
private ContentModel createRandomFileWithTitle(String title)
|
||||
{
|
||||
return createRandomFile(title, null, null);
|
||||
}
|
||||
|
||||
private ContentModel createRandomFileWithDescription(String description)
|
||||
{
|
||||
return createRandomFile(null, description, null);
|
||||
}
|
||||
|
||||
private ContentModel createRandomFileWithTag(String tag)
|
||||
{
|
||||
return createRandomFile(null, null, tag);
|
||||
}
|
||||
|
||||
private ContentModel createRandomFile(String title, String description, String tag)
|
||||
{
|
||||
return createFile(getRandomFile(FileType.TEXT_PLAIN), getRandomName("dummy text "), title, description, tag);
|
||||
}
|
||||
|
||||
private ContentModel createFile(String filename, String content)
|
||||
{
|
||||
return createFile(filename, content, null, null, null);
|
||||
}
|
||||
|
||||
private ContentModel createFile(String filename, String content, String title, String description, String tag)
|
||||
{
|
||||
FileModel fileModel = new FileModel(filename, FileType.TEXT_PLAIN, content);
|
||||
fileModel.setTitle(title);
|
||||
fileModel.setDescription(description);
|
||||
|
||||
// Create inside the dedicated parent folder so queries scoped with ANCESTOR will pick this up
|
||||
// (and so bootstrap / cross-class content does NOT match those queries).
|
||||
FileModel file = dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(testParentFolder)
|
||||
.createContent(fileModel);
|
||||
|
||||
if (StringUtils.isNotBlank(tag))
|
||||
{
|
||||
dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(file)
|
||||
.addTagToContent(RestTagModel.builder().tag(tag).create());
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
private ContentModel createFolder(String folderName)
|
||||
{
|
||||
return createFolder(folderName, null, null, null);
|
||||
}
|
||||
|
||||
private ContentModel createFolder(String folderName, String title, String description, String tag)
|
||||
{
|
||||
FolderModel folderModel = new FolderModel(folderName, title, description);
|
||||
|
||||
FolderModel folder = dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(testParentFolder)
|
||||
.createFolder(folderModel);
|
||||
|
||||
if (StringUtils.isNotBlank(tag))
|
||||
{
|
||||
dataContent
|
||||
.usingAdmin()
|
||||
.usingResource(folder)
|
||||
.addTagToContent(RestTagModel.builder().tag(tag).create());
|
||||
}
|
||||
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FileType;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
/**
|
||||
* Tests verifying a range of simple AFTS queries using the cm:name field.
|
||||
*/
|
||||
public class ElasticsearchTokenisationTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
private static final String ALPHABETIC_NO_SPACE = "TestFileabc.txt";
|
||||
private static final String NUMERIC_NO_SPACE = "TestFile123.txt";
|
||||
private static final String ALPHABETIC_WITH_SPACE = "TestFile abc.txt";
|
||||
private static final String NUMERIC_WITH_SPACE = "TestFile 123.txt";
|
||||
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
|
||||
@Autowired
|
||||
private DataContent dataContent;
|
||||
|
||||
@Autowired
|
||||
private DataSite dataSite;
|
||||
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
|
||||
@Autowired
|
||||
protected SearchQueryService searchQueryService;
|
||||
|
||||
private UserModel user;
|
||||
private SiteModel siteModel;
|
||||
|
||||
/**
|
||||
* Create a site containing four documents.
|
||||
*/
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
user = dataUser.createRandomTestUser();
|
||||
siteModel = dataSite.usingUser(user).createPrivateRandomSite();
|
||||
|
||||
createContent(ALPHABETIC_NO_SPACE, "", siteModel, user);
|
||||
createContent(NUMERIC_NO_SPACE, "", siteModel, user);
|
||||
createContent(ALPHABETIC_WITH_SPACE, "", siteModel, user);
|
||||
createContent(NUMERIC_WITH_SPACE, "", siteModel, user);
|
||||
}
|
||||
|
||||
/** Check that searching for "TestFileabc.txt" only returns that file. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchNoSpaceAlphabetic()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("name:\"TestFileabc.txt\""), user, ALPHABETIC_NO_SPACE);
|
||||
}
|
||||
|
||||
/** Check that searching for "TestFile abc.txt" only returns that file. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchWithSpaceAlphabetic()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("name:\"TestFile abc.txt\""), user, ALPHABETIC_WITH_SPACE);
|
||||
}
|
||||
|
||||
/** Check that searching for "TestFile123.txt" returns both files with numbers in. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchNoSpaceNumeric()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("name:\"TestFile123.txt\""), user, NUMERIC_NO_SPACE, NUMERIC_WITH_SPACE);
|
||||
}
|
||||
|
||||
/** Check that searching for "TestFile 123.txt" returns both files with numbers in. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchWithSpaceNumeric()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("name:\"TestFile 123.txt\""), user, NUMERIC_NO_SPACE, NUMERIC_WITH_SPACE);
|
||||
}
|
||||
|
||||
/** Check that searching for "TestFile" doesn't return TestFileabc.txt. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchBaseFileName()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("name:\"TestFile\""), user, NUMERIC_NO_SPACE, ALPHABETIC_WITH_SPACE, NUMERIC_WITH_SPACE);
|
||||
}
|
||||
|
||||
/** Check that searching for TestFileabc.txt with no quotes returns just that file. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchNoQuotesNoSpaceAlphabetic()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("name:TestFileabc.txt"), user, ALPHABETIC_NO_SPACE);
|
||||
}
|
||||
|
||||
/** Check that searching for TestFile123.txt with no quotes returns both files with numbers in. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchNoQuotesNoSpaceNumeric()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("name:TestFile123.txt"), user, NUMERIC_NO_SPACE, NUMERIC_WITH_SPACE);
|
||||
}
|
||||
|
||||
/** Check that searching for TestFile doesn't return TestFileabc.txt. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchBaseFileNameNoQuotes()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("name:TestFile"), user, NUMERIC_NO_SPACE, ALPHABETIC_WITH_SPACE, NUMERIC_WITH_SPACE);
|
||||
}
|
||||
|
||||
/** Check that including an escaped space (_x0020_) in double quotes means that nothing is matched. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchEncodedSpaceInQuotes()
|
||||
{
|
||||
searchQueryService.expectNoResultsFromQuery(req("name:\"TestFile_x0020_abc.txt\""), user);
|
||||
}
|
||||
|
||||
/** Check that searching for 'TestFileabc.txt' only returns that file. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchNoSpaceAlphabeticSingleQuote()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("name:'TestFileabc.txt'"), user, ALPHABETIC_NO_SPACE);
|
||||
}
|
||||
|
||||
/** Check that searching for 'TestFile abc.txt' only returns that file. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchWithSpaceAlphabeticSingleQuote()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("name:'TestFile abc.txt'"), user, ALPHABETIC_WITH_SPACE);
|
||||
}
|
||||
|
||||
/** Check that searching for 'TestFile123.txt' returns both files with numbers in. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchNoSpaceNumericSingleQuote()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("name:'TestFile123.txt'"), user, NUMERIC_NO_SPACE, NUMERIC_WITH_SPACE);
|
||||
}
|
||||
|
||||
/** Check that searching for 'TestFile 123.txt' returns both files with numbers in. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchWithSpaceNumericSingleQuote()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("name:'TestFile 123.txt'"), user, NUMERIC_NO_SPACE, NUMERIC_WITH_SPACE);
|
||||
}
|
||||
|
||||
/** Check that searching for 'TestFile' doesn't return TestFileabc.txt. */
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchBaseFileNameSingleQuote()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("name:'TestFile'"), user, NUMERIC_NO_SPACE, ALPHABETIC_WITH_SPACE, NUMERIC_WITH_SPACE);
|
||||
}
|
||||
|
||||
private FileModel createContent(String filename, String content, SiteModel site, UserModel user)
|
||||
{
|
||||
FileModel fileModel = new FileModel(filename, FileType.TEXT_PLAIN, content);
|
||||
return dataContent.usingUser(user).usingSite(site)
|
||||
.createContent(fileModel);
|
||||
}
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
import static org.alfresco.utility.report.log.Step.STEP;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.rest.repo.resource.Categories;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
|
||||
/**
|
||||
* Tests verifying indexing of secondary children and ANCESTOR and CATEGORY index in Elasticsearch.
|
||||
*/
|
||||
@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert", "PMD.MethodNamingConventions", "PMD.LocalVariableNamingConventions"}) // these are testng tests and use searchQueryService.expectResultsFromQuery for assertion
|
||||
public class NodeWithCategoryIndexingTests extends NodesSecondaryChildrenRelatedTests
|
||||
{
|
||||
|
||||
@Autowired
|
||||
private Categories categories;
|
||||
|
||||
/* A --- B (folders) \____ \ K --- L (categories) */
|
||||
@BeforeClass(alwaysRun = true)
|
||||
@Override
|
||||
public void dataPreparation()
|
||||
{
|
||||
super.dataPreparation();
|
||||
|
||||
// given
|
||||
STEP("Create nested folders in site's Document Library.");
|
||||
folders.add().nestedRandomFolders(A, B).create();
|
||||
|
||||
STEP("Create nested categories.");
|
||||
categories.add().nestedRandomCategories(K, L).create();
|
||||
|
||||
STEP("Link folders to category.");
|
||||
folders.modify(A).linkTo(categories.get(L));
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testParentQueryAgainstCategory()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that searching by PARENT and category will find one descendant node: categoryL.");
|
||||
SearchRequest query = req("PARENT:" + categories.get(K).getId());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, categories.get(L).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testParentQueryAgainstFolder()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that searching by PARENT and category will find one descendant node: folderA.");
|
||||
SearchRequest query = req("PARENT:" + categories.get(L).getId());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, folders.get(A).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testParentQueryAgainstFolderAfterCategoryDeletion()
|
||||
{
|
||||
// given
|
||||
STEP("Create nested folders in site's Document Library.");
|
||||
folders.add().randomFolder(C).create();
|
||||
|
||||
STEP("Create nested categories.");
|
||||
categories.add().randomCategory(M).create();
|
||||
|
||||
STEP("Link folders to category.");
|
||||
folders.modify(C).linkTo(categories.get(M));
|
||||
|
||||
// when
|
||||
STEP("Verify that searching by PARENT and category will find one descendant node: folderC.");
|
||||
SearchRequest query = req("PARENT:" + categories.get(M).getId());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, folders.get(C).getName());
|
||||
|
||||
// then
|
||||
STEP("Delete categoryM.");
|
||||
categories.delete(categories.get(M));
|
||||
|
||||
STEP("Verify that searching by PARENT and deleted category will find no descendant nodes.");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH, enabled = false) // re-enable after ACS-6588, ACS-6592
|
||||
public void testParentQueryAgainstFolderAfterParentCategoryDeletion()
|
||||
{
|
||||
// given
|
||||
STEP("Create nested folders in site's Document Library.");
|
||||
folders.add().randomFolder(X).create();
|
||||
|
||||
STEP("Create nested categories.");
|
||||
categories.add().nestedRandomCategories(P, Q).create();
|
||||
|
||||
STEP("Link folders to category.");
|
||||
folders.modify(X).linkTo(categories.get(Q));
|
||||
|
||||
// when
|
||||
STEP("Verify that searching by PARENT and category will find one descendant node: folderC.");
|
||||
SearchRequest query = req("PARENT:" + categories.get(Q).getId());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, folders.get(X).getName());
|
||||
|
||||
// then
|
||||
STEP("Delete categoryP.");
|
||||
categories.delete(categories.get(P));
|
||||
|
||||
STEP("Verify that searching by PARENT and deleted category will find no descendant nodes.");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSearchByPath()
|
||||
{
|
||||
// given
|
||||
String Kname = categories.get(K).getName();
|
||||
String Lname = categories.get(L).getName();
|
||||
String Aname = folders.get(A).getName();
|
||||
|
||||
// then
|
||||
STEP("Verify that searching by PATH and category will find: folderA");
|
||||
SearchRequest query = req(format("PATH:'/cm:categoryRoot/cm:generalclassifiable/cm:%s/cm:%s/cm:%s'", Kname, Lname, Aname));
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, folders.get(A).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void ensureCategoriesAreNotTransitive_lookingForExactChildren()
|
||||
{
|
||||
// given
|
||||
String Kname = categories.get(K).getName();
|
||||
String Lname = categories.get(L).getName();
|
||||
String Aname = folders.get(A).getName();
|
||||
String Bname = folders.get(B).getName();
|
||||
|
||||
// then
|
||||
STEP("Verify that searching by PATH for nested folder will return no results (Dependency to category is not transitive)");
|
||||
SearchRequest query = req(format("PATH:'/cm:categoryRoot/cm:generalclassifiable/cm:%s/cm:%s/cm:%s/cm:%s'", Kname, Lname, Aname, Bname));
|
||||
searchQueryService.expectNoResultsFromQuery(query, testUser);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void ensureCategoriesAreNotTransitive_lookingForAllDescendants()
|
||||
{
|
||||
// given
|
||||
String Kname = categories.get(K).getName();
|
||||
String Lname = categories.get(L).getName();
|
||||
|
||||
// then
|
||||
STEP("Verify that searching by PATH and category will find: folderA");
|
||||
SearchRequest query = req(format("PATH:'/cm:categoryRoot/cm:generalclassifiable/cm:%s/cm:%s//*'", Kname, Lname));
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, folders.get(A).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void ensureCategoriesAreNotTransitive_lookingForAllDescendants_byParentCategory()
|
||||
{
|
||||
// given
|
||||
String Kname = categories.get(K).getName();
|
||||
|
||||
// then
|
||||
STEP("Verify that searching by PATH and category will find: folderA");
|
||||
SearchRequest query = req(format("PATH:'/cm:categoryRoot/cm:generalclassifiable/cm:%s//*'", Kname));
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, categories.get(L).getName(), folders.get(A).getName());
|
||||
}
|
||||
}
|
||||
+368
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
import static org.alfresco.utility.report.log.Step.STEP;
|
||||
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.utility.Utility;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
|
||||
/**
|
||||
* Tests verifying indexing of secondary children and ANCESTOR index in Elasticsearch.
|
||||
*/
|
||||
@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert"}) // these are TAS E2E tests and use searchQueryService.expectResultsFromQuery for assertion
|
||||
public class NodesSecondaryAncestorIndexingTests extends NodesSecondaryChildrenRelatedTests
|
||||
{
|
||||
|
||||
private FileModel fileInP;
|
||||
|
||||
/**
|
||||
* Creates a user and a private site containing below hierarchy of folders.
|
||||
*
|
||||
* <pre>
|
||||
* Site
|
||||
* DL (Document Library)
|
||||
* += fA += fB += fC (folderC)
|
||||
* / / |
|
||||
* + + +
|
||||
* += fK += fL += fM
|
||||
* | +
|
||||
* + |
|
||||
* += fX += fY += fZ
|
||||
* += fP += file -+ fA
|
||||
* += fQ
|
||||
* += fR
|
||||
* += fS
|
||||
* </pre>
|
||||
*
|
||||
* Parent += Child - primary parent-child relationship Parent +- Child - secondary parent-child relationship
|
||||
*/
|
||||
@BeforeClass(alwaysRun = true)
|
||||
@Override
|
||||
public void dataPreparation()
|
||||
{
|
||||
super.dataPreparation();
|
||||
|
||||
// given
|
||||
STEP("Create few sets of nested folders in site's Document Library.");
|
||||
folders.add().nestedRandomFolders(A, B, C).create();
|
||||
folders.add().nestedRandomFolders(K, L, M).create();
|
||||
folders.add().nestedRandomFolders(X, Y, Z).create();
|
||||
folders.add().randomFolders(P, Q, R, S).create();
|
||||
fileInP = folders.modify(P).add().randomFile().create();
|
||||
|
||||
STEP("Create few secondary parent-child relationships.");
|
||||
folders.modify(K).add().secondaryContent(folders.get(B));
|
||||
folders.modify(X).add().secondaryContent(folders.get(K));
|
||||
folders.modify(L).add().secondaryContent(folders.get(C), folders.get(Y));
|
||||
folders.modify(M).add().secondaryContent(folders.get(C));
|
||||
folders.modify(A).add().secondaryContent(fileInP);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryAncestorWithNodeHavingOneSecondaryChild()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that searching by ANCESTOR and folderM will find one descendant node: folderC.");
|
||||
SearchRequest query = req("ANCESTOR:" + folders.get(M).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
folders.get(C).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryAncestorWithNodeHavingTwoSecondaryChildren()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that searching by ANCESTOR and folderL will find nodes: folderM, folderC, folderY and folderZ.");
|
||||
SearchRequest queryAncestorL = req("ANCESTOR:" + folders.get(L).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(queryAncestorL, testUser,
|
||||
// primary descendant
|
||||
folders.get(M).getName(),
|
||||
// secondary descendants
|
||||
folders.get(C).getName(),
|
||||
folders.get(Y).getName(),
|
||||
folders.get(Z).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryAncestorWithDocumentAsSecondaryChild()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that searching by ANCESTOR and folderA will find nodes: folderB, folderC and fileInP.");
|
||||
SearchRequest query = req("ANCESTOR:" + folders.get(A).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// primary descendants
|
||||
folders.get(B).getName(),
|
||||
folders.get(C).getName(),
|
||||
// secondary descendant
|
||||
fileInP.getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryAncestorWithNodeHavingComplexSecondaryRelationship()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that all descendant of folderX can be found.");
|
||||
SearchRequest query = req("ANCESTOR:" + folders.get(X).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// primary descendants
|
||||
folders.get(Y).getName(),
|
||||
folders.get(Z).getName(),
|
||||
// secondary descendants
|
||||
folders.get(B).getName(),
|
||||
folders.get(C).getName(),
|
||||
folders.get(K).getName(),
|
||||
folders.get(L).getName(),
|
||||
folders.get(M).getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that removing secondary parent-child relationship will result in updating ES index: ANCESTOR. Test changes below folders hierarchy:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fQ
|
||||
* +
|
||||
* |
|
||||
* += fR
|
||||
* </pre>
|
||||
*
|
||||
* into:
|
||||
*
|
||||
* <pre>
|
||||
* DL += fQ += fR
|
||||
* </pre>
|
||||
*/
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryAncestorWithDeletedSecondaryRelationship()
|
||||
{
|
||||
// given
|
||||
STEP("Add to folderQ a secondary child folderR and verify if it can be found using ANCESTOR index and secondary child node reference.");
|
||||
folders.modify(Q).add().secondaryContent(folders.get(R));
|
||||
|
||||
STEP("Verify that searching by ANCESTOR and folderQ will find secondary descendant node: folderR.");
|
||||
SearchRequest query = req("ANCESTOR:" + folders.get(Q).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// secondary descendant
|
||||
folders.get(R).getName());
|
||||
|
||||
// when
|
||||
STEP("Delete the secondary parent-child relationship between folderQ and FolderR.");
|
||||
folders.modify(Q).remove().secondaryContent(folders.get(R));
|
||||
|
||||
// then
|
||||
STEP("Verify that folderQ cannot be found by ANCESTOR and folderQ anymore.");
|
||||
searchQueryService.expectNoResultsFromQuery(query, testUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that removing a node D (fD) having a secondary children relationship will remove the relationships and update ANCESTOR index in ES. Test changes below folders hierarchy from:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fQ
|
||||
* +
|
||||
* |
|
||||
* += fE += fF
|
||||
* +
|
||||
* |
|
||||
* += fR
|
||||
* </pre>
|
||||
*
|
||||
* into:
|
||||
*
|
||||
* <pre>
|
||||
* DL += fQ += fR
|
||||
* </pre>
|
||||
*/
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryAncestorWithDeletedSecondaryParentNode()
|
||||
{
|
||||
// given
|
||||
STEP("Create two nested folders (E and F) in Document Library.");
|
||||
FolderModel folderE = folders.add().randomFolder("E").create();
|
||||
FolderModel folderF = folders.modify(folderE).add().randomFolder("F").create();
|
||||
STEP("Make folderE a secondary children of folderQ and folderR a secondary children of folderE.");
|
||||
folders.modify(Q).add().secondaryContent(folderE);
|
||||
folders.modify(folderE).add().secondaryContent(folders.get(R));
|
||||
|
||||
STEP("Verify that searching by ANCESTOR and folderQ will find its secondary descendant: folderE, folderF and folderR.");
|
||||
SearchRequest queryAncestorQ = req("ANCESTOR:" + folders.get(Q).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(queryAncestorQ, testUser,
|
||||
// secondary descendants
|
||||
folderE.getName(),
|
||||
folderF.getName(),
|
||||
folders.get(R).getName());
|
||||
|
||||
// when
|
||||
STEP("Delete folderE with its content.");
|
||||
folders.modify(folderE).delete();
|
||||
|
||||
// then
|
||||
STEP("Verify that searching by ANCESTOR and folderQ will not find any nodes.");
|
||||
searchQueryService.expectNoResultsFromQuery(queryAncestorQ, testUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that moving folderD (fD) containing secondary children from hierarchy:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fQ += fD +- fP += file
|
||||
* += fR
|
||||
* </pre>
|
||||
*
|
||||
* to:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fQ
|
||||
* += fR += fD +- fP += file
|
||||
* </pre>
|
||||
*
|
||||
* will update ANCESTOR index in ES.
|
||||
*/
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryAncestorWithMovedSecondaryParentNode()
|
||||
{
|
||||
// given
|
||||
STEP("Create folderD inside folderQ, and add folderP to D as a secondary child.");
|
||||
FolderModel folderD = folders.modify(Q).add().randomFolder("D").create();
|
||||
folders.modify(folderD).add().secondaryContent(folders.get(P));
|
||||
|
||||
STEP("Verify that searching by ANCESTOR and folderQ will find its primary and secondary descendant nodes: folderD, folderP and file.");
|
||||
SearchRequest queryAncestorQ = req("ANCESTOR:" + folders.get(Q).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(queryAncestorQ, testUser,
|
||||
// primary descendant
|
||||
folderD.getName(),
|
||||
// secondary descendants
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName());
|
||||
STEP("Verify that searching by ANCESTOR and folderR will not find any descendant nodes.");
|
||||
SearchRequest queryAncestorR = req("ANCESTOR:" + folders.get(R).getNodeRef());
|
||||
searchQueryService.expectNoResultsFromQuery(queryAncestorR, testUser);
|
||||
|
||||
// when
|
||||
STEP("Move folderD from folderQ to folderR.");
|
||||
folders.modify(folderD).moveTo(folders.get(R));
|
||||
|
||||
// then
|
||||
STEP("Verify that search result for ANCESTOR and folderQ will not find any descendant anymore.");
|
||||
searchQueryService.expectNoResultsFromQuery(queryAncestorQ, testUser);
|
||||
STEP("Verify that searching by ANCESTOR and folderR will find its primary and secondary descendant nodes: folderD, folderP and file.");
|
||||
searchQueryService.expectResultsFromQuery(queryAncestorR, testUser,
|
||||
// primary descendant
|
||||
folderD.getName(),
|
||||
// secondary descendants
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName());
|
||||
|
||||
STEP("Clean-up - delete folderD.");
|
||||
folders.modify(folderD).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that copying folder will also result in copying folder's secondary children and update ANCESTOR index in ES. Test changes below folders hierarchy:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fS += fG += fH
|
||||
* +
|
||||
* /
|
||||
* += fP += file
|
||||
* += fT
|
||||
* </pre>
|
||||
*
|
||||
* into:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fS += fG += fH
|
||||
* +
|
||||
* /
|
||||
* += fP += file
|
||||
* \
|
||||
* +
|
||||
* += fT += fG-c += fH-c
|
||||
* </pre>
|
||||
*/
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryAncestorWithCopiedSecondaryParentNode() throws InterruptedException
|
||||
{
|
||||
// given
|
||||
STEP("Create nested folders (G and H) inside folderS and folderT in Document Library. Make folderP a secondary child of folderG.");
|
||||
FolderModel folderG = folders.modify(S).add().randomFolder("G").create();
|
||||
FolderModel folderH = folders.modify(folderG).add().randomFolder("H").create();
|
||||
FolderModel folderT = folders.add().randomFolder("T").create();
|
||||
folders.modify(folderG).add().secondaryContent(folders.get(P));
|
||||
|
||||
STEP("Verify that searching by ANCESTOR and folderS will find its descendant nodes: folderG, folderH, folderP and file in P.");
|
||||
SearchRequest queryAncestorS = req("ANCESTOR:" + folders.get(S).getNodeRef());
|
||||
Utility.sleep(500, 60000, () -> searchQueryService.expectResultsFromQuery(queryAncestorS, testUser,
|
||||
// primary descendants
|
||||
folderG.getName(),
|
||||
folderH.getName(),
|
||||
// secondary descendants
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName()));
|
||||
STEP("Verify that searching by ANCESTOR and folderT will not find any nodes.");
|
||||
SearchRequest queryAncestorT = req("ANCESTOR:" + folderT.getNodeRef());
|
||||
searchQueryService.expectNoResultsFromQuery(queryAncestorT, testUser);
|
||||
|
||||
// when
|
||||
STEP("Copy folderG with its content to folderT.");
|
||||
FolderModel folderGCopy = folders.modify(folderG).copyTo(folderT);
|
||||
|
||||
// then
|
||||
STEP("Verify that searching by ANCESTOR and folderS will find its descendant nodes: folderG, folderH, folderP and file in P.");
|
||||
searchQueryService.expectResultsFromQuery(queryAncestorS, testUser,
|
||||
// primary descendants
|
||||
folderG.getName(),
|
||||
folderH.getName(),
|
||||
// secondary descendants
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName());
|
||||
STEP("Verify that searching by ANCESTOR and folderT will find its descendant nodes: folderG-copy, folderH-copy, folderP, file.");
|
||||
searchQueryService.expectResultsFromQuery(queryAncestorT, testUser,
|
||||
// primary descendants
|
||||
folderGCopy.getName(),
|
||||
folderH.getName(), // the same name as folderH-copy
|
||||
// secondary descendants
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName());
|
||||
|
||||
STEP("Clean-up - delete folderG and folderT (with G's copy).");
|
||||
folders.modify(folderG).delete();
|
||||
folders.modify(folderT).delete();
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.alfresco.utility.report.log.Step.STEP;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
|
||||
import org.alfresco.rest.core.RestWrapper;
|
||||
import org.alfresco.rest.repo.resource.Folders;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
public abstract class NodesSecondaryChildrenRelatedTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
@SuppressWarnings("PMD.OneDeclarationPerLine")
|
||||
protected static final String A = "A", B = "B", C = "C", K = "K", L = "L", M = "M", P = "P", Q = "Q", R = "R", S = "S", X = "X", Y = "Y", Z = "Z";
|
||||
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
@Autowired
|
||||
private DataSite dataSite;
|
||||
@Autowired
|
||||
protected DataContent dataContent;
|
||||
@Autowired
|
||||
protected RestWrapper restClient;
|
||||
@Autowired
|
||||
protected SearchQueryService searchQueryService;
|
||||
|
||||
protected UserModel testUser;
|
||||
protected SiteModel testSite;
|
||||
protected Folders folders;
|
||||
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
STEP("Verify environment health.");
|
||||
serverHealth.isServerReachable();
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
STEP("Create a test user and private site.");
|
||||
testUser = dataUser.createRandomTestUser();
|
||||
testSite = dataSite.usingUser(testUser).createPrivateRandomSite();
|
||||
folders = new Folders(dataContent, restClient, testUser, testSite);
|
||||
}
|
||||
|
||||
@AfterClass(alwaysRun = true)
|
||||
public void dataCleanUp()
|
||||
{
|
||||
STEP("Clean up data after tests.");
|
||||
dataSite.usingUser(testUser).deleteSite(testSite);
|
||||
dataUser.usingAdmin().deleteUser(testUser);
|
||||
}
|
||||
}
|
||||
+361
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
import static org.alfresco.utility.report.log.Step.STEP;
|
||||
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
|
||||
/**
|
||||
* Tests verifying indexing of secondary children and PARENT index in Elasticsearch.
|
||||
*/
|
||||
@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert"}) // these are TAS E2E tests and use searchQueryService.expectResultsFromQuery for assertion
|
||||
public class NodesSecondaryParentIndexingTests extends NodesSecondaryChildrenRelatedTests
|
||||
{
|
||||
|
||||
private FileModel fileInP;
|
||||
|
||||
/**
|
||||
* Creates a user and a private site containing below hierarchy of folders.
|
||||
*
|
||||
* <pre>
|
||||
* Site
|
||||
* DL (Document Library)
|
||||
* += fA += fB += fC (folderC)
|
||||
* / / |
|
||||
* + + +
|
||||
* += fK += fL += fM
|
||||
* | +
|
||||
* + |
|
||||
* += fX += fY += fZ
|
||||
* += fP += file -+ fA
|
||||
* += fQ
|
||||
* += fR
|
||||
* += fS
|
||||
* </pre>
|
||||
*
|
||||
* Parent += Child - primary parent-child relationship Parent +- Child - secondary parent-child relationship
|
||||
*/
|
||||
@BeforeClass(alwaysRun = true)
|
||||
@Override
|
||||
public void dataPreparation()
|
||||
{
|
||||
super.dataPreparation();
|
||||
|
||||
// given
|
||||
STEP("Create few sets of nested folders in site's Document Library.");
|
||||
folders.add().nestedRandomFolders(A, B, C).create();
|
||||
folders.add().nestedRandomFolders(K, L, M).create();
|
||||
folders.add().nestedRandomFolders(X, Y, Z).create();
|
||||
folders.add().randomFolders(P, Q, R, S).create();
|
||||
fileInP = folders.modify(P).add().randomFile().create();
|
||||
|
||||
STEP("Create few secondary parent-child relationships.");
|
||||
folders.modify(K).add().secondaryContent(folders.get(B));
|
||||
folders.modify(X).add().secondaryContent(folders.get(K));
|
||||
folders.modify(L).add().secondaryContent(folders.get(C), folders.get(Y));
|
||||
folders.modify(M).add().secondaryContent(folders.get(C));
|
||||
folders.modify(A).add().secondaryContent(fileInP);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryParentWithNodeHavingOneSecondaryChild()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that searching by PARENT and folderM will find node folderC.");
|
||||
SearchRequest query = req("PARENT:" + folders.get(M).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
folders.get(C).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryParentWithNodeHavingTwoSecondaryChildren()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that searching by PARENT and folderL will find nodes: folderM, FolderC and folderY.");
|
||||
SearchRequest query = req("PARENT:" + folders.get(L).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// primary child
|
||||
folders.get(M).getName(),
|
||||
// secondary children
|
||||
folders.get(C).getName(),
|
||||
folders.get(Y).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryParentWithDocumentAsSecondaryChild()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that searching by PARENT and folderA will find nodes: folderB and file.");
|
||||
SearchRequest query = req("PARENT:" + folders.get(A).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// primary child
|
||||
folders.get(B).getName(),
|
||||
// secondary child
|
||||
fileInP.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that removing secondary parent-child relationship will result in updating ES index: PARENT. Test changes below folders hierarchy:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fQ
|
||||
* +
|
||||
* |
|
||||
* += fR
|
||||
* </pre>
|
||||
*
|
||||
* into:
|
||||
*
|
||||
* <pre>
|
||||
* DL += fQ += fR
|
||||
* </pre>
|
||||
*/
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryParentWithDeletedSecondaryRelationship()
|
||||
{
|
||||
// given
|
||||
STEP("Add to folderQ a secondary child folderR and verify if it can be found using PARENT index and secondary parent node reference.");
|
||||
folders.modify(Q).add().secondaryContent(folders.get(R));
|
||||
|
||||
SearchRequest query = req("PARENT:" + folders.get(Q).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// secondary child
|
||||
folders.get(R).getName());
|
||||
|
||||
// when
|
||||
STEP("Delete the secondary parent relationship between folderQ and FolderR and verify that folderR cannot be found by PARENT and folderQ anymore.");
|
||||
folders.modify(Q).remove().secondaryContent(folders.get(R));
|
||||
|
||||
// then
|
||||
searchQueryService.expectNoResultsFromQuery(query, testUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that removing a node D (fD) having a secondary children relationship will remove the relationships and update PARENT index in ES. Test changes below folders hierarchy from:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fQ
|
||||
* +
|
||||
* |
|
||||
* += fE += fF
|
||||
* +
|
||||
* |
|
||||
* += fR
|
||||
* </pre>
|
||||
*
|
||||
* into:
|
||||
*
|
||||
* <pre>
|
||||
* DL += fQ += fR
|
||||
* </pre>
|
||||
*/
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryParentWithDeletedSecondaryParentNode()
|
||||
{
|
||||
// given
|
||||
STEP("Create two nested folders (E and F) in Document Library.");
|
||||
FolderModel folderE = folders.add().randomFolder("E").create();
|
||||
FolderModel folderF = folders.modify(folderE).add().randomFolder("F").create();
|
||||
STEP("Make folderE a secondary children of folderQ and folderR a secondary children of folderE.");
|
||||
folders.modify(Q).add().secondaryContent(folderE);
|
||||
folders.modify(folderE).add().secondaryContent(folders.get(R));
|
||||
|
||||
STEP("Verify that searching by PARENT and folderQ will find its secondary child: folderE.");
|
||||
SearchRequest queryParentQ = req("PARENT:" + folders.get(Q).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(queryParentQ, testUser,
|
||||
// secondary child
|
||||
folderE.getName());
|
||||
STEP("Verify that searching by PARENT and folderE will find its primary and secondary children: folderF and folderR.");
|
||||
SearchRequest queryParentD = req("PARENT:" + folderE.getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(queryParentD, testUser,
|
||||
// primary child
|
||||
folderF.getName(),
|
||||
// secondary child
|
||||
folders.get(R).getName());
|
||||
|
||||
// when
|
||||
STEP("Delete folderE and verify that PARENT was updated for nodes folderQ and folderR.");
|
||||
folders.modify(folderE).delete();
|
||||
|
||||
// then
|
||||
searchQueryService.expectNoResultsFromQuery(queryParentQ, testUser);
|
||||
searchQueryService.expectNoResultsFromQuery(queryParentD, testUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that moving folder D (fD) containing secondary children from hierarchy:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fQ += fD +- fP += file
|
||||
* += fR
|
||||
* </pre>
|
||||
*
|
||||
* to:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fQ
|
||||
* += fR += fD +- fP += file
|
||||
* </pre>
|
||||
*
|
||||
* will update PARENT index in ES.
|
||||
*/
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryParentWithMovedSecondaryParentNode()
|
||||
{
|
||||
// given
|
||||
STEP("Create folderD inside folderQ, and add folderP to D as a secondary child.");
|
||||
FolderModel folderD = folders.modify(Q).add().randomFolder("D").create();
|
||||
folders.modify(folderD).add().secondaryContent(folders.get(P));
|
||||
|
||||
STEP("Verify that searching by PARENT and folderD will find node folderP.");
|
||||
SearchRequest queryParentD = req("PARENT:" + folderD.getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(queryParentD, testUser,
|
||||
// secondary child
|
||||
folders.get(P).getName());
|
||||
STEP("Verify that searching by PARENT and folderQ will find node folderD.");
|
||||
SearchRequest queryParentQ = req("PARENT:" + folders.get(Q).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(queryParentQ, testUser,
|
||||
// primary child
|
||||
folderD.getName());
|
||||
|
||||
// when
|
||||
STEP("Move folderD from folderQ to folderR.");
|
||||
folders.modify(folderD).moveTo(folders.get(R));
|
||||
|
||||
// then
|
||||
STEP("Verify that search result for PARENT and folderD didn't change.");
|
||||
searchQueryService.expectResultsFromQuery(queryParentD, testUser,
|
||||
// secondary child
|
||||
folders.get(P).getName());
|
||||
STEP("Verify that searching by PARENT and folderQ doesn't return any node anymore.");
|
||||
searchQueryService.expectNoResultsFromQuery(queryParentQ, testUser);
|
||||
STEP("Verify that searching by PARENT and folderR will find node folderD.");
|
||||
SearchRequest queryParentR = req("PARENT:" + folders.get(R).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(queryParentR, testUser,
|
||||
// primary child
|
||||
folderD.getName());
|
||||
|
||||
STEP("Clean-up - delete folderD.");
|
||||
folders.modify(folderD).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that copying folder will also result in copying folder's secondary children and update PARENT index in ES. Test changes below folders hierarchy:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fS += fG += fH
|
||||
* +
|
||||
* /
|
||||
* += fP += file
|
||||
* += fT
|
||||
* </pre>
|
||||
*
|
||||
* into:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fS += fG += fH
|
||||
* +
|
||||
* /
|
||||
* += fP += file
|
||||
* \
|
||||
* +
|
||||
* += fT += fG-c += fH-c
|
||||
* </pre>
|
||||
*/
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryParentWithCopiedSecondaryParentNode()
|
||||
{
|
||||
// given
|
||||
STEP("Create nested folders (G and H) inside folderS and folderT in Document Library. Make folderP a secondary child of folderG.");
|
||||
FolderModel folderG = folders.modify(S).add().randomFolder("G").create();
|
||||
FolderModel folderH = folders.modify(folderG).add().randomFolder("H").create();
|
||||
FolderModel folderT = folders.add().randomFolder("T").create();
|
||||
folders.modify(folderG).add().secondaryContent(folders.get(P));
|
||||
|
||||
STEP("Verify that searching by PARENT and folderG will find nodes: folderH, folderP and file.");
|
||||
SearchRequest queryParentG = req("PARENT:" + folderG.getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(queryParentG, testUser,
|
||||
// primary child
|
||||
folderH.getName(),
|
||||
// secondary child
|
||||
folders.get(P).getName());
|
||||
STEP("Verify that searching by PARENT and folderS will find nodes: folderG, folderH, folderP and file.");
|
||||
SearchRequest queryParentS = req("PARENT:" + folders.get(S).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(queryParentS, testUser,
|
||||
// primary child
|
||||
folderG.getName());
|
||||
STEP("Verify that searching by PARENT and folderP will find node: file.");
|
||||
SearchRequest queryParentP = req("PARENT:" + folders.get(P).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(queryParentP, testUser,
|
||||
// primary child
|
||||
fileInP.getName());
|
||||
|
||||
// when
|
||||
STEP("Copy folderG with its content to folderT.");
|
||||
FolderModel folderGCopy = folders.modify(folderG).copyTo(folderT);
|
||||
|
||||
// then
|
||||
STEP("Verify that search result for PARENT and folderS didn't change.");
|
||||
searchQueryService.expectResultsFromQuery(queryParentS, testUser,
|
||||
// primary child
|
||||
folderG.getName());
|
||||
STEP("Verify that searching by PARENT and folderS/folderG will find nodes: folderH, folderP and file in P.");
|
||||
searchQueryService.expectResultsFromQuery(queryParentG, testUser,
|
||||
// primary child
|
||||
folderH.getName(),
|
||||
// secondary child
|
||||
folders.get(P).getName());
|
||||
STEP("Verify that folderG was copied with secondary parent-child relationship and PARENT reflects that - search by folderT/folderGCopy should find nodes: folderH, folderP and file in P.");
|
||||
SearchRequest queryParentGCopy = req("PARENT:" + folderGCopy.getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(queryParentGCopy, testUser,
|
||||
// primary child
|
||||
folderH.getName(), // name is the same as folderH-copy
|
||||
// secondary child
|
||||
folders.get(P).getName());
|
||||
STEP("Verify that this time searching by PARENT and folderP will find node: file.");
|
||||
searchQueryService.expectResultsFromQuery(queryParentP, testUser,
|
||||
// primary child
|
||||
fileInP.getName());
|
||||
|
||||
STEP("Clean-up - delete folderG and folderT (with G's copy).");
|
||||
folders.modify(folderG).delete();
|
||||
folders.modify(folderT).delete();
|
||||
}
|
||||
}
|
||||
+392
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
import static org.alfresco.utility.report.log.Step.STEP;
|
||||
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
|
||||
/**
|
||||
* Tests verifying indexing of secondary children and PATH index in Elasticsearch.
|
||||
*/
|
||||
@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert"}) // these are TAS E2E tests and use searchQueryService.expectResultsFromQuery for assertion
|
||||
public class NodesSecondaryPathIndexingTests extends NodesSecondaryChildrenRelatedTests
|
||||
{
|
||||
|
||||
private FileModel fileInP;
|
||||
|
||||
/**
|
||||
* Creates a user and a private site containing below hierarchy of folders.
|
||||
*
|
||||
* <pre>
|
||||
* Site
|
||||
* DL (Document Library)
|
||||
* += fA += fB += fC (folderC)
|
||||
* / / |
|
||||
* + + +
|
||||
* += fK += fL += fM
|
||||
* | +
|
||||
* + |
|
||||
* += fX += fY += fZ
|
||||
* += fP += file -+ fA
|
||||
* += fQ
|
||||
* += fR
|
||||
* += fS
|
||||
* </pre>
|
||||
*
|
||||
* Parent += Child - primary parent-child relationship Parent +- Child - secondary parent-child relationship
|
||||
*/
|
||||
@BeforeClass(alwaysRun = true)
|
||||
@Override
|
||||
public void dataPreparation()
|
||||
{
|
||||
super.dataPreparation();
|
||||
|
||||
// given
|
||||
STEP("Create few sets of nested folders in site's Document Library.");
|
||||
folders.add().nestedRandomFolders(A, B, C).create();
|
||||
folders.add().nestedRandomFolders(K, L, M).create();
|
||||
folders.add().nestedRandomFolders(X, Y, Z).create();
|
||||
folders.add().randomFolders(P, Q, R, S).create();
|
||||
fileInP = folders.modify(P).add().randomFile().create();
|
||||
|
||||
STEP("Create few secondary parent-child relationships.");
|
||||
folders.modify(K).add().secondaryContent(folders.get(B));
|
||||
folders.modify(X).add().secondaryContent(folders.get(K));
|
||||
folders.modify(L).add().secondaryContent(folders.get(C), folders.get(Y));
|
||||
folders.modify(M).add().secondaryContent(folders.get(C));
|
||||
folders.modify(A).add().secondaryContent(fileInP);
|
||||
|
||||
STEP("Add to folderQ a secondary child folderR.");
|
||||
folders.modify(Q).add().secondaryContent(folders.get(R));
|
||||
|
||||
// when
|
||||
STEP("Delete the secondary parent relationship between folderQ and FolderR.");
|
||||
folders.modify(Q).remove().secondaryContent(folders.get(R));
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryPathWithNodeHavingOneSecondaryChild()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that folderC can be found by secondary PATH using secondary parent folderM.");
|
||||
SearchRequest query = req("PATH:\"//cm:" + folders.get(M).getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// secondary path
|
||||
folders.get(C).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryPathWithNodeHavingOnePrimaryAndTwoSecondaryChildren()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that primary and secondary children of folderL can be found using PATH index.");
|
||||
SearchRequest query = req("PATH:\"//cm:" + folders.get(L).getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// primary path
|
||||
folders.get(M).getName(),
|
||||
// secondary path
|
||||
folders.get(C).getName(),
|
||||
folders.get(Y).getName(),
|
||||
folders.get(Z).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryPathWithNodeHavingDocumentAsSecondaryChild()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that a file being a secondary child of folderA can be found using PATH index.");
|
||||
SearchRequest query = req("PATH:\"//cm:" + folders.get(A).getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// primary path
|
||||
folders.get(B).getName(),
|
||||
folders.get(C).getName(),
|
||||
// secondary path
|
||||
fileInP.getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryPathWithNodeHavingComplexSecondaryRelationship()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that all secondary children of folderX can be found.");
|
||||
SearchRequest query = req("PATH:\"//cm:" + folders.get(X).getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// primary path
|
||||
folders.get(Y).getName(),
|
||||
folders.get(Z).getName(),
|
||||
// secondary path
|
||||
folders.get(B).getName(),
|
||||
folders.get(C).getName(),
|
||||
folders.get(K).getName(),
|
||||
folders.get(L).getName(),
|
||||
folders.get(M).getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that removing secondary parent-child relationship will result in updating ES index: PATH. Test changes below folders hierarchy:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fQ
|
||||
* +
|
||||
* |
|
||||
* += fR
|
||||
* </pre>
|
||||
*
|
||||
* into:
|
||||
*
|
||||
* <pre>
|
||||
* DL += fQ += fR
|
||||
* </pre>
|
||||
*/
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryPathWithDeletedSecondaryRelationship()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that folderR cannot be found by PATH and folderQ anymore.");
|
||||
SearchRequest query = req("PATH:\"//cm:" + folders.get(Q).getName() + "//*\"");
|
||||
searchQueryService.expectNoResultsFromQuery(query, testUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that removing a node D (fD) having a secondary children relationship will remove the relationships and update PATH index in ES. Test changes below folders hierarchy from:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fQ
|
||||
* +
|
||||
* |
|
||||
* += fE += fF
|
||||
* +
|
||||
* |
|
||||
* += fR
|
||||
* </pre>
|
||||
*
|
||||
* into:
|
||||
*
|
||||
* <pre>
|
||||
* DL += fQ += fR
|
||||
* </pre>
|
||||
*/
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryPathWithDeletedSecondaryParentNode()
|
||||
{
|
||||
// given
|
||||
STEP("Create two nested folders (E and F) in Document Library.");
|
||||
FolderModel folderE = folders.add().randomFolder("E").create();
|
||||
FolderModel folderF = folders.modify(folderE).add().randomFolder("F").create();
|
||||
STEP("Make folderE a secondary children of folderQ and folderR a secondary children of folderE.");
|
||||
folders.modify(Q).add().secondaryContent(folderE);
|
||||
folders.modify(folderE).add().secondaryContent(folders.get(R));
|
||||
|
||||
STEP("Verify that searching by PATH and folderQ will find nodes: folderE, folderF and folderR.");
|
||||
SearchRequest queryPathQ = req("PATH:\"//cm:" + folders.get(Q).getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(queryPathQ, testUser,
|
||||
// secondary path
|
||||
folderE.getName(),
|
||||
folderF.getName(),
|
||||
folders.get(R).getName());
|
||||
STEP("Verify that searching by PATH and folderE will find its primary and secondary children: folderF and folderR.");
|
||||
SearchRequest queryPathD = req("PATH:\"//cm:" + folderE.getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(queryPathD, testUser,
|
||||
// primary path
|
||||
folderF.getName(),
|
||||
// secondary path
|
||||
folders.get(R).getName());
|
||||
|
||||
// when
|
||||
STEP("Delete folderE and verify that PATH was updated for nodes folderQ and folderR.");
|
||||
folders.modify(folderE).delete();
|
||||
|
||||
// then
|
||||
searchQueryService.expectNoResultsFromQuery(queryPathQ, testUser);
|
||||
searchQueryService.expectNoResultsFromQuery(queryPathD, testUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that moving folder D (fD) containing secondary children from hierarchy:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fQ += fD +- fP += file
|
||||
* += fR
|
||||
* </pre>
|
||||
*
|
||||
* to:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fQ
|
||||
* += fR += fD +- fP += file
|
||||
* </pre>
|
||||
*
|
||||
* will update PATH index in ES.
|
||||
*/
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryPathWithMovedSecondaryParentNode()
|
||||
{
|
||||
// given
|
||||
STEP("Create folderD inside folderQ, and add folderP as a secondary child.");
|
||||
FolderModel folderD = folders.modify(Q).add().randomFolder("D").create();
|
||||
folders.modify(folderD).add().secondaryContent(folders.get(P));
|
||||
folders.modify(M).add().secondaryContent(folderD);
|
||||
|
||||
STEP("Verify that searching by PATH and folderD will find nodes: folderP and file.");
|
||||
SearchRequest queryPathD = req("PATH:\"//cm:" + folderD.getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(queryPathD, testUser,
|
||||
// secondary path
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName());
|
||||
STEP("Verify that searching by PATH and folderQ will find nodes: folderD, folderP and file.");
|
||||
SearchRequest queryPathQ = req("PATH:\"//cm:" + folders.get(Q).getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(queryPathQ, testUser,
|
||||
// primary path
|
||||
folderD.getName(),
|
||||
// secondary path
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName());
|
||||
|
||||
// when
|
||||
STEP("Move folderD from folderQ to folderR.");
|
||||
folders.modify(folderD).moveTo(folders.get(R));
|
||||
|
||||
// then
|
||||
STEP("Verify that search result for PATH and folderD didn't change.");
|
||||
searchQueryService.expectResultsFromQuery(queryPathD, testUser,
|
||||
// secondary path
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName());
|
||||
STEP("Verify that searching by PATH and folderQ doesn't return any node anymore.");
|
||||
searchQueryService.expectNoResultsFromQuery(queryPathQ, testUser);
|
||||
STEP("Verify that searching by PATH and folderR will find nodes: folderD, folderP and file.");
|
||||
SearchRequest queryPathR = req("PATH:\"//cm:" + folders.get(R).getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(queryPathR, testUser,
|
||||
// primary path
|
||||
folderD.getName(),
|
||||
// secondary path
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName());
|
||||
|
||||
STEP("Clean-up - delete folderD.");
|
||||
folders.modify(folderD).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that copying folder will also result in copying folder's secondary children and update PATH index in ES. Test changes below folders hierarchy:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fS += fG += fH
|
||||
* +
|
||||
* /
|
||||
* += fP += file
|
||||
* += fT
|
||||
* </pre>
|
||||
*
|
||||
* into:
|
||||
*
|
||||
* <pre>
|
||||
* DL
|
||||
* += fS += fG += fH
|
||||
* +
|
||||
* /
|
||||
* += fP += file
|
||||
* \
|
||||
* +
|
||||
* += fT += fG-c += fH-c
|
||||
* </pre>
|
||||
*/
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryParentWithCopiedSecondaryParentNode()
|
||||
{
|
||||
// given
|
||||
STEP("Create nested folders (G and H) inside folderS and folderT in Document Library. Make folderP a secondary child of folderG.");
|
||||
FolderModel folderG = folders.modify(S).add().randomFolder("G").create();
|
||||
FolderModel folderH = folders.modify(folderG).add().randomFolder("H").create();
|
||||
FolderModel folderT = folders.add().randomFolder("T").create();
|
||||
folders.modify(folderG).add().secondaryContent(folders.get(P));
|
||||
|
||||
STEP("Verify that searching by PATH and folderG will find nodes: folderH, folderP and file.");
|
||||
SearchRequest queryPathG = req("PATH:\"//cm:" + folderG.getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(queryPathG, testUser,
|
||||
// primary path
|
||||
folderH.getName(),
|
||||
// secondary path
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName());
|
||||
STEP("Verify that searching by PATH and folderS will find nodes: folderG, folderH, folderP and file.");
|
||||
SearchRequest queryPathS = req("PATH:\"//cm:" + folders.get(S).getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(queryPathS, testUser,
|
||||
// primary path
|
||||
folderG.getName(),
|
||||
folderH.getName(),
|
||||
// secondary path
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName());
|
||||
|
||||
// when
|
||||
STEP("Copy folderG with its content to folderT.");
|
||||
FolderModel folderGCopy = folders.modify(folderG).copyTo(folderT);
|
||||
|
||||
// then
|
||||
STEP("Verify that search result for PATH and folderS didn't change.");
|
||||
searchQueryService.expectResultsFromQuery(queryPathS, testUser,
|
||||
// primary path
|
||||
folderH.getName(),
|
||||
folderG.getName(),
|
||||
// secondary path
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName());
|
||||
STEP("Verify that searching by PATH and folderS/folderG will find nodes: folderH, folderP and file in P.");
|
||||
SearchRequest queryPathSG = req("PATH:\"//cm:" + folders.get(S).getName() + "/cm:" + folderG.getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(queryPathSG, testUser,
|
||||
// primary path
|
||||
folderH.getName(),
|
||||
// secondary path
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName());
|
||||
STEP("Verify that folderG was copied with secondary parent-child relationship and PATH reflects that - search by folderT/folderGCopy should find nodes: folderH, folderP and file in P.");
|
||||
SearchRequest queryPathTGCopy = req("PATH:\"//cm:" + folderT.getName() + "/cm:" + folderGCopy.getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(queryPathTGCopy, testUser,
|
||||
// primary path
|
||||
folderH.getName(), // the same name as folderH-copy
|
||||
// secondary path
|
||||
folders.get(P).getName(),
|
||||
fileInP.getName());
|
||||
|
||||
STEP("Clean-up - delete folderG and folderT (with G's copy).");
|
||||
folders.modify(folderG).delete();
|
||||
folders.modify(folderT).delete();
|
||||
}
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
import static org.alfresco.utility.report.log.Step.STEP;
|
||||
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.utility.Utility;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
|
||||
@SuppressWarnings({"PMD.JUnitTestsShouldIncludeAssert"}) // these are TAS E2E tests and use searchQueryService.expectResultsFromQuery for assertion
|
||||
public class PathFieldsIndexingTests extends NodesSecondaryChildrenRelatedTests
|
||||
{
|
||||
/**
|
||||
* Creates a user and a private site containing below hierarchy of folders.
|
||||
*
|
||||
* <pre>
|
||||
* Site
|
||||
* DL (Document Library)
|
||||
* += fA += fB += fC (folderC)
|
||||
* / / |
|
||||
* + + +
|
||||
* += fK += fL += fM
|
||||
* | +
|
||||
* + |
|
||||
* += fX += fY += fZ
|
||||
* </pre>
|
||||
*
|
||||
* Parent += Child - primary parent-child relationship Parent +- Child - secondary parent-child relationship
|
||||
*/
|
||||
@BeforeClass(alwaysRun = true)
|
||||
@Override
|
||||
public void dataPreparation()
|
||||
{
|
||||
super.dataPreparation();
|
||||
|
||||
// given
|
||||
STEP("Create some nested folders.");
|
||||
folders.add().nestedRandomFolders(A, B, C).create();
|
||||
folders.add().nestedRandomFolders(K, L, M).create();
|
||||
folders.add().nestedRandomFolders(X, Y, Z).create();
|
||||
|
||||
STEP("Add some extra secondary parent relationships.");
|
||||
folders.modify(K).add().secondaryContent(folders.get(B));
|
||||
folders.modify(X).add().secondaryContent(folders.get(K));
|
||||
folders.modify(L).add().secondaryContent(folders.get(C), folders.get(Y));
|
||||
folders.modify(M).add().secondaryContent(folders.get(C));
|
||||
|
||||
STEP("Wait for the batch indexer to index the last secondary association (M +- C).");
|
||||
SearchRequest indexingProbe = req("PARENT:" + folders.get(M).getNodeRef());
|
||||
try
|
||||
{
|
||||
Utility.sleep(500, 60000, () -> searchQueryService.expectResultsFromQuery(
|
||||
indexingProbe, testUser, folders.get(C).getName()));
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
Thread.currentThread().interrupt();
|
||||
throw new AssertionError("Interrupted while waiting for batch indexer", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testPrimaryParentField()
|
||||
{
|
||||
STEP("Check that only M has L as a primary parent.");
|
||||
SearchRequest query = req("PRIMARYPARENT:" + folders.get(L).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
folders.get(M).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testParentFieldIncludesSecondaryParents()
|
||||
{
|
||||
STEP("Check that three nodes have L as a primary or secondary parent.");
|
||||
SearchRequest query = req("PARENT:" + folders.get(L).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
folders.get(C).getName(), folders.get(M).getName(), folders.get(Y).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testAncestorFieldIncludesSecondaryAssociations()
|
||||
{
|
||||
STEP("Check which nodes have K as an ancestor.");
|
||||
SearchRequest query = req("ANCESTOR:" + folders.get(K).getNodeRef());
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
folders.get(B).getName(), folders.get(C).getName(), folders.get(L).getName(), folders.get(M).getName(),
|
||||
folders.get(Y).getName(), folders.get(Z).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryPathWithNodeHavingOneSecondaryChild()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that folderC can be found by secondary PATH using secondary parent folderM.");
|
||||
SearchRequest query = req("PATH:\"//cm:" + folders.get(M).getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// secondary path
|
||||
folders.get(C).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryPathWithNodeHavingOnePrimaryAndTwoSecondaryChildren()
|
||||
{
|
||||
// then
|
||||
STEP("Verify that primary and secondary children of folderL can be found using PATH index.");
|
||||
SearchRequest query = req("PATH:\"//cm:" + folders.get(L).getName() + "//*\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// primary path
|
||||
folders.get(M).getName(),
|
||||
// secondary path
|
||||
folders.get(C).getName(),
|
||||
folders.get(Y).getName(),
|
||||
folders.get(Z).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testQueryThroughSecondaryPath()
|
||||
{
|
||||
// then
|
||||
STEP("Verify we can get direct children of a secondary paths (X-K=L) referenced in a query.");
|
||||
SearchRequest query = req("PATH:\"//cm:" + folders.get(X).getName() + "//cm:" + folders.get(L).getName() + "/*\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// primary path from L
|
||||
folders.get(M).getName(),
|
||||
// secondary path from L
|
||||
folders.get(C).getName(),
|
||||
folders.get(Y).getName());
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void testSecondaryPathWithNodeHavingComplexSecondaryRelationship()
|
||||
{
|
||||
// then
|
||||
STEP("Verify we can find all secondary descendents (excluding direct children) of folderX.");
|
||||
SearchRequest query = req("PATH:\"//cm:" + folders.get(X).getName() + "//*//*\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser,
|
||||
// primary path
|
||||
folders.get(Z).getName(),
|
||||
// secondary path
|
||||
folders.get(Y).getName(), // Y _is_ a direct primary child, but this query should only find it via the secondary path.
|
||||
folders.get(B).getName(),
|
||||
folders.get(C).getName(),
|
||||
folders.get(L).getName(),
|
||||
folders.get(M).getName());
|
||||
}
|
||||
}
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
|
||||
import java.util.Arrays;
|
||||
import jakarta.json.Json;
|
||||
import jakarta.json.JsonObject;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.elasticsearch.utility.ElasticsearchRESTHelper;
|
||||
import org.alfresco.rest.core.RestWrapper;
|
||||
import org.alfresco.rest.model.RestCategoryModel;
|
||||
import org.alfresco.rest.model.RestNodeBodyMoveCopyModel;
|
||||
import org.alfresco.rest.model.RestNodeModel;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.ContentModel;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
import org.alfresco.utility.report.log.Step;
|
||||
|
||||
/**
|
||||
* Tests to check that paths are updated correctly.
|
||||
*/
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializer.class)
|
||||
public class PathUpdateTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
@Autowired
|
||||
protected RestWrapper restClient;
|
||||
@Autowired
|
||||
private ElasticsearchRESTHelper helper;
|
||||
@Autowired
|
||||
protected SearchQueryService searchQueryService;
|
||||
|
||||
private UserModel testUser;
|
||||
private SiteModel testSite;
|
||||
|
||||
/**
|
||||
* Create a user and a private site containing some nested folders with a document in.
|
||||
*/
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
serverHealth.isServerReachable();
|
||||
serverHealth.assertServerIsOnline();
|
||||
|
||||
Step.STEP("Create a test user and private site.");
|
||||
testUser = dataUser.createRandomTestUser();
|
||||
testSite = helper.createPrivateSite(testUser);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeFileNameUpdatesPath()
|
||||
{
|
||||
Step.STEP("Create a file in the site.");
|
||||
FileModel testFile = helper.createFileInSite(testUser, testSite);
|
||||
|
||||
Step.STEP("Update the filename.");
|
||||
RestNodeModel updatedFile = renameNode(testFile);
|
||||
|
||||
Step.STEP("Check the path is updated");
|
||||
SearchRequest query = req("PATH:\"" + pathInSite(testSite, updatedFile.getName()) + "\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, updatedFile.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeFileParentUpdatesPath()
|
||||
{
|
||||
Step.STEP("Create a file next to a folder.");
|
||||
FileModel testFile = helper.createFileInSite(testUser, testSite);
|
||||
FolderModel testFolder = helper.createFolderInSite(testUser, testSite);
|
||||
|
||||
Step.STEP("Move the file into the folder and check the path is updated.");
|
||||
moveNode(testFile, testFolder);
|
||||
|
||||
Step.STEP("Check the path is updated");
|
||||
SearchRequest query = req("PATH:\"" + pathInSite(testSite, testFolder.getName(), testFile.getName()) + "\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFile.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeFolderNameUpdatesPath()
|
||||
{
|
||||
Step.STEP("Create a file in a folder.");
|
||||
FolderModel testFolder = helper.createFolderInSite(testUser, testSite);
|
||||
FileModel testFile = helper.createFileInFolder(testUser, testFolder);
|
||||
|
||||
Step.STEP("Update the folder's name.");
|
||||
RestNodeModel updatedFolder = renameNode(testFolder);
|
||||
|
||||
Step.STEP("Check the path is updated");
|
||||
SearchRequest query = req("PATH:\"" + pathInSite(testSite, updatedFolder.getName(), testFile.getName()) + "\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFile.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeFolderParentUpdatesPath()
|
||||
{
|
||||
Step.STEP("Create two folders with a file in the first.");
|
||||
FolderModel firstFolder = helper.createFolderInSite(testUser, testSite);
|
||||
FolderModel secondFolder = helper.createFolderInSite(testUser, testSite);
|
||||
FileModel testFile = helper.createFileInFolder(testUser, firstFolder);
|
||||
|
||||
Step.STEP("Move the first folder into the second.");
|
||||
moveNode(firstFolder, secondFolder);
|
||||
|
||||
Step.STEP("Check the path is updated");
|
||||
SearchRequest query = req("PATH:\"" + pathInSite(testSite, secondFolder.getName(), firstFolder.getName(), testFile.getName()) + "\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFile.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeCategoriesUpdatesPath()
|
||||
{
|
||||
Step.STEP("Create two categories and a file in the first.");
|
||||
RestCategoryModel firstCategory = helper.createCategory();
|
||||
RestCategoryModel secondCategory = helper.createCategory();
|
||||
FileModel testFile = helper.createFileInSite(testUser, testSite);
|
||||
helper.linkToCategory(testUser, testFile, firstCategory);
|
||||
|
||||
Step.STEP("Remove the file from the first category and add it to the second.");
|
||||
helper.unlinkFromCategory(testUser, testFile, firstCategory);
|
||||
helper.linkToCategory(testUser, testFile, secondCategory);
|
||||
|
||||
Step.STEP("Check there is no path for the first category.");
|
||||
SearchRequest query = req("PATH:\"" + categoryPath(firstCategory.getName(), testFile.getName()) + "\"");
|
||||
searchQueryService.expectNoResultsFromQuery(query, testUser);
|
||||
|
||||
Step.STEP("Check there is a path for the second category.");
|
||||
query = req("PATH:\"" + categoryPath(secondCategory.getName(), testFile.getName()) + "\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFile.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeCategoryNameUpdatesPath()
|
||||
{
|
||||
Step.STEP("Create a category and a file in it.");
|
||||
RestCategoryModel category = helper.createCategory();
|
||||
FileModel testFile = helper.createFileInSite(testUser, testSite);
|
||||
helper.linkToCategory(testUser, testFile, category);
|
||||
|
||||
Step.STEP("Update the name of the category.");
|
||||
String newCategoryName = category.getName() + "_updated";
|
||||
restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI()
|
||||
.usingCategory(category)
|
||||
.updateCategory(RestCategoryModel.builder().name(newCategoryName).create());
|
||||
|
||||
Step.STEP("Check there is a path with the updated category name.");
|
||||
SearchRequest query = req("PATH:\"" + categoryPath(newCategoryName, testFile.getName()) + "\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFile.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeCategoryPathUpdatesPath()
|
||||
{
|
||||
Step.STEP("Create two nested categories and assign a file to the child.");
|
||||
RestCategoryModel parentCategory = helper.createCategory();
|
||||
RestCategoryModel childCategory = helper.createCategory(parentCategory);
|
||||
FileModel testFile = helper.createFileInSite(testUser, testSite);
|
||||
helper.linkToCategory(testUser, testFile, parentCategory, childCategory);
|
||||
|
||||
Step.STEP("Update the parent category name and check the file's paths are updated.");
|
||||
String newCategoryName = parentCategory.getName() + "_updated";
|
||||
restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI()
|
||||
.usingCategory(parentCategory)
|
||||
.updateCategory(RestCategoryModel.builder().name(newCategoryName).create());
|
||||
|
||||
Step.STEP("Check there is a path with the updated category name.");
|
||||
SearchRequest query = req("PATH:\"" + categoryPath(newCategoryName, childCategory.getName(), testFile.getName()) + "\"");
|
||||
searchQueryService.expectResultsFromQuery(query, testUser, testFile.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename the specified node to have "_updated" on the end.
|
||||
*
|
||||
* @param node
|
||||
* The node to update.
|
||||
* @return The updated node.
|
||||
*/
|
||||
private RestNodeModel renameNode(ContentModel node)
|
||||
{
|
||||
String newName = node.getName() + "_updated";
|
||||
JsonObject renameJson = Json.createObjectBuilder().add("properties",
|
||||
Json.createObjectBuilder().add("cm:name", newName)).build();
|
||||
return restClient.authenticateUser(testUser).withCoreAPI().usingNode(node).updateNode(renameJson.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the specified node to a folder.
|
||||
*
|
||||
* @param node
|
||||
* The node to move.
|
||||
* @param targetFolder
|
||||
* The folder to move the node to.
|
||||
* @return The updated node.
|
||||
*/
|
||||
private RestNodeModel moveNode(ContentModel node, FolderModel targetFolder)
|
||||
{
|
||||
RestNodeBodyMoveCopyModel moveBody = new RestNodeBodyMoveCopyModel();
|
||||
moveBody.setTargetParentId(targetFolder.getNodeRef());
|
||||
return restClient.authenticateUser(testUser).withCoreAPI().usingNode(node).move(moveBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a path to a file or folder in a site.
|
||||
*
|
||||
* @param site
|
||||
* The site object.
|
||||
* @param documentLibraryNames
|
||||
* The list of names of nodes from the document library to the target file or folder.
|
||||
* @return An absolute path suitable for use in a path query.
|
||||
*/
|
||||
private String pathInSite(SiteModel site, String... documentLibraryNames)
|
||||
{
|
||||
return "/app:company_home/st:sites/cm:" + site.getId() + "/cm:documentLibrary/cm:" + stream(documentLibraryNames).collect(joining("/cm:"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a path to a file or folder via the category hierarchy.
|
||||
*
|
||||
* @param nodeNames
|
||||
* The ordered list of node names from the root category to the node that was categorised.
|
||||
* @return An absolute path through the categories to the specified node.
|
||||
*/
|
||||
private String categoryPath(String... nodeNames)
|
||||
{
|
||||
return "/cm:categoryRoot/cm:generalclassifiable/cm:" + Arrays.stream(nodeNames).collect(joining("/cm:"));
|
||||
}
|
||||
}
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import org.alfresco.rest.core.RestWrapper;
|
||||
import org.alfresco.rest.search.RestRequestQueryModel;
|
||||
import org.alfresco.rest.search.RestRequestTemplatesModel;
|
||||
import org.alfresco.rest.search.SearchNodeModel;
|
||||
import org.alfresco.rest.search.SearchRequest;
|
||||
import org.alfresco.rest.search.SearchResponse;
|
||||
import org.alfresco.utility.Utility;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
|
||||
/** A class providing methods for testing search queries. */
|
||||
public class SearchQueryService
|
||||
{
|
||||
/** Maximum time to allow for search query to return correct results. */
|
||||
private static final int MAX_TIME = 10000;
|
||||
@Autowired
|
||||
private RestWrapper client;
|
||||
|
||||
/** Assert that the query returns no results. */
|
||||
public void expectNoResultsFromQuery(SearchRequest searchRequest, UserModel testUser)
|
||||
{
|
||||
expectResultsFromQuery(searchRequest, testUser);
|
||||
}
|
||||
|
||||
/** Assert that the query returns something, without checking exactly what it returns. */
|
||||
public void expectSomeResultsFromQuery(SearchRequest searchRequest, UserModel testUser)
|
||||
{
|
||||
Consumer<SearchResponse> assertNotEmpty = searchResponse -> assertFalse(searchResponse.isEmpty());
|
||||
expectResultsFromQuery(searchRequest, testUser, assertNotEmpty);
|
||||
}
|
||||
|
||||
public void expectResultsInOrder(SearchRequest searchRequest, UserModel user, boolean isAscending, String... expected)
|
||||
{
|
||||
Consumer<SearchResponse> response = searchResponse -> assertNamesInOrder(searchResponse, isAscending, expected);
|
||||
expectResultsFromQuery(searchRequest, user, response);
|
||||
}
|
||||
|
||||
public void expectResultsInOrder(SearchRequest searchRequest, UserModel user, String... expectedOrder)
|
||||
{
|
||||
Consumer<SearchResponse> response = searchResponse -> assertNamesInOrder(searchResponse, expectedOrder);
|
||||
expectResultsFromQuery(searchRequest, user, response);
|
||||
}
|
||||
|
||||
public void expectResultsStartingWithOneOf(SearchRequest searchRequest, UserModel user, String... expected)
|
||||
{
|
||||
Consumer<SearchResponse> response = searchResponse -> {
|
||||
List<String> expectedFirstElements = List.of(expected);
|
||||
String actualFirstElement = searchResponse.getEntries().stream()
|
||||
.map(SearchNodeModel::getModel)
|
||||
.map(SearchNodeModel::getName)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
assertTrue(expectedFirstElements.contains(actualFirstElement), "Unexpected search results - actual first element: " + actualFirstElement + ", expected one of: " + expectedFirstElements + " |");
|
||||
};
|
||||
expectResultsFromQuery(searchRequest, user, response);
|
||||
}
|
||||
|
||||
public void expectResultsFromQuery(SearchRequest searchRequest, UserModel user, String... expected)
|
||||
{
|
||||
Consumer<SearchResponse> assertNames = searchResponse -> assertNames(searchResponse, expected);
|
||||
expectResultsFromQuery(searchRequest, user, assertNames);
|
||||
}
|
||||
|
||||
/** Check that the specified results are all included in the result set. */
|
||||
public void expectResultsInclude(SearchRequest searchRequest, UserModel user, String... expected)
|
||||
{
|
||||
Consumer<SearchResponse> assertNames = searchResponse -> assertNamesInclude(searchResponse, expected);
|
||||
expectResultsFromQuery(searchRequest, user, assertNames);
|
||||
}
|
||||
|
||||
public void expectNodeTypesFromQuery(SearchRequest searchRequest, UserModel user, String... expected)
|
||||
{
|
||||
Consumer<SearchResponse> assertNodeTypes = searchResponse -> assertNodeTypes(searchResponse, expected);
|
||||
expectResultsFromQuery(searchRequest, user, assertNodeTypes);
|
||||
}
|
||||
|
||||
public void expectNodeRefsFromQuery(SearchRequest searchRequest, UserModel user, String... expectedNodeRefs)
|
||||
{
|
||||
Consumer<SearchResponse> assertNames = searchResponse -> assertNodeRefs(searchResponse, expectedNodeRefs);
|
||||
expectResultsFromQuery(searchRequest, user, assertNames);
|
||||
}
|
||||
|
||||
public void expectAllResultsFromQuery(SearchRequest searchRequest, UserModel user, Predicate<SearchNodeModel> assertionMethod)
|
||||
{
|
||||
Function<SearchNodeModel, String> failureMessage = searchNodeModel -> "'" + searchNodeModel.getName() + "' did not satisfy predicate.";
|
||||
expectAllResultsFromQuery(searchRequest, user, assertionMethod, failureMessage);
|
||||
}
|
||||
|
||||
public void expectAllResultsFromQuery(SearchRequest searchRequest, UserModel user, Predicate<SearchNodeModel> assertionMethod, Function<SearchNodeModel, String> failureMessageFunction)
|
||||
{
|
||||
expectResultsFromQuery(searchRequest, user, searchResponse -> assertAllSearchResults(searchResponse, assertionMethod, failureMessageFunction));
|
||||
}
|
||||
|
||||
public void expectTotalHitsFromQuery(SearchRequest searchRequest, UserModel user, int expected)
|
||||
{
|
||||
expectResultsFromQuery(searchRequest, user, searchResponse -> assertTotalHitsResults(searchResponse, expected));
|
||||
}
|
||||
|
||||
private void expectResultsFromQuery(SearchRequest searchRequest, UserModel user, Consumer<SearchResponse> assertionMethod)
|
||||
{
|
||||
try
|
||||
{
|
||||
Utility.sleep(1000, MAX_TIME, () -> {
|
||||
SearchResponse response = client.authenticateUser(user)
|
||||
.withSearchAPI()
|
||||
.search(searchRequest);
|
||||
client.assertStatusCodeIs(HttpStatus.OK);
|
||||
assertionMethod.accept(response);
|
||||
});
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
fail("InterruptedException received while waiting for results.");
|
||||
}
|
||||
}
|
||||
|
||||
public void expectErrorFromQuery(SearchRequest searchRequest, org.alfresco.utility.model.UserModel user,
|
||||
HttpStatus expectedStatusCode, String containsErrorString)
|
||||
{
|
||||
client.authenticateUser(user).withSearchAPI().search(searchRequest);
|
||||
client.assertStatusCodeIs(expectedStatusCode);
|
||||
client.assertLastError().containsSummary(containsErrorString);
|
||||
}
|
||||
|
||||
private void assertNodeRefs(SearchResponse actual, String... expected)
|
||||
{
|
||||
Set<String> result = actual.getEntries().stream()
|
||||
.map(SearchNodeModel::getModel)
|
||||
.map(SearchNodeModel::getId)
|
||||
.collect(Collectors.toSet());
|
||||
Set<String> expectedList = Sets.newHashSet(expected);
|
||||
assertEquals(result, expectedList, "Unexpected search results - got " + result + " expected " + expectedList);
|
||||
}
|
||||
|
||||
private void assertNames(SearchResponse actual, String... expected)
|
||||
{
|
||||
Set<String> result = actual.getEntries().stream()
|
||||
.map(SearchNodeModel::getModel)
|
||||
.map(SearchNodeModel::getName)
|
||||
.collect(Collectors.toSet());
|
||||
Set<String> expectedList = Sets.newHashSet(expected);
|
||||
assertEquals(result, expectedList, "Unexpected search results - got " + result + " expected " + expectedList);
|
||||
}
|
||||
|
||||
/** Check that the given names are included in the result set. */
|
||||
private void assertNamesInclude(SearchResponse actual, String... expected)
|
||||
{
|
||||
Set<String> expectedSet = Sets.newHashSet(expected);
|
||||
// Filter to only include results that were expected.
|
||||
Set<String> filteredResults = actual.getEntries().stream()
|
||||
.map(SearchNodeModel::getModel)
|
||||
.map(SearchNodeModel::getName)
|
||||
.filter(name -> expectedSet.contains(name))
|
||||
.collect(Collectors.toSet());
|
||||
assertEquals(filteredResults, expectedSet, "Did not receive all the expected search results - got " + filteredResults + " from expected set " + expectedSet);
|
||||
}
|
||||
|
||||
private List<String> orderNames(boolean isAscending, String... filename)
|
||||
{
|
||||
List<String> orderedNames = Arrays.stream(filename).sorted().collect(Collectors.toList());
|
||||
if (!isAscending)
|
||||
{
|
||||
orderedNames = orderedNames.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
|
||||
}
|
||||
return orderedNames;
|
||||
}
|
||||
|
||||
private void assertNamesInOrder(SearchResponse actual, boolean isAscending, String... expected)
|
||||
{
|
||||
List<String> expectedInOrder = orderNames(isAscending, expected);
|
||||
List<String> result = actual.getEntries().stream()
|
||||
.map(SearchNodeModel::getModel)
|
||||
.map(SearchNodeModel::getName)
|
||||
.collect(Collectors.toList());
|
||||
assertEquals(result, expectedInOrder, "Unexpected search results - got " + result + " expected " + expectedInOrder);
|
||||
}
|
||||
|
||||
private void assertNamesInOrder(SearchResponse actual, String... expectedOrder)
|
||||
{
|
||||
List<String> expectedInOrder = List.of(expectedOrder);
|
||||
List<String> result = actual.getEntries().stream()
|
||||
.map(SearchNodeModel::getModel)
|
||||
.map(SearchNodeModel::getName)
|
||||
.toList();
|
||||
assertEquals(result, expectedInOrder, "Unexpected search results - got " + result + " expected " + expectedInOrder);
|
||||
}
|
||||
|
||||
private void assertNodeTypes(SearchResponse actual, String... expected)
|
||||
{
|
||||
Set<String> result = actual.getEntries().stream()
|
||||
.map(SearchNodeModel::getModel)
|
||||
.map(SearchNodeModel::getNodeType)
|
||||
.collect(Collectors.toSet());
|
||||
Set<String> expectedList = Sets.newHashSet(expected);
|
||||
assertEquals(result, expectedList, "Unexpected search results - got " + result + " expected " + expectedList);
|
||||
}
|
||||
|
||||
private void assertAllSearchResults(SearchResponse actual, Predicate<SearchNodeModel> assertion, Function<SearchNodeModel, String> failureMessageFunction)
|
||||
{
|
||||
String result = actual.getEntries().stream()
|
||||
.map(SearchNodeModel::getModel)
|
||||
.filter(Predicate.not(assertion))
|
||||
.map(failureMessageFunction)
|
||||
.collect(Collectors.joining("\n"));
|
||||
assertTrue(result.isEmpty(), "assertAllSearchResults failed with these issues:\n" + result);
|
||||
}
|
||||
|
||||
private void assertTotalHitsResults(SearchResponse actual, int expected)
|
||||
{
|
||||
int totalItems = actual.getPagination().getTotalItems();
|
||||
assertEquals(totalItems, expected, "Unexpected totalItems results - got " + totalItems + " expected " + expected);
|
||||
}
|
||||
|
||||
public static SearchRequest req(String query)
|
||||
{
|
||||
return req(null, query);
|
||||
}
|
||||
|
||||
public static SearchRequest req(String language, String query)
|
||||
{
|
||||
RestRequestQueryModel restRequestQueryModel = new RestRequestQueryModel();
|
||||
restRequestQueryModel.setQuery(query);
|
||||
Optional.ofNullable(language).ifPresent(restRequestQueryModel::setLanguage);
|
||||
return new SearchRequest(restRequestQueryModel);
|
||||
}
|
||||
|
||||
public static SearchRequest req(String language, String query, Map<String, String> templates)
|
||||
{
|
||||
RestRequestQueryModel restRequestQueryModel = new RestRequestQueryModel();
|
||||
restRequestQueryModel.setQuery(query);
|
||||
Optional.ofNullable(language).ifPresent(restRequestQueryModel::setLanguage);
|
||||
SearchRequest request = new SearchRequest(restRequestQueryModel);
|
||||
List<RestRequestTemplatesModel> templatesModels = templates.entrySet().stream()
|
||||
.map(entry -> RestRequestTemplatesModel.builder().name(entry.getKey()).template(entry.getValue()).create())
|
||||
.toList();
|
||||
request.setTemplates(templatesModels);
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch.basicAuth;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
|
||||
import org.alfresco.tas.AlfrescoStackInitializer;
|
||||
import org.alfresco.tas.SearchEngineType;
|
||||
|
||||
/**
|
||||
* ACS Stack Docker Compose initializer with Basic Authentication for Search Engine service (Opensearch or Elasticsearch).
|
||||
*/
|
||||
public class AlfrescoStackInitializerESBasicAuth extends AlfrescoStackInitializer
|
||||
{
|
||||
|
||||
// Default Elasticsearch credentials
|
||||
private static final String SEARCH_ENGINE_USERNAME = "elastic";
|
||||
private static final String SEARCH_ENGINE_PASSWORD = "bob123"; // pragma: allowlist secret
|
||||
private static final String OPENSEARCH_TEST_ROLE = "test_role";
|
||||
|
||||
@Override
|
||||
public void configureSecuritySettings(GenericContainer searchEngineContainer)
|
||||
{
|
||||
SearchEngineType usedEngine = getImagesConfig().getSearchEngineType();
|
||||
|
||||
if (SearchEngineType.OPENSEARCH_ENGINE.equals(usedEngine))
|
||||
{
|
||||
configureNewUser(searchEngineContainer);
|
||||
}
|
||||
}
|
||||
|
||||
private void configureNewUser(GenericContainer opensearchContainer)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Using password hash for setting up test only
|
||||
String passwordHash = hashPassword(opensearchContainer, SEARCH_ENGINE_PASSWORD);
|
||||
|
||||
addNewUser(opensearchContainer, SEARCH_ENGINE_USERNAME, passwordHash);
|
||||
addNewRole(opensearchContainer, OPENSEARCH_TEST_ROLE, CUSTOM_ALFRESCO_INDEX);
|
||||
addNewRoleMapping(opensearchContainer, OPENSEARCH_TEST_ROLE, SEARCH_ENGINE_USERNAME);
|
||||
applyNewSecurityConfigs(opensearchContainer);
|
||||
}
|
||||
catch (IOException | InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private String hashPassword(GenericContainer opensearchContainer, String password) throws IOException, InterruptedException
|
||||
{
|
||||
return opensearchContainer.execInContainer("/usr/share/opensearch/plugins/opensearch-security/tools/hash.sh", "-p", password)
|
||||
.getStdout().strip();
|
||||
}
|
||||
|
||||
private void applyNewSecurityConfigs(GenericContainer opensearchContainer) throws IOException, InterruptedException
|
||||
{
|
||||
opensearchContainer.execInContainer("sh", "-c", "/usr/share/opensearch/plugins/opensearch-security/tools/securityadmin.sh " +
|
||||
"-cd /usr/share/opensearch/config/opensearch-security " +
|
||||
"-icl -nhnv " +
|
||||
"-cert /usr/share/opensearch/config/kirk.pem " +
|
||||
"-cacert /usr/share/opensearch/config/root-ca.pem " +
|
||||
"-key /usr/share/opensearch/config/kirk-key.pem");
|
||||
}
|
||||
|
||||
private void addNewRoleMapping(GenericContainer opensearchContainer, String role, String username) throws IOException, InterruptedException
|
||||
{
|
||||
opensearchContainer.execInContainer(
|
||||
"sh", "-c", "echo '\n\n" + newOpensearchRoleMapping(role, username) + "' >> /usr/share/opensearch/config/opensearch-security/roles_mapping.yml");
|
||||
}
|
||||
|
||||
private void addNewRole(GenericContainer opensearchContainer, String role, String index) throws IOException, InterruptedException
|
||||
{
|
||||
opensearchContainer.execInContainer(
|
||||
"sh", "-c", "echo '\n\n" + newOpensearchRule(role, index) + "' >> /usr/share/opensearch/config/opensearch-security/roles.yml");
|
||||
}
|
||||
|
||||
private void addNewUser(GenericContainer opensearchContainer, String username, String passwordHash) throws IOException, InterruptedException
|
||||
{
|
||||
opensearchContainer.execInContainer(
|
||||
"sh", "-c", "echo '\n\n" + newOpensearchUser(username, passwordHash) + "' >> /usr/share/opensearch/config/opensearch-security/internal_users.yml");
|
||||
}
|
||||
|
||||
private String newOpensearchUser(String username, String passwordHash)
|
||||
{
|
||||
return username + ":\n" +
|
||||
" hash: \"" + passwordHash + "\"\n" +
|
||||
" reserved: false\n" +
|
||||
" backend_roles:\n" +
|
||||
" - \"all_access\"\n" +
|
||||
" description: \"New user for testing purposes\"";
|
||||
}
|
||||
|
||||
private String newOpensearchRule(String role, String indexName)
|
||||
{
|
||||
return role + ":\n" +
|
||||
" cluster_permissions:\n" +
|
||||
" - cluster_all\n" +
|
||||
" index_permissions:\n" +
|
||||
" - index_patterns:\n" +
|
||||
" - \"" + indexName + "\"\n" +
|
||||
" - \"alfresco-reindex-state\"\n" +
|
||||
" - \"alfresco-reindex-dead-letter\"\n" +
|
||||
" allowed_actions:\n" +
|
||||
" - \"*\"\n";
|
||||
}
|
||||
|
||||
private String newOpensearchRoleMapping(String role, String user)
|
||||
{
|
||||
return role + ":\n" +
|
||||
" reserved: true\n" +
|
||||
" users:\n" +
|
||||
" - \"" + user + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GenericContainer createBatchIndexingContainer()
|
||||
{
|
||||
GenericContainer container = super.createBatchIndexingContainer();
|
||||
container.withEnv("SPRING_ELASTICSEARCH_REST_USERNAME", SEARCH_ENGINE_USERNAME);
|
||||
container.withEnv("SPRING_ELASTICSEARCH_REST_PASSWORD", SEARCH_ENGINE_PASSWORD);
|
||||
container.withEnv("ALFRESCO_REINDEX_CONTINUOUS_ALFRESCOINDEXNAME", CUSTOM_ALFRESCO_INDEX);
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GenericContainer createSearchEngineContainer()
|
||||
{
|
||||
SearchEngineType usedEngine = getImagesConfig().getSearchEngineType();
|
||||
|
||||
if (SearchEngineType.OPENSEARCH_ENGINE.equals(usedEngine))
|
||||
{
|
||||
return super.createOpensearchContainer()
|
||||
.withEnv("plugins.security.disabled", "false")
|
||||
.withEnv("plugins.security.ssl.http.enabled", "false");
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.createElasticContainer()
|
||||
.withEnv("xpack.security.enabled", "true")
|
||||
.withEnv("ELASTIC_PASSWORD", SEARCH_ENGINE_PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GenericContainer createAlfrescoContainer()
|
||||
{
|
||||
GenericContainer container = super.createAlfrescoContainer();
|
||||
String javaOpts = (String) container.getEnvMap().get("JAVA_OPTS");
|
||||
javaOpts = javaOpts + " -Delasticsearch.user=" + SEARCH_ENGINE_USERNAME + " " +
|
||||
"-Delasticsearch.password=" + SEARCH_ENGINE_PASSWORD;
|
||||
container.getEnvMap().put("JAVA_OPTS", javaOpts);
|
||||
return container;
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch.basicAuth;
|
||||
|
||||
import static org.alfresco.elasticsearch.SearchQueryService.req;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.elasticsearch.SearchQueryService;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FileType;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.network.ServerHealth;
|
||||
|
||||
/**
|
||||
* Basic test for Elasticsearch server with Basic Authentication. The aim of this class is to test that Basic Authentication is working as expected.
|
||||
*/
|
||||
@ContextConfiguration(locations = "classpath:alfresco-elasticsearch-context.xml",
|
||||
initializers = AlfrescoStackInitializerESBasicAuth.class)
|
||||
public class ElasticsearchBasicAuthTests extends AbstractTestNGSpringContextTests
|
||||
{
|
||||
private static final String FILE_0_NAME = "test.txt";
|
||||
|
||||
@Autowired
|
||||
private ServerHealth serverHealth;
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
@Autowired
|
||||
private DataContent dataContent;
|
||||
@Autowired
|
||||
private DataSite dataSite;
|
||||
@Autowired
|
||||
private SearchQueryService searchQueryService;
|
||||
|
||||
private UserModel userSite1;
|
||||
private SiteModel siteModel1;
|
||||
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation()
|
||||
{
|
||||
serverHealth.assertServerIsOnline();
|
||||
userSite1 = dataUser.createRandomTestUser();
|
||||
siteModel1 = dataSite.usingUser(userSite1).createPrivateRandomSite();
|
||||
createContent(FILE_0_NAME, "This is the first test", siteModel1, userSite1);
|
||||
}
|
||||
|
||||
private FileModel createContent(String filename, String content, SiteModel site, UserModel user)
|
||||
{
|
||||
FileModel fileModel = new FileModel(filename, FileType.TEXT_PLAIN, content);
|
||||
return dataContent.usingUser(user).usingSite(site)
|
||||
.createContent(fileModel);
|
||||
}
|
||||
|
||||
@Test(groups = TestGroup.SEARCH)
|
||||
public void searchCanFindAFile()
|
||||
{
|
||||
searchQueryService.expectResultsFromQuery(req("first AND SITE:\"" + siteModel1.getId() + "\""), userSite1, FILE_0_NAME);
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch.retry;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testng.IRetryAnalyzer;
|
||||
import org.testng.ITestResult;
|
||||
|
||||
public class RetryAnalyzer implements IRetryAnalyzer
|
||||
{
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RetryAnalyzer.class);
|
||||
private static final int RETRY_LIMIT = 3;
|
||||
private int retryNumber = 0;
|
||||
|
||||
@Override
|
||||
public boolean retry(ITestResult testResult)
|
||||
{
|
||||
retryNumber++;
|
||||
Throwable throwable = testResult.getThrowable();
|
||||
if (retryNumber == RETRY_LIMIT)
|
||||
{
|
||||
LOGGER.info("Retry limit reached: {}, shouldRetry: {}", retryNumber, false, throwable);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean shouldRetry = throwable != null
|
||||
&& (throwable instanceof IllegalStateException && throwable.getMessage() != null
|
||||
&& throwable.getMessage().contains("connection still allocated"))
|
||||
|| (throwable instanceof ConcurrentModificationException)
|
||||
|| (throwable instanceof AssertionError && throwable.getMessage() != null
|
||||
&& throwable.getMessage().contains("Maximum retry period reached"));
|
||||
LOGGER.info("Retry: {}, shouldRetry: {}", retryNumber, shouldRetry, throwable);
|
||||
return shouldRetry;
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch.retry;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.testng.IAnnotationTransformer;
|
||||
import org.testng.annotations.ITestAnnotation;
|
||||
|
||||
/**
|
||||
* Add the {@link RetryAnalyzer} to each test.
|
||||
*/
|
||||
public class RetryAnnotationTransformer implements IAnnotationTransformer
|
||||
{
|
||||
@Override
|
||||
public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod)
|
||||
{
|
||||
annotation.setRetryAnalyzer(RetryAnalyzer.class);
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Tas Elasticsearch
|
||||
* %%
|
||||
* Copyright (C) 2026 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.elasticsearch.utility;
|
||||
|
||||
import static org.alfresco.utility.model.FileType.TEXT_PLAIN;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import org.alfresco.rest.core.RestWrapper;
|
||||
import org.alfresco.rest.model.RestCategoryLinkBodyModel;
|
||||
import org.alfresco.rest.model.RestCategoryModel;
|
||||
import org.alfresco.utility.data.DataContent;
|
||||
import org.alfresco.utility.data.DataSite;
|
||||
import org.alfresco.utility.data.DataUser;
|
||||
import org.alfresco.utility.data.RandomData;
|
||||
import org.alfresco.utility.model.ContentModel;
|
||||
import org.alfresco.utility.model.FileModel;
|
||||
import org.alfresco.utility.model.FolderModel;
|
||||
import org.alfresco.utility.model.SiteModel;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
|
||||
/** Helper methods for Elasticsearch E2E tests. */
|
||||
public class ElasticsearchRESTHelper
|
||||
{
|
||||
/** The alias for the root of the category hierarchy. */
|
||||
public static final String ROOT_CATEGORY_ALIAS = "-root-";
|
||||
/** The root of the category hierarchy. */
|
||||
private static final RestCategoryModel ROOT_CATEGORY = RestCategoryModel.builder().id(ROOT_CATEGORY_ALIAS).name(ROOT_CATEGORY_ALIAS).create();
|
||||
|
||||
@Autowired
|
||||
private RestWrapper client;
|
||||
@Autowired
|
||||
private DataSite dataSite;
|
||||
@Autowired
|
||||
private DataContent dataContent;
|
||||
@Autowired
|
||||
private DataUser dataUser;
|
||||
|
||||
/**
|
||||
* Create a private site.
|
||||
*
|
||||
* @param user
|
||||
* The user to use.
|
||||
* @return The new site.
|
||||
*/
|
||||
public SiteModel createPrivateSite(UserModel user)
|
||||
{
|
||||
return dataSite.usingUser(user).createPrivateRandomSite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a folder in a site.
|
||||
*
|
||||
* @param user
|
||||
* The user to use.
|
||||
* @param site
|
||||
* The site to create the folder in.
|
||||
* @return The new folder.
|
||||
*/
|
||||
public FolderModel createFolderInSite(UserModel user, SiteModel site)
|
||||
{
|
||||
return dataContent.usingUser(user).usingSite(site).createFolder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a text file in a site.
|
||||
*
|
||||
* @param user
|
||||
* The user to use.
|
||||
* @param site
|
||||
* The site to create the file in.
|
||||
* @return The new file.
|
||||
*/
|
||||
public FileModel createFileInSite(UserModel user, SiteModel site)
|
||||
{
|
||||
FileModel fileModel = FileModel.getRandomFileModel(TEXT_PLAIN);
|
||||
return dataContent.usingUser(user).usingSite(site).createContent(fileModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file in a folder.
|
||||
*
|
||||
* @param user
|
||||
* The user to use.
|
||||
* @param folder
|
||||
* The folder to create the file in.
|
||||
* @return The new file.
|
||||
*/
|
||||
public FileModel createFileInFolder(UserModel user, FolderModel folder)
|
||||
{
|
||||
FileModel fileModel = FileModel.getRandomFileModel(TEXT_PLAIN);
|
||||
return dataContent.usingUser(user).usingResource(folder).createContent(fileModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a category.
|
||||
*
|
||||
* @param ancestorCategories
|
||||
* The path of categories between the root and the new category, or leave blank to create the category at the "-root-".
|
||||
* @return The newly created category.
|
||||
*/
|
||||
public RestCategoryModel createCategory(RestCategoryModel... ancestorCategories)
|
||||
{
|
||||
RestCategoryModel parent = (ancestorCategories.length > 0 ? ancestorCategories[ancestorCategories.length - 1] : ROOT_CATEGORY);
|
||||
return client.authenticateUser(dataUser.getAdminUser()).withCoreAPI()
|
||||
.usingCategory(parent)
|
||||
.createSingleCategory(RestCategoryModel.builder().name(RandomData.getRandomAlphanumeric()).create());
|
||||
}
|
||||
|
||||
/**
|
||||
* Link a file or folder to a category.
|
||||
*
|
||||
* @param user
|
||||
* The user who should create the link.
|
||||
* @param node
|
||||
* The file or folder to be linked.
|
||||
* @param categoryHierarchy
|
||||
* The full list of categories from the root (excluding "-root-") to the category to use.
|
||||
* @return The category that was linked to.
|
||||
*/
|
||||
public RestCategoryModel linkToCategory(UserModel user, ContentModel node, RestCategoryModel... categoryHierarchy)
|
||||
{
|
||||
RestCategoryModel linkedToCategory = (categoryHierarchy.length > 0 ? categoryHierarchy[categoryHierarchy.length - 1] : ROOT_CATEGORY);
|
||||
return client.authenticateUser(user).withCoreAPI().usingNode(node)
|
||||
.linkToCategory(RestCategoryLinkBodyModel.builder().categoryId(linkedToCategory.getId()).create());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink a node from a category.
|
||||
*
|
||||
* @param user
|
||||
* The user who should remove the link.
|
||||
* @param node
|
||||
* The node to unlink.
|
||||
* @param categoryHierarchy
|
||||
* The full list of categories from the root (excluding "-root-") to the category to use.
|
||||
*/
|
||||
public void unlinkFromCategory(UserModel user, ContentModel node, RestCategoryModel... categoryHierarchy)
|
||||
{
|
||||
RestCategoryModel linkedToCategory = (categoryHierarchy.length > 0 ? categoryHierarchy[categoryHierarchy.length - 1] : ROOT_CATEGORY);
|
||||
client.authenticateUser(user).withCoreAPI().usingNode(node).unlinkFromCategory(linkedToCategory.getId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/context
|
||||
http://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<context:annotation-config />
|
||||
<context:component-scan base-package="org.alfresco" />
|
||||
|
||||
<import resource="classpath:dataprep-context.xml" />
|
||||
|
||||
<bean id="elasticsearchRESTHelper" class="org.alfresco.elasticsearch.utility.ElasticsearchRESTHelper" />
|
||||
<bean id="searchQueryService" class="org.alfresco.elasticsearch.SearchQueryService" />
|
||||
</beans>
|
||||
@@ -0,0 +1,5 @@
|
||||
# Exact Term search is switched off by default as it introduces index size overhead.
|
||||
#to enable it, please uncomment the following on a datatype or property name basis
|
||||
alfresco.cross.locale.datatype.0={http://www.alfresco.org/model/dictionary/1.0}content
|
||||
alfresco.cross.locale.property.0={http://www.alfresco.org/model/content/1.0}content
|
||||
alfresco.cross.locale.property.1={http://www.alfresco.org/model/content/1.0}name
|
||||
@@ -0,0 +1,16 @@
|
||||
# Root logger option
|
||||
rootLogger.level=info
|
||||
rootLogger.appenderRef.stdout.ref=ConsoleAppender
|
||||
|
||||
###### Console appender definition #######
|
||||
appender.console.type=Console
|
||||
appender.console.name=ConsoleAppender
|
||||
appender.console.layout.type=PatternLayout
|
||||
appender.console.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
|
||||
|
||||
logger.alfresco-elasticsearch.name=org.alfresco.elasticsearch
|
||||
logger.alfresco-elasticsearch.level=info
|
||||
logger.alfresco-rest-core.name=org.alfresco.rest.core
|
||||
logger.alfresco-rest-core.level=warn
|
||||
logger.alfresco-utility-report-log.name=org.alfresco.utility.report.log
|
||||
logger.alfresco-utility-report-log.level=info
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
|
||||
<suite name="REST API tests Elasticsearch with Basic Auth" preserve-order="true" verbose="1">
|
||||
<listeners>
|
||||
<listener class-name="org.alfresco.elasticsearch.retry.RetryAnnotationTransformer" />
|
||||
</listeners>
|
||||
<test name="elasticsearch-basic-auth" verbose="3" preserve-order="true">
|
||||
<packages>
|
||||
<package name="org.alfresco.elasticsearch.basicAuth"/>
|
||||
</packages>
|
||||
</test>
|
||||
</suite>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
|
||||
<suite name="REST API tests for Elasticsearch" verbose="1" parallel="false">
|
||||
<listeners>
|
||||
<listener class-name="org.alfresco.utility.testng.AlphabeticalPriorityInterceptor" />
|
||||
</listeners>
|
||||
<test name="elasticsearch" verbose="3" preserve-order="false">
|
||||
<packages>
|
||||
<package name="org.alfresco.elasticsearch" />
|
||||
</packages>
|
||||
</test>
|
||||
</suite>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
|
||||
<suite name="Parallel REST API tests for Elasticsearch" verbose="1" parallel="false">
|
||||
<listeners>
|
||||
<listener class-name="org.alfresco.elasticsearch.retry.RetryAnnotationTransformer" />
|
||||
</listeners>
|
||||
<suite-files>
|
||||
<suite-file path="elasticsearch-suite.xml" />
|
||||
</suite-files>
|
||||
</suite>
|
||||
@@ -0,0 +1,2 @@
|
||||
# By default this property is set to 30 seconds
|
||||
pull.pause.timeout = 120
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>content-repository-community-tests</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
||||
+31
-30
@@ -1,30 +1,31 @@
|
||||
package org.alfresco.email.imap;
|
||||
|
||||
import org.alfresco.email.EmailTest;
|
||||
import org.alfresco.utility.model.*;
|
||||
import org.alfresco.utility.testrail.ExecutionType;
|
||||
import org.alfresco.utility.testrail.annotation.TestRail;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class ImapReadMessagesAcsTests extends EmailTest
|
||||
{
|
||||
@BeforeClass(alwaysRun=true)
|
||||
public void dataPreparation() throws Exception
|
||||
{
|
||||
testUser = dataUser.createRandomTestUser();
|
||||
testSite = dataSite.usingUser(testUser).createIMAPSite();
|
||||
adminUser = dataUser.getAdminUser();
|
||||
adminSite = dataSite.usingAdmin().createIMAPSite();
|
||||
}
|
||||
|
||||
@TestRail(section = { TestGroup.PROTOCOLS, TestGroup.IMAP }, executionType = ExecutionType.REGRESSION,
|
||||
description = "Verify that site manager can see wiki pages via IMAP")
|
||||
@Test(groups = { TestGroup.PROTOCOLS, TestGroup.IMAP, TestGroup.CORE })
|
||||
public void siteManagerCanViewWikiPages() throws Exception
|
||||
{
|
||||
dataWiki.usingUser(testUser).usingSite(testSite).createRandomWiki();
|
||||
/* @Category(IntermittentlyFailingTests.class) ACS-959 Intermittent failure on next line. @Category not supported by TAS tests. */
|
||||
// ACS-2268 comment out: imapProtocol.authenticateUser(testUser).usingSiteWikiContainer(testSite).assertThat().countMessagesIs(1);
|
||||
}
|
||||
}
|
||||
package org.alfresco.email.imap;
|
||||
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.email.EmailTest;
|
||||
import org.alfresco.utility.model.*;
|
||||
import org.alfresco.utility.testrail.ExecutionType;
|
||||
import org.alfresco.utility.testrail.annotation.TestRail;
|
||||
|
||||
public class ImapReadMessagesAcsTests extends EmailTest
|
||||
{
|
||||
@BeforeClass(alwaysRun = true)
|
||||
public void dataPreparation() throws Exception
|
||||
{
|
||||
testUser = dataUser.createRandomTestUser();
|
||||
testSite = dataSite.usingUser(testUser).createIMAPSite();
|
||||
adminUser = dataUser.getAdminUser();
|
||||
adminSite = dataSite.usingAdmin().createIMAPSite();
|
||||
}
|
||||
|
||||
@TestRail(section = {TestGroup.PROTOCOLS, TestGroup.IMAP}, executionType = ExecutionType.REGRESSION,
|
||||
description = "Verify that site manager can see wiki pages via IMAP")
|
||||
@Test(groups = {TestGroup.PROTOCOLS, TestGroup.IMAP, TestGroup.CORE})
|
||||
public void siteManagerCanViewWikiPages() throws Exception
|
||||
{
|
||||
dataWiki.usingUser(testUser).usingSite(testSite).createRandomWiki();
|
||||
/* @Category(IntermittentlyFailingTests.class) ACS-959 Intermittent failure on next line. @Category not supported by TAS tests. */
|
||||
// ACS-2268 comment out: imapProtocol.authenticateUser(testUser).usingSiteWikiContainer(testSite).assertThat().countMessagesIs(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
# Root logger option
|
||||
log4j.rootLogger=INFO, file, stdout
|
||||
|
||||
# Direct log messages to a log file
|
||||
log4j.appender.file=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.file.File=./target/reports/alfresco-tas.log
|
||||
log4j.appender.file.MaxBackupIndex=10
|
||||
log4j.appender.file.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.file.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
|
||||
# Direct log messages to stdout
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.Target=System.out
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
|
||||
# TestRail particular log file
|
||||
# Direct log messages to a log file
|
||||
log4j.appender.testrailLog=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.testrailLog.File=./target/reports/alfresco-testrail.log
|
||||
log4j.appender.testrailLog.MaxBackupIndex=10
|
||||
log4j.appender.testrailLog.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.testrailLog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
|
||||
log4j.category.testrail=INFO, testrailLog
|
||||
log4j.additivity.testrail=false
|
||||
@@ -0,0 +1,42 @@
|
||||
# Root logger option
|
||||
rootLogger.level=info
|
||||
rootLogger.appenderRef.stdout.ref=ConsoleAppender
|
||||
rootLogger.appenderRef.rolling.ref=RollingAppender
|
||||
|
||||
###### File appender definition #######
|
||||
appender.rolling.type=RollingFile
|
||||
appender.rolling.name=RollingAppender
|
||||
appender.rolling.fileName=./target/reports/alfresco-tas.log
|
||||
appender.rolling.filePattern=./target/reports/alfresco-tas.log.%i
|
||||
appender.rolling.layout.type=PatternLayout
|
||||
appender.rolling.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
|
||||
appender.rolling.policies.type = Policies
|
||||
appender.rolling.policies.size.type=SizeBasedTriggeringPolicy
|
||||
appender.rolling.policies.size.size=10MB
|
||||
appender.rolling.strategy.type=DefaultRolloverStrategy
|
||||
appender.rolling.strategy.max=10
|
||||
|
||||
###### Console appender definition #######
|
||||
appender.console.type=Console
|
||||
appender.console.name=ConsoleAppender
|
||||
appender.console.layout.type=PatternLayout
|
||||
appender.console.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
|
||||
|
||||
# TestRail particular log file
|
||||
# Direct log messages to a log file
|
||||
logger.testrail.name=testrail
|
||||
logger.testrail.level=info
|
||||
logger.testrail.additivity=false
|
||||
logger.testrail.appenderRef.testrail.ref=TestrailAppender
|
||||
|
||||
appender.testrail.name=TestrailAppender
|
||||
appender.testrail.type=RollingFile
|
||||
appender.testrail.fileName=./target/reports/alfresco-testrail.log
|
||||
appender.testrail.filePattern=./target/reports/alfresco-testrail.log.%i
|
||||
appender.testrail.layout.type=PatternLayout
|
||||
appender.testrail.layout.pattern=%d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
|
||||
appender.testrail.policies.type=Policies
|
||||
appender.testrail.policies.size.type=SizeBasedTriggeringPolicy
|
||||
appender.testrail.policies.size.size=10MB
|
||||
appender.testrail.strategy.type=DefaultRolloverStrategy
|
||||
appender.testrail.strategy.max=10
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>content-repository-community-tests</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<properties>
|
||||
<suiteXmlFile>${project.basedir}/src/test/resources/integration-suite.xml</suiteXmlFile>
|
||||
<dependency.groovy.version>3.0.9</dependency.groovy.version>
|
||||
<dependency.groovy.version>3.0.22</dependency.groovy.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -114,8 +114,10 @@
|
||||
<suiteXmlFiles>
|
||||
<suiteXmlFile>${suiteXmlFile}</suiteXmlFile>
|
||||
</suiteXmlFiles>
|
||||
<!-- Keeping illegal-access=warn for Java 11 compatibility, even though it has no effect on JDK 21 -->
|
||||
<argLine>
|
||||
--illegal-access=warn
|
||||
--add-opens=java.base/java.lang=ALL-UNNAMED
|
||||
</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
# Root logger option
|
||||
log4j.rootLogger=INFO, file, stdout
|
||||
|
||||
# Direct log messages to a log file
|
||||
log4j.appender.file=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.file.File=./target/reports/alfresco-tas.log
|
||||
log4j.appender.file.MaxBackupIndex=10
|
||||
log4j.appender.file.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.file.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
|
||||
# Direct log messages to stdout
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.Target=System.out
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
|
||||
# TestRail particular log file
|
||||
# Direct log messages to a log file
|
||||
log4j.appender.testrailLog=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.testrailLog.File=./target/reports/alfresco-testrail.log
|
||||
log4j.appender.testrailLog.MaxBackupIndex=10
|
||||
log4j.appender.testrailLog.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.testrailLog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
|
||||
log4j.category.testrail=INFO, testrailLog
|
||||
log4j.additivity.testrail=false
|
||||
@@ -0,0 +1,42 @@
|
||||
# Root logger option
|
||||
rootLogger.level=info
|
||||
rootLogger.appenderRef.stdout.ref=ConsoleAppender
|
||||
rootLogger.appenderRef.rolling.ref=RollingAppender
|
||||
|
||||
###### File appender definition #######
|
||||
appender.rolling.type=RollingFile
|
||||
appender.rolling.name=RollingAppender
|
||||
appender.rolling.fileName=./target/reports/alfresco-tas.log
|
||||
appender.rolling.filePattern=./target/reports/alfresco-tas.log.%i
|
||||
appender.rolling.layout.type=PatternLayout
|
||||
appender.rolling.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
|
||||
appender.rolling.policies.type = Policies
|
||||
appender.rolling.policies.size.type=SizeBasedTriggeringPolicy
|
||||
appender.rolling.policies.size.size=10MB
|
||||
appender.rolling.strategy.type=DefaultRolloverStrategy
|
||||
appender.rolling.strategy.max=10
|
||||
|
||||
###### Console appender definition #######
|
||||
appender.console.type=Console
|
||||
appender.console.name=ConsoleAppender
|
||||
appender.console.layout.type=PatternLayout
|
||||
appender.console.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
|
||||
|
||||
# TestRail particular log file
|
||||
# Direct log messages to a log file
|
||||
logger.testrail.name=testrail
|
||||
logger.testrail.level=info
|
||||
logger.testrail.additivity=false
|
||||
logger.testrail.appenderRef.testrail.ref=TestrailAppender
|
||||
|
||||
appender.testrail.name=TestrailAppender
|
||||
appender.testrail.type=RollingFile
|
||||
appender.testrail.fileName=./target/reports/alfresco-testrail.log
|
||||
appender.testrail.filePattern=./target/reports/alfresco-testrail.log.%i
|
||||
appender.testrail.layout.type=PatternLayout
|
||||
appender.testrail.layout.pattern=%d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
|
||||
appender.testrail.policies.type=Policies
|
||||
appender.testrail.policies.size.type=SizeBasedTriggeringPolicy
|
||||
appender.testrail.policies.size.size=10MB
|
||||
appender.testrail.strategy.type=DefaultRolloverStrategy
|
||||
appender.testrail.strategy.max=10
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>content-repository-community-tests</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
@@ -49,7 +49,7 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.alfresco.tas</groupId>
|
||||
<artifactId>alfresco-community-repo-restapi-test</artifactId>
|
||||
<artifactId>restapi</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
@@ -64,8 +64,8 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>jakarta.json</artifactId>
|
||||
<groupId>org.eclipse.parsson</groupId>
|
||||
<artifactId>parsson</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -73,6 +73,12 @@
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<version>${dependency.awaitility.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -84,8 +90,10 @@
|
||||
<suiteXmlFiles>
|
||||
<suiteXmlFile>${suiteXmlFile}</suiteXmlFile>
|
||||
</suiteXmlFiles>
|
||||
<!-- Keeping illegal-access=warn for Java 11 compatibility, even though it has no effect on JDK 21 -->
|
||||
<argLine>
|
||||
--illegal-access=warn
|
||||
--add-opens=java.base/java.lang=ALL-UNNAMED
|
||||
</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
# Root logger option
|
||||
log4j.rootLogger=INFO, file, stdout
|
||||
|
||||
# Direct log messages to a log file
|
||||
log4j.appender.file=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.file.File=./target/reports/alfresco-tas.log
|
||||
log4j.appender.file.MaxBackupIndex=10
|
||||
log4j.appender.file.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.file.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
|
||||
# Direct log messages to stdout
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.Target=System.out
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
|
||||
# TestRail particular log file
|
||||
# Direct log messages to a log file
|
||||
log4j.appender.testrailLog=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.testrailLog.File=./target/reports/alfresco-testrail.log
|
||||
log4j.appender.testrailLog.MaxBackupIndex=10
|
||||
log4j.appender.testrailLog.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.testrailLog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
|
||||
log4j.category.testrail=INFO, testrailLog
|
||||
log4j.additivity.testrail=false
|
||||
@@ -0,0 +1,42 @@
|
||||
# Root logger option
|
||||
rootLogger.level=info
|
||||
rootLogger.appenderRef.stdout.ref=ConsoleAppender
|
||||
rootLogger.appenderRef.rolling.ref=RollingAppender
|
||||
|
||||
###### File appender definition #######
|
||||
appender.rolling.type=RollingFile
|
||||
appender.rolling.name=RollingAppender
|
||||
appender.rolling.fileName=./target/reports/alfresco-tas.log
|
||||
appender.rolling.filePattern=./target/reports/alfresco-tas.log.%i
|
||||
appender.rolling.layout.type=PatternLayout
|
||||
appender.rolling.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
|
||||
appender.rolling.policies.type = Policies
|
||||
appender.rolling.policies.size.type=SizeBasedTriggeringPolicy
|
||||
appender.rolling.policies.size.size=10MB
|
||||
appender.rolling.strategy.type=DefaultRolloverStrategy
|
||||
appender.rolling.strategy.max=10
|
||||
|
||||
###### Console appender definition #######
|
||||
appender.console.type=Console
|
||||
appender.console.name=ConsoleAppender
|
||||
appender.console.layout.type=PatternLayout
|
||||
appender.console.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
|
||||
|
||||
# TestRail particular log file
|
||||
# Direct log messages to a log file
|
||||
logger.testrail.name=testrail
|
||||
logger.testrail.level=info
|
||||
logger.testrail.additivity=false
|
||||
logger.testrail.appenderRef.testrail.ref=TestrailAppender
|
||||
|
||||
appender.testrail.name=TestrailAppender
|
||||
appender.testrail.type=RollingFile
|
||||
appender.testrail.fileName=./target/reports/alfresco-testrail.log
|
||||
appender.testrail.filePattern=./target/reports/alfresco-testrail.log.%i
|
||||
appender.testrail.layout.type=PatternLayout
|
||||
appender.testrail.layout.pattern=%d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
|
||||
appender.testrail.policies.type=Policies
|
||||
appender.testrail.policies.size.type=SizeBasedTriggeringPolicy
|
||||
appender.testrail.policies.size.size=10MB
|
||||
appender.testrail.strategy.type=DefaultRolloverStrategy
|
||||
appender.testrail.strategy.max=10
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>content-repository-community-tests</artifactId>
|
||||
<version>23.1.0-A4</version>
|
||||
<version>26.2.0-A.31-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user