Compare commits
624 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
39f4f8565a | |
|
|
de62d97f34 | |
|
|
1716c77486 | |
|
|
8b2932c764 | |
|
|
b026a90a0c | |
|
|
b9710a7b03 | |
|
|
f8e7abf9b1 | |
|
|
c10779dc6d | |
|
|
e960cc5ec2 | |
|
|
89b648a7f9 | |
|
|
a80291223c | |
|
|
74e439fbbc | |
|
|
99936be5f4 | |
|
|
2d63804206 | |
|
|
03e6b8e9ef | |
|
|
86e0a66fd9 | |
|
|
398a5edc95 | |
|
|
63c97ff93f | |
|
|
c3f56b5e9f | |
|
|
5ddeb0331c | |
|
|
ef5d4e0b45 | |
|
|
0ecd844d9e | |
|
|
39dfb2da5c | |
|
|
42c039d695 | |
|
|
fc226637e9 | |
|
|
817887dcce | |
|
|
1f61c4839a | |
|
|
16382ccfbd | |
|
|
7f0a44adc0 | |
|
|
d6e6216222 | |
|
|
3405832800 | |
|
|
a49bf667a9 | |
|
|
beb30eee62 | |
|
|
b9a3dd9d99 | |
|
|
31e8268760 | |
|
|
f56462a14d | |
|
|
454dd3d799 | |
|
|
8d095e1d99 | |
|
|
66e5339e3a | |
|
|
f69ca66cd6 | |
|
|
e108076aca | |
|
|
de8781e182 | |
|
|
06c52e48d4 | |
|
|
ee177a0f70 | |
|
|
836b34832b | |
|
|
68dd29b29b | |
|
|
bcaa5f1ae5 | |
|
|
7afce9daa2 | |
|
|
a8ae76b617 | |
|
|
6a983f97e6 | |
|
|
9336b5e298 | |
|
|
470435457d | |
|
|
28c39b65d2 | |
|
|
06097c8a20 | |
|
|
c0a2b93e45 | |
|
|
9126c80e41 | |
|
|
8d758d5446 | |
|
|
5f91d8b5c6 | |
|
|
0154c06422 | |
|
|
62bcdc5d78 | |
|
|
ab12b35b6a | |
|
|
5adf14cb24 | |
|
|
1f40f055ae | |
|
|
5578b79648 | |
|
|
94c03a9aec | |
|
|
050581067a | |
|
|
ef2db2c9c0 | |
|
|
6ffdd9ccbf | |
|
|
88ad859d3e | |
|
|
9babe49391 | |
|
|
fc391a7c66 | |
|
|
99baf2bde2 | |
|
|
e0f572b54c | |
|
|
37be6cdde3 | |
|
|
a93cc218d9 | |
|
|
d641fdb606 | |
|
|
423ede0746 | |
|
|
2432603d31 | |
|
|
eec38719d8 | |
|
|
d28526e141 | |
|
|
c6d94b47cc | |
|
|
534c81e5fc | |
|
|
18569c49cc | |
|
|
9a3daf824b | |
|
|
b12425c203 | |
|
|
41d0dbcd3a | |
|
|
496b15048a | |
|
|
de0360b118 | |
|
|
b7998c1c31 | |
|
|
ca5fc0cb29 | |
|
|
2e0cffe7c4 | |
|
|
00e040be14 | |
|
|
d7697f4718 | |
|
|
e66acccfda | |
|
|
f92c5a44ee | |
|
|
126885d86f | |
|
|
5b3d295d93 | |
|
|
21ef0bf755 | |
|
|
b7758ef178 | |
|
|
062771fa56 | |
|
|
66ff6682dd | |
|
|
053f562e0f | |
|
|
af1453402c | |
|
|
2fb3ced8cc | |
|
|
5eeeb9a612 | |
|
|
3c1dd5202c | |
|
|
dc4677673b | |
|
|
da244812c4 | |
|
|
7b20a9142d | |
|
|
a00e470f1b | |
|
|
33d8cee592 | |
|
|
33740f519a | |
|
|
6184183734 | |
|
|
f8cdff781c | |
|
|
df58ed3af9 | |
|
|
a2de993063 | |
|
|
c78aece72a | |
|
|
98dcfaf995 | |
|
|
d5b1e8e0b2 | |
|
|
4235a416dc | |
|
|
8e66ccf189 | |
|
|
f1d08f8ae0 | |
|
|
069b6e673f | |
|
|
6a97d93608 | |
|
|
a35739b55f | |
|
|
c92d5f6ea6 | |
|
|
95042946d2 | |
|
|
c64429a29f | |
|
|
649acf902f | |
|
|
4ba1c96caf | |
|
|
c6d152cb3b | |
|
|
b3ef56078d | |
|
|
4294b2415a | |
|
|
5a36a9cea3 | |
|
|
ac7dd6c2bf | |
|
|
dd46167402 | |
|
|
7e0062c0ad | |
|
|
9a083e068d | |
|
|
91aa759baa | |
|
|
42b99479b5 | |
|
|
adfc8ddd0c | |
|
|
6edb46cad7 | |
|
|
d926ec1af4 | |
|
|
bbc27c5285 | |
|
|
867f91793c | |
|
|
c1663b1d1b | |
|
|
e948ec96fe | |
|
|
37e618c7cb | |
|
|
bd70159204 | |
|
|
eaf00190be | |
|
|
aebfdb90a7 | |
|
|
1918a7a6f6 | |
|
|
ced7e368c1 | |
|
|
5c9902ce84 | |
|
|
0de3210053 | |
|
|
fcfa3e55c1 | |
|
|
ccec8d8cd7 | |
|
|
1b6ffad6b1 | |
|
|
beaa0a8846 | |
|
|
12466fb0bc | |
|
|
3f2edb0d9a | |
|
|
8bdafb5f3a | |
|
|
58c1a58797 | |
|
|
7646d488b2 | |
|
|
da6b123606 | |
|
|
c0220c78b4 | |
|
|
bc7fb1f43e | |
|
|
fe2d215518 | |
|
|
b642b5a4fa | |
|
|
3d8be7fe76 | |
|
|
f296a20b27 | |
|
|
0946d5dd0c | |
|
|
3900cc3671 | |
|
|
a53af864b3 | |
|
|
e139465335 | |
|
|
58f2d22048 | |
|
|
dce7995d9a | |
|
|
337dc86567 | |
|
|
8c1ee710d6 | |
|
|
1badd931e2 | |
|
|
e2c08f16d5 | |
|
|
42f9a7638b | |
|
|
c99cfcddc9 | |
|
|
a4cb44ea8f | |
|
|
21a8d61050 | |
|
|
ca501a202c | |
|
|
57e9f56e14 | |
|
|
f1474609e9 | |
|
|
a369ef1f4a | |
|
|
f838897e6b | |
|
|
f0b13952aa | |
|
|
3ab8b28914 | |
|
|
5dc3f9062f | |
|
|
d142d91cb8 | |
|
|
afe343bd13 | |
|
|
2294d2abc2 | |
|
|
392dffc495 | |
|
|
35e5a8c04a | |
|
|
96315aa197 | |
|
|
21855c67a3 | |
|
|
7b16f4c0f4 | |
|
|
0aeabe06da | |
|
|
0cb6115fdc | |
|
|
1a82d83117 | |
|
|
29ecc0492e | |
|
|
861a10083c | |
|
|
f6f3708197 | |
|
|
4274b724a8 | |
|
|
a657ce5012 | |
|
|
d2906a6db8 | |
|
|
c88963c34b | |
|
|
a529c02577 | |
|
|
355483f1b2 | |
|
|
7be8e6b1dd | |
|
|
0b24aca3cd | |
|
|
f88fad4711 | |
|
|
a767d73182 | |
|
|
0a0f2c3bfa | |
|
|
7dc6a86a7d | |
|
|
b087f085c1 | |
|
|
58f58ee094 | |
|
|
ba5b89e374 | |
|
|
1c577de9ba | |
|
|
4f0e4442ab | |
|
|
a038d2341c | |
|
|
df921e915d | |
|
|
7df736a77c | |
|
|
c231a6fddb | |
|
|
2e27c583b4 | |
|
|
459d4413b4 | |
|
|
dc3070f45b | |
|
|
162a104ebd | |
|
|
91f5b33bac | |
|
|
53f0e18216 | |
|
|
3427f42b81 | |
|
|
24ebe9da26 | |
|
|
7b9fd21d2a | |
|
|
d36e05ece8 | |
|
|
9baef8b255 | |
|
|
86e1003c16 | |
|
|
edb59ebaeb | |
|
|
73a4c9d028 | |
|
|
d6bc24e0f2 | |
|
|
5dfcc3f694 | |
|
|
899dc8e0f0 | |
|
|
85e096698a | |
|
|
bd83741f24 | |
|
|
882f3b7d65 | |
|
|
03b40a5244 | |
|
|
cedc000215 | |
|
|
9f89afc39a | |
|
|
37e71c4858 | |
|
|
e82a91ca1a | |
|
|
1e9752109e | |
|
|
506850caea | |
|
|
1a8959575c | |
|
|
ac93deebc6 | |
|
|
93c2e08780 | |
|
|
b056c5facd | |
|
|
c1e8ea5aba | |
|
|
253b8d38b8 | |
|
|
2b3bc4f28b | |
|
|
43cae1d2e3 | |
|
|
b4bc1f066a | |
|
|
7d335b2c72 | |
|
|
543bbfeb74 | |
|
|
906112c080 | |
|
|
6be756919b | |
|
|
3783247c74 | |
|
|
1228dec04d | |
|
|
d0a3ccc045 | |
|
|
6c72ca5f63 | |
|
|
f0d9d12ca7 | |
|
|
e605f732e2 | |
|
|
52f3d64b6a | |
|
|
3a1618faa2 | |
|
|
bd1d9db77b | |
|
|
b5d9dd2f02 | |
|
|
a560a6efee | |
|
|
37e4405c04 | |
|
|
483bca52ef | |
|
|
27cf52b007 | |
|
|
6a4041e21c | |
|
|
713d88d1fd | |
|
|
ef8f6128a3 | |
|
|
277f76ef03 | |
|
|
7b33574fd0 | |
|
|
c883f56a4e | |
|
|
0cfd8daed3 | |
|
|
c09982b0df | |
|
|
eb6570c992 | |
|
|
6861ed5e94 | |
|
|
3f127f01f1 | |
|
|
2e3adcde02 | |
|
|
47a68f098a | |
|
|
d0f63c8ac3 | |
|
|
dfee5bf6aa | |
|
|
eef3b355f6 | |
|
|
bc421bb197 | |
|
|
f6da2ee6cb | |
|
|
e79f1a5e73 | |
|
|
5cb9aad6ee | |
|
|
0b70e897f0 | |
|
|
0ee9a9b788 | |
|
|
75867796b7 | |
|
|
3f7dc2a8b2 | |
|
|
1d4532cd62 | |
|
|
b980df8697 | |
|
|
1dd0580d30 | |
|
|
de5904a600 | |
|
|
57cd0e8dd7 | |
|
|
7965aac70d | |
|
|
cf9ba8b310 | |
|
|
7635c67865 | |
|
|
965d6c137a | |
|
|
cb4427d9f1 | |
|
|
33fa9ed45d | |
|
|
4346b44f80 | |
|
|
2c6fcdc719 | |
|
|
44f99e968c | |
|
|
b52517cc47 | |
|
|
bd5d68f0f2 | |
|
|
bfe9fae150 | |
|
|
c997f35b04 | |
|
|
47aa8be850 | |
|
|
e8fb2696d6 | |
|
|
dcf3752515 | |
|
|
10f0a87c7b | |
|
|
20d1e5228e | |
|
|
8c93652de7 | |
|
|
ed493f3093 | |
|
|
d376b02dc5 | |
|
|
197e8a00ed | |
|
|
0c01475843 | |
|
|
c0ea21eed6 | |
|
|
5ad55fb82f | |
|
|
12b4940563 | |
|
|
25301f6228 | |
|
|
a6e1f232c2 | |
|
|
a7ad7ea100 | |
|
|
a9445ca3c3 | |
|
|
bce6d058f5 | |
|
|
69e6b461ad | |
|
|
4d5f4195db | |
|
|
70ab14e3e6 | |
|
|
e90489f361 | |
|
|
8db0a9b195 | |
|
|
b43526da60 | |
|
|
d4b95ca323 | |
|
|
73329ce4f5 | |
|
|
869fc81811 | |
|
|
45636a0489 | |
|
|
fc9bc2866e | |
|
|
30ef2f7c6b | |
|
|
d7c1c2149c | |
|
|
b49be05171 | |
|
|
4ceef1ab0c | |
|
|
8e99d85d33 | |
|
|
1d0969dede | |
|
|
aaf67cf81c | |
|
|
8a515f20db | |
|
|
77c76124af | |
|
|
4d7ad86776 | |
|
|
d12f5734e6 | |
|
|
b02c2bdaa7 | |
|
|
7927335303 | |
|
|
c922b10c31 | |
|
|
c31a53641f | |
|
|
5df28fa2ca | |
|
|
23776a7686 | |
|
|
8ee3d32b4f | |
|
|
5227248799 | |
|
|
80a4cd6877 | |
|
|
324cbb2c1f | |
|
|
258d1812ff | |
|
|
b836426db0 | |
|
|
b4e67e1dfb | |
|
|
b8c7743616 | |
|
|
bcf2a395cf | |
|
|
a946ad1de4 | |
|
|
e1d9607410 | |
|
|
f23c648937 | |
|
|
0f2faf48dc | |
|
|
297e491232 | |
|
|
267ff6d3a8 | |
|
|
7b6a4bd136 | |
|
|
6997a87d0c | |
|
|
9e4fb9647e | |
|
|
4b74ab6cec | |
|
|
6409d9277d | |
|
|
82c7e78a8c | |
|
|
901da45e59 | |
|
|
31c6c6df13 | |
|
|
a0f34d5973 | |
|
|
d968deb124 | |
|
|
6c0d596eac | |
|
|
76fa64498a | |
|
|
1d524760eb | |
|
|
271d72c9f3 | |
|
|
614d47159d | |
|
|
82f2a6539f | |
|
|
f9593d494b | |
|
|
c8e0f0c3e1 | |
|
|
6aa01af49e | |
|
|
6f9493a517 | |
|
|
effedb0c23 | |
|
|
eed6103359 | |
|
|
e1cb3eb16c | |
|
|
e4633e1cdb | |
|
|
fc6e3702da | |
|
|
91a616bb38 | |
|
|
f49b2b808d | |
|
|
f6018d1071 | |
|
|
958193d797 | |
|
|
3427625379 | |
|
|
8724b12032 | |
|
|
1f64e8afb0 | |
|
|
c28f8c8977 | |
|
|
e339fed6c9 | |
|
|
04112774d4 | |
|
|
e400e4d22b | |
|
|
b385c45c35 | |
|
|
c37556919b | |
|
|
62d52a20ba | |
|
|
52807b0aa0 | |
|
|
b3949997fd | |
|
|
7efcf879c1 | |
|
|
93614cf29d | |
|
|
80bd82bde1 | |
|
|
0239a1e174 | |
|
|
fb48e831f7 | |
|
|
9463119a67 | |
|
|
a40774e484 | |
|
|
c86e38a8ac | |
|
|
37e221a712 | |
|
|
5bc993fe79 | |
|
|
4dbe157ecb | |
|
|
caa4b26a98 | |
|
|
952975a3c7 | |
|
|
7347b17a47 | |
|
|
16602cc839 | |
|
|
4361a51d9d | |
|
|
dc3e9e2bbd | |
|
|
ae54f28308 | |
|
|
b783da5bde | |
|
|
be72d20f8a | |
|
|
e85ab38c6b | |
|
|
3b1bcb1a99 | |
|
|
42ef9c4d95 | |
|
|
09b18191ed | |
|
|
745bd861af | |
|
|
4af14a9998 | |
|
|
589ee2ca10 | |
|
|
db84121d5d | |
|
|
deac513c43 | |
|
|
f2e838443d | |
|
|
d0e5971c7e | |
|
|
88d2f897ed | |
|
|
05fe7a4c92 | |
|
|
4b21e94ad2 | |
|
|
d695bd494d | |
|
|
922a4f6334 | |
|
|
8f0f2ba012 | |
|
|
4a1926fe40 | |
|
|
fbb743afeb | |
|
|
3b8933c3d3 | |
|
|
f5383d5541 | |
|
|
ee08712120 | |
|
|
24c3390379 | |
|
|
0789b1c8c1 | |
|
|
c572c2bedd | |
|
|
1d27452994 | |
|
|
53c6503cde | |
|
|
3881b86697 | |
|
|
49e8599d8f | |
|
|
535ae072f4 | |
|
|
efa8e97c4f | |
|
|
9101842200 | |
|
|
27dfd9d5a2 | |
|
|
74b954acf2 | |
|
|
f72fac400f | |
|
|
185a926a94 | |
|
|
aecaf4e86a | |
|
|
fe190141cc | |
|
|
d51efb6120 | |
|
|
6c800388a1 | |
|
|
159143c04d | |
|
|
265f74bb5f | |
|
|
98844d8c2a | |
|
|
3914813fb8 | |
|
|
1114e94924 | |
|
|
42e5260ba5 | |
|
|
ccc4d3bd6a | |
|
|
cde7763cc2 | |
|
|
c80f959579 | |
|
|
f4e861b3a8 | |
|
|
37de7be0ee | |
|
|
9962e0c9c9 | |
|
|
2d4a8eebf8 | |
|
|
630e19f8bc | |
|
|
9f329f5f3a | |
|
|
544599e316 | |
|
|
b4fb1f7443 | |
|
|
034e1a2513 | |
|
|
c2c5ba4689 | |
|
|
0d90ae4331 | |
|
|
fffccf1b2f | |
|
|
0a71d120f9 | |
|
|
4bd64ce8b5 | |
|
|
24b8a33e9c | |
|
|
8268b755ef | |
|
|
d78b728aa2 | |
|
|
49bff5eadc | |
|
|
4eda6313eb | |
|
|
0ff4b0e234 | |
|
|
41bc25ce85 | |
|
|
f8e65f83bd | |
|
|
cc463411ea | |
|
|
0436a14591 | |
|
|
ce75fb96e0 | |
|
|
88d8838209 | |
|
|
b525aef026 | |
|
|
85a6b7e24d | |
|
|
48a39fcef8 | |
|
|
2f453c0af9 | |
|
|
64bd1543e8 | |
|
|
1a66a60897 | |
|
|
0822dd93f6 | |
|
|
5ea077f13e | |
|
|
194ed53b85 | |
|
|
1eb2414134 | |
|
|
87c5826480 | |
|
|
306b4f1829 | |
|
|
95530cc4b3 | |
|
|
5bac575b29 | |
|
|
5f6012cbe4 | |
|
|
bbbc8e70f5 | |
|
|
6f20cb8bdc | |
|
|
476bb78315 | |
|
|
146e4bc362 | |
|
|
904f4260b9 | |
|
|
16fbcb354d | |
|
|
591d3f3834 | |
|
|
07793f9971 | |
|
|
1c7c839d45 | |
|
|
a4121451ce | |
|
|
553cd60b8f | |
|
|
1db56cfd86 | |
|
|
9db431015a | |
|
|
eed956d931 | |
|
|
50c7738056 | |
|
|
92ad62a9fb | |
|
|
4aae65cd4d | |
|
|
8c76d5b0cf | |
|
|
daa967d74d | |
|
|
544b94c919 | |
|
|
140cf845b5 | |
|
|
9346717bde | |
|
|
d6d92e4896 | |
|
|
aa222c4893 | |
|
|
c2db8f55d6 | |
|
|
887a4d5a9a | |
|
|
5183fb054a | |
|
|
1bf8d9e4c0 | |
|
|
56a60dca53 | |
|
|
12c65918b3 | |
|
|
cb33bbd436 | |
|
|
154e03054e | |
|
|
8d8333980c | |
|
|
fde9c72a0f | |
|
|
749a2d9d1f | |
|
|
2ae8e1571b | |
|
|
8219a10e74 | |
|
|
9f994b33de | |
|
|
fe8b51d072 | |
|
|
24ff820174 | |
|
|
8a44989e32 | |
|
|
7789bc87d8 | |
|
|
65032356e9 | |
|
|
1804fa7ee1 | |
|
|
3d3da162de | |
|
|
b01f3a35df | |
|
|
094f8bb56b | |
|
|
d8d4b3eba5 | |
|
|
5e003a7f2f | |
|
|
5195740642 | |
|
|
12c53cc17b | |
|
|
4d6c421358 | |
|
|
281aab85f2 | |
|
|
ca0a9832a6 | |
|
|
b4a6cc9ab4 | |
|
|
c24069ee26 | |
|
|
504069e17f | |
|
|
705c27b986 | |
|
|
f6cdc8a3ca | |
|
|
8bdac2c758 | |
|
|
a200deab56 | |
|
|
5cda854e8d | |
|
|
f33c09385d | |
|
|
dec8fe9a84 | |
|
|
dfd3190392 | |
|
|
b76e0eaeab | |
|
|
6052985c15 | |
|
|
e05d13098a | |
|
|
9faea02296 | |
|
|
5252a7e481 | |
|
|
0c580690cc | |
|
|
315000a600 | |
|
|
d3960c1da4 | |
|
|
9778a8b4b3 | |
|
|
171e723f50 | |
|
|
9a1417f3dc | |
|
|
9cc9cad29a | |
|
|
e88f54a21e | |
|
|
ec2599db9a | |
|
|
d592690cf7 | |
|
|
36959044a1 | |
|
|
7bdf878dd4 | |
|
|
5603b9c6a3 | |
|
|
cccef5d575 | |
|
|
db55e237a5 | |
|
|
3e5f626d11 | |
|
|
5f305526d7 | |
|
|
a5fa9016ec |
36
.asf.yaml
36
.asf.yaml
|
|
@ -17,12 +17,11 @@
|
|||
|
||||
github:
|
||||
features:
|
||||
# Enable issue management
|
||||
issues: true
|
||||
# Enable wiki for documentation
|
||||
wiki: true
|
||||
# Enable projects for project management boards
|
||||
# Enable projects for project (task)management boards
|
||||
projects: true
|
||||
discussions: true
|
||||
description: A graph database that supports more than 100+ billion data, high performance and scalability (Include OLTP Engine & REST-API & Backends)
|
||||
homepage: https://hugegraph.apache.org
|
||||
del_branch_on_merge: true
|
||||
|
|
@ -34,23 +33,32 @@ github:
|
|||
protected_branches:
|
||||
master:
|
||||
required_status_checks:
|
||||
# strict means "Require branches to be up-to-date before merging".
|
||||
strict: true
|
||||
# strict means "Require PR to be up-to-date before merging". (enable when branch unstable)
|
||||
strict: false
|
||||
# contexts are the names of checks that must pass (now only enable the basic check)
|
||||
contexts:
|
||||
- Analyze (java)
|
||||
- CodeQL
|
||||
- build (memory, 8)
|
||||
- build (memory, 11)
|
||||
- check-license
|
||||
- build-server (memory, 11)
|
||||
- build-commons (11)
|
||||
required_pull_request_reviews:
|
||||
dismiss_stale_reviews: true
|
||||
require_code_owner_reviews: false
|
||||
required_approving_review_count: 2
|
||||
required_approving_review_count: 1
|
||||
# (for non-committer): assign/edit/close issues & PR, without write access to the code
|
||||
collaborators:
|
||||
- haohao0103
|
||||
- kenssa4eedfd
|
||||
- Tsukilc
|
||||
|
||||
# refer https://cwiki.apache.org/confluence/display/INFRA/Git+-+.asf.yaml+features#Git.asf.yamlfeatures-Notificationsettingsforrepositories
|
||||
notifications:
|
||||
pullrequests_status: dev@hugegraph.apache.org
|
||||
# before use the config, we should ensure the "mail" address has set well (exist)
|
||||
#pullrequests_comment: issues@hugegraph.apache.org
|
||||
#issues: issues@hugegraph.apache.org
|
||||
#discussions: issues@hugegraph.apache.org
|
||||
|
||||
# use https://selfserve.apache.org to manage it
|
||||
pullrequests_status: issues@hugegraph.apache.org
|
||||
pullrequests_comment: issues@hugegraph.apache.org
|
||||
pullrequests_bot_dependabot: issues@hugegraph.apache.org
|
||||
issues: issues@hugegraph.apache.org
|
||||
issues_status: dev@hugegraph.apache.org
|
||||
issues_comment: issues@hugegraph.apache.org
|
||||
discussions: dev@hugegraph.apache.org
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# IMPORTANT: .dockerignore does NOT inherit .gitignore — patterns must be restated.
|
||||
|
||||
# Build output
|
||||
**/target/
|
||||
|
||||
# Pre-extracted release dirs / archives
|
||||
apache-hugegraph-*/
|
||||
**/*.tar
|
||||
**/*.tar.gz
|
||||
**/*.zip
|
||||
**/*.war
|
||||
|
||||
# IDE / OS
|
||||
.idea/
|
||||
.vscode/
|
||||
**/*.iml
|
||||
**/*.iws
|
||||
**/.DS_Store
|
||||
|
||||
# Build / runtime artifacts
|
||||
**/logs/
|
||||
**/*.log
|
||||
**/*.class
|
||||
**/gen-java/
|
||||
**/upload-files/
|
||||
**/build/
|
||||
**/node_modules/
|
||||
|
||||
# Env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Git internals
|
||||
.git
|
||||
.gitignore
|
||||
.gitattributes
|
||||
.github
|
||||
|
||||
# Compose / docs not needed in build context
|
||||
**/docker-compose*.yml
|
||||
**/docker-compose*.yaml
|
||||
**/*.md
|
||||
docs/
|
||||
|
|
@ -21,7 +21,79 @@ root = true
|
|||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
max_line_length = 100
|
||||
ij_wrap_on_typing = true
|
||||
ij_visual_guides = 100
|
||||
|
||||
|
||||
[*.{java,xml,py}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{java,xml}]
|
||||
# Ignore the IDEA unsupported warning & it works well (indeed)
|
||||
continuation_indent_size = 8
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
|
||||
[*.java]
|
||||
ij_java_doc_add_p_tag_on_empty_lines = true
|
||||
ij_java_doc_do_not_wrap_if_one_line = true
|
||||
ij_java_annotation_parameter_wrap = normal
|
||||
ij_java_align_multiline_annotation_parameters = true
|
||||
ij_java_class_count_to_use_import_on_demand = 100
|
||||
ij_java_names_count_to_use_import_on_demand = 100
|
||||
ij_java_imports_layout = $*, |, java.**, |, javax.**, |, org.**, |, com.**, |, *
|
||||
|
||||
ij_java_line_comment_at_first_column = false
|
||||
ij_java_align_multiline_chained_methods = true
|
||||
ij_java_align_multiline_parameters_in_calls = true
|
||||
ij_java_align_multiline_binary_operation = true
|
||||
ij_java_align_multiline_assignment = true
|
||||
ij_java_align_multiline_ternary_operation = true
|
||||
ij_java_align_multiline_throws_list = true
|
||||
ij_java_align_multiline_extends_list = true
|
||||
ij_java_align_multiline_array_initializer_expression = true
|
||||
|
||||
ij_java_call_parameters_wrap = normal
|
||||
ij_java_method_parameters_wrap = normal
|
||||
ij_java_resource_list_wrap = normal
|
||||
ij_java_extends_list_wrap = normal
|
||||
ij_java_throws_list_wrap = normal
|
||||
ij_java_method_call_chain_wrap = normal
|
||||
ij_java_binary_operation_wrap = normal
|
||||
ij_java_ternary_operation_wrap = normal
|
||||
ij_java_for_statement_wrap = normal
|
||||
ij_java_array_initializer_wrap = normal
|
||||
ij_java_assignment_wrap = normal
|
||||
ij_java_assert_statement_wrap = normal
|
||||
ij_java_if_brace_force = if_multiline
|
||||
ij_java_do_while_brace_force = always
|
||||
ij_java_while_brace_force = if_multiline
|
||||
ij_java_for_brace_force = if_multiline
|
||||
ij_java_wrap_long_lines = true
|
||||
ij_java_parameter_annotation_wrap = normal
|
||||
ij_java_enum_constants_wrap = split_into_lines
|
||||
|
||||
ij_java_keep_blank_lines_in_declarations = 1
|
||||
ij_java_keep_blank_lines_in_code = 1
|
||||
ij_java_keep_blank_lines_between_package_declaration_and_header = 1
|
||||
ij_java_keep_blank_lines_before_right_brace = 1
|
||||
ij_java_blank_lines_around_class = 1
|
||||
ij_java_blank_lines_after_class_header = 1
|
||||
|
||||
ij_smart_tabs = true
|
||||
|
||||
[*.xml]
|
||||
max_line_length = 120
|
||||
ij_visual_guides = 120
|
||||
ij_xml_text_wrap = off
|
||||
ij_xml_space_inside_empty_tag = true
|
||||
|
||||
[.yaml]
|
||||
ij_yaml_spaces_within_braces = false
|
||||
ij_yaml_spaces_within_brackets = false
|
||||
|
||||
[.properties]
|
||||
ij_properties_keep_blank_lines = true
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@
|
|||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.asf.yaml export-ignore
|
||||
checkstyle.xml export-ignore
|
||||
apache-release.sh export-ignore
|
||||
.licenserc.yaml export-ignore
|
||||
.editorconfig export-ignore
|
||||
hugegraph-store/hg-store-dist/src/assembly/static/bin/libjemalloc.so export-ignore
|
||||
hugegraph-store/hg-store-dist/src/assembly/static/bin/libjemalloc_aarch64.so export-ignore
|
||||
|
||||
# ignored directory
|
||||
.github/ export-ignore
|
||||
hugegraph-dist/scripts/ export-ignore
|
||||
style/ export-ignore
|
||||
#assembly/ export-ignore
|
||||
.idea/ export-ignore
|
||||
install-dist/scripts/ export-ignore
|
||||
hugegraph-commons/hugegraph-dist/ export-ignore
|
||||
docker/ export-ignore
|
||||
|
|
|
|||
|
|
@ -10,18 +10,18 @@ body:
|
|||
value: >-
|
||||
### Note (特别注意) :
|
||||
|
||||
> 1. 请先**搜索**现有的[Issues](https://github.com/hugegraph/hugegraph/issues) 与
|
||||
[FAQ](https://hugegraph.github.io/hugegraph-doc/guides/faq.html) 中没有与您相同
|
||||
> 1. 请先**搜索**现有的[Issues](https://github.com/apache/hugegraph/issues) 与
|
||||
[FAQ](https://hugegraph.apache.org/docs/guides/faq/) 中没有与您相同
|
||||
/ 相关的问题 (请勿重复提交)
|
||||
|
||||
> 2. 我们需要尽可能**详细**的信息来**复现**问题, 越详细的信息 (包括**日志 / 截图 / 配置**等)
|
||||
会**越快**被响应和处理
|
||||
|
||||
> 3. Issue 标题请保持原有模板分类(例如:`[Bug]`), 长段描述之间可以增加`空行`或使用`序号`标记, 保持排版清晰
|
||||
> 3. Issue 标题请保留原有分类标签 (例如:`[Bug]`), 长段描述请增加`空行`并使用`序号`标记, 保持排版清晰
|
||||
|
||||
> 4. 请在对应的模块提交 issue, 缺乏有效信息 / 长时间 (> 14 天) 没有回复的 issue 可能会被 **关闭**
|
||||
(更新时会再开启)
|
||||
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Bug Type (问题类型)
|
||||
|
|
@ -31,36 +31,38 @@ body:
|
|||
- other exception / error (其他异常报错)
|
||||
- server status (启动/运行异常)
|
||||
- logic (逻辑设计问题)
|
||||
- performence (性能下降)
|
||||
- performance (性能下降)
|
||||
- others (please edit later)
|
||||
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Before submit
|
||||
options:
|
||||
- label: 我已经确认现有的 [Issues](https://github.com/hugegraph/hugegraph/issues) 与 [FAQ](https://hugegraph.github.io/hugegraph-doc/guides/faq.html) 中没有相同 / 重复问题
|
||||
- label: '我已经确认现有的 [Issues](https://github.com/apache/hugegraph/issues) 与
|
||||
[FAQ](https://hugegraph.apache.org/docs/guides/faq/) 中没有相同 / 重复问题 (I have confirmed
|
||||
and searched that there are no similar problems in the historical issue and documents)'
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment (环境信息)
|
||||
description: |
|
||||
> server version could get from [rest-api](https://hugegraph.github.io/hugegraph-doc/clients/restful-api/other.html) (http://localhost:8080/versions)
|
||||
> server version could get from [rest-api](https://hugegraph.apache.org/docs/clients/restful-api/) (http://localhost:8080/versions)
|
||||
value: |
|
||||
- Server Version: v0.11.x
|
||||
- Server Version: 1.0.0 (Apache Release Version)
|
||||
- Backend: RocksDB x nodes, HDD or SSD
|
||||
- OS: xx CPUs, xx G RAM, Centos 7.x
|
||||
- OS: xx CPUs, xx G RAM, Ubuntu 2x.x / CentOS 7.x
|
||||
- Data Size: xx vertices, xx edges <!-- (like 1000W 点, 9000W 边) -->
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected & Actual behavior (期望与实际表现)
|
||||
description: |
|
||||
> we can refer [How to create a minimal reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) (如何提供最简的可复现用例)
|
||||
> if possible, please provide screenshots or GIF (请提供清晰的截图, 动图录屏更佳)
|
||||
placeholder: |
|
||||
> if possible, please provide **screenshots** / Pic or `GIF` (请提供清晰的截图, 动图录屏更佳)
|
||||
placeholder: |
|
||||
type the main problem here
|
||||
|
||||
```java
|
||||
|
|
@ -69,7 +71,7 @@ body:
|
|||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Vertex/Edge example (问题点 / 边数据举例)
|
||||
|
|
@ -84,7 +86,7 @@ body:
|
|||
"vertex": { "id": "xxx" }
|
||||
}
|
||||
render: javascript
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Schema [VertexLabel, EdgeLabel, IndexLabel] (元数据结构)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ blank_issues_enabled: false
|
|||
|
||||
# 设置提 issue 前的参考文档
|
||||
contact_links:
|
||||
- name: HugeGraph Server Doc
|
||||
url: https://hugegraph.github.io/hugegraph-doc/quickstart/hugegraph-server.html
|
||||
- name: HugeGraph Server Doc (EN/CN)
|
||||
url: https://hugegraph.apache.org/docs/quickstart/hugegraph-server/
|
||||
about: Please search question here before opening a new issue
|
||||
- name: HugeGraph API Doc
|
||||
url: https://hugegraph.github.io/hugegraph-doc/clients/hugegraph-api.html
|
||||
- name: HugeGraph API Doc (EN/CN)
|
||||
url: https://hugegraph.apache.org/docs/clients/
|
||||
about: Please search usage here before opening a new issue
|
||||
|
|
|
|||
|
|
@ -10,6 +10,6 @@ body:
|
|||
label: Feature Description (功能描述)
|
||||
description: |
|
||||
> 请简要描述新功能 / 需求的使用场景或上下文, 最好能给个具体的例子说明
|
||||
placeholder: type the feature description here
|
||||
placeholder: type the feature description here
|
||||
validations:
|
||||
required: true
|
||||
|
|
|
|||
|
|
@ -8,18 +8,18 @@ body:
|
|||
value: >-
|
||||
### Note (特别注意) :
|
||||
|
||||
> 1. 请先**搜索**现有的[Issues](https://github.com/hugegraph/hugegraph/issues) 与
|
||||
[FAQ](https://hugegraph.github.io/hugegraph-doc/guides/faq.html) 中没有与您相同
|
||||
> 1. 请先**搜索**现有的[Issues](https://github.com/apache/hugegraph/issues) 与
|
||||
[FAQ](https://hugegraph.apache.org/docs/guides/faq/) 中没有与您相同
|
||||
/ 相关的问题 (请勿重复提交)
|
||||
|
||||
> 2. 我们需要尽可能**详细**的信息来**复现**问题, 越详细的信息 (包括**日志 / 截图 / 配置**等)
|
||||
会**越快**被响应和处理
|
||||
|
||||
> 3. Issue 标题请保持原有模板分类(例如:`[Bug]`), 长段描述之间可以增加`空行`或使用`序号`标记, 保持排版清晰
|
||||
> 3. Issue 标题请保留原有分类标签 (例如:`[Bug]`), 长段描述请增加`空行`并使用`序号`标记, 保持排版清晰
|
||||
|
||||
> 4. 请在对应的模块提交 issue, 缺乏有效信息 / 长时间 (> 14 天) 没有回复的 issue 可能会被 **关闭**
|
||||
(更新时会再开启)
|
||||
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Problem Type (问题类型)
|
||||
|
|
@ -29,37 +29,40 @@ body:
|
|||
- server status (启动/运行异常)
|
||||
- configs (配置项 / 文档相关)
|
||||
- struct / logic (架构 / 逻辑设计问题)
|
||||
- performence (性能优化)
|
||||
- performance (性能优化)
|
||||
- other exception / error (其他异常报错)
|
||||
- others (please edit later)
|
||||
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Before submit
|
||||
options:
|
||||
- label: 我已经确认现有的 [Issues](https://github.com/hugegraph/hugegraph/issues) 与 [FAQ](https://hugegraph.github.io/hugegraph-doc/guides/faq.html) 中没有相同 / 重复问题
|
||||
- label: '我已经确认现有的 [Issues](https://github.com/apache/hugegraph/issues) 与
|
||||
[FAQ](https://hugegraph.apache.org/docs/guides/faq/) 中没有相同 / 重复问题 (I have confirmed
|
||||
and searched that there are no similar problems in the historical issue and documents)'
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment (环境信息)
|
||||
description: |
|
||||
> server version could get from [rest-api](https://hugegraph.github.io/hugegraph-doc/clients/restful-api/other.html) (http://localhost:8080/versions)
|
||||
> server version could get from [rest-api](https://hugegraph.apache.org/docs/clients/restful-api/) (http://localhost:8080/versions)
|
||||
value: |
|
||||
- Server Version: v0.11.x
|
||||
- Server Version: 1.0.0 (Apache Release Version)
|
||||
- Backend: RocksDB x nodes, HDD or SSD
|
||||
- OS: xx CPUs, xx G RAM, Centos 7.x
|
||||
- OS: xx CPUs, xx G RAM, Ubuntu 2x.x / CentOS 7.x
|
||||
- Data Size: xx vertices, xx edges <!-- (like 1000W 点, 9000W 边) -->
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Your Question (问题描述)
|
||||
description: |
|
||||
> 图使用 / 配置相关问题,请优先参考 [REST-API 文档](https://hugegraph.github.io/hugegraph-doc/clients/hugegraph-api.html), 以及 [Server 配置文档](https://hugegraph.github.io/hugegraph-doc/config/config-option.html)
|
||||
> if possible, please provide screenshots or GIF (请提供清晰的截图, 动图录屏更佳)
|
||||
placeholder: |
|
||||
> 图使用 / 配置相关问题,请优先参考 [REST-API 文档](https://hugegraph.apache.org/docs/clients/restful-api/),
|
||||
> 以及 [Server 配置文档](https://hugegraph.apache.org/docs/config/)
|
||||
> if possible, please provide **screenshots** / Pic or `GIF` (请提供清晰的截图, 动图录屏更佳)
|
||||
placeholder: |
|
||||
type the main problem here
|
||||
|
||||
```java
|
||||
|
|
@ -68,7 +71,7 @@ body:
|
|||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Vertex/Edge example (问题点 / 边数据举例)
|
||||
|
|
@ -83,7 +86,7 @@ body:
|
|||
"vertex": { "id": "xxx" }
|
||||
}
|
||||
render: javascript
|
||||
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Schema [VertexLabel, EdgeLabel, IndexLabel] (元数据结构)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
<!--
|
||||
Thank you very much for contributing to Apache HugeGraph, we are happy that you want to help us improve it!
|
||||
|
||||
Here are some tips for you:
|
||||
1. If this is your first time, please read the [contributing guidelines](https://github.com/apache/hugegraph/blob/master/CONTRIBUTING.md)
|
||||
|
||||
2. If a PR fix/close an issue, type the message "close xxx" (xxx is the link of related
|
||||
issue) in the content, GitHub will auto link it (Required)
|
||||
|
||||
3. Name the PR title in "Google Commit Format", start with "feat | fix | perf | refactor | doc | chore",
|
||||
such like: "feat(core): support the PageRank algorithm" or "fix: wrong break in the compute loop" (module is optional)
|
||||
skip it if you are unsure about which is the best component.
|
||||
|
||||
4. One PR address one issue, better not to mix up multiple issues.
|
||||
|
||||
5. Put an `x` in the `[ ]` to mark the item as CHECKED. `[x]` (or click it directly after
|
||||
published)
|
||||
-->
|
||||
|
||||
## Purpose of the PR
|
||||
|
||||
- close #xxx <!-- or use "fix #xxx", "xxx" is the ID-link of related issue, e.g: close #1024 -->
|
||||
|
||||
<!--
|
||||
Please explain more context in this section, clarify why the changes are needed.
|
||||
e.g:
|
||||
- If you propose a new API, clarify the use case for a new API.
|
||||
- If you fix a bug, you can clarify why it is a bug, and should be associated with an issue.
|
||||
-->
|
||||
|
||||
## Main Changes
|
||||
|
||||
<!-- Please clarify what changes you are proposing. The purpose of this section is to outline the changes and how this PR fixes the issue. These change logs are helpful for better and faster reviews.)
|
||||
|
||||
For example:
|
||||
|
||||
- If you introduce a new feature, please show detailed design here or add the link of design documentation.
|
||||
- If you refactor some codes with changing classes, showing the class hierarchy will help reviewers.
|
||||
- If there is a discussion in the mailing list, please add the link. -->
|
||||
|
||||
## Verifying these changes
|
||||
|
||||
<!-- Please pick the proper options below -->
|
||||
|
||||
- [ ] Trivial rework / code cleanup without any test coverage. (No Need)
|
||||
- [ ] Already covered by existing tests, such as *(please modify tests here)*.
|
||||
- [ ] Need tests and can be verified as follows:
|
||||
- xxx
|
||||
|
||||
## Does this PR potentially affect the following parts?
|
||||
|
||||
<!-- DO NOT REMOVE THIS SECTION. CHECK THE PROPER BOX ONLY. -->
|
||||
|
||||
- [ ] Dependencies ([add/update license](https://hugegraph.apache.org/docs/contribution-guidelines/contribute/#321-check-licenses) info & [regenerate_known_dependencies.sh](../install-dist/scripts/dependency/regenerate_known_dependencies.sh)) <!-- Don't forget to add/update the info in "LICENSE" & "NOTICE" files (both in root & dist module) -->
|
||||
- [ ] Modify configurations
|
||||
- [ ] The public API
|
||||
- [ ] Other affects (typed here)
|
||||
- [ ] Nope
|
||||
|
||||
|
||||
## Documentation Status
|
||||
|
||||
<!-- DO NOT REMOVE THIS SECTION. CHECK THE PROPER BOX ONLY. -->
|
||||
|
||||
- [ ] `Doc - TODO` <!-- Your PR changes impact docs and you will update later -->
|
||||
- [ ] `Doc - Done` <!-- Related docs have been already added or updated -->
|
||||
- [ ] `Doc - No Need` <!-- Your PR changes don't impact/need docs -->
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||
<servers>
|
||||
<server>
|
||||
<id>github</id>
|
||||
<username>${env.GITHUB_ACTOR}</username>
|
||||
<password>${env.GITHUB_TOKEN}</password>
|
||||
</server>
|
||||
</servers>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>local-repo</id>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<url>https://repo.maven.apache.org/maven2</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>staged-releases</id>
|
||||
<url>https://repository.apache.org/content/groups/staging/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>staged-releases</id>
|
||||
<url>https://repository.apache.org/content/groups/staging/</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<activeProfiles>
|
||||
<activeProfile>local-repo</activeProfile>
|
||||
</activeProfiles>
|
||||
</settings>
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
language: java
|
||||
|
||||
jdk:
|
||||
- openjdk8
|
||||
|
||||
dist: xenial
|
||||
|
||||
sudo: required
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
- $HOME/downloads
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /^release-.*$/
|
||||
- /^test-.*$/
|
||||
- /^v[0-9]\..*$/
|
||||
|
||||
install: mvn compile -Dmaven.javadoc.skip=true | grep -v "Downloading\|Downloaded"
|
||||
|
||||
before_script:
|
||||
- $TRAVIS_DIR/install-backend.sh
|
||||
|
||||
script:
|
||||
- mvn test -P core-test,$BACKEND
|
||||
- $TRAVIS_DIR/run-api-test.sh
|
||||
- $TRAVIS_DIR/run-unit-test.sh
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: tinkerpop-test
|
||||
if: branch =~ env(RELEASE_BRANCH)
|
||||
env: BACKEND=memory SUITE=tinkerpop
|
||||
script: $TRAVIS_DIR/run-tinkerpop-test.sh
|
||||
|
||||
- stage: tinkerpop-test
|
||||
if: branch =~ env(RELEASE_BRANCH)
|
||||
env: BACKEND=cassandra SUITE=tinkerpop
|
||||
script: $TRAVIS_DIR/run-tinkerpop-test.sh
|
||||
|
||||
- stage: tinkerpop-test
|
||||
if: branch =~ env(RELEASE_BRANCH)
|
||||
env: BACKEND=scylladb SUITE=tinkerpop
|
||||
script: $TRAVIS_DIR/run-tinkerpop-test.sh
|
||||
|
||||
- stage: tinkerpop-test
|
||||
if: branch =~ env(RELEASE_BRANCH)
|
||||
env: BACKEND=mysql SUITE=tinkerpop
|
||||
script: $TRAVIS_DIR/run-tinkerpop-test.sh
|
||||
|
||||
- stage: tinkerpop-test
|
||||
if: branch =~ env(RELEASE_BRANCH)
|
||||
env: BACKEND=hbase SUITE=structure
|
||||
script: $TRAVIS_DIR/run-tinkerpop-test.sh
|
||||
|
||||
- stage: tinkerpop-test
|
||||
if: branch =~ env(RELEASE_BRANCH)
|
||||
env: BACKEND=hbase SUITE=process
|
||||
script: $TRAVIS_DIR/run-tinkerpop-test.sh
|
||||
|
||||
- stage: tinkerpop-test
|
||||
if: branch =~ env(RELEASE_BRANCH)
|
||||
env: BACKEND=rocksdb SUITE=tinkerpop
|
||||
script: $TRAVIS_DIR/run-tinkerpop-test.sh
|
||||
|
||||
- stage: tinkerpop-test
|
||||
if: branch =~ env(RELEASE_BRANCH)
|
||||
env: BACKEND=postgresql SUITE=tinkerpop
|
||||
script: $TRAVIS_DIR/run-tinkerpop-test.sh
|
||||
|
||||
- stage: deploy
|
||||
if: tag =~ env(RELEASE_TAG)
|
||||
script: skip
|
||||
|
||||
before_deploy:
|
||||
- |
|
||||
if [ $DEPLOYED = 0 ]; then
|
||||
DEPLOYED=1
|
||||
mvn clean package -DskipTests
|
||||
else
|
||||
echo "Skipping before_deploy since it has been executed once."
|
||||
fi
|
||||
deploy:
|
||||
- provider: releases
|
||||
skip_cleanup: true
|
||||
file_glob: true
|
||||
file: "$TRAVIS_BUILD_DIR/hugegraph-*.tar.gz"
|
||||
api_key:
|
||||
secure: nSGEnk5tJsTHMGABO8OJMTk47L3QA2O4xHXLKGIuTK5kLGJkrl0Bs27IveggLxf1E7DSrhcfNW44yIjn5lWA/5QX3DweWH4FGDAgWYFyj7QsBZl7WT2NReMW7BLzpvsIjyEKDw5pLbYBOpOrHflmyQB4w0oXTZVl6awll7JicuL1air7lpqJ4Ju4ukj1mo6rcX3MHRkzgMuS5eemArm8T15y9IlDqK3gmjVUOVo7LoYDFaq3eMzQ7ufrOFiiVIV2LUxn2t2/YRcKU05zal4IKifMKbXISX5u7ZwYe8T14ZQ7eMTOzmFY21gv3HEg+KXWh4gq8HvG4FKbzrl+KYSacJ1xINJQaQGIOD7Pz7vzQdj9wpM/WLqmYF5SE5ZYzXV3ejrtYlEUpJQSnpyiHlfyRuDRzYq/dB2V1ua6t9xkbjL/a2uqHa9WjbOi1jqw2E2XSnx794pzCvKYn1RLOiqpzVlEnb3cPb5M7vl9wsGf5MFLS8zLmMa5DyJn+e45W6GpW1zT4uLl2yR3Ja9ROlVapAb4aMyhKnWQZtUMDVhD6Xjj/CAumeOQLe31NM3i8NK2L0iEYSFWUigLEcCapXo3aOVlzckdJ6p7k94dFd5wcg/EY2aFMhyjYUKUFP1V535uB4ZKvf/dotMKY29nsYyceYgOJ/VNZt2BPZ8oCdc8+ys=
|
||||
on:
|
||||
tags: true
|
||||
- provider: script
|
||||
skip_cleanup: true
|
||||
script:
|
||||
echo $GPG_KEY_ENC_PASSW | gpg -d --passphrase-fd 0 $TRAVIS_DIR/private-key.gpg.gpg | gpg --import &&
|
||||
mvn deploy --settings $TRAVIS_DIR/maven.xml -DskipTests=true -B -U -P release
|
||||
on:
|
||||
tags: true
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- BACKEND=memory
|
||||
- BACKEND=cassandra
|
||||
- BACKEND=scylladb
|
||||
- BACKEND=mysql
|
||||
- BACKEND=hbase
|
||||
- BACKEND=rocksdb
|
||||
- BACKEND=postgresql
|
||||
global:
|
||||
- RELEASE_BRANCH=^release-.*$
|
||||
- RELEASE_TAG=^v[0-9]\..*$
|
||||
- TRAVIS_DIR=hugegraph-dist/src/assembly/travis
|
||||
- DEPLOYED=0
|
||||
- secure: dYmFTVeEtRzLNaHp9ToTV/+CkTD0/rEj+K7dRN8wsv/yg4pbqwnyWbSXcqMlj6iNFzAz3nPbmFLCvAWON9/SMN9iJIT6x/xfcf+LqCT8QWczo2nc9xeX144ij2VHX1Drvtk5fRTMaTXRfWEQIrjqx2yrJTIdrXWzWSaZLFv1CRCUizEiGGCePnzUWxx9dBypNyC6IaH6lIv8uN5E6+10SYhb7UJGjWUMDg1bCeW9X7X2wg4QpsGDzlGFXT2EBPU/dAb5attTAtW8dKxrCZqZJTPWe0BarXDBR4PO15BC+a0V1g8LwexedtDjJeFRcGPaJ5NN4d3jDSusCzt5Jf4U0Wa1gDRMVTU3jT+KYkm5eoV4wOZMySobjh6VpQH/LyL0QTDy5apRPAxw+wO+tc91P+nkJmnlr3pN8abtMZ6NciZizUBYQRgR/m2Ir0stvtbZxBQOATuPtBgNDKiDgVdSYcRJzSqYEMFOn35AvsDZ9aUsyC8k29PCUZ0gQO2Is6cV1ClFBnM52hfU9XX0gu+NviSnYNGvcokod8z9VjGtnM7V3LYjqXSFqO9kkMbOmkME1tD2Bh/klw2/OM+2tBBZiAgxB89st5jSUHI4a2hpUyaQBezJUcU9t2vVT/zAVEIqzw2PDxkMU7t0n6L1x+qUIUTG/WynfIni5msxuR7HoiU=
|
||||
- secure: XbX6AX5zDPc2PcWYAMW+6fazqRRUqpgQkt4eXUugLuVIYZBmJ0WqncEhJ4+mdwOGPIhnP2HsOaSeK2eE/O+iLY2XpBFbugoBgm9VaZlCC4CY1gRNHaanYg64Lrm3NPY3n08IHRMazHqMpJwUqNO+OG/6QwkepULQLj5Rluf716AoXHa7IEJhAIrwr+OXQvdEaJdUXlS1lRycXVeYtOewl7qYxCO4dD4RMhPlNykh9KEK7fd5wnPkiUsp1SwF4g5XsaLvGXmT/qQ1nj8oa9Caej/iaj6HMKG3BO057mq4KK5JDxTPWhBueNpEkUwldAnrMhYWLRnNf4IyjUsaB/Pmi6HspzcaiORPLYwPmdvLGGSnYwbtO+fAHebgpgOnj/vGmRmY4YtIkYdFtbPBI0HpbGB77tqNRFCe/5deLrjx0hXJBfoKTy7d42SI1eBhNR0svZYUHkSfuXwly6hMTlH1DN/bumMFxfXDkY9PFHlzV1Mn3vb9BxKTaP88hJsWk7JqgniqUF7EWAc0EhHMbJct2gC0pDc95z4Yy9391n7/XWJErhIdYon1Ukds5+a43xFXoy76gR4LuMDpzzCnutMjhC2yDuGaZx/DfkPBb5JFU7SHtTKj05zb73Moogi7qqbH8jwcwoSfogAKyrIAWTcAgvJ2LVnRzwdsiLTc6MEagiM=
|
||||
- secure: GKdjRHR35FBPY6oGBfjVdGxnVeoMmZHgNCnsyGNvRVJiQLhXBjUQ9bYjxPRX3JkmztdofNVd3gV8xqIOfLD6XA/0qHVvZ5GlWK4O77eGDur5InobzMlRDIUvJkpqM2SdSU8vKoAUBWgKnfzvlbA14kiwCID72zDVa/E8G0gBl0GZ2zWXIWRg0zC3ZyaJwTZC8WLqn0Kl7UxMy6i/xmK1F+apLooFIBEXYZuoH3pY83L4BvPozmJzT66HonrfcnaifaVHShBntPhQ6Sjlq5suMhtQENcPqWxaORL69s6Y/uF+RAdmnRHPZnXFhjRvq3rQAdMKM/DBOcaYlu1aIxCJJmWLm+b+75EQgdtwYGtwDpunzKXzH7ewB6rTYAghJNjGxt/KfROniooAs8mO3DqhygV5/BInJk0aKab5GlHCAioesV7TKSObhSDlKU8CSBRr/j8T7anL+lkXVZ89fkGg+EBSoXkVCNwQjank1NrHWCJQpNVndHzWIoGk0gcjTf01+iMDGIJDQZhcEuLu8wZbvDjLo/qEah247G0JabDMNBdyMZpg96bWDgArMkWa2FnWX25A7Cfgm1JymXdOZCIJrHbYWWAyoaXNrjZezQ8NPnE5uOWJIKfBnjnyPtKPoWjLuqpXmqcggbl8iRiy1EdWs/N39Q7qcFtJ5lTN9q1kDeI=
|
||||
- secure: MOn1SM0cwpKshyhCAZ5pdDVcu1VZ6s11lOuO/3rskKwxsWfL3duxg5yV5iAmyIPeBB69kzYg6zEIHOM7cjocR55bakurkaYiSE+OBYKickjryNnYFJ6gAaEmgykFHpwhlIKkyceiApgMJ4LFDNhd48GGjBLfpJZcLFfHbCwg3Mpr+KSihrrv9z2+omEuwsW43FoIoeFiYdx3Z2LoggLtGKbWjjbbf/Lq+ozb+O6J7pTJpIV3WwZ42M39HB+9whiMHeu5PAoOXzDsScyJ4fkInMbTUg1mqWlzg1mlAfOgFOmAzNcBhTqOBYkF0QDsOP/V6cXKVMe/P9AU+MPf86vrNgse8iW5DF5VNoyN15HztvNdyvdSTJRncym1LfloYaYFyhBZ87PLT98erenqmFJym7atrcH5xiQwD+19g35H09Fmw8N2ZsHZKHccZuKqb3bg0+UvdJk4M0PGo/XXse9smHckv6dRjdVZNvdObHcy1jXtGfkp085MYYW2KZ6Zemds54nxe5FYtSs+TOLJNS07wwLc85cDuvirg+4oOsWnaDhr63R4gQPkc34brP+R1e0V5Hsh9tJjjrpl5xWycMbAo4sXIJbf+qrvwwofeZmFtPGtRBirDORycpP9nAczWXE8sDyQxtPbOFzZWefb+zo0gMdtkRfi0693qWft5VcY5+I=
|
||||
- secure: XIFolSD+Jsx9EyHOo+UHeWeBd+RGbxm5FMiBPe8r3fWr3Evs1HAk6//YyPSvGMGUfJwvEPZ5o5GBGT1hPIy8s9+wgHAOxUWwLFkSHGdeXUHMI6zWCQ09DomFhgBidJGnT63mAnwLOTeOtV953hEJDOWp+nHXB9VrDy1rPR25NIyF6eS2ym7OZBa1tnk87pVXbLBFhmYn75D2Bc0VEc+7HM6WuGc+VQ5hZv04lrlRl4kOdkrrV97ie2v42oW7FnebzqjgTgsEbzlXriXeLlBsNCkdenn8U5pqW0V2Odv8iLNalCvF4rrVxsMTeWpqEEL/keLhFO31B20E6BOlKTJsDYgd/ehYMUdboPmxeOuwY6MA1V98gnXyxu8eVKm3RTV81pma7GxcHEVsnl6kZ3eK2LA0xD5UzOKK4nkUs0gAtEahykjbVJaVva0AOsDUId4U0+fHPRmMA4mbXcsxnivnIAC1XKu45C9/Hot8xgI9EChLKIJmT1DvR+Ey0NT27olOjbCYHUalnM37kd3H33dSfnke9f0Pygm2y61UOjz/t4NAPVPfQSwPgC248Rwb6COoqB72gctLJPfXOpUn8Nzcnv6Sb22YBJ139W9HQ7mLuTE5LpsU7x/9HNH50ILkLFMNLj31fCsLBbXFXafJTuKQ3Y5I69P/56orcL8WWjuchcY=
|
||||
|
|
@ -6,7 +6,7 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
TAG_NAME: ${{ github.ref_name }}
|
||||
steps:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
name: "Auto PR Commenter"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
add-review-comment:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Add review comment
|
||||
uses: peter-evans/create-or-update-comment@v4
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
@codecov-ai-reviewer review
|
||||
|
|
@ -1,31 +1,57 @@
|
|||
name: third-party dependencies check
|
||||
|
||||
name: "3rd-party"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- /^release-.*$/
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
dependency-check:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SCRIPT_DEPENDENCY: hugegraph-dist/scripts/dependency
|
||||
USE_STAGE: 'false' # Whether to include the stage repository.
|
||||
SCRIPT_DEPENDENCY: install-dist/scripts/dependency
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up JDK 8
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '8'
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
|
||||
- name: use staged maven repo settings
|
||||
if: ${{ env.USE_STAGE == 'true' }}
|
||||
run: |
|
||||
cp $HOME/.m2/settings.xml /tmp/settings.xml
|
||||
mv -vf .github/configs/settings.xml $HOME/.m2/settings.xml
|
||||
|
||||
- name: mvn install
|
||||
run: |
|
||||
mvn install -DskipTests=true
|
||||
mvn install -Dmaven.test.skip=true -ntp --fail-at-end
|
||||
- name: generate current dependencies
|
||||
run: |
|
||||
bash $SCRIPT_DEPENDENCY/regenerate_known_dependencies.sh current-dependencies.txt
|
||||
- name: check third dependencies
|
||||
run: |
|
||||
bash $SCRIPT_DEPENDENCY/check_dependencies.sh
|
||||
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v4
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v3
|
||||
# Refer: https://github.com/actions/dependency-review-action
|
||||
with:
|
||||
# TODO: reset critical to low before releasing
|
||||
fail-on-severity: critical
|
||||
# Action will fail if dependencies don't match the list
|
||||
#allow-licenses: Apache-2.0, MIT
|
||||
#deny-licenses: GPL-3.0, AGPL-1.0, AGPL-3.0, LGPL-2.0, CC-BY-3.0
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
name: hugegraph-ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- 'release-*'
|
||||
- 'test-*'
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
TRAVIS_DIR: hugegraph-dist/src/assembly/travis
|
||||
REPORT_DIR: target/site/jacoco
|
||||
BACKEND: ${{ matrix.BACKEND }}
|
||||
TRIGGER_BRANCH_NAME: ${{ github.ref_name }}
|
||||
HEAD_BRANCH_NAME: ${{ github.head_ref }}
|
||||
BASE_BRANCH_NAME: ${{ github.base_ref }}
|
||||
TARGET_BRANCH_NAME: ${{ github.base_ref != '' && github.base_ref || github.ref_name }}
|
||||
RELEASE_BRANCH: ${{ startsWith(github.ref_name, 'release-') || startsWith(github.ref_name, 'test-') || startsWith(github.base_ref, 'release-') }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
BACKEND: [memory, cassandra, scylladb, hbase, rocksdb, mysql, postgresql]
|
||||
JAVA_VERSION: ['8', '11']
|
||||
|
||||
steps:
|
||||
- name: Install JDK ${{ matrix.JAVA_VERSION }}
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: ${{ matrix.JAVA_VERSION }}
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Compile
|
||||
run: |
|
||||
mvn clean compile -U -Dmaven.javadoc.skip=true | grep -v "Downloading\|Downloaded"
|
||||
|
||||
- name: Install JDK 8
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '8'
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Prepare env and service
|
||||
run: |
|
||||
$TRAVIS_DIR/install-backend.sh $BACKEND
|
||||
|
||||
- name: Install JDK ${{ matrix.JAVA_VERSION }}
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: ${{ matrix.JAVA_VERSION }}
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Run unit test
|
||||
run: |
|
||||
$TRAVIS_DIR/run-unit-test.sh $BACKEND
|
||||
|
||||
- name: Run core test
|
||||
run: |
|
||||
$TRAVIS_DIR/run-core-test.sh $BACKEND
|
||||
|
||||
- name: Run api test
|
||||
run: |
|
||||
$TRAVIS_DIR/run-api-test.sh $BACKEND $REPORT_DIR
|
||||
|
||||
- name: Run raft test
|
||||
if: ${{ env.BACKEND == 'rocksdb' }}
|
||||
run: |
|
||||
$TRAVIS_DIR/run-api-test-for-raft.sh $BACKEND $REPORT_DIR
|
||||
|
||||
- name: Run TinkerPop test
|
||||
if: ${{ env.RELEASE_BRANCH == 'true' }}
|
||||
run: |
|
||||
$TRAVIS_DIR/run-tinkerpop-test.sh $BACKEND tinkerpop
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3.0.0
|
||||
with:
|
||||
file: ${{ env.REPORT_DIR }}/*.xml
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
name: "Cluster Test CI"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- 'release-*'
|
||||
- 'test-*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
cluster-test:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
USE_STAGE: 'false' # Whether to include the stage repository.
|
||||
|
||||
steps:
|
||||
- name: Install JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 5
|
||||
|
||||
- name: use staged maven repo settings
|
||||
if: ${{ env.USE_STAGE == 'true' }}
|
||||
run: |
|
||||
cp $HOME/.m2/settings.xml /tmp/settings.xml
|
||||
mv -vf .github/configs/settings.xml $HOME/.m2/settings.xml
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
mvn clean package -U -Dmaven.javadoc.skip=true -Dmaven.test.skip=true -ntp
|
||||
|
||||
- name: Run simple cluster test
|
||||
run: |
|
||||
mvn test -pl hugegraph-cluster-test/hugegraph-clustertest-test -am -P simple-cluster-test
|
||||
|
||||
- name: Run multi cluster test
|
||||
run: |
|
||||
mvn test -pl hugegraph-cluster-test/hugegraph-clustertest-test -am -P multi-cluster-test
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, release-*, v*.* ]
|
||||
branches: [ master, release-* ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
# branches: [ master ] # enable in all PR
|
||||
# The branches below must be a subset of the branches above
|
||||
# branches: [ master ] # enable in all PRs
|
||||
schedule:
|
||||
- cron: '33 0 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
env:
|
||||
USE_STAGE: 'false' # Whether to include the stage repository.
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
|
|
@ -26,40 +26,46 @@ jobs:
|
|||
language: [ 'java' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Java JDK
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '8'
|
||||
- name: Setup Java JDK
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '11'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
- name: use staged maven repo settings
|
||||
if: ${{ env.USE_STAGE == 'true' }}
|
||||
run: |
|
||||
cp $HOME/.m2/settings.xml /tmp/settings.xml
|
||||
mv -vf .github/configs/settings.xml $HOME/.m2/settings.xml
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
name: "HugeGraph-Commons CI"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- /^release-.*$/
|
||||
- /^test-.*$/
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build-commons:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
USE_STAGE: 'false' # Whether to include the stage repository.
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
JAVA_VERSION: ['11']
|
||||
|
||||
steps:
|
||||
- name: Install JDK ${{ matrix.JAVA_VERSION }}
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: ${{ matrix.JAVA_VERSION }}
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Use staged maven repo settings
|
||||
if: ${{ env.USE_STAGE == 'true' }}
|
||||
run: |
|
||||
cp $HOME/.m2/settings.xml /tmp/settings.xml
|
||||
cp -vf .github/configs/settings.xml $HOME/.m2/settings.xml && cat $HOME/.m2/settings.xml
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
mvn install -Dmaven.javadoc.skip=true -ntp -Dmaven.test.skip=true
|
||||
|
||||
- name: Run common test
|
||||
run: |
|
||||
mvn test -pl hugegraph-commons/hugegraph-common -Dtest=UnitTestSuite -DskipCommonsTests=false
|
||||
|
||||
- name: Run rpc test
|
||||
run: |
|
||||
mvn test -pl hugegraph-commons/hugegraph-rpc -Dtest=UnitTestSuite -DskipCommonsTests=false
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3.0.0
|
||||
with:
|
||||
file: target/jacoco.xml
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
name: "Docker Build CI"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- 'release-*'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**/Dockerfile*'
|
||||
- '.dockerignore'
|
||||
- 'hugegraph-server/hugegraph-dist/docker/**'
|
||||
- 'hugegraph-server/hugegraph-dist/src/assembly/static/bin/util.sh'
|
||||
|
||||
jobs:
|
||||
docker-build:
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
dockerfile:
|
||||
- hugegraph-pd/Dockerfile
|
||||
- hugegraph-store/Dockerfile
|
||||
- hugegraph-server/Dockerfile
|
||||
- hugegraph-server/Dockerfile-hstore
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build ${{ matrix.dockerfile }}
|
||||
run: |
|
||||
IMAGE_ID=$(docker build -q -f ${{ matrix.dockerfile }} .)
|
||||
echo "Built: $IMAGE_ID"
|
||||
echo "IMAGE_ID=$IMAGE_ID" >> "$GITHUB_ENV"
|
||||
HC=$(docker inspect --format='{{json .Config.Healthcheck}}' "$IMAGE_ID")
|
||||
echo "Healthcheck: $HC"
|
||||
[[ "$HC" != "null" ]] || { echo "ERROR: HEALTHCHECK missing in ${{ matrix.dockerfile }}"; exit 1; }
|
||||
|
||||
- name: Test server entrypoint property mapping
|
||||
if: matrix.dockerfile == 'hugegraph-server/Dockerfile'
|
||||
run: bash hugegraph-server/hugegraph-dist/docker/test/test-docker-entrypoint.sh
|
||||
|
||||
# The startup preflight needs a socket-table tool, and the base image
|
||||
# ships none of its own. Without one every start reports "unknown" and
|
||||
# a duplicate start is no longer refused, so assert the image can
|
||||
# actually answer. Only the server images run check_port.
|
||||
# TODO(docker-ci): this pins the probe dependency, not the behaviour it
|
||||
# protects. A full duplicate-start/stop check needs a booted server with
|
||||
# a backend, which belongs with the e2e job rather than the image build.
|
||||
- name: Port preflight can answer inside ${{ matrix.dockerfile }}
|
||||
if: ${{ startsWith(matrix.dockerfile, 'hugegraph-server/') }}
|
||||
run: |
|
||||
STATE=$(docker run --rm "$IMAGE_ID" bash -c \
|
||||
'source /hugegraph-server/bin/util.sh && port_listen_state 8080')
|
||||
echo "port_listen_state 8080 -> $STATE"
|
||||
# Assert a positive answer rather than "not unknown": an empty $STATE
|
||||
# would otherwise satisfy the check. The step's default `bash -e`
|
||||
# already aborts on a failed `docker run`, so this only states intent.
|
||||
[[ "$STATE" == "free" || "$STATE" == "busy" ]] || {
|
||||
echo "ERROR: no usable socket-table tool (ss/netstat) in ${{ matrix.dockerfile }}"
|
||||
exit 1
|
||||
}
|
||||
|
|
@ -1,17 +1,19 @@
|
|||
name: License checker
|
||||
name: "License Checker"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- /^v[0-9]\..*$/
|
||||
- 'release-*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
check-license:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
USE_STAGE: 'false' # Whether to include the stage repository.
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check License Header
|
||||
uses: apache/skywalking-eyes@main
|
||||
|
|
@ -19,4 +21,11 @@ jobs:
|
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
log: info
|
||||
token: ${{ github.token }}
|
||||
config: .licenserc.yaml
|
||||
|
||||
- name: License Check (RAT)
|
||||
run: |
|
||||
mvn apache-rat:check -ntp
|
||||
find ./ -name rat.txt -print0 | xargs -0 -I file cat file > merged-rat.txt
|
||||
grep "Binaries" merged-rat.txt -C 3 && cat merged-rat.txt
|
||||
|
|
|
|||
|
|
@ -0,0 +1,327 @@
|
|||
name: "HugeGraph-PD & Store & Hstore CI"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- 'release-*'
|
||||
- 'test-*'
|
||||
pull_request:
|
||||
|
||||
# TODO: consider merge to one ci.yml file
|
||||
jobs:
|
||||
struct:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
USE_STAGE: 'false'
|
||||
steps:
|
||||
- name: Install JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 5
|
||||
|
||||
- name: Use staged maven repo settings
|
||||
run: |
|
||||
cp $HOME/.m2/settings.xml /tmp/settings.xml || true
|
||||
mv -vf .github/configs/settings.xml $HOME/.m2/settings.xml
|
||||
|
||||
- name: Resolve project revision
|
||||
run: echo "REVISION=$(mvn -q -DforceStdout help:evaluate -Dexpression=revision -f pom.xml)" >> $GITHUB_ENV
|
||||
|
||||
- name: Build or fetch hugegraph-struct
|
||||
run: |
|
||||
if [ -f hugegraph-struct/pom.xml ]; then
|
||||
echo "[INFO] Found hugegraph-struct source, building from source"
|
||||
mvn -U -ntp -DskipTests -pl hugegraph-struct -am install
|
||||
else
|
||||
echo "[INFO] hugegraph-struct source not found, fetching artifact $REVISION"
|
||||
if [ -z "$REVISION" ]; then echo "[ERROR] revision not resolved"; exit 1; fi
|
||||
mvn -U -ntp dependency:get -Dartifact=org.apache.hugegraph:hugegraph-struct:$REVISION
|
||||
fi
|
||||
|
||||
- name: Run hugegraph-struct test
|
||||
if: ${{ hashFiles('hugegraph-struct/pom.xml') != '' }}
|
||||
run: |
|
||||
mvn -U -ntp -pl hugegraph-struct test
|
||||
|
||||
pd:
|
||||
needs: struct
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# TODO: avoid duplicated env setup in pd & store
|
||||
USE_STAGE: 'false' # Whether to include the stage repository.
|
||||
# TODO: remove outdated env
|
||||
TRAVIS_DIR: hugegraph-server/hugegraph-dist/src/assembly/travis
|
||||
REPORT_DIR: target/site/jacoco
|
||||
|
||||
steps:
|
||||
- name: Install JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 5
|
||||
|
||||
- name: use staged maven repo settings
|
||||
if: ${{ env.USE_STAGE == 'true' }}
|
||||
run: |
|
||||
cp $HOME/.m2/settings.xml /tmp/settings.xml
|
||||
mv -vf .github/configs/settings.xml $HOME/.m2/settings.xml
|
||||
|
||||
- name: Run common test
|
||||
run: |
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am -P pd-common-test
|
||||
|
||||
- name: Run core test
|
||||
run: |
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am -P pd-core-test
|
||||
|
||||
# The above tests do not require starting a PD instance.
|
||||
|
||||
- name: Package
|
||||
# todo remove --fail-at-end after test
|
||||
run: |
|
||||
mvn clean package -U -Dmaven.javadoc.skip=true -Dmaven.test.skip=true -ntp --fail-at-end
|
||||
|
||||
- name: Check startup test prerequisites (PD)
|
||||
id: pd-preflight
|
||||
run: |
|
||||
# These jobs run on Linux, where the suites clean up ports with fuser.
|
||||
# lsof is no longer required: check_port now uses ss/netstat.
|
||||
for tool in fuser curl java; do
|
||||
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||
echo "can_run=false" >> "$GITHUB_OUTPUT"
|
||||
echo "skip_reason=missing tool: $tool" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
echo "can_run=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Run start-hugegraph-pd.sh foreground mode tests
|
||||
if: steps.pd-preflight.outputs.can_run == 'true'
|
||||
run: |
|
||||
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
|
||||
PD_DIR=hugegraph-pd/apache-hugegraph-pd-$VERSION/
|
||||
$TRAVIS_DIR/test-start-hugegraph-pd.sh $PD_DIR
|
||||
|
||||
- name: Startup tests skipped (missing prerequisites)
|
||||
if: steps.pd-preflight.outputs.can_run == 'false'
|
||||
run: |
|
||||
echo "::notice::PD startup tests skipped — ${{ steps.pd-preflight.outputs.skip_reason }}"
|
||||
|
||||
- name: Prepare env and service
|
||||
run: |
|
||||
$TRAVIS_DIR/start-pd.sh
|
||||
|
||||
- name: Run client test
|
||||
run: |
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am -P pd-client-test
|
||||
|
||||
- name: Run rest test
|
||||
run: |
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am -P pd-rest-test
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3.0.0
|
||||
with:
|
||||
file: ${{ env.REPORT_DIR }}/*.xml
|
||||
|
||||
store:
|
||||
needs: struct
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
USE_STAGE: 'false' # Whether to include the stage repository.
|
||||
# TODO: remove outdated env
|
||||
TRAVIS_DIR: hugegraph-server/hugegraph-dist/src/assembly/travis
|
||||
REPORT_DIR: target/site/jacoco
|
||||
|
||||
steps:
|
||||
- name: Install JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 5
|
||||
|
||||
- name: use staged maven repo settings
|
||||
if: ${{ env.USE_STAGE == 'true' }}
|
||||
run: |
|
||||
cp $HOME/.m2/settings.xml /tmp/settings.xml
|
||||
mv -vf .github/configs/settings.xml $HOME/.m2/settings.xml
|
||||
|
||||
- name: Package
|
||||
# todo remove --fail-at-end after test
|
||||
run: |
|
||||
mvn clean package -U -Dmaven.javadoc.skip=true -Dmaven.test.skip=true -ntp --fail-at-end
|
||||
|
||||
- name: Check startup test prerequisites (Store)
|
||||
id: store-preflight
|
||||
run: |
|
||||
# These jobs run on Linux, where the suites clean up ports with fuser.
|
||||
# lsof is no longer required: check_port now uses ss/netstat.
|
||||
for tool in fuser curl java; do
|
||||
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||
echo "can_run=false" >> "$GITHUB_OUTPUT"
|
||||
echo "skip_reason=missing tool: $tool" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
LIMIT_N=$(ulimit -n)
|
||||
if [[ "$LIMIT_N" != "unlimited" ]] && (( LIMIT_N < 1024 )); then
|
||||
echo "can_run=false" >> "$GITHUB_OUTPUT"
|
||||
echo "skip_reason=ulimit -n is $LIMIT_N (store requires >= 1024)" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
echo "can_run=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Run start-hugegraph-store.sh foreground mode tests
|
||||
if: steps.store-preflight.outputs.can_run == 'true'
|
||||
run: |
|
||||
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
|
||||
STORE_DIR=hugegraph-store/apache-hugegraph-store-$VERSION/
|
||||
$TRAVIS_DIR/test-start-hugegraph-store.sh $STORE_DIR
|
||||
|
||||
- name: Startup tests skipped (missing prerequisites)
|
||||
if: steps.store-preflight.outputs.can_run == 'false'
|
||||
run: |
|
||||
echo "::notice::Store startup tests skipped — ${{ steps.store-preflight.outputs.skip_reason }}"
|
||||
|
||||
- name: Prepare env and service
|
||||
run: |
|
||||
$TRAVIS_DIR/start-pd.sh
|
||||
$TRAVIS_DIR/start-store.sh
|
||||
|
||||
- name: Run common test
|
||||
run: |
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-common-test
|
||||
|
||||
- name: Run client test
|
||||
run: |
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-client-test
|
||||
|
||||
- name: Run core test
|
||||
run: |
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-core-test
|
||||
|
||||
- name: Run rocksdb test
|
||||
run: |
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-rocksdb-test
|
||||
|
||||
- name: Run server test
|
||||
run: |
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-server-test
|
||||
|
||||
- name: Run raft-core test
|
||||
run: |
|
||||
mvn test -pl hugegraph-store/hg-store-test -am -P store-raftcore-test
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3.0.0
|
||||
with:
|
||||
file: ${{ env.REPORT_DIR }}/*.xml
|
||||
|
||||
hstore:
|
||||
needs: struct
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
USE_STAGE: 'false' # Whether to include the stage repository.
|
||||
TRAVIS_DIR: hugegraph-server/hugegraph-dist/src/assembly/travis
|
||||
REPORT_DIR: target/site/jacoco
|
||||
BACKEND: hstore
|
||||
RELEASE_BRANCH: ${{ startsWith(github.ref_name, 'release-') || startsWith(github.ref_name, 'test-') || startsWith(github.base_ref, 'release-') }}
|
||||
|
||||
steps:
|
||||
- name: Install JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: use staged maven repo settings
|
||||
if: ${{ env.USE_STAGE == 'true' }}
|
||||
run: |
|
||||
cp $HOME/.m2/settings.xml /tmp/settings.xml
|
||||
mv -vf .github/configs/settings.xml $HOME/.m2/settings.xml
|
||||
|
||||
- name: Package
|
||||
# todo remove --fail-at-end after test
|
||||
run: |
|
||||
mvn clean package -U -Dmaven.javadoc.skip=true -Dmaven.test.skip=true -ntp --fail-at-end
|
||||
|
||||
- name: Prepare env and service
|
||||
run: |
|
||||
$TRAVIS_DIR/install-backend.sh $BACKEND
|
||||
|
||||
- name: Run unit test
|
||||
run: |
|
||||
$TRAVIS_DIR/run-unit-test.sh $BACKEND
|
||||
|
||||
- name: Run core test
|
||||
run: |
|
||||
$TRAVIS_DIR/run-core-test.sh $BACKEND
|
||||
|
||||
- name: Run api test
|
||||
run: |
|
||||
$TRAVIS_DIR/run-api-test.sh $BACKEND $REPORT_DIR
|
||||
|
||||
- name: Run raft test
|
||||
if: ${{ env.BACKEND == 'rocksdb' }}
|
||||
run: |
|
||||
$TRAVIS_DIR/run-api-test-for-raft.sh $BACKEND $REPORT_DIR
|
||||
|
||||
- name: Run TinkerPop test
|
||||
if: ${{ env.RELEASE_BRANCH == 'true' }}
|
||||
run: |
|
||||
$TRAVIS_DIR/run-tinkerpop-test.sh $BACKEND tinkerpop
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3.0.0
|
||||
with:
|
||||
file: ${{ env.REPORT_DIR }}/*.xml
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
name: "Rerun CI"
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows:
|
||||
- "HugeGraph-Server CI"
|
||||
- "HugeGraph-Commons CI"
|
||||
- "HugeGraph-PD & Store & Hstore CI"
|
||||
- "Cluster Test CI"
|
||||
types:
|
||||
- completed
|
||||
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
MAX_RERUNS: '2'
|
||||
RETRY_DELAY_SECONDS: '180'
|
||||
|
||||
jobs:
|
||||
decide-rerun-action:
|
||||
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
action: ${{ steps.decision.outputs.action }}
|
||||
steps:
|
||||
- name: Decide rerun action
|
||||
id: decision
|
||||
env:
|
||||
WORKFLOW_NAME: ${{ github.event.workflow_run.name }}
|
||||
RUN_ID: ${{ github.event.workflow_run.id }}
|
||||
RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }}
|
||||
EVENT_NAME: ${{ github.event.workflow_run.event }}
|
||||
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
action="skip"
|
||||
reason="unsupported event: $EVENT_NAME"
|
||||
|
||||
if [[ "$EVENT_NAME" == "push" || "$EVENT_NAME" == "pull_request" ]]; then
|
||||
if (( RUN_ATTEMPT > MAX_RERUNS )); then
|
||||
reason="retry limit reached"
|
||||
else
|
||||
action="rerun"
|
||||
reason="within retry limit"
|
||||
fi
|
||||
fi
|
||||
|
||||
{
|
||||
echo "action=$action"
|
||||
echo "reason=$reason"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
{
|
||||
echo "### Rerun CI decision"
|
||||
echo ""
|
||||
echo "- Workflow: $WORKFLOW_NAME"
|
||||
echo "- Source event: $EVENT_NAME"
|
||||
echo "- Head branch: $HEAD_BRANCH"
|
||||
echo "- Run ID: $RUN_ID"
|
||||
echo "- Current attempt: $RUN_ATTEMPT"
|
||||
echo "- Max automatic reruns: $MAX_RERUNS"
|
||||
echo "- Delay seconds: $RETRY_DELAY_SECONDS"
|
||||
echo "- Action: $action"
|
||||
echo "- Reason: $reason"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
rerun-failed-jobs:
|
||||
needs: decide-rerun-action
|
||||
if: needs.decide-rerun-action.outputs.action == 'rerun'
|
||||
permissions:
|
||||
actions: write
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Wait before rerun
|
||||
run: |
|
||||
sleep "$RETRY_DELAY_SECONDS"
|
||||
|
||||
- name: Rerun failed jobs
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
run: |
|
||||
gh run rerun ${{ github.event.workflow_run.id }} --failed
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
name: "RISC-V Server CI"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
BINFMT_IMAGE: >-
|
||||
tonistiigi/binfmt@sha256:400a4873b838d1b89194d982c45e5fb3cda4593fbfd7e08a02e76b03b21166f0
|
||||
RISCV64_BASE_IMAGE: >-
|
||||
ubuntu@sha256:4edded5722eb644868b7b976033d241d2ab3fff0a170924df69b200a59a2b994
|
||||
DRAGONWELL_RISCV64_ARCHIVE: >-
|
||||
Alibaba_Dragonwell_Extended_11.0.31.28.11_riscv64_linux.tar.gz
|
||||
DRAGONWELL_RISCV64_SHA256: >-
|
||||
7df2d308f0dca7a779d2854e6da19214f99cd77aa6d4982c3bf981a77266a79b
|
||||
DRAGONWELL_RISCV64_URL: >-
|
||||
https://github.com/dragonwell-project/dragonwell11/releases/download/dragonwell-extended-11.0.31.28_jdk-11.0.31-ga/Alibaba_Dragonwell_Extended_11.0.31.28.11_riscv64_linux.tar.gz
|
||||
RISCV64_CONTAINER: >-
|
||||
hugegraph-riscv64-ci-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
|
||||
jobs:
|
||||
build-server-riscv64:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 90
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Start isolated RISC-V environment
|
||||
run: |
|
||||
docker run --privileged --rm "$BINFMT_IMAGE" --install riscv64
|
||||
docker run --detach --init --platform linux/riscv64 \
|
||||
--name "$RISCV64_CONTAINER" "$RISCV64_BASE_IMAGE" sleep infinity
|
||||
test "$(docker exec "$RISCV64_CONTAINER" uname -m)" = riscv64
|
||||
|
||||
- name: Prepare source and Java 11
|
||||
run: |
|
||||
ARCHIVE_PATH="$RUNNER_TEMP/$DRAGONWELL_RISCV64_ARCHIVE"
|
||||
curl --fail --location --retry 3 --show-error \
|
||||
"$DRAGONWELL_RISCV64_URL" --output "$ARCHIVE_PATH"
|
||||
echo "$DRAGONWELL_RISCV64_SHA256 $ARCHIVE_PATH" | sha256sum -c -
|
||||
|
||||
docker exec "$RISCV64_CONTAINER" mkdir -p /workspace /opt/dragonwell
|
||||
git ls-files -z | tar --null --files-from=- -cf - | \
|
||||
docker exec -i "$RISCV64_CONTAINER" tar -xf - -C /workspace
|
||||
docker cp "$ARCHIVE_PATH" \
|
||||
"$RISCV64_CONTAINER:/tmp/$DRAGONWELL_RISCV64_ARCHIVE"
|
||||
|
||||
- name: Build and smoke test on RISC-V
|
||||
run: |
|
||||
docker exec \
|
||||
--env DRAGONWELL_RISCV64_ARCHIVE="$DRAGONWELL_RISCV64_ARCHIVE" \
|
||||
--env DRAGONWELL_RISCV64_SHA256="$DRAGONWELL_RISCV64_SHA256" \
|
||||
"$RISCV64_CONTAINER" bash -euo pipefail -c '
|
||||
test "$(uname -m)" = riscv64
|
||||
apt-get -q update
|
||||
apt-get -q install -y --no-install-recommends \
|
||||
ca-certificates curl jq libatomic1 libgcc-s1 libstdc++6 \
|
||||
lsof maven procps \
|
||||
protobuf-compiler protobuf-compiler-grpc-java-plugin
|
||||
|
||||
ARCHIVE_PATH="/tmp/$DRAGONWELL_RISCV64_ARCHIVE"
|
||||
echo "$DRAGONWELL_RISCV64_SHA256 $ARCHIVE_PATH" | sha256sum -c -
|
||||
tar -xzf "$ARCHIVE_PATH" --strip-components=1 -C /opt/dragonwell
|
||||
rm "$ARCHIVE_PATH"
|
||||
export JAVA_HOME=/opt/dragonwell
|
||||
export PATH="$JAVA_HOME/bin:$PATH"
|
||||
|
||||
java -XshowSettings:vm -version
|
||||
test -x /usr/bin/protoc
|
||||
test -x /usr/bin/grpc_java_plugin
|
||||
|
||||
cd /workspace
|
||||
MAVEN_OPTS="-XX:TieredStopAtLevel=1 -XX:ActiveProcessorCount=2" \
|
||||
mvn clean package -B -ntp -Drocksdb-only \
|
||||
-P riscv64-protobuf-tools \
|
||||
-pl hugegraph-server/hugegraph-dist -am \
|
||||
-Dmaven.test.skip=true -Dmaven.javadoc.skip=true
|
||||
hugegraph-server/hugegraph-dist/src/assembly/travis/check-rocksdb-only-dist.sh \
|
||||
hugegraph-server/apache-hugegraph-server-*/
|
||||
hugegraph-server/hugegraph-dist/src/assembly/travis/run-native-runtime-smoke-test.sh \
|
||||
hugegraph-server/apache-hugegraph-server-*/
|
||||
'
|
||||
|
||||
- name: Show server log on failure
|
||||
if: failure()
|
||||
run: |
|
||||
if docker container inspect "$RISCV64_CONTAINER" >/dev/null 2>&1; then
|
||||
docker exec "$RISCV64_CONTAINER" bash -c '
|
||||
find /workspace -path "*/logs/hugegraph-server.log" \
|
||||
-exec tail -n 200 {} \;
|
||||
' || true
|
||||
fi
|
||||
|
||||
- name: Clean up RISC-V environment
|
||||
if: always()
|
||||
run: |
|
||||
if docker container inspect "$RISCV64_CONTAINER" >/dev/null 2>&1; then
|
||||
docker rm --force --volumes "$RISCV64_CONTAINER"
|
||||
fi
|
||||
if docker container inspect "$RISCV64_CONTAINER" >/dev/null 2>&1; then
|
||||
echo "RISC-V CI container still exists: $RISCV64_CONTAINER" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
name: "HugeGraph-Server CI"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- 'release-*'
|
||||
- 'test-*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
wait-storage-shell-test:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run wait-storage.sh peer failover tests
|
||||
run: hugegraph-server/hugegraph-dist/src/assembly/travis/test-wait-storage.sh
|
||||
|
||||
build-server:
|
||||
# TODO: we need test & replace it to ubuntu-24.04 or ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
USE_STAGE: 'false' # Whether to include the stage repository.
|
||||
TRAVIS_DIR: hugegraph-server/hugegraph-dist/src/assembly/travis
|
||||
REPORT_DIR: target/site/jacoco
|
||||
BACKEND: ${{ matrix.BACKEND }}
|
||||
TRIGGER_BRANCH_NAME: ${{ github.ref_name }}
|
||||
HEAD_BRANCH_NAME: ${{ github.head_ref }}
|
||||
BASE_BRANCH_NAME: ${{ github.base_ref }}
|
||||
TARGET_BRANCH_NAME: ${{ github.base_ref != '' && github.base_ref || github.ref_name }}
|
||||
RELEASE_BRANCH: ${{ startsWith(github.ref_name, 'release-') || startsWith(github.ref_name, 'test-') }}
|
||||
RAFT_MODE: ${{ startsWith(github.head_ref, 'test') || startsWith(github.head_ref, 'raft') }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
BACKEND: [ memory, rocksdb, hbase ]
|
||||
JAVA_VERSION: [ '11' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 5
|
||||
|
||||
# TODO: Remove this step after install-backend.sh updated
|
||||
- name: Install Java8 for backend
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '8'
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Prepare backend environment
|
||||
run: |
|
||||
$TRAVIS_DIR/install-backend.sh $BACKEND && jps -l
|
||||
|
||||
- name: Install Java ${{ matrix.JAVA_VERSION }}
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{ matrix.JAVA_VERSION }}
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Use staged maven repo settings
|
||||
if: ${{ env.USE_STAGE == 'true' }}
|
||||
run: |
|
||||
cp $HOME/.m2/settings.xml /tmp/settings.xml
|
||||
cp -vf .github/configs/settings.xml $HOME/.m2/settings.xml && cat $HOME/.m2/settings.xml
|
||||
|
||||
- name: Compile
|
||||
run: |
|
||||
mvn clean compile -U -Dmaven.javadoc.skip=true -ntp
|
||||
|
||||
- name: Run check_port unit tests
|
||||
if: ${{ env.BACKEND == 'rocksdb' }}
|
||||
run: |
|
||||
# Validates the ss/netstat port preflight that replaced lsof
|
||||
$TRAVIS_DIR/test-check-port.sh \
|
||||
hugegraph-server/hugegraph-dist/src/assembly/static \
|
||||
hugegraph-store/hg-store-dist/src/assembly/static/bin/util.sh
|
||||
|
||||
- name: Run Java security properties tests
|
||||
if: ${{ env.BACKEND == 'rocksdb' }}
|
||||
run: |
|
||||
mvn package -Dmaven.test.skip=true -pl hugegraph-server/hugegraph-dist -am -ntp
|
||||
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
|
||||
SERVER_DIR=hugegraph-server/apache-hugegraph-server-$VERSION/
|
||||
$TRAVIS_DIR/test-java-security-properties.sh $SERVER_DIR
|
||||
|
||||
- name: Check startup test prerequisites
|
||||
id: server-preflight
|
||||
if: ${{ env.BACKEND == 'rocksdb' }}
|
||||
run: |
|
||||
# Note: lsof removed from prerequisites; check_port now uses ss, falling back
|
||||
# to netstat, and warns without blocking when neither can answer.
|
||||
# This job always runs on Linux (ubuntu-22.04), which uses fuser for port cleanup.
|
||||
for tool in crontab curl java; do
|
||||
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||
echo "can_run=false" >> "$GITHUB_OUTPUT"
|
||||
echo "skip_reason=missing tool: $tool" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
if ! command -v fuser >/dev/null 2>&1; then
|
||||
echo "can_run=false" >> "$GITHUB_OUTPUT"
|
||||
echo "skip_reason=missing tool: fuser (required for Linux port cleanup)" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
echo "can_run=true" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Run start-hugegraph.sh foreground mode tests
|
||||
if: ${{ env.BACKEND == 'rocksdb' && steps.server-preflight.outputs.can_run == 'true' }}
|
||||
run: |
|
||||
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
|
||||
SERVER_DIR=hugegraph-server/apache-hugegraph-server-$VERSION/
|
||||
$TRAVIS_DIR/test-start-hugegraph.sh $SERVER_DIR
|
||||
|
||||
- name: Startup tests skipped (missing prerequisites)
|
||||
if: ${{ env.BACKEND == 'rocksdb' && steps.server-preflight.outputs.can_run == 'false' }}
|
||||
run: |
|
||||
echo "::notice::Server startup tests skipped — ${{ steps.server-preflight.outputs.skip_reason }}"
|
||||
|
||||
- name: Run unit test
|
||||
run: |
|
||||
$TRAVIS_DIR/run-unit-test.sh $BACKEND
|
||||
|
||||
- name: Run core test
|
||||
run: |
|
||||
$TRAVIS_DIR/run-core-test.sh $BACKEND
|
||||
|
||||
- name: Run api test
|
||||
run: |
|
||||
if [ "$BACKEND" = "rocksdb" ]; then
|
||||
$TRAVIS_DIR/run-api-test.sh $BACKEND $REPORT_DIR true
|
||||
else
|
||||
$TRAVIS_DIR/run-api-test.sh $BACKEND $REPORT_DIR
|
||||
fi
|
||||
|
||||
# TODO: disable raft test in normal PR due to the always timeout problem
|
||||
- name: Run raft test
|
||||
if: ${{ env.RAFT_MODE == 'true' && env.BACKEND == 'rocksdb' }}
|
||||
run: |
|
||||
$TRAVIS_DIR/run-api-test-for-raft.sh $BACKEND $REPORT_DIR
|
||||
|
||||
- name: Run TinkerPop test
|
||||
if: ${{ env.RELEASE_BRANCH == 'true' }}
|
||||
run: |
|
||||
echo "[WARNING] Enter Tinkerpop Test, current 'github.ref_name' is ${{ github.ref_name }}"
|
||||
$TRAVIS_DIR/run-tinkerpop-test.sh $BACKEND tinkerpop
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
# TODO: update to v5 later
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: ${{ env.REPORT_DIR }}/*.xml
|
||||
|
||||
build-server-macos-rocksdb:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-15-intel
|
||||
server_java_options: ''
|
||||
- os: macos-15
|
||||
server_java_options: '-Xms512m -Xmx2g'
|
||||
env:
|
||||
USE_STAGE: 'false' # Whether to include the stage repository.
|
||||
TRAVIS_DIR: hugegraph-server/hugegraph-dist/src/assembly/travis
|
||||
REPORT_DIR: target/site/jacoco
|
||||
BACKEND: rocksdb
|
||||
JAVA_VERSION: '11'
|
||||
SERVER_JAVA_OPTIONS: ${{ matrix.server_java_options }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 5
|
||||
|
||||
- name: Install Java ${{ env.JAVA_VERSION }}
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
|
||||
- name: Use staged maven repo settings
|
||||
if: ${{ env.USE_STAGE == 'true' }}
|
||||
run: |
|
||||
cp $HOME/.m2/settings.xml /tmp/settings.xml
|
||||
cp -vf .github/configs/settings.xml $HOME/.m2/settings.xml && cat $HOME/.m2/settings.xml
|
||||
|
||||
- name: Compile
|
||||
run: |
|
||||
mvn clean compile -pl hugegraph-server/hugegraph-test -am -U -Dmaven.javadoc.skip=true -ntp
|
||||
|
||||
- name: Run check_port unit tests
|
||||
# Validates the ss/netstat port preflight that replaced lsof
|
||||
run: |
|
||||
$TRAVIS_DIR/test-check-port.sh \
|
||||
hugegraph-server/hugegraph-dist/src/assembly/static \
|
||||
hugegraph-store/hg-store-dist/src/assembly/static/bin/util.sh
|
||||
|
||||
- name: Run RocksDB core test
|
||||
run: |
|
||||
$TRAVIS_DIR/run-core-test.sh $BACKEND
|
||||
|
||||
- name: Run RocksDB API test
|
||||
run: |
|
||||
$TRAVIS_DIR/run-api-test.sh $BACKEND $REPORT_DIR true
|
||||
|
||||
- name: Show server log on failure
|
||||
if: failure()
|
||||
run: |
|
||||
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
|
||||
SERVER_DIR=hugegraph-server/apache-hugegraph-server-$VERSION/
|
||||
if [ -f "$SERVER_DIR/logs/hugegraph-server.log" ]; then
|
||||
tail -n 200 "$SERVER_DIR/logs/hugegraph-server.log"
|
||||
fi
|
||||
|
||||
- name: Stop RocksDB server
|
||||
if: always()
|
||||
run: |
|
||||
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
|
||||
SERVER_DIR=hugegraph-server/apache-hugegraph-server-$VERSION/
|
||||
if [ -f "$SERVER_DIR/bin/pid" ]; then
|
||||
$TRAVIS_DIR/stop-server.sh $SERVER_DIR || true
|
||||
fi
|
||||
|
||||
build-server-riscv64:
|
||||
uses: ./.github/workflows/riscv64-ci.yml
|
||||
permissions:
|
||||
contents: read
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
name: Mark stale issues and pull requests
|
||||
name: "Mark stale issues and pull requests"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
|
|
@ -20,8 +20,8 @@ jobs:
|
|||
stale-pr-message: 'Due to the lack of activity, the current pr is marked as stale and will be closed after 180 days, any update will remove the stale label'
|
||||
stale-issue-label: 'inactive'
|
||||
stale-pr-label: 'inactive'
|
||||
exempt-issue-labels: 'feature,bug,enhancement,improvement,wontfix,todo,guide,doc,help wanted'
|
||||
exempt-pr-labels: 'feature,bug,enhancement,improvement,wontfix,todo,guide,doc,help wanted'
|
||||
exempt-issue-labels: 'feature,bug,enhancement,improvement,todo,guide,doc,help wanted,security'
|
||||
exempt-pr-labels: 'feature,bug,enhancement,improvement,todo,guide,doc,help wanted,security'
|
||||
exempt-all-milestones: true
|
||||
|
||||
days-before-issue-stale: 15
|
||||
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
days-before-pr-stale: 30
|
||||
days-before-pr-close: 180
|
||||
operations-per-run: 10
|
||||
start-date: '2017-10-01T00:00:00Z'
|
||||
start-date: '2016-10-01T00:00:00Z'
|
||||
|
||||
exempt-all-assignees: true
|
||||
remove-stale-when-updated: true
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ gen-java
|
|||
.svn
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
.idea/
|
||||
.idea/*
|
||||
!.idea/vcs.xml
|
||||
!.idea/icon.png
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
|
@ -56,7 +57,7 @@ build/
|
|||
*.pyc
|
||||
|
||||
# maven ignore
|
||||
apache-hugegraph-incubating-*/
|
||||
apache-hugegraph-*/
|
||||
output/
|
||||
*.war
|
||||
*.zip
|
||||
|
|
@ -65,6 +66,8 @@ output/
|
|||
tree.txt
|
||||
*.versionsBackup
|
||||
.flattened-pom.xml
|
||||
**/dependency-reduced-pom.xml
|
||||
install-dist/dist.sh
|
||||
|
||||
|
||||
# eclipse ignore
|
||||
|
|
@ -83,3 +86,35 @@ hs_err_pid*
|
|||
.mtj.tmp/
|
||||
# blueJ files
|
||||
*.ctxt
|
||||
|
||||
# docker volumes ignore
|
||||
hugegraph-server/hugegraph-dist/docker/data/
|
||||
|
||||
# AI-IDE prompt files (We only keep AGENTS.md, other files could soft-linked it when needed)
|
||||
# Serena MCP memories
|
||||
.serena/*
|
||||
!.serena/project.yml
|
||||
!.serena/memories/
|
||||
.mcp.json
|
||||
# Claude Projects
|
||||
CLAUDE.md
|
||||
CLAUDE_*.md
|
||||
# Gemini/Google
|
||||
GEMINI.md
|
||||
# GitHub Copilot / Microsoft
|
||||
copilot-instructions.md
|
||||
.copilot-instructions.md
|
||||
# Cursor IDE
|
||||
cursor-instructions.md
|
||||
.cursor-instructions.md
|
||||
cursor.md
|
||||
# Windsurf/Codeium
|
||||
windsurf.md
|
||||
windsurf-instructions.md
|
||||
codeium.md
|
||||
codeium-instructions.md
|
||||
# Other AI coding assistants
|
||||
.ai-instructions.md
|
||||
*.ai-prompt.md
|
||||
WARP.md
|
||||
.mcp.json
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<project version="4">
|
||||
<component name="IssueNavigationConfiguration">
|
||||
<option name="links">
|
||||
<list>
|
||||
<IssueNavigationLink>
|
||||
<option name="issueRegexp" value="#(\d+)" />
|
||||
<!--
|
||||
GitHub share the sequence number of issues and pull requests, and it will redirect to
|
||||
the right place when the the sequence number not match kind.
|
||||
-->
|
||||
<option name="linkRegexp" value="https://github.com/apache/hugegraph/pull/$1" />
|
||||
</IssueNavigationLink>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -26,7 +26,9 @@ header: # `header` section is configurations for source codes license header.
|
|||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
|
@ -34,20 +36,20 @@ header: # `header` section is configurations for source codes license header.
|
|||
limitations under the License.
|
||||
# `pattern` is optional regexp if all the file headers are the same as `license` or the license of `spdx-id` and `copyright-owner`.
|
||||
pattern: |
|
||||
Licensed to the Apache Software Foundation under one or more contributor
|
||||
license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright
|
||||
ownership. The Apache Software Foundation licenses this file to you under
|
||||
the Apache License, Version 2.0 \(the "License"\); you may
|
||||
not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
Licensed to the Apache Software Foundation \(ASF\) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
\(the "License"\); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
paths: # `paths` are the path list that will be checked (and fixed) by license-eye, default is ['**'].
|
||||
- '**'
|
||||
|
||||
|
|
@ -55,6 +57,9 @@ header: # `header` section is configurations for source codes license header.
|
|||
- '.gitignore'
|
||||
- '.gitattributes'
|
||||
- 'LICENSE'
|
||||
- 'NOTICE'
|
||||
- 'DISCLAIMER'
|
||||
- '.serena/**'
|
||||
- '**/*.versionsBackup'
|
||||
- '**/*.versionsBackup'
|
||||
- '**/*.proto'
|
||||
|
|
@ -66,7 +71,7 @@ header: # `header` section is configurations for source codes license header.
|
|||
- '**/*.properties'
|
||||
- '**/RaftRequests.java'
|
||||
- 'dist/**/*'
|
||||
- 'hugegraph-dist'
|
||||
- 'hugegraph-server/hugegraph-dist'
|
||||
- '**/assembly/static/bin/hugegraph.service'
|
||||
- 'scripts/dev/reviewers'
|
||||
- 'scripts/dev/reviewers'
|
||||
|
|
@ -87,6 +92,17 @@ header: # `header` section is configurations for source codes license header.
|
|||
- '**/META-INF/MANIFEST.MF'
|
||||
- '.repository/**'
|
||||
- '**/.flattened-pom.xml'
|
||||
- 'hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/id/SnowflakeIdGenerator.java'
|
||||
- 'hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/HugeScriptTraversal.java'
|
||||
- 'hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/type/Nameable.java'
|
||||
- 'hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/type/define/Cardinality.java'
|
||||
- 'hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/util/StringEncoding.java'
|
||||
- 'hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/tinkerpop/ProcessBasicSuite.java'
|
||||
- 'hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/tinkerpop/StructureBasicSuite.java'
|
||||
- 'hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessor.java'
|
||||
- 'hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java'
|
||||
- 'hugegraph-struct/src/main/java/org/apache/hugegraph/type/define/Cardinality.java'
|
||||
- 'hugegraph-struct/src/main/java/org/apache/hugegraph/type/Namifiable.java'
|
||||
comment: on-failure # on what condition license-eye will comment on the pull request, `on-failure`, `always`, `never`.
|
||||
|
||||
# license-location-threshold specifies the index threshold where the license header can be located,
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
/cache
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# Architecture and Module Structure
|
||||
|
||||
## Three-Tier Architecture
|
||||
|
||||
### 1. Client Layer
|
||||
- Gremlin/Cypher queries, REST APIs, Swagger UI
|
||||
|
||||
### 2. Server Layer (hugegraph-server, 8 submodules)
|
||||
- **REST API** (hugegraph-api): GraphAPI, SchemaAPI, GremlinAPI, CypherAPI, AuthAPI, GraphSpaceAPI (distributed only), ManagerAPI (distributed only)
|
||||
- **Graph Engine** (hugegraph-core): Schema (with TTL update), traversal, task scheduling, GraphSpace multi-tenancy
|
||||
- **Backend Interface**: Pluggable via `BackendStore`
|
||||
|
||||
### 3. Storage Layer
|
||||
- RocksDB (default/embedded), HStore (distributed/production)
|
||||
- HBase (deprecated; planned for removal in 2.0)
|
||||
|
||||
## Module Structure (7 top-level modules)
|
||||
|
||||
### hugegraph-server (8 submodules)
|
||||
`hugegraph-core`, `hugegraph-api` (includes `opencypher/`, `space/`), `hugegraph-dist`, `hugegraph-test`, `hugegraph-example`, plus backends: `hugegraph-rocksdb`, `hugegraph-hstore`, `hugegraph-hbase`
|
||||
|
||||
### hugegraph-pd (8 submodules)
|
||||
Placement Driver: `hg-pd-core`, `hg-pd-service`, `hg-pd-client`, `hg-pd-common`, `hg-pd-grpc`, `hg-pd-cli`, `hg-pd-dist`, `hg-pd-test`
|
||||
|
||||
### hugegraph-store (9 submodules)
|
||||
Distributed storage + Raft: `hg-store-core`, `hg-store-node`, `hg-store-client`, `hg-store-common`, `hg-store-grpc`, `hg-store-rocksdb`, `hg-store-cli`, `hg-store-dist`, `hg-store-test`
|
||||
|
||||
### Others
|
||||
- **hugegraph-commons**: Shared utilities, RPC framework
|
||||
- **hugegraph-struct**: Data structures (must build before PD/Store)
|
||||
- **install-dist**: Distribution packaging, license files
|
||||
- **hugegraph-cluster-test**: Cluster integration tests
|
||||
|
||||
## Distributed Deployment (BETA)
|
||||
PD + Store + Server (3+ nodes each), all gRPC. Docker compose configs in `docker/` directory, using bridge networking (migrated from host mode).
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# Code Style and Conventions
|
||||
|
||||
## Style Tools
|
||||
- `.editorconfig` — Primary style definition
|
||||
- `style/checkstyle.xml` — Enforcement (note: `hugegraph-style.xml` was removed)
|
||||
- `.licenserc.yaml` + apache-rat-plugin + skywalking-eyes — License header validation
|
||||
|
||||
## Core Rules
|
||||
- **Line length**: 100 chars (120 for XML)
|
||||
- **Indent**: 4 spaces, continuation 8 spaces
|
||||
- **Charset**: UTF-8, LF line endings, final newline
|
||||
- **Imports**: Sorted `$*` → `java` → `javax` → `org` → `com` → `*`, no star imports (threshold 100)
|
||||
- **Braces**: `if`/`while`/`for` forced if multiline, `do-while` always
|
||||
- **Blank lines**: Max 1 in code/declarations
|
||||
- **JavaDoc**: `<p>` on empty lines, no wrap if one line
|
||||
|
||||
## Naming
|
||||
- Packages: `org.apache.hugegraph.*` (lowercase dot-separated)
|
||||
- Classes: PascalCase, Methods/Variables: camelCase, Constants: UPPER_SNAKE_CASE
|
||||
|
||||
## License
|
||||
- All source files require Apache 2.0 header
|
||||
- gRPC generated code excluded from checks
|
||||
- Validate: `mvn apache-rat:check -ntp` + `mvn editorconfig:check`
|
||||
|
||||
## Build
|
||||
- Java 11 target, `-Xlint:unchecked`, Lombok 1.18.30 (provided/optional)
|
||||
- Swagger: `io.swagger.core.v3:swagger-jaxrs2-jakarta` for REST API docs
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# HugeGraph Ecosystem and Related Projects
|
||||
|
||||
## This Repo: apache/hugegraph (server, OLTP)
|
||||
|
||||
## Ecosystem
|
||||
| Repo | Purpose |
|
||||
|------|---------|
|
||||
| hugegraph-toolchain | Loader, Hubble (visualization), Tools CLI, Java Client |
|
||||
| hugegraph-computer | OLAP: PageRank, Connected Components, Shortest Path |
|
||||
| incubator-hugegraph-ai | Graph RAG, KG construction, NL→Gremlin/Cypher |
|
||||
| hugegraph-doc | Docs & website (hugegraph.apache.org) |
|
||||
|
||||
## Data Flow
|
||||
```
|
||||
Sources → hugegraph-loader → hugegraph-server → Hubble / Computer / AI
|
||||
```
|
||||
|
||||
## Integrations
|
||||
- Big Data: Flink, Spark, HDFS
|
||||
- Queries: Gremlin (TinkerPop 3.5.1), OpenCypher, REST API + Swagger UI
|
||||
- Storage: RocksDB (default), HStore (distributed)
|
||||
|
||||
## Version: Server 1.7.0, TinkerPop 3.5.1, Java 11+
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
# Implementation Patterns and Guidelines
|
||||
|
||||
## Backend Architecture
|
||||
- Backends implement `BackendStore` interface from `hugegraph-core`
|
||||
- Each backend = separate Maven module under `hugegraph-server/`
|
||||
- Configured via `hugegraph.properties` → `backend` property
|
||||
- **Supported backends**: RocksDB (default/embedded), HStore (distributed), HBase (deprecated; planned for removal in 2.0)
|
||||
|
||||
## GraphSpace Multi-Tenancy
|
||||
- Core: `hugegraph-core/.../space/` (GraphSpace, SchemaTemplate, Service, register/)
|
||||
- API: `hugegraph-api/.../api/space/GraphSpaceAPI.java` (includes GS profile endpoints)
|
||||
- **Standalone mode**: GraphSpaceAPI and ManagerAPI are disabled
|
||||
|
||||
## Auth System
|
||||
- Disabled by default, enable via `bin/enable-auth.sh`
|
||||
- ConfigAuthenticator was removed, use standard auth
|
||||
- Multi-level: Users, Groups, Projects, Targets, Access control
|
||||
- Location: `hugegraph-api/.../api/auth/`
|
||||
|
||||
## gRPC Protocol
|
||||
- PD protos: `hugegraph-pd/hg-pd-grpc/src/main/proto/`
|
||||
- Store protos: `hugegraph-store/hg-store-grpc/src/main/proto/`
|
||||
- After `.proto` changes: `mvn clean compile` → `target/generated-sources/protobuf/`
|
||||
|
||||
## Query Languages
|
||||
- **Gremlin**: Native TinkerPop 3.5.1
|
||||
- **OpenCypher**: `hugegraph-api/opencypher/`
|
||||
- TinkerPop exceptions are passed through in Gremlin responses
|
||||
|
||||
## Schema
|
||||
- Labels support TTL with runtime update
|
||||
- Edge label conflicting conditions are handled safely
|
||||
|
||||
## Testing
|
||||
- **Profiles**: `unit-test`, `core-test`, `api-test`, `tinkerpop-structure-test`, `tinkerpop-process-test`
|
||||
- **Backends in CI**: memory, rocksdb, hbase (matrix)
|
||||
- **Single test class**: `mvn test -pl hugegraph-server/hugegraph-test -am -P core-test,memory -Dtest=ClassName`
|
||||
- TinkerPop tests: only on `release-*`/`test-*` branches
|
||||
- Raft tests: only on `test*`/`raft*` branches
|
||||
|
||||
## Docker
|
||||
- Single-node: `docker/docker-compose.yml` (bridge network, pd+store+server)
|
||||
- Cluster: `docker/docker-compose-3pd-3store-3server.yml`
|
||||
- Container logs: stdout-based
|
||||
|
||||
## CI Pipelines
|
||||
- `server-ci.yml`: compile + unit/core/API tests (memory/rocksdb/hbase × Java 11)
|
||||
- `rerun-ci.yml`: auto-rerun flaky failures (max 2 reruns, 180s delay)
|
||||
- `auto-pr-review.yml`: auto-comment on new PRs
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# Key File and Directory Locations
|
||||
|
||||
## Build & Config
|
||||
- `pom.xml` — Root multi-module POM
|
||||
- `.editorconfig` — Code style rules
|
||||
- `style/checkstyle.xml` — Checkstyle enforcement (hugegraph-style.xml was removed)
|
||||
- `.licenserc.yaml` — License checker config
|
||||
|
||||
## Server (hugegraph-server)
|
||||
- Core engine: `hugegraph-core/src/main/java/org/apache/hugegraph/` → `backend/`, `schema/`, `traversal/`, `task/`
|
||||
- GraphSpace: `hugegraph-core/.../space/` → `GraphSpace.java`, `SchemaTemplate.java`, `Service.java`, `register/`
|
||||
- REST APIs: `hugegraph-api/src/main/java/org/apache/hugegraph/api/` → `graph/`, `schema/`, `gremlin/`, `cypher/`, `auth/`, `space/` (GraphSpaceAPI), `metrics/`, `arthas/`
|
||||
- OpenCypher: `hugegraph-api/.../opencypher/`
|
||||
- Backend interface: `hugegraph-core/.../backend/store/BackendStore.java`
|
||||
- Distribution: `hugegraph-dist/src/assembly/static/` → `bin/`, `conf/`, `lib/`, `logs/`
|
||||
- Tests: `hugegraph-test/src/main/java/.../` → `unit/`, `core/`, `api/`, `tinkerpop/`
|
||||
|
||||
## Docker
|
||||
- `docker/docker-compose.yml` — Single-node (bridge network, pd+store+server)
|
||||
- `docker/docker-compose-3pd-3store-3server.yml` — 3-node cluster
|
||||
- `docker/docker-compose.dev.yml` — Dev mode
|
||||
|
||||
## PD Module
|
||||
- Proto: `hugegraph-pd/hg-pd-grpc/src/main/proto/`
|
||||
- Dist: `hugegraph-pd/hg-pd-dist/src/assembly/static/`
|
||||
|
||||
## Store Module
|
||||
- Proto: `hugegraph-store/hg-store-grpc/src/main/proto/`
|
||||
- Dist: `hugegraph-store/hg-store-dist/src/assembly/static/`
|
||||
|
||||
## CI Workflows (.github/workflows/)
|
||||
- `server-ci.yml` — Server tests (matrix: memory/rocksdb/hbase × Java 11)
|
||||
- `pd-store-ci.yml` — PD, Store & HStore tests
|
||||
- `commons-ci.yml` — Commons tests
|
||||
- `cluster-test-ci.yml` — Cluster integration
|
||||
- `licence-checker.yml` — License headers
|
||||
- `rerun-ci.yml` — Auto-rerun for flaky workflows
|
||||
- `auto-pr-review.yml` — Auto-comment on new PRs
|
||||
- `check-dependencies.yml` — Dependency checks
|
||||
- `codeql-analysis.yml` — CodeQL security scanning
|
||||
- `stale.yml` — Stale issue/PR cleanup
|
||||
|
||||
## Docs
|
||||
- `README.md`, `BUILDING.md`, `CONTRIBUTING.md`, `AGENTS.md`, `CLAUDE.md`
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# Apache HugeGraph Project Overview
|
||||
|
||||
## Project Purpose
|
||||
Apache HugeGraph is a fast-speed, highly-scalable graph database supporting 10+ billion vertices/edges for OLTP workloads. Graduated from Apache Incubator (incubating branding removed).
|
||||
|
||||
## Key Capabilities
|
||||
- Apache TinkerPop 3 compliant graph database
|
||||
- Gremlin + OpenCypher query languages
|
||||
- Schema metadata management (VertexLabel, EdgeLabel, PropertyKey, IndexLabel) with TTL update support
|
||||
- Multi-type indexes (exact, range, complex conditions)
|
||||
- Pluggable backend storage (RocksDB default, HStore distributed)
|
||||
- GraphSpace multi-tenancy (standalone mode disables GraphSpaceAPI/ManagerAPI)
|
||||
- Swagger UI for REST API documentation
|
||||
- Integration with Flink/Spark/HDFS
|
||||
|
||||
## Technology Stack
|
||||
- **Language**: Java 11+ (required)
|
||||
- **Build**: Maven 3.5+
|
||||
- **Graph Framework**: Apache TinkerPop 3.5.1
|
||||
- **RPC**: gRPC + Protocol Buffers
|
||||
- **API Docs**: Swagger (io.swagger.core.v3)
|
||||
- **Storage**: RocksDB (default/embedded), HStore (distributed/production), HBase (deprecated; planned for removal in 2.0)
|
||||
|
||||
## Version
|
||||
- Current: 1.7.0 (`${revision}` property, Maven flatten plugin)
|
||||
- License: Apache License 2.0
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
# Suggested Development Commands
|
||||
|
||||
## Build
|
||||
```bash
|
||||
mvn clean install -DskipTests # Full build
|
||||
mvn clean install -pl hugegraph-server -am -DskipTests # Server only
|
||||
mvn clean compile -U -Dmaven.javadoc.skip=true -ntp # Compile only
|
||||
mvn clean package -DskipTests # Distribution → install-dist/target/
|
||||
```
|
||||
|
||||
## Test
|
||||
```bash
|
||||
# Server tests (memory/rocksdb/hbase backends)
|
||||
mvn test -pl hugegraph-server/hugegraph-test -am -P unit-test
|
||||
mvn test -pl hugegraph-server/hugegraph-test -am -P core-test,memory
|
||||
mvn test -pl hugegraph-server/hugegraph-test -am -P core-test,rocksdb
|
||||
mvn test -pl hugegraph-server/hugegraph-test -am -P api-test,rocksdb
|
||||
|
||||
# Single test class
|
||||
mvn test -pl hugegraph-server/hugegraph-test -am -P core-test,memory -Dtest=YourTestClass
|
||||
|
||||
# TinkerPop compliance (release/test branches only)
|
||||
mvn test -pl hugegraph-server/hugegraph-test -am -P tinkerpop-structure-test,memory
|
||||
|
||||
# PD/Store (build struct first)
|
||||
mvn install -pl hugegraph-struct -am -DskipTests
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am
|
||||
mvn test -pl hugegraph-store/hg-store-test -am
|
||||
```
|
||||
|
||||
## Validation
|
||||
```bash
|
||||
mvn apache-rat:check -ntp # License headers
|
||||
mvn editorconfig:check # Code style (.editorconfig)
|
||||
mvn checkstyle:check # Code style (style/checkstyle.xml)
|
||||
mvn clean compile -Dmaven.javadoc.skip=true # Compile warnings
|
||||
```
|
||||
|
||||
## Server Ops
|
||||
Scripts in `hugegraph-server/hugegraph-dist/src/assembly/static/bin/` (or extracted distribution `bin/`):
|
||||
```bash
|
||||
bin/init-store.sh && bin/start-hugegraph.sh # Init + start
|
||||
bin/stop-hugegraph.sh # Stop
|
||||
bin/enable-auth.sh # Enable auth
|
||||
```
|
||||
|
||||
## Docker
|
||||
```bash
|
||||
cd docker && docker compose up -d # Single-node (bridge network)
|
||||
cd docker && docker compose -f docker-compose-3pd-3store-3server.yml up -d # Cluster
|
||||
```
|
||||
|
||||
## Distributed Build (BETA)
|
||||
```bash
|
||||
mvn install -pl hugegraph-struct -am -DskipTests # 1. Struct first
|
||||
mvn clean package -pl hugegraph-pd -am -DskipTests # 2. PD
|
||||
mvn clean package -pl hugegraph-store -am -DskipTests # 3. Store
|
||||
```
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# Task Completion Checklist
|
||||
|
||||
## 1. Code Quality (MANDATORY)
|
||||
```bash
|
||||
mvn apache-rat:check -ntp # License headers
|
||||
mvn editorconfig:check # Style (.editorconfig)
|
||||
mvn checkstyle:check # Style (style/checkstyle.xml)
|
||||
mvn clean compile -Dmaven.javadoc.skip=true # Compile warnings
|
||||
```
|
||||
|
||||
## 2. Testing
|
||||
- Choose backend: `memory` (fast), `rocksdb` (realistic), `hbase` (deprecated compatibility)
|
||||
- Single test: `-Dtest=ClassName` works with all profiles
|
||||
- Bug fix → existing tests; New feature → write tests; Refactor → affected module tests
|
||||
|
||||
## 3. Dependencies (if adding new)
|
||||
1. License file → `install-dist/release-docs/licenses/`
|
||||
2. Declare in `install-dist/release-docs/LICENSE`
|
||||
3. Append NOTICE → `install-dist/release-docs/NOTICE`
|
||||
4. Run `./install-dist/scripts/dependency/regenerate_known_dependencies.sh`
|
||||
|
||||
## 4. CI Awareness
|
||||
- `server-ci.yml`: memory/rocksdb/hbase × Java 11
|
||||
- `rerun-ci.yml`: auto-retries flaky failures
|
||||
- `licence-checker.yml`: header validation
|
||||
- Raft tests: only `test*`/`raft*` branches
|
||||
- TinkerPop tests: only `release-*`/`test-*` branches
|
||||
|
||||
## 5. Commit
|
||||
- NEVER commit unless explicitly asked
|
||||
- Format: `feat|fix|refactor(module): msg`
|
||||
- Include issue ID if available
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
# list of languages for which language servers are started; choose from:
|
||||
# al bash clojure cpp csharp
|
||||
# csharp_omnisharp dart elixir elm erlang
|
||||
# fortran fsharp go groovy haskell
|
||||
# java julia kotlin lua markdown
|
||||
# matlab nix pascal perl php
|
||||
# php_phpactor powershell python python_jedi r
|
||||
# rego ruby ruby_solargraph rust scala
|
||||
# swift terraform toml typescript typescript_vts
|
||||
# vue yaml zig
|
||||
# (This list may be outdated. For the current list, see values of Language enum here:
|
||||
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
|
||||
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
|
||||
# Note:
|
||||
# - For C, use cpp
|
||||
# - For JavaScript, use typescript
|
||||
# - For Free Pascal/Lazarus, use pascal
|
||||
# Special requirements:
|
||||
# Some languages require additional setup/installations.
|
||||
# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
|
||||
# When using multiple languages, the first language server that supports a given file will be used for that file.
|
||||
# The first language is the default language and the respective language server will be used as a fallback.
|
||||
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
|
||||
languages:
|
||||
- java
|
||||
|
||||
# the encoding used by text files in the project
|
||||
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
|
||||
encoding: "utf-8"
|
||||
|
||||
# whether to use project's .gitignore files to ignore files
|
||||
ignore_all_files_in_gitignore: true
|
||||
|
||||
# list of additional paths to ignore in this project.
|
||||
# Same syntax as gitignore, so you can use * and **.
|
||||
# Note: global ignored_paths from serena_config.yml are also applied additively.
|
||||
ignored_paths:
|
||||
# --- HBase is deprecated and planned for removal in 2.0 ---
|
||||
- "hugegraph-server/hugegraph-hbase/**"
|
||||
# --- gRPC generated Java (235k lines, never hand-edited, regenerated by mvn compile) ---
|
||||
- "hugegraph-pd/hg-pd-grpc/src/main/java/**"
|
||||
- "hugegraph-store/hg-store-grpc/src/main/java/**"
|
||||
# --- License/legal files (maintained through the release/dependency workflow) ---
|
||||
- "install-dist/release-docs/licenses/**"
|
||||
- "install-dist/scripts/dependency/known-dependencies.txt"
|
||||
# --- Rarely modified tests/examples ---
|
||||
- "hugegraph-server/hugegraph-test/**/tinkerpop/**"
|
||||
- "hugegraph-server/hugegraph-example/**"
|
||||
- "hugegraph-cluster-test/**"
|
||||
# --- Note: target/, .flattened-pom.xml, .idea/*, apache-hugegraph-*/ already covered by .gitignore ---
|
||||
|
||||
# whether the project is in read-only mode
|
||||
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
|
||||
# Added on 2025-04-18
|
||||
read_only: false
|
||||
|
||||
# list of tool names to exclude.
|
||||
# This extends the existing exclusions (e.g. from the global configuration)
|
||||
#
|
||||
# Below is the complete list of tools for convenience.
|
||||
# To make sure you have the latest list of tools, and to view their descriptions,
|
||||
# execute `uv run scripts/print_tool_overview.py`.
|
||||
#
|
||||
# * `activate_project`: Activates a project by name.
|
||||
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
|
||||
# * `create_text_file`: Creates/overwrites a file in the project directory.
|
||||
# * `delete_lines`: Deletes a range of lines within a file.
|
||||
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
|
||||
# * `execute_shell_command`: Executes a shell command.
|
||||
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
|
||||
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
|
||||
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
|
||||
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
|
||||
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
|
||||
# * `initial_instructions`: Gets the initial instructions for the current project.
|
||||
# Should only be used in settings where the system prompt cannot be set,
|
||||
# e.g. in clients you have no control over, like Claude Desktop.
|
||||
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
|
||||
# * `insert_at_line`: Inserts content at a given line in a file.
|
||||
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
|
||||
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
|
||||
# * `list_memories`: Lists memories in Serena's project-specific memory store.
|
||||
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
|
||||
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
|
||||
# * `read_file`: Reads a file within the project directory.
|
||||
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
|
||||
# * `remove_project`: Removes a project from the Serena configuration.
|
||||
# * `replace_lines`: Replaces a range of lines within a file with new content.
|
||||
# * `replace_symbol_body`: Replaces the full definition of a symbol.
|
||||
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
|
||||
# * `search_for_pattern`: Performs a search for a pattern in the project.
|
||||
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
|
||||
# * `switch_modes`: Activates modes by providing a list of their names
|
||||
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
|
||||
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
|
||||
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
|
||||
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
||||
excluded_tools: []
|
||||
|
||||
# initial prompt for the project. It will always be given to the LLM upon activating the project
|
||||
# (contrary to the memories, which are loaded on demand).
|
||||
initial_prompt: ""
|
||||
# the name by which the project can be referenced within Serena
|
||||
project_name: "server"
|
||||
|
||||
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default).
|
||||
# This extends the existing inclusions (e.g. from the global configuration).
|
||||
included_optional_tools: []
|
||||
|
||||
# list of mode names to that are always to be included in the set of active modes
|
||||
# The full set of modes to be activated is base_modes + default_modes.
|
||||
# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
|
||||
# Otherwise, this setting overrides the global configuration.
|
||||
# Set this to [] to disable base modes for this project.
|
||||
# Set this to a list of mode names to always include the respective modes for this project.
|
||||
base_modes:
|
||||
|
||||
# list of mode names that are to be activated by default.
|
||||
# The full set of modes to be activated is base_modes + default_modes.
|
||||
# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply.
|
||||
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
|
||||
# This setting can, in turn, be overridden by CLI parameters (--mode).
|
||||
default_modes:
|
||||
|
||||
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
|
||||
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
|
||||
fixed_tools: []
|
||||
|
||||
# time budget (seconds) per tool call for the retrieval of additional symbol information
|
||||
# such as docstrings or parameter information.
|
||||
# This overrides the corresponding setting in the global configuration; see the documentation there.
|
||||
# If null or missing, use the setting from the global configuration.
|
||||
symbol_info_budget:
|
||||
|
||||
# The language backend to use for this project.
|
||||
# If not set, the global setting from serena_config.yml is used.
|
||||
# Valid values: LSP, JetBrains
|
||||
# Note: the backend is fixed at startup. If a project with a different backend
|
||||
# is activated post-init, an error will be returned.
|
||||
language_backend:
|
||||
|
||||
# list of regex patterns which, when matched, mark a memory entry as read‑only.
|
||||
# Extends the list from the global configuration, merging the two lists.
|
||||
read_only_memory_patterns: []
|
||||
|
||||
# line ending convention to use when writing source files.
|
||||
# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default)
|
||||
# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings.
|
||||
line_ending:
|
||||
|
||||
# list of regex patterns for memories to completely ignore.
|
||||
# Matching memories will not appear in list_memories or activate_project output
|
||||
# and cannot be accessed via read_memory or write_memory.
|
||||
# To access ignored memory files, use the read_file tool on the raw file path.
|
||||
# Extends the list from the global configuration, merging the two lists.
|
||||
# Example: ["_archive/.*", "_episodes/.*"]
|
||||
ignored_memory_patterns: []
|
||||
|
||||
# advanced configuration option allowing to configure language server-specific options.
|
||||
# Maps the language key to the options.
|
||||
# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available.
|
||||
# No documentation on options means no options are available.
|
||||
ls_specific_settings: {}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
# AGENTS.md
|
||||
|
||||
Single source of truth for AI coding agents.
|
||||
README.md covers human-facing deployment/ecosystem context; only consult it on demand.
|
||||
|
||||
## Stack & Modules
|
||||
|
||||
Apache HugeGraph — Apache TinkerPop 3 compliant graph database.
|
||||
Java 11+, Maven 3.5+. Version managed via `${revision}` (currently `1.8.0`).
|
||||
|
||||
```
|
||||
Client (Gremlin / Cypher / REST)
|
||||
│
|
||||
Server = hugegraph-server
|
||||
├─ hugegraph-api REST, Gremlin/Cypher, auth
|
||||
├─ hugegraph-core engine, schema, traversal, BackendStore interface
|
||||
└─ Backend impls rocksdb (default, embedded) │ hstore (distributed)
|
||||
▼
|
||||
hugegraph-pd (placement) + hugegraph-store (Raft)
|
||||
```
|
||||
|
||||
Top-level modules: `hugegraph-server` · `hugegraph-pd` · `hugegraph-store` ·
|
||||
`hugegraph-commons` (shared utils & RPC) · `hugegraph-struct` (data types; dep of PD/Store).
|
||||
|
||||
Server submodules worth knowing: `hugegraph-core`, `hugegraph-api`,
|
||||
`hugegraph-rocksdb`, `hugegraph-hstore`, `hugegraph-test`, `hugegraph-dist`.
|
||||
|
||||
## Code Search Anchors
|
||||
|
||||
| Area | Path |
|
||||
|---|---|
|
||||
| Graph engine | `hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/` |
|
||||
| REST APIs | `hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/` |
|
||||
| Backend interface | `hugegraph-server/hugegraph-core/.../backend/store/BackendStore.java` |
|
||||
| Auth | `hugegraph-server/hugegraph-api/.../api/auth/` |
|
||||
| gRPC protos | `hugegraph-{pd,store}/hg-{pd,store}-grpc/src/main/proto/` |
|
||||
|
||||
Config roots (under each dist module's `src/assembly/static/conf/`):
|
||||
- Server — `hugegraph.properties`, `rest-server.properties`, `gremlin-server.yaml`
|
||||
- PD / Store — `application.yml`
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
# All modules
|
||||
mvn clean install -DskipTests
|
||||
|
||||
# Single module
|
||||
mvn clean install -pl hugegraph-server -am -DskipTests
|
||||
```
|
||||
|
||||
Distributed build order (for HStore-enabled dev):
|
||||
|
||||
```bash
|
||||
mvn install -pl hugegraph-struct -am -DskipTests # 1. shared data types
|
||||
mvn clean package -pl hugegraph-pd -am -DskipTests # 2. placement driver
|
||||
mvn clean package -pl hugegraph-store -am -DskipTests # 3. distributed storage
|
||||
mvn clean package -pl hugegraph-server -am -DskipTests # 4. server
|
||||
```
|
||||
|
||||
Runtime scripts (human-run) live in `hugegraph-server/hugegraph-dist/src/assembly/static/bin/`:
|
||||
`init-store.sh`, `start-hugegraph.sh`, `stop-hugegraph.sh`.
|
||||
|
||||
## Testing
|
||||
|
||||
Server tests implicitly prefix `mvn test -pl hugegraph-server/hugegraph-test -am`:
|
||||
|
||||
| Profile | Suffix |
|
||||
|---|---|
|
||||
| Unit | `-P unit-test` |
|
||||
| Core | `-P core-test,rocksdb` (swap `rocksdb` for `memory`) |
|
||||
| API | `-P api-test,rocksdb` |
|
||||
| TinkerPop structure / process | `-P tinkerpop-{structure,process}-test,memory` |
|
||||
| Single class | `-P core-test,rocksdb -Dtest=YourTestClass` |
|
||||
|
||||
PD / Store tests (need `hugegraph-struct` installed first):
|
||||
|
||||
```bash
|
||||
mvn install -pl hugegraph-struct -am -DskipTests
|
||||
mvn test -pl hugegraph-pd/hg-pd-test -am
|
||||
mvn test -pl hugegraph-store/hg-store-test -am
|
||||
```
|
||||
|
||||
Before writing new tests, check existing suites under `hugegraph-server/hugegraph-test/`.
|
||||
|
||||
## Style & Pre-commit
|
||||
|
||||
- Line 100, 4-space indent, LF, UTF-8, **no star imports**
|
||||
- Commit format: `feat|fix|refactor(module): msg`
|
||||
- Run before pushing:
|
||||
```bash
|
||||
mvn editorconfig:format # enforce code style
|
||||
mvn clean compile -Dmaven.javadoc.skip=true # surface warnings
|
||||
```
|
||||
|
||||
## Cross-module notes
|
||||
|
||||
- `.proto` edits: `mvn clean compile` regenerates gRPC stubs under
|
||||
`target/generated-sources/protobuf/` (output packages `*/grpc/` are excluded from Apache RAT).
|
||||
- Adding a third-party dep: update `install-dist/release-docs/{LICENSE,NOTICE,licenses/}`
|
||||
and `install-dist/scripts/dependency/known-dependencies.txt`.
|
||||
- `hugegraph-commons` is shared by every module; `hugegraph-struct` must precede PD/Store;
|
||||
server backends depend on `hugegraph-core`.
|
||||
|
||||
## Additional context files
|
||||
|
||||
`.serena/memories/` — notably `suggested_commands.md` and `task_completion_checklist.md`
|
||||
when a task needs depth beyond this file.
|
||||
37
BUILDING.md
37
BUILDING.md
|
|
@ -1,28 +1,35 @@
|
|||
Building hugegraph
|
||||
Building HugeGraph
|
||||
--------------
|
||||
|
||||
Required:
|
||||
|
||||
* Java 8/11
|
||||
* Maven
|
||||
* Java 11
|
||||
* Maven 3.5+
|
||||
|
||||
To build without executing tests: `mvn clean package -Dmaven.test.skip=true`
|
||||
|
||||
## Building in IDEA
|
||||
|
||||
To build without executing tests:
|
||||
|
||||
```
|
||||
mvn clean
|
||||
mvn package -DskipTests
|
||||
```
|
||||
1. Click on "File" -> "Open", choose your project location.
|
||||
2. Open maven view by click "View" -> "Tool Windows" -> "Maven Projects".
|
||||
3. Choose root module "hugegraph: Distributed Graph Database", unfold the menu of "Lifecycle".
|
||||
4. Click the "Toggle 'Skip Tests' Mode" button which is located on the top navibar of "Maven Projects" window to skip tests.
|
||||
5. Double click "package" or "install" to build a project.
|
||||
|
||||
## Building on Eclipse IDE
|
||||
Note that this has only been tested on Eclipse Neon.2 Release (4.6.2) with m2e (1.7.0.20160603-1933) and m2e-wtp (1.3.1.20160831-1005) plugin.
|
||||
Could also refer [Dev-In-IDEA](https://hugegraph.apache.org/docs/contribution-guidelines/hugegraph-server-idea-setup/) for more details.
|
||||
|
||||
## Building in Eclipse
|
||||
|
||||
> Note: this has only been tested on Eclipse Neon.2 Release (4.6.2) with m2e (1.7.0.20160603-1933) and m2e-wtp (1.3.1.20160831-1005) plugin.
|
||||
|
||||
To build without executing tests:
|
||||
|
||||
1. Right-click on your project -> "Run As..." -> "Run Configurations..."
|
||||
2. On "Goals", populate with `install`
|
||||
3. Select the options `Update Snapshots` and `Skip Tests`
|
||||
4. Before clicking "Run", make sure that Eclipse knows where `JAVA_HOME` is. On same window, go to "Environment" tab and click "New".
|
||||
4. Before clicking "Run", make sure that Eclipse knows where `JAVA_HOME` is. In the same window, go to "Environment" tab and click "New".
|
||||
5. Under "Name:", add `JAVA_HOME`
|
||||
6. Under "Value:", add the path where `java` is located
|
||||
7. Click "OK"
|
||||
|
|
@ -32,13 +39,3 @@ To find the Java binary in your environment, run the appropriate command for you
|
|||
* Linux/macOS: `which java`
|
||||
* Windows: `for %i in (java.exe) do @echo. %~$PATH:i`
|
||||
|
||||
## Building on IDEA
|
||||
|
||||
To build without executing tests:
|
||||
|
||||
1. Click on "File" -> "Open", choose your project location.
|
||||
2. Open maven view by click "View" -> "Tool Windows" -> "Maven Projects".
|
||||
3. Choose root module "hugegraph: Distributed Graph Database", unfold the menu of "Lifecycle".
|
||||
4. Click the "Toggle 'Skip Tests' Mode" button which is located on the top
|
||||
navibar of "Maven Projects" window to skip tests.
|
||||
5. Double click "package" or "install" to build project.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
# How to Contribute to HugeGraph
|
||||
|
||||
Thanks for taking the time to contribute! As an open source project, HugeGraph is looking forward to be contributed from everyone, and we are also grateful to all of the contributors.
|
||||
> Refer [website-doc](https://hugegraph.apache.org/docs/contribution-guidelines/) for the latest information.
|
||||
|
||||
Thanks for taking the time to contribute!
|
||||
As an open source project, HugeGraph is looking forward to being contributed from everyone, and we are also grateful to all the contributors.
|
||||
|
||||
The following is a contribution guide for HugeGraph:
|
||||
|
||||
|
|
@ -8,19 +11,21 @@ The following is a contribution guide for HugeGraph:
|
|||
|
||||
## 1. Preparation
|
||||
|
||||
**Recommended**: You can use [GitHub desktop](https://desktop.github.com/) to greatly simplify the PR process.
|
||||
|
||||
We can contribute by reporting issues, submitting code patches or any other feedback.
|
||||
|
||||
Before submitting the code, we need to do some preparation:
|
||||
|
||||
1. Sign up or login to GitHub: [https://github.com](https://github.com)
|
||||
1. Sign up or login to GitHub: [https://github.com](https://github.com)
|
||||
|
||||
2. Fork HugeGraph repo from GitHub: [https://github.com/apache/incubator-hugegraph/fork](https://github.com/apache/incubator-hugegraph/fork)
|
||||
2. Fork HugeGraph repo from GitHub: [https://github.com/apache/hugegraph/fork](https://github.com/apache/hugegraph/fork)
|
||||
|
||||
3. Clone code from fork repo to local: [https://github.com/${GITHUB_USER_NAME}/incubator-hugegraph](https://github.com/${GITHUB_USER_NAME}/incubator-hugegraph)
|
||||
3. Clone code from fork repo to local: [https://github.com/${GITHUB_USER_NAME}/hugegraph](https://github.com/${GITHUB_USER_NAME}/hugegraph)
|
||||
|
||||
```shell
|
||||
# clone code from remote to local repo
|
||||
git clone https://github.com/${GITHUB_USER_NAME}/incubator-hugegraph.git hugegraph
|
||||
git clone https://github.com/${GITHUB_USER_NAME}/hugegraph.git hugegraph
|
||||
```
|
||||
|
||||
4. Configure local HugeGraph repo
|
||||
|
|
@ -29,24 +34,22 @@ Before submitting the code, we need to do some preparation:
|
|||
cd hugegraph
|
||||
|
||||
# add upstream to synchronize the latest code
|
||||
git remote add hugegraph https://github.com/apache/incubator-hugegraph
|
||||
git remote add hugegraph https://github.com/apache/hugegraph
|
||||
|
||||
# set name and email to push code to github
|
||||
git config user.name "{full-name}" # like "Jermy Li"
|
||||
git config user.email "{email-address-of-github}" # like "jermy@apache.org"
|
||||
```
|
||||
|
||||
Optional: You can use [GitHub desktop](https://desktop.github.com/) to greatly simplify the commit and update process.
|
||||
|
||||
## 2. Create an Issue on GitHub
|
||||
|
||||
If you encounter bugs or have any questions, please go to [GitHub Issues](https://github.com/apache/incubator-hugegraph/issues) to report them and feel free to [create an issue](https://github.com/apache/incubator-hugegraph/issues/new).
|
||||
If you encounter bugs or have any questions, please go to [GitHub Issues](https://github.com/apache/hugegraph/issues) to report them and feel free to [create an issue](https://github.com/apache/hugegraph/issues/new).
|
||||
|
||||
## 3. Make changes of code locally
|
||||
|
||||
#### 3.1 Create a new branch
|
||||
|
||||
Please don't use master branch for development. Instead we should create a new branch:
|
||||
Please don't use master branch for development. Instead, we should create a new branch:
|
||||
|
||||
```shell
|
||||
# checkout master branch
|
||||
|
|
@ -63,12 +66,26 @@ Assume that we need to modify some files like "HugeGraph.java" and "HugeFactory.
|
|||
|
||||
```shell
|
||||
# modify code to fix a bug
|
||||
vim hugegraph-core/src/main/java/com/baidu/hugegraph/HugeGraph.java
|
||||
vim hugegraph-core/src/main/java/com/baidu/hugegraph/HugeFactory.java
|
||||
vim hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/HugeGraph.java
|
||||
vim hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/HugeFactory.java
|
||||
# run test locally (optional)
|
||||
mvn test -Pcore-test,memory
|
||||
```
|
||||
Note: In order to be consistent with the code style easily, if you use [IDEA](https://www.jetbrains.com/idea/) as your IDE, you can directly [import](https://www.jetbrains.com/help/idea/configuring-code-style.html) our code style [configuration file](./hugegraph-style.xml).
|
||||
Note: Code style is defined by the `.editorconfig` file at the repository root. Checkstyle rules are defined in `style/checkstyle.xml`. Configure your IDE accordingly.
|
||||
|
||||
##### 3.2.1 Check licenses
|
||||
If we want to add new third-party dependencies to the `HugeGraph` project, we need to do the following things:
|
||||
1. Find the third-party dependent repository, put the dependent `license` file into [./install-dist/release-docs/licenses/](https://github.com/apache/hugegraph/tree/master/install-dist/release-docs/licenses) path.
|
||||
2. Declare the dependency in [./install-dist/release-docs/LICENSE](https://github.com/apache/hugegraph/blob/master/install-dist/release-docs/LICENSE) `LICENSE` information.
|
||||
3. Find the NOTICE file in the repository and append it to [./install-dist/release-docs/NOTICE](https://github.com/apache/hugegraph/blob/master/install-dist/release-docs/NOTICE) file (skip this step if there is no NOTICE file).
|
||||
4. Execute locally [./install-dist/scripts/dependency/regenerate_known_dependencies.sh](https://github.com/apache/hugegraph/blob/master/install-dist/scripts/dependency/regenerate_known_dependencies.sh) to update the dependency list [known-dependencies.txt](https://github.com/apache/hugegraph/blob/master/install-dist/scripts/dependency/known-dependencies.txt) (or manually update).
|
||||
|
||||
**Example**: A new third-party dependency is introduced into the project -> `ant-1.9.1.jar`
|
||||
- The project source code is located at: https://github.com/apache/ant/tree/rel/1.9.1
|
||||
- LICENSE file: https://github.com/apache/ant/blob/rel/1.9.1/LICENSE
|
||||
- NOTICE file: https://github.com/apache/ant/blob/rel/1.9.1/NOTICE
|
||||
|
||||
The license information of `ant-1.9.1.jar` needs to be specified in the LICENSE file, and the notice information needs to be specified in the NOTICE file. The detailed LICENSE file corresponding to ant-1.9.1.jar needs to be copied to our licenses/ directory. Finally, update the known-dependencies.txt file.
|
||||
|
||||
#### 3.3 Commit changes to git repo
|
||||
|
||||
|
|
@ -76,8 +93,8 @@ After the code has been completed, we submit them to the local git repo:
|
|||
|
||||
```shell
|
||||
# add files to local git index
|
||||
git add hugegraph-core/src/main/java/com/baidu/hugegraph/HugeGraph.java
|
||||
git add hugegraph-core/src/main/java/com/baidu/hugegraph/HugeFactory.java
|
||||
git add hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/HugeGraph.java
|
||||
git add hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/HugeFactory.java
|
||||
# commit to local git repo
|
||||
git commit
|
||||
```
|
||||
|
|
@ -90,7 +107,7 @@ Fix bug: run deploy multiple times
|
|||
fix #ISSUE_ID
|
||||
```
|
||||
|
||||
> Please remember to fill in the issue id, which was generated by GitHub after issue creation.
|
||||
> Please remember to fill in the issue id, which GitHub generated after issue creation.
|
||||
|
||||
#### 3.4 Push commit to GitHub fork repo
|
||||
|
||||
|
|
@ -106,11 +123,7 @@ Note that since GitHub requires submitting code through `username + token` (inst
|
|||
|
||||
## 4. Create a Pull Request
|
||||
|
||||
Go to the web page of GitHub fork repo, there would be a chance to create a Pull Request after pushing to a new branch, just click button "Compare & pull request" to do it. Then edit the description for proposed changes, which can just be copied from the commit message.
|
||||
|
||||
Please sign the HugeGraph CLA when contributing code for the first time. You can sign the CLA by just posting a Pull Request Comment same as the below format:
|
||||
|
||||
`I have read the CLA Document and I hereby sign the CLA`
|
||||
Go to the web page of GitHub fork repo, there would be a chance to create a Pull Request after pushing to a new branch, click the button "Compare & pull request" to do it. Then edit the description for proposed changes, which can just be copied from the commit message.
|
||||
|
||||
Note: please make sure the email address you used to submit the code is bound to the GitHub account. For how to bind the email address, please refer to https://github.com/settings/emails:
|
||||
<img width="1280" alt="image" src="https://user-images.githubusercontent.com/9625821/163522445-2a50a72a-dea2-434f-9868-3a0d40d0d037.png">
|
||||
|
|
@ -120,7 +133,7 @@ Note: please make sure the email address you used to submit the code is bound to
|
|||
Maintainers will start the code review after all the **automatic** checks are passed:
|
||||
|
||||
- Check: Contributor License Agreement is signed
|
||||
- Check: Travis CI builds is passed (automatically Test and Deploy)
|
||||
- Check: Travis CI builds are passed (automatically Test and Deploy)
|
||||
|
||||
The commit will be accepted and merged if there is no problem after review.
|
||||
|
||||
|
|
@ -160,8 +173,8 @@ And push it to GitHub fork repo again:
|
|||
git push -f origin bugfix-branch:bugfix-branch
|
||||
```
|
||||
|
||||
GitHub will automatically update the Pull Request after we push it, just wait for code review.
|
||||
GitHub will automatically update the Pull Request after we push it, wait for code review.
|
||||
|
||||
Any question please contact to us through [hugegraph@googlegroups.com](mailto:hugegraph@googlegroups.com) or [other contact information](https://hugegraph.github.io/hugegraph-doc/).
|
||||
For Any question, please contact us through [dev@hugegraph.apache.org](mailto:dev@hugegraph.apache.org) ([subscriber](https://hugegraph.apache.org/docs/contribution-guidelines/subscribe/) only)
|
||||
|
||||
|
||||
|
|
|
|||
68
Dockerfile
68
Dockerfile
|
|
@ -1,68 +0,0 @@
|
|||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
FROM ubuntu:xenial
|
||||
|
||||
LABEL maintainer="HugeGraph Docker Maintainers <hugegraph@googlegroups.com>"
|
||||
|
||||
ENV PKG_URL https://github.com/hugegraph
|
||||
|
||||
# 1. Install needed dependencies of GraphServer & RocksDB
|
||||
RUN set -x \
|
||||
&& apt-get -q update \
|
||||
&& apt-get -q install -y --no-install-recommends --no-install-suggests \
|
||||
curl \
|
||||
lsof \
|
||||
g++ \
|
||||
gcc \
|
||||
openjdk-8-jdk \
|
||||
&& apt-get clean
|
||||
# && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 2. Init HugeGraph Sever
|
||||
# (Optional) You can set the ip of github to speed up the local build
|
||||
# && echo "192.30.253.112 github.com\n151.101.44.249 github.global.ssl.fastly.net" >> /etc/hosts \
|
||||
ENV SERVER_VERSION 0.12.0
|
||||
RUN set -e \
|
||||
&& mkdir -p /root/hugegraph-server \
|
||||
&& curl -L -S ${PKG_URL}/hugegraph/releases/download/v${SERVER_VERSION}/hugegraph-${SERVER_VERSION}.tar.gz -o /root/server.tar.gz \
|
||||
&& tar xzf /root/server.tar.gz --strip-components 1 -C /root/hugegraph-server \
|
||||
&& rm /root/server.tar.gz \
|
||||
&& cd /root/hugegraph-server/ \
|
||||
&& sed -i "s/^restserver.url.*$/restserver.url=http:\/\/0.0.0.0:8080/g" ./conf/rest-server.properties \
|
||||
&& sed -n '65p' ./bin/start-hugegraph.sh | grep "&" > /dev/null && sed -i 65{s/\&$/#/g} ./bin/start-hugegraph.sh \
|
||||
&& sed -n '75p' ./bin/start-hugegraph.sh | grep "exit" > /dev/null && sed -i 75{s/^/#/g} ./bin/start-hugegraph.sh \
|
||||
&& ./bin/init-store.sh
|
||||
|
||||
# 3. Prepare for HugeGraph Studio
|
||||
ENV STUDIO_VERSION 0.10.0
|
||||
# (Optional) You can set the ip of github to speed up the local build
|
||||
# && echo "192.30.253.112 github.com\n151.101.44.249 github.global.ssl.fastly.net" >> /etc/hosts \
|
||||
RUN set -e \
|
||||
&& mkdir -p /root/hugegraph-studio \
|
||||
&& curl -L -S ${PKG_URL}/hugegraph-studio/releases/download/v${STUDIO_VERSION}/hugegraph-studio-${STUDIO_VERSION}.tar.gz -o /root/studio.tar.gz \
|
||||
&& tar xzf /root/studio.tar.gz --strip-components 1 -C /root/hugegraph-studio \
|
||||
&& rm /root/studio.tar.gz \
|
||||
&& cd /root/hugegraph-studio/ \
|
||||
&& sed -i "s/^studio.server.host.*$/studio.server.host=0.0.0.0/g" ./conf/hugegraph-studio.properties \
|
||||
&& sed -i "s/^graph.server.host.*$/graph.server.host=0.0.0.0/g" ./conf/hugegraph-studio.properties
|
||||
|
||||
EXPOSE 8080 8088
|
||||
WORKDIR /root
|
||||
VOLUME /root
|
||||
|
||||
ENTRYPOINT ["./hugegraph-server/bin/start-hugegraph.sh"]
|
||||
20
LICENSE
20
LICENSE
|
|
@ -1,4 +1,4 @@
|
|||
Apache License
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
|
|
@ -200,3 +200,21 @@ Apache License
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
========================================================================
|
||||
Apache 2.0 licenses
|
||||
========================================================================
|
||||
|
||||
The following components are provided under the Apache License. See project link for details.
|
||||
The text of each license is the standard Apache 2.0 license.
|
||||
|
||||
hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/tinkerpop/StructureBasicSuite.java from https://github.com/apache/tinkerpop
|
||||
hugegraph-server/hugegraph-test/src/main/java/org/apache/hugegraph/tinkerpop/ProcessBasicSuite.java from https://github.com/apache/tinkerpop
|
||||
hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/id/SnowflakeIdGenerator.java from https://github.com/twitter-archive/snowflake
|
||||
hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/HugeScriptTraversal.java from https://github.com/apache/tinkerpop
|
||||
hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/type/Nameable.java from https://github.com/JanusGraph/janusgraph
|
||||
hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/type/define/Cardinality.java from https://github.com/JanusGraph/janusgraph
|
||||
hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/util/StringEncoding.java from https://github.com/JanusGraph/janusgraph
|
||||
hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherOpProcessor.java from https://github.com/opencypher/cypher-for-gremlin
|
||||
hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/opencypher/CypherPlugin.java from https://github.com/opencypher/cypher-for-gremlin
|
||||
hugegraph-struct/src/main/java/org/apache/hugegraph/type/define/Cardinality.java from https://github.com/JanusGraph/janusgraph
|
||||
hugegraph-struct/src/main/java/org/apache/hugegraph/type/Namifiable.java from https://github.com/JanusGraph/janusgraph
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
Apache HugeGraph
|
||||
Copyright 2022-2026 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
|
||||
The initial codebase was donated to the ASF by HugeGraph Authors, copyright 2017-2021.
|
||||
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
This product contains code form the Apache TinkerPop Project:
|
||||
-----------------------------------------------------------------------
|
||||
Apache TinkerPop
|
||||
Copyright 2015-2022 The Apache Software Foundation.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
Activiti
|
||||
------------------------------------------------------------------------
|
||||
Activiti BPM Platform
|
||||
Copyright 2010-2014 Alfresco Software, Ltd.
|
||||
|
||||
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriterHelper.java
|
||||
contains DelegatingXMLStreamWriter.java and IndentingXMLStreamWriter.java from
|
||||
https://github.com/Activiti/Activiti/tree/activiti-5.16/modules/activiti-bpmn-converter/src/main/java/org/activiti/bpmn/converter
|
||||
|
||||
------------------------------------------------------------------------
|
||||
Apache Kerby
|
||||
------------------------------------------------------------------------
|
||||
Apache Kerby
|
||||
Copyright 2015-2017 The Apache Software Foundation
|
||||
|
||||
gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/auth/JaasKrbUtil.java
|
||||
from
|
||||
https://github.com/apache/directory-kerby/blob/kerby-all-1.0.0/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/client/JaasKrbUtil.java
|
||||
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
This product contains code form the JanusGraph Project:
|
||||
-----------------------------------------------------------------------
|
||||
==============================================================
|
||||
JanusGraph: Distributed Graph Database
|
||||
Copyright 2012 with JanusGraph Authors
|
||||
==============================================================
|
||||
|
||||
This product includes software developed by JanusGraph contributors listed
|
||||
in CONTRIBUTORS.txt; JanusGraph copyright holders are listed in AUTHORS.txt.
|
||||
|
||||
This product is based on Titan, originally developed by Aurelius (acquired by
|
||||
DataStax) and the following individuals:
|
||||
|
||||
* Matthias Broecheler
|
||||
* Dan LaRocque
|
||||
* Marko A. Rodriguez
|
||||
* Stephen Mallette
|
||||
* Pavel Yaskevich
|
||||
398
README.md
398
README.md
|
|
@ -1,48 +1,390 @@
|
|||
<div align="center">
|
||||
<img width="720" alt="hugegraph-logo" src="https://user-images.githubusercontent.com/17706099/149281100-c296db08-2861-4174-a31f-e2a92ebeeb72.png" style="zoom:100%;" />
|
||||
</div>
|
||||
<h1 align="center">
|
||||
<img width="720" alt="hugegraph-logo" src="https://github.com/apache/hugegraph/assets/38098239/e02ffaed-4562-486b-ba8f-e68d02bb0ea6" style="zoom:100%;" />
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
A graph database that supports more than 10+ billion data, high performance and scalability
|
||||
</p>
|
||||
<hr/>
|
||||
<h3 align="center">A graph database that supports more than 10 billion vertices & edges, high performance and scalability</h3>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
[](https://github.com/hugegraph/hugegraph/actions/workflows/ci.yml)
|
||||
[](https://codecov.io/gh/hugegraph/hugegraph)
|
||||
[](https://github.com/hugegraph/hugegraph/releases)
|
||||
[](https://github.com/apache/hugegraph/actions/workflows/server-ci.yml)
|
||||
[](https://github.com/apache/hugegraph/actions/workflows/pd-store-ci.yml)
|
||||
[](https://github.com/apache/hugegraph/releases)
|
||||
[](https://deepwiki.com/apache/hugegraph)
|
||||
|
||||
[HugeGraph](https://hugegraph.apache.org/) is a fast-speed and highly-scalable [graph database](https://en.wikipedia.org/wiki/Graph_database). Billions of vertices and edges can be easily stored into and queried from HugeGraph due to its excellent OLTP ability. As compliance to [Apache TinkerPop 3](https://tinkerpop.apache.org/) framework, various complicated graph queries can be accomplished through [Gremlin](https://tinkerpop.apache.org/gremlin.html)(a powerful graph traversal language).
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
**Quick Navigation:** [Architecture](#architecture) • [Quick Start](#quick-start) • [Module Map](#module-map) • [Ecosystem](#ecosystem) • [For Contributors](#for-contributors) • [Community](#community)
|
||||
|
||||
---
|
||||
|
||||
## What is Apache HugeGraph?
|
||||
|
||||
[HugeGraph](https://hugegraph.apache.org/) is a fast and highly-scalable [graph database](https://en.wikipedia.org/wiki/Graph_database).
|
||||
Billions of vertices and edges can be easily stored into and queried from HugeGraph due to its excellent OLTP capabilities.
|
||||
HugeGraph is compliant with the [Apache TinkerPop 3](https://tinkerpop.apache.org/) framework allowing complicated graph queries to be
|
||||
achieved through the powerful [Gremlin](https://tinkerpop.apache.org/gremlin.html) graph traversal language.
|
||||
|
||||
## Features
|
||||
|
||||
- Compliance to [Apache TinkerPop 3](https://tinkerpop.apache.org/), supporting [Gremlin](https://tinkerpop.apache.org/gremlin.html)
|
||||
- Schema Metadata Management, including VertexLabel, EdgeLabel, PropertyKey and IndexLabel
|
||||
- Multi-type Indexes, supporting exact query, range query and complex conditions combination query
|
||||
- Plug-in Backend Store Driver Framework, supporting RocksDB, Cassandra, ScyllaDB, HBase and MySQL now and easy to add other backend store driver if needed
|
||||
- Integration with Hadoop/Spark
|
||||
- **Schema Metadata Management**: VertexLabel, EdgeLabel, PropertyKey, and IndexLabel
|
||||
- **Multi-type Indexes**: Exact query, range query, and complex conditions combination query
|
||||
- **Plug-in Backend Store Framework**: RocksDB powers standalone deployments and HStore powers distributed clusters. See the [backend evolution guide](hugegraph-server/README.md#backend-evolution-and-compatibility) for compatibility details.
|
||||
- **Big Data Integration**: Seamless integration with `Flink`/`Spark`/`HDFS`
|
||||
- **Complete Graph Ecosystem**: In/out-memory Graph Computing + Graph Visualization & Tools + Graph Learning & AI
|
||||
- **Dual Query Language Support**: [Gremlin](https://tinkerpop.apache.org/gremlin.html) (via [Apache TinkerPop 3](https://tinkerpop.apache.org/)) and [Cypher](https://en.wikipedia.org/wiki/Cypher_(query_language)) (OpenCypher)
|
||||
|
||||
## Getting Started
|
||||
## Ecosystem
|
||||
|
||||
The project [homepage](https://hugegraph.apache.org/docs/) contains more information on HugeGraph and provides links to **documentation**, getting-started guides and release downloads.
|
||||
Complete **HugeGraph** ecosystem components:
|
||||
|
||||
And here are links of other repositories:
|
||||
1. [hugegraph-toolchain](https://github.com/apache/incubator-hugegraph-toolchain) (include loader/dashboard/tool/client)
|
||||
2. [hugegraph-computer](https://github.com/apache/incubator-hugegraph-computer) (graph computing system)
|
||||
3. [hugegraph-commons](https://github.com/apache/incubator-hugegraph-commons) (include common & rpc module)
|
||||
4. [hugegraph-website](https://github.com/apache/incubator-hugegraph-doc) (include doc & website code)
|
||||
1. **[hugegraph-toolchain](https://github.com/apache/hugegraph-toolchain)** - Graph tools suite
|
||||
- [Loader](https://github.com/apache/hugegraph-toolchain/tree/master/hugegraph-loader) - Data import tool
|
||||
- [Dashboard](https://github.com/apache/hugegraph-toolchain/tree/master/hugegraph-hubble) - Web visualization platform
|
||||
- [Tool](https://github.com/apache/hugegraph-toolchain/tree/master/hugegraph-tools) - Command-line utilities
|
||||
- [Client](https://github.com/apache/hugegraph-toolchain/tree/master/hugegraph-client) - Java/Python client SDK
|
||||
|
||||
2. **[hugegraph-computer](https://github.com/apache/hugegraph-computer)** - Integrated **graph computing** system
|
||||
|
||||
3. **[hugegraph-ai](https://github.com/apache/hugegraph-ai)** - **Graph AI/LLM/Knowledge Graph** integration
|
||||
|
||||
4. **[hugegraph-website](https://github.com/apache/hugegraph-doc)** - **Documentation & website** repository
|
||||
|
||||
## Architecture
|
||||
|
||||
HugeGraph supports both **standalone** and **distributed** deployments:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Client Layer │
|
||||
│ Gremlin Console │ REST API │ Cypher │ SDK/Tools │
|
||||
└─────────────────────────┬───────────────────────────┘
|
||||
│
|
||||
┌─────────────────────────▼───────────────────────────┐
|
||||
│ HugeGraph Server (:8080) │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
|
||||
│ │ REST API │ │ Gremlin │ │ Cypher Engine │ │
|
||||
│ │(Jersey 3)│ │ (TP 3.5) │ │ (OpenCypher) │ │
|
||||
│ └────┬─────┘ └────┬─────┘ └────────┬─────────┘ │
|
||||
│ └─────────────┼─────────────────┘ │
|
||||
│ ┌────────▼────────┐ │
|
||||
│ │ Graph Engine │ │
|
||||
│ │(hugegraph-core) │ │
|
||||
│ └────────┬────────┘ │
|
||||
└─────────────────────┼───────────────────────────────┘
|
||||
│
|
||||
┌─────────────────┴─────────────────┐
|
||||
│ │
|
||||
┌──────────────▼──────────────┐ ┌──────────────▼──────────────┐
|
||||
│ Standalone Mode │ │ Distributed Mode │
|
||||
│ ┌───────────────────────┐ │ │ ┌───────────────────────┐ │
|
||||
│ │ RocksDB │ │ │ │ HugeGraph-PD │ │
|
||||
│ │ (embedded) │ │ │ │ (Raft, 3-5 nodes) │ │
|
||||
│ └───────────────────────┘ │ │ │ :8620/:8686 │ │
|
||||
│ │ │ └───────────┬───────────┘ │
|
||||
│ Use Case: │ │ │ │
|
||||
│ Development/Testing │ │ ┌───────────▼───────────┐ │
|
||||
│ Single Node │ │ │ HStore │ │
|
||||
│ │ │ │ (Raft, 3+ nodes) │ │
|
||||
│ Data Scale: < 1TB │ │ │ :8520 │ │
|
||||
└─────────────────────────────┘ │ └───────────────────────┘ │
|
||||
│ │
|
||||
│ Use Case: │
|
||||
│ Production/HA/Cluster │
|
||||
│ │
|
||||
│ Data Scale: < 1000 TB │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
See the [backend evolution guide](hugegraph-server/README.md#backend-evolution-and-compatibility) for lifecycle and historical compatibility guidance.
|
||||
|
||||
### Deployment Mode Comparison
|
||||
|
||||
| Mode | Components | Use Case | Data Scale | High Availability |
|
||||
|------|------------|----------|------------|-------------------|
|
||||
| **Standalone** | Server + RocksDB | Development, Testing, Single Node | < 1TB | Basic |
|
||||
| **Distributed** | Server + PD + HStore | Production, HA, Horizontal Scaling | < 1000 TB | Yes |
|
||||
|
||||
### Module Overview
|
||||
|
||||
| Module | Description |
|
||||
|--------|-------------|
|
||||
| [hugegraph-server](hugegraph-server) | Core graph engine with REST API, Gremlin/Cypher support, and pluggable backends (RocksDB default) |
|
||||
| [hugegraph-pd](hugegraph-pd/README.md) | Placement Driver for distributed mode - handles meta storage, partition management and cluster scheduling |
|
||||
| [hugegraph-store](hugegraph-store/README.md) | Distributed storage with Raft consensus for high availability and horizontal scaling |
|
||||
| [hugegraph-commons](hugegraph-commons) | Shared utilities, RPC framework and common components |
|
||||
|
||||
<details>
|
||||
<summary><b>📊 Click to view detailed architecture diagram (Mermaid)</b></summary>
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
CLIENTS["Client Layer<br/>Gremlin Console · REST Client · Cypher Client · SDK/Tools"]
|
||||
|
||||
subgraph Server["HugeGraph Server :8080"]
|
||||
API[REST API<br/>Jersey 3]
|
||||
GS[Gremlin Server<br/>TinkerPop 3.5]
|
||||
CS[Cypher Engine<br/>OpenCypher]
|
||||
CORE[Graph Engine<br/>hugegraph-core]
|
||||
|
||||
API --> CORE
|
||||
GS --> CORE
|
||||
CS --> CORE
|
||||
end
|
||||
|
||||
subgraph Storage["Storage Layer"]
|
||||
subgraph Standalone["Standalone Mode"]
|
||||
ROCKS[(RocksDB<br/>Embedded)]
|
||||
end
|
||||
|
||||
subgraph Distributed["Distributed Mode"]
|
||||
PD_HSTORE[PD + HStore<br/>Raft Cluster]
|
||||
end
|
||||
end
|
||||
|
||||
CLIENTS --> Server
|
||||
CORE --> ROCKS
|
||||
CORE --> PD_HSTORE
|
||||
|
||||
style Server fill:#e1f5ff
|
||||
style Distributed fill:#fff4e1
|
||||
style Standalone fill:#f0f0f0
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 5 Minutes Quick Start
|
||||
|
||||
```bash
|
||||
# Start HugeGraph (standalone mode)
|
||||
docker run -itd --name=hugegraph -p 8080:8080 hugegraph/hugegraph:1.7.0
|
||||
|
||||
# Verify server is running
|
||||
curl http://localhost:8080/versions
|
||||
|
||||
# Try a Gremlin query
|
||||
curl -X POST http://localhost:8080/gremlin \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"gremlin":"g.V().limit(5)"}'
|
||||
```
|
||||
|
||||
> **Production Note**: For production environments or public network exposure, you **must** enable the [AuthSystem](https://hugegraph.apache.org/docs/config/config-authentication/) for security.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Java 11+** (required)
|
||||
- **Maven 3.5+** (for building from source)
|
||||
|
||||
### Option 1: Docker (Fastest)
|
||||
|
||||
Docker is the quickest way to get started for **testing or development**:
|
||||
|
||||
```bash
|
||||
# Basic usage
|
||||
docker run -itd --name=hugegraph -p 8080:8080 hugegraph/hugegraph:1.7.0
|
||||
|
||||
# With sample graph preloaded
|
||||
docker run -itd --name=hugegraph -e PRELOAD=true -p 8080:8080 hugegraph/hugegraph:1.7.0
|
||||
|
||||
# With authentication enabled
|
||||
docker run -itd --name=hugegraph -e PASSWORD=your_password -p 8080:8080 hugegraph/hugegraph:1.7.0
|
||||
```
|
||||
|
||||
For advanced Docker configurations, see:
|
||||
|
||||
* [Docker Documentation](https://hugegraph.apache.org/docs/quickstart/hugegraph-server/#3-deploy)
|
||||
* [Docker Compose Examples](./docker/)
|
||||
* [Docker README](./docker/README.md)
|
||||
* [Server Docker README](hugegraph-server/hugegraph-dist/docker/README.md)
|
||||
|
||||
> **Docker Desktop (Mac/Windows)**: The 3-node distributed cluster (`docker/docker-compose-3pd-3store-3server.yml`) uses Docker bridge networking and works on all platforms including Docker Desktop. Allocate at least 12 GB memory to Docker Desktop.
|
||||
|
||||
> **Note**: Docker images are convenience releases, not **official ASF distribution artifacts**. See [ASF Release Distribution Policy](https://infra.apache.org/release-distribution.html#dockerhub) for details.
|
||||
>
|
||||
> **Version Tags**: Use release tags (e.g., `1.7.0`) for stable deployments. The `latest` tag should only be used for testing or development.
|
||||
|
||||
|
||||
<details>
|
||||
<summary><b>Option 2: Download Binary Package</b></summary>
|
||||
|
||||
Download pre-built packages from the [Download Page](https://hugegraph.apache.org/docs/download/download/):
|
||||
|
||||
```bash
|
||||
# Download and extract
|
||||
# For historical 1.7.0 and earlier releases, use the archive URL and
|
||||
# set PACKAGE=apache-hugegraph-incubating-{version} instead.
|
||||
BASE_URL="https://downloads.apache.org/hugegraph/{version}"
|
||||
PACKAGE="apache-hugegraph-{version}"
|
||||
# Historical alternative:
|
||||
# BASE_URL="https://archive.apache.org/dist/incubator/hugegraph/{version}"
|
||||
# PACKAGE="apache-hugegraph-incubating-{version}"
|
||||
|
||||
wget ${BASE_URL}/${PACKAGE}.tar.gz
|
||||
tar -xzf ${PACKAGE}.tar.gz
|
||||
cd ${PACKAGE}
|
||||
|
||||
# Initialize backend storage
|
||||
bin/init-store.sh
|
||||
|
||||
# Start server
|
||||
bin/start-hugegraph.sh
|
||||
|
||||
# Check server status
|
||||
bin/monitor-hugegraph.sh
|
||||
```
|
||||
|
||||
For detailed instructions, see the [Binary Installation Guide](https://hugegraph.apache.org/docs/quickstart/hugegraph-server/#32-download-the-binary-tar-tarball).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Option 3: Build from Source</b></summary>
|
||||
|
||||
Build from source for development or customization:
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone https://github.com/apache/hugegraph.git
|
||||
cd hugegraph
|
||||
|
||||
# Build all modules (skip tests for faster build)
|
||||
mvn clean package -DskipTests
|
||||
|
||||
# Extract built package
|
||||
tar -xzf target/apache-hugegraph-{version}.tar.gz
|
||||
cd apache-hugegraph-{version}/apache-hugegraph-server-{version}
|
||||
|
||||
# Initialize and start
|
||||
bin/init-store.sh
|
||||
bin/start-hugegraph.sh
|
||||
```
|
||||
|
||||
For detailed build instructions, see [BUILDING.md](BUILDING.md) and [Build from Source Guide](https://hugegraph.apache.org/docs/quickstart/hugegraph-server/#33-source-code-compilation).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Verify Installation</b></summary>
|
||||
|
||||
Once the server is running, verify the installation:
|
||||
|
||||
```bash
|
||||
# Check server version
|
||||
curl http://localhost:8080/versions
|
||||
|
||||
# Expected output:
|
||||
# {
|
||||
# "versions": {
|
||||
# "version": "v1",
|
||||
# "core": "1.7.0",
|
||||
# "gremlin": "3.5.1",
|
||||
# "api": "1.7.0"
|
||||
# }
|
||||
# }
|
||||
|
||||
# Try Gremlin console (if installed locally)
|
||||
bin/gremlin-console.sh
|
||||
|
||||
# In Gremlin console:
|
||||
gremlin> :remote connect tinkerpop.server conf/remote.yaml
|
||||
gremlin> :> g.V().limit(5)
|
||||
```
|
||||
|
||||
For comprehensive documentation, visit the [HugeGraph Documentation](https://hugegraph.apache.org/docs/).
|
||||
|
||||
</details>
|
||||
|
||||
## Module Map
|
||||
|
||||
**Developer Navigation**: Find the right module for your task
|
||||
|
||||
| I want to... | Module | Key Path |
|
||||
|--------------|--------|----------|
|
||||
| Understand graph operations | `hugegraph-core` | `StandardHugeGraph.java` |
|
||||
| Modify REST APIs | `hugegraph-api` | `src/.../api/` |
|
||||
| Add storage backend | `hugegraph-core` | `BackendStore.java` |
|
||||
| Develop Gremlin features | `hugegraph-core` | `src/.../traversal/` |
|
||||
| Develop Cypher features | `hugegraph-api` | `src/.../opencypher/` |
|
||||
| Work on distributed coordination | `hugegraph-pd` | `hg-pd-core/` |
|
||||
| Work on distributed storage | `hugegraph-store` | `hg-store-core/` |
|
||||
| Add backend implementations | `hugegraph-server/hugegraph-{backend}` | `hugegraph-rocksdb/`, `hugegraph-hstore/` |
|
||||
| Understand configuration | `hugegraph-dist` | `src/assembly/static/conf/` |
|
||||
| Run tests | `hugegraph-test` | Test suites with multiple profiles |
|
||||
|
||||
For detailed architecture and development guidance, see [AGENTS.md](AGENTS.md).
|
||||
|
||||
<details>
|
||||
<summary><b>For Contributors</b></summary>
|
||||
|
||||
**New to HugeGraph?** Follow this path to get started:
|
||||
|
||||
1. **Understand the Architecture**
|
||||
- Read [AGENTS.md](AGENTS.md) for detailed module structure and development patterns
|
||||
- Review the [Architecture Diagram](#architecture) above
|
||||
|
||||
2. **Set Up Your Environment**
|
||||
- Install Java 11+ and Maven 3.5+
|
||||
- Follow [BUILDING.md](BUILDING.md) for build instructions
|
||||
- Configure your IDE to use `.editorconfig` for code style and `style/checkstyle.xml` for Checkstyle rules
|
||||
|
||||
3. **Find Your First Issue**
|
||||
- Browse [Good First Issues](https://github.com/apache/hugegraph/issues?q=label%3A%22good+first+issue%22)
|
||||
- Check [Help Wanted Issues](https://github.com/apache/hugegraph/issues?q=label%3A%22help+wanted%22)
|
||||
|
||||
4. **Learn the Codebase**
|
||||
- Use the [Module Map](#module-map) to navigate
|
||||
- Try [DeepWiki](https://deepwiki.com/apache/hugegraph) for AI-powered codebase understanding
|
||||
- Run tests to understand behavior: `mvn test -pl hugegraph-server/hugegraph-test -am -P core-test,memory`
|
||||
- Try modifying a test and see what breaks
|
||||
|
||||
5. **Code Standards**
|
||||
- Line length: 100 characters
|
||||
- Indentation: 4 spaces
|
||||
- No star imports
|
||||
- Commit format: `feat|fix|refactor(module): description`
|
||||
|
||||
6. **Submit Your Contribution**
|
||||
- Read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines
|
||||
- Follow the [Contribution Guidelines](https://hugegraph.apache.org/docs/contribution-guidelines/)
|
||||
- Use [GitHub Desktop](https://desktop.github.com/) to simplify the PR process
|
||||
|
||||
</details>
|
||||
|
||||
## Contributing
|
||||
|
||||
Welcome to contribute to HugeGraph, please see [`How to Contribute`](CONTRIBUTING.md) for more information.
|
||||
Welcome to contribute to HugeGraph!
|
||||
|
||||
- **How to Contribute**: See [CONTRIBUTING.md](CONTRIBUTING.md) and [Contribution Guidelines](https://hugegraph.apache.org/docs/contribution-guidelines/)
|
||||
- **Code Style**: Configure your IDE to use `.editorconfig` for code style and `style/checkstyle.xml` for Checkstyle rules
|
||||
- **PR Tool**: [GitHub Desktop](https://desktop.github.com/) is recommended for simpler workflow
|
||||
|
||||
Thank you to all the contributors who have helped make HugeGraph better!
|
||||
|
||||
[](https://github.com/apache/hugegraph/graphs/contributors)
|
||||
|
||||
## License
|
||||
|
||||
HugeGraph is licensed under Apache 2.0 License.
|
||||
HugeGraph is licensed under [Apache 2.0 License](LICENSE).
|
||||
|
||||
## Community
|
||||
|
||||
**Get Help & Stay Connected**
|
||||
|
||||
- **[GitHub Issues](https://github.com/apache/hugegraph/issues)**: Report bugs and request features (quick response)
|
||||
- **Mailing List**: [dev@hugegraph.apache.org](mailto:dev@hugegraph.apache.org) ([subscribe here](https://hugegraph.apache.org/docs/contribution-guidelines/subscribe/))
|
||||
- **Slack**: [ASF HugeGraph Channel](https://the-asf.slack.com/archives/C059UU2FJ23)
|
||||
- **WeChat**: Scan the QR code to follow Apache HugeGraph official account
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/apache/hugegraph-doc/blob/master/assets/images/wechat.png?raw=true" alt="WeChat QR Code" width="300"/>
|
||||
</p>
|
||||
|
||||
## Thanks
|
||||
|
||||
HugeGraph relies on the [TinkerPop](http://tinkerpop.apache.org) framework, we refer to the storage structure of Titan and the schema definition of DataStax.
|
||||
Thanks to TinkerPop, thanks to Titan, thanks to DataStax. Thanks to all other organizations or authors who contributed to the project.
|
||||
HugeGraph relies on the [Apache TinkerPop](http://tinkerpop.apache.org) framework. We are grateful to the TinkerPop community, Titan, and DataStax for their foundational work. Thanks to all contributors and organizations who have helped make HugeGraph possible.
|
||||
|
||||
You are welcome to contribute to HugeGraph, and we are looking forward to working with you to build an excellent open source community.
|
||||
You are welcome to contribute to HugeGraph, and we look forward to working with you to build an excellent open-source community.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,284 @@
|
|||
# HugeGraph Docker Deployment
|
||||
|
||||
This directory contains Docker Compose files for running HugeGraph:
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `docker-compose.yml` | Single-node cluster using pre-built images from Docker Hub |
|
||||
| `docker-compose.dev.yml` | Single-node cluster built from source (for developers) |
|
||||
| `docker-compose-3pd-3store-3server.yml` | 3-node distributed cluster (PD + Store + Server) |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Docker Engine** 20.10+ (or Docker Desktop 4.x+)
|
||||
- **Docker Compose** v2 (included in Docker Desktop)
|
||||
- **Memory**: Allocate at least **12 GB** to Docker Desktop (Settings → Resources → Memory). The 3-node cluster runs 9 JVM processes (3 PD + 3 Store + 3 Server) which are memory-intensive. Insufficient memory causes OOM kills that appear as silent Raft failures.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The 12 GB minimum is for Docker Desktop. On Linux with native Docker, ensure the host has at least 12 GB of free memory.
|
||||
---
|
||||
|
||||
## Single-Node Setup
|
||||
|
||||
Two compose files are available for running a single-node cluster (1 PD + 1 Store + 1 Server):
|
||||
|
||||
### Option A: Quick Start (pre-built images)
|
||||
|
||||
Uses pre-built images from Docker Hub. Best for **end users** who want to run HugeGraph quickly.
|
||||
|
||||
```bash
|
||||
cd docker
|
||||
HUGEGRAPH_VERSION=1.7.0 docker compose up -d
|
||||
```
|
||||
|
||||
- Images: `hugegraph/pd:1.7.0`, `hugegraph/store:1.7.0`, `hugegraph/server:1.7.0`
|
||||
- `pull_policy: always` — always pulls the specified image tag
|
||||
|
||||
> **Note**: Use release tags (e.g., `1.7.0`) for stable deployments. The `latest` tag is intended for testing or development only.
|
||||
- PD healthcheck endpoint: `/v1/health`
|
||||
- Single PD, single Store (`HG_PD_INITIAL_STORE_LIST: store:8500`), single Server
|
||||
- Server healthcheck endpoint: `/versions`
|
||||
|
||||
### Option B: Development Build (build from source)
|
||||
|
||||
Builds images locally from source Dockerfiles. Best for **developers** who want to test local changes.
|
||||
|
||||
```bash
|
||||
cd docker
|
||||
docker compose -f docker-compose.dev.yml up -d
|
||||
```
|
||||
|
||||
- Images: built from source via `build: context: ..` with Dockerfiles
|
||||
- No `pull_policy` — builds locally, doesn't pull
|
||||
- Entrypoint scripts are baked into the built image (no volume mounts)
|
||||
- PD healthcheck endpoint: `/v1/health`
|
||||
- Otherwise identical env vars and structure to the quickstart file
|
||||
|
||||
### Key Differences
|
||||
|
||||
| | `docker-compose.yml` (quickstart) | `docker-compose.dev.yml` (dev build) |
|
||||
|---|---|---|
|
||||
| **Images** | Pull from Docker Hub | Build from source |
|
||||
| **Who it's for** | End users | Developers |
|
||||
| **pull_policy** | `always` | not set (build) |
|
||||
|
||||
**Verify** (both options):
|
||||
```bash
|
||||
curl http://localhost:8080/versions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3-Node Cluster Quickstart
|
||||
|
||||
```bash
|
||||
cd docker
|
||||
HUGEGRAPH_VERSION=1.7.0 docker compose -f docker-compose-3pd-3store-3server.yml up -d
|
||||
|
||||
# To stop and remove all data volumes (clean restart)
|
||||
docker compose -f docker-compose-3pd-3store-3server.yml down -v
|
||||
```
|
||||
|
||||
**Startup ordering** is enforced via `depends_on` with `condition: service_healthy`:
|
||||
|
||||
1. **PD nodes** start first and must pass healthchecks (`/v1/health`)
|
||||
2. **Store nodes** start after all PD nodes are healthy
|
||||
3. **Server nodes** start after all Store nodes are healthy
|
||||
|
||||
This ensures PD and Store are healthy before the server starts. The server entrypoint still performs a best-effort partition wait after launch, so partition assignment may take a little longer.
|
||||
|
||||
**Verify the cluster is healthy**:
|
||||
|
||||
```bash
|
||||
# Check PD health
|
||||
curl http://localhost:8620/v1/health
|
||||
|
||||
# Check Store health
|
||||
curl http://localhost:8520/v1/health
|
||||
|
||||
# Check Server (Graph API)
|
||||
curl http://localhost:8080/versions
|
||||
|
||||
# List registered stores via PD
|
||||
curl http://localhost:8620/v1/stores
|
||||
|
||||
# List partitions
|
||||
curl http://localhost:8620/v1/partitions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Variable Reference
|
||||
|
||||
Configuration is injected via environment variables. The old `docker/configs/application-pd*.yml` and `docker/configs/application-store*.yml` files are no longer used.
|
||||
|
||||
### PD Environment Variables
|
||||
|
||||
| Variable | Required | Default | Maps To (`application.yml`) | Description |
|
||||
|----------|----------|---------|-----------------------------|-------------|
|
||||
| `HG_PD_GRPC_HOST` | Yes | — | `grpc.host` | This node's hostname/IP for gRPC |
|
||||
| `HG_PD_RAFT_ADDRESS` | Yes | — | `raft.address` | This node's Raft address (e.g. `pd0:8610`) |
|
||||
| `HG_PD_RAFT_PEERS_LIST` | Yes | — | `raft.peers-list` | All PD peers (e.g. `pd0:8610,pd1:8610,pd2:8610`) |
|
||||
| `HG_PD_INITIAL_STORE_LIST` | Yes | — | `pd.initial-store-list` | Expected stores (e.g. `store0:8500,store1:8500,store2:8500`) |
|
||||
| `HG_PD_GRPC_PORT` | No | `8686` | `grpc.port` | gRPC server port |
|
||||
| `HG_PD_REST_PORT` | No | `8620` | `server.port` | REST API port |
|
||||
| `HG_PD_DATA_PATH` | No | `/hugegraph-pd/pd_data` | `pd.data-path` | Metadata storage path |
|
||||
| `HG_PD_INITIAL_STORE_COUNT` | No | `1` | `pd.initial-store-count` | Min stores for cluster availability |
|
||||
|
||||
**Deprecated aliases** (still work but log a warning):
|
||||
|
||||
| Deprecated | Use Instead |
|
||||
|------------|-------------|
|
||||
| `GRPC_HOST` | `HG_PD_GRPC_HOST` |
|
||||
| `RAFT_ADDRESS` | `HG_PD_RAFT_ADDRESS` |
|
||||
| `RAFT_PEERS` | `HG_PD_RAFT_PEERS_LIST` |
|
||||
| `PD_INITIAL_STORE_LIST` | `HG_PD_INITIAL_STORE_LIST` |
|
||||
|
||||
### Store Environment Variables
|
||||
|
||||
| Variable | Required | Default | Maps To (`application.yml`) | Description |
|
||||
|----------|----------|---------|-----------------------------|-------------|
|
||||
| `HG_STORE_PD_ADDRESS` | Yes | — | `pdserver.address` | PD gRPC addresses (e.g. `pd0:8686,pd1:8686,pd2:8686`) |
|
||||
| `HG_STORE_GRPC_HOST` | Yes | — | `grpc.host` | This node's hostname (e.g. `store0`) |
|
||||
| `HG_STORE_RAFT_ADDRESS` | Yes | — | `raft.address` | This node's Raft address (e.g. `store0:8510`) |
|
||||
| `HG_STORE_GRPC_PORT` | No | `8500` | `grpc.port` | gRPC server port |
|
||||
| `HG_STORE_REST_PORT` | No | `8520` | `server.port` | REST API port |
|
||||
| `HG_STORE_DATA_PATH` | No | `/hugegraph-store/storage` | `app.data-path` | Data storage path |
|
||||
|
||||
**Deprecated aliases** (still work but log a warning):
|
||||
|
||||
| Deprecated | Use Instead |
|
||||
|------------|-------------|
|
||||
| `PD_ADDRESS` | `HG_STORE_PD_ADDRESS` |
|
||||
| `GRPC_HOST` | `HG_STORE_GRPC_HOST` |
|
||||
| `RAFT_ADDRESS` | `HG_STORE_RAFT_ADDRESS` |
|
||||
|
||||
### Server Environment Variables
|
||||
|
||||
| Variable | Required | Default | Maps To | Description |
|
||||
|----------|----------|---------|-----------------------------|-------------|
|
||||
| `HG_SERVER_BACKEND` | Yes | — | `backend` in `hugegraph.properties` | Storage backend (e.g. `hstore`) |
|
||||
| `HG_SERVER_PD_PEERS` | Yes | — | `pd.peers` | PD cluster addresses (e.g. `pd0:8686,pd1:8686,pd2:8686`) |
|
||||
| `STORE_REST` | No | — | Used by `wait-partition.sh` | Store REST endpoint for partition verification (e.g. `store0:8520`) |
|
||||
| `PASSWORD` | No | — | Enables auth mode | Optional authentication password; ignored when `HG_SERVER_INIT_STORE_ENABLED` is `false` (see below) |
|
||||
| `HG_SERVER_INIT_STORE_ENABLED` | No | `true` | `init_store.enabled` in `rest-server.properties` | Set `false` in PD/HStore deployments so init-store skips local backend and admin initialization |
|
||||
|
||||
> **The built-in authenticator with `HG_SERVER_INIT_STORE_ENABLED=false`
|
||||
> requires `usePD=true` and an HStore-backed `auth.graph_store`, unless
|
||||
> `auth.remote_url` delegates auth elsewhere.** With init-store skipped, the
|
||||
> server creates the built-in admin in PD metadata, and only an HStore auth
|
||||
> graph uses the PD-backed auth manager that can read that account. init-store
|
||||
> exits non-zero when the combination is unusable, rather than leaving a server
|
||||
> nobody can log in to. A custom `auth.authenticator` is exempt because it
|
||||
> manages its own identities.
|
||||
>
|
||||
> `docker/init_complete` is written by init-store itself, and only after it has
|
||||
> initialized. A skipped run therefore records nothing, whether it was disabled
|
||||
> by the variable or by the property in a mounted `rest-server.properties`, so a
|
||||
> later re-enable is still able to initialize. The marker only short-circuits
|
||||
> re-initialization: init-store runs on every container start, and a disabled
|
||||
> one performs the fail-closed check above first, so a marker left by an
|
||||
> earlier release or an earlier enabled run cannot bypass it.
|
||||
>
|
||||
> **`PASSWORD` does not reach that path.** init-store reads it from standard
|
||||
> input, and a disabled one returns before doing so. The admin is instead
|
||||
> created from `auth.admin_pa`, whose `pa` default is public, so init-store
|
||||
> refuses to skip unless it is explicitly set to a non-empty value in a mounted
|
||||
> `rest-server.properties`. It applies only when the account is first created,
|
||||
> so changing it later does not rotate an existing password.
|
||||
|
||||
**Deprecated aliases** (still work but log a warning):
|
||||
|
||||
| Deprecated | Use Instead |
|
||||
|------------|-------------|
|
||||
| `BACKEND` | `HG_SERVER_BACKEND` |
|
||||
| `PD_PEERS` | `HG_SERVER_PD_PEERS` |
|
||||
|
||||
---
|
||||
|
||||
## Port Reference
|
||||
|
||||
The table below reflects the published host ports in `docker-compose-3pd-3store-3server.yml`.
|
||||
The single-node compose file (`docker-compose.yml`) only publishes the REST/API ports (`8620`, `8520`, `8080`) by default.
|
||||
|
||||
| Service | Container Port | Host Port | Protocol | Purpose |
|
||||
|---------|---------------|-----------|----------|---------|
|
||||
| pd0 | 8620 | 8620 | HTTP | REST API |
|
||||
| pd0 | 8686 | 8686 | gRPC | PD gRPC |
|
||||
| pd0 | 8610 | — | TCP | Raft (internal only) |
|
||||
| pd1 | 8620 | 8621 | HTTP | REST API |
|
||||
| pd1 | 8686 | 8687 | gRPC | PD gRPC |
|
||||
| pd2 | 8620 | 8622 | HTTP | REST API |
|
||||
| pd2 | 8686 | 8688 | gRPC | PD gRPC |
|
||||
| store0 | 8500 | 8500 | gRPC | Store gRPC |
|
||||
| store0 | 8510 | 8510 | TCP | Raft |
|
||||
| store0 | 8520 | 8520 | HTTP | REST API |
|
||||
| store1 | 8500 | 8501 | gRPC | Store gRPC |
|
||||
| store1 | 8510 | 8511 | TCP | Raft |
|
||||
| store1 | 8520 | 8521 | HTTP | REST API |
|
||||
| store2 | 8500 | 8502 | gRPC | Store gRPC |
|
||||
| store2 | 8510 | 8512 | TCP | Raft |
|
||||
| store2 | 8520 | 8522 | HTTP | REST API |
|
||||
| server0 | 8080 | 8080 | HTTP | Graph API |
|
||||
| server1 | 8080 | 8081 | HTTP | Graph API |
|
||||
| server2 | 8080 | 8082 | HTTP | Graph API |
|
||||
|
||||
---
|
||||
|
||||
## Healthcheck Endpoints
|
||||
|
||||
| Service | Endpoint | Expected |
|
||||
|---------|----------|----------|
|
||||
| PD | `GET /v1/health` | `200 OK` |
|
||||
| Store | `GET /v1/health` | `200 OK` |
|
||||
| Server | `GET /versions` | `200 OK` with version JSON |
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Containers Exiting or Restarting (OOM Kills)
|
||||
|
||||
**Symptom**: Containers exit with code 137, or restart loops. Raft logs show election timeouts.
|
||||
|
||||
**Cause**: Docker Desktop does not have enough memory. The 9 JVM processes require at least 12 GB.
|
||||
|
||||
**Fix**: Docker Desktop → Settings → Resources → Memory → set to **12 GB** or higher. Restart Docker Desktop.
|
||||
|
||||
```bash
|
||||
# Check if containers were OOM killed
|
||||
docker inspect hg-pd0 | grep -i oom
|
||||
docker stats --no-stream
|
||||
```
|
||||
|
||||
### Raft Leader Election Failure
|
||||
|
||||
**Symptom**: PD logs show repeated `Leader election timeout`. Store nodes cannot register.
|
||||
|
||||
**Cause**: PD nodes cannot reach each other on the Raft port (8610), or `HG_PD_RAFT_PEERS_LIST` is misconfigured.
|
||||
|
||||
**Fix**:
|
||||
1. Verify all PD containers are running: `docker compose -f docker-compose-3pd-3store-3server.yml ps`
|
||||
2. Check PD logs: `docker logs hg-pd0`
|
||||
3. Verify network connectivity: `docker exec hg-pd0 ping pd1`
|
||||
4. Ensure `HG_PD_RAFT_PEERS_LIST` is identical on all PD nodes
|
||||
|
||||
### Partition Assignment Not Completing
|
||||
|
||||
**Symptom**: Server starts but graph operations fail. Store logs show `partition not found`.
|
||||
|
||||
**Cause**: PD has not finished assigning partitions to stores, or stores did not register successfully.
|
||||
|
||||
**Fix**:
|
||||
1. Check registered stores: `curl http://localhost:8620/v1/stores`
|
||||
2. Check partition status: `curl http://localhost:8620/v1/partitions`
|
||||
3. Wait for partition assignment (can take 1–3 minutes after all stores register)
|
||||
4. Check server logs for the `wait-partition.sh` script output: `docker logs hg-server0`
|
||||
|
||||
### Connection Refused Errors
|
||||
|
||||
**Symptom**: Stores cannot connect to PD, or Server cannot connect to Store.
|
||||
|
||||
**Cause**: Services are using `127.0.0.1` instead of container hostnames, or the `hg-net` bridge network is misconfigured.
|
||||
|
||||
**Fix**: Ensure all `HG_*` env vars use container hostnames (`pd0`, `store0`, etc.), not `127.0.0.1` or `localhost`.
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
name: hugegraph-3x3
|
||||
|
||||
networks:
|
||||
hg-net:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
hg-pd0-data:
|
||||
hg-pd1-data:
|
||||
hg-pd2-data:
|
||||
hg-store0-data:
|
||||
hg-store1-data:
|
||||
hg-store2-data:
|
||||
|
||||
# ── Shared service defaults ──────────────────────────────────────────
|
||||
x-pd-common: &pd-common
|
||||
image: hugegraph/pd:${HUGEGRAPH_VERSION:-latest}
|
||||
pull_policy: missing
|
||||
restart: unless-stopped
|
||||
networks: [hg-net]
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -fsS http://localhost:8620/v1/health >/dev/null || exit 1"]
|
||||
interval: 15s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
start_period: 120s
|
||||
|
||||
x-store-common: &store-common
|
||||
image: hugegraph/store:${HUGEGRAPH_VERSION:-latest}
|
||||
pull_policy: missing
|
||||
restart: unless-stopped
|
||||
networks: [hg-net]
|
||||
depends_on:
|
||||
pd0: { condition: service_healthy }
|
||||
pd1: { condition: service_healthy }
|
||||
pd2: { condition: service_healthy }
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -fsS http://localhost:8520/v1/health >/dev/null || exit 1"]
|
||||
interval: 15s
|
||||
timeout: 15s
|
||||
retries: 40
|
||||
start_period: 120s
|
||||
|
||||
x-server-common: &server-common
|
||||
image: hugegraph/server:${HUGEGRAPH_VERSION:-latest}
|
||||
pull_policy: missing
|
||||
restart: unless-stopped
|
||||
networks: [hg-net]
|
||||
depends_on:
|
||||
store0: { condition: service_healthy }
|
||||
store1: { condition: service_healthy }
|
||||
store2: { condition: service_healthy }
|
||||
environment:
|
||||
STORE_REST: store0:8520
|
||||
HG_SERVER_BACKEND: hstore
|
||||
HG_SERVER_PD_PEERS: pd0:8686,pd1:8686,pd2:8686
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -fsS http://localhost:8080/versions >/dev/null || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 60s
|
||||
|
||||
# ── Services ──────────────────────────────────────────────────────────
|
||||
|
||||
services:
|
||||
# --- PD cluster (3 nodes) ---
|
||||
pd0:
|
||||
<<: *pd-common
|
||||
container_name: hg-pd0
|
||||
hostname: pd0
|
||||
networks: [ hg-net ]
|
||||
environment:
|
||||
HG_PD_GRPC_HOST: pd0
|
||||
HG_PD_GRPC_PORT: "8686"
|
||||
HG_PD_REST_PORT: "8620"
|
||||
HG_PD_RAFT_ADDRESS: pd0:8610
|
||||
HG_PD_RAFT_PEERS_LIST: pd0:8610,pd1:8610,pd2:8610
|
||||
HG_PD_INITIAL_STORE_LIST: store0:8500,store1:8500,store2:8500
|
||||
HG_PD_DATA_PATH: /hugegraph-pd/pd_data
|
||||
HG_PD_INITIAL_STORE_COUNT: 3
|
||||
ports: ["8620:8620", "8686:8686"]
|
||||
volumes:
|
||||
- hg-pd0-data:/hugegraph-pd/pd_data
|
||||
|
||||
pd1:
|
||||
<<: *pd-common
|
||||
container_name: hg-pd1
|
||||
hostname: pd1
|
||||
networks: [ hg-net ]
|
||||
environment:
|
||||
HG_PD_GRPC_HOST: pd1
|
||||
HG_PD_GRPC_PORT: "8686"
|
||||
HG_PD_REST_PORT: "8620"
|
||||
HG_PD_RAFT_ADDRESS: pd1:8610
|
||||
HG_PD_RAFT_PEERS_LIST: pd0:8610,pd1:8610,pd2:8610
|
||||
HG_PD_INITIAL_STORE_LIST: store0:8500,store1:8500,store2:8500
|
||||
HG_PD_DATA_PATH: /hugegraph-pd/pd_data
|
||||
HG_PD_INITIAL_STORE_COUNT: 3
|
||||
ports: ["8621:8620", "8687:8686"]
|
||||
volumes:
|
||||
- hg-pd1-data:/hugegraph-pd/pd_data
|
||||
|
||||
pd2:
|
||||
<<: *pd-common
|
||||
container_name: hg-pd2
|
||||
hostname: pd2
|
||||
networks: [ hg-net ]
|
||||
environment:
|
||||
HG_PD_GRPC_HOST: pd2
|
||||
HG_PD_GRPC_PORT: "8686"
|
||||
HG_PD_REST_PORT: "8620"
|
||||
HG_PD_RAFT_ADDRESS: pd2:8610
|
||||
HG_PD_RAFT_PEERS_LIST: pd0:8610,pd1:8610,pd2:8610
|
||||
HG_PD_INITIAL_STORE_LIST: store0:8500,store1:8500,store2:8500
|
||||
HG_PD_DATA_PATH: /hugegraph-pd/pd_data
|
||||
HG_PD_INITIAL_STORE_COUNT: 3
|
||||
ports: ["8622:8620", "8688:8686"]
|
||||
volumes:
|
||||
- hg-pd2-data:/hugegraph-pd/pd_data
|
||||
|
||||
# --- Store cluster (3 nodes) ---
|
||||
store0:
|
||||
<<: *store-common
|
||||
container_name: hg-store0
|
||||
hostname: store0
|
||||
environment:
|
||||
HG_STORE_PD_ADDRESS: pd0:8686,pd1:8686,pd2:8686
|
||||
HG_STORE_GRPC_HOST: store0
|
||||
HG_STORE_GRPC_PORT: "8500"
|
||||
HG_STORE_REST_PORT: "8520"
|
||||
HG_STORE_RAFT_ADDRESS: store0:8510
|
||||
HG_STORE_DATA_PATH: /hugegraph-store/storage
|
||||
ports: ["8500:8500", "8510:8510", "8520:8520"]
|
||||
volumes:
|
||||
- hg-store0-data:/hugegraph-store/storage
|
||||
|
||||
store1:
|
||||
<<: *store-common
|
||||
container_name: hg-store1
|
||||
hostname: store1
|
||||
environment:
|
||||
HG_STORE_PD_ADDRESS: pd0:8686,pd1:8686,pd2:8686
|
||||
HG_STORE_GRPC_HOST: store1
|
||||
HG_STORE_GRPC_PORT: "8500"
|
||||
HG_STORE_REST_PORT: "8520"
|
||||
HG_STORE_RAFT_ADDRESS: store1:8510
|
||||
HG_STORE_DATA_PATH: /hugegraph-store/storage
|
||||
ports: ["8501:8500", "8511:8510", "8521:8520"]
|
||||
volumes:
|
||||
- hg-store1-data:/hugegraph-store/storage
|
||||
|
||||
store2:
|
||||
<<: *store-common
|
||||
container_name: hg-store2
|
||||
hostname: store2
|
||||
environment:
|
||||
HG_STORE_PD_ADDRESS: pd0:8686,pd1:8686,pd2:8686
|
||||
HG_STORE_GRPC_HOST: store2
|
||||
HG_STORE_GRPC_PORT: "8500"
|
||||
HG_STORE_REST_PORT: "8520"
|
||||
HG_STORE_RAFT_ADDRESS: store2:8510
|
||||
HG_STORE_DATA_PATH: /hugegraph-store/storage
|
||||
ports: ["8502:8500", "8512:8510", "8522:8520"]
|
||||
volumes:
|
||||
- hg-store2-data:/hugegraph-store/storage
|
||||
|
||||
# --- Server cluster (3 nodes) ---
|
||||
server0:
|
||||
<<: *server-common
|
||||
container_name: hg-server0
|
||||
hostname: server0
|
||||
ports: ["8080:8080"]
|
||||
|
||||
server1:
|
||||
<<: *server-common
|
||||
container_name: hg-server1
|
||||
hostname: server1
|
||||
ports: ["8081:8080"]
|
||||
|
||||
server2:
|
||||
<<: *server-common
|
||||
container_name: hg-server2
|
||||
hostname: server2
|
||||
ports: ["8082:8080"]
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
name: hugegraph-single
|
||||
|
||||
networks:
|
||||
hg-net:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
hg-pd-data:
|
||||
hg-store-data:
|
||||
|
||||
services:
|
||||
pd:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: hugegraph-pd/Dockerfile
|
||||
container_name: hg-pd
|
||||
hostname: pd
|
||||
restart: unless-stopped
|
||||
networks: [hg-net]
|
||||
environment:
|
||||
HG_PD_GRPC_HOST: pd
|
||||
HG_PD_GRPC_PORT: "8686"
|
||||
HG_PD_REST_PORT: "8620"
|
||||
HG_PD_RAFT_ADDRESS: pd:8610
|
||||
HG_PD_RAFT_PEERS_LIST: pd:8610
|
||||
HG_PD_INITIAL_STORE_LIST: store:8500
|
||||
HG_PD_DATA_PATH: /hugegraph-pd/pd_data
|
||||
ports:
|
||||
- "8620:8620"
|
||||
volumes:
|
||||
- hg-pd-data:/hugegraph-pd/pd_data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -fsS http://localhost:8620/v1/health >/dev/null || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 20s
|
||||
|
||||
store:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: hugegraph-store/Dockerfile
|
||||
container_name: hg-store
|
||||
hostname: store
|
||||
restart: unless-stopped
|
||||
networks: [hg-net]
|
||||
depends_on:
|
||||
pd:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
HG_STORE_PD_ADDRESS: pd:8686
|
||||
HG_STORE_GRPC_HOST: store
|
||||
HG_STORE_GRPC_PORT: "8500"
|
||||
HG_STORE_REST_PORT: "8520"
|
||||
HG_STORE_RAFT_ADDRESS: store:8510
|
||||
HG_STORE_DATA_PATH: /hugegraph-store/storage
|
||||
ports:
|
||||
- "8520:8520"
|
||||
volumes:
|
||||
- hg-store-data:/hugegraph-store/storage
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -fsS http://localhost:8520/v1/health >/dev/null || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
start_period: 30s
|
||||
|
||||
server:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: hugegraph-server/Dockerfile-hstore
|
||||
container_name: hg-server
|
||||
hostname: server
|
||||
restart: unless-stopped
|
||||
networks: [hg-net]
|
||||
depends_on:
|
||||
store:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
HG_SERVER_BACKEND: hstore
|
||||
HG_SERVER_PD_PEERS: pd:8686
|
||||
ports:
|
||||
- "8080:8080"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -fsS http://localhost:8080/versions >/dev/null || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 60s
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
name: hugegraph-single
|
||||
|
||||
networks:
|
||||
hg-net:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
hg-pd-data:
|
||||
hg-store-data:
|
||||
|
||||
services:
|
||||
|
||||
pd:
|
||||
image: hugegraph/pd:${HUGEGRAPH_VERSION:-latest}
|
||||
pull_policy: always
|
||||
container_name: hg-pd
|
||||
hostname: pd
|
||||
restart: unless-stopped
|
||||
networks: [hg-net]
|
||||
environment:
|
||||
HG_PD_GRPC_HOST: pd
|
||||
HG_PD_GRPC_PORT: "8686"
|
||||
HG_PD_REST_PORT: "8620"
|
||||
HG_PD_RAFT_ADDRESS: pd:8610
|
||||
HG_PD_RAFT_PEERS_LIST: pd:8610
|
||||
HG_PD_INITIAL_STORE_LIST: store:8500
|
||||
HG_PD_DATA_PATH: /hugegraph-pd/pd_data
|
||||
ports:
|
||||
- "8620:8620"
|
||||
volumes:
|
||||
- hg-pd-data:/hugegraph-pd/pd_data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -fsS http://localhost:8620/v1/health >/dev/null || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 30s
|
||||
|
||||
store:
|
||||
image: hugegraph/store:${HUGEGRAPH_VERSION:-latest}
|
||||
pull_policy: always
|
||||
container_name: hg-store
|
||||
hostname: store
|
||||
restart: unless-stopped
|
||||
networks: [hg-net]
|
||||
depends_on:
|
||||
pd:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
HG_STORE_PD_ADDRESS: pd:8686
|
||||
HG_STORE_GRPC_HOST: store
|
||||
HG_STORE_GRPC_PORT: "8500"
|
||||
HG_STORE_REST_PORT: "8520"
|
||||
HG_STORE_RAFT_ADDRESS: store:8510
|
||||
HG_STORE_DATA_PATH: /hugegraph-store/storage
|
||||
ports:
|
||||
- "8520:8520"
|
||||
volumes:
|
||||
- hg-store-data:/hugegraph-store/storage
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -fsS http://localhost:8520/v1/health >/dev/null || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
start_period: 60s
|
||||
|
||||
server:
|
||||
image: hugegraph/server:${HUGEGRAPH_VERSION:-latest}
|
||||
pull_policy: always
|
||||
container_name: hg-server
|
||||
hostname: server
|
||||
restart: unless-stopped
|
||||
networks: [hg-net]
|
||||
depends_on:
|
||||
store:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
HG_SERVER_BACKEND: hstore
|
||||
HG_SERVER_PD_PEERS: pd:8686
|
||||
ports:
|
||||
- "8080:8080"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -fsS http://localhost:8080/versions >/dev/null || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 60s
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Standalone HBase 2.6.5 image for HugeGraph HBase backend testing.
|
||||
# Exposes ZooKeeper on 2181 with znode /hbase (matching hugegraph.properties defaults).
|
||||
|
||||
FROM eclipse-temurin:11-jre-jammy
|
||||
|
||||
ARG HBASE_VERSION=2.6.5
|
||||
ARG HBASE_PRIMARY_URL=https://archive.apache.org/dist/hbase/${HBASE_VERSION}/hbase-${HBASE_VERSION}-bin.tar.gz
|
||||
ARG HBASE_FALLBACK_URL=https://downloads.apache.org/hbase/${HBASE_VERSION}/hbase-${HBASE_VERSION}-bin.tar.gz
|
||||
ARG ALLOW_UNVERIFIED_DOWNLOAD=false
|
||||
|
||||
ENV HBASE_VERSION=${HBASE_VERSION}
|
||||
ENV HBASE_HOME=/opt/hbase
|
||||
ENV PATH=${HBASE_HOME}/bin:${PATH}
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
netcat-openbsd \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN set -eux; \
|
||||
download_ok=0; \
|
||||
downloaded_url=""; \
|
||||
for url in "${HBASE_PRIMARY_URL}" "${HBASE_FALLBACK_URL}"; do \
|
||||
[ -n "${url}" ] || continue; \
|
||||
echo "Downloading HBase from ${url}"; \
|
||||
if curl -fL --retry 8 --retry-delay 5 --retry-all-errors \
|
||||
--connect-timeout 30 --max-time 1800 "${url}" -o /tmp/hbase.tar.gz; then \
|
||||
download_ok=1; \
|
||||
downloaded_url="${url}"; \
|
||||
break; \
|
||||
fi; \
|
||||
echo "Download failed for ${url}, trying next source..."; \
|
||||
rm -f /tmp/hbase.tar.gz; \
|
||||
done; \
|
||||
if [ "${download_ok}" -ne 1 ]; then \
|
||||
echo "Unable to download HBase ${HBASE_VERSION} tarball from configured sources"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
checksum_ok=0; \
|
||||
for checksum_url in "${downloaded_url}.sha512" "${HBASE_PRIMARY_URL}.sha512" "${HBASE_FALLBACK_URL}.sha512"; do \
|
||||
[ -n "${checksum_url}" ] || continue; \
|
||||
if curl -fL --retry 5 --retry-delay 3 --retry-all-errors \
|
||||
--connect-timeout 30 "${checksum_url}" -o /tmp/hbase.tar.gz.sha512 2>/dev/null; then \
|
||||
checksum_ok=1; \
|
||||
break; \
|
||||
fi; \
|
||||
done; \
|
||||
if [ "${checksum_ok}" -eq 1 ]; then \
|
||||
echo "Verifying SHA512 checksum..."; \
|
||||
expected_hash=$(grep -Eio '[a-f0-9]{128}' /tmp/hbase.tar.gz.sha512 | head -n 1 | tr 'A-F' 'a-f' || true); \
|
||||
if [ -z "$expected_hash" ]; then \
|
||||
expected_hash=$(awk '{for (i = 1; i <= NF; i++) if (length($i) >= 8 && $i ~ /^[0-9A-Fa-f]+$/) hash = hash $i} END {if (length(hash) >= 128) print tolower(substr(hash, 1, 128))}' /tmp/hbase.tar.gz.sha512 || true); \
|
||||
fi; \
|
||||
if [ -n "$expected_hash" ]; then \
|
||||
actual_hash=$(sha512sum /tmp/hbase.tar.gz | awk '{print $1}'); \
|
||||
if [ "$expected_hash" = "$actual_hash" ]; then \
|
||||
echo "SHA512 verified OK"; \
|
||||
else \
|
||||
echo "ERROR: SHA512 mismatch"; \
|
||||
echo " Expected: $expected_hash"; \
|
||||
echo " Actual: $actual_hash"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
else \
|
||||
if [ "${ALLOW_UNVERIFIED_DOWNLOAD}" = "true" ]; then \
|
||||
echo "WARNING: Could not parse SHA512 file (ALLOW_UNVERIFIED_DOWNLOAD=true)"; \
|
||||
else \
|
||||
echo "ERROR: Could not parse SHA512 file"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
fi; \
|
||||
else \
|
||||
if [ "${ALLOW_UNVERIFIED_DOWNLOAD}" = "true" ]; then \
|
||||
echo "WARNING: Could not download SHA512 file (ALLOW_UNVERIFIED_DOWNLOAD=true)"; \
|
||||
else \
|
||||
echo "ERROR: Could not download SHA512 file"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
fi; \
|
||||
tar -xzf /tmp/hbase.tar.gz -C /opt; \
|
||||
mv /opt/hbase-${HBASE_VERSION} ${HBASE_HOME}; \
|
||||
rm -f /tmp/hbase.tar.gz /tmp/hbase.tar.gz.sha512
|
||||
|
||||
# hbase-site.xml: standalone mode, znode=/hbase (matches hugegraph.properties)
|
||||
COPY hbase-site.xml ${HBASE_HOME}/conf/hbase-site.xml
|
||||
|
||||
# Entrypoint: start HBase then tail the master log
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
EXPOSE 2181 16000 16010 16020 16030
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
# HugeGraph + HBase Backend
|
||||
|
||||
This guide covers running HugeGraph with HBase backend.
|
||||
|
||||
> **Deprecation notice:** The HBase backend is deprecated and is planned for removal in HugeGraph 2.0. Existing deployments should plan a migration to a maintained backend.
|
||||
|
||||
> All commands below run from the repository root (this project folder).
|
||||
|
||||
Use this once at the start of your terminal session:
|
||||
|
||||
```bash
|
||||
ROOT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
||||
cd "$ROOT_DIR"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Start Paths (Choose One)
|
||||
|
||||
<details>
|
||||
<summary><b>Option 1: Standalone HugeGraph (using start-hugegraph.sh)</b></summary>
|
||||
|
||||
Prerequisite: build local artifact first.
|
||||
mvn clean package -DskipTests
|
||||
```
|
||||
cd "$ROOT_DIR"
|
||||
```
|
||||
|
||||
```bash
|
||||
# 1) Start HBase
|
||||
docker compose -p hg-hbase -f docker/hbase/docker-compose.hbase.yml down -v
|
||||
docker compose -p hg-hbase -f docker/hbase/docker-compose.hbase.yml build --no-cache hbase
|
||||
HBASE_MASTER_HOSTNAME=localhost HBASE_REGIONSERVER_HOSTNAME=localhost \
|
||||
docker compose -p hg-hbase -f docker/hbase/docker-compose.hbase.yml up -d
|
||||
until docker exec hg-hbase-test nc -z localhost 2181 >/dev/null 2>&1; do sleep 2; done
|
||||
echo "HBase ZooKeeper is reachable on 2181"
|
||||
# Optional troubleshooting stream:
|
||||
# docker compose -p hg-hbase -f docker/hbase/docker-compose.hbase.yml logs -f hbase
|
||||
```
|
||||
```bash
|
||||
# 2) Configure HugeGraph (standalone runtime)
|
||||
SERVER_DIR="$(find . -maxdepth 4 -type d -path './hugegraph-server/apache-hugegraph-server-*' | head -n 1)"
|
||||
[ -n "$SERVER_DIR" ] || { echo "Build artifact not found"; exit 1; }
|
||||
CONF="$SERVER_DIR/conf/graphs/hugegraph.properties"
|
||||
|
||||
perl -pi -e 's/^backend=.*/backend=hbase/' "$CONF"
|
||||
perl -pi -e 's/^serializer=.*/serializer=hbase/' "$CONF"
|
||||
perl -pi -e 's/^#(hbase\.hosts=.*)/$1/' "$CONF"
|
||||
perl -pi -e 's/^#(hbase\.port=.*)/$1/' "$CONF"
|
||||
perl -pi -e 's/^#(hbase\.znode_parent=.*)/$1/' "$CONF"
|
||||
perl -pi -e 's/^hbase\.hosts=.*/hbase.hosts=localhost/' "$CONF"
|
||||
perl -pi -e 's/^hbase\.port=.*/hbase.port=2181/' "$CONF"
|
||||
perl -pi -e 's|^hbase\.znode_parent=.*|hbase.znode_parent=/hbase|' "$CONF"
|
||||
|
||||
grep -E '^(backend|serializer|hbase\.)' "$CONF"
|
||||
```
|
||||
|
||||
```bash
|
||||
# 3) Init and start server
|
||||
cd "$SERVER_DIR"
|
||||
printf 'pa\npa\n' | ./bin/init-store.sh
|
||||
./bin/start-hugegraph.sh
|
||||
|
||||
# 4) Verify backend logs mention hbase
|
||||
cd "$ROOT_DIR"
|
||||
grep -Eai 'hbase|rocksdb|hstore' "$SERVER_DIR"/logs/*.log | tail -n 30
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Option 2: Docker HugeGraph (fully containerized)</b></summary>
|
||||
|
||||
```
|
||||
cd "$ROOT_DIR"
|
||||
```
|
||||
|
||||
```bash
|
||||
# 1) Start HBase
|
||||
docker compose -p hg-hbase -f docker/hbase/docker-compose.hbase.yml down -v
|
||||
docker compose -p hg-hbase -f docker/hbase/docker-compose.hbase.yml build --no-cache hbase
|
||||
HBASE_HOSTNAME=hbase docker compose -p hg-hbase -f docker/hbase/docker-compose.hbase.yml up -d
|
||||
until docker exec hg-hbase-test nc -z localhost 2181 >/dev/null 2>&1; do sleep 2; done
|
||||
echo "HBase ZooKeeper is reachable on 2181"
|
||||
# Optional troubleshooting stream:
|
||||
# docker compose -p hg-hbase -f docker/hbase/docker-compose.hbase.yml logs -f hbase
|
||||
```
|
||||
|
||||
```bash
|
||||
# 2) Build HugeGraph server image
|
||||
docker build -f hugegraph-server/Dockerfile -t hugegraph/server:dev .
|
||||
|
||||
# 3) Resolve HBase network
|
||||
HBASE_NETWORK="$(docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{println $k}}{{end}}' hg-hbase-test | head -n 1)"
|
||||
echo "$HBASE_NETWORK"
|
||||
```
|
||||
|
||||
```bash
|
||||
# 4) One-shot init-store
|
||||
docker rm -f hg-server-init >/dev/null 2>&1 || true
|
||||
docker run --rm --name hg-server-init \
|
||||
--network "$HBASE_NETWORK" \
|
||||
hugegraph/server:dev \
|
||||
bash -lc '
|
||||
set -euo pipefail
|
||||
CONF=/hugegraph-server/conf/graphs/hugegraph.properties
|
||||
perl -pi -e "s/^backend=.*/backend=hbase/" "$CONF"
|
||||
perl -pi -e "s/^serializer=.*/serializer=hbase/" "$CONF"
|
||||
perl -pi -e "s/^#(hbase\.hosts=.*)/\$1/" "$CONF"
|
||||
perl -pi -e "s/^#(hbase\.port=.*)/\$1/" "$CONF"
|
||||
perl -pi -e "s/^#(hbase\.znode_parent=.*)/\$1/" "$CONF"
|
||||
perl -pi -e "s/^hbase\.hosts=.*/hbase.hosts=hbase/" "$CONF"
|
||||
perl -pi -e "s/^hbase\.port=.*/hbase.port=2181/" "$CONF"
|
||||
perl -pi -e "s|^hbase\.znode_parent=.*|hbase.znode_parent=/hbase|" "$CONF"
|
||||
printf "pa\npa\n" | ./bin/init-store.sh
|
||||
'
|
||||
```
|
||||
|
||||
```bash
|
||||
# 5) Start HugeGraph container
|
||||
docker rm -f hg-server-dev-hbase >/dev/null 2>&1 || true
|
||||
docker run -d --name hg-server-dev-hbase \
|
||||
--network "$HBASE_NETWORK" \
|
||||
-p 8080:8080 \
|
||||
-p 8182:8182 \
|
||||
hugegraph/server:dev \
|
||||
bash -lc '
|
||||
set -euo pipefail
|
||||
CONF=/hugegraph-server/conf/graphs/hugegraph.properties
|
||||
perl -pi -e "s/^backend=.*/backend=hbase/" "$CONF"
|
||||
perl -pi -e "s/^serializer=.*/serializer=hbase/" "$CONF"
|
||||
perl -pi -e "s/^#(hbase\.hosts=.*)/\$1/" "$CONF"
|
||||
perl -pi -e "s/^#(hbase\.port=.*)/\$1/" "$CONF"
|
||||
perl -pi -e "s/^#(hbase\.znode_parent=.*)/\$1/" "$CONF"
|
||||
perl -pi -e "s/^hbase\.hosts=.*/hbase.hosts=hbase/" "$CONF"
|
||||
perl -pi -e "s/^hbase\.port=.*/hbase.port=2181/" "$CONF"
|
||||
perl -pi -e "s|^hbase\.znode_parent=.*|hbase.znode_parent=/hbase|" "$CONF"
|
||||
./bin/start-hugegraph.sh -t 120
|
||||
tail -f /hugegraph-server/logs/hugegraph-server.log
|
||||
'
|
||||
```
|
||||
|
||||
```bash
|
||||
# 6) Verify hbase backend
|
||||
docker exec hg-server-dev-hbase bash -lc "grep -E '^(backend|serializer|hbase\.)' /hugegraph-server/conf/graphs/hugegraph.properties"
|
||||
docker exec hg-server-dev-hbase bash -lc "grep -Ei 'hbase|rocksdb|hstore' /hugegraph-server/logs/*.log | tail -n 30"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
After either path is up, run the shared tests below.
|
||||
|
||||
---
|
||||
|
||||
## Common Testing Steps
|
||||
|
||||
### Apache HugeGraph Persistent Runbook (REST Engine)
|
||||
|
||||
### Prerequisites and Constants
|
||||
|
||||
- Base URL: `http://localhost:8080`
|
||||
- Graph target name: `hugegraph`
|
||||
- Storage backend: persistent (HBase/RocksDB/HStore)
|
||||
|
||||
---
|
||||
|
||||
### Step 1: Purge Database (Fresh Restart)
|
||||
|
||||
Wipe any conflicting test records and data schema.
|
||||
|
||||
```bash
|
||||
curl -X DELETE "http://localhost:8080/graphspaces/DEFAULT/graphs/hugegraph/clear?confirm_message=I%27m+sure+to+delete+all+data"
|
||||
```
|
||||
|
||||
Status `204 No Content` confirms success.
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Provision Structural Schema
|
||||
|
||||
1) Register property keys:
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"name": "name", "data_type": "TEXT", "cardinality": "SINGLE"}' \
|
||||
"http://localhost:8080/graphs/hugegraph/schema/propertykeys"
|
||||
```
|
||||
|
||||
2) Register vertex label (PRIMARY_KEY):
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"name": "person", "id_strategy": "PRIMARY_KEY", "properties": ["name"], "primary_keys": ["name"]}' \
|
||||
"http://localhost:8080/graphs/hugegraph/schema/vertexlabels"
|
||||
```
|
||||
|
||||
3) Register edge label:
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"name": "knows", "source_label": "person", "target_label": "person", "properties": []}' \
|
||||
"http://localhost:8080/graphs/hugegraph/schema/edgelabels"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Populate Graph Elements
|
||||
|
||||
1) Batch write vertices (Alice and Bob):
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d '[{"label": "person", "properties": {"name": "Alice"}}, {"label": "person", "properties": {"name": "Bob"}}]' \
|
||||
"http://localhost:8080/graphs/hugegraph/graph/vertices/batch"
|
||||
```
|
||||
|
||||
Response should include IDs similar to `1:Alice` and `1:Bob`.
|
||||
|
||||
2) Create directed edge (Alice knows Bob):
|
||||
|
||||
```bash
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"label": "knows", "outV": "1:Alice", "inV": "1:Bob", "properties": {}}' \
|
||||
"http://localhost:8080/graphs/hugegraph/graph/edges"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Synchronous Verification and Traversal
|
||||
|
||||
1) Verify target K-hop output:
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:8080/graphs/hugegraph/traversers/kout?source=%221:Alice%22&direction=OUT&max_depth=1"
|
||||
```
|
||||
|
||||
Expected output: `{"vertices":["1:Bob"]}`
|
||||
|
||||
2) Verify relation path structure:
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:8080/graphs/hugegraph/traversers/rays?source=%221:Alice%22&direction=OUT&label=knows&max_depth=1"
|
||||
```
|
||||
|
||||
Expected output contains: `rays":[{"objects":["1:Alice","1:Bob"]}]`
|
||||
|
||||
---
|
||||
|
||||
### Troubleshooting Cheat Sheet
|
||||
|
||||
- URI syntax error: do not append literal `"` inside bare URLs. Use URL-encoded values (`%22`).
|
||||
- Property missing errors: prefer native `/traversers/*` APIs for synchronous reads.
|
||||
|
||||
---
|
||||
|
||||
## Cleanup
|
||||
|
||||
Run cleanup only after testing is complete.
|
||||
|
||||
### Standalone HugeGraph + Docker HBase
|
||||
|
||||
```bash
|
||||
cd "$SERVER_DIR" && ./bin/stop-hugegraph.sh
|
||||
cd "$ROOT_DIR"
|
||||
docker compose -p hg-hbase -f docker/hbase/docker-compose.hbase.yml down -v
|
||||
```
|
||||
|
||||
### Docker HugeGraph + Docker HBase
|
||||
|
||||
```bash
|
||||
docker rm -f hg-server-init >/dev/null 2>&1 || true
|
||||
docker rm -f hg-server-dev-hbase
|
||||
docker compose -p hg-hbase -f docker/hbase/docker-compose.hbase.yml down -v
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Fix |
|
||||
|---|---|
|
||||
| `UnknownHostException: hbase:16000` | HugeGraph container is not on same Docker network as HBase. Verify `HBASE_NETWORK` and `--network`. |
|
||||
| RocksDB logs in server output | `backend=rocksdb` still active; re-run backend config and restart. |
|
||||
| `TableNotFoundException` on API calls | Tables not initialized; re-run `init-store.sh` from selected path. |
|
||||
| Port 8182 already in use | `lsof -i :8182` then `kill <PID>`. |
|
||||
| HBase container not starting | Check `lsof -i :2181`; increase Docker memory to >= 4 GB. |
|
||||
|
||||
---
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
- [ ] `backend=hbase` in `hugegraph.properties`
|
||||
- [ ] Server logs show HBase client messages (not RocksDB/HStore)
|
||||
- [ ] HBase tables exist in `default_hugegraph:*`
|
||||
- [ ] REST runbook queries return expected graph data
|
||||
- [ ] Data survives server restart
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- HBase official docs: https://hbase.apache.org/
|
||||
- HugeGraph HBase backend: `hugegraph-server/hugegraph-hbase/`
|
||||
- HBase Docker Compose: `docker/hbase/docker-compose.hbase.yml`
|
||||
- HBase Docker config: `docker/hbase/hbase-site.xml`
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# HBase standalone for local HugeGraph HBase backend development & testing.
|
||||
#
|
||||
# Usage:
|
||||
# Start: docker compose -f docker/hbase/docker-compose.hbase.yml up -d
|
||||
# Standalone HugeGraph on host:
|
||||
# HBASE_MASTER_HOSTNAME=localhost HBASE_REGIONSERVER_HOSTNAME=localhost \
|
||||
# docker compose -f docker/hbase/docker-compose.hbase.yml up -d
|
||||
# Wait: until docker exec hg-hbase-test nc -z localhost 2181; do sleep 2; done
|
||||
# Verify: nc -z localhost 2181 && echo "ZooKeeper OK"
|
||||
# Test: mvn test -pl hugegraph-server/hugegraph-test -am -P core-test,hbase
|
||||
# Stop: docker compose -f docker/hbase/docker-compose.hbase.yml down -v
|
||||
#
|
||||
# HugeGraph test config (hugegraph-server/hugegraph-test/src/main/resources/hugegraph.properties):
|
||||
# hbase.hosts=localhost
|
||||
# hbase.port=2181
|
||||
# hbase.znode_parent=/hbase
|
||||
#
|
||||
# Ports exposed to host:
|
||||
# 2181 - ZooKeeper (HBase embedded)
|
||||
# 16000 - HBase Master RPC
|
||||
# 16010 - HBase Master Web UI -> http://localhost:16010
|
||||
# 16020 - HBase RegionServer RPC
|
||||
# 16030 - HBase RegionServer Web UI -> http://localhost:16030
|
||||
|
||||
services:
|
||||
hbase:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
HBASE_VERSION: "2.6.5"
|
||||
# Optional overrides for flaky networks/corporate mirrors.
|
||||
HBASE_PRIMARY_URL: "${HBASE_PRIMARY_URL:-https://downloads.apache.org/hbase/2.6.5/hbase-2.6.5-bin.tar.gz}"
|
||||
HBASE_FALLBACK_URL: "${HBASE_FALLBACK_URL:-https://archive.apache.org/dist/hbase/2.6.5/hbase-2.6.5-bin.tar.gz}"
|
||||
image: hugegraph/hbase:2.6.5
|
||||
container_name: hg-hbase-test
|
||||
hostname: "${HBASE_HOSTNAME:-hbase}"
|
||||
environment:
|
||||
HBASE_HOSTNAME: "${HBASE_HOSTNAME:-hbase}"
|
||||
HBASE_MASTER_HOSTNAME: "${HBASE_MASTER_HOSTNAME:-}"
|
||||
HBASE_REGIONSERVER_HOSTNAME: "${HBASE_REGIONSERVER_HOSTNAME:-}"
|
||||
ports:
|
||||
- "2181:2181" # ZooKeeper (matches hbase.port in hugegraph.properties)
|
||||
- "16000:16000" # Master RPC
|
||||
- "16010:16010" # Master Web UI -> http://localhost:16010
|
||||
- "16020:16020" # RegionServer RPC
|
||||
- "16030:16030" # RegionServer Web UI -> http://localhost:16030
|
||||
volumes:
|
||||
- hbase-data:/tmp/hbase
|
||||
- hbase-zk-data:/tmp/zookeeper
|
||||
healthcheck:
|
||||
# nc -z confirms ZooKeeper is accepting connections
|
||||
test: ["CMD", "nc", "-z", "localhost", "2181"]
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
start_period: 90s
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
hbase-data:
|
||||
hbase-zk-data:
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
set -e
|
||||
|
||||
HBASE_HOSTNAME="${HBASE_HOSTNAME:-hbase}"
|
||||
HBASE_MASTER_HOSTNAME="${HBASE_MASTER_HOSTNAME:-${HBASE_HOSTNAME}}"
|
||||
HBASE_REGIONSERVER_HOSTNAME="${HBASE_REGIONSERVER_HOSTNAME:-${HBASE_HOSTNAME}}"
|
||||
HBASE_SITE_XML="${HBASE_HOME}/conf/hbase-site.xml"
|
||||
|
||||
escape_sed_replacement() {
|
||||
local value="$1"
|
||||
if [[ "$value" == *$'\n'* ]]; then
|
||||
echo "Property values must not contain newlines" >&2
|
||||
exit 1
|
||||
fi
|
||||
printf '%s' "$value" | sed -e 's/[\\&|]/\\&/g'
|
||||
}
|
||||
|
||||
set_xml_property_value() {
|
||||
local property_name="$1"
|
||||
local property_value
|
||||
property_value=$(escape_sed_replacement "$2")
|
||||
|
||||
# The in-place replacement below expects the standard HBase layout where
|
||||
# <name>...</name> is followed by <value>...</value> on the next line.
|
||||
# Fail loudly if the property entry is missing to avoid silent misconfig.
|
||||
if ! grep -Fq "<name>${property_name}</name>" "${HBASE_SITE_XML}"; then
|
||||
echo "Missing required property '${property_name}' in ${HBASE_SITE_XML}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sed -i "/<name>${property_name//./\\.}<\\/name>/ {n; s|<value>.*</value>|<value>${property_value}</value>|;}" "${HBASE_SITE_XML}"
|
||||
}
|
||||
|
||||
set_xml_property_value "hbase.master.hostname" "${HBASE_MASTER_HOSTNAME}"
|
||||
set_xml_property_value "hbase.regionserver.hostname" "${HBASE_REGIONSERVER_HOSTNAME}"
|
||||
|
||||
echo "Starting HBase ${HBASE_VERSION} standalone..."
|
||||
echo "HBase container hostname fallback: ${HBASE_HOSTNAME}"
|
||||
echo "HBase advertised master hostname: ${HBASE_MASTER_HOSTNAME}"
|
||||
echo "HBase advertised regionserver hostname: ${HBASE_REGIONSERVER_HOSTNAME}"
|
||||
|
||||
|
||||
# Start services explicitly to avoid SSH-based helper assumptions in containers
|
||||
${HBASE_HOME}/bin/hbase-daemon.sh start zookeeper
|
||||
${HBASE_HOME}/bin/hbase-daemon.sh start master
|
||||
${HBASE_HOME}/bin/hbase-daemon.sh start regionserver
|
||||
|
||||
echo "HBase started. Waiting for ZooKeeper on port 2181..."
|
||||
zk_attempts=0
|
||||
until nc -z localhost 2181; do
|
||||
zk_attempts=$((zk_attempts + 1))
|
||||
if [ "$zk_attempts" -ge 120 ]; then
|
||||
echo "Timed out waiting for ZooKeeper on 2181"
|
||||
${HBASE_HOME}/bin/hbase-daemon.sh status || true
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo "ZooKeeper is ready."
|
||||
|
||||
echo "Waiting for HBase Master..."
|
||||
master_attempts=0
|
||||
until echo "status 'simple'" | ${HBASE_HOME}/bin/hbase shell -n 2>/dev/null | grep -E -q "([1-9][0-9]*[[:space:]]+live[[:space:]]+servers|[1-9][0-9]*[[:space:]]+servers|servers:[[:space:]]*[1-9])"; do
|
||||
master_attempts=$((master_attempts + 1))
|
||||
if [ "$master_attempts" -ge 180 ]; then
|
||||
echo "Timed out waiting for HBase master/regionserver readiness"
|
||||
${HBASE_HOME}/bin/hbase-daemon.sh status || true
|
||||
tail -n 80 ${HBASE_HOME}/logs/hbase-*.out ${HBASE_HOME}/logs/hbase-*.log \
|
||||
2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
echo "HBase is ready. Master + RegionServer online."
|
||||
|
||||
# Tail all daemon logs so `docker logs` includes startup/runtime issues
|
||||
shopt -s nullglob
|
||||
log_files=("${HBASE_HOME}/logs"/hbase-*.out "${HBASE_HOME}/logs"/hbase-*.log)
|
||||
if [ ${#log_files[@]} -gt 0 ]; then
|
||||
exec tail -F "${log_files[@]}"
|
||||
fi
|
||||
exec tail -f /dev/null
|
||||
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
|
||||
<configuration>
|
||||
<!-- Standalone mode: use local filesystem -->
|
||||
<property>
|
||||
<name>hbase.rootdir</name>
|
||||
<value>file:///tmp/hbase</value>
|
||||
</property>
|
||||
<!-- Embedded ZooKeeper data directory -->
|
||||
<property>
|
||||
<name>hbase.zookeeper.property.dataDir</name>
|
||||
<value>/tmp/zookeeper</value>
|
||||
</property>
|
||||
<!-- Keep ZooKeeper quorum explicit for containerized single-node setup -->
|
||||
<property>
|
||||
<name>hbase.zookeeper.quorum</name>
|
||||
<value>localhost</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>hbase.zookeeper.property.clientPort</name>
|
||||
<value>2181</value>
|
||||
</property>
|
||||
<!-- Allow unlimited ZooKeeper client connections -->
|
||||
<property>
|
||||
<name>hbase.zookeeper.property.maxClientCnxns</name>
|
||||
<value>0</value>
|
||||
</property>
|
||||
<!-- Match hugegraph.properties: hbase.znode_parent=/hbase -->
|
||||
<property>
|
||||
<name>zookeeper.znode.parent</name>
|
||||
<value>/hbase</value>
|
||||
</property>
|
||||
<!-- Longer tick + session for slow CI/test environments -->
|
||||
<property>
|
||||
<name>hbase.zookeeper.property.tickTime</name>
|
||||
<value>6000</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>zookeeper.session.timeout</name>
|
||||
<value>180000</value>
|
||||
</property>
|
||||
<!-- Pseudo-distributed mode so master + regionserver run as distinct daemons -->
|
||||
<property>
|
||||
<name>hbase.cluster.distributed</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
<!-- Local FS in single-node Docker may not support async WAL hflush -->
|
||||
<property>
|
||||
<name>hbase.wal.provider</name>
|
||||
<value>filesystem</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>hbase.unsafe.stream.capability.enforce</name>
|
||||
<value>false</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>hbase.master.hostname</name>
|
||||
<value>localhost</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>hbase.regionserver.hostname</name>
|
||||
<value>localhost</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>hbase.master.ipc.address</name>
|
||||
<value>0.0.0.0</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>hbase.regionserver.ipc.address</name>
|
||||
<value>0.0.0.0</value>
|
||||
</property>
|
||||
</configuration>
|
||||
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.apache.hugegraph</groupId>
|
||||
<artifactId>hugegraph</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hugegraph-api</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.hugegraph</groupId>
|
||||
<artifactId>hugegraph-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.hugegraph</groupId>
|
||||
<artifactId>hugegraph-rpc</artifactId>
|
||||
<exclusions>
|
||||
<!-- conflict with jraft -->
|
||||
<exclusion>
|
||||
<groupId>com.alipay.sofa</groupId>
|
||||
<artifactId>bolt</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.alipay.sofa.common</groupId>
|
||||
<artifactId>sofa-common-tools</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.alipay.sofa</groupId>
|
||||
<artifactId>hessian</artifactId>
|
||||
</exclusion>
|
||||
<!-- conflict with cassandra-netty/tinkerpop-server -->
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tinkerpop</groupId>
|
||||
<artifactId>gremlin-server</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.github.jeremyh</groupId>
|
||||
<artifactId>jBCrypt</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.core</groupId>
|
||||
<artifactId>jersey-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.containers</groupId>
|
||||
<artifactId>jersey-container-grizzly2-http</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.containers</groupId>
|
||||
<artifactId>jersey-container-grizzly2-servlet</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.media</groupId>
|
||||
<artifactId>jersey-media-json-jackson</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-jaxb-annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish.grizzly</groupId>
|
||||
<artifactId>grizzly-http</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.grizzly</groupId>
|
||||
<artifactId>grizzly-framework</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.grizzly</groupId>
|
||||
<artifactId>grizzly-http-server</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.grizzly</groupId>
|
||||
<artifactId>grizzly-http-servlet</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.dropwizard.metrics</groupId>
|
||||
<artifactId>metrics-jersey3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.opencypher.gremlin</groupId>
|
||||
<artifactId>translation</artifactId>
|
||||
<version>1.0.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-jaxrs2-jakarta</artifactId>
|
||||
<version>2.1.9</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<index>true</index>
|
||||
<manifest>
|
||||
<addDefaultImplementationEntries>
|
||||
false
|
||||
</addDefaultImplementationEntries>
|
||||
<addDefaultSpecificationEntries>
|
||||
true
|
||||
</addDefaultSpecificationEntries>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<!-- TODO: update it -->
|
||||
<Implementation-Version>0.69.0.0</Implementation-Version>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -1,195 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jakarta.ws.rs.ForbiddenException;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.NotSupportedException;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.apache.hugegraph.metrics.MetricsUtil;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeException;
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.JsonUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public class API {
|
||||
|
||||
protected static final Logger LOG = Log.logger(API.class);
|
||||
|
||||
public static final String CHARSET = "UTF-8";
|
||||
|
||||
public static final String TEXT_PLAIN = MediaType.TEXT_PLAIN;
|
||||
public static final String APPLICATION_JSON = MediaType.APPLICATION_JSON;
|
||||
public static final String APPLICATION_JSON_WITH_CHARSET =
|
||||
APPLICATION_JSON + ";charset=" + CHARSET;
|
||||
public static final String JSON = MediaType.APPLICATION_JSON_TYPE
|
||||
.getSubtype();
|
||||
|
||||
public static final String ACTION_APPEND = "append";
|
||||
public static final String ACTION_ELIMINATE = "eliminate";
|
||||
public static final String ACTION_CLEAR = "clear";
|
||||
|
||||
private static final Meter SUCCEED_METER =
|
||||
MetricsUtil.registerMeter(API.class, "commit-succeed");
|
||||
private static final Meter ILLEGAL_ARG_ERROR_METER =
|
||||
MetricsUtil.registerMeter(API.class, "illegal-arg");
|
||||
private static final Meter EXPECTED_ERROR_METER =
|
||||
MetricsUtil.registerMeter(API.class, "expected-error");
|
||||
private static final Meter UNKNOWN_ERROR_METER =
|
||||
MetricsUtil.registerMeter(API.class, "unknown-error");
|
||||
|
||||
public static HugeGraph graph(GraphManager manager, String graph) {
|
||||
HugeGraph g = manager.graph(graph);
|
||||
if (g == null) {
|
||||
throw new NotFoundException(String.format(
|
||||
"Graph '%s' does not exist", graph));
|
||||
}
|
||||
return g;
|
||||
}
|
||||
|
||||
public static HugeGraph graph4admin(GraphManager manager, String graph) {
|
||||
return graph(manager, graph).hugegraph();
|
||||
}
|
||||
|
||||
public static <R> R commit(HugeGraph g, Callable<R> callable) {
|
||||
Consumer<Throwable> rollback = (error) -> {
|
||||
if (error != null) {
|
||||
LOG.error("Failed to commit", error);
|
||||
}
|
||||
try {
|
||||
g.tx().rollback();
|
||||
} catch (Throwable e) {
|
||||
LOG.error("Failed to rollback", e);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
R result = callable.call();
|
||||
g.tx().commit();
|
||||
SUCCEED_METER.mark();
|
||||
return result;
|
||||
} catch (IllegalArgumentException | NotFoundException |
|
||||
ForbiddenException e) {
|
||||
ILLEGAL_ARG_ERROR_METER.mark();
|
||||
rollback.accept(null);
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
EXPECTED_ERROR_METER.mark();
|
||||
rollback.accept(e);
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
UNKNOWN_ERROR_METER.mark();
|
||||
rollback.accept(e);
|
||||
// TODO: throw the origin exception 'e'
|
||||
throw new HugeException("Failed to commit", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void commit(HugeGraph g, Runnable runnable) {
|
||||
commit(g, () -> {
|
||||
runnable.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static Object[] properties(Map<String, Object> properties) {
|
||||
Object[] list = new Object[properties.size() * 2];
|
||||
int i = 0;
|
||||
for (Map.Entry<String, Object> prop : properties.entrySet()) {
|
||||
list[i++] = prop.getKey();
|
||||
list[i++] = prop.getValue();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
protected static void checkCreatingBody(Checkable body) {
|
||||
E.checkArgumentNotNull(body, "The request body can't be empty");
|
||||
body.checkCreate(false);
|
||||
}
|
||||
|
||||
protected static void checkUpdatingBody(Checkable body) {
|
||||
E.checkArgumentNotNull(body, "The request body can't be empty");
|
||||
body.checkUpdate();
|
||||
}
|
||||
|
||||
protected static void checkCreatingBody(
|
||||
Collection<? extends Checkable> bodies) {
|
||||
E.checkArgumentNotNull(bodies, "The request body can't be empty");
|
||||
for (Checkable body : bodies) {
|
||||
E.checkArgument(body != null,
|
||||
"The batch body can't contain null record");
|
||||
body.checkCreate(true);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void checkUpdatingBody(
|
||||
Collection<? extends Checkable> bodies) {
|
||||
E.checkArgumentNotNull(bodies, "The request body can't be empty");
|
||||
for (Checkable body : bodies) {
|
||||
E.checkArgumentNotNull(body,
|
||||
"The batch body can't contain null record");
|
||||
body.checkUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static Map<String, Object> parseProperties(String properties) {
|
||||
if (properties == null || properties.isEmpty()) {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
|
||||
Map<String, Object> props = null;
|
||||
try {
|
||||
props = JsonUtil.fromJson(properties, Map.class);
|
||||
} catch (Exception ignored) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// If properties is the string "null", props will be null
|
||||
E.checkArgument(props != null,
|
||||
"Invalid request with properties: %s", properties);
|
||||
return props;
|
||||
}
|
||||
|
||||
public static boolean checkAndParseAction(String action) {
|
||||
E.checkArgumentNotNull(action, "The action param can't be empty");
|
||||
if (action.equals(ACTION_APPEND)) {
|
||||
return true;
|
||||
} else if (action.equals(ACTION_ELIMINATE)) {
|
||||
return false;
|
||||
} else {
|
||||
throw new NotSupportedException(
|
||||
String.format("Not support action '%s'", action));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.auth;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Path;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.auth.HugeAccess;
|
||||
import org.apache.hugegraph.auth.HugePermission;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.exception.NotFoundException;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/auth/accesses")
|
||||
@Singleton
|
||||
@Tag(name = "AccessAPI")
|
||||
public class AccessAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(AccessAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String create(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
JsonAccess jsonAccess) {
|
||||
LOG.debug("Graph [{}] create access: {}", graph, jsonAccess);
|
||||
checkCreatingBody(jsonAccess);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeAccess access = jsonAccess.build();
|
||||
access.id(manager.authManager().createAccess(access));
|
||||
return manager.serializer(g).writeAuthElement(access);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String update(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id,
|
||||
JsonAccess jsonAccess) {
|
||||
LOG.debug("Graph [{}] update access: {}", graph, jsonAccess);
|
||||
checkUpdatingBody(jsonAccess);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeAccess access;
|
||||
try {
|
||||
access = manager.authManager().getAccess(UserAPI.parseId(id));
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException("Invalid access id: " + id);
|
||||
}
|
||||
access = jsonAccess.build(access);
|
||||
manager.authManager().updateAccess(access);
|
||||
return manager.serializer(g).writeAuthElement(access);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("group") String group,
|
||||
@QueryParam("target") String target,
|
||||
@QueryParam("limit") @DefaultValue("100") long limit) {
|
||||
LOG.debug("Graph [{}] list belongs by group {} or target {}",
|
||||
graph, group, target);
|
||||
E.checkArgument(group == null || target == null,
|
||||
"Can't pass both group and target at the same time");
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
List<HugeAccess> belongs;
|
||||
if (group != null) {
|
||||
Id id = UserAPI.parseId(group);
|
||||
belongs = manager.authManager().listAccessByGroup(id, limit);
|
||||
} else if (target != null) {
|
||||
Id id = UserAPI.parseId(target);
|
||||
belongs = manager.authManager().listAccessByTarget(id, limit);
|
||||
} else {
|
||||
belongs = manager.authManager().listAllAccess(limit);
|
||||
}
|
||||
return manager.serializer(g).writeAuthElements("accesses", belongs);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
LOG.debug("Graph [{}] get access: {}", graph, id);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeAccess access = manager.authManager().getAccess(UserAPI.parseId(id));
|
||||
return manager.serializer(g).writeAuthElement(access);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
public void delete(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
LOG.debug("Graph [{}] delete access: {}", graph, id);
|
||||
|
||||
@SuppressWarnings("unused") // just check if the graph exists
|
||||
HugeGraph g = graph(manager, graph);
|
||||
try {
|
||||
manager.authManager().deleteAccess(UserAPI.parseId(id));
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException("Invalid access id: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(value = {"id", "access_creator",
|
||||
"access_create", "access_update"})
|
||||
private static class JsonAccess implements Checkable {
|
||||
|
||||
@JsonProperty("group")
|
||||
private String group;
|
||||
@JsonProperty("target")
|
||||
private String target;
|
||||
@JsonProperty("access_permission")
|
||||
private HugePermission permission;
|
||||
@JsonProperty("access_description")
|
||||
private String description;
|
||||
|
||||
public HugeAccess build(HugeAccess access) {
|
||||
E.checkArgument(this.group == null ||
|
||||
access.source().equals(UserAPI.parseId(this.group)),
|
||||
"The group of access can't be updated");
|
||||
E.checkArgument(this.target == null ||
|
||||
access.target().equals(UserAPI.parseId(this.target)),
|
||||
"The target of access can't be updated");
|
||||
E.checkArgument(this.permission == null ||
|
||||
access.permission().equals(this.permission),
|
||||
"The permission of access can't be updated");
|
||||
if (this.description != null) {
|
||||
access.description(this.description);
|
||||
}
|
||||
return access;
|
||||
}
|
||||
|
||||
public HugeAccess build() {
|
||||
HugeAccess access = new HugeAccess(UserAPI.parseId(this.group),
|
||||
UserAPI.parseId(this.target));
|
||||
access.permission(this.permission);
|
||||
access.description(this.description);
|
||||
return access;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCreate(boolean isBatch) {
|
||||
E.checkArgumentNotNull(this.group,
|
||||
"The group of access can't be null");
|
||||
E.checkArgumentNotNull(this.target,
|
||||
"The target of access can't be null");
|
||||
E.checkArgumentNotNull(this.permission,
|
||||
"The permission of access can't be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkUpdate() {
|
||||
E.checkArgumentNotNull(this.description,
|
||||
"The description of access can't be null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.auth;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import jakarta.inject.Singleton;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.auth.HugeBelong;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.exception.NotFoundException;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/auth/belongs")
|
||||
@Singleton
|
||||
@Tag(name = "BelongAPI")
|
||||
public class BelongAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(BelongAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String create(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
JsonBelong jsonBelong) {
|
||||
LOG.debug("Graph [{}] create belong: {}", graph, jsonBelong);
|
||||
checkCreatingBody(jsonBelong);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeBelong belong = jsonBelong.build();
|
||||
belong.id(manager.authManager().createBelong(belong));
|
||||
return manager.serializer(g).writeAuthElement(belong);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String update(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id,
|
||||
JsonBelong jsonBelong) {
|
||||
LOG.debug("Graph [{}] update belong: {}", graph, jsonBelong);
|
||||
checkUpdatingBody(jsonBelong);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeBelong belong;
|
||||
try {
|
||||
belong = manager.authManager().getBelong(UserAPI.parseId(id));
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException("Invalid belong id: " + id);
|
||||
}
|
||||
belong = jsonBelong.build(belong);
|
||||
manager.authManager().updateBelong(belong);
|
||||
return manager.serializer(g).writeAuthElement(belong);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("user") String user,
|
||||
@QueryParam("group") String group,
|
||||
@QueryParam("limit") @DefaultValue("100") long limit) {
|
||||
LOG.debug("Graph [{}] list belongs by user {} or group {}",
|
||||
graph, user, group);
|
||||
E.checkArgument(user == null || group == null,
|
||||
"Can't pass both user and group at the same time");
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
List<HugeBelong> belongs;
|
||||
if (user != null) {
|
||||
Id id = UserAPI.parseId(user);
|
||||
belongs = manager.authManager().listBelongByUser(id, limit);
|
||||
} else if (group != null) {
|
||||
Id id = UserAPI.parseId(group);
|
||||
belongs = manager.authManager().listBelongByGroup(id, limit);
|
||||
} else {
|
||||
belongs = manager.authManager().listAllBelong(limit);
|
||||
}
|
||||
return manager.serializer(g).writeAuthElements("belongs", belongs);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
LOG.debug("Graph [{}] get belong: {}", graph, id);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeBelong belong = manager.authManager().getBelong(UserAPI.parseId(id));
|
||||
return manager.serializer(g).writeAuthElement(belong);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
public void delete(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
LOG.debug("Graph [{}] delete belong: {}", graph, id);
|
||||
|
||||
@SuppressWarnings("unused") // just check if the graph exists
|
||||
HugeGraph g = graph(manager, graph);
|
||||
try {
|
||||
manager.authManager().deleteBelong(UserAPI.parseId(id));
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException("Invalid belong id: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(value = {"id", "belong_creator",
|
||||
"belong_create", "belong_update"})
|
||||
private static class JsonBelong implements Checkable {
|
||||
|
||||
@JsonProperty("user")
|
||||
private String user;
|
||||
@JsonProperty("group")
|
||||
private String group;
|
||||
@JsonProperty("belong_description")
|
||||
private String description;
|
||||
|
||||
public HugeBelong build(HugeBelong belong) {
|
||||
E.checkArgument(this.user == null ||
|
||||
belong.source().equals(UserAPI.parseId(this.user)),
|
||||
"The user of belong can't be updated");
|
||||
E.checkArgument(this.group == null ||
|
||||
belong.target().equals(UserAPI.parseId(this.group)),
|
||||
"The group of belong can't be updated");
|
||||
if (this.description != null) {
|
||||
belong.description(this.description);
|
||||
}
|
||||
return belong;
|
||||
}
|
||||
|
||||
public HugeBelong build() {
|
||||
HugeBelong belong = new HugeBelong(UserAPI.parseId(this.user),
|
||||
UserAPI.parseId(this.group));
|
||||
belong.description(this.description);
|
||||
return belong;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCreate(boolean isBatch) {
|
||||
E.checkArgumentNotNull(this.user,
|
||||
"The user of belong can't be null");
|
||||
E.checkArgumentNotNull(this.group,
|
||||
"The group of belong can't be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkUpdate() {
|
||||
E.checkArgumentNotNull(this.description,
|
||||
"The description of belong can't be null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.auth;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import jakarta.inject.Singleton;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.auth.HugeGroup;
|
||||
import org.apache.hugegraph.backend.id.IdGenerator;
|
||||
import org.apache.hugegraph.exception.NotFoundException;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/auth/groups")
|
||||
@Singleton
|
||||
@Tag(name = "GroupAPI")
|
||||
public class GroupAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(GroupAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String create(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
JsonGroup jsonGroup) {
|
||||
LOG.debug("Graph [{}] create group: {}", graph, jsonGroup);
|
||||
checkCreatingBody(jsonGroup);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeGroup group = jsonGroup.build();
|
||||
group.id(manager.authManager().createGroup(group));
|
||||
return manager.serializer(g).writeAuthElement(group);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String update(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id,
|
||||
JsonGroup jsonGroup) {
|
||||
LOG.debug("Graph [{}] update group: {}", graph, jsonGroup);
|
||||
checkUpdatingBody(jsonGroup);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeGroup group;
|
||||
try {
|
||||
group = manager.authManager().getGroup(UserAPI.parseId(id));
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException("Invalid group id: " + id);
|
||||
}
|
||||
group = jsonGroup.build(group);
|
||||
manager.authManager().updateGroup(group);
|
||||
return manager.serializer(g).writeAuthElement(group);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("limit") @DefaultValue("100") long limit) {
|
||||
LOG.debug("Graph [{}] list groups", graph);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
List<HugeGroup> groups = manager.authManager().listAllGroups(limit);
|
||||
return manager.serializer(g).writeAuthElements("groups", groups);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
LOG.debug("Graph [{}] get group: {}", graph, id);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeGroup group = manager.authManager().getGroup(IdGenerator.of(id));
|
||||
return manager.serializer(g).writeAuthElement(group);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
public void delete(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
LOG.debug("Graph [{}] delete group: {}", graph, id);
|
||||
|
||||
@SuppressWarnings("unused") // just check if the graph exists
|
||||
HugeGraph g = graph(manager, graph);
|
||||
try {
|
||||
manager.authManager().deleteGroup(IdGenerator.of(id));
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException("Invalid group id: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(value = {"id", "group_creator",
|
||||
"group_create", "group_update"})
|
||||
private static class JsonGroup implements Checkable {
|
||||
|
||||
@JsonProperty("group_name")
|
||||
private String name;
|
||||
@JsonProperty("group_description")
|
||||
private String description;
|
||||
|
||||
public HugeGroup build(HugeGroup group) {
|
||||
E.checkArgument(this.name == null || group.name().equals(this.name),
|
||||
"The name of group can't be updated");
|
||||
if (this.description != null) {
|
||||
group.description(this.description);
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
public HugeGroup build() {
|
||||
HugeGroup group = new HugeGroup(this.name);
|
||||
group.description(this.description);
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCreate(boolean isBatch) {
|
||||
E.checkArgumentNotNull(this.name,
|
||||
"The name of group can't be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkUpdate() {
|
||||
E.checkArgumentNotNull(this.description,
|
||||
"The description of group can't be null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.auth;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import javax.security.sasl.AuthenticationException;
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.HeaderParam;
|
||||
import jakarta.ws.rs.NotAuthorizedException;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.AuthenticationFilter;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.auth.AuthConstant;
|
||||
import org.apache.hugegraph.auth.UserWithRole;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
@Path("graphs/{graph}/auth")
|
||||
@Singleton
|
||||
@Tag(name = "LoginAPI")
|
||||
public class LoginAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(LoginAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Path("login")
|
||||
@Status(Status.OK)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String login(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
JsonLogin jsonLogin) {
|
||||
LOG.debug("Graph [{}] user login: {}", graph, jsonLogin);
|
||||
checkCreatingBody(jsonLogin);
|
||||
|
||||
try {
|
||||
String token = manager.authManager()
|
||||
.loginUser(jsonLogin.name, jsonLogin.password);
|
||||
HugeGraph g = graph(manager, graph);
|
||||
return manager.serializer(g)
|
||||
.writeMap(ImmutableMap.of("token", token));
|
||||
} catch (AuthenticationException e) {
|
||||
throw new NotAuthorizedException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("logout")
|
||||
@Status(Status.OK)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public void logout(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@HeaderParam(HttpHeaders.AUTHORIZATION) String auth) {
|
||||
E.checkArgument(StringUtils.isNotEmpty(auth),
|
||||
"Request header Authorization must not be null");
|
||||
LOG.debug("Graph [{}] user logout: {}", graph, auth);
|
||||
|
||||
if (!auth.startsWith(AuthenticationFilter.BEARER_TOKEN_PREFIX)) {
|
||||
throw new BadRequestException(
|
||||
"Only HTTP Bearer authentication is supported");
|
||||
}
|
||||
|
||||
String token = auth.substring(AuthenticationFilter.BEARER_TOKEN_PREFIX
|
||||
.length());
|
||||
|
||||
manager.authManager().logoutUser(token);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("verify")
|
||||
@Status(Status.OK)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String verifyToken(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@HeaderParam(HttpHeaders.AUTHORIZATION)
|
||||
String token) {
|
||||
E.checkArgument(StringUtils.isNotEmpty(token),
|
||||
"Request header Authorization must not be null");
|
||||
LOG.debug("Graph [{}] get user: {}", graph, token);
|
||||
|
||||
if (!token.startsWith(AuthenticationFilter.BEARER_TOKEN_PREFIX)) {
|
||||
throw new BadRequestException(
|
||||
"Only HTTP Bearer authentication is supported");
|
||||
}
|
||||
|
||||
token = token.substring(AuthenticationFilter.BEARER_TOKEN_PREFIX
|
||||
.length());
|
||||
UserWithRole userWithRole = manager.authManager().validateUser(token);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
return manager.serializer(g)
|
||||
.writeMap(ImmutableMap.of(AuthConstant.TOKEN_USER_NAME,
|
||||
userWithRole.username(),
|
||||
AuthConstant.TOKEN_USER_ID,
|
||||
userWithRole.userId()));
|
||||
}
|
||||
|
||||
private static class JsonLogin implements Checkable {
|
||||
|
||||
@JsonProperty("user_name")
|
||||
private String name;
|
||||
@JsonProperty("user_password")
|
||||
private String password;
|
||||
|
||||
@Override
|
||||
public void checkCreate(boolean isBatch) {
|
||||
E.checkArgument(!StringUtils.isEmpty(this.name),
|
||||
"The name of user can't be null");
|
||||
E.checkArgument(!StringUtils.isEmpty(this.password),
|
||||
"The password of user can't be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkUpdate() {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.auth;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.auth.HugeTarget;
|
||||
import org.apache.hugegraph.exception.NotFoundException;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.JsonUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/auth/targets")
|
||||
@Singleton
|
||||
@Tag(name = "TargetAPI")
|
||||
public class TargetAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(TargetAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String create(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
JsonTarget jsonTarget) {
|
||||
LOG.debug("Graph [{}] create target: {}", graph, jsonTarget);
|
||||
checkCreatingBody(jsonTarget);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeTarget target = jsonTarget.build();
|
||||
target.id(manager.authManager().createTarget(target));
|
||||
return manager.serializer(g).writeAuthElement(target);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String update(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id,
|
||||
JsonTarget jsonTarget) {
|
||||
LOG.debug("Graph [{}] update target: {}", graph, jsonTarget);
|
||||
checkUpdatingBody(jsonTarget);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeTarget target;
|
||||
try {
|
||||
target = manager.authManager().getTarget(UserAPI.parseId(id));
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException("Invalid target id: " + id);
|
||||
}
|
||||
target = jsonTarget.build(target);
|
||||
manager.authManager().updateTarget(target);
|
||||
return manager.serializer(g).writeAuthElement(target);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("limit") @DefaultValue("100") long limit) {
|
||||
LOG.debug("Graph [{}] list targets", graph);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
List<HugeTarget> targets = manager.authManager().listAllTargets(limit);
|
||||
return manager.serializer(g).writeAuthElements("targets", targets);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
LOG.debug("Graph [{}] get target: {}", graph, id);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeTarget target = manager.authManager().getTarget(UserAPI.parseId(id));
|
||||
return manager.serializer(g).writeAuthElement(target);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
public void delete(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
LOG.debug("Graph [{}] delete target: {}", graph, id);
|
||||
|
||||
@SuppressWarnings("unused") // just check if the graph exists
|
||||
HugeGraph g = graph(manager, graph);
|
||||
try {
|
||||
manager.authManager().deleteTarget(UserAPI.parseId(id));
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException("Invalid target id: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(value = {"id", "target_creator",
|
||||
"target_create", "target_update"})
|
||||
private static class JsonTarget implements Checkable {
|
||||
|
||||
@JsonProperty("target_name")
|
||||
private String name;
|
||||
@JsonProperty("target_graph")
|
||||
private String graph;
|
||||
@JsonProperty("target_url")
|
||||
private String url;
|
||||
@JsonProperty("target_resources") // error when List<HugeResource>
|
||||
private List<Map<String, Object>> resources;
|
||||
|
||||
public HugeTarget build(HugeTarget target) {
|
||||
E.checkArgument(this.name == null ||
|
||||
target.name().equals(this.name),
|
||||
"The name of target can't be updated");
|
||||
E.checkArgument(this.graph == null ||
|
||||
target.graph().equals(this.graph),
|
||||
"The graph of target can't be updated");
|
||||
if (this.url != null) {
|
||||
target.url(this.url);
|
||||
}
|
||||
if (this.resources != null) {
|
||||
target.resources(JsonUtil.toJson(this.resources));
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
public HugeTarget build() {
|
||||
HugeTarget target = new HugeTarget(this.name, this.graph, this.url);
|
||||
if (this.resources != null) {
|
||||
target.resources(JsonUtil.toJson(this.resources));
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCreate(boolean isBatch) {
|
||||
E.checkArgumentNotNull(this.name,
|
||||
"The name of target can't be null");
|
||||
E.checkArgumentNotNull(this.graph,
|
||||
"The graph of target can't be null");
|
||||
E.checkArgumentNotNull(this.url,
|
||||
"The url of target can't be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkUpdate() {
|
||||
E.checkArgument(this.url != null ||
|
||||
this.resources != null,
|
||||
"Expect one of target url/resources");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,234 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.auth;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.auth.HugeUser;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.id.IdGenerator;
|
||||
import org.apache.hugegraph.exception.NotFoundException;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.hugegraph.util.StringEncoding;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/auth/users")
|
||||
@Singleton
|
||||
@Tag(name = "UserAPI")
|
||||
public class UserAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(UserAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String create(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
JsonUser jsonUser) {
|
||||
LOG.debug("Graph [{}] create user: {}", graph, jsonUser);
|
||||
checkCreatingBody(jsonUser);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeUser user = jsonUser.build();
|
||||
user.id(manager.authManager().createUser(user));
|
||||
return manager.serializer(g).writeAuthElement(user);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String update(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id,
|
||||
JsonUser jsonUser) {
|
||||
LOG.debug("Graph [{}] update user: {}", graph, jsonUser);
|
||||
checkUpdatingBody(jsonUser);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeUser user;
|
||||
try {
|
||||
user = manager.authManager().getUser(UserAPI.parseId(id));
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException("Invalid user id: " + id);
|
||||
}
|
||||
user = jsonUser.build(user);
|
||||
manager.authManager().updateUser(user);
|
||||
return manager.serializer(g).writeAuthElement(user);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("limit") @DefaultValue("100") long limit) {
|
||||
LOG.debug("Graph [{}] list users", graph);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
List<HugeUser> users = manager.authManager().listAllUsers(limit);
|
||||
return manager.serializer(g).writeAuthElements("users", users);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
LOG.debug("Graph [{}] get user: {}", graph, id);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeUser user = manager.authManager().getUser(IdGenerator.of(id));
|
||||
return manager.serializer(g).writeAuthElement(user);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{id}/role")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String role(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
LOG.debug("Graph [{}] get user role: {}", graph, id);
|
||||
|
||||
@SuppressWarnings("unused") // just check if the graph exists
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeUser user = manager.authManager().getUser(IdGenerator.of(id));
|
||||
return manager.authManager().rolePermission(user).toJson();
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
public void delete(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
LOG.debug("Graph [{}] delete user: {}", graph, id);
|
||||
|
||||
@SuppressWarnings("unused") // just check if the graph exists
|
||||
HugeGraph g = graph(manager, graph);
|
||||
try {
|
||||
manager.authManager().deleteUser(IdGenerator.of(id));
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException("Invalid user id: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
protected static Id parseId(String id) {
|
||||
return IdGenerator.of(id);
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(value = {"id", "user_creator",
|
||||
"user_create", "user_update"})
|
||||
private static class JsonUser implements Checkable {
|
||||
|
||||
@JsonProperty("user_name")
|
||||
private String name;
|
||||
@JsonProperty("user_password")
|
||||
private String password;
|
||||
@JsonProperty("user_phone")
|
||||
private String phone;
|
||||
@JsonProperty("user_email")
|
||||
private String email;
|
||||
@JsonProperty("user_avatar")
|
||||
private String avatar;
|
||||
@JsonProperty("user_description")
|
||||
private String description;
|
||||
|
||||
public HugeUser build(HugeUser user) {
|
||||
E.checkArgument(this.name == null || user.name().equals(this.name),
|
||||
"The name of user can't be updated");
|
||||
if (this.password != null) {
|
||||
user.password(StringEncoding.hashPassword(this.password));
|
||||
}
|
||||
if (this.phone != null) {
|
||||
user.phone(this.phone);
|
||||
}
|
||||
if (this.email != null) {
|
||||
user.email(this.email);
|
||||
}
|
||||
if (this.avatar != null) {
|
||||
user.avatar(this.avatar);
|
||||
}
|
||||
if (this.description != null) {
|
||||
user.description(this.description);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public HugeUser build() {
|
||||
HugeUser user = new HugeUser(this.name);
|
||||
user.password(StringEncoding.hashPassword(this.password));
|
||||
user.phone(this.phone);
|
||||
user.email(this.email);
|
||||
user.avatar(this.avatar);
|
||||
user.description(this.description);
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCreate(boolean isBatch) {
|
||||
E.checkArgument(!StringUtils.isEmpty(this.name),
|
||||
"The name of user can't be null");
|
||||
E.checkArgument(!StringUtils.isEmpty(this.password),
|
||||
"The password of user can't be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkUpdate() {
|
||||
E.checkArgument(!StringUtils.isEmpty(this.password) ||
|
||||
this.phone != null ||
|
||||
this.email != null ||
|
||||
this.avatar != null,
|
||||
"Expect one of user password/phone/email/avatar]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,291 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.annotation.Priority;
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import jakarta.ws.rs.NotAuthorizedException;
|
||||
import jakarta.ws.rs.Priorities;
|
||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||
import jakarta.ws.rs.container.ContainerRequestFilter;
|
||||
import jakarta.ws.rs.container.PreMatching;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.SecurityContext;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.tinkerpop.gremlin.server.auth.AuthenticationException;
|
||||
import org.glassfish.grizzly.http.server.Request;
|
||||
import org.glassfish.grizzly.utils.Charsets;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.auth.HugeAuthenticator;
|
||||
import org.apache.hugegraph.auth.HugeAuthenticator.RequiredPerm;
|
||||
import org.apache.hugegraph.auth.HugeAuthenticator.RolePerm;
|
||||
import org.apache.hugegraph.auth.HugeAuthenticator.User;
|
||||
import org.apache.hugegraph.auth.RolePermission;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@Provider
|
||||
@PreMatching
|
||||
@Priority(Priorities.AUTHENTICATION)
|
||||
public class AuthenticationFilter implements ContainerRequestFilter {
|
||||
|
||||
public static final String BASIC_AUTH_PREFIX = "Basic ";
|
||||
public static final String BEARER_TOKEN_PREFIX = "Bearer ";
|
||||
|
||||
private static final Logger LOG = Log.logger(AuthenticationFilter.class);
|
||||
|
||||
private static final List<String> WHITE_API_LIST = ImmutableList.of(
|
||||
"auth/login",
|
||||
"versions"
|
||||
);
|
||||
|
||||
@Context
|
||||
private jakarta.inject.Provider<GraphManager> managerProvider;
|
||||
|
||||
@Context
|
||||
private jakarta.inject.Provider<Request> requestProvider;
|
||||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext context) throws IOException {
|
||||
if (AuthenticationFilter.isWhiteAPI(context)) {
|
||||
return;
|
||||
}
|
||||
User user = this.authenticate(context);
|
||||
Authorizer authorizer = new Authorizer(user, context.getUriInfo());
|
||||
context.setSecurityContext(authorizer);
|
||||
}
|
||||
|
||||
protected User authenticate(ContainerRequestContext context) {
|
||||
GraphManager manager = this.managerProvider.get();
|
||||
E.checkState(manager != null, "Context GraphManager is absent");
|
||||
|
||||
if (!manager.requireAuthentication()) {
|
||||
// Return anonymous user with admin role if disable authentication
|
||||
return User.ANONYMOUS;
|
||||
}
|
||||
|
||||
// Get peer info
|
||||
Request request = this.requestProvider.get();
|
||||
String peer = null;
|
||||
String path = null;
|
||||
if (request != null) {
|
||||
peer = request.getRemoteAddr() + ":" + request.getRemotePort();
|
||||
path = request.getRequestURI();
|
||||
}
|
||||
|
||||
Map<String, String> credentials = new HashMap<>();
|
||||
// Extract authentication credentials
|
||||
String auth = context.getHeaderString(HttpHeaders.AUTHORIZATION);
|
||||
if (auth == null) {
|
||||
throw new NotAuthorizedException(
|
||||
"Authentication credentials are required",
|
||||
"Missing authentication credentials");
|
||||
}
|
||||
|
||||
if (auth.startsWith(BASIC_AUTH_PREFIX)) {
|
||||
auth = auth.substring(BASIC_AUTH_PREFIX.length());
|
||||
auth = new String(DatatypeConverter.parseBase64Binary(auth),
|
||||
Charsets.ASCII_CHARSET);
|
||||
String[] values = auth.split(":");
|
||||
if (values.length != 2) {
|
||||
throw new BadRequestException(
|
||||
"Invalid syntax for username and password");
|
||||
}
|
||||
|
||||
final String username = values[0];
|
||||
final String password = values[1];
|
||||
|
||||
if (StringUtils.isEmpty(username) ||
|
||||
StringUtils.isEmpty(password)) {
|
||||
throw new BadRequestException(
|
||||
"Invalid syntax for username and password");
|
||||
}
|
||||
|
||||
credentials.put(HugeAuthenticator.KEY_USERNAME, username);
|
||||
credentials.put(HugeAuthenticator.KEY_PASSWORD, password);
|
||||
} else if (auth.startsWith(BEARER_TOKEN_PREFIX)) {
|
||||
String token = auth.substring(BEARER_TOKEN_PREFIX.length());
|
||||
credentials.put(HugeAuthenticator.KEY_TOKEN, token);
|
||||
} else {
|
||||
throw new BadRequestException(
|
||||
"Only HTTP Basic or Bearer authentication is supported");
|
||||
}
|
||||
|
||||
credentials.put(HugeAuthenticator.KEY_ADDRESS, peer);
|
||||
credentials.put(HugeAuthenticator.KEY_PATH, path);
|
||||
|
||||
// Validate the extracted credentials
|
||||
try {
|
||||
return manager.authenticate(credentials);
|
||||
} catch (AuthenticationException e) {
|
||||
throw new NotAuthorizedException("Authentication failed",
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static class Authorizer implements SecurityContext {
|
||||
|
||||
private final UriInfo uri;
|
||||
private final User user;
|
||||
private final Principal principal;
|
||||
|
||||
public Authorizer(final User user, final UriInfo uri) {
|
||||
E.checkNotNull(user, "user");
|
||||
E.checkNotNull(uri, "uri");
|
||||
this.uri = uri;
|
||||
this.user = user;
|
||||
this.principal = new UserPrincipal();
|
||||
}
|
||||
|
||||
public String username() {
|
||||
return this.user.username();
|
||||
}
|
||||
|
||||
public RolePermission role() {
|
||||
return this.user.role();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getUserPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserInRole(String required) {
|
||||
if (required.equals(HugeAuthenticator.KEY_DYNAMIC)) {
|
||||
// Let the resource itself determine dynamically
|
||||
return true;
|
||||
} else {
|
||||
return this.matchPermission(required);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return "https".equals(this.uri.getRequestUri().getScheme());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticationScheme() {
|
||||
return SecurityContext.BASIC_AUTH;
|
||||
}
|
||||
|
||||
private boolean matchPermission(String required) {
|
||||
boolean valid;
|
||||
RequiredPerm requiredPerm;
|
||||
|
||||
if (!required.startsWith(HugeAuthenticator.KEY_OWNER)) {
|
||||
// Permission format like: "admin"
|
||||
requiredPerm = new RequiredPerm();
|
||||
requiredPerm.owner(required);
|
||||
} else {
|
||||
// The required like: $owner=graph1 $action=vertex_write
|
||||
requiredPerm = RequiredPerm.fromPermission(required);
|
||||
|
||||
/*
|
||||
* Replace owner value(it may be a variable) if the permission
|
||||
* format like: "$owner=$graph $action=vertex_write"
|
||||
*/
|
||||
String owner = requiredPerm.owner();
|
||||
if (owner.startsWith(HugeAuthenticator.VAR_PREFIX)) {
|
||||
// Replace `$graph` with graph name like "graph1"
|
||||
int prefixLen = HugeAuthenticator.VAR_PREFIX.length();
|
||||
assert owner.length() > prefixLen;
|
||||
owner = owner.substring(prefixLen);
|
||||
owner = this.getPathParameter(owner);
|
||||
requiredPerm.owner(owner);
|
||||
}
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Verify permission {} {} for user '{}' with role {}",
|
||||
requiredPerm.action().string(),
|
||||
requiredPerm.resourceObject(),
|
||||
this.user.username(), this.user.role());
|
||||
}
|
||||
|
||||
// verify role permission
|
||||
valid = RolePerm.match(this.role(), requiredPerm);
|
||||
|
||||
if (!valid && LOG.isInfoEnabled() &&
|
||||
!required.equals(HugeAuthenticator.USER_ADMIN)) {
|
||||
LOG.info("User '{}' is denied to {} {}",
|
||||
this.user.username(), requiredPerm.action().string(),
|
||||
requiredPerm.resourceObject());
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
private String getPathParameter(String key) {
|
||||
List<String> params = this.uri.getPathParameters().get(key);
|
||||
E.checkState(params != null && params.size() == 1,
|
||||
"There is no matched path parameter: '%s'", key);
|
||||
return params.get(0);
|
||||
}
|
||||
|
||||
private final class UserPrincipal implements Principal {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return Authorizer.this.user.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Authorizer.this.user.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Authorizer.this.user.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return Authorizer.this.user.equals(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isWhiteAPI(ContainerRequestContext context) {
|
||||
String path = context.getUriInfo().getPath();
|
||||
|
||||
for (String whiteApi : WHITE_API_LIST) {
|
||||
if (path.endsWith(whiteApi)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.NameBinding;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
import jakarta.ws.rs.ext.ReaderInterceptor;
|
||||
import jakarta.ws.rs.ext.ReaderInterceptorContext;
|
||||
|
||||
@Provider
|
||||
@Singleton
|
||||
@DecompressInterceptor.Decompress
|
||||
public class DecompressInterceptor implements ReaderInterceptor {
|
||||
|
||||
public static final String GZIP = "gzip";
|
||||
|
||||
@Override
|
||||
public Object aroundReadFrom(ReaderInterceptorContext context)
|
||||
throws IOException {
|
||||
// NOTE: Currently we just support GZIP
|
||||
String encoding = context.getHeaders().getFirst("Content-Encoding");
|
||||
if (!GZIP.equalsIgnoreCase(encoding)) {
|
||||
return context.proceed();
|
||||
}
|
||||
context.setInputStream(new GZIPInputStream(context.getInputStream()));
|
||||
return context.proceed();
|
||||
}
|
||||
|
||||
|
||||
@NameBinding
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Decompress {
|
||||
String value() default GZIP;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.filter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.ServiceUnavailableException;
|
||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||
import jakarta.ws.rs.container.ContainerRequestFilter;
|
||||
import jakarta.ws.rs.container.PreMatching;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.PathSegment;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
|
||||
import org.apache.hugegraph.config.HugeConfig;
|
||||
import org.apache.hugegraph.config.ServerOptions;
|
||||
import org.apache.hugegraph.define.WorkLoad;
|
||||
import org.apache.hugegraph.util.Bytes;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
|
||||
@Provider
|
||||
@Singleton
|
||||
@PreMatching
|
||||
public class LoadDetectFilter implements ContainerRequestFilter {
|
||||
|
||||
private static final Set<String> WHITE_API_LIST = ImmutableSet.of(
|
||||
"",
|
||||
"apis",
|
||||
"metrics",
|
||||
"versions"
|
||||
);
|
||||
|
||||
// Call gc every 30+ seconds if memory is low and request frequently
|
||||
private static final RateLimiter GC_RATE_LIMITER =
|
||||
RateLimiter.create(1.0 / 30);
|
||||
|
||||
@Context
|
||||
private jakarta.inject.Provider<HugeConfig> configProvider;
|
||||
@Context
|
||||
private jakarta.inject.Provider<WorkLoad> loadProvider;
|
||||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext context) {
|
||||
if (LoadDetectFilter.isWhiteAPI(context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
HugeConfig config = this.configProvider.get();
|
||||
|
||||
int maxWorkerThreads = config.get(ServerOptions.MAX_WORKER_THREADS);
|
||||
WorkLoad load = this.loadProvider.get();
|
||||
// There will be a thread doesn't work, dedicated to statistics
|
||||
if (load.incrementAndGet() >= maxWorkerThreads) {
|
||||
throw new ServiceUnavailableException(String.format(
|
||||
"The server is too busy to process the request, " +
|
||||
"you can config %s to adjust it or try again later",
|
||||
ServerOptions.MAX_WORKER_THREADS.name()));
|
||||
}
|
||||
|
||||
long minFreeMemory = config.get(ServerOptions.MIN_FREE_MEMORY);
|
||||
long allocatedMem = Runtime.getRuntime().totalMemory() -
|
||||
Runtime.getRuntime().freeMemory();
|
||||
long presumableFreeMem = (Runtime.getRuntime().maxMemory() -
|
||||
allocatedMem) / Bytes.MB;
|
||||
if (presumableFreeMem < minFreeMemory) {
|
||||
gcIfNeeded();
|
||||
throw new ServiceUnavailableException(String.format(
|
||||
"The server available memory %s(MB) is below than " +
|
||||
"threshold %s(MB) and can't process the request, " +
|
||||
"you can config %s to adjust it or try again later",
|
||||
presumableFreeMem, minFreeMemory,
|
||||
ServerOptions.MIN_FREE_MEMORY.name()));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isWhiteAPI(ContainerRequestContext context) {
|
||||
List<PathSegment> segments = context.getUriInfo().getPathSegments();
|
||||
E.checkArgument(segments.size() > 0, "Invalid request uri '%s'",
|
||||
context.getUriInfo().getPath());
|
||||
String rootPath = segments.get(0).getPath();
|
||||
return WHITE_API_LIST.contains(rootPath);
|
||||
}
|
||||
|
||||
private static void gcIfNeeded() {
|
||||
if (GC_RATE_LIMITER.tryAcquire(1)) {
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.filter;
|
||||
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||
import jakarta.ws.rs.container.ContainerResponseContext;
|
||||
import jakarta.ws.rs.container.ContainerResponseFilter;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
|
||||
import org.apache.hugegraph.define.WorkLoad;
|
||||
|
||||
@Provider
|
||||
@Singleton
|
||||
public class LoadReleaseFilter implements ContainerResponseFilter {
|
||||
|
||||
@Context
|
||||
private jakarta.inject.Provider<WorkLoad> loadProvider;
|
||||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext requestContext,
|
||||
ContainerResponseContext responseContext) {
|
||||
if (LoadDetectFilter.isWhiteAPI(requestContext)) {
|
||||
return;
|
||||
}
|
||||
|
||||
WorkLoad load = this.loadProvider.get();
|
||||
load.decrementAndGet();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import jakarta.ws.rs.NameBinding;
|
||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||
import jakarta.ws.rs.container.ContainerResponseContext;
|
||||
import jakarta.ws.rs.container.ContainerResponseFilter;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
|
||||
@Provider
|
||||
public class StatusFilter implements ContainerResponseFilter {
|
||||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext requestContext,
|
||||
ContainerResponseContext responseContext)
|
||||
throws IOException {
|
||||
if (responseContext.getStatus() == 200) {
|
||||
for (Annotation i : responseContext.getEntityAnnotations()) {
|
||||
if (i instanceof Status) {
|
||||
responseContext.setStatus(((Status) i).value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NameBinding
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Status {
|
||||
int OK = 200;
|
||||
int CREATED = 201;
|
||||
int ACCEPTED = 202;
|
||||
|
||||
int value();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,574 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.graph;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
|
||||
import org.apache.tinkerpop.gremlin.structure.Direction;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.tinkerpop.gremlin.util.function.TriFunction;
|
||||
import org.apache.hugegraph.config.ServerOptions;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.UpdateStrategy;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.CompressInterceptor.Compress;
|
||||
import org.apache.hugegraph.api.filter.DecompressInterceptor.Decompress;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.backend.id.EdgeId;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.ConditionQuery;
|
||||
import org.apache.hugegraph.config.HugeConfig;
|
||||
import org.apache.hugegraph.exception.NotFoundException;
|
||||
import org.apache.hugegraph.schema.EdgeLabel;
|
||||
import org.apache.hugegraph.schema.PropertyKey;
|
||||
import org.apache.hugegraph.schema.VertexLabel;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.traversal.optimize.QueryHolder;
|
||||
import org.apache.hugegraph.traversal.optimize.TraversalUtil;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/graph/edges")
|
||||
@Singleton
|
||||
@Tag(name = "EdgeAPI")
|
||||
public class EdgeAPI extends BatchAPI {
|
||||
|
||||
private static final Logger LOG = Log.logger(EdgeAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed(name = "single-create")
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=edge_write"})
|
||||
public String create(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
JsonEdge jsonEdge) {
|
||||
LOG.debug("Graph [{}] create edge: {}", graph, jsonEdge);
|
||||
checkCreatingBody(jsonEdge);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
||||
if (jsonEdge.sourceLabel != null && jsonEdge.targetLabel != null) {
|
||||
/*
|
||||
* NOTE: If the vertex id is correct but label not match with id,
|
||||
* we allow to create it here
|
||||
*/
|
||||
vertexLabel(g, jsonEdge.sourceLabel,
|
||||
"Invalid source vertex label '%s'");
|
||||
vertexLabel(g, jsonEdge.targetLabel,
|
||||
"Invalid target vertex label '%s'");
|
||||
}
|
||||
|
||||
Vertex srcVertex = getVertex(g, jsonEdge.source, jsonEdge.sourceLabel);
|
||||
Vertex tgtVertex = getVertex(g, jsonEdge.target, jsonEdge.targetLabel);
|
||||
|
||||
Edge edge = commit(g, () -> {
|
||||
return srcVertex.addEdge(jsonEdge.label, tgtVertex,
|
||||
jsonEdge.properties());
|
||||
});
|
||||
|
||||
return manager.serializer(g).writeEdge(edge);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed(name = "batch-create")
|
||||
@Decompress
|
||||
@Path("batch")
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=edge_write"})
|
||||
public String create(@Context HugeConfig config,
|
||||
@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("check_vertex")
|
||||
@DefaultValue("true") boolean checkVertex,
|
||||
List<JsonEdge> jsonEdges) {
|
||||
LOG.debug("Graph [{}] create edges: {}", graph, jsonEdges);
|
||||
checkCreatingBody(jsonEdges);
|
||||
checkBatchSize(config, jsonEdges);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
||||
TriFunction<HugeGraph, Object, String, Vertex> getVertex =
|
||||
checkVertex ? EdgeAPI::getVertex : EdgeAPI::newVertex;
|
||||
|
||||
return this.commit(config, g, jsonEdges.size(), () -> {
|
||||
List<Id> ids = new ArrayList<>(jsonEdges.size());
|
||||
for (JsonEdge jsonEdge : jsonEdges) {
|
||||
/*
|
||||
* NOTE: If the query param 'checkVertex' is false,
|
||||
* then the label is correct and not matched id,
|
||||
* it will be allowed currently
|
||||
*/
|
||||
Vertex srcVertex = getVertex.apply(g, jsonEdge.source,
|
||||
jsonEdge.sourceLabel);
|
||||
Vertex tgtVertex = getVertex.apply(g, jsonEdge.target,
|
||||
jsonEdge.targetLabel);
|
||||
Edge edge = srcVertex.addEdge(jsonEdge.label, tgtVertex,
|
||||
jsonEdge.properties());
|
||||
ids.add((Id) edge.id());
|
||||
}
|
||||
return manager.serializer(g).writeIds(ids);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch update steps are same like vertices
|
||||
*/
|
||||
@PUT
|
||||
@Timed(name = "batch-update")
|
||||
@Decompress
|
||||
@Path("batch")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=edge_write"})
|
||||
public String update(@Context HugeConfig config,
|
||||
@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
BatchEdgeRequest req) {
|
||||
BatchEdgeRequest.checkUpdate(req);
|
||||
LOG.debug("Graph [{}] update edges: {}", graph, req);
|
||||
checkUpdatingBody(req.jsonEdges);
|
||||
checkBatchSize(config, req.jsonEdges);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Map<Id, JsonEdge> map = new HashMap<>(req.jsonEdges.size());
|
||||
TriFunction<HugeGraph, Object, String, Vertex> getVertex =
|
||||
req.checkVertex ? EdgeAPI::getVertex : EdgeAPI::newVertex;
|
||||
|
||||
return this.commit(config, g, map.size(), () -> {
|
||||
// 1.Put all newEdges' properties into map (combine first)
|
||||
req.jsonEdges.forEach(newEdge -> {
|
||||
Id newEdgeId = getEdgeId(graph(manager, graph), newEdge);
|
||||
JsonEdge oldEdge = map.get(newEdgeId);
|
||||
this.updateExistElement(oldEdge, newEdge,
|
||||
req.updateStrategies);
|
||||
map.put(newEdgeId, newEdge);
|
||||
});
|
||||
|
||||
// 2.Get all oldEdges and update with new ones
|
||||
Object[] ids = map.keySet().toArray();
|
||||
Iterator<Edge> oldEdges = g.edges(ids);
|
||||
oldEdges.forEachRemaining(oldEdge -> {
|
||||
JsonEdge newEdge = map.get(oldEdge.id());
|
||||
this.updateExistElement(g, oldEdge, newEdge,
|
||||
req.updateStrategies);
|
||||
});
|
||||
|
||||
// 3.Add all finalEdges
|
||||
List<Edge> edges = new ArrayList<>(map.size());
|
||||
map.values().forEach(finalEdge -> {
|
||||
Vertex srcVertex = getVertex.apply(g, finalEdge.source,
|
||||
finalEdge.sourceLabel);
|
||||
Vertex tgtVertex = getVertex.apply(g, finalEdge.target,
|
||||
finalEdge.targetLabel);
|
||||
edges.add(srcVertex.addEdge(finalEdge.label, tgtVertex,
|
||||
finalEdge.properties()));
|
||||
});
|
||||
|
||||
// If return ids, the ids.size() maybe different with the origins'
|
||||
return manager.serializer(g).writeEdges(edges.iterator(), false);
|
||||
});
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed(name = "single-update")
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=edge_write"})
|
||||
public String update(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id,
|
||||
@QueryParam("action") String action,
|
||||
JsonEdge jsonEdge) {
|
||||
LOG.debug("Graph [{}] update edge: {}", graph, jsonEdge);
|
||||
checkUpdatingBody(jsonEdge);
|
||||
|
||||
if (jsonEdge.id != null) {
|
||||
E.checkArgument(id.equals(jsonEdge.id),
|
||||
"The ids are different between url and " +
|
||||
"request body ('%s' != '%s')", id, jsonEdge.id);
|
||||
}
|
||||
|
||||
// Parse action param
|
||||
boolean append = checkAndParseAction(action);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeEdge edge = (HugeEdge) g.edge(id);
|
||||
EdgeLabel edgeLabel = edge.schemaLabel();
|
||||
|
||||
for (String key : jsonEdge.properties.keySet()) {
|
||||
PropertyKey pkey = g.propertyKey(key);
|
||||
E.checkArgument(edgeLabel.properties().contains(pkey.id()),
|
||||
"Can't update property for edge '%s' because " +
|
||||
"there is no property key '%s' in its edge label",
|
||||
id, key);
|
||||
}
|
||||
|
||||
commit(g, () -> updateProperties(edge, jsonEdge, append));
|
||||
|
||||
return manager.serializer(g).writeEdge(edge);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Compress
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=edge_read"})
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("vertex_id") String vertexId,
|
||||
@QueryParam("direction") String direction,
|
||||
@QueryParam("label") String label,
|
||||
@QueryParam("properties") String properties,
|
||||
@QueryParam("keep_start_p")
|
||||
@DefaultValue("false") boolean keepStartP,
|
||||
@QueryParam("offset") @DefaultValue("0") long offset,
|
||||
@QueryParam("page") String page,
|
||||
@QueryParam("limit") @DefaultValue("100") long limit) {
|
||||
LOG.debug("Graph [{}] query edges by vertex: {}, direction: {}, " +
|
||||
"label: {}, properties: {}, offset: {}, page: {}, limit: {}",
|
||||
graph, vertexId, direction,
|
||||
label, properties, offset, page, limit);
|
||||
|
||||
Map<String, Object> props = parseProperties(properties);
|
||||
if (page != null) {
|
||||
E.checkArgument(offset == 0,
|
||||
"Not support querying edges based on paging " +
|
||||
"and offset together");
|
||||
}
|
||||
|
||||
Id vertex = VertexAPI.checkAndParseVertexId(vertexId);
|
||||
Direction dir = parseDirection(direction);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
||||
GraphTraversal<?, Edge> traversal;
|
||||
if (vertex != null) {
|
||||
if (label != null) {
|
||||
traversal = g.traversal().V(vertex).toE(dir, label);
|
||||
} else {
|
||||
traversal = g.traversal().V(vertex).toE(dir);
|
||||
}
|
||||
} else {
|
||||
if (label != null) {
|
||||
traversal = g.traversal().E().hasLabel(label);
|
||||
} else {
|
||||
traversal = g.traversal().E();
|
||||
}
|
||||
}
|
||||
|
||||
// Convert relational operator like P.gt()/P.lt()
|
||||
for (Map.Entry<String, Object> prop : props.entrySet()) {
|
||||
Object value = prop.getValue();
|
||||
if (!keepStartP && value instanceof String &&
|
||||
((String) value).startsWith(TraversalUtil.P_CALL)) {
|
||||
prop.setValue(TraversalUtil.parsePredicate((String) value));
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Object> entry : props.entrySet()) {
|
||||
traversal = traversal.has(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
if (page == null) {
|
||||
traversal = traversal.range(offset, offset + limit);
|
||||
} else {
|
||||
traversal = traversal.has(QueryHolder.SYSPROP_PAGE, page)
|
||||
.limit(limit);
|
||||
}
|
||||
|
||||
try {
|
||||
return manager.serializer(g).writeEdges(traversal, page != null);
|
||||
} finally {
|
||||
if (g.tx().isOpen()) {
|
||||
g.tx().close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=edge_read"})
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
LOG.debug("Graph [{}] get edge by id '{}'", graph, id);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
try {
|
||||
Edge edge = g.edge(id);
|
||||
return manager.serializer(g).writeEdge(edge);
|
||||
} finally {
|
||||
if (g.tx().isOpen()) {
|
||||
g.tx().close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=edge_delete"})
|
||||
public void delete(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id,
|
||||
@QueryParam("label") String label) {
|
||||
LOG.debug("Graph [{}] remove vertex by id '{}'", graph, id);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
commit(g, () -> {
|
||||
try {
|
||||
g.removeEdge(label, id);
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"No such edge with id: '%s', %s", id, e));
|
||||
} catch (NoSuchElementException e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"No such edge with id: '%s'", id));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void checkBatchSize(HugeConfig config,
|
||||
List<JsonEdge> edges) {
|
||||
int max = config.get(ServerOptions.MAX_EDGES_PER_BATCH);
|
||||
if (edges.size() > max) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Too many edges for one time post, " +
|
||||
"the maximum number is '%s'", max));
|
||||
}
|
||||
if (edges.size() == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The number of edges can't be 0");
|
||||
}
|
||||
}
|
||||
|
||||
private static Vertex getVertex(HugeGraph graph,
|
||||
Object id, String label) {
|
||||
HugeVertex vertex;
|
||||
try {
|
||||
vertex = (HugeVertex) graph.vertices(id).next();
|
||||
} catch (NoSuchElementException e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Invalid vertex id '%s'", id));
|
||||
}
|
||||
if (label != null && !vertex.label().equals(label)) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"The label of vertex '%s' is unmatched, users expect " +
|
||||
"label '%s', actual label stored is '%s'",
|
||||
id, label, vertex.label()));
|
||||
}
|
||||
// Clone a new vertex to support multi-thread access
|
||||
return vertex.copy();
|
||||
}
|
||||
|
||||
private static Vertex newVertex(HugeGraph g, Object id, String label) {
|
||||
VertexLabel vl = vertexLabel(g, label, "Invalid vertex label '%s'");
|
||||
Id idValue = HugeVertex.getIdValue(id);
|
||||
return new HugeVertex(g, idValue, vl);
|
||||
}
|
||||
|
||||
private static VertexLabel vertexLabel(HugeGraph graph, String label,
|
||||
String message) {
|
||||
try {
|
||||
// NOTE: don't use SchemaManager because it will throw 404
|
||||
return graph.vertexLabel(label);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(String.format(message, label));
|
||||
}
|
||||
}
|
||||
|
||||
public static Direction parseDirection(String direction) {
|
||||
if (direction == null || direction.isEmpty()) {
|
||||
return Direction.BOTH;
|
||||
}
|
||||
try {
|
||||
return Direction.valueOf(direction);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Direction value must be in [OUT, IN, BOTH], " +
|
||||
"but got '%s'", direction));
|
||||
}
|
||||
}
|
||||
|
||||
private Id getEdgeId(HugeGraph g, JsonEdge newEdge) {
|
||||
String sortKeys = "";
|
||||
Id labelId = g.edgeLabel(newEdge.label).id();
|
||||
List<Id> sortKeyIds = g.edgeLabel(labelId).sortKeys();
|
||||
if (!sortKeyIds.isEmpty()) {
|
||||
List<Object> sortKeyValues = new ArrayList<>(sortKeyIds.size());
|
||||
sortKeyIds.forEach(skId -> {
|
||||
PropertyKey pk = g.propertyKey(skId);
|
||||
String sortKey = pk.name();
|
||||
Object sortKeyValue = newEdge.properties.get(sortKey);
|
||||
E.checkArgument(sortKeyValue != null,
|
||||
"The value of sort key '%s' can't be null",
|
||||
sortKey);
|
||||
sortKeyValue = pk.validValueOrThrow(sortKeyValue);
|
||||
sortKeyValues.add(sortKeyValue);
|
||||
});
|
||||
sortKeys = ConditionQuery.concatValues(sortKeyValues);
|
||||
}
|
||||
EdgeId edgeId = new EdgeId(HugeVertex.getIdValue(newEdge.source),
|
||||
Directions.OUT, labelId, sortKeys,
|
||||
HugeVertex.getIdValue(newEdge.target));
|
||||
if (newEdge.id != null) {
|
||||
E.checkArgument(edgeId.asString().equals(newEdge.id),
|
||||
"The ids are different between server and " +
|
||||
"request body ('%s' != '%s'). And note the sort " +
|
||||
"key values should either be null or equal to " +
|
||||
"the origin value when specified edge id",
|
||||
edgeId, newEdge.id);
|
||||
}
|
||||
return edgeId;
|
||||
}
|
||||
|
||||
protected static class BatchEdgeRequest {
|
||||
|
||||
@JsonProperty("edges")
|
||||
public List<JsonEdge> jsonEdges;
|
||||
@JsonProperty("update_strategies")
|
||||
public Map<String, UpdateStrategy> updateStrategies;
|
||||
@JsonProperty("check_vertex")
|
||||
public boolean checkVertex = false;
|
||||
@JsonProperty("create_if_not_exist")
|
||||
public boolean createIfNotExist = true;
|
||||
|
||||
private static void checkUpdate(BatchEdgeRequest req) {
|
||||
E.checkArgumentNotNull(req, "BatchEdgeRequest can't be null");
|
||||
E.checkArgumentNotNull(req.jsonEdges,
|
||||
"Parameter 'edges' can't be null");
|
||||
E.checkArgument(req.updateStrategies != null &&
|
||||
!req.updateStrategies.isEmpty(),
|
||||
"Parameter 'update_strategies' can't be empty");
|
||||
E.checkArgument(req.createIfNotExist == true,
|
||||
"Parameter 'create_if_not_exist' " +
|
||||
"dose not support false now");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("BatchEdgeRequest{jsonEdges=%s," +
|
||||
"updateStrategies=%s," +
|
||||
"checkVertex=%s,createIfNotExist=%s}",
|
||||
this.jsonEdges, this.updateStrategies,
|
||||
this.checkVertex, this.createIfNotExist);
|
||||
}
|
||||
}
|
||||
|
||||
private static class JsonEdge extends JsonElement {
|
||||
|
||||
@JsonProperty("outV")
|
||||
public Object source;
|
||||
@JsonProperty("outVLabel")
|
||||
public String sourceLabel;
|
||||
@JsonProperty("inV")
|
||||
public Object target;
|
||||
@JsonProperty("inVLabel")
|
||||
public String targetLabel;
|
||||
|
||||
@Override
|
||||
public void checkCreate(boolean isBatch) {
|
||||
E.checkArgumentNotNull(this.label, "Expect the label of edge");
|
||||
E.checkArgumentNotNull(this.source, "Expect source vertex id");
|
||||
E.checkArgumentNotNull(this.target, "Expect target vertex id");
|
||||
if (isBatch) {
|
||||
E.checkArgumentNotNull(this.sourceLabel,
|
||||
"Expect source vertex label");
|
||||
E.checkArgumentNotNull(this.targetLabel,
|
||||
"Expect target vertex label");
|
||||
} else {
|
||||
E.checkArgument(this.sourceLabel == null &&
|
||||
this.targetLabel == null ||
|
||||
this.sourceLabel != null &&
|
||||
this.targetLabel != null,
|
||||
"The both source and target vertex label " +
|
||||
"are either passed in, or not passed in");
|
||||
}
|
||||
this.checkUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkUpdate() {
|
||||
E.checkArgumentNotNull(this.properties,
|
||||
"The properties of edge can't be null");
|
||||
|
||||
for (Map.Entry<String, Object> entry : this.properties.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
E.checkArgumentNotNull(value, "Not allowed to set value of " +
|
||||
"property '%s' to null for edge '%s'",
|
||||
key, this.id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] properties() {
|
||||
return API.properties(this.properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("JsonEdge{label=%s, " +
|
||||
"source-vertex=%s, source-vertex-label=%s, " +
|
||||
"target-vertex=%s, target-vertex-label=%s, " +
|
||||
"properties=%s}",
|
||||
this.label, this.source, this.sourceLabel,
|
||||
this.target, this.targetLabel,
|
||||
this.properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,479 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.graph;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
|
||||
import org.apache.tinkerpop.gremlin.structure.T;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.config.ServerOptions;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.UpdateStrategy;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.CompressInterceptor.Compress;
|
||||
import org.apache.hugegraph.api.filter.DecompressInterceptor.Decompress;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.id.SplicingIdGenerator;
|
||||
import org.apache.hugegraph.backend.query.ConditionQuery;
|
||||
import org.apache.hugegraph.config.HugeConfig;
|
||||
import org.apache.hugegraph.exception.NotFoundException;
|
||||
import org.apache.hugegraph.schema.PropertyKey;
|
||||
import org.apache.hugegraph.schema.VertexLabel;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.traversal.optimize.QueryHolder;
|
||||
import org.apache.hugegraph.traversal.optimize.Text;
|
||||
import org.apache.hugegraph.traversal.optimize.TraversalUtil;
|
||||
import org.apache.hugegraph.type.define.IdStrategy;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.JsonUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/graph/vertices")
|
||||
@Singleton
|
||||
@Tag(name = "VertexAPI")
|
||||
public class VertexAPI extends BatchAPI {
|
||||
|
||||
private static final Logger LOG = Log.logger(VertexAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed(name = "single-create")
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=vertex_write"})
|
||||
public String create(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
JsonVertex jsonVertex) {
|
||||
LOG.debug("Graph [{}] create vertex: {}", graph, jsonVertex);
|
||||
checkCreatingBody(jsonVertex);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Vertex vertex = commit(g, () -> g.addVertex(jsonVertex.properties()));
|
||||
|
||||
return manager.serializer(g).writeVertex(vertex);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed(name = "batch-create")
|
||||
@Decompress
|
||||
@Path("batch")
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=vertex_write"})
|
||||
public String create(@Context HugeConfig config,
|
||||
@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
List<JsonVertex> jsonVertices) {
|
||||
LOG.debug("Graph [{}] create vertices: {}", graph, jsonVertices);
|
||||
checkCreatingBody(jsonVertices);
|
||||
checkBatchSize(config, jsonVertices);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
||||
return this.commit(config, g, jsonVertices.size(), () -> {
|
||||
List<Id> ids = new ArrayList<>(jsonVertices.size());
|
||||
for (JsonVertex vertex : jsonVertices) {
|
||||
ids.add((Id) g.addVertex(vertex.properties()).id());
|
||||
}
|
||||
return manager.serializer(g).writeIds(ids);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch update steps like:
|
||||
* 1. Get all newVertices' ID & combine first
|
||||
* 2. Get all oldVertices & update
|
||||
* 3. Add the final vertex together
|
||||
*/
|
||||
@PUT
|
||||
@Timed(name = "batch-update")
|
||||
@Decompress
|
||||
@Path("batch")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=vertex_write"})
|
||||
public String update(@Context HugeConfig config,
|
||||
@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
BatchVertexRequest req) {
|
||||
BatchVertexRequest.checkUpdate(req);
|
||||
LOG.debug("Graph [{}] update vertices: {}", graph, req);
|
||||
checkUpdatingBody(req.jsonVertices);
|
||||
checkBatchSize(config, req.jsonVertices);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Map<Id, JsonVertex> map = new HashMap<>(req.jsonVertices.size());
|
||||
|
||||
return this.commit(config, g, map.size(), () -> {
|
||||
/*
|
||||
* 1.Put all newVertices' properties into map (combine first)
|
||||
* - Consider primary-key & user-define ID mode first
|
||||
*/
|
||||
req.jsonVertices.forEach(newVertex -> {
|
||||
Id newVertexId = getVertexId(g, newVertex);
|
||||
JsonVertex oldVertex = map.get(newVertexId);
|
||||
this.updateExistElement(oldVertex, newVertex,
|
||||
req.updateStrategies);
|
||||
map.put(newVertexId, newVertex);
|
||||
});
|
||||
|
||||
// 2.Get all oldVertices and update with new vertices
|
||||
Object[] ids = map.keySet().toArray();
|
||||
Iterator<Vertex> oldVertices = g.vertices(ids);
|
||||
oldVertices.forEachRemaining(oldVertex -> {
|
||||
JsonVertex newVertex = map.get(oldVertex.id());
|
||||
this.updateExistElement(g, oldVertex, newVertex,
|
||||
req.updateStrategies);
|
||||
});
|
||||
|
||||
// 3.Add finalVertices and return them
|
||||
List<Vertex> vertices = new ArrayList<>(map.size());
|
||||
map.values().forEach(finalVertex -> {
|
||||
vertices.add(g.addVertex(finalVertex.properties()));
|
||||
});
|
||||
|
||||
// If return ids, the ids.size() maybe different with the origins'
|
||||
return manager.serializer(g)
|
||||
.writeVertices(vertices.iterator(), false);
|
||||
});
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed(name = "single-update")
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=vertex_write"})
|
||||
public String update(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String idValue,
|
||||
@QueryParam("action") String action,
|
||||
JsonVertex jsonVertex) {
|
||||
LOG.debug("Graph [{}] update vertex: {}", graph, jsonVertex);
|
||||
checkUpdatingBody(jsonVertex);
|
||||
|
||||
Id id = checkAndParseVertexId(idValue);
|
||||
// Parse action param
|
||||
boolean append = checkAndParseAction(action);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
HugeVertex vertex = (HugeVertex) g.vertex(id);
|
||||
VertexLabel vertexLabel = vertex.schemaLabel();
|
||||
|
||||
for (String key : jsonVertex.properties.keySet()) {
|
||||
PropertyKey pkey = g.propertyKey(key);
|
||||
E.checkArgument(vertexLabel.properties().contains(pkey.id()),
|
||||
"Can't update property for vertex '%s' because " +
|
||||
"there is no property key '%s' in its vertex label",
|
||||
id, key);
|
||||
}
|
||||
|
||||
commit(g, () -> updateProperties(vertex, jsonVertex, append));
|
||||
|
||||
return manager.serializer(g).writeVertex(vertex);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Compress
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=vertex_read"})
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("label") String label,
|
||||
@QueryParam("properties") String properties,
|
||||
@QueryParam("keep_start_p")
|
||||
@DefaultValue("false") boolean keepStartP,
|
||||
@QueryParam("offset") @DefaultValue("0") long offset,
|
||||
@QueryParam("page") String page,
|
||||
@QueryParam("limit") @DefaultValue("100") long limit) {
|
||||
LOG.debug("Graph [{}] query vertices by label: {}, properties: {}, " +
|
||||
"offset: {}, page: {}, limit: {}",
|
||||
graph, label, properties, offset, page, limit);
|
||||
|
||||
Map<String, Object> props = parseProperties(properties);
|
||||
if (page != null) {
|
||||
E.checkArgument(offset == 0,
|
||||
"Not support querying vertices based on paging " +
|
||||
"and offset together");
|
||||
}
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
||||
GraphTraversal<Vertex, Vertex> traversal = g.traversal().V();
|
||||
if (label != null) {
|
||||
traversal = traversal.hasLabel(label);
|
||||
}
|
||||
|
||||
// Convert relational operator like P.gt()/P.lt()
|
||||
for (Map.Entry<String, Object> prop : props.entrySet()) {
|
||||
Object value = prop.getValue();
|
||||
if (!keepStartP && value instanceof String &&
|
||||
((String) value).startsWith(TraversalUtil.P_CALL)) {
|
||||
prop.setValue(TraversalUtil.parsePredicate((String) value));
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Object> entry : props.entrySet()) {
|
||||
traversal = traversal.has(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
if (page == null) {
|
||||
traversal = traversal.range(offset, offset + limit);
|
||||
} else {
|
||||
traversal = traversal.has(QueryHolder.SYSPROP_PAGE, page)
|
||||
.limit(limit);
|
||||
}
|
||||
|
||||
try {
|
||||
return manager.serializer(g).writeVertices(traversal,
|
||||
page != null);
|
||||
} finally {
|
||||
if (g.tx().isOpen()) {
|
||||
g.tx().close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=vertex_read"})
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String idValue) {
|
||||
LOG.debug("Graph [{}] get vertex by id '{}'", graph, idValue);
|
||||
|
||||
Id id = checkAndParseVertexId(idValue);
|
||||
HugeGraph g = graph(manager, graph);
|
||||
try {
|
||||
Vertex vertex = g.vertex(id);
|
||||
return manager.serializer(g).writeVertex(vertex);
|
||||
} finally {
|
||||
if (g.tx().isOpen()) {
|
||||
g.tx().close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=vertex_delete"})
|
||||
public void delete(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String idValue,
|
||||
@QueryParam("label") String label) {
|
||||
LOG.debug("Graph [{}] remove vertex by id '{}'", graph, idValue);
|
||||
|
||||
Id id = checkAndParseVertexId(idValue);
|
||||
HugeGraph g = graph(manager, graph);
|
||||
commit(g, () -> {
|
||||
try {
|
||||
g.removeVertex(label, id);
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"No such vertex with id: '%s', %s", id, e));
|
||||
} catch (NoSuchElementException e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"No such vertex with id: '%s'", id));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static Id checkAndParseVertexId(String idValue) {
|
||||
if (idValue == null) {
|
||||
return null;
|
||||
}
|
||||
boolean uuid = idValue.startsWith("U\"");
|
||||
if (uuid) {
|
||||
idValue = idValue.substring(1);
|
||||
}
|
||||
try {
|
||||
Object id = JsonUtil.fromJson(idValue, Object.class);
|
||||
return uuid ? Text.uuid((String) id) : HugeVertex.getIdValue(id);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"The vertex id must be formatted as Number/String/UUID" +
|
||||
", but got '%s'", idValue));
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkBatchSize(HugeConfig config,
|
||||
List<JsonVertex> vertices) {
|
||||
int max = config.get(ServerOptions.MAX_VERTICES_PER_BATCH);
|
||||
if (vertices.size() > max) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Too many vertices for one time post, " +
|
||||
"the maximum number is '%s'", max));
|
||||
}
|
||||
if (vertices.size() == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The number of vertices can't be 0");
|
||||
}
|
||||
}
|
||||
|
||||
private static Id getVertexId(HugeGraph g, JsonVertex vertex) {
|
||||
VertexLabel vertexLabel = g.vertexLabel(vertex.label);
|
||||
String labelId = vertexLabel.id().asString();
|
||||
IdStrategy idStrategy = vertexLabel.idStrategy();
|
||||
E.checkArgument(idStrategy != IdStrategy.AUTOMATIC,
|
||||
"Automatic Id strategy is not supported now");
|
||||
|
||||
if (idStrategy == IdStrategy.PRIMARY_KEY) {
|
||||
List<Id> pkIds = vertexLabel.primaryKeys();
|
||||
List<Object> pkValues = new ArrayList<>(pkIds.size());
|
||||
for (Id pkId : pkIds) {
|
||||
String propertyKey = g.propertyKey(pkId).name();
|
||||
Object propertyValue = vertex.properties.get(propertyKey);
|
||||
E.checkArgument(propertyValue != null,
|
||||
"The value of primary key '%s' can't be null",
|
||||
propertyKey);
|
||||
pkValues.add(propertyValue);
|
||||
}
|
||||
|
||||
String value = ConditionQuery.concatValues(pkValues);
|
||||
return SplicingIdGenerator.splicing(labelId, value);
|
||||
} else if (idStrategy == IdStrategy.CUSTOMIZE_UUID) {
|
||||
return Text.uuid(String.valueOf(vertex.id));
|
||||
} else {
|
||||
assert idStrategy == IdStrategy.CUSTOMIZE_NUMBER ||
|
||||
idStrategy == IdStrategy.CUSTOMIZE_STRING;
|
||||
return HugeVertex.getIdValue(vertex.id);
|
||||
}
|
||||
}
|
||||
|
||||
private static class BatchVertexRequest {
|
||||
|
||||
@JsonProperty("vertices")
|
||||
public List<JsonVertex> jsonVertices;
|
||||
@JsonProperty("update_strategies")
|
||||
public Map<String, UpdateStrategy> updateStrategies;
|
||||
@JsonProperty("create_if_not_exist")
|
||||
public boolean createIfNotExist = true;
|
||||
|
||||
private static void checkUpdate(BatchVertexRequest req) {
|
||||
E.checkArgumentNotNull(req, "BatchVertexRequest can't be null");
|
||||
E.checkArgumentNotNull(req.jsonVertices,
|
||||
"Parameter 'vertices' can't be null");
|
||||
E.checkArgument(req.updateStrategies != null &&
|
||||
!req.updateStrategies.isEmpty(),
|
||||
"Parameter 'update_strategies' can't be empty");
|
||||
E.checkArgument(req.createIfNotExist,
|
||||
"Parameter 'create_if_not_exist' " +
|
||||
"dose not support false now");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("BatchVertexRequest{jsonVertices=%s," +
|
||||
"updateStrategies=%s,createIfNotExist=%s}",
|
||||
this.jsonVertices, this.updateStrategies,
|
||||
this.createIfNotExist);
|
||||
}
|
||||
}
|
||||
|
||||
private static class JsonVertex extends JsonElement {
|
||||
|
||||
@Override
|
||||
public void checkCreate(boolean isBatch) {
|
||||
this.checkUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkUpdate() {
|
||||
E.checkArgumentNotNull(this.properties,
|
||||
"The properties of vertex can't be null");
|
||||
|
||||
for (Map.Entry<String, Object> e : this.properties.entrySet()) {
|
||||
String key = e.getKey();
|
||||
Object value = e.getValue();
|
||||
E.checkArgumentNotNull(value, "Not allowed to set value of " +
|
||||
"property '%s' to null for vertex '%s'",
|
||||
key, this.id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] properties() {
|
||||
Object[] props = API.properties(this.properties);
|
||||
int newSize = props.length;
|
||||
int appendIndex = newSize;
|
||||
if (this.label != null) {
|
||||
newSize += 2;
|
||||
}
|
||||
if (this.id != null) {
|
||||
newSize += 2;
|
||||
}
|
||||
if (newSize == props.length) {
|
||||
return props;
|
||||
}
|
||||
|
||||
Object[] newProps = Arrays.copyOf(props, newSize);
|
||||
if (this.label != null) {
|
||||
newProps[appendIndex++] = T.label;
|
||||
newProps[appendIndex++] = this.label;
|
||||
}
|
||||
if (this.id != null) {
|
||||
newProps[appendIndex++] = T.id;
|
||||
// Keep value++ to avoid code trap
|
||||
newProps[appendIndex++] = this.id;
|
||||
}
|
||||
return newProps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("JsonVertex{label=%s, properties=%s}",
|
||||
this.label, this.properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.gremlin;
|
||||
|
||||
import org.opencypher.gremlin.translation.TranslationFacade;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.api.filter.CompressInterceptor.Compress;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
@Path("graphs/{graph}/cypher")
|
||||
@Singleton
|
||||
@Tag(name = "CypherAPI")
|
||||
public class CypherAPI extends GremlinQueryAPI {
|
||||
|
||||
private static final Logger LOG = Log.logger(CypherAPI.class);
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Compress(buffer = (1024 * 40))
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public Response query(@PathParam("graph") String graph,
|
||||
@Context HttpHeaders headers,
|
||||
@QueryParam("cypher") String cypher) {
|
||||
LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
|
||||
|
||||
return this.queryByCypher(graph, headers, cypher);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Compress
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public Response post(@PathParam("graph") String graph,
|
||||
@Context HttpHeaders headers,
|
||||
String cypher) {
|
||||
LOG.debug("Graph [{}] query by cypher: {}", graph, cypher);
|
||||
return this.queryByCypher(graph, headers, cypher);
|
||||
}
|
||||
|
||||
private Response queryByCypher(String graph,
|
||||
HttpHeaders headers,
|
||||
String cypher) {
|
||||
E.checkArgument(cypher != null && !cypher.isEmpty(),
|
||||
"The cypher parameter can't be null or empty");
|
||||
|
||||
String gremlin = this.translateCpyher2Gremlin(graph, cypher);
|
||||
LOG.debug("translated gremlin is {}", gremlin);
|
||||
|
||||
String auth = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
|
||||
String request = "{" +
|
||||
"\"gremlin\":\"" + gremlin + "\"," +
|
||||
"\"bindings\":{}," +
|
||||
"\"language\":\"gremlin-groovy\"," +
|
||||
"\"aliases\":{\"g\":\"__g_" + graph + "\"}}";
|
||||
|
||||
Response response = this.client().doPostRequest(auth, request);
|
||||
return transformResponseIfNeeded(response);
|
||||
}
|
||||
|
||||
private String translateCpyher2Gremlin(String graph, String cypher) {
|
||||
TranslationFacade translator = new TranslationFacade();
|
||||
String gremlin = translator.toGremlinGroovy(cypher);
|
||||
gremlin = this.buildQueryableGremlin(graph, gremlin);
|
||||
return gremlin;
|
||||
}
|
||||
|
||||
private String buildQueryableGremlin(String graph, String gremlin) {
|
||||
/*
|
||||
* `CREATE (a:person { name : 'test', age: 20) return a`
|
||||
* would be translated to:
|
||||
* `g.addV('person').as('a').property(single, 'name', 'test') ...`,
|
||||
* but hugegraph don't support `.property(single, k, v)`,
|
||||
* so we replace it to `.property(k, v)` here
|
||||
*/
|
||||
gremlin = gremlin.replace(".property(single,", ".property(");
|
||||
|
||||
return gremlin;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.gremlin;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
|
||||
import org.apache.hugegraph.api.filter.CompressInterceptor.Compress;
|
||||
import org.apache.hugegraph.config.HugeConfig;
|
||||
import org.apache.hugegraph.metrics.MetricsUtil;
|
||||
import com.codahale.metrics.Histogram;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import jakarta.inject.Singleton;
|
||||
|
||||
@Path("gremlin")
|
||||
@Singleton
|
||||
@Tag(name = "GremlinAPI")
|
||||
public class GremlinAPI extends GremlinQueryAPI {
|
||||
|
||||
private static final Histogram GREMLIN_INPUT_HISTOGRAM =
|
||||
MetricsUtil.registerHistogram(GremlinAPI.class, "gremlin-input");
|
||||
private static final Histogram GREMLIN_OUTPUT_HISTOGRAM =
|
||||
MetricsUtil.registerHistogram(GremlinAPI.class, "gremlin-output");
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Compress
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public Response post(@Context HugeConfig conf,
|
||||
@Context HttpHeaders headers,
|
||||
String request) {
|
||||
/* The following code is reserved for forwarding request */
|
||||
// context.getRequestDispatcher(location).forward(request, response);
|
||||
// return Response.seeOther(UriBuilder.fromUri(location).build())
|
||||
// .build();
|
||||
// Response.temporaryRedirect(UriBuilder.fromUri(location).build())
|
||||
// .build();
|
||||
String auth = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
|
||||
Response response = this.client().doPostRequest(auth, request);
|
||||
GREMLIN_INPUT_HISTOGRAM.update(request.length());
|
||||
GREMLIN_OUTPUT_HISTOGRAM.update(response.getLength());
|
||||
return transformResponseIfNeeded(response);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Compress(buffer = (1024 * 40))
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public Response get(@Context HugeConfig conf,
|
||||
@Context HttpHeaders headers,
|
||||
@Context UriInfo uriInfo) {
|
||||
String auth = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
|
||||
String query = uriInfo.getRequestUri().getRawQuery();
|
||||
MultivaluedMap<String, String> params = uriInfo.getQueryParameters();
|
||||
Response response = this.client().doGetRequest(auth, params);
|
||||
GREMLIN_INPUT_HISTOGRAM.update(query.length());
|
||||
GREMLIN_OUTPUT_HISTOGRAM.update(response.getLength());
|
||||
return transformResponseIfNeeded(response);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.gremlin;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.ws.rs.client.Entity;
|
||||
import jakarta.ws.rs.client.WebTarget;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import org.apache.hugegraph.api.filter.CompressInterceptor;
|
||||
import org.apache.hugegraph.rest.AbstractRestClient;
|
||||
import org.apache.hugegraph.testutil.Whitebox;
|
||||
import org.apache.hugegraph.util.E;
|
||||
|
||||
public class GremlinClient extends AbstractRestClient {
|
||||
|
||||
private final WebTarget webTarget;
|
||||
|
||||
public GremlinClient(String url, int timeout,
|
||||
int maxTotal, int maxPerRoute) {
|
||||
super(url, timeout, maxTotal, maxPerRoute);
|
||||
this.webTarget = Whitebox.getInternalState(this, "target");
|
||||
E.checkNotNull(this.webTarget, "target");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkStatus(Response response, Response.Status... statuses) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public Response doPostRequest(String auth, String req) {
|
||||
Entity<?> body = Entity.entity(req, MediaType.APPLICATION_JSON);
|
||||
return this.webTarget.request()
|
||||
.header(HttpHeaders.AUTHORIZATION, auth)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.acceptEncoding(CompressInterceptor.GZIP)
|
||||
.post(body);
|
||||
}
|
||||
|
||||
public Response doGetRequest(String auth,
|
||||
MultivaluedMap<String, String> params) {
|
||||
WebTarget target = this.webTarget;
|
||||
for (Map.Entry<String, List<String>> entry : params.entrySet()) {
|
||||
E.checkArgument(entry.getValue().size() == 1,
|
||||
"Invalid query param '%s', can only accept " +
|
||||
"one value, but got %s",
|
||||
entry.getKey(), entry.getValue());
|
||||
target = target.queryParam(entry.getKey(), entry.getValue().get(0));
|
||||
}
|
||||
return target.request()
|
||||
.header(HttpHeaders.AUTHORIZATION, auth)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.acceptEncoding(CompressInterceptor.GZIP)
|
||||
.get();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.job;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.server.RestServer;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.job.AlgorithmJob;
|
||||
import org.apache.hugegraph.job.JobBuilder;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.JsonUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
@Path("graphs/{graph}/jobs/algorithm")
|
||||
@Singleton
|
||||
public class AlgorithmAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(RestServer.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Path("/{name}")
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public Map<String, Id> post(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String algorithm,
|
||||
Map<String, Object> parameters) {
|
||||
LOG.debug("Graph [{}] schedule algorithm job: {}", graph, parameters);
|
||||
E.checkArgument(algorithm != null && !algorithm.isEmpty(),
|
||||
"The algorithm name can't be empty");
|
||||
if (parameters == null) {
|
||||
parameters = ImmutableMap.of();
|
||||
}
|
||||
if (!AlgorithmJob.check(algorithm, parameters)) {
|
||||
throw new NotFoundException("Not found algorithm: " + algorithm);
|
||||
}
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Map<String, Object> input = ImmutableMap.of("algorithm", algorithm,
|
||||
"parameters", parameters);
|
||||
JobBuilder<Object> builder = JobBuilder.of(g);
|
||||
builder.name("algorithm:" + algorithm)
|
||||
.input(JsonUtil.toJson(input))
|
||||
.job(new AlgorithmJob());
|
||||
return ImmutableMap.of("task_id", builder.schedule().id());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.job;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.job.ComputerJob;
|
||||
import org.apache.hugegraph.job.JobBuilder;
|
||||
import org.apache.hugegraph.task.HugeTask;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.JsonUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
@Path("graphs/{graph}/jobs/computer")
|
||||
@Singleton
|
||||
@Tag(name = "ComputerAPI")
|
||||
public class ComputerAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(ComputerAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Path("/{name}")
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public Map<String, Id> post(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String computer,
|
||||
Map<String, Object> parameters) {
|
||||
LOG.debug("Graph [{}] schedule computer job: {}", graph, parameters);
|
||||
E.checkArgument(computer != null && !computer.isEmpty(),
|
||||
"The computer name can't be empty");
|
||||
if (parameters == null) {
|
||||
parameters = ImmutableMap.of();
|
||||
}
|
||||
if (!ComputerJob.check(computer, parameters)) {
|
||||
throw new NotFoundException("Not found computer: " + computer);
|
||||
}
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Map<String, Object> input = ImmutableMap.of("computer", computer,
|
||||
"parameters", parameters);
|
||||
JobBuilder<Object> builder = JobBuilder.of(g);
|
||||
builder.name("computer:" + computer)
|
||||
.input(JsonUtil.toJson(input))
|
||||
.job(new ComputerJob());
|
||||
HugeTask<Object> task = builder.schedule();
|
||||
return ImmutableMap.of("task_id", task.id());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,210 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.job;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.apache.hugegraph.metrics.MetricsUtil;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeException;
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.job.GremlinJob;
|
||||
import org.apache.hugegraph.job.JobBuilder;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.JsonUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.Histogram;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
@Path("graphs/{graph}/jobs/gremlin")
|
||||
@Singleton
|
||||
@Tag(name = "GremlinAPI")
|
||||
public class GremlinAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(GremlinAPI.class);
|
||||
|
||||
private static final int MAX_NAME_LENGTH = 256;
|
||||
|
||||
private static final Histogram GREMLIN_JOB_INPUT_HISTOGRAM =
|
||||
MetricsUtil.registerHistogram(GremlinAPI.class, "gremlin-input");
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=gremlin_execute"})
|
||||
public Map<String, Id> post(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
GremlinRequest request) {
|
||||
LOG.debug("Graph [{}] schedule gremlin job: {}", graph, request);
|
||||
checkCreatingBody(request);
|
||||
GREMLIN_JOB_INPUT_HISTOGRAM.update(request.gremlin.length());
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
request.aliase(graph, "graph");
|
||||
JobBuilder<Object> builder = JobBuilder.of(g);
|
||||
builder.name(request.name())
|
||||
.input(request.toJson())
|
||||
.job(new GremlinJob());
|
||||
return ImmutableMap.of("task_id", builder.schedule().id());
|
||||
}
|
||||
|
||||
public static class GremlinRequest implements Checkable {
|
||||
|
||||
// See org.apache.tinkerpop.gremlin.server.channel.HttpChannelizer
|
||||
@JsonProperty
|
||||
private String gremlin;
|
||||
@JsonProperty
|
||||
private Map<String, Object> bindings = new HashMap<>();
|
||||
@JsonProperty
|
||||
private String language = "gremlin-groovy";
|
||||
@JsonProperty
|
||||
private Map<String, String> aliases = new HashMap<>();
|
||||
|
||||
public String gremlin() {
|
||||
return this.gremlin;
|
||||
}
|
||||
|
||||
public void gremlin(String gremlin) {
|
||||
this.gremlin = gremlin;
|
||||
}
|
||||
|
||||
public Map<String, Object> bindings() {
|
||||
return this.bindings;
|
||||
}
|
||||
|
||||
public void bindings(Map<String, Object> bindings) {
|
||||
this.bindings = bindings;
|
||||
}
|
||||
|
||||
public void binding(String name, Object value) {
|
||||
this.bindings.put(name, value);
|
||||
}
|
||||
|
||||
public String language() {
|
||||
return this.language;
|
||||
}
|
||||
|
||||
public void language(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public Map<String, String> aliases() {
|
||||
return this.aliases;
|
||||
}
|
||||
|
||||
public void aliases(Map<String, String> aliases) {
|
||||
this.aliases = aliases;
|
||||
}
|
||||
|
||||
public void aliase(String key, String value) {
|
||||
this.aliases.put(key, value);
|
||||
}
|
||||
|
||||
public String name() {
|
||||
// Get the first line of script as the name
|
||||
String firstLine = this.gremlin.split("\r\n|\r|\n", 2)[0];
|
||||
final Charset charset = Charset.forName(CHARSET);
|
||||
final byte[] bytes = firstLine.getBytes(charset);
|
||||
if (bytes.length <= MAX_NAME_LENGTH) {
|
||||
return firstLine;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reference https://stackoverflow.com/questions/3576754/truncating-strings-by-bytes
|
||||
*/
|
||||
CharsetDecoder decoder = charset.newDecoder();
|
||||
decoder.onMalformedInput(CodingErrorAction.IGNORE);
|
||||
decoder.reset();
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes, 0, MAX_NAME_LENGTH);
|
||||
try {
|
||||
return decoder.decode(buffer).toString();
|
||||
} catch (CharacterCodingException e) {
|
||||
throw new HugeException("Failed to decode truncated bytes of " +
|
||||
"gremlin first line", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkCreate(boolean isBatch) {
|
||||
E.checkArgumentNotNull(this.gremlin,
|
||||
"The gremlin parameter can't be null");
|
||||
E.checkArgumentNotNull(this.language,
|
||||
"The language parameter can't be null");
|
||||
E.checkArgument(this.aliases == null || this.aliases.isEmpty(),
|
||||
"There is no need to pass gremlin aliases");
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("gremlin", this.gremlin);
|
||||
map.put("bindings", this.bindings);
|
||||
map.put("language", this.language);
|
||||
map.put("aliases", this.aliases);
|
||||
return JsonUtil.toJson(map);
|
||||
}
|
||||
|
||||
public static GremlinRequest fromJson(String json) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = JsonUtil.fromJson(json, Map.class);
|
||||
String gremlin = (String) map.get("gremlin");
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> bindings = (Map<String, Object>)
|
||||
map.get("bindings");
|
||||
String language = (String) map.get("language");
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> aliases = (Map<String, String>)
|
||||
map.get("aliases");
|
||||
|
||||
GremlinRequest request = new GremlinRequest();
|
||||
request.gremlin(gremlin);
|
||||
request.bindings(bindings);
|
||||
request.language(language);
|
||||
request.aliases(aliases);
|
||||
return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.job;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
@Path("graphs/{graph}/jobs/rebuild")
|
||||
@Singleton
|
||||
@Tag(name = "RebuildAPI")
|
||||
public class RebuildAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(RebuildAPI.class);
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("vertexlabels/{name}")
|
||||
@Status(Status.ACCEPTED)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=index_write"})
|
||||
public Map<String, Id> vertexLabelRebuild(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Graph [{}] rebuild vertex label: {}", graph, name);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
return ImmutableMap.of("task_id",
|
||||
g.schema().vertexLabel(name).rebuildIndex());
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("edgelabels/{name}")
|
||||
@Status(Status.ACCEPTED)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=index_write"})
|
||||
public Map<String, Id> edgeLabelRebuild(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Graph [{}] rebuild edge label: {}", graph, name);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
return ImmutableMap.of("task_id",
|
||||
g.schema().edgeLabel(name).rebuildIndex());
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("indexlabels/{name}")
|
||||
@Status(Status.ACCEPTED)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=index_write"})
|
||||
public Map<String, Id> indexLabelRebuild(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Graph [{}] rebuild index label: {}", graph, name);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
return ImmutableMap.of("task_id",
|
||||
g.schema().indexLabel(name).rebuild());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.job;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.NotSupportedException;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.groovy.util.Maps;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.id.IdGenerator;
|
||||
import org.apache.hugegraph.backend.page.PageInfo;
|
||||
import org.apache.hugegraph.task.HugeTask;
|
||||
import org.apache.hugegraph.task.TaskScheduler;
|
||||
import org.apache.hugegraph.task.TaskStatus;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Path("graphs/{graph}/tasks")
|
||||
@Singleton
|
||||
@Tag(name = "TaskAPI")
|
||||
public class TaskAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(TaskAPI.class);
|
||||
private static final long NO_LIMIT = -1L;
|
||||
|
||||
public static final String ACTION_CANCEL = "cancel";
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public Map<String, Object> list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("status") String status,
|
||||
@QueryParam("ids") List<Long> ids,
|
||||
@QueryParam("limit")
|
||||
@DefaultValue("100") long limit,
|
||||
@QueryParam("page") String page) {
|
||||
LOG.debug("Graph [{}] list tasks with status {}, ids {}, " +
|
||||
"limit {}, page {}", graph, status, ids, limit, page);
|
||||
|
||||
TaskScheduler scheduler = graph(manager, graph).taskScheduler();
|
||||
|
||||
Iterator<HugeTask<Object>> iter;
|
||||
|
||||
if (!ids.isEmpty()) {
|
||||
E.checkArgument(status == null,
|
||||
"Not support status when query task by ids, " +
|
||||
"but got status='%s'", status);
|
||||
E.checkArgument(page == null,
|
||||
"Not support page when query task by ids, " +
|
||||
"but got page='%s'", page);
|
||||
// Set limit to NO_LIMIT to ignore limit when query task by ids
|
||||
limit = NO_LIMIT;
|
||||
List<Id> idList = ids.stream().map(IdGenerator::of)
|
||||
.collect(Collectors.toList());
|
||||
iter = scheduler.tasks(idList);
|
||||
} else {
|
||||
if (status == null) {
|
||||
iter = scheduler.tasks(null, limit, page);
|
||||
} else {
|
||||
iter = scheduler.tasks(parseStatus(status), limit, page);
|
||||
}
|
||||
}
|
||||
|
||||
List<Object> tasks = new ArrayList<>();
|
||||
while (iter.hasNext()) {
|
||||
tasks.add(iter.next().asMap(false));
|
||||
}
|
||||
if (limit != NO_LIMIT && tasks.size() > limit) {
|
||||
tasks = tasks.subList(0, (int) limit);
|
||||
}
|
||||
|
||||
if (page == null) {
|
||||
return Maps.of("tasks", tasks);
|
||||
} else {
|
||||
return Maps.of("tasks", tasks, "page", PageInfo.pageInfo(iter));
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public Map<String, Object> get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") long id) {
|
||||
LOG.debug("Graph [{}] get task: {}", graph, id);
|
||||
|
||||
TaskScheduler scheduler = graph(manager, graph).taskScheduler();
|
||||
return scheduler.task(IdGenerator.of(id)).asMap();
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
public void delete(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") long id) {
|
||||
LOG.debug("Graph [{}] delete task: {}", graph, id);
|
||||
|
||||
TaskScheduler scheduler = graph(manager, graph).taskScheduler();
|
||||
HugeTask<?> task = scheduler.delete(IdGenerator.of(id));
|
||||
E.checkArgument(task != null, "There is no task with id '%s'", id);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{id}")
|
||||
@Status(Status.ACCEPTED)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public Map<String, Object> update(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") long id,
|
||||
@QueryParam("action") String action) {
|
||||
LOG.debug("Graph [{}] cancel task: {}", graph, id);
|
||||
|
||||
if (!ACTION_CANCEL.equals(action)) {
|
||||
throw new NotSupportedException(String.format(
|
||||
"Not support action '%s'", action));
|
||||
}
|
||||
|
||||
TaskScheduler scheduler = graph(manager, graph).taskScheduler();
|
||||
HugeTask<?> task = scheduler.task(IdGenerator.of(id));
|
||||
if (!task.completed() && !task.cancelling()) {
|
||||
scheduler.cancel(task);
|
||||
if (task.cancelling() || task.cancelled()) {
|
||||
return task.asMap();
|
||||
}
|
||||
}
|
||||
|
||||
assert task.completed() || task.cancelling();
|
||||
throw new BadRequestException(String.format(
|
||||
"Can't cancel task '%s' which is completed or cancelling",
|
||||
id));
|
||||
}
|
||||
|
||||
private static TaskStatus parseStatus(String status) {
|
||||
try {
|
||||
return TaskStatus.valueOf(status.toUpperCase());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Status value must be in %s, but got '%s'",
|
||||
Arrays.asList(TaskStatus.values()), status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.metrics;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.metrics.MetricsModule;
|
||||
import org.apache.hugegraph.metrics.ServerReporter;
|
||||
import org.apache.hugegraph.metrics.SystemMetrics;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.backend.store.BackendMetrics;
|
||||
import org.apache.hugegraph.util.InsertionOrderUtil;
|
||||
import org.apache.hugegraph.util.JsonUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.Metric;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Singleton
|
||||
@Path("metrics")
|
||||
@Tag(name = "MetricsAPI")
|
||||
public class MetricsAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(MetricsAPI.class);
|
||||
|
||||
private SystemMetrics systemMetrics;
|
||||
|
||||
static {
|
||||
JsonUtil.registerModule(new MetricsModule(SECONDS, MILLISECONDS, false));
|
||||
}
|
||||
|
||||
public MetricsAPI() {
|
||||
this.systemMetrics = new SystemMetrics();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("system")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner= $action=metrics_read"})
|
||||
public String system() {
|
||||
return JsonUtil.toJson(this.systemMetrics.metrics());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("backend")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner= $action=metrics_read"})
|
||||
public String backend(@Context GraphManager manager) {
|
||||
Map<String, Map<String, Object>> results = InsertionOrderUtil.newMap();
|
||||
for (String graph : manager.graphs()) {
|
||||
HugeGraph g = manager.graph(graph);
|
||||
Map<String, Object> metrics = InsertionOrderUtil.newMap();
|
||||
metrics.put(BackendMetrics.BACKEND, g.backend());
|
||||
try {
|
||||
metrics.putAll(g.metadata(null, "metrics"));
|
||||
} catch (Throwable e) {
|
||||
metrics.put(BackendMetrics.EXCEPTION, e.toString());
|
||||
LOG.debug("Failed to get backend metrics", e);
|
||||
}
|
||||
results.put(graph, metrics);
|
||||
}
|
||||
return JsonUtil.toJson(results);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner= $action=metrics_read"})
|
||||
public String all() {
|
||||
ServerReporter reporter = ServerReporter.instance();
|
||||
Map<String, Map<String, ? extends Metric>> result = new LinkedHashMap<>();
|
||||
result.put("gauges", reporter.gauges());
|
||||
result.put("counters", reporter.counters());
|
||||
result.put("histograms", reporter.histograms());
|
||||
result.put("meters", reporter.meters());
|
||||
result.put("timers", reporter.timers());
|
||||
return JsonUtil.toJson(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("gauges")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner= $action=metrics_read"})
|
||||
public String gauges() {
|
||||
ServerReporter reporter = ServerReporter.instance();
|
||||
return JsonUtil.toJson(reporter.gauges());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("counters")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner= $action=metrics_read"})
|
||||
public String counters() {
|
||||
ServerReporter reporter = ServerReporter.instance();
|
||||
return JsonUtil.toJson(reporter.counters());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("histograms")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner= $action=metrics_read"})
|
||||
public String histograms() {
|
||||
ServerReporter reporter = ServerReporter.instance();
|
||||
return JsonUtil.toJson(reporter.histograms());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("meters")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner= $action=metrics_read"})
|
||||
public String meters() {
|
||||
ServerReporter reporter = ServerReporter.instance();
|
||||
return JsonUtil.toJson(reporter.meters());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("timers")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner= $action=metrics_read"})
|
||||
public String timers() {
|
||||
ServerReporter reporter = ServerReporter.instance();
|
||||
return JsonUtil.toJson(reporter.timers());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,287 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.profile;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.ForbiddenException;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.NotSupportedException;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.SecurityContext;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.auth.HugeAuthenticator.RequiredPerm;
|
||||
import org.apache.hugegraph.auth.HugePermission;
|
||||
import org.apache.hugegraph.config.HugeConfig;
|
||||
import org.apache.hugegraph.type.define.GraphMode;
|
||||
import org.apache.hugegraph.type.define.GraphReadMode;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.JsonUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
@Path("graphs")
|
||||
@Singleton
|
||||
@Tag(name = "GraphsAPI")
|
||||
public class GraphsAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(GraphsAPI.class);
|
||||
|
||||
private static final String CONFIRM_CLEAR = "I'm sure to delete all data";
|
||||
private static final String CONFIRM_DROP = "I'm sure to drop the graph";
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$dynamic"})
|
||||
public Object list(@Context GraphManager manager,
|
||||
@Context SecurityContext sc) {
|
||||
Set<String> graphs = manager.graphs();
|
||||
// Filter by user role
|
||||
Set<String> filterGraphs = new HashSet<>();
|
||||
for (String graph : graphs) {
|
||||
String role = RequiredPerm.roleFor(graph, HugePermission.READ);
|
||||
if (sc.isUserInRole(role)) {
|
||||
try {
|
||||
HugeGraph g = graph(manager, graph);
|
||||
filterGraphs.add(g.name());
|
||||
} catch (ForbiddenException ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImmutableMap.of("graphs", filterGraphs);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{name}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$name"})
|
||||
public Object get(@Context GraphManager manager,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Get graph by name '{}'", name);
|
||||
|
||||
HugeGraph g = graph(manager, name);
|
||||
return ImmutableMap.of("name", g.name(), "backend", g.backend());
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("{name}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin"})
|
||||
public void drop(@Context GraphManager manager,
|
||||
@PathParam("name") String name,
|
||||
@QueryParam("confirm_message") String message) {
|
||||
LOG.debug("Drop graph by name '{}'", name);
|
||||
|
||||
E.checkArgument(CONFIRM_DROP.equals(message),
|
||||
"Please take the message: %s", CONFIRM_DROP);
|
||||
manager.dropGraph(name);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Path("{name}")
|
||||
@Consumes(TEXT_PLAIN)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin"})
|
||||
public Object create(@Context GraphManager manager,
|
||||
@PathParam("name") String name,
|
||||
@QueryParam("clone_graph_name") String clone,
|
||||
String configText) {
|
||||
LOG.debug("Create graph '{}' with clone graph '{}', config text '{}'",
|
||||
name, clone, configText);
|
||||
HugeGraph graph;
|
||||
if (StringUtils.isNotEmpty(clone)) {
|
||||
graph = manager.cloneGraph(clone, name, configText);
|
||||
} else {
|
||||
graph = manager.createGraph(name, configText);
|
||||
}
|
||||
return ImmutableMap.of("name", graph.name(),
|
||||
"backend", graph.backend());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{name}/conf")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed("admin")
|
||||
public File getConf(@Context GraphManager manager,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Get graph configuration by name '{}'", name);
|
||||
|
||||
HugeGraph g = graph4admin(manager, name);
|
||||
|
||||
HugeConfig config = (HugeConfig) g.configuration();
|
||||
File file = config.file();
|
||||
if (file == null) {
|
||||
throw new NotSupportedException("Can't access the api in " +
|
||||
"a node which started with non local file config.");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("{name}/clear")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@RolesAllowed("admin")
|
||||
public void clear(@Context GraphManager manager,
|
||||
@PathParam("name") String name,
|
||||
@QueryParam("confirm_message") String message) {
|
||||
LOG.debug("Clear graph by name '{}'", name);
|
||||
|
||||
E.checkArgument(CONFIRM_CLEAR.equals(message),
|
||||
"Please take the message: %s", CONFIRM_CLEAR);
|
||||
HugeGraph g = graph(manager, name);
|
||||
g.truncateBackend();
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{name}/snapshot_create")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$name"})
|
||||
public Object createSnapshot(@Context GraphManager manager,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Create snapshot for graph '{}'", name);
|
||||
|
||||
HugeGraph g = graph(manager, name);
|
||||
g.createSnapshot();
|
||||
return ImmutableMap.of(name, "snapshot_created");
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{name}/snapshot_resume")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$name"})
|
||||
public Object resumeSnapshot(@Context GraphManager manager,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Resume snapshot for graph '{}'", name);
|
||||
|
||||
HugeGraph g = graph(manager, name);
|
||||
g.resumeSnapshot();
|
||||
return ImmutableMap.of(name, "snapshot_resumed");
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{name}/compact")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin"})
|
||||
public String compact(@Context GraphManager manager,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Manually compact graph '{}'", name);
|
||||
|
||||
HugeGraph g = graph(manager, name);
|
||||
return JsonUtil.toJson(g.metadata(null, "compact"));
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{name}/mode")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$name"})
|
||||
public Map<String, GraphMode> mode(@Context GraphManager manager,
|
||||
@PathParam("name") String name,
|
||||
GraphMode mode) {
|
||||
LOG.debug("Set mode to: '{}' of graph '{}'", mode, name);
|
||||
|
||||
E.checkArgument(mode != null, "Graph mode can't be null");
|
||||
HugeGraph g = graph(manager, name);
|
||||
g.mode(mode);
|
||||
return ImmutableMap.of("mode", mode);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{name}/mode")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$name"})
|
||||
public Map<String, GraphMode> mode(@Context GraphManager manager,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Get mode of graph '{}'", name);
|
||||
|
||||
HugeGraph g = graph(manager, name);
|
||||
return ImmutableMap.of("mode", g.mode());
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{name}/graph_read_mode")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed("admin")
|
||||
public Map<String, GraphReadMode> graphReadMode(
|
||||
@Context GraphManager manager,
|
||||
@PathParam("name") String name,
|
||||
GraphReadMode readMode) {
|
||||
LOG.debug("Set graph-read-mode to: '{}' of graph '{}'",
|
||||
readMode, name);
|
||||
|
||||
E.checkArgument(readMode != null,
|
||||
"Graph-read-mode can't be null");
|
||||
HugeGraph g = graph(manager, name);
|
||||
g.readMode(readMode);
|
||||
return ImmutableMap.of("graph_read_mode", readMode);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{name}/graph_read_mode")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$name"})
|
||||
public Map<String, GraphReadMode> graphReadMode(
|
||||
@Context GraphManager manager,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Get graph-read-mode of graph '{}'", name);
|
||||
|
||||
HugeGraph g = graph(manager, name);
|
||||
return ImmutableMap.of("graph_read_mode", g.readMode());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.profile;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.version.ApiVersion;
|
||||
import org.apache.hugegraph.version.CoreVersion;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
@Path("versions")
|
||||
@Singleton
|
||||
@Tag(name = "VersionAPI")
|
||||
public class VersionAPI extends API {
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@PermitAll
|
||||
public Object list() {
|
||||
Map<String, String> versions = ImmutableMap.of("version", "v1",
|
||||
"core", CoreVersion.VERSION.toString(),
|
||||
"gremlin", CoreVersion.GREMLIN_VERSION,
|
||||
"api", ApiVersion.VERSION.toString());
|
||||
return ImmutableMap.of("versions", versions);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,217 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.raft;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeException;
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.store.raft.RaftAddPeerJob;
|
||||
import org.apache.hugegraph.backend.store.raft.RaftGroupManager;
|
||||
import org.apache.hugegraph.backend.store.raft.RaftRemovePeerJob;
|
||||
import org.apache.hugegraph.job.JobBuilder;
|
||||
import org.apache.hugegraph.util.DateUtil;
|
||||
import org.apache.hugegraph.util.JsonUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
@Path("graphs/{graph}/raft")
|
||||
@Singleton
|
||||
@Tag(name = "RaftAPI")
|
||||
public class RaftAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(RaftAPI.class);
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("list_peers")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin"})
|
||||
public Map<String, List<String>> listPeers(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("group")
|
||||
@DefaultValue("default")
|
||||
String group) {
|
||||
LOG.debug("Graph [{}] prepare to get leader", graph);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
RaftGroupManager raftManager = raftGroupManager(g, group, "list_peers");
|
||||
List<String> peers = raftManager.listPeers();
|
||||
return ImmutableMap.of(raftManager.group(), peers);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("get_leader")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin"})
|
||||
public Map<String, String> getLeader(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("group")
|
||||
@DefaultValue("default")
|
||||
String group) {
|
||||
LOG.debug("Graph [{}] prepare to get leader", graph);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
RaftGroupManager raftManager = raftGroupManager(g, group, "get_leader");
|
||||
String leaderId = raftManager.getLeader();
|
||||
return ImmutableMap.of(raftManager.group(), leaderId);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.OK)
|
||||
@Path("transfer_leader")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin"})
|
||||
public Map<String, String> transferLeader(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("group")
|
||||
@DefaultValue("default")
|
||||
String group,
|
||||
@QueryParam("endpoint")
|
||||
String endpoint) {
|
||||
LOG.debug("Graph [{}] prepare to transfer leader to: {}",
|
||||
graph, endpoint);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
RaftGroupManager raftManager = raftGroupManager(g, group,
|
||||
"transfer_leader");
|
||||
String leaderId = raftManager.transferLeaderTo(endpoint);
|
||||
return ImmutableMap.of(raftManager.group(), leaderId);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.OK)
|
||||
@Path("set_leader")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin"})
|
||||
public Map<String, String> setLeader(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("group")
|
||||
@DefaultValue("default")
|
||||
String group,
|
||||
@QueryParam("endpoint")
|
||||
String endpoint) {
|
||||
LOG.debug("Graph [{}] prepare to set leader to: {}",
|
||||
graph, endpoint);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
RaftGroupManager raftManager = raftGroupManager(g, group, "set_leader");
|
||||
String leaderId = raftManager.setLeader(endpoint);
|
||||
return ImmutableMap.of(raftManager.group(), leaderId);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.OK)
|
||||
@Path("add_peer")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin"})
|
||||
public Map<String, Id> addPeer(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("group") @DefaultValue("default")
|
||||
String group,
|
||||
@QueryParam("endpoint") String endpoint) {
|
||||
LOG.debug("Graph [{}] prepare to add peer: {}", graph, endpoint);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
RaftGroupManager raftManager = raftGroupManager(g, group, "add_peer");
|
||||
|
||||
JobBuilder<String> builder = JobBuilder.of(g);
|
||||
String name = String.format("raft-group-[%s]-add-peer-[%s]-at-[%s]",
|
||||
raftManager.group(), endpoint,
|
||||
DateUtil.now());
|
||||
Map<String, String> inputs = new HashMap<>();
|
||||
inputs.put("endpoint", endpoint);
|
||||
builder.name(name)
|
||||
.input(JsonUtil.toJson(inputs))
|
||||
.job(new RaftAddPeerJob());
|
||||
return ImmutableMap.of("task_id", builder.schedule().id());
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.OK)
|
||||
@Path("remove_peer")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin"})
|
||||
public Map<String, Id> removePeer(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("group")
|
||||
@DefaultValue("default") String group,
|
||||
@QueryParam("endpoint") String endpoint) {
|
||||
LOG.debug("Graph [{}] prepare to remove peer: {}", graph, endpoint);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
RaftGroupManager raftManager = raftGroupManager(g, group,
|
||||
"remove_peer");
|
||||
JobBuilder<String> builder = JobBuilder.of(g);
|
||||
String name = String.format("raft-group-[%s]-remove-peer-[%s]-at-[%s]",
|
||||
raftManager.group(), endpoint,
|
||||
DateUtil.now());
|
||||
Map<String, String> inputs = new HashMap<>();
|
||||
inputs.put("endpoint", endpoint);
|
||||
builder.name(name)
|
||||
.input(JsonUtil.toJson(inputs))
|
||||
.job(new RaftRemovePeerJob());
|
||||
return ImmutableMap.of("task_id", builder.schedule().id());
|
||||
}
|
||||
|
||||
private static RaftGroupManager raftGroupManager(HugeGraph graph,
|
||||
String group,
|
||||
String operation) {
|
||||
RaftGroupManager raftManager = graph.raftGroupManager();
|
||||
if (raftManager == null) {
|
||||
throw new HugeException("Allowed %s operation only when " +
|
||||
"working on raft mode", operation);
|
||||
}
|
||||
return raftManager;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,274 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.schema;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.schema.EdgeLabel;
|
||||
import org.apache.hugegraph.schema.Userdata;
|
||||
import org.apache.hugegraph.type.define.Frequency;
|
||||
import org.apache.hugegraph.type.define.GraphMode;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
@Path("graphs/{graph}/schema/edgelabels")
|
||||
@Singleton
|
||||
@Tag(name = "EdgeLabelAPI")
|
||||
public class EdgeLabelAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(EdgeLabelAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=edge_label_write"})
|
||||
public String create(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
JsonEdgeLabel jsonEdgeLabel) {
|
||||
LOG.debug("Graph [{}] create edge label: {}", graph, jsonEdgeLabel);
|
||||
checkCreatingBody(jsonEdgeLabel);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
EdgeLabel.Builder builder = jsonEdgeLabel.convert2Builder(g);
|
||||
EdgeLabel edgeLabel = builder.create();
|
||||
return manager.serializer(g).writeEdgeLabel(edgeLabel);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{name}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=edge_label_write"})
|
||||
public String update(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name,
|
||||
@QueryParam("action") String action,
|
||||
JsonEdgeLabel jsonEdgeLabel) {
|
||||
LOG.debug("Graph [{}] {} edge label: {}",
|
||||
graph, action, jsonEdgeLabel);
|
||||
|
||||
checkUpdatingBody(jsonEdgeLabel);
|
||||
E.checkArgument(name.equals(jsonEdgeLabel.name),
|
||||
"The name in url(%s) and body(%s) are different",
|
||||
name, jsonEdgeLabel.name);
|
||||
// Parse action param
|
||||
boolean append = checkAndParseAction(action);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
EdgeLabel.Builder builder = jsonEdgeLabel.convert2Builder(g);
|
||||
EdgeLabel edgeLabel = append ? builder.append() : builder.eliminate();
|
||||
return manager.serializer(g).writeEdgeLabel(edgeLabel);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=edge_label_read"})
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("names") List<String> names) {
|
||||
boolean listAll = CollectionUtils.isEmpty(names);
|
||||
if (listAll) {
|
||||
LOG.debug("Graph [{}] list edge labels", graph);
|
||||
} else {
|
||||
LOG.debug("Graph [{}] get edge labels by names {}", graph, names);
|
||||
}
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
List<EdgeLabel> labels;
|
||||
if (listAll) {
|
||||
labels = g.schema().getEdgeLabels();
|
||||
} else {
|
||||
labels = new ArrayList<>(names.size());
|
||||
for (String name : names) {
|
||||
labels.add(g.schema().getEdgeLabel(name));
|
||||
}
|
||||
}
|
||||
return manager.serializer(g).writeEdgeLabels(labels);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{name}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=edge_label_read"})
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Graph [{}] get edge label by name '{}'", graph, name);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
EdgeLabel edgeLabel = g.schema().getEdgeLabel(name);
|
||||
return manager.serializer(g).writeEdgeLabel(edgeLabel);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("{name}")
|
||||
@Status(Status.ACCEPTED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=edge_label_delete"})
|
||||
public Map<String, Id> delete(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Graph [{}] remove edge label by name '{}'", graph, name);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
// Throw 404 if not exists
|
||||
g.schema().getEdgeLabel(name);
|
||||
return ImmutableMap.of("task_id",
|
||||
g.schema().edgeLabel(name).remove());
|
||||
}
|
||||
|
||||
/**
|
||||
* JsonEdgeLabel is only used to receive create and append requests
|
||||
*/
|
||||
@JsonIgnoreProperties(value = {"index_labels", "status"})
|
||||
private static class JsonEdgeLabel implements Checkable {
|
||||
|
||||
@JsonProperty("id")
|
||||
public long id;
|
||||
@JsonProperty("name")
|
||||
public String name;
|
||||
@JsonProperty("source_label")
|
||||
public String sourceLabel;
|
||||
@JsonProperty("target_label")
|
||||
public String targetLabel;
|
||||
@JsonProperty("frequency")
|
||||
public Frequency frequency;
|
||||
@JsonProperty("properties")
|
||||
public String[] properties;
|
||||
@JsonProperty("sort_keys")
|
||||
public String[] sortKeys;
|
||||
@JsonProperty("nullable_keys")
|
||||
public String[] nullableKeys;
|
||||
@JsonProperty("ttl")
|
||||
public long ttl;
|
||||
@JsonProperty("ttl_start_time")
|
||||
public String ttlStartTime;
|
||||
@JsonProperty("enable_label_index")
|
||||
public Boolean enableLabelIndex;
|
||||
@JsonProperty("user_data")
|
||||
public Userdata userdata;
|
||||
@JsonProperty("check_exist")
|
||||
public Boolean checkExist;
|
||||
|
||||
@Override
|
||||
public void checkCreate(boolean isBatch) {
|
||||
E.checkArgumentNotNull(this.name,
|
||||
"The name of edge label can't be null");
|
||||
}
|
||||
|
||||
private EdgeLabel.Builder convert2Builder(HugeGraph g) {
|
||||
EdgeLabel.Builder builder = g.schema().edgeLabel(this.name);
|
||||
if (this.id != 0) {
|
||||
E.checkArgument(this.id > 0,
|
||||
"Only positive number can be assign as " +
|
||||
"edge label id");
|
||||
E.checkArgument(g.mode() == GraphMode.RESTORING,
|
||||
"Only accept edge label id when graph in " +
|
||||
"RESTORING mode, but '%s' is in mode '%s'",
|
||||
g, g.mode());
|
||||
builder.id(this.id);
|
||||
}
|
||||
if (this.sourceLabel != null) {
|
||||
builder.sourceLabel(this.sourceLabel);
|
||||
}
|
||||
if (this.targetLabel != null) {
|
||||
builder.targetLabel(this.targetLabel);
|
||||
}
|
||||
if (this.frequency != null) {
|
||||
builder.frequency(this.frequency);
|
||||
}
|
||||
if (this.properties != null) {
|
||||
builder.properties(this.properties);
|
||||
}
|
||||
if (this.sortKeys != null) {
|
||||
builder.sortKeys(this.sortKeys);
|
||||
}
|
||||
if (this.nullableKeys != null) {
|
||||
builder.nullableKeys(this.nullableKeys);
|
||||
}
|
||||
if (this.enableLabelIndex != null) {
|
||||
builder.enableLabelIndex(this.enableLabelIndex);
|
||||
}
|
||||
if (this.userdata != null) {
|
||||
builder.userdata(this.userdata);
|
||||
}
|
||||
if (this.checkExist != null) {
|
||||
builder.checkExist(this.checkExist);
|
||||
}
|
||||
if (this.ttl != 0) {
|
||||
builder.ttl(this.ttl);
|
||||
}
|
||||
if (this.ttlStartTime != null) {
|
||||
E.checkArgument(this.ttl > 0,
|
||||
"Only set ttlStartTime when ttl is " +
|
||||
"positive, but got ttl: %s", this.ttl);
|
||||
builder.ttlStartTime(this.ttlStartTime);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("JsonEdgeLabel{" +
|
||||
"name=%s, sourceLabel=%s, targetLabel=%s, frequency=%s, " +
|
||||
"sortKeys=%s, nullableKeys=%s, properties=%s, ttl=%s, " +
|
||||
"ttlStartTime=%s}",
|
||||
this.name, this.sourceLabel, this.targetLabel,
|
||||
this.frequency, this.sortKeys, this.nullableKeys,
|
||||
this.properties, this.ttl, this.ttlStartTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,294 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.schema;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.schema.IndexLabel;
|
||||
import org.apache.hugegraph.schema.SchemaElement;
|
||||
import org.apache.hugegraph.schema.Userdata;
|
||||
import org.apache.hugegraph.type.HugeType;
|
||||
import org.apache.hugegraph.type.define.GraphMode;
|
||||
import org.apache.hugegraph.type.define.IndexType;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
@Path("graphs/{graph}/schema/indexlabels")
|
||||
@Singleton
|
||||
@Tag(name = "IndexLabelAPI")
|
||||
public class IndexLabelAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(IndexLabelAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.ACCEPTED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=index_label_write"})
|
||||
public String create(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
JsonIndexLabel jsonIndexLabel) {
|
||||
LOG.debug("Graph [{}] create index label: {}", graph, jsonIndexLabel);
|
||||
checkCreatingBody(jsonIndexLabel);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
IndexLabel.Builder builder = jsonIndexLabel.convert2Builder(g);
|
||||
SchemaElement.TaskWithSchema il = builder.createWithTask();
|
||||
il.indexLabel(mapIndexLabel(il.indexLabel()));
|
||||
return manager.serializer(g).writeTaskWithSchema(il);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{name}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String update(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name,
|
||||
@QueryParam("action") String action,
|
||||
IndexLabelAPI.JsonIndexLabel jsonIndexLabel) {
|
||||
LOG.debug("Graph [{}] {} index label: {}",
|
||||
graph, action, jsonIndexLabel);
|
||||
checkUpdatingBody(jsonIndexLabel);
|
||||
E.checkArgument(name.equals(jsonIndexLabel.name),
|
||||
"The name in url(%s) and body(%s) are different",
|
||||
name, jsonIndexLabel.name);
|
||||
// Parse action parameter
|
||||
boolean append = checkAndParseAction(action);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
IndexLabel.Builder builder = jsonIndexLabel.convert2Builder(g);
|
||||
IndexLabel indexLabel = append ? builder.append() : builder.eliminate();
|
||||
return manager.serializer(g).writeIndexlabel(mapIndexLabel(indexLabel));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=index_label_read"})
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("names") List<String> names) {
|
||||
boolean listAll = CollectionUtils.isEmpty(names);
|
||||
if (listAll) {
|
||||
LOG.debug("Graph [{}] list index labels", graph);
|
||||
} else {
|
||||
LOG.debug("Graph [{}] get index labels by names {}", graph, names);
|
||||
}
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
List<IndexLabel> labels;
|
||||
if (listAll) {
|
||||
labels = g.schema().getIndexLabels();
|
||||
} else {
|
||||
labels = new ArrayList<>(names.size());
|
||||
for (String name : names) {
|
||||
labels.add(g.schema().getIndexLabel(name));
|
||||
}
|
||||
}
|
||||
return manager.serializer(g).writeIndexlabels(mapIndexLabels(labels));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{name}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=index_label_read"})
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Graph [{}] get index label by name '{}'", graph, name);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
IndexLabel indexLabel = g.schema().getIndexLabel(name);
|
||||
return manager.serializer(g).writeIndexlabel(mapIndexLabel(indexLabel));
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("{name}")
|
||||
@Status(Status.ACCEPTED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=index_label_delete"})
|
||||
public Map<String, Id> delete(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Graph [{}] remove index label by name '{}'", graph, name);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
// Throw 404 if not exists
|
||||
g.schema().getIndexLabel(name);
|
||||
return ImmutableMap.of("task_id",
|
||||
g.schema().indexLabel(name).remove());
|
||||
}
|
||||
|
||||
private static List<IndexLabel> mapIndexLabels(List<IndexLabel> labels) {
|
||||
List<IndexLabel> results = new ArrayList<>(labels.size());
|
||||
for (IndexLabel il : labels) {
|
||||
results.add(mapIndexLabel(il));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map RANGE_INT/RANGE_FLOAT/RANGE_LONG/RANGE_DOUBLE to RANGE
|
||||
*/
|
||||
private static IndexLabel mapIndexLabel(IndexLabel label) {
|
||||
if (label.indexType().isRange()) {
|
||||
label = (IndexLabel) label.copy();
|
||||
label.indexType(IndexType.RANGE);
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* JsonIndexLabel is only used to receive create and append requests
|
||||
*/
|
||||
@JsonIgnoreProperties(value = {"status"})
|
||||
private static class JsonIndexLabel implements Checkable {
|
||||
|
||||
@JsonProperty("id")
|
||||
public long id;
|
||||
@JsonProperty("name")
|
||||
public String name;
|
||||
@JsonProperty("base_type")
|
||||
public HugeType baseType;
|
||||
@JsonProperty("base_value")
|
||||
public String baseValue;
|
||||
@JsonProperty("index_type")
|
||||
public IndexType indexType;
|
||||
@JsonProperty("fields")
|
||||
public String[] fields;
|
||||
@JsonProperty("user_data")
|
||||
public Userdata userdata;
|
||||
@JsonProperty("check_exist")
|
||||
public Boolean checkExist;
|
||||
@JsonProperty("rebuild")
|
||||
public Boolean rebuild;
|
||||
|
||||
@Override
|
||||
public void checkCreate(boolean isBatch) {
|
||||
E.checkArgumentNotNull(this.name,
|
||||
"The name of index label can't be null");
|
||||
E.checkArgumentNotNull(this.baseType,
|
||||
"The base type of index label '%s' " +
|
||||
"can't be null", this.name);
|
||||
E.checkArgument(this.baseType == HugeType.VERTEX_LABEL ||
|
||||
this.baseType == HugeType.EDGE_LABEL,
|
||||
"The base type of index label '%s' can only be " +
|
||||
"either VERTEX_LABEL or EDGE_LABEL", this.name);
|
||||
E.checkArgumentNotNull(this.baseValue,
|
||||
"The base value of index label '%s' " +
|
||||
"can't be null", this.name);
|
||||
E.checkArgumentNotNull(this.indexType,
|
||||
"The index type of index label '%s' " +
|
||||
"can't be null", this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkUpdate() {
|
||||
E.checkArgumentNotNull(this.name,
|
||||
"The name of index label can't be null");
|
||||
E.checkArgument(this.baseType == null,
|
||||
"The base type of index label '%s' must be null",
|
||||
this.name);
|
||||
E.checkArgument(this.baseValue == null,
|
||||
"The base value of index label '%s' must be null",
|
||||
this.name);
|
||||
E.checkArgument(this.indexType == null,
|
||||
"The index type of index label '%s' must be null",
|
||||
this.name);
|
||||
}
|
||||
|
||||
private IndexLabel.Builder convert2Builder(HugeGraph g) {
|
||||
IndexLabel.Builder builder = g.schema().indexLabel(this.name);
|
||||
if (this.id != 0) {
|
||||
E.checkArgument(this.id > 0,
|
||||
"Only positive number can be assign as " +
|
||||
"index label id");
|
||||
E.checkArgument(g.mode() == GraphMode.RESTORING,
|
||||
"Only accept index label id when graph in " +
|
||||
"RESTORING mode, but '%s' is in mode '%s'",
|
||||
g, g.mode());
|
||||
builder.id(this.id);
|
||||
}
|
||||
if (this.baseType != null) {
|
||||
assert this.baseValue != null;
|
||||
builder.on(this.baseType, this.baseValue);
|
||||
}
|
||||
if (this.indexType != null) {
|
||||
builder.indexType(this.indexType);
|
||||
}
|
||||
if (this.fields != null && this.fields.length > 0) {
|
||||
builder.by(this.fields);
|
||||
}
|
||||
if (this.userdata != null) {
|
||||
builder.userdata(this.userdata);
|
||||
}
|
||||
if (this.checkExist != null) {
|
||||
builder.checkExist(this.checkExist);
|
||||
}
|
||||
if (this.rebuild != null) {
|
||||
builder.rebuild(this.rebuild);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("JsonIndexLabel{name=%s, baseType=%s," +
|
||||
"baseValue=%s, indexType=%s, fields=%s}",
|
||||
this.name, this.baseType, this.baseValue,
|
||||
this.indexType, this.fields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,274 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.schema;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.id.IdGenerator;
|
||||
import org.apache.hugegraph.schema.PropertyKey;
|
||||
import org.apache.hugegraph.schema.SchemaElement;
|
||||
import org.apache.hugegraph.schema.Userdata;
|
||||
import org.apache.hugegraph.type.define.AggregateType;
|
||||
import org.apache.hugegraph.type.define.Cardinality;
|
||||
import org.apache.hugegraph.type.define.DataType;
|
||||
import org.apache.hugegraph.type.define.GraphMode;
|
||||
import org.apache.hugegraph.type.define.WriteType;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
@Path("graphs/{graph}/schema/propertykeys")
|
||||
@Singleton
|
||||
@Tag(name = "PropertyKeyAPI")
|
||||
public class PropertyKeyAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(PropertyKeyAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.ACCEPTED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=property_key_write"})
|
||||
public String create(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
JsonPropertyKey jsonPropertyKey) {
|
||||
LOG.debug("Graph [{}] create property key: {}",
|
||||
graph, jsonPropertyKey);
|
||||
checkCreatingBody(jsonPropertyKey);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
PropertyKey.Builder builder = jsonPropertyKey.convert2Builder(g);
|
||||
SchemaElement.TaskWithSchema pk = builder.createWithTask();
|
||||
return manager.serializer(g).writeTaskWithSchema(pk);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Status(Status.ACCEPTED)
|
||||
@Path("{name}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=property_key_write"})
|
||||
public String update(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name,
|
||||
@QueryParam("action") String action,
|
||||
PropertyKeyAPI.JsonPropertyKey jsonPropertyKey) {
|
||||
LOG.debug("Graph [{}] {} property key: {}",
|
||||
graph, action, jsonPropertyKey);
|
||||
checkUpdatingBody(jsonPropertyKey);
|
||||
E.checkArgument(name.equals(jsonPropertyKey.name),
|
||||
"The name in url(%s) and body(%s) are different",
|
||||
name, jsonPropertyKey.name);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
if (ACTION_CLEAR.equals(action)) {
|
||||
PropertyKey propertyKey = g.propertyKey(name);
|
||||
E.checkArgument(propertyKey.olap(),
|
||||
"Only olap property key can do action clear, " +
|
||||
"but got '%s'", propertyKey);
|
||||
Id id = g.clearPropertyKey(propertyKey);
|
||||
SchemaElement.TaskWithSchema pk =
|
||||
new SchemaElement.TaskWithSchema(propertyKey, id);
|
||||
return manager.serializer(g).writeTaskWithSchema(pk);
|
||||
}
|
||||
|
||||
// Parse action parameter
|
||||
boolean append = checkAndParseAction(action);
|
||||
|
||||
PropertyKey.Builder builder = jsonPropertyKey.convert2Builder(g);
|
||||
PropertyKey propertyKey = append ?
|
||||
builder.append() :
|
||||
builder.eliminate();
|
||||
SchemaElement.TaskWithSchema pk =
|
||||
new SchemaElement.TaskWithSchema(propertyKey, IdGenerator.ZERO);
|
||||
return manager.serializer(g).writeTaskWithSchema(pk);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=property_key_read"})
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("names") List<String> names) {
|
||||
boolean listAll = CollectionUtils.isEmpty(names);
|
||||
if (listAll) {
|
||||
LOG.debug("Graph [{}] list property keys", graph);
|
||||
} else {
|
||||
LOG.debug("Graph [{}] get property keys by names {}", graph, names);
|
||||
}
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
List<PropertyKey> propKeys;
|
||||
if (listAll) {
|
||||
propKeys = g.schema().getPropertyKeys();
|
||||
} else {
|
||||
propKeys = new ArrayList<>(names.size());
|
||||
for (String name : names) {
|
||||
propKeys.add(g.schema().getPropertyKey(name));
|
||||
}
|
||||
}
|
||||
return manager.serializer(g).writePropertyKeys(propKeys);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{name}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=property_key_read"})
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Graph [{}] get property key by name '{}'", graph, name);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
PropertyKey propertyKey = g.schema().getPropertyKey(name);
|
||||
return manager.serializer(g).writePropertyKey(propertyKey);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Status(Status.ACCEPTED)
|
||||
@Path("{name}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=property_key_delete"})
|
||||
public Map<String, Id> delete(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Graph [{}] remove property key by name '{}'", graph, name);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
// Throw 404 if not exists
|
||||
g.schema().getPropertyKey(name);
|
||||
return ImmutableMap.of("task_id",
|
||||
g.schema().propertyKey(name).remove());
|
||||
}
|
||||
|
||||
/**
|
||||
* JsonPropertyKey is only used to receive create and append requests
|
||||
*/
|
||||
@JsonIgnoreProperties(value = {"status"})
|
||||
private static class JsonPropertyKey implements Checkable {
|
||||
|
||||
@JsonProperty("id")
|
||||
public long id;
|
||||
@JsonProperty("name")
|
||||
public String name;
|
||||
@JsonProperty("cardinality")
|
||||
public Cardinality cardinality;
|
||||
@JsonProperty("data_type")
|
||||
public DataType dataType;
|
||||
@JsonProperty("aggregate_type")
|
||||
public AggregateType aggregateType;
|
||||
@JsonProperty("write_type")
|
||||
public WriteType writeType;
|
||||
@JsonProperty("properties")
|
||||
public String[] properties;
|
||||
@JsonProperty("user_data")
|
||||
public Userdata userdata;
|
||||
@JsonProperty("check_exist")
|
||||
public Boolean checkExist;
|
||||
|
||||
@Override
|
||||
public void checkCreate(boolean isBatch) {
|
||||
E.checkArgumentNotNull(this.name,
|
||||
"The name of property key can't be null");
|
||||
E.checkArgument(this.properties == null ||
|
||||
this.properties.length == 0,
|
||||
"Not allowed to pass properties when " +
|
||||
"creating property key since it doesn't " +
|
||||
"support meta properties currently");
|
||||
}
|
||||
|
||||
private PropertyKey.Builder convert2Builder(HugeGraph g) {
|
||||
PropertyKey.Builder builder = g.schema().propertyKey(this.name);
|
||||
if (this.id != 0) {
|
||||
E.checkArgument(this.id > 0,
|
||||
"Only positive number can be assign as " +
|
||||
"property key id");
|
||||
E.checkArgument(g.mode() == GraphMode.RESTORING,
|
||||
"Only accept property key id when graph in " +
|
||||
"RESTORING mode, but '%s' is in mode '%s'",
|
||||
g, g.mode());
|
||||
builder.id(this.id);
|
||||
}
|
||||
if (this.cardinality != null) {
|
||||
builder.cardinality(this.cardinality);
|
||||
}
|
||||
if (this.dataType != null) {
|
||||
builder.dataType(this.dataType);
|
||||
}
|
||||
if (this.aggregateType != null) {
|
||||
builder.aggregateType(this.aggregateType);
|
||||
}
|
||||
if (this.writeType != null) {
|
||||
builder.writeType(this.writeType);
|
||||
}
|
||||
if (this.userdata != null) {
|
||||
builder.userdata(this.userdata);
|
||||
}
|
||||
if (this.checkExist != null) {
|
||||
builder.checkExist(this.checkExist);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("JsonPropertyKey{name=%s, cardinality=%s, " +
|
||||
"dataType=%s, aggregateType=%s, " +
|
||||
"writeType=%s, properties=%s}",
|
||||
this.name, this.cardinality,
|
||||
this.dataType, this.aggregateType,
|
||||
this.writeType, this.properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.schema;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.schema.SchemaManager;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Path("graphs/{graph}/schema")
|
||||
@Singleton
|
||||
@Tag(name = "SchemaAPI")
|
||||
public class SchemaAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(SchemaAPI.class);
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=schema_read"})
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph) {
|
||||
LOG.debug("Graph [{}] list all schema", graph);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
SchemaManager schema = g.schema();
|
||||
|
||||
Map<String, List<?>> schemaMap = new LinkedHashMap<>(4);
|
||||
schemaMap.put("propertykeys", schema.getPropertyKeys());
|
||||
schemaMap.put("vertexlabels", schema.getVertexLabels());
|
||||
schemaMap.put("edgelabels", schema.getEdgeLabels());
|
||||
schemaMap.put("indexlabels", schema.getIndexLabels());
|
||||
|
||||
return manager.serializer(g).writeMap(schemaMap);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,266 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.schema;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.StatusFilter.Status;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.schema.Userdata;
|
||||
import org.apache.hugegraph.schema.VertexLabel;
|
||||
import org.apache.hugegraph.type.define.GraphMode;
|
||||
import org.apache.hugegraph.type.define.IdStrategy;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
@Path("graphs/{graph}/schema/vertexlabels")
|
||||
@Singleton
|
||||
@Tag(name = "VertexLabelAPI")
|
||||
public class VertexLabelAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(VertexLabelAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=vertex_label_write"})
|
||||
public String create(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
JsonVertexLabel jsonVertexLabel) {
|
||||
LOG.debug("Graph [{}] create vertex label: {}",
|
||||
graph, jsonVertexLabel);
|
||||
checkCreatingBody(jsonVertexLabel);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
VertexLabel.Builder builder = jsonVertexLabel.convert2Builder(g);
|
||||
VertexLabel vertexLabel = builder.create();
|
||||
return manager.serializer(g).writeVertexLabel(vertexLabel);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Timed
|
||||
@Path("{name}")
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=vertex_label_write"})
|
||||
public String update(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name,
|
||||
@QueryParam("action") String action,
|
||||
JsonVertexLabel jsonVertexLabel) {
|
||||
LOG.debug("Graph [{}] {} vertex label: {}",
|
||||
graph, action, jsonVertexLabel);
|
||||
|
||||
checkUpdatingBody(jsonVertexLabel);
|
||||
E.checkArgument(name.equals(jsonVertexLabel.name),
|
||||
"The name in url(%s) and body(%s) are different",
|
||||
name, jsonVertexLabel.name);
|
||||
// Parse action parameter
|
||||
boolean append = checkAndParseAction(action);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
VertexLabel.Builder builder = jsonVertexLabel.convert2Builder(g);
|
||||
VertexLabel vertexLabel = append ?
|
||||
builder.append() :
|
||||
builder.eliminate();
|
||||
return manager.serializer(g).writeVertexLabel(vertexLabel);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=vertex_label_read"})
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("names") List<String> names) {
|
||||
boolean listAll = CollectionUtils.isEmpty(names);
|
||||
if (listAll) {
|
||||
LOG.debug("Graph [{}] list vertex labels", graph);
|
||||
} else {
|
||||
LOG.debug("Graph [{}] get vertex labels by names {}", graph, names);
|
||||
}
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
List<VertexLabel> labels;
|
||||
if (listAll) {
|
||||
labels = g.schema().getVertexLabels();
|
||||
} else {
|
||||
labels = new ArrayList<>(names.size());
|
||||
for (String name : names) {
|
||||
labels.add(g.schema().getVertexLabel(name));
|
||||
}
|
||||
}
|
||||
return manager.serializer(g).writeVertexLabels(labels);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("{name}")
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=vertex_label_read"})
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Graph [{}] get vertex label by name '{}'", graph, name);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
VertexLabel vertexLabel = g.schema().getVertexLabel(name);
|
||||
return manager.serializer(g).writeVertexLabel(vertexLabel);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Timed
|
||||
@Path("{name}")
|
||||
@Status(Status.ACCEPTED)
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
@RolesAllowed({"admin", "$owner=$graph $action=vertex_label_delete"})
|
||||
public Map<String, Id> delete(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("name") String name) {
|
||||
LOG.debug("Graph [{}] remove vertex label by name '{}'", graph, name);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
// Throw 404 if not exists
|
||||
g.schema().getVertexLabel(name);
|
||||
return ImmutableMap.of("task_id",
|
||||
g.schema().vertexLabel(name).remove());
|
||||
}
|
||||
|
||||
/**
|
||||
* JsonVertexLabel is only used to receive create and append requests
|
||||
*/
|
||||
@JsonIgnoreProperties(value = {"index_labels", "status"})
|
||||
private static class JsonVertexLabel implements Checkable {
|
||||
|
||||
@JsonProperty("id")
|
||||
public long id;
|
||||
@JsonProperty("name")
|
||||
public String name;
|
||||
@JsonProperty("id_strategy")
|
||||
public IdStrategy idStrategy;
|
||||
@JsonProperty("properties")
|
||||
public String[] properties;
|
||||
@JsonProperty("primary_keys")
|
||||
public String[] primaryKeys;
|
||||
@JsonProperty("nullable_keys")
|
||||
public String[] nullableKeys;
|
||||
@JsonProperty("ttl")
|
||||
public long ttl;
|
||||
@JsonProperty("ttl_start_time")
|
||||
public String ttlStartTime;
|
||||
@JsonProperty("enable_label_index")
|
||||
public Boolean enableLabelIndex;
|
||||
@JsonProperty("user_data")
|
||||
public Userdata userdata;
|
||||
@JsonProperty("check_exist")
|
||||
public Boolean checkExist;
|
||||
|
||||
@Override
|
||||
public void checkCreate(boolean isBatch) {
|
||||
E.checkArgumentNotNull(this.name,
|
||||
"The name of vertex label can't be null");
|
||||
}
|
||||
|
||||
private VertexLabel.Builder convert2Builder(HugeGraph g) {
|
||||
VertexLabel.Builder builder = g.schema().vertexLabel(this.name);
|
||||
if (this.id != 0) {
|
||||
E.checkArgument(this.id > 0,
|
||||
"Only positive number can be assign as " +
|
||||
"vertex label id");
|
||||
E.checkArgument(g.mode() == GraphMode.RESTORING,
|
||||
"Only accept vertex label id when graph in " +
|
||||
"RESTORING mode, but '%s' is in mode '%s'",
|
||||
g, g.mode());
|
||||
builder.id(this.id);
|
||||
}
|
||||
if (this.idStrategy != null) {
|
||||
builder.idStrategy(this.idStrategy);
|
||||
}
|
||||
if (this.properties != null) {
|
||||
builder.properties(this.properties);
|
||||
}
|
||||
if (this.primaryKeys != null) {
|
||||
builder.primaryKeys(this.primaryKeys);
|
||||
}
|
||||
if (this.nullableKeys != null) {
|
||||
builder.nullableKeys(this.nullableKeys);
|
||||
}
|
||||
if (this.enableLabelIndex != null) {
|
||||
builder.enableLabelIndex(this.enableLabelIndex);
|
||||
}
|
||||
if (this.userdata != null) {
|
||||
builder.userdata(this.userdata);
|
||||
}
|
||||
if (this.checkExist != null) {
|
||||
builder.checkExist(this.checkExist);
|
||||
}
|
||||
if (this.ttl != 0) {
|
||||
builder.ttl(this.ttl);
|
||||
}
|
||||
if (this.ttlStartTime != null) {
|
||||
E.checkArgument(this.ttl > 0,
|
||||
"Only set ttlStartTime when ttl is " +
|
||||
"positive, but got ttl: %s", this.ttl);
|
||||
builder.ttlStartTime(this.ttlStartTime);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("JsonVertexLabel{" +
|
||||
"name=%s, idStrategy=%s, primaryKeys=%s, nullableKeys=%s, " +
|
||||
"properties=%s, ttl=%s, ttlStartTime=%s}",
|
||||
this.name, this.idStrategy, this.primaryKeys,
|
||||
this.nullableKeys, this.properties, this.ttl,
|
||||
this.ttlStartTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.ShortestPathTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@Path("graphs/{graph}/traversers/allshortestpaths")
|
||||
@Singleton
|
||||
@Tag(name = "AllShortestPathsAPI")
|
||||
public class AllShortestPathsAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(AllShortestPathsAPI.class);
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("source") String source,
|
||||
@QueryParam("target") String target,
|
||||
@QueryParam("direction") String direction,
|
||||
@QueryParam("label") String edgeLabel,
|
||||
@QueryParam("max_depth") int depth,
|
||||
@QueryParam("max_degree")
|
||||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("skip_degree")
|
||||
@DefaultValue("0") long skipDegree,
|
||||
@QueryParam("capacity")
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity) {
|
||||
LOG.debug("Graph [{}] get shortest path from '{}', to '{}' with " +
|
||||
"direction {}, edge label {}, max depth '{}', " +
|
||||
"max degree '{}', skipped degree '{}' and capacity '{}'",
|
||||
graph, source, target, direction, edgeLabel, depth,
|
||||
maxDegree, skipDegree, capacity);
|
||||
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(source);
|
||||
Id targetId = VertexAPI.checkAndParseVertexId(target);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
||||
ShortestPathTraverser traverser = new ShortestPathTraverser(g);
|
||||
List<String> edgeLabels = edgeLabel == null ? ImmutableList.of() :
|
||||
ImmutableList.of(edgeLabel);
|
||||
HugeTraverser.PathSet paths = traverser.allShortestPaths(
|
||||
sourceId, targetId, dir, edgeLabels,
|
||||
depth, maxDegree, skipDegree, capacity);
|
||||
return manager.serializer(g).writePaths("paths", paths, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,200 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PATHS_LIMIT;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.traversal.algorithm.CustomizedCrosspointsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/traversers/customizedcrosspoints")
|
||||
@Singleton
|
||||
@Tag(name = "CustomizedCrosspointsAPI")
|
||||
public class CustomizedCrosspointsAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(CustomizedCrosspointsAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String post(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
CrosspointsRequest request) {
|
||||
E.checkArgumentNotNull(request,
|
||||
"The crosspoints request body can't be null");
|
||||
E.checkArgumentNotNull(request.sources,
|
||||
"The sources of crosspoints request " +
|
||||
"can't be null");
|
||||
E.checkArgument(request.pathPatterns != null &&
|
||||
!request.pathPatterns.isEmpty(),
|
||||
"The steps of crosspoints request can't be empty");
|
||||
|
||||
LOG.debug("Graph [{}] get customized crosspoints from source vertex " +
|
||||
"'{}', with path_pattern '{}', with_path '{}', with_vertex " +
|
||||
"'{}', capacity '{}' and limit '{}'", graph, request.sources,
|
||||
request.pathPatterns, request.withPath, request.withVertex,
|
||||
request.capacity, request.limit);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Iterator<Vertex> sources = request.sources.vertices(g);
|
||||
List<CustomizedCrosspointsTraverser.PathPattern> patterns;
|
||||
patterns = pathPatterns(g, request);
|
||||
|
||||
CustomizedCrosspointsTraverser traverser =
|
||||
new CustomizedCrosspointsTraverser(g);
|
||||
CustomizedCrosspointsTraverser.CrosspointsPaths paths;
|
||||
paths = traverser.crosspointsPaths(sources, patterns, request.capacity,
|
||||
request.limit);
|
||||
Iterator<Vertex> iter = QueryResults.emptyIterator();
|
||||
if (!request.withVertex) {
|
||||
return manager.serializer(g).writeCrosspoints(paths, iter,
|
||||
request.withPath);
|
||||
}
|
||||
Set<Id> ids = new HashSet<>();
|
||||
if (request.withPath) {
|
||||
for (HugeTraverser.Path p : paths.paths()) {
|
||||
ids.addAll(p.vertices());
|
||||
}
|
||||
} else {
|
||||
ids = paths.crosspoints();
|
||||
}
|
||||
if (!ids.isEmpty()) {
|
||||
iter = g.vertices(ids.toArray());
|
||||
}
|
||||
return manager.serializer(g).writeCrosspoints(paths, iter,
|
||||
request.withPath);
|
||||
}
|
||||
|
||||
private static List<CustomizedCrosspointsTraverser.PathPattern>
|
||||
pathPatterns(HugeGraph graph, CrosspointsRequest request) {
|
||||
int stepSize = request.pathPatterns.size();
|
||||
List<CustomizedCrosspointsTraverser.PathPattern> pathPatterns;
|
||||
pathPatterns = new ArrayList<>(stepSize);
|
||||
for (PathPattern pattern : request.pathPatterns) {
|
||||
CustomizedCrosspointsTraverser.PathPattern pathPattern;
|
||||
pathPattern = new CustomizedCrosspointsTraverser.PathPattern();
|
||||
for (Step step : pattern.steps) {
|
||||
pathPattern.add(step.jsonToStep(graph));
|
||||
}
|
||||
pathPatterns.add(pathPattern);
|
||||
}
|
||||
return pathPatterns;
|
||||
}
|
||||
|
||||
private static class CrosspointsRequest {
|
||||
|
||||
@JsonProperty("sources")
|
||||
public Vertices sources;
|
||||
@JsonProperty("path_patterns")
|
||||
public List<PathPattern> pathPatterns;
|
||||
@JsonProperty("capacity")
|
||||
public long capacity = Long.parseLong(DEFAULT_CAPACITY);
|
||||
@JsonProperty("limit")
|
||||
public int limit = Integer.parseInt(DEFAULT_PATHS_LIMIT);
|
||||
@JsonProperty("with_path")
|
||||
public boolean withPath = false;
|
||||
@JsonProperty("with_vertex")
|
||||
public boolean withVertex = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("CrosspointsRequest{sourceVertex=%s," +
|
||||
"pathPatterns=%s,withPath=%s,withVertex=%s," +
|
||||
"capacity=%s,limit=%s}", this.sources,
|
||||
this.pathPatterns, this.withPath,
|
||||
this.withVertex, this.capacity, this.limit);
|
||||
}
|
||||
}
|
||||
|
||||
private static class PathPattern {
|
||||
|
||||
@JsonProperty("steps")
|
||||
public List<Step> steps;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PathPattern{steps=%s", this.steps);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Step {
|
||||
|
||||
@JsonProperty("direction")
|
||||
public Directions direction;
|
||||
@JsonProperty("labels")
|
||||
public List<String> labels;
|
||||
@JsonProperty("properties")
|
||||
public Map<String, Object> properties;
|
||||
@JsonAlias("degree")
|
||||
@JsonProperty("max_degree")
|
||||
public long maxDegree = Long.parseLong(DEFAULT_MAX_DEGREE);
|
||||
@JsonProperty("skip_degree")
|
||||
public long skipDegree = 0L;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Step{direction=%s,labels=%s,properties=%s," +
|
||||
"maxDegree=%s,skipDegree=%s}",
|
||||
this.direction, this.labels, this.properties,
|
||||
this.maxDegree, this.skipDegree);
|
||||
}
|
||||
|
||||
private CustomizedCrosspointsTraverser.Step jsonToStep(HugeGraph g) {
|
||||
return new CustomizedCrosspointsTraverser.Step(g, this.direction,
|
||||
this.labels,
|
||||
this.properties,
|
||||
this.maxDegree,
|
||||
this.skipDegree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PATHS_LIMIT;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_SAMPLE;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_WEIGHT;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.traversal.algorithm.CustomizePathsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.WeightedEdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/traversers/customizedpaths")
|
||||
@Singleton
|
||||
@Tag(name = "CustomizedPathsAPI")
|
||||
public class CustomizedPathsAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(CustomizedPathsAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String post(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
PathRequest request) {
|
||||
E.checkArgumentNotNull(request, "The path request body can't be null");
|
||||
E.checkArgumentNotNull(request.sources,
|
||||
"The sources of path request can't be null");
|
||||
E.checkArgument(request.steps != null && !request.steps.isEmpty(),
|
||||
"The steps of path request can't be empty");
|
||||
if (request.sortBy == null) {
|
||||
request.sortBy = SortBy.NONE;
|
||||
}
|
||||
|
||||
LOG.debug("Graph [{}] get customized paths from source vertex '{}', " +
|
||||
"with steps '{}', sort by '{}', capacity '{}', limit '{}' " +
|
||||
"and with_vertex '{}'", graph, request.sources, request.steps,
|
||||
request.sortBy, request.capacity, request.limit,
|
||||
request.withVertex);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Iterator<Vertex> sources = request.sources.vertices(g);
|
||||
List<WeightedEdgeStep> steps = step(g, request);
|
||||
boolean sorted = request.sortBy != SortBy.NONE;
|
||||
|
||||
CustomizePathsTraverser traverser = new CustomizePathsTraverser(g);
|
||||
List<HugeTraverser.Path> paths;
|
||||
paths = traverser.customizedPaths(sources, steps, sorted,
|
||||
request.capacity, request.limit);
|
||||
|
||||
if (sorted) {
|
||||
boolean incr = request.sortBy == SortBy.INCR;
|
||||
paths = CustomizePathsTraverser.topNPath(paths, incr,
|
||||
request.limit);
|
||||
}
|
||||
|
||||
if (!request.withVertex) {
|
||||
return manager.serializer(g).writePaths("paths", paths, false);
|
||||
}
|
||||
|
||||
Set<Id> ids = new HashSet<>();
|
||||
for (HugeTraverser.Path p : paths) {
|
||||
ids.addAll(p.vertices());
|
||||
}
|
||||
Iterator<Vertex> iter = QueryResults.emptyIterator();
|
||||
if (!ids.isEmpty()) {
|
||||
iter = g.vertices(ids.toArray());
|
||||
}
|
||||
return manager.serializer(g).writePaths("paths", paths, false, iter);
|
||||
}
|
||||
|
||||
private static List<WeightedEdgeStep> step(HugeGraph graph,
|
||||
PathRequest req) {
|
||||
int stepSize = req.steps.size();
|
||||
List<WeightedEdgeStep> steps = new ArrayList<>(stepSize);
|
||||
for (Step step : req.steps) {
|
||||
steps.add(step.jsonToStep(graph));
|
||||
}
|
||||
return steps;
|
||||
}
|
||||
|
||||
private static class PathRequest {
|
||||
|
||||
@JsonProperty("sources")
|
||||
public Vertices sources;
|
||||
@JsonProperty("steps")
|
||||
public List<Step> steps;
|
||||
@JsonProperty("sort_by")
|
||||
public SortBy sortBy;
|
||||
@JsonProperty("capacity")
|
||||
public long capacity = Long.parseLong(DEFAULT_CAPACITY);
|
||||
@JsonProperty("limit")
|
||||
public int limit = Integer.parseInt(DEFAULT_PATHS_LIMIT);
|
||||
@JsonProperty("with_vertex")
|
||||
public boolean withVertex = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PathRequest{sourceVertex=%s,steps=%s," +
|
||||
"sortBy=%s,capacity=%s,limit=%s," +
|
||||
"withVertex=%s}", this.sources, this.steps,
|
||||
this.sortBy, this.capacity, this.limit,
|
||||
this.withVertex);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Step {
|
||||
|
||||
@JsonProperty("direction")
|
||||
public Directions direction;
|
||||
@JsonProperty("labels")
|
||||
public List<String> labels;
|
||||
@JsonProperty("properties")
|
||||
public Map<String, Object> properties;
|
||||
@JsonAlias("degree")
|
||||
@JsonProperty("max_degree")
|
||||
public long maxDegree = Long.parseLong(DEFAULT_MAX_DEGREE);
|
||||
@JsonProperty("skip_degree")
|
||||
public long skipDegree = 0L;
|
||||
@JsonProperty("weight_by")
|
||||
public String weightBy;
|
||||
@JsonProperty("default_weight")
|
||||
public double defaultWeight = Double.parseDouble(DEFAULT_WEIGHT);
|
||||
@JsonProperty("sample")
|
||||
public long sample = Long.parseLong(DEFAULT_SAMPLE);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Step{direction=%s,labels=%s,properties=%s," +
|
||||
"maxDegree=%s,skipDegree=%s," +
|
||||
"weightBy=%s,defaultWeight=%s,sample=%s}",
|
||||
this.direction, this.labels, this.properties,
|
||||
this.maxDegree, this.skipDegree,
|
||||
this.weightBy, this.defaultWeight,
|
||||
this.sample);
|
||||
}
|
||||
|
||||
private WeightedEdgeStep jsonToStep(HugeGraph g) {
|
||||
return new WeightedEdgeStep(g, this.direction, this.labels,
|
||||
this.properties, this.maxDegree,
|
||||
this.skipDegree, this.weightBy,
|
||||
this.defaultWeight, this.sample);
|
||||
}
|
||||
}
|
||||
|
||||
private enum SortBy {
|
||||
INCR,
|
||||
DECR,
|
||||
NONE
|
||||
}
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PAGE_LIMIT;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.CompressInterceptor.Compress;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.ConditionQuery;
|
||||
import org.apache.hugegraph.backend.store.Shard;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.type.HugeType;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Path("graphs/{graph}/traversers/edges")
|
||||
@Singleton
|
||||
@Tag(name = "EdgesAPI")
|
||||
public class EdgesAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(EdgesAPI.class);
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Compress
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("ids") List<String> stringIds) {
|
||||
LOG.debug("Graph [{}] get edges by ids: {}", graph, stringIds);
|
||||
|
||||
E.checkArgument(stringIds != null && !stringIds.isEmpty(),
|
||||
"The ids parameter can't be null or empty");
|
||||
|
||||
Object[] ids = new Id[stringIds.size()];
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
ids[i] = HugeEdge.getIdValue(stringIds.get(i), false);
|
||||
}
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
||||
Iterator<Edge> edges = g.edges(ids);
|
||||
return manager.serializer(g).writeEdges(edges, false);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("shards")
|
||||
@Compress
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String shards(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("split_size") long splitSize) {
|
||||
LOG.debug("Graph [{}] get vertex shards with split size '{}'",
|
||||
graph, splitSize);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
List<Shard> shards = g.metadata(HugeType.EDGE_OUT, "splits", splitSize);
|
||||
return manager.serializer(g).writeList("shards", shards);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Path("scan")
|
||||
@Compress
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String scan(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("start") String start,
|
||||
@QueryParam("end") String end,
|
||||
@QueryParam("page") String page,
|
||||
@QueryParam("page_limit")
|
||||
@DefaultValue(DEFAULT_PAGE_LIMIT) long pageLimit) {
|
||||
LOG.debug("Graph [{}] query edges by shard(start: {}, end: {}, " +
|
||||
"page: {}) ", graph, start, end, page);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
||||
ConditionQuery query = new ConditionQuery(HugeType.EDGE_OUT);
|
||||
query.scan(start, end);
|
||||
query.page(page);
|
||||
if (query.paging()) {
|
||||
query.limit(pageLimit);
|
||||
}
|
||||
Iterator<Edge> edges = g.edges(query);
|
||||
|
||||
return manager.serializer(g).writeEdges(edges, query.paging());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_ELEMENTS_LIMIT;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.Query;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.KneighborTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.records.KneighborRecords;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@Path("graphs/{graph}/traversers/kneighbor")
|
||||
@Singleton
|
||||
@Tag(name = "KneighborAPI")
|
||||
public class KneighborAPI extends TraverserAPI {
|
||||
|
||||
private static final Logger LOG = Log.logger(KneighborAPI.class);
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("source") String sourceV,
|
||||
@QueryParam("direction") String direction,
|
||||
@QueryParam("label") String edgeLabel,
|
||||
@QueryParam("max_depth") int depth,
|
||||
@QueryParam("max_degree")
|
||||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("limit")
|
||||
@DefaultValue(DEFAULT_ELEMENTS_LIMIT) int limit) {
|
||||
LOG.debug("Graph [{}] get k-neighbor from '{}' with " +
|
||||
"direction '{}', edge label '{}', max depth '{}', " +
|
||||
"max degree '{}' and limit '{}'",
|
||||
graph, sourceV, direction, edgeLabel, depth,
|
||||
maxDegree, limit);
|
||||
|
||||
Id source = VertexAPI.checkAndParseVertexId(sourceV);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
||||
Set<Id> ids;
|
||||
try (KneighborTraverser traverser = new KneighborTraverser(g)) {
|
||||
ids = traverser.kneighbor(source, dir, edgeLabel,
|
||||
depth, maxDegree, limit);
|
||||
}
|
||||
return manager.serializer(g).writeList("vertices", ids);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String post(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
Request request) {
|
||||
E.checkArgumentNotNull(request, "The request body can't be null");
|
||||
E.checkArgumentNotNull(request.source,
|
||||
"The source of request can't be null");
|
||||
E.checkArgument(request.step != null,
|
||||
"The steps of request can't be null");
|
||||
if (request.countOnly) {
|
||||
E.checkArgument(!request.withVertex && !request.withPath,
|
||||
"Can't return vertex or path when count only");
|
||||
}
|
||||
|
||||
LOG.debug("Graph [{}] get customized kneighbor from source vertex " +
|
||||
"'{}', with step '{}', limit '{}', count_only '{}', " +
|
||||
"with_vertex '{}' and with_path '{}'",
|
||||
graph, request.source, request.step, request.limit,
|
||||
request.countOnly, request.withVertex, request.withPath);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Id sourceId = HugeVertex.getIdValue(request.source);
|
||||
|
||||
EdgeStep step = step(g, request.step);
|
||||
|
||||
KneighborRecords results;
|
||||
try (KneighborTraverser traverser = new KneighborTraverser(g)) {
|
||||
results = traverser.customizedKneighbor(sourceId, step,
|
||||
request.maxDepth,
|
||||
request.limit);
|
||||
}
|
||||
|
||||
long size = results.size();
|
||||
if (request.limit != Query.NO_LIMIT && size > request.limit) {
|
||||
size = request.limit;
|
||||
}
|
||||
List<Id> neighbors = request.countOnly ?
|
||||
ImmutableList.of() : results.ids(request.limit);
|
||||
|
||||
HugeTraverser.PathSet paths = new HugeTraverser.PathSet();
|
||||
if (request.withPath) {
|
||||
paths.addAll(results.paths(request.limit));
|
||||
}
|
||||
Iterator<Vertex> iter = QueryResults.emptyIterator();
|
||||
if (request.withVertex && !request.countOnly) {
|
||||
Set<Id> ids = new HashSet<>(neighbors);
|
||||
if (request.withPath) {
|
||||
for (HugeTraverser.Path p : paths) {
|
||||
ids.addAll(p.vertices());
|
||||
}
|
||||
}
|
||||
if (!ids.isEmpty()) {
|
||||
iter = g.vertices(ids.toArray());
|
||||
}
|
||||
}
|
||||
return manager.serializer(g).writeNodesWithPath("kneighbor", neighbors,
|
||||
size, paths, iter);
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
|
||||
@JsonProperty("source")
|
||||
public Object source;
|
||||
@JsonProperty("step")
|
||||
public TraverserAPI.Step step;
|
||||
@JsonProperty("max_depth")
|
||||
public int maxDepth;
|
||||
@JsonProperty("limit")
|
||||
public int limit = Integer.parseInt(DEFAULT_ELEMENTS_LIMIT);
|
||||
@JsonProperty("count_only")
|
||||
public boolean countOnly = false;
|
||||
@JsonProperty("with_vertex")
|
||||
public boolean withVertex = false;
|
||||
@JsonProperty("with_path")
|
||||
public boolean withPath = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PathRequest{source=%s,step=%s,maxDepth=%s" +
|
||||
"limit=%s,countOnly=%s,withVertex=%s," +
|
||||
"withPath=%s}", this.source, this.step,
|
||||
this.maxDepth, this.limit, this.countOnly,
|
||||
this.withVertex, this.withPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_ELEMENTS_LIMIT;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.Query;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.KoutTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.records.KoutRecords;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@Path("graphs/{graph}/traversers/kout")
|
||||
@Singleton
|
||||
@Tag(name = "KoutAPI")
|
||||
public class KoutAPI extends TraverserAPI {
|
||||
|
||||
private static final Logger LOG = Log.logger(KoutAPI.class);
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("source") String source,
|
||||
@QueryParam("direction") String direction,
|
||||
@QueryParam("label") String edgeLabel,
|
||||
@QueryParam("max_depth") int depth,
|
||||
@QueryParam("nearest")
|
||||
@DefaultValue("true") boolean nearest,
|
||||
@QueryParam("max_degree")
|
||||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("capacity")
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity,
|
||||
@QueryParam("limit")
|
||||
@DefaultValue(DEFAULT_ELEMENTS_LIMIT) int limit) {
|
||||
LOG.debug("Graph [{}] get k-out from '{}' with " +
|
||||
"direction '{}', edge label '{}', max depth '{}', nearest " +
|
||||
"'{}', max degree '{}', capacity '{}' and limit '{}'",
|
||||
graph, source, direction, edgeLabel, depth, nearest,
|
||||
maxDegree, capacity, limit);
|
||||
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(source);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
||||
Set<Id> ids;
|
||||
try (KoutTraverser traverser = new KoutTraverser(g)) {
|
||||
ids = traverser.kout(sourceId, dir, edgeLabel, depth,
|
||||
nearest, maxDegree, capacity, limit);
|
||||
}
|
||||
return manager.serializer(g).writeList("vertices", ids);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String post(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
Request request) {
|
||||
E.checkArgumentNotNull(request, "The request body can't be null");
|
||||
E.checkArgumentNotNull(request.source,
|
||||
"The source of request can't be null");
|
||||
E.checkArgument(request.step != null,
|
||||
"The steps of request can't be null");
|
||||
if (request.countOnly) {
|
||||
E.checkArgument(!request.withVertex && !request.withPath,
|
||||
"Can't return vertex or path when count only");
|
||||
}
|
||||
|
||||
LOG.debug("Graph [{}] get customized kout from source vertex '{}', " +
|
||||
"with step '{}', max_depth '{}', nearest '{}', " +
|
||||
"count_only '{}', capacity '{}', limit '{}', " +
|
||||
"with_vertex '{}' and with_path '{}'",
|
||||
graph, request.source, request.step, request.maxDepth,
|
||||
request.nearest, request.countOnly, request.capacity,
|
||||
request.limit, request.withVertex, request.withPath);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Id sourceId = HugeVertex.getIdValue(request.source);
|
||||
|
||||
EdgeStep step = step(g, request.step);
|
||||
|
||||
KoutRecords results;
|
||||
try (KoutTraverser traverser = new KoutTraverser(g)) {
|
||||
results = traverser.customizedKout(sourceId, step,
|
||||
request.maxDepth,
|
||||
request.nearest,
|
||||
request.capacity,
|
||||
request.limit);
|
||||
}
|
||||
|
||||
long size = results.size();
|
||||
if (request.limit != Query.NO_LIMIT && size > request.limit) {
|
||||
size = request.limit;
|
||||
}
|
||||
List<Id> neighbors = request.countOnly ?
|
||||
ImmutableList.of() : results.ids(request.limit);
|
||||
|
||||
HugeTraverser.PathSet paths = new HugeTraverser.PathSet();
|
||||
if (request.withPath) {
|
||||
paths.addAll(results.paths(request.limit));
|
||||
}
|
||||
|
||||
Iterator<Vertex> iter = QueryResults.emptyIterator();
|
||||
if (request.withVertex && !request.countOnly) {
|
||||
Set<Id> ids = new HashSet<>(neighbors);
|
||||
if (request.withPath) {
|
||||
for (HugeTraverser.Path p : paths) {
|
||||
ids.addAll(p.vertices());
|
||||
}
|
||||
}
|
||||
if (!ids.isEmpty()) {
|
||||
iter = g.vertices(ids.toArray());
|
||||
}
|
||||
}
|
||||
return manager.serializer(g).writeNodesWithPath("kout", neighbors,
|
||||
size, paths, iter);
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
|
||||
@JsonProperty("source")
|
||||
public Object source;
|
||||
@JsonProperty("step")
|
||||
public TraverserAPI.Step step;
|
||||
@JsonProperty("max_depth")
|
||||
public int maxDepth;
|
||||
@JsonProperty("nearest")
|
||||
public boolean nearest = true;
|
||||
@JsonProperty("count_only")
|
||||
public boolean countOnly = false;
|
||||
@JsonProperty("capacity")
|
||||
public long capacity = Long.parseLong(DEFAULT_CAPACITY);
|
||||
@JsonProperty("limit")
|
||||
public int limit = Integer.parseInt(DEFAULT_ELEMENTS_LIMIT);
|
||||
@JsonProperty("with_vertex")
|
||||
public boolean withVertex = false;
|
||||
@JsonProperty("with_path")
|
||||
public boolean withPath = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("KoutRequest{source=%s,step=%s,maxDepth=%s" +
|
||||
"nearest=%s,countOnly=%s,capacity=%s," +
|
||||
"limit=%s,withVertex=%s,withPath=%s}",
|
||||
this.source, this.step, this.maxDepth,
|
||||
this.nearest, this.countOnly, this.capacity,
|
||||
this.limit, this.withVertex, this.withPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.MultiNodeShortestPathTraverser;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/traversers/multinodeshortestpath")
|
||||
@Singleton
|
||||
@Tag(name = "MultiNodeShortestPathAPI")
|
||||
public class MultiNodeShortestPathAPI extends TraverserAPI {
|
||||
|
||||
private static final Logger LOG = Log.logger(MultiNodeShortestPathAPI.class);
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String post(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
Request request) {
|
||||
E.checkArgumentNotNull(request, "The request body can't be null");
|
||||
E.checkArgumentNotNull(request.vertices,
|
||||
"The vertices of request can't be null");
|
||||
E.checkArgument(request.step != null,
|
||||
"The steps of request can't be null");
|
||||
|
||||
LOG.debug("Graph [{}] get multiple node shortest path from " +
|
||||
"vertices '{}', with step '{}', max_depth '{}', capacity " +
|
||||
"'{}' and with_vertex '{}'",
|
||||
graph, request.vertices, request.step, request.maxDepth,
|
||||
request.capacity, request.withVertex);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Iterator<Vertex> vertices = request.vertices.vertices(g);
|
||||
|
||||
EdgeStep step = step(g, request.step);
|
||||
|
||||
List<HugeTraverser.Path> paths;
|
||||
try (MultiNodeShortestPathTraverser traverser =
|
||||
new MultiNodeShortestPathTraverser(g)) {
|
||||
paths = traverser.multiNodeShortestPath(vertices, step,
|
||||
request.maxDepth,
|
||||
request.capacity);
|
||||
}
|
||||
|
||||
if (!request.withVertex) {
|
||||
return manager.serializer(g).writePaths("paths", paths, false);
|
||||
}
|
||||
|
||||
Set<Id> ids = new HashSet<>();
|
||||
for (HugeTraverser.Path p : paths) {
|
||||
ids.addAll(p.vertices());
|
||||
}
|
||||
Iterator<Vertex> iter = QueryResults.emptyIterator();
|
||||
if (!ids.isEmpty()) {
|
||||
iter = g.vertices(ids.toArray());
|
||||
}
|
||||
return manager.serializer(g).writePaths("paths", paths, false, iter);
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
|
||||
@JsonProperty("vertices")
|
||||
public Vertices vertices;
|
||||
@JsonProperty("step")
|
||||
public Step step;
|
||||
@JsonProperty("max_depth")
|
||||
public int maxDepth;
|
||||
@JsonProperty("capacity")
|
||||
public long capacity = Long.parseLong(DEFAULT_CAPACITY);
|
||||
@JsonProperty("with_vertex")
|
||||
public boolean withVertex = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Request{vertices=%s,step=%s,maxDepth=%s" +
|
||||
"capacity=%s,withVertex=%s}",
|
||||
this.vertices, this.step, this.maxDepth,
|
||||
this.capacity, this.withVertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEPTH;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PATHS_LIMIT;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.traversal.algorithm.CollectionPathsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.PathsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/traversers/paths")
|
||||
@Singleton
|
||||
@Tag(name = "PathsAPI")
|
||||
public class PathsAPI extends TraverserAPI {
|
||||
|
||||
private static final Logger LOG = Log.logger(PathsAPI.class);
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("source") String source,
|
||||
@QueryParam("target") String target,
|
||||
@QueryParam("direction") String direction,
|
||||
@QueryParam("label") String edgeLabel,
|
||||
@QueryParam("max_depth") int depth,
|
||||
@QueryParam("max_degree")
|
||||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("capacity")
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity,
|
||||
@QueryParam("limit")
|
||||
@DefaultValue(DEFAULT_PATHS_LIMIT) int limit) {
|
||||
LOG.debug("Graph [{}] get paths from '{}', to '{}' with " +
|
||||
"direction {}, edge label {}, max depth '{}', " +
|
||||
"max degree '{}', capacity '{}' and limit '{}'",
|
||||
graph, source, target, direction, edgeLabel, depth,
|
||||
maxDegree, capacity, limit);
|
||||
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(source);
|
||||
Id targetId = VertexAPI.checkAndParseVertexId(target);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
PathsTraverser traverser = new PathsTraverser(g);
|
||||
HugeTraverser.PathSet paths = traverser.paths(sourceId, dir, targetId,
|
||||
dir.opposite(), edgeLabel,
|
||||
depth, maxDegree, capacity,
|
||||
limit);
|
||||
return manager.serializer(g).writePaths("paths", paths, false);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Consumes(APPLICATION_JSON)
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String post(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
Request request) {
|
||||
E.checkArgumentNotNull(request, "The request body can't be null");
|
||||
E.checkArgumentNotNull(request.sources,
|
||||
"The sources of request can't be null");
|
||||
E.checkArgumentNotNull(request.targets,
|
||||
"The targets of request can't be null");
|
||||
E.checkArgumentNotNull(request.step,
|
||||
"The step of request can't be null");
|
||||
E.checkArgument(request.depth > 0 && request.depth <= DEFAULT_MAX_DEPTH,
|
||||
"The depth of request must be in (0, %s], " +
|
||||
"but got: %s", DEFAULT_MAX_DEPTH, request.depth);
|
||||
|
||||
LOG.debug("Graph [{}] get paths from source vertices '{}', target " +
|
||||
"vertices '{}', with step '{}', max depth '{}', " +
|
||||
"capacity '{}', limit '{}' and with_vertex '{}'",
|
||||
graph, request.sources, request.targets, request.step,
|
||||
request.depth, request.capacity, request.limit,
|
||||
request.withVertex);
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Iterator<Vertex> sources = request.sources.vertices(g);
|
||||
Iterator<Vertex> targets = request.targets.vertices(g);
|
||||
EdgeStep step = step(g, request.step);
|
||||
|
||||
CollectionPathsTraverser traverser = new CollectionPathsTraverser(g);
|
||||
Collection<HugeTraverser.Path> paths;
|
||||
paths = traverser.paths(sources, targets, step, request.depth,
|
||||
request.nearest, request.capacity,
|
||||
request.limit);
|
||||
|
||||
if (!request.withVertex) {
|
||||
return manager.serializer(g).writePaths("paths", paths, false);
|
||||
}
|
||||
|
||||
Set<Id> ids = new HashSet<>();
|
||||
for (HugeTraverser.Path p : paths) {
|
||||
ids.addAll(p.vertices());
|
||||
}
|
||||
Iterator<Vertex> iter = QueryResults.emptyIterator();
|
||||
if (!ids.isEmpty()) {
|
||||
iter = g.vertices(ids.toArray());
|
||||
}
|
||||
return manager.serializer(g).writePaths("paths", paths, false, iter);
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
|
||||
@JsonProperty("sources")
|
||||
public Vertices sources;
|
||||
@JsonProperty("targets")
|
||||
public Vertices targets;
|
||||
@JsonProperty("step")
|
||||
public TraverserAPI.Step step;
|
||||
@JsonProperty("max_depth")
|
||||
public int depth;
|
||||
@JsonProperty("nearest")
|
||||
public boolean nearest = false;
|
||||
@JsonProperty("capacity")
|
||||
public long capacity = Long.parseLong(DEFAULT_CAPACITY);
|
||||
@JsonProperty("limit")
|
||||
public int limit = Integer.parseInt(DEFAULT_PATHS_LIMIT);
|
||||
@JsonProperty("with_vertex")
|
||||
public boolean withVertex = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PathRequest{sources=%s,targets=%s,step=%s," +
|
||||
"maxDepth=%s,nearest=%s,capacity=%s," +
|
||||
"limit=%s,withVertex=%s}", this.sources,
|
||||
this.targets, this.step, this.depth,
|
||||
this.nearest, this.capacity,
|
||||
this.limit, this.withVertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PATHS_LIMIT;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.SubGraphTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Path("graphs/{graph}/traversers/rays")
|
||||
@Singleton
|
||||
@Tag(name = "RaysAPI")
|
||||
public class RaysAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(RaysAPI.class);
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("source") String sourceV,
|
||||
@QueryParam("direction") String direction,
|
||||
@QueryParam("label") String edgeLabel,
|
||||
@QueryParam("max_depth") int depth,
|
||||
@QueryParam("max_degree")
|
||||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("capacity")
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity,
|
||||
@QueryParam("limit")
|
||||
@DefaultValue(DEFAULT_PATHS_LIMIT) int limit) {
|
||||
LOG.debug("Graph [{}] get rays paths from '{}' with " +
|
||||
"direction '{}', edge label '{}', max depth '{}', " +
|
||||
"max degree '{}', capacity '{}' and limit '{}'",
|
||||
graph, sourceV, direction, edgeLabel, depth, maxDegree,
|
||||
capacity, limit);
|
||||
|
||||
Id source = VertexAPI.checkAndParseVertexId(sourceV);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
||||
SubGraphTraverser traverser = new SubGraphTraverser(g);
|
||||
HugeTraverser.PathSet paths = traverser.rays(source, dir, edgeLabel,
|
||||
depth, maxDegree,
|
||||
capacity, limit);
|
||||
return manager.serializer(g).writePaths("rays", paths, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PATHS_LIMIT;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.SubGraphTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Path("graphs/{graph}/traversers/rings")
|
||||
@Singleton
|
||||
@Tag(name = "RingsAPI")
|
||||
public class RingsAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(RingsAPI.class);
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("source") String sourceV,
|
||||
@QueryParam("direction") String direction,
|
||||
@QueryParam("label") String edgeLabel,
|
||||
@QueryParam("max_depth") int depth,
|
||||
@QueryParam("source_in_ring")
|
||||
@DefaultValue("true") boolean sourceInRing,
|
||||
@QueryParam("max_degree")
|
||||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("capacity")
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity,
|
||||
@QueryParam("limit")
|
||||
@DefaultValue(DEFAULT_PATHS_LIMIT) int limit) {
|
||||
LOG.debug("Graph [{}] get rings paths reachable from '{}' with " +
|
||||
"direction '{}', edge label '{}', max depth '{}', " +
|
||||
"source in ring '{}', max degree '{}', capacity '{}' " +
|
||||
"and limit '{}'",
|
||||
graph, sourceV, direction, edgeLabel, depth, sourceInRing,
|
||||
maxDegree, capacity, limit);
|
||||
|
||||
Id source = VertexAPI.checkAndParseVertexId(sourceV);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
||||
SubGraphTraverser traverser = new SubGraphTraverser(g);
|
||||
HugeTraverser.PathSet paths = traverser.rings(source, dir, edgeLabel,
|
||||
depth, sourceInRing,
|
||||
maxDegree, capacity, limit);
|
||||
return manager.serializer(g).writePaths("rings", paths, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_ELEMENTS_LIMIT;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.traversal.algorithm.SameNeighborTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Path("graphs/{graph}/traversers/sameneighbors")
|
||||
@Singleton
|
||||
@Tag(name = "SameNeighborsAPI")
|
||||
public class SameNeighborsAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(SameNeighborsAPI.class);
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("vertex") String vertex,
|
||||
@QueryParam("other") String other,
|
||||
@QueryParam("direction") String direction,
|
||||
@QueryParam("label") String edgeLabel,
|
||||
@QueryParam("max_degree")
|
||||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("limit")
|
||||
@DefaultValue(DEFAULT_ELEMENTS_LIMIT) int limit) {
|
||||
LOG.debug("Graph [{}] get same neighbors between '{}' and '{}' with " +
|
||||
"direction {}, edge label {}, max degree '{}' and limit '{}'",
|
||||
graph, vertex, other, direction, edgeLabel, maxDegree, limit);
|
||||
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(vertex);
|
||||
Id targetId = VertexAPI.checkAndParseVertexId(other);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
SameNeighborTraverser traverser = new SameNeighborTraverser(g);
|
||||
Set<Id> neighbors = traverser.sameNeighbors(sourceId, targetId, dir,
|
||||
edgeLabel, maxDegree, limit);
|
||||
return manager.serializer(g).writeList("same_neighbors", neighbors);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.ShortestPathTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@Path("graphs/{graph}/traversers/shortestpath")
|
||||
@Singleton
|
||||
@Tag(name = "ShortestPathAPI")
|
||||
public class ShortestPathAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(ShortestPathAPI.class);
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("source") String source,
|
||||
@QueryParam("target") String target,
|
||||
@QueryParam("direction") String direction,
|
||||
@QueryParam("label") String edgeLabel,
|
||||
@QueryParam("max_depth") int depth,
|
||||
@QueryParam("max_degree")
|
||||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("skip_degree")
|
||||
@DefaultValue("0") long skipDegree,
|
||||
@QueryParam("capacity")
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity) {
|
||||
LOG.debug("Graph [{}] get shortest path from '{}', to '{}' with " +
|
||||
"direction {}, edge label {}, max depth '{}', " +
|
||||
"max degree '{}', skipped maxDegree '{}' and capacity '{}'",
|
||||
graph, source, target, direction, edgeLabel, depth,
|
||||
maxDegree, skipDegree, capacity);
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(source);
|
||||
Id targetId = VertexAPI.checkAndParseVertexId(target);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
|
||||
ShortestPathTraverser traverser = new ShortestPathTraverser(g);
|
||||
|
||||
List<String> edgeLabels = edgeLabel == null ? ImmutableList.of() :
|
||||
ImmutableList.of(edgeLabel);
|
||||
HugeTraverser.Path path = traverser.shortestPath(sourceId, targetId,
|
||||
dir, edgeLabels, depth,
|
||||
maxDegree, skipDegree,
|
||||
capacity);
|
||||
return manager.serializer(g).writeList("path", path.vertices());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 HugeGraph Authors
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to You under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PATHS_LIMIT;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser.WeightedPaths;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Path("graphs/{graph}/traversers/singlesourceshortestpath")
|
||||
@Singleton
|
||||
@Tag(name = "SingleSourceShortestPathAPI")
|
||||
public class SingleSourceShortestPathAPI extends API {
|
||||
|
||||
private static final Logger LOG = Log.logger(SingleSourceShortestPathAPI.class);
|
||||
|
||||
@GET
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@QueryParam("source") String source,
|
||||
@QueryParam("direction") String direction,
|
||||
@QueryParam("label") String edgeLabel,
|
||||
@QueryParam("weight") String weight,
|
||||
@QueryParam("max_degree")
|
||||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("skip_degree")
|
||||
@DefaultValue("0") long skipDegree,
|
||||
@QueryParam("capacity")
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity,
|
||||
@QueryParam("limit")
|
||||
@DefaultValue(DEFAULT_PATHS_LIMIT) int limit,
|
||||
@QueryParam("with_vertex") boolean withVertex) {
|
||||
LOG.debug("Graph [{}] get single source shortest path from '{}' " +
|
||||
"with direction {}, edge label {}, weight property {}, " +
|
||||
"max degree '{}', limit '{}' and with vertex '{}'",
|
||||
graph, source, direction, edgeLabel,
|
||||
weight, maxDegree, withVertex);
|
||||
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(source);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
SingleSourceShortestPathTraverser traverser =
|
||||
new SingleSourceShortestPathTraverser(g);
|
||||
WeightedPaths paths = traverser.singleSourceShortestPaths(
|
||||
sourceId, dir, edgeLabel, weight,
|
||||
maxDegree, skipDegree, capacity, limit);
|
||||
Iterator<Vertex> iterator = QueryResults.emptyIterator();
|
||||
assert paths != null;
|
||||
if (!paths.isEmpty() && withVertex) {
|
||||
iterator = g.vertices(paths.vertices().toArray());
|
||||
}
|
||||
return manager.serializer(g).writeWeightedPaths(paths, iterator);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue