mirror of
https://github.com/Alfresco/acs-community-packaging.git
synced 2026-09-16 18:13:21 +00:00
Compare commits
975
Commits
7.2.0-A1
...
26.2.0-A.28
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
c6813e5831 | ||
|
|
455e01276f | ||
|
|
fe5cffe9eb | ||
|
|
713683fcbe | ||
|
|
928cab113c | ||
|
|
8e3309825d | ||
|
|
ce00290c4a | ||
|
|
4867fc3184 | ||
|
|
dd2e4cef45 | ||
|
|
e3b92fc5be | ||
|
|
ab26a06320 | ||
|
|
ad3f81a228 | ||
|
|
6542cb450f | ||
|
|
d0f5c71fbd | ||
|
|
c80456ab22 | ||
|
|
40c87cfb0a | ||
|
|
b789531dad | ||
|
|
b9c9174540 | ||
|
|
224b8ec58e | ||
|
|
81531d0b1f | ||
|
|
7ee72f0d4f | ||
|
|
1abd367d65 | ||
|
|
afc69f6599 | ||
|
|
3aa7c8a85d | ||
|
|
39fa717e26 | ||
|
|
e6592268c9 | ||
|
|
5a970fc51d | ||
|
|
6b6eace458 | ||
|
|
85f4e39eda | ||
|
|
a0f0ea290a | ||
|
|
b95d722665 | ||
|
|
cbd6db52c5 | ||
|
|
a922aeaeac | ||
|
|
c0cd626441 | ||
|
|
87702ddb31 | ||
|
|
0f580b6460 | ||
|
|
c1d1cdfd77 | ||
|
|
72cb0fdf23 | ||
|
|
b3de113739 | ||
|
|
fa0bb84ef8 | ||
|
|
8276693097 | ||
|
|
7cacdb44df | ||
|
|
de63c15578 | ||
|
|
be18ab2f5a | ||
|
|
b5c9df0430 | ||
|
|
06ccf16cb9 | ||
|
|
c36068f133 | ||
|
|
1533fe4e8d | ||
|
|
0e96410f0a | ||
|
|
8f3704630f | ||
|
|
2ce2e12c32 | ||
|
|
ea66a7ede5 | ||
|
|
51d294fa21 | ||
|
|
c8d1503253 | ||
|
|
dd82d94c69 | ||
|
|
f090bca704 | ||
|
|
5176baa5ba | ||
|
|
4b1b166e1e | ||
|
|
071da32246 | ||
|
|
b2f3c5f2f6 | ||
|
|
1130a5cbcd | ||
|
|
4cb1d6498a | ||
|
|
659399a878 | ||
|
|
80d1af0c67 | ||
|
|
7ecfae2b75 | ||
|
|
24322d2de4 | ||
|
|
57e68ed370 | ||
|
|
2ca1d8ab25 | ||
|
|
04389650bc | ||
|
|
6c6f3c0a2a | ||
|
|
5d2a9a1aae | ||
|
|
5b5545c640 | ||
|
|
a260d2938c | ||
|
|
c3bff50bfe | ||
|
|
d68920fa16 | ||
|
|
22b42d274f | ||
|
|
0df449e1e0 | ||
|
|
c07a9d4e22 | ||
|
|
00158d6390 | ||
|
|
5645836ac4 | ||
|
|
0b83737fba | ||
|
|
227a82bffd | ||
|
|
bf453a519d | ||
|
|
5ada61ecef | ||
|
|
0f8ea28abe | ||
|
|
e6920f0c71 | ||
|
|
357ed8478c | ||
|
|
b9523d659a | ||
|
|
c2ffbf02ef | ||
|
|
809b3062cd | ||
|
|
fe72a557c5 | ||
|
|
8e1bfeb88e | ||
|
|
401fc13d24 | ||
|
|
93d3e02402 | ||
|
|
9f0cafa954 | ||
|
|
8fc08ab6cf | ||
|
|
c08dd55b8e | ||
|
|
f959da443c | ||
|
|
66cee04556 | ||
|
|
8b7efa4e94 | ||
|
|
c193bcaf52 | ||
|
|
d9843034ae | ||
|
|
024d3e4d2d | ||
|
|
55b1580c22 | ||
|
|
33ebedee3c | ||
|
|
ae40abc1dd | ||
|
|
2f7b625465 | ||
|
|
3b57620a06 | ||
|
|
3c95f04005 | ||
|
|
28e18308b4 | ||
|
|
ff833729fe | ||
|
|
f2292c288b | ||
|
|
a8df8cb7ed | ||
|
|
baf76608f7 | ||
|
|
ddae3ae0ab | ||
|
|
b4b6ff0d27 | ||
|
|
ac00a34bc4 | ||
|
|
7362b4c989 | ||
|
|
cc86cd2342 | ||
|
|
2c6c14bb62 |
+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.28
|
||||
DEVELOPMENT_VERSION: 26.2.0-A.29-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
|
||||
+2
-1
@@ -36,5 +36,6 @@ helm/alfresco-content-services-community/charts/*
|
||||
helm/alfresco-content-services-community/requirements.lock
|
||||
helm/alfresco-content-services-community*.tgz
|
||||
|
||||
# Travis deployment folder
|
||||
# Travis deployment folders
|
||||
deploy_dir
|
||||
deploy_dir_ags
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
-179
@@ -1,179 +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 (7.2.0-....) for the docker image to build successfully.
|
||||
- RELEASE_VERSION=7.2.0-A1
|
||||
- DEVELOPMENT_VERSION=7.2.0-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 -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 -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 -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 -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 -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 -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 -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 -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 -f tests/tas-integration/pom.xml -Pall-tas-tests -Denvironment=default -DrunBugs=false
|
||||
|
||||
- name: "Update latest images"
|
||||
stage: docker_latest
|
||||
script: travis_retry travis_wait 30 mvn -B -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:
|
||||
# Move the final artifacts to a single folder (deploy_dir) to be copied to S3
|
||||
- mkdir -p deploy_dir
|
||||
- cp distribution/target/alfresco.war deploy_dir
|
||||
- cp distribution/target/*-distribution*.zip deploy_dir
|
||||
- ls -lA deploy_dir
|
||||
- mkdir -p deploy_dir_ags
|
||||
- cp distribution-ags/target/*.zip deploy_dir_ags
|
||||
- ls -lA deploy_dir_ags
|
||||
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
|
||||
-334
@@ -1,334 +0,0 @@
|
||||
<h1> 7.2.0 Community
|
||||
</h1>
|
||||
<h2>
|
||||
New Features
|
||||
</h2>
|
||||
<h1> 7.1.0 Community
|
||||
</h1>
|
||||
<h2>
|
||||
New Features
|
||||
</h2>
|
||||
<li>Removal of 3rd party libraries
|
||||
|
||||
With the offloading of both transforms and metadata extraction to T-Engines a number of 3rd party libraries
|
||||
are no longer needed within the content repository. They do still exist within the T-Engines performing the
|
||||
same tasks. Any AMPs that where making use of these will need to provide these libraries themselves. This will
|
||||
reduce the footprint of the repository and allow more frequent releases of the T-Engines to take advantage of
|
||||
new functionality or security patches in these libraries.
|
||||
<ul>
|
||||
<li>PdfBox org.apache.pdfbox:pdfbox:2.0.21 removed - transforms are now performed in T-Engines</li>
|
||||
<li>PdfBox org.apache.pdfbox:fontbox:2.0.21 removed - transforms are now performed in T-Engines</li>
|
||||
<li>PdfBox org.apache.pdfbox:pdfbox-tools:2.0.21 removed - transforms are now performed in T-Engines</li>
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<h1> 7.0.0 Community
|
||||
</h1>
|
||||
<h2>
|
||||
New Features
|
||||
</h2>
|
||||
|
||||
<ul>
|
||||
<li>Metadata Extract
|
||||
|
||||
The out of the box extraction of metadata is now generally performed asynchronously via a T-Engine connected to the
|
||||
repository either as part of the Alfresco Transform Service or as a Local transformer. This provides better security,
|
||||
scalability and reliability. The framework used for metadata extraction within the content repository remains,
|
||||
allowing custom extractors / embedders of metadata to still function, as long as they don't extend the extractors
|
||||
that have been removed. Ideally such custom code should be gradually moved into a T-Engine. For more information see
|
||||
[Metadata Extractors](https://github.com/Alfresco/acs-packaging/blob/master/docs/metadata-extract-embbed.md). </li>
|
||||
<li>Removal of Legacy transformers
|
||||
|
||||
In ACS 6, the Alfresco Transform Service and Local transformers where introduced to help offload the transformation
|
||||
of content to a separate process. In ACS 7, the out of the box Legacy transformers and transformation framework have
|
||||
been removed. This helps provide greater clarity around installation and administration of transformations and
|
||||
technically a more scalable, reliable and secure environment.</li>
|
||||
|
||||
<li>Custom Transforms and Renditions
|
||||
|
||||
ACS 7 provides a number of content transforms, but also allows custom transforms to be added.
|
||||
|
||||
It is possible to create custom transforms that run in separate processes known as T-Engines. The same engines may
|
||||
be used in Community and Enterprise Editions.
|
||||
|
||||
For more information, see [Custom Transforms and Renditions](https://github.com/Alfresco/acs-packaging/blob/master/docs/custom-transforms-and-renditions.md)
|
||||
</li>
|
||||
|
||||
<li>Core All-In-One (AIO) Transform Engine
|
||||
|
||||
We have previously used T-Engines for Community and Enterprise Editions that run in separate processes. (https://docs.alfresco.com/transform-service/latest/)
|
||||
|
||||
The Core All-In-One (AIO) Transform Engine combines the current 5x core T-Engines (LibreOffice, imagemagick,
|
||||
Alfresco PDF Renderer, Tika) packaged together into a single Docker image. Enterprise deployments require
|
||||
greater scalability and we anticipate in these situations the individual T-Engines will be preferable.
|
||||
|
||||
For Community deployments the AIO T-Engine, running it in a single JVM is recommended. In addition the
|
||||
AIO solution has been updated at with the option to build a single AIO T-Engine.
|
||||
</li>
|
||||
|
||||
<li>Events related to node and association actions
|
||||
|
||||
With Alfresco Content Services 7.0, the Content Repository publishes events related to an initial set of actions
|
||||
to nodes and associations. This is the first time that this feature is introduced as part of the ACS Core Services,
|
||||
and it will be used in many use cases, as an example by the Alfresco SDK 5. For the moment the supported events
|
||||
are related to node creation/update/deletion, secondary child association creation/deletion, peer association
|
||||
creation/deletion.
|
||||
</li>
|
||||
|
||||
<li>New REST API Endpoints:
|
||||
|
||||
File Rendition Management API is now available under /s
|
||||
POST '/nodes/{nodeId}/s/{Id}/renditions'
|
||||
GET '/nodes/{nodeId}/s/{Id}/renditions'
|
||||
GET '/nodes/{nodeId}/s/{Id}/renditions/{renditionId}'
|
||||
GET '/nodes/{nodeId}/s/{Id}/renditions/{renditionId}/content'
|
||||
|
||||
Site Membership Management API is now available under /sites
|
||||
GET '/sites/{siteId}/group-members'
|
||||
POST '/sites/{siteId}/group-members'
|
||||
GET '/sites/{siteId}/group-members/{groupId}'
|
||||
PUT '/sites/{siteId}/group-members/{groupId}'
|
||||
DELETE '/sites/{siteId}/group-members/{groupId}'
|
||||
|
||||
Model API: https://develop.envalfresco.com/api-explorer/?urls.primaryName=Model API
|
||||
</li>
|
||||
|
||||
<li>Recommended Database Patch
|
||||
|
||||
ACS 7 contains a recommended database patch, which adds two indexes to the alf_node table and three to alf_transaction.
|
||||
This patch is optional, but recommended for larger implementations as it can have a big positive performance impact.
|
||||
These indexes are not automatically applied during upgrade, as the amount of time needed to create them might be
|
||||
considerable. They should be run manually after the upgrade process completes.
|
||||
|
||||
To apply the patch, an admin should set the following Alfresco global property to “true”. Like other patches it will
|
||||
only be run once, so there is no need to reset the property afterwards.
|
||||
|
||||
system.new-node-transaction-indexes.ignored=false
|
||||
|
||||
Until this step is completed, you will see Schema Validation warnings reported in the alfresco.log on each startup.
|
||||
The log will also indicate that the patch was not run.
|
||||
|
||||
INFO [org.alfresco.repo.domain.schema.SchemaBootstrap] [...] Ignoring script patch (post-Hibernate): patch.db-V6.3-add-indexes-node-transaction
|
||||
...
|
||||
WARN [org.alfresco.repo.domain.schema.SchemaBootstrap] [...] Schema validation found ... potential problems, results written to ...
|
||||
</li>
|
||||
|
||||
|
||||
<h1> Release Notes - Alfresco - Version Community Edition 201911 GA
|
||||
</h1>
|
||||
<h2> Bug
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/MNT-20436'>MNT-20436</a>] - “POST /nodes/{nodeId}/children” RestAPI does not create a node without having a mandatory value object, but it outputs the 201 successful response.
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/MNT-20714'>MNT-20714</a>] - [HotFix] /nodes/{nodeId}/content REST API fails for content created by a deleted user
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/MNT-20770'>MNT-20770</a>] - Share non responsive during direct download from S3 if content store selector is also configured
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/MNT-20863'>MNT-20863</a>] - Changing cm:name with REST API /nodes/{nodeId} does not update Primary Path
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h1> Release Notes - Alfresco - Version Community Edition 201910 EA
|
||||
</h1>
|
||||
<h2>
|
||||
New Features
|
||||
</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<h4>Custom Transforms and Renditions</h4>
|
||||
<p>Alfresco Content Services (ACS) provides a number of content
|
||||
transforms, but also allows custom transforms to be added.
|
||||
<p>It is now possible to create custom transforms that run in
|
||||
separate processes known as T-Engines (short for Transformer
|
||||
Engines). The same engines may be used in Community and
|
||||
Enterprise Editions. They may be directly connected to the ACS
|
||||
repository as Local Transforms, but in the Enterprise edition there
|
||||
is the option to include them as part of the Transform Service
|
||||
which provides more balanced throughput and better administration
|
||||
capabilities.
|
||||
<p>For more information see <a href='https://github.com/Alfresco/acs-packaging/blob/master/docs/custom-transforms-and-renditions.md'>Custom Transforms and Renditions</a>
|
||||
</li>
|
||||
<li>
|
||||
<h4>Open-source Transformation Engines</h4>
|
||||
<p>The core T-Engine images can now be used in Community
|
||||
Edition.</p>
|
||||
<p>T-Engines code has been Open-Sourced and is available on Github:</p>
|
||||
<ul><a href='https://github.com/Alfresco/alfresco-transform-core'>alfresco/alfresco-transform-core</a></ul>
|
||||
<p>Images are available on Docker Hub:</p>
|
||||
<ul><a href='https://hub.docker.com/r/alfresco/alfresco-imagemagick'>alfresco/alfresco-imagemagick</a></ul>
|
||||
<ul><a href='https://hub.docker.com/r/alfresco/alfresco-pdf-renderer'>alfresco/alfresco-pdf-renderer</a></ul>
|
||||
<ul><a href='https://hub.docker.com/r/alfresco/alfresco-libreoffice'>alfresco/alfresco-libreoffice</a></ul>
|
||||
<ul><a href='https://hub.docker.com/r/alfresco/alfresco-tika'>alfresco/alfresco-tika</a></ul>
|
||||
<ul><a href='https://hub.docker.com/r/alfresco/alfresco-transform-misc'>alfresco/alfresco-transform-misc</a></ul>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<h4>Removal of external executables from docker image</h4>
|
||||
<p>With the introduction of the new Local Transform Service
|
||||
in Alfresco Community Edition, the capability of executing
|
||||
remote transformations on T-Engines was enabled. Because of
|
||||
this, the external executables (Alfresco-Pdf-renderer, Libreoffice
|
||||
and Imagemagick) have been removed from the docker container to
|
||||
facilitate the usage of out-of-process transformations.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h2> Bug
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22013'>ALF-22013</a>] - Docker Image for Base Tomcat locale is POSIX
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22060'>ALF-22060</a>] - Reader on the backing store is obtained twice in CachingContentStore
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22056'>ALF-22056</a>] - onCopyCompleteBehaviour not called in order of copy-action
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22073'>ALF-22073</a>] - MailActionExecutor doesn't consider email bodies with a HTML doctype as HTML
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-21988'>ALF-21988</a>] - Tab order for number ranges not ok
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22097'>ALF-22097</a>] - T Engine - add source nodeId parameter
|
||||
</li>
|
||||
</ul>
|
||||
<h2> Improvement
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/REPO-4318'>REPO-4318</a>] - [COMPLETE] Extraction of transformers and metadata extractors
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
|
||||
<h1> Release Notes - Alfresco - Version Community Edition 201901 GA
|
||||
</h1>
|
||||
<h2>
|
||||
New Features
|
||||
</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<h4>ActiveMQ:</h4>
|
||||
Alfresco ActiveMQ Docker images: <a href='https://github.com/Alfresco/alfresco-docker-activemq'>GitHub Repo</a> <a href='https://hub.docker.com/r/alfresco/alfresco-activemq/'>DockerHub Repo</a><p>
|
||||
</li>
|
||||
<li>
|
||||
<h4>Alfresco Benchmark Framework:</h4>
|
||||
<p>The benchmark framework project provides a way to run highly scalable, easy-to-run Java-based load and benchmark tests on an Alfresco instance.</p>
|
||||
<p>It comprises the following: <a href='https://github.com/Alfresco/alfresco-bm-manager'>Alfresco BM Manager</a> and Alfresco BM Drivers.</p>
|
||||
<p>The currently provided drivers are:</p>
|
||||
<ul>
|
||||
<li><a href='https://github.com/Alfresco/alfresco-bm-load-data'>Alfresco Benchmark Load Data</a></li>
|
||||
<li><a href='https://github.com/Alfresco/alfresco-bm-rest-api'>Alfresco Benchmark Rest Api</a></li>
|
||||
<li><a href='https://github.com/Alfresco/alfresco-bm-load-users'>Alfresco Benchmark Load Users</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4>Java 11 support</h4>
|
||||
<p>ACS is now runnable with OpenJDK 11.0.1. It still remains compatible with JDK 1.8.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h2> Bug
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22049'>ALF-22049</a>] - Alfresco does not start
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22041'>ALF-22041</a>] - EKS deployment - SOLR_ALFRESCO_HOST set to wrong host name
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22031'>ALF-22031</a>] - REST API calls silently rollback after the returning a success status
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-21963'>ALF-21963</a>] - Workflow - backslash in nodeRef properties url.
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-21803'>ALF-21803</a>] - Unable to add users to sites whose 'short name' is a substring of 'site'
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-21664'>ALF-21664</a>] - Exception on workflow image by REST API
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-20854'>ALF-20854</a>] - webdav error opening Spanish Accent files
|
||||
</li>
|
||||
</ul>
|
||||
<h2> Improvement
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/REPO-3668'>REPO-3668</a>] - Renditions: Transform event consumer
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/REPO-7'>REPO-7</a>] - Embed ActiveMQ in the Platform
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/REPO-1957'>REPO-1957</a>] - Transformations Improvement Plan
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
Deprecations
|
||||
</h2>
|
||||
<ul>
|
||||
<li>
|
||||
TransformService and RenditionService: All Java APIs related to TransformService and RenditionService have been deprecated; the ability to perform arbitrary transformations will be phased out as the new DBP Transform Service takes effect. Renditions can be triggered using the existing repository REST API but will be processed asynchronously using the new services.<br/>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
Known issues
|
||||
</h2>
|
||||
<ul>
|
||||
<li>
|
||||
Due to the changes to the RenditionService the Media Management AMP is not supported yet.<br/>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
|
||||
<h1> Release Notes - Alfresco - Version Community Edition 201810 EA
|
||||
</h1>
|
||||
|
||||
<h2> Bug
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-21783'>ALF-21783</a>] - ScriptAuthorityService: No way to get more than 100 results with some methods
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-21917'>ALF-21917</a>] - Document list edit metadata incorrect url escaping
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22001'>ALF-22001</a>] - Faceted search does not work in Japanese
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22030'>ALF-22030</a>] - ADF UI freezes noticeably on a periodic basis during KeyCloak auth requests
|
||||
</li>
|
||||
</ul>
|
||||
<h2> Improvement
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/REPO-2491'>REPO-2491</a>] - Renditions: Rendition Testing
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/REPO-3651'>REPO-3651</a>] - AWS Load Tests: Infrastructure and Revamp
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/REPO-3663'>REPO-3663</a>] - AWS Load Tests: Initial AWS Cost Estimation with BMF
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/REPO-3667'>REPO-3667</a>] - Renditions: Transform event producer
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/REPO-3677'>REPO-3677</a>] - AWS Services: Basic Deployment
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/REPO-3703'>REPO-3703</a>] - AWS Services: Native Services of ACS
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
|
||||
<h1> Release Notes - Alfresco - Version Community Edition 201808 EA
|
||||
</h1>
|
||||
<h2> Bug
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-21992'>ALF-21992</a>] - BehaviourFilterImpl.isEnabled(NodeRef, QName) is checking wrong QName in case of subClass
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22006'>ALF-22006</a>] - VersionServicePolicies cannot be disabled on a specific node
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22007'>ALF-22007</a>] - TransactionListeners are executed in unpredictable order
|
||||
</li>
|
||||
</ul>
|
||||
<h2> Improvement
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22011'>ALF-22011</a>] - Upgrade to XMLBeans 3.0.0
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h1> Release Notes - Alfresco - Version Community Edition 201806 GA
|
||||
</h1>
|
||||
<h2> Bug
|
||||
</h2>
|
||||
<ul>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22000'>ALF-22000</a>] - Docker: HTML link 693ce565f4c4:8080/share but name
|
||||
</li>
|
||||
<li>[<a href='https://issues.alfresco.com/jira/browse/ALF-22008'>ALF-22008</a>] - It is not possible to upload a document without versions using CMIS
|
||||
</li>
|
||||
</ul>
|
||||
@@ -18,6 +18,9 @@ The `alfresco/alfresco-content-repository-community` image extends the `alfresco
|
||||
`alfresco-community-repo` project to add additional ACS components.
|
||||
|
||||
# Build:
|
||||
For more detailed build instructions, see the [Development Tomcat Environment](https://github.com/Alfresco/acs-community-packaging/tree/master/dev/README.md)
|
||||
page.
|
||||
|
||||
To build the project, including the distribution zip, but not the Docker images, issue the following commands:
|
||||
```
|
||||
$ # The comP alias includes the following:
|
||||
@@ -28,11 +31,11 @@ $ cd ..
|
||||
## Docker Alfresco
|
||||
Releases are published to https://hub.docker.com/r/alfresco/alfresco-content-repository-community/tags/
|
||||
|
||||
To build the Docker images, you will need to build the `alfresco-community-repo` and
|
||||
`acs-community-packaging` projects. The simplest way is to use the `comRD` and `comPD` aliases.
|
||||
To build the Docker images, you will need to build the `alfresco-community-repo`, `alfresco-community-share` and
|
||||
`acs-community-packaging` projects. The simplest way is to use the `comRD`, `comSD` and `comPD` aliases.
|
||||
For more information, see [build aliases](dev/aliases). `latest` images are created locally.
|
||||
```
|
||||
comRD && comPD
|
||||
comRD && comSD && comPD
|
||||
```
|
||||
|
||||
## Docker-compose & Kubernetes
|
||||
|
||||
+82
-52
@@ -2,37 +2,86 @@
|
||||
|
||||
|
||||
It is possible to use Docker containers to test your code, but it is normally more convenient to simply run the
|
||||
repository webapp (`alfresco.war`) in a tomcat instance.
|
||||
repository webapp (`alfresco.war`) and Share webapp (`share.war`) in a tomcat instance. Options are also available to
|
||||
apply selected AMPs
|
||||
|
||||
## Build -repo projects
|
||||
Build the `alfresco-community-repo` (if you have not
|
||||
## Setting up your development environment
|
||||
Although it is possible to work on individual github projects, we recommend working on
|
||||
the `alfresco-community-repo`, `alfresco-community-share` and `acs-community-packaging`
|
||||
in a single Intellij IDEA project. They depend on each other and typically you
|
||||
will want to make changes to all of them if you are changing the repository code.
|
||||
|
||||
~~~
|
||||
mkdir work
|
||||
cd work
|
||||
git clone git@github.com:Alfresco/alfresco-community-repo.git
|
||||
git clone git@github.com:Alfresco/alfresco-community-share.git
|
||||
git clone git@github.com:Alfresco/acs-community-packaging.git
|
||||
~~~
|
||||
|
||||
## Aliases
|
||||
There are a set of aliases to help with building. You may find them useful, as they will help you only build selected parts
|
||||
of the code base and will save you lots of typing.
|
||||
|
||||
Aliases ending in `D` provide Maven commands for building local Docker images. The AMPS environment variable will be of
|
||||
interest, if you wish to build AMPs included in the repo and share projects.
|
||||
|
||||
The `aliases` file includes a more detailed description.
|
||||
~~~
|
||||
source acs-community-packaging/dev/aliases
|
||||
~~~
|
||||
|
||||
## Link the projects
|
||||
Generally you will want to link the different projects together by modifying the top level
|
||||
pom.xml files of each downstream project so that they reference the SNAPSHOT versions of the
|
||||
upstream projects. To help do this see the `acs-community-packaging` project's `scripts/dev/linkPoms.sh` and
|
||||
`scripts/dev/unlinkPoms.sh` scripts.
|
||||
|
||||
~~~
|
||||
sh acs-community-packaging/scripts/dev/linkPoms.sh
|
||||
~~~
|
||||
|
||||
## Build the alfresco-community-repo project
|
||||
Build the `alfresco-community-repo` projects (if you have not
|
||||
done so already), so that your changes are in the community alfresco.war file.
|
||||
~~~
|
||||
$ # The `comR` alias includes the following commands:
|
||||
$ cd alfresco-community-repo
|
||||
$ mvn clean install -Pbuild-docker-images -DskipTests=true -Dversion.edition=Community
|
||||
$ cd ..
|
||||
# The `comR` alias includes the following commands:
|
||||
cd alfresco-community-repo
|
||||
mvn clean install -DskipTests=true -Dversion.edition=Community
|
||||
cd ..
|
||||
~~~
|
||||
|
||||
## Build the Share project
|
||||
Build the `alfresco-community-share` project (if you have not done so already), so that your
|
||||
changes are in the community share.war file, which also depends on your `alfresco-community-repo` project version.
|
||||
~~~
|
||||
# The `entS` alias is the same as the following commands:
|
||||
cd alfresco-community-share
|
||||
mvn clean install -DskipTests -Dmaven.javadoc.skip=true
|
||||
cd ..
|
||||
~~~
|
||||
|
||||
## Docker test environment
|
||||
The repository code will need to talk to other ACS components, such as a databases, message queue and transformers.
|
||||
The repository code will need to talk to other ACS components, such as a database, message queue and transformers.
|
||||
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:
|
||||
$ cd acs-community-packaging
|
||||
$ docker-compose -f dev/docker-compose.yml up -d
|
||||
Creating dev_transform-core-aio_1 ... done
|
||||
Creating dev_postgres_1 ... done
|
||||
Creating dev_solr6_1 ... done
|
||||
# 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.
|
||||
cd acs-community-packaging
|
||||
docker compose -f dev/docker-compose.yml up
|
||||
Creating dev_activemq_1 ... done
|
||||
$ cd ..
|
||||
Creating dev_solr6_1 ... done
|
||||
Creating dev_postgres_1 ... done
|
||||
Creating dev_transform-core-aio_1 ... done
|
||||
...
|
||||
cd ..
|
||||
~~~
|
||||
|
||||
## Alfresco Global Properties and Log4j
|
||||
## Alfresco global properties and Log4j
|
||||
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
|
||||
~~~
|
||||
|
||||
@@ -41,14 +90,16 @@ Create the development tomcat environment, apply AMPs on top of the repository c
|
||||
run tomcat. The `run` profile is what starts tomcat. The `withShare` applies
|
||||
the Share services AMP and adds the `share.war` to tomcat.
|
||||
Once started, you will be able to access Share on `http://localhost:8080/share` and various repository
|
||||
endpoints via `http://localhost:8080/alfresco/`. `entT` is an alias for the
|
||||
following command and `entTDebug` will allow a debugger to be attached.
|
||||
endpoints via `http://localhost:8080/alfresco/`. `comT` is an alias for the
|
||||
following command and `comTDebug` will allow a debugger to be attached.
|
||||
~~~
|
||||
$ # The alias comT is the same as the following commands. comTDebug may also be used.
|
||||
$ cd acs-community-packaging
|
||||
$ mvn clean install -Prun -rf dev
|
||||
$ cd ..
|
||||
# The alias comT is the same as the following commands:
|
||||
cd acs-community-packaging
|
||||
mvn clean install -Prun -rf dev
|
||||
[INFO] Scanning for projects...
|
||||
[INFO] ------------------------------------------------------------------------
|
||||
[INFO] Reactor Build Order:
|
||||
[INFO]
|
||||
[INFO] Development Tomcat Environment [pom]
|
||||
[INFO] Tomcat Configuration [pom]
|
||||
[INFO] Repo WAR with amps [war]
|
||||
@@ -56,59 +107,38 @@ $ cd ..
|
||||
[INFO] Tomcat [war]
|
||||
...
|
||||
INFO: Starting ProtocolHandler ["http-bio-8080"]
|
||||
$ cd ..
|
||||
cd ..
|
||||
~~~
|
||||
|
||||
If you kill the tomcat instance (^C) and wish to restart it, use the following command
|
||||
or the `comO` alias, or `comODebug` to attach a debuger.
|
||||
or the `comO` alias, or `comODebug` to attach a debugger.
|
||||
~~~
|
||||
$ mvn install -Prun,withShare -rf dev-acs-amps-overlay
|
||||
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.
|
||||
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
|
||||
^C
|
||||
... Stopped 'sysAdmin' subsystem, ID: [sysAdmin, default]
|
||||
|
||||
$ docker-compose -f dev/docker-compose.yml stop
|
||||
Stopping dev_transform-core-aio_1 ... done
|
||||
Stopping dev_transform-router_1 ... done
|
||||
Stopping dev_solr6_1 ... done
|
||||
Stopping dev_postgres_1 ... done
|
||||
Stopping dev_activemq_1 ... done
|
||||
Stopping dev_shared-file-store_1 ... done
|
||||
|
||||
$ 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
|
||||
Removing dev_transform-router_1 ... done
|
||||
Removing dev_solr6_1 ... done
|
||||
Removing dev_postgres_1 ... done
|
||||
Removing dev_activemq_1 ... done
|
||||
Removing dev_shared-file-store_1 ... done
|
||||
~~~
|
||||
|
||||
If you have not removed the containers, it is possible to restart the tomcat instance with
|
||||
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`.
|
||||
|
||||
## Aliases
|
||||
You may also find the aliases specified in the following file useful, as they will help you only build selected parts
|
||||
of the code base and will save you lots of typing.
|
||||
|
||||
Aliases ending in `D` provide Maven commands for building local Docker images. The AMPS environment variable will be of
|
||||
interest, if you wish to build AMPs included in the repo and share projects.
|
||||
|
||||
The `aliases` file includes a more detailed description.
|
||||
~~~
|
||||
$ source acs-community-packaging/dev/aliases
|
||||
~~~
|
||||
|
||||
+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'
|
||||
|
||||
+106
-160
@@ -7,13 +7,12 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-dev-community-tomcat-env</artifactId>
|
||||
<version>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<dependency.sis.version>1.0</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,179 +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>none</solr.secureComms>
|
||||
<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>
|
||||
@@ -298,6 +234,16 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>third-party-licenses</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -307,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>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</version>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-dev-community-tomcat-env</artifactId>
|
||||
<version>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</version>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-dev-community-tomcat-env</artifactId>
|
||||
<version>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</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>
|
||||
+16
-12
@@ -6,7 +6,7 @@ version: "2"
|
||||
|
||||
services:
|
||||
transform-core-aio:
|
||||
image: alfresco/alfresco-transform-core-aio:2.5.3
|
||||
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,25 +25,29 @@ services:
|
||||
- 5432:5432
|
||||
|
||||
solr6:
|
||||
image: alfresco/alfresco-search-services:2.0.0
|
||||
image: alfresco/alfresco-search-services:2.0.21-A.2
|
||||
mem_limit: 2g
|
||||
environment:
|
||||
#Solr needs to know how to register itself with Alfresco
|
||||
- SOLR_ALFRESCO_HOST=alfresco
|
||||
- SOLR_ALFRESCO_PORT=8080
|
||||
SOLR_ALFRESCO_HOST: "host.docker.internal"
|
||||
SOLR_ALFRESCO_PORT: "8080"
|
||||
#Alfresco needs to know how to call solr
|
||||
- SOLR_SOLR_HOST=solr6
|
||||
- SOLR_SOLR_PORT=8983
|
||||
SOLR_SOLR_HOST: "localhost"
|
||||
SOLR_SOLR_PORT: "8983"
|
||||
#Create the default alfresco and archive cores
|
||||
- SOLR_CREATE_ALFRESCO_DEFAULTS=alfresco,archive
|
||||
SOLR_CREATE_ALFRESCO_DEFAULTS: "alfresco,archive"
|
||||
#HTTP by default
|
||||
- ALFRESCO_SECURE_COMMS=none
|
||||
- "SOLR_JAVA_MEM=-Xms2g -Xmx2g"
|
||||
ALFRESCO_SECURE_COMMS: "secret"
|
||||
JAVA_TOOL_OPTIONS:
|
||||
"
|
||||
-Dalfresco.secureComms.secret=secret
|
||||
"
|
||||
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
|
||||
|
||||
+13
-1
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>acs-community-packaging</artifactId>
|
||||
<version>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
@@ -26,4 +26,16 @@
|
||||
<module>dev-acs-amps-overlay</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<!-- Versions higher than 3.3.0 were causing the tomcat dev environment to fail to start with a
|
||||
ClassNotFoundException: org.springframework.web.context.ContextLoaderListener -->
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>3.2.2</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>acs-community-packaging</artifactId>
|
||||
<version>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>acs-community-packaging</artifactId>
|
||||
<version>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
<!-- Share -->
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}/dependency/alfresco-content-services-share-distribution-${project.version}</directory>
|
||||
<directory>${project.build.directory}/dependency/alfresco-content-services-share-distribution-${dependency.acs-packaging.version}</directory>
|
||||
<outputDirectory/>
|
||||
<includes>
|
||||
<include>web-server/**</include>
|
||||
@@ -47,7 +47,7 @@
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}/dependency/alfresco-content-services-share-distribution-${project.version}/web-extension-samples</directory>
|
||||
<directory>${project.build.directory}/dependency/alfresco-content-services-share-distribution-${dependency.acs-packaging.version}/web-extension-samples</directory>
|
||||
<outputDirectory>web-server/shared/classes/alfresco/web-extension</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
||||
@@ -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,5 +1,19 @@
|
||||
### Apply AGS community repo AMP to ACS image
|
||||
FROM alfresco/alfresco-content-repository-community:${image.tag}
|
||||
ARG BASE_IMAGE
|
||||
# BUILD STAGE AGS
|
||||
FROM debian:11-slim AS AGSBUILDER
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive; \
|
||||
apt-get update -qqy && apt-get -yqq install unzip && \
|
||||
mkdir -p /build/gs-api-explorer
|
||||
|
||||
### Copy the AGS war from the local context
|
||||
COPY target/gs-api-explorer-*.war /build
|
||||
|
||||
RUN unzip -q /build/gs-api-explorer-*.war -d /build/gs-api-explorer && \
|
||||
chmod -R g-w,o= /build
|
||||
|
||||
# ACTUAL IMAGE
|
||||
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.
|
||||
@@ -13,11 +27,7 @@ RUN java -jar /usr/local/tomcat/alfresco-mmt/alfresco-mmt*.jar install \
|
||||
/usr/local/tomcat/webapps/alfresco -directory -nobackup
|
||||
|
||||
### Copy gs-api-explorer war into webapps folder
|
||||
COPY target/gs-api-explorer-*.war /usr/local/tomcat/webapps/
|
||||
|
||||
### Unpack gs-api-explorer.war
|
||||
RUN mkdir /usr/local/tomcat/webapps/gs-api-explorer && cd /usr/local/tomcat/webapps/gs-api-explorer && \
|
||||
jar -xvf /usr/local/tomcat/webapps/gs-api-explorer-*.war && rm -f /usr/local/tomcat/webapps/gs-api-explorer-*.war
|
||||
COPY --chown=root:Alfresco --from=AGSBUILDER /build/gs-api-explorer /usr/local/tomcat/webapps/gs-api-explorer
|
||||
|
||||
# All files in the tomcat folder must be owned by root user and Alfresco group as mentioned in the parent Dockerfile
|
||||
RUN chgrp -R Alfresco /usr/local/tomcat && \
|
||||
@@ -26,4 +36,4 @@ RUN chgrp -R Alfresco /usr/local/tomcat && \
|
||||
chmod -R g+r /usr/local/tomcat/webapps
|
||||
|
||||
# Switching back to alfresco user after having added amps files to run the container as non-root
|
||||
USER alfresco
|
||||
USER alfresco
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>content-services-community-docker</artifactId>
|
||||
<version>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</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>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</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>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</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>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</version>
|
||||
</parent>
|
||||
</project>
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
<artifactId>acs-community-packaging</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Alfresco Content Services Community Packaging</name>
|
||||
<version>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<relativePath>../alfresco-community-repo/pom.xml</relativePath>
|
||||
<version>14.12</version>
|
||||
<version>26.2.0.91</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<dependency.alfresco-community-repo.version>14.12</dependency.alfresco-community-repo.version>
|
||||
<dependency.alfresco-community-share.version>14.9</dependency.alfresco-community-share.version>
|
||||
<dependency.acs-packaging.version>7.2.0-A1</dependency.acs-packaging.version> <!-- for Share distribution zip -->
|
||||
<dependency.alfresco-community-repo.version>26.2.0.91</dependency.alfresco-community-repo.version>
|
||||
<dependency.alfresco-community-share.version>26.2.0.64</dependency.alfresco-community-share.version>
|
||||
<dependency.acs-packaging.version>26.2.0-A.28</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>7.2.0-A1</tag>
|
||||
<tag>26.2.0-A.28</tag>
|
||||
</scm>
|
||||
|
||||
<distributionManagement>
|
||||
@@ -39,9 +39,31 @@
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>alfresco-public</id>
|
||||
<url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>alfresco-public-snapshots</id>
|
||||
<url>https://artifacts.alfresco.com/nexus/content/groups/public-snapshots</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<modules>
|
||||
<module>distribution</module>
|
||||
<module>public-javadoc</module>
|
||||
<module>docker-alfresco</module>
|
||||
<module>docker-share</module>
|
||||
</modules>
|
||||
@@ -64,6 +86,7 @@
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<modules>
|
||||
<module>public-javadoc</module>
|
||||
<module>dev</module>
|
||||
</modules>
|
||||
</profile>
|
||||
@@ -136,10 +159,20 @@
|
||||
<artifactId>alfresco-community-repo-distribution</artifactId>
|
||||
<version>${dependency.alfresco-community-repo.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-mmt</artifactId>
|
||||
<version>${dependency.alfresco-community-repo.version}</version>
|
||||
</dependency>
|
||||
|
||||
<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>
|
||||
@@ -160,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>
|
||||
@@ -209,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>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</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"
|
||||
pullAndBuildSameBranchOnUpstream "${UPSTREAM_REPO}" "-P$BUILD_PROFILE -Pags -Dlicense.failOnNotUptodateHeader=true"
|
||||
else
|
||||
pullUpstreamTagAndBuildDockerImage "${UPSTREAM_REPO}" "${COM_DEPENDENCY_VERSION}" "-Pbuild-docker-images -Pags"
|
||||
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 -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 -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 -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
+13
-19
@@ -2,18 +2,18 @@
|
||||
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() {
|
||||
local REPO="${1}"
|
||||
local TAG_OR_BRANCH="${2}"
|
||||
|
||||
printf "Clonning \"%s\" on %s\n" "${TAG_OR_BRANCH}" "${REPO}"
|
||||
printf "Cloning \"%s\" on %s\n" "${TAG_OR_BRANCH}" "${REPO}"
|
||||
|
||||
# clone the repository branch/tag
|
||||
pushd "$(dirname "${BASH_SOURCE[0]}")/../../../" >/dev/null
|
||||
@@ -67,7 +67,7 @@ function evaluatePomProperty() {
|
||||
|
||||
pushd "$(dirname "${BASH_SOURCE[0]}")/../../" >/dev/null
|
||||
|
||||
mvn -B -q help:evaluate -Dexpression="${KEY}" -DforceStdout
|
||||
mvn -B -ntp -q help:evaluate -Dexpression="${KEY}" -DforceStdout
|
||||
|
||||
popd >/dev/null
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -122,7 +116,7 @@ function buildUpstreamTag() {
|
||||
|
||||
cd "$(basename "${UPSTREAM_REPO%.git}")"
|
||||
|
||||
mvn -B -V clean package -DskipTests -Dmaven.javadoc.skip=true "-Dimage.tag=${TAG}" ${EXTRA_BUILD_ARGUMENTS}
|
||||
mvn -B -ntp -V clean package -DskipTests -Dmaven.javadoc.skip=true "-Dimage.tag=${TAG}" ${EXTRA_BUILD_ARGUMENTS}
|
||||
|
||||
popd
|
||||
}
|
||||
@@ -135,8 +129,8 @@ function buildSameBranchOnUpstream() {
|
||||
|
||||
cd "$(basename "${UPSTREAM_REPO%.git}")"
|
||||
|
||||
mvn -B -V -q clean install -DskipTests -Dmaven.javadoc.skip=true ${EXTRA_BUILD_ARGUMENTS}
|
||||
mvn -B -V -q install -DskipTests -f packaging/tests/pom.xml
|
||||
mvn -B -ntp -V -q clean install -DskipTests -Dmaven.javadoc.skip=true ${EXTRA_BUILD_ARGUMENTS}
|
||||
mvn -B -ntp -V -q install -DskipTests -f packaging/tests/pom.xml
|
||||
|
||||
popd
|
||||
}
|
||||
@@ -152,7 +146,7 @@ function pullUpstreamTagAndBuildDockerImage() {
|
||||
|
||||
cd "$(basename "${UPSTREAM_REPO%.git}")"
|
||||
|
||||
mvn -B -V clean package -DskipTests -Dmaven.javadoc.skip=true "-Dimage.tag=${TAG}" ${EXTRA_BUILD_ARGUMENTS}
|
||||
mvn -B -ntp -V clean package -DskipTests -Dmaven.javadoc.skip=true "-Dimage.tag=${TAG}" ${EXTRA_BUILD_ARGUMENTS}
|
||||
|
||||
popd
|
||||
}
|
||||
@@ -169,8 +163,8 @@ function pullAndBuildSameBranchOnUpstream() {
|
||||
|
||||
cd "$(basename "${UPSTREAM_REPO%.git}")"
|
||||
|
||||
mvn -B -V -q clean install -DskipTests -Dmaven.javadoc.skip=true ${EXTRA_BUILD_ARGUMENTS}
|
||||
mvn -B -V -q install -DskipTests -f packaging/tests/pom.xml
|
||||
mvn -B -ntp -V -q clean install -DskipTests -Dmaven.javadoc.skip=true ${EXTRA_BUILD_ARGUMENTS}
|
||||
mvn -B -ntp -V -q install -DskipTests -f packaging/tests/pom.xml
|
||||
|
||||
popd
|
||||
}
|
||||
Regular → Executable
Regular → Executable
+2
-6
@@ -8,7 +8,7 @@ set -vex
|
||||
#
|
||||
|
||||
if [ -z "${RELEASE_VERSION}" ]; then
|
||||
echo "Please provide a RELEASE_VERSION in the format <acs-version>-<additional-info> (7.2.0-EA or 7.2.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,11 +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 \
|
||||
"${SOURCE}/alfresco-governance-services-community-${RELEASE_VERSION}.zip" \
|
||||
"${DESTINATION}/alfresco-governance-services-community-${RELEASE_VERSION}.zip"
|
||||
|
||||
aws s3 cp --acl private --recursive --copy-props none "${SOURCE}" "${DESTINATION}"
|
||||
|
||||
set +vex
|
||||
echo "=========================== Finishing Copy to Release Bucket Script =========================="
|
||||
|
||||
@@ -8,24 +8,16 @@ set -vex
|
||||
#
|
||||
|
||||
if [ -z "${RELEASE_VERSION}" ]; then
|
||||
echo "Please provide a RELEASE_VERSION in the format <acs-version>-<additional-info> (7.2.0-EA or 7.2.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 \
|
||||
"${SOURCE}/alfresco.war" \
|
||||
"${DESTINATION}/alfresco.war"
|
||||
|
||||
aws s3 cp --acl private \
|
||||
"${SOURCE}/alfresco-content-services-community-distribution-${RELEASE_VERSION}.zip" \
|
||||
"${DESTINATION}/alfresco-content-services-community-distribution-${RELEASE_VERSION}.zip"
|
||||
|
||||
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,20 +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> (7.2.0-EA or 7.2.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}" \
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "========================== Starting Prepare Staging Deploy Script ==========================="
|
||||
PS4="\[\e[35m\]+ \[\e[m\]"
|
||||
set -vex
|
||||
pushd "$(dirname "${BASH_SOURCE[0]}")/../../"
|
||||
|
||||
# Identify latest annotated tag (latest version)
|
||||
export VERSION=$(git describe --abbrev=0 --tags)
|
||||
|
||||
# Get third party license scripts.
|
||||
git clone --depth=1 https://github.com/Alfresco/third-party-license-overrides.git
|
||||
|
||||
# Move the final artifacts to a single folder (deploy_dir) to be copied to S3
|
||||
mkdir -p deploy_dir
|
||||
cp distribution/target/alfresco.war deploy_dir
|
||||
cp distribution/target/*-distribution*.zip deploy_dir
|
||||
# Create third party license csv file and add it to the deploy directory.
|
||||
unzip deploy_dir/*-distribution*.zip -d deploy_dir/community-acs
|
||||
zippaths=""
|
||||
for file in `find deploy_dir/community-acs -name "*.amp" -o -name "*.war" -not -name "ROOT.war" -not -name "_vti_bin.war"`
|
||||
do
|
||||
zippaths+="$file|"
|
||||
done
|
||||
zippaths=${zippaths::-1}
|
||||
python3 ./third-party-license-overrides/thirdPartyLicenseCSVCreator.py --zippaths "${zippaths}" --version "${VERSION}" --combined --output "deploy_dir"
|
||||
rm -rf deploy_dir/community-acs
|
||||
echo "Local deploy directory content:"
|
||||
ls -lA deploy_dir
|
||||
|
||||
# Create deploy directory for AGS.
|
||||
mkdir -p deploy_dir_ags
|
||||
cp distribution-ags/target/*.zip deploy_dir_ags
|
||||
# Generate third party license csv for AGS.
|
||||
unzip deploy_dir_ags/*.zip -d deploy_dir_ags/community-ags
|
||||
zippaths=""
|
||||
for file in `find deploy_dir_ags/community-ags -name "*.amp" -o -name "*.war"`
|
||||
do
|
||||
zippaths+="$file|"
|
||||
done
|
||||
zippaths=${zippaths::-1}
|
||||
python3 ./third-party-license-overrides/thirdPartyLicenseCSVCreator.py --zippaths ${zippaths} --version "${VERSION}" --combined --output "deploy_dir_ags"
|
||||
rm -rf deploy_dir_ags/community-ags
|
||||
echo "Local AGS deploy directory content:"
|
||||
ls -lA deploy_dir_ags
|
||||
|
||||
# Tidy up.
|
||||
rm -rf ./third-party-license-overrides
|
||||
|
||||
popd
|
||||
set +vex
|
||||
echo "========================== Finishing Prepare Staging Deploy Script =========================="
|
||||
@@ -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> (7.2.0-EA or 7.2.0-SNAPSHOT)"
|
||||
echo "Please provide a RELEASE_VERSION in the format <acs-version>-<additional-info> (e.g. 7.2.0-A2)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function updatePomParent() {
|
||||
local PROJECT="$1"
|
||||
local PARENT_POM_VERSION="$2"
|
||||
|
||||
if [ -d "${ROOT_DIR}/${PROJECT}" -a -n "${PARENT_POM_VERSION}" ]
|
||||
then
|
||||
# Cannot use "mvn versions:update-parent" as the version must exist before it can be set. The ed command is also faster.
|
||||
pushd "${ROOT_DIR}/${PROJECT}" &>/dev/null
|
||||
ed -s pom.xml &>/tmp/$$.log << EOF
|
||||
/<parent>
|
||||
/<version>.*<\/version>/s//<version>${PARENT_POM_VERSION}<\/version>/
|
||||
wq
|
||||
EOF
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo
|
||||
echo "\"update of <pom><parent><version> version failed on ${PROJECT}"
|
||||
cat "/tmp/$$.log"
|
||||
exit 1
|
||||
fi
|
||||
printf "%-25s %46s=%s\n" "${PROJECT}" "<pom><parent><version>" "${PARENT_POM_VERSION}"
|
||||
popd &>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
function updatePomProperty() {
|
||||
local PROJECT="$1"
|
||||
local PROPERTY_VALUE="$2"
|
||||
local PROPERTY_NAME="$3"
|
||||
|
||||
if [ -d "${ROOT_DIR}/${PROJECT}" -a -n "${PROPERTY_VALUE}" ]
|
||||
then
|
||||
# Can use "mvn versions:set-property", but ed is so much faster.
|
||||
# mvn -B versions:set-property versions:commit -Dproperty="${PROPERTY_NAME}" "-DnewVersion=${PROPERTY_VALUE}"
|
||||
pushd "${ROOT_DIR}/${PROJECT}" &>/dev/null
|
||||
ed -s pom.xml &>/tmp/$$.log << EOF
|
||||
/\(<${PROPERTY_NAME}>\).*\(<\/${PROPERTY_NAME}>\)/s//\1${PROPERTY_VALUE}\2/
|
||||
wq
|
||||
EOF
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo
|
||||
echo "\"update of <${PROPERTY_NAME}> failed on ${PROJECT}"
|
||||
cat "/tmp/$$.log"
|
||||
exit 1
|
||||
fi
|
||||
printf "%-25s %46s=%s\n" "${PROJECT}" "<${PROPERTY_NAME}>" "${PROPERTY_VALUE}"
|
||||
popd &>/dev/null
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="${SCRIPT_DIR}/../../.."
|
||||
ENV_FILE=".linkPoms.env"
|
||||
ENV_PATH="${ROOT_DIR}/${ENV_FILE}"
|
||||
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/dev_functions.sh"
|
||||
|
||||
usage() {
|
||||
echo "Updates the downstream projects with the versions of the upstream projects. Reversed by unlinkPoms.sh" 1>&2;
|
||||
echo 1>&2;
|
||||
echo "Usage: $0 [-b <branch>] [-mpxuh]" 1>&2;
|
||||
echo " -m: Checkout master of each project" 1>&2;
|
||||
echo " -b: Checkout the <branch> of each project or master if <branch> is blank" 1>&2;
|
||||
echo " -p: Pull the latest version of each project" 1>&2;
|
||||
echo " -x: Skip the extract of values from each project" 1>&2;
|
||||
echo " -u: Skip the update of values in each project" 1>&2;
|
||||
echo " -h: Display this help" 1>&2;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
function checkout() {
|
||||
local PROJECT="${1}"
|
||||
local BRANCH="${2}"
|
||||
|
||||
if [ -d "${ROOT_DIR}/${PROJECT}" ]
|
||||
then
|
||||
pushd "${ROOT_DIR}/${PROJECT}" &>/dev/null
|
||||
git checkout "${BRANCH}" &>/tmp/$$.log
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo
|
||||
echo "\"git checkout ${BRANCH}\" failed on ${PROJECT}"
|
||||
cat "/tmp/$$.log"
|
||||
exit 1
|
||||
fi
|
||||
echo "${PROJECT} is now on ${BRANCH}"
|
||||
popd &>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
function pull_latest() {
|
||||
local PROJECT="${1}"
|
||||
|
||||
if [ -d "${ROOT_DIR}/${PROJECT}" ]
|
||||
then
|
||||
pushd "${ROOT_DIR}/${PROJECT}" &>/dev/null
|
||||
git pull &>/tmp/$$.log
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo
|
||||
echo "\"git pull\" failed on ${PROJECT}"
|
||||
cat "/tmp/$$.log"
|
||||
exit 1
|
||||
fi
|
||||
echo "${PROJECT} is now using latest"
|
||||
popd &>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
function readTopLevelTag() {
|
||||
local TAG_NAME="${1}"
|
||||
local POM_FILE="${2}"
|
||||
# Might be possible to generalise this function to accept an XPath so it could be used in place of sed commands
|
||||
|
||||
# Read the file with an IFS (Input Field Separator) of the start of XML tag character <
|
||||
local IFS=\>
|
||||
local DEPTH=-99
|
||||
while read -d \< ENTITY CONTENT
|
||||
do
|
||||
if [[ $ENTITY == project\ * ]] # outer <project> tag
|
||||
then
|
||||
DEPTH=0
|
||||
elif [[ $ENTITY == /* ]] # end tag
|
||||
then
|
||||
((DEPTH=DEPTH-1))
|
||||
else # start tag
|
||||
((DEPTH=DEPTH+1))
|
||||
fi
|
||||
|
||||
if [[ $ENTITY = "${TAG_NAME}" ]] && [[ $DEPTH == 1 ]] ; then
|
||||
echo $CONTENT
|
||||
exit
|
||||
fi
|
||||
done < $POM_FILE
|
||||
exit 1
|
||||
}
|
||||
|
||||
function exportPomVersion() {
|
||||
local PROJECT="${1}"
|
||||
local ENV_NAME="${2}"
|
||||
|
||||
if [ -d "${ROOT_DIR}/${PROJECT}" ]
|
||||
then
|
||||
pushd "${ROOT_DIR}/${PROJECT}" &>/dev/null
|
||||
# Same as slower/simpler: "mvn help:evaluate -Dexpression=project.version"
|
||||
VERSION=$(readTopLevelTag version pom.xml)
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo
|
||||
echo "\"readTopLevelTagContent version pom.xml\" failed on ${PROJECT}"
|
||||
exit 1
|
||||
fi
|
||||
echo "export ${ENV_NAME}=${VERSION}" >> "${ENV_PATH}"
|
||||
popd &>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
function exportPomParent() {
|
||||
local PROJECT="${1}"
|
||||
local ENV_NAME="${2}"
|
||||
|
||||
if [ -d "${ROOT_DIR}/${PROJECT}" ]
|
||||
then
|
||||
pushd "${ROOT_DIR}/${PROJECT}" &>/dev/null
|
||||
# Same as slower/simpler: "mvn help:evaluate -Dexpression=project.parent.version"
|
||||
PROPERTY_VALUE=$(sed -n '/<parent>/,/<\/parent>/p' pom.xml | sed -n "s/.*<version>\(.*\)<\/version>/\1/p" | sed 's/\r//g')
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo
|
||||
echo "\"sed -n '/<parent>/,/<\/parent>/p' pom.xml | sed -n \\\"s/.*<version>\(.*\)<\/version>/\1/p\\\" | sed 's/\r//g'\" failed on ${PROJECT}"
|
||||
exit 1
|
||||
fi
|
||||
echo "export ${ENV_NAME}=${PROPERTY_VALUE}" >> "${ENV_PATH}"
|
||||
popd &>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
# Original version was simpler/slower: exportPomPropertyOrig <project> <env_name> project.parent.version
|
||||
function exportPomProperty() {
|
||||
local PROJECT="${1}"
|
||||
local ENV_NAME="${2}"
|
||||
local PROPERTY_NAME="${3}"
|
||||
|
||||
if [ -d "${ROOT_DIR}/${PROJECT}" ]
|
||||
then
|
||||
pushd "${ROOT_DIR}/${PROJECT}" &>/dev/null
|
||||
# Same as slower/simpler: "mvn help:evaluate -Dexpression=${PROPERTY_NAME}"
|
||||
PROPERTY_VALUE=$(sed -n '/<properties>/,/<\/properties>/p' pom.xml | sed -n "s/.*<${PROPERTY_NAME}>\(.*\)<\/${PROPERTY_NAME}>/\1/p" | sed 's/\r//g')
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
echo
|
||||
echo "\"sed -n '/<properties>/,/<\/properties>/p' pom.xml | sed -n \\\"s/.*<${PROPERTY_NAME}>\(.*\)<\/${PROPERTY_NAME}>/\1/p\\\" | sed 's/\r//g'\" failed on ${PROJECT}"
|
||||
exit 1
|
||||
fi
|
||||
echo "export ${ENV_NAME}=${PROPERTY_VALUE}" >> "${ENV_PATH}"
|
||||
popd &>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
while getopts "b:mpxuh" arg; do
|
||||
case $arg in
|
||||
b)
|
||||
B_FLAG_SET="true"
|
||||
;;
|
||||
m)
|
||||
M_FLAG_B_FLAG_SET="true"
|
||||
;;
|
||||
p)
|
||||
# git pull after git checkout
|
||||
;;
|
||||
x)
|
||||
SKIP_EXPORT="true"
|
||||
;;
|
||||
u)
|
||||
SKIP_UPDATE="true"
|
||||
;;
|
||||
h | *)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [ -n "${B_FLAG_SET}" -a -n "${M_FLAG_B_FLAG_SET}" ]
|
||||
then
|
||||
echo "-m and -b may not both be set"
|
||||
exit 1
|
||||
fi
|
||||
OPTIND=1
|
||||
while getopts "b:mpxuh" arg; do
|
||||
case $arg in
|
||||
b)
|
||||
BRANCH="${OPTARG:-master}"
|
||||
checkout alfresco-community-repo "${BRANCH}"
|
||||
checkout alfresco-community-share "${BRANCH}"
|
||||
checkout acs-community-packaging "${BRANCH}"
|
||||
;;
|
||||
m)
|
||||
BRANCH="master"
|
||||
checkout alfresco-community-repo "${BRANCH}"
|
||||
checkout alfresco-community-share "${BRANCH}"
|
||||
checkout acs-community-packaging "${BRANCH}"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
OPTIND=1
|
||||
while getopts "b:mpxuh" arg; do
|
||||
case $arg in
|
||||
p)
|
||||
pull_latest alfresco-community-repo
|
||||
pull_latest alfresco-community-share
|
||||
pull_latest acs-community-packaging
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z ${SKIP_EXPORT+x} ]
|
||||
then
|
||||
rm -f "${ENV_FILE}"
|
||||
|
||||
exportPomVersion alfresco-community-repo COM_R_VERSION
|
||||
|
||||
exportPomVersion alfresco-enterprise-repo ENT_R_VERSION
|
||||
exportPomParent alfresco-enterprise-repo ENT_R_PARENT
|
||||
exportPomProperty alfresco-enterprise-repo ENT_R_DEP_COM_R dependency.alfresco-community-repo.version
|
||||
|
||||
exportPomVersion alfresco-community-share COM_S_VERSION
|
||||
exportPomProperty alfresco-community-share COM_S_DEP_COM_R dependency.alfresco-community-repo.version
|
||||
exportPomProperty alfresco-community-share COM_S_DEP_ENT_R dependency.alfresco-enterprise-repo.version
|
||||
|
||||
exportPomVersion acs-community-packaging COM_P_VERSION
|
||||
exportPomParent acs-community-packaging COM_P_PARENT
|
||||
exportPomProperty acs-community-packaging COM_P_DEP_COM_R dependency.alfresco-community-repo.version
|
||||
exportPomProperty acs-community-packaging COM_P_DEP_COM_S dependency.alfresco-community-share.version
|
||||
|
||||
cat "${ENV_FILE}"
|
||||
fi
|
||||
|
||||
if [ -z ${SKIP_UPDATE+x} ]
|
||||
then
|
||||
if [ ! -f "${ENV_FILE}" ]
|
||||
then
|
||||
echo ""${ENV_FILE}" does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source "${ENV_FILE}"
|
||||
|
||||
updatePomProperty alfresco-community-share "$COM_R_VERSION" dependency.alfresco-community-repo.version
|
||||
|
||||
updatePomParent acs-community-packaging "$COM_R_VERSION"
|
||||
updatePomProperty acs-community-packaging "$COM_R_VERSION" dependency.alfresco-community-repo.version
|
||||
updatePomProperty acs-community-packaging "$COM_S_VERSION" dependency.alfresco-community-share.version
|
||||
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
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
set -o errexit
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="${SCRIPT_DIR}/../../.."
|
||||
ENV_FILE=".linkPoms.env"
|
||||
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/dev_functions.sh"
|
||||
|
||||
usage() {
|
||||
echo "Reverts changes made by linkPoms.sh using values stored in ${ENV_FILE}" 1>&2;
|
||||
echo 1>&2;
|
||||
echo "Usage: $0 [-h]" 1>&2;
|
||||
echo " -h: Display this help" 1>&2;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
while getopts "lh" arg; do
|
||||
case $arg in
|
||||
l)
|
||||
LOGGING_OUT=`tty`
|
||||
;;
|
||||
h | *)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
source ${ENV_FILE}
|
||||
|
||||
updatePomProperty alfresco-community-share "$COM_S_DEP_COM_R" dependency.alfresco-community-repo.version
|
||||
|
||||
updatePomParent acs-community-packaging "$COM_P_PARENT"
|
||||
updatePomProperty acs-community-packaging "$COM_P_DEP_COM_R" dependency.alfresco-community-repo.version
|
||||
updatePomProperty acs-community-packaging "$COM_P_DEP_COM_S" dependency.alfresco-community-share.version
|
||||
|
||||
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.5.3
|
||||
SOLR6_TAG=2.0.2
|
||||
POSTGRES_TAG=13.3
|
||||
ACTIVEMQ_TAG=5.16.1
|
||||
SOLR6_TAG=2.0.21-A.2
|
||||
ACTIVEMQ_TAG=6.2.0-jre17-rockylinux8
|
||||
ELASTICSEARCH_TAG=8.17.0
|
||||
BATCH_INDEXING_TAG=5.7.0-A.5
|
||||
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/
|
||||
|
||||
@@ -28,7 +28,8 @@ services:
|
||||
-Ddb.url=jdbc:postgresql://postgres:5432/alfresco
|
||||
-Dsolr.host=solr6
|
||||
-Dsolr.port=8983
|
||||
-Dsolr.secureComms=none
|
||||
-Dsolr.secureComms=secret
|
||||
-Dsolr.sharedSecret=secret
|
||||
-Dsolr.base.url=/solr
|
||||
-Dindex.subsystem.name=solr6
|
||||
-Dalfresco.restApi.basicAuthScheme=true
|
||||
@@ -63,15 +64,19 @@ services:
|
||||
image: alfresco/alfresco-search-services:${SOLR6_TAG}
|
||||
environment:
|
||||
#Solr needs to know how to register itself with Alfresco
|
||||
- SOLR_ALFRESCO_HOST=alfresco
|
||||
- SOLR_ALFRESCO_PORT=8080
|
||||
SOLR_ALFRESCO_HOST: "alfresco"
|
||||
SOLR_ALFRESCO_PORT: "8080"
|
||||
#Alfresco needs to know how to call solr
|
||||
- SOLR_SOLR_HOST=solr6
|
||||
- SOLR_SOLR_PORT=8983
|
||||
SOLR_SOLR_HOST: "solr6"
|
||||
SOLR_SOLR_PORT: "8983"
|
||||
#Create the default alfresco and archive cores
|
||||
- SOLR_CREATE_ALFRESCO_DEFAULTS=alfresco,archive
|
||||
SOLR_CREATE_ALFRESCO_DEFAULTS: "alfresco,archive"
|
||||
#HTTP by default
|
||||
- ALFRESCO_SECURE_COMMS=none
|
||||
ALFRESCO_SECURE_COMMS: "secret"
|
||||
JAVA_TOOL_OPTIONS:
|
||||
"
|
||||
-Dalfresco.secureComms.secret=secret
|
||||
"
|
||||
ports:
|
||||
- 8083:8983 #Browser port
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@ services:
|
||||
-Ddb.url=jdbc:postgresql://postgres:5432/alfresco
|
||||
-Dsolr.host=solr6
|
||||
-Dsolr.port=8983
|
||||
-Dsolr.secureComms=none
|
||||
-Dsolr.secureComms=secret
|
||||
-Dsolr.sharedSecret=secret
|
||||
-Dsolr.base.url=/solr
|
||||
-Dindex.subsystem.name=solr6
|
||||
-Dalfresco.restApi.basicAuthScheme=true
|
||||
@@ -66,15 +67,19 @@ services:
|
||||
image: alfresco/alfresco-search-services:${SOLR6_TAG}
|
||||
environment:
|
||||
#Solr needs to know how to register itself with Alfresco
|
||||
- SOLR_ALFRESCO_HOST=alfresco
|
||||
- SOLR_ALFRESCO_PORT=8080
|
||||
SOLR_ALFRESCO_HOST: "alfresco"
|
||||
SOLR_ALFRESCO_PORT: "8080"
|
||||
#Alfresco needs to know how to call solr
|
||||
- SOLR_SOLR_HOST=solr6
|
||||
- SOLR_SOLR_PORT=8983
|
||||
SOLR_SOLR_HOST: "solr6"
|
||||
SOLR_SOLR_PORT: "8983"
|
||||
#Create the default alfresco and archive cores
|
||||
- SOLR_CREATE_ALFRESCO_DEFAULTS=alfresco,archive
|
||||
SOLR_CREATE_ALFRESCO_DEFAULTS: "alfresco,archive"
|
||||
#HTTP by default
|
||||
- ALFRESCO_SECURE_COMMS=none
|
||||
ALFRESCO_SECURE_COMMS: "secret"
|
||||
JAVA_TOOL_OPTIONS:
|
||||
"
|
||||
-Dalfresco.secureComms.secret=secret
|
||||
"
|
||||
ports:
|
||||
- 8083:8983 #Browser port
|
||||
|
||||
|
||||
+4
-1
@@ -6,15 +6,18 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>acs-community-packaging</artifactId>
|
||||
<version>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</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>
|
||||
<module>tas-integration</module>
|
||||
<module>tas-distribution-zip</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>content-repository-community-tests</artifactId>
|
||||
<version>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</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
|
||||
@@ -0,0 +1,76 @@
|
||||
<?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-distribution-test</artifactId>
|
||||
<name>Community Distribution test</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>content-repository-community-tests</artifactId>
|
||||
<version>26.2.0-A.28</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<dependency.assertj-core.version>3.13.2</dependency.assertj-core.version>
|
||||
<suiteXmlFile>${project.basedir}/src/test/resources/distribution-suite.xml</suiteXmlFile>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>3.26.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.alfresco.tas</groupId>
|
||||
<artifactId>restapi</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-model</artifactId>
|
||||
<version>3.9.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>run-distribution-zip-contents-check</id>
|
||||
<properties>
|
||||
<suiteXmlFile>${project.basedir}/src/test/resources/distribution-suite.xml</suiteXmlFile>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<suiteXmlFiles>
|
||||
<suiteXmlFile>${suiteXmlFile}</suiteXmlFile>
|
||||
</suiteXmlFiles>
|
||||
<argLine>
|
||||
--illegal-access=warn
|
||||
</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>
|
||||
+128
@@ -0,0 +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 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,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
|
||||
<suite name="Alfresco tests Community Distribution Zip" preserve-order="true" verbose="1">
|
||||
<test name="distribution" verbose="3" preserve-order="true">
|
||||
<classes>
|
||||
<class name="org.alfresco.distribution.CheckDistributionZipContents"></class>
|
||||
</classes>
|
||||
</test>
|
||||
</suite>
|
||||
@@ -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.28</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>7.2.0-A1</version>
|
||||
<version>26.2.0-A.28</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