Compare commits
637 Commits
Author | SHA1 | Date |
---|---|---|
Luca Frosini | 2207e87a09 | |
Luca Frosini | d696e5a354 | |
Luca Frosini | f3aca2111a | |
Luca Frosini | 67ac46497d | |
Luca Frosini | b78e06e745 | |
Luca Frosini | 9f37197d58 | |
Luca Frosini | 053060fbd5 | |
Luca Frosini | 9b506685ff | |
Luca Frosini | e3dcaae93a | |
Luca Frosini | 47caed1e16 | |
Luca Frosini | bf0caa01c5 | |
Luca Frosini | 9cafb91277 | |
Luca Frosini | 95cdb46f00 | |
Luca Frosini | 75104813dc | |
Luca Frosini | 77209f01cf | |
Luca Frosini | 9c087550d2 | |
Luca Frosini | 638823062a | |
Luca Frosini | 322a9509fb | |
Luca Frosini | 07fb1e4213 | |
Luca Frosini | 3c8932cd1f | |
Luca Frosini | 7cebe2beb5 | |
Luca Frosini | 3c05b571c9 | |
Luca Frosini | a13cb0ed8f | |
Luca Frosini | 51731bbd04 | |
Luca Frosini | c17bf6de9a | |
Luca Frosini | e47b147301 | |
Luca Frosini | 173100f123 | |
Luca Frosini | 75353a57d1 | |
Luca Frosini | 7915074947 | |
Luca Frosini | 703b284b92 | |
luca.frosini | 41e88f0c6e | |
luca.frosini | 50d1974424 | |
luca.frosini | 3d055d7168 | |
luca.frosini | 017dff0630 | |
luca.frosini | e9450c6a95 | |
luca.frosini | c5e05d3495 | |
luca.frosini | 9a5f755c75 | |
luca.frosini | edcf71deba | |
luca.frosini | 47f181ea76 | |
luca.frosini | 04bdf3bff5 | |
luca.frosini | ded9f23deb | |
luca.frosini | 0cf6e58dd4 | |
luca.frosini | 1913af6663 | |
luca.frosini | e766c3bb92 | |
luca.frosini | 3bd109e993 | |
luca.frosini | d5137f8a82 | |
luca.frosini | 69fb4eafa0 | |
luca.frosini | 3bfbe5ed9b | |
luca.frosini | 1199570e57 | |
luca.frosini | 79b6527057 | |
luca.frosini | 080255e4ec | |
luca.frosini | 89d3604886 | |
luca.frosini | 2eef938c71 | |
luca.frosini | ca07b2f9f1 | |
luca.frosini | e5cf080bcd | |
luca.frosini | 5426e171ad | |
luca.frosini | 0f1d3e544b | |
luca.frosini | b189ad0e72 | |
luca.frosini | e378d275bc | |
luca.frosini | d132dfad92 | |
luca.frosini | 657802918a | |
luca.frosini | 4c900e6864 | |
luca.frosini | 91c25084cd | |
luca.frosini | 72742086fd | |
luca.frosini | c29377bc6d | |
luca.frosini | ce8f089de8 | |
luca.frosini | 5194e734aa | |
luca.frosini | 117905ff7e | |
luca.frosini | 2bfadb3a08 | |
Luca Frosini | 88196d4ca7 | |
Luca Frosini | 1082e60f81 | |
Luca Frosini | 43b8fbbacd | |
Luca Frosini | cdbd2d2fb7 | |
Luca Frosini | 6dfa3da0db | |
Luca Frosini | 2b3cf622b0 | |
Luca Frosini | 08e7fd0ea9 | |
Luca Frosini | 5d7d0d26d2 | |
Luca Frosini | e046ee68b0 | |
Luca Frosini | e4508f779d | |
Luca Frosini | c919033843 | |
Luca Frosini | 1597ddcfd6 | |
Luca Frosini | bfc83b99ba | |
Luca Frosini | 1f18f1f95f | |
Luca Frosini | b6977f52a0 | |
Luca Frosini | c9ef3a438a | |
Luca Frosini | dbd7d51d1b | |
Luca Frosini | 9f14bb3b62 | |
Luca Frosini | 628149bf28 | |
Luca Frosini | 4edbf54eb2 | |
Luca Frosini | 72a2233dbe | |
Luca Frosini | f117096847 | |
Luca Frosini | 29202bf152 | |
Luca Frosini | 53be7fcd10 | |
Luca Frosini | 4b54aa3103 | |
Luca Frosini | f7152f0be1 | |
Luca Frosini | 72842ff53b | |
Luca Frosini | 39de19d109 | |
Luca Frosini | 8bfb0c3b39 | |
Luca Frosini | 2d9f469a46 | |
Luca Frosini | cbcb4804b7 | |
Luca Frosini | f2cb73c563 | |
Luca Frosini | 7453f3be78 | |
Luca Frosini | 0a79e2d769 | |
Luca Frosini | e5adc1e3a7 | |
Luca Frosini | 96eb118dba | |
Luca Frosini | 434d42a1d5 | |
Luca Frosini | 80bee8029b | |
Luca Frosini | f3e1c29aef | |
Luca Frosini | ad2c722fb2 | |
Luca Frosini | e30794baa6 | |
Luca Frosini | 959b350674 | |
Luca Frosini | 7d14ca0496 | |
Luca Frosini | cb8c38b07b | |
Luca Frosini | 56c8f672a9 | |
Luca Frosini | 2cc08d4b25 | |
Luca Frosini | a273dd3f8b | |
Luca Frosini | b01fdcb81c | |
Luca Frosini | 551db893a3 | |
Luca Frosini | e31405850a | |
Luca Frosini | 2f6cd311ba | |
Luca Frosini | b2b2306c37 | |
Luca Frosini | 180e043e65 | |
Luca Frosini | 4e015d5a44 | |
Luca Frosini | 45cc8fcae9 | |
Luca Frosini | b48a497c30 | |
Luca Frosini | 5c2041df67 | |
Luca Frosini | 0ba536843d | |
Luca Frosini | 770061cf9f | |
Luca Frosini | da5649ec6f | |
Luca Frosini | 95f6e3940b | |
Luca Frosini | 1971d5f0de | |
Luca Frosini | fca67f8cb5 | |
Luca Frosini | e5554f3935 | |
Luca Frosini | 4ef0cb2112 | |
Luca Frosini | 9fa254add0 | |
Luca Frosini | c68771669e | |
Luca Frosini | 7e220d7f77 | |
Luca Frosini | 98c7a3c59c | |
Luca Frosini | 6df1eed2c2 | |
Luca Frosini | 662f3a90f2 | |
Luca Frosini | 94ebf3830c | |
Luca Frosini | ea3c484c31 | |
Luca Frosini | 92caa0377a | |
Luca Frosini | 72b2be6010 | |
Luca Frosini | 161977ae00 | |
Luca Frosini | 2cc004df4c | |
Luca Frosini | fef4bd8479 | |
Luca Frosini | 018cb95905 | |
Luca Frosini | c39484784f | |
Luca Frosini | f1afe85744 | |
Luca Frosini | 0fd2f53a5d | |
Luca Frosini | f8c9033f3c | |
Luca Frosini | 527e418e16 | |
Luca Frosini | b80a251896 | |
Luca Frosini | 5dabca9c30 | |
Luca Frosini | 32a6873e72 | |
Luca Frosini | 814b88c1d1 | |
Luca Frosini | 8638559d43 | |
Luca Frosini | 9c9e57314a | |
Luca Frosini | bcc4496590 | |
Luca Frosini | d979198399 | |
Luca Frosini | bcadc5b873 | |
Luca Frosini | 335837e17c | |
Luca Frosini | 08530b490f | |
Luca Frosini | fdabed4a87 | |
Luca Frosini | f8e6a0cc43 | |
Luca Frosini | 67006b8f67 | |
Luca Frosini | ae863a7aea | |
Luca Frosini | 020c4edd32 | |
Luca Frosini | 9cd3144099 | |
Luca Frosini | 320bfa3f37 | |
Luca Frosini | 2aada77a84 | |
Luca Frosini | cb88b480ee | |
Luca Frosini | ffa61e3d6b | |
Luca Frosini | b1b846c584 | |
Luca Frosini | a86e200a21 | |
Luca Frosini | 6776146d89 | |
Luca Frosini | 9b1190f359 | |
Luca Frosini | 78b1bb6639 | |
Luca Frosini | 8bbac10f68 | |
Luca Frosini | 9fba08b73a | |
Luca Frosini | e77bc20edd | |
Luca Frosini | 4e40358fde | |
Luca Frosini | 2d6e853e6f | |
Luca Frosini | 55798dca6f | |
Luca Frosini | 6a339bef2e | |
Luca Frosini | 4291ba0f98 | |
Luca Frosini | 84b44f9476 | |
Luca Frosini | 257ec29433 | |
Luca Frosini | f00dc325df | |
Luca Frosini | 0bf5d10ccd | |
Luca Frosini | b55bfbf4fb | |
Luca Frosini | 3a057c8870 | |
Luca Frosini | 3e748162fc | |
Luca Frosini | 5083e392f9 | |
Luca Frosini | e137394230 | |
Luca Frosini | 623a9215b2 | |
Luca Frosini | 141f1ec36d | |
Luca Frosini | b3e7bbdd82 | |
Luca Frosini | 129251d232 | |
Luca Frosini | 25bce13814 | |
Luca Frosini | 9314e67830 | |
Luca Frosini | ab3c28f7d0 | |
Luca Frosini | 6bf4ff2f92 | |
Luca Frosini | 7c9021f0be | |
Luca Frosini | 5d3f42ae6d | |
Luca Frosini | da9119c47d | |
Luca Frosini | 9b7a08d66b | |
Luca Frosini | e6856b7653 | |
Luca Frosini | 099aefc4f5 | |
Luca Frosini | fc80d7912d | |
Luca Frosini | ec11c9f8ab | |
Luca Frosini | 5f16bbd867 | |
Luca Frosini | e4c766bbaa | |
Luca Frosini | 130df26eab | |
Luca Frosini | cfa089131a | |
Luca Frosini | cf150065d0 | |
Luca Frosini | 00d60021fc | |
Luca Frosini | 1060843069 | |
Luca Frosini | 150f533fc7 | |
Luca Frosini | 73e7f67d7d | |
Luca Frosini | 8f88be604c | |
Luca Frosini | 17ffb800cf | |
Luca Frosini | b7b58971a5 | |
Luca Frosini | 5c2f8dd779 | |
Luca Frosini | ba900171f1 | |
Luca Frosini | 284e9f9ad8 | |
Luca Frosini | 957a36cc21 | |
Luca Frosini | bcd4038932 | |
Luca Frosini | 816effa271 | |
Luca Frosini | 3c9e14c945 | |
Luca Frosini | 52fe1929f8 | |
Luca Frosini | f13b62872f | |
Luca Frosini | e5fe3ba68d | |
Luca Frosini | e86e9d0581 | |
Luca Frosini | 7e0c035647 | |
Luca Frosini | 151783e813 | |
Luca Frosini | 673a6fadeb | |
Luca Frosini | 292ae3f2c2 | |
Luca Frosini | 2253c4445a | |
Luca Frosini | 3b2f91a9ee | |
Luca Frosini | a6f7409a4d | |
Luca Frosini | 2b4a7bbf26 | |
Luca Frosini | 0e40f2aa03 | |
Luca Frosini | 402c584c49 | |
Luca Frosini | c7c8ffa332 | |
Luca Frosini | e22cbcc0f5 | |
Luca Frosini | 86245a0899 | |
Luca Frosini | 0e1237c1fb | |
Luca Frosini | c5fcdba140 | |
Luca Frosini | e752502a7d | |
Luca Frosini | a307a32180 | |
Luca Frosini | 3e14830902 | |
Luca Frosini | 82150cb4b1 | |
Luca Frosini | 8cf15cc15d | |
Luca Frosini | b7bbf4d85c | |
Luca Frosini | 5764c3333a | |
Luca Frosini | 5cac2c8bdd | |
Luca Frosini | 43a191d762 | |
Luca Frosini | 8cef40d084 | |
Luca Frosini | c60d99381d | |
Luca Frosini | 708f9e38e9 | |
Luca Frosini | 8671142fe2 | |
Luca Frosini | 28e430e0ac | |
Luca Frosini | fdba5aaa6b | |
Luca Frosini | 9636ce2206 | |
Luca Frosini | 0e2ddd827e | |
Luca Frosini | bd49512c0f | |
Luca Frosini | 860050139d | |
Luca Frosini | e771cdda4c | |
Luca Frosini | 50fea5ba56 | |
Luca Frosini | c4d5028f83 | |
Luca Frosini | 48c5c3f52e | |
Luca Frosini | 9c55c48300 | |
Luca Frosini | 60b43cc1b4 | |
Luca Frosini | c0e731b01a | |
Luca Frosini | 7c6cb56b0b | |
Luca Frosini | df63cd50ac | |
Luca Frosini | 9ac2b4c3ec | |
Luca Frosini | 6b5c3fb267 | |
Luca Frosini | 6ef47a947e | |
Luca Frosini | c9336eb423 | |
Luca Frosini | c55867b0d9 | |
Luca Frosini | 686e0ff851 | |
Luca Frosini | 287abfe454 | |
Luca Frosini | 4912f47de3 | |
Luca Frosini | 581870479a | |
Luca Frosini | 927ba80290 | |
Luca Frosini | 5c5a4a84ab | |
Luca Frosini | 8252055126 | |
Luca Frosini | 1756db032f | |
Luca Frosini | 562ad114c3 | |
Luca Frosini | a72dfe904e | |
Luca Frosini | acda92b5aa | |
Luca Frosini | 327e0498cd | |
Luca Frosini | f45f30d300 | |
Luca Frosini | c23d2fc5b7 | |
Luca Frosini | fff3ba8710 | |
Luca Frosini | fae090d6bc | |
Luca Frosini | 83cbdd04bc | |
Luca Frosini | d80fa8d303 | |
Luca Frosini | 57c12a9303 | |
Luca Frosini | 53af6be0b9 | |
Luca Frosini | a5aee6353e | |
Luca Frosini | 3455159ecc | |
Luca Frosini | 42433c4007 | |
Luca Frosini | 7b2d9930a7 | |
Luca Frosini | 599ce4405b | |
Luca Frosini | 66810e67a4 | |
Luca Frosini | 0aa7135e90 | |
Luca Frosini | 4bfe35ec21 | |
Luca Frosini | 4751b4b7a7 | |
Luca Frosini | 1ff80117e2 | |
Luca Frosini | fc11f507c8 | |
Luca Frosini | 2150271c06 | |
Luca Frosini | a62df9847b | |
Luca Frosini | f13a36c1c6 | |
Luca Frosini | 1f52f9e5df | |
Luca Frosini | 02db577bfc | |
Luca Frosini | d31ce29887 | |
Luca Frosini | 5ae8b98b2c | |
Luca Frosini | 531aa00e40 | |
Luca Frosini | 007a4e610b | |
Luca Frosini | 0814e3475d | |
Luca Frosini | 2b551f0ee1 | |
Luca Frosini | 0d194a28a3 | |
Luca Frosini | 7f3bbdd1ca | |
Luca Frosini | d40dcc2f95 | |
Luca Frosini | de99335a56 | |
Luca Frosini | e6934b5819 | |
Luca Frosini | a0cf451650 | |
Luca Frosini | 24ad5b9a1f | |
Luca Frosini | cff70a3545 | |
Luca Frosini | 6dbac9bff4 | |
Luca Frosini | b0fc3c0f59 | |
Luca Frosini | 13dd7884dd | |
Luca Frosini | 169644b63e | |
Luca Frosini | 9e320e55ff | |
Luca Frosini | 2b1fab587a | |
Luca Frosini | ce3b7092d8 | |
Luca Frosini | df5ad99ede | |
Luca Frosini | dd93e66922 | |
Luca Frosini | e2c0c2a165 | |
Luca Frosini | 3686d4ed8a | |
Luca Frosini | bceb4acd74 | |
Luca Frosini | 2ed6c66285 | |
Luca Frosini | 8ce89f4140 | |
Luca Frosini | 01d2e527a1 | |
Luca Frosini | b88f72a6d2 | |
Luca Frosini | 41a8011afb | |
Luca Frosini | bb60d40cd5 | |
Luca Frosini | e05713031f | |
Luca Frosini | 48dbe09e3e | |
Luca Frosini | 3ec771527c | |
Luca Frosini | 08312a1a19 | |
Luca Frosini | ff7b8d7399 | |
Luca Frosini | eff29b0176 | |
Luca Frosini | e3e8ffd393 | |
Luca Frosini | c477d8f200 | |
Luca Frosini | d9ed838a89 | |
Luca Frosini | a320786185 | |
Luca Frosini | 4dd7aa5efb | |
Luca Frosini | d5ef34dd49 | |
Luca Frosini | 45b28e9e8a | |
Luca Frosini | 286fe4c420 | |
Luca Frosini | 623a5120df | |
Luca Frosini | 5c22db37c7 | |
Luca Frosini | 137608b1f1 | |
Luca Frosini | 4d290b3a13 | |
Luca Frosini | ce4eb099f6 | |
Luca Frosini | 14be0342cf | |
Luca Frosini | e10dbc9c4a | |
Luca Frosini | 35feb32c93 | |
Luca Frosini | 04090463c9 | |
Luca Frosini | 7acdc6a718 | |
Luca Frosini | c227d5069d | |
Luca Frosini | 7db232b970 | |
Luca Frosini | 38b3c9aa72 | |
Luca Frosini | 49b8424965 | |
Luca Frosini | 88bea376f3 | |
Luca Frosini | a2c2c0044e | |
Luca Frosini | 4c427b3e26 | |
Luca Frosini | e198f1a089 | |
Luca Frosini | 1ccec24890 | |
Luca Frosini | 8395cebf3c | |
Luca Frosini | 9cb889e227 | |
Luca Frosini | e7699bfc95 | |
Luca Frosini | 1262179b8f | |
Luca Frosini | 17714db2a7 | |
Luca Frosini | 093aba2018 | |
Luca Frosini | b5b89fb2c2 | |
Luca Frosini | 9411de7341 | |
Luca Frosini | efbf730ab8 | |
Luca Frosini | e10fec1a07 | |
Luca Frosini | 74753120ab | |
Luca Frosini | 8822df842d | |
Luca Frosini | 51d1f11d56 | |
Luca Frosini | bf179a44fd | |
Luca Frosini | 61e2498084 | |
Luca Frosini | 6c86abbd8a | |
Luca Frosini | 8c455f0f05 | |
Luca Frosini | b53c51b46f | |
Luca Frosini | d1deb98377 | |
Luca Frosini | 0f86ce828d | |
Luca Frosini | e16940af94 | |
Luca Frosini | e2763d6804 | |
Luca Frosini | 48bf3ec676 | |
Luca Frosini | bcd6ee9bbe | |
Luca Frosini | 638be14835 | |
Luca Frosini | 2eb643f500 | |
Luca Frosini | d6fedec30e | |
Luca Frosini | debc1330f7 | |
Luca Frosini | a416de2886 | |
Luca Frosini | bd5eaf50f6 | |
Luca Frosini | cceed61fba | |
Luca Frosini | 9a0d965745 | |
Luca Frosini | b90a3c97d4 | |
Luca Frosini | d40d320239 | |
Luca Frosini | db98454ee4 | |
Luca Frosini | c07f5fc354 | |
Luca Frosini | 895e8b0d9a | |
Luca Frosini | f29ea703d3 | |
Luca Frosini | 17b41a0d01 | |
Luca Frosini | cb6e0917d2 | |
Luca Frosini | 8a3159ee4b | |
Luca Frosini | 489cd05268 | |
Luca Frosini | b572c22003 | |
Luca Frosini | 100133b21c | |
Luca Frosini | 5d3dc89766 | |
Luca Frosini | 8f6a7397a9 | |
Luca Frosini | 36977e1c16 | |
Luca Frosini | 2bef5f60ec | |
Luca Frosini | f41f43fff3 | |
Luca Frosini | 00570c3301 | |
Luca Frosini | 838754f84a | |
Luca Frosini | 013af6c3f3 | |
Luca Frosini | e291485133 | |
Luca Frosini | 9bb4c11602 | |
Luca Frosini | 1a581e39ef | |
Luca Frosini | e8bdb5b880 | |
Luca Frosini | 2370d4daac | |
Luca Frosini | 79aabdd91e | |
Luca Frosini | d39350aedf | |
Luca Frosini | e7a2c2edd4 | |
Luca Frosini | cead201063 | |
Luca Frosini | b81f76ccc6 | |
Luca Frosini | 97335067d1 | |
Luca Frosini | 0624e047d1 | |
Luca Frosini | 37b2955fa7 | |
Luca Frosini | b24b600118 | |
Luca Frosini | f797247c5f | |
Luca Frosini | 34bb35d2e7 | |
Luca Frosini | c0cca0aa07 | |
Luca Frosini | 9d8ab9a828 | |
Luca Frosini | 4af1bb7ad2 | |
Luca Frosini | a1aade856a | |
Luca Frosini | 51fc8627b8 | |
Luca Frosini | 0073fb84ce | |
Luca Frosini | 1a720e09fd | |
Luca Frosini | c85b66e026 | |
Luca Frosini | ea71d5d8e5 | |
Luca Frosini | 78d6fe1eb5 | |
Luca Frosini | 21073fc37d | |
Luca Frosini | b74d768630 | |
Luca Frosini | 1dbf73bc41 | |
Luca Frosini | f298f59dc5 | |
Luca Frosini | 8b43f00fb2 | |
Luca Frosini | ef2cb362f3 | |
Luca Frosini | e0a018e083 | |
Luca Frosini | 1a4f5e81a4 | |
Luca Frosini | b0e6aff70c | |
Luca Frosini | a52f8980e8 | |
Luca Frosini | eca3d130c8 | |
Luca Frosini | a7d4bc8dbb | |
Luca Frosini | f1e3fbdcf4 | |
Luca Frosini | 7b21261ce6 | |
Luca Frosini | 303c092cfb | |
Luca Frosini | 6bdbf5bd8c | |
Luca Frosini | d4725aeea5 | |
Luca Frosini | 49dd701c01 | |
Luca Frosini | 91fe3633c2 | |
Luca Frosini | 0155efbd99 | |
Luca Frosini | b66b1fcd56 | |
Luca Frosini | bd22fb48c3 | |
Luca Frosini | 4df3e7cf5c | |
Luca Frosini | c8e383ea3c | |
Luca Frosini | 2edc222f6e | |
Luca Frosini | bd05ffd7b3 | |
Luca Frosini | 3e1b48d645 | |
Luca Frosini | fab1cb240a | |
Luca Frosini | 5ac1039d60 | |
Luca Frosini | f47891949d | |
Luca Frosini | cf19709756 | |
Luca Frosini | d11fb1849d | |
Luca Frosini | 6cc6d855da | |
Luca Frosini | dfe24ae9e1 | |
Luca Frosini | 917e50b684 | |
Luca Frosini | 8c3b4b5495 | |
Luca Frosini | a8732e2045 | |
Luca Frosini | 65aaefa8d2 | |
Luca Frosini | f173af41fd | |
Luca Frosini | ec33fbb147 | |
Luca Frosini | bdb930092f | |
Luca Frosini | dda7bf6234 | |
Luca Frosini | 196d362416 | |
Luca Frosini | e6ef944e97 | |
Luca Frosini | 190f500684 | |
Luca Frosini | cc4e6ea3b6 | |
Luca Frosini | 2b5f28d184 | |
Luca Frosini | 4f998f90ca | |
Luca Frosini | 14d573b4e9 | |
Luca Frosini | 20ae014249 | |
Luca Frosini | dd6bf694b4 | |
Luca Frosini | 58674cec7a | |
Luca Frosini | abc354ee1b | |
Luca Frosini | e8e29532fa | |
Luca Frosini | b438820f86 | |
Luca Frosini | 0476395fd0 | |
Luca Frosini | f845e52a5a | |
Luca Frosini | e1423021d9 | |
Luca Frosini | 0c718dcee6 | |
Luca Frosini | 41260b972d | |
Luca Frosini | c9b2a3b6f7 | |
Luca Frosini | 06c4e5f539 | |
Luca Frosini | 9069397b58 | |
Luca Frosini | 69a22d2461 | |
Luca Frosini | 04ae243f1d | |
Luca Frosini | 0f3c5e38e0 | |
Luca Frosini | d7c6257978 | |
Luca Frosini | e769f7ef2b | |
Luca Frosini | 3333d15b87 | |
Luca Frosini | 25bf1ab1c5 | |
Luca Frosini | bdbb954ba5 | |
Luca Frosini | f00127a82a | |
Luca Frosini | 904e475f2f | |
Luca Frosini | 8d1fc7f045 | |
Luca Frosini | 89b98f625f | |
Luca Frosini | 8a1a7738bc | |
Luca Frosini | 2324531ab9 | |
Luca Frosini | fbed0f57b8 | |
Luca Frosini | e3b3c67c42 | |
Luca Frosini | 7d822e07cb | |
Luca Frosini | eb27d67845 | |
Luca Frosini | 73db422373 | |
Luca Frosini | dfe711f419 | |
Luca Frosini | 1d81da329a | |
Luca Frosini | 72164da170 | |
Luca Frosini | b31befdc8d | |
Luca Frosini | e00b2010d1 | |
Luca Frosini | bf637e439f | |
Luca Frosini | 7051a5b952 | |
Luca Frosini | 6fad1ba8d1 | |
Luca Frosini | 6d00320353 | |
Luca Frosini | 8cf2e5a3d8 | |
Luca Frosini | 98df904693 | |
Luca Frosini | 68b3e2c71a | |
Luca Frosini | 6cab331db0 | |
Luca Frosini | 08a2b09b57 | |
Luca Frosini | dd8919f9de | |
Luca Frosini | 34d9c0759d | |
Luca Frosini | e90829157a | |
Luca Frosini | 98aec729cc | |
Luca Frosini | 97a28c3157 | |
Luca Frosini | 0d5c734d5b | |
Luca Frosini | 7776774415 | |
Luca Frosini | 81218d3e5c | |
Luca Frosini | 4f3b7055e1 | |
Luca Frosini | b42893cbdc | |
Luca Frosini | d0eb17812b | |
Luca Frosini | 1a6d5733a2 | |
Luca Frosini | 2f5885579b | |
Luca Frosini | 5ca0bbf023 | |
Luca Frosini | 56604195cc | |
Luca Frosini | 9333243baa | |
Luca Frosini | 1877291fba | |
Luca Frosini | 4ece53108f | |
Luca Frosini | f29c258375 | |
Luca Frosini | 4fc83e6091 | |
Luca Frosini | dbefb6c6e2 | |
Luca Frosini | 93d886ba37 | |
Luca Frosini | 501b65c92d | |
Luca Frosini | 2b3c39d9bb | |
Luca Frosini | 243cae757f | |
Luca Frosini | bbf1755f72 | |
Luca Frosini | 878b5cfae9 | |
Luca Frosini | eb8e0b7bea | |
Luca Frosini | 51b76c89e6 | |
Luca Frosini | 826a990b42 | |
Luca Frosini | 0bb373dee6 | |
Luca Frosini | 61a979aec7 | |
Luca Frosini | 269fc894b4 | |
Luca Frosini | cf0cafdcd6 | |
Luca Frosini | 40490b287f | |
Luca Frosini | 2d32d782e9 | |
Luca Frosini | 786bbc337f | |
Luca Frosini | 4e479f0db0 | |
Luca Frosini | f258ff80a2 | |
Luca Frosini | 09ae3573f8 | |
Luca Frosini | 989df62fe2 | |
Luca Frosini | 1d33bfefcb | |
Luca Frosini | 6b011e4e09 | |
Luca Frosini | 9436e96d90 | |
Luca Frosini | 275880c428 | |
Luca Frosini | 7a434f999a | |
Luca Frosini | ab0892eaf3 | |
Luca Frosini | 2f5b23addf | |
Luca Frosini | 8cd17acfe9 | |
Luca Frosini | b384c08972 | |
Luca Frosini | 1733b66c05 | |
Luca Frosini | 36b59cf13c | |
Luca Frosini | 420068f16f | |
Fabio Sinibaldi | a6226f0a23 | |
lucafrosini | feea967d86 | |
lucafrosini | b459719b6d | |
Luca Frosini | 6d78103b17 | |
Luca Frosini | d9e90d51a5 | |
Luca Frosini | c851894ca9 | |
Luca Frosini | 7d144c4fa7 | |
Luca Frosini | fd1d196eac | |
Luca Frosini | 3c328f54b8 | |
Luca Frosini | a7962d5f79 | |
Luca Frosini | 72e1d8f809 | |
Luca Frosini | 18108d1324 | |
Luca Frosini | f86ddd76a8 | |
Luca Frosini | 6f7f71daa6 | |
Luca Frosini | 4c9a294b1a | |
Luca Frosini | 861ddc0f4b | |
Luca Frosini | ae173ccbd4 | |
Luca Frosini | c7890e2121 | |
Luca Frosini | 06930ba4e1 | |
Luca Frosini | 7a0dbf48a9 | |
Luca Frosini | 1ef0537f0e | |
Luca Frosini | 0743445abe | |
Luca Frosini | edbf4cd1d0 | |
Luca Frosini | 62bb1d7a50 | |
Luca Frosini | d7cbc03f97 | |
Luca Frosini | cb963dd702 |
|
@ -1,3 +1,4 @@
|
||||||
target
|
target
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
|
/**/.DS_Store
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
/org.eclipse.jdt.core.prefs
|
||||||
|
/org.eclipse.core.resources.prefs
|
|
@ -1,6 +0,0 @@
|
||||||
eclipse.preferences.version=1
|
|
||||||
encoding//src/main/java=UTF-8
|
|
||||||
encoding//src/main/resources=UTF-8
|
|
||||||
encoding//src/test/java=UTF-8
|
|
||||||
encoding//src/test/resources=UTF-8
|
|
||||||
encoding/<project>=UTF-8
|
|
|
@ -1,5 +0,0 @@
|
||||||
eclipse.preferences.version=1
|
|
||||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
|
||||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
|
||||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
|
||||||
org.eclipse.jdt.core.compiler.source=1.8
|
|
231
CHANGELOG.md
231
CHANGELOG.md
|
@ -1,123 +1,176 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [1-4-3] [r4.23.0] - 2020-05-22
|
# Changelog for gCube Catalogue (gCat) Service
|
||||||
|
|
||||||
### Changed
|
## [v2.6.0]
|
||||||
|
|
||||||
- [#19295] Social Post is disabled if not explicitly enabled by the client
|
- Enhanced gcube-smartgears-bom version to 2.5.1 [#27999]
|
||||||
|
|
||||||
**Fixes**
|
|
||||||
|
|
||||||
- [#19395] Fixed a null pointer exception while creating the JSON object to send to CKAN to create the user
|
|
||||||
|
|
||||||
|
|
||||||
## [v1-4-2] [r4.20.0]- 2020-02-14
|
## [v2.5.4]
|
||||||
|
|
||||||
### Added
|
- Added Content-Location HTTP Header in the response of Item creation #27940
|
||||||
|
|
||||||
**Features**
|
|
||||||
|
## [v2.5.3]
|
||||||
|
|
||||||
|
- Improved profile schema #26471
|
||||||
|
- Catalogue core operation has been moved in a dedicated library #27118
|
||||||
|
|
||||||
|
|
||||||
|
## [v2.5.2]
|
||||||
|
|
||||||
|
- Enforce private to a rejected item to avoid issue #26391
|
||||||
|
- Added profile validation (i.e. regex validity and default value conformity) #26142
|
||||||
|
|
||||||
|
|
||||||
|
## [v2.5.1]
|
||||||
|
|
||||||
|
- Migrated code to reorganized E/R format [#24992]
|
||||||
|
- Moderation notification are now sent as separated thread [#25614]
|
||||||
|
|
||||||
|
|
||||||
|
## [v2.5.0]
|
||||||
|
|
||||||
|
- Switched from commons-lang3 to commons-lang to avoid duplicates
|
||||||
|
- Set resource-registry-publisher dependency scope to provided
|
||||||
|
- Fixed RequestFilter to avoid to remove info to Smartgears
|
||||||
|
- Switching to Facet Based IS [#20225]
|
||||||
|
- Delete of item in a moderated catalogue produces a notification [#24305]
|
||||||
|
- The user performing create/update item in moderated catalogue receive confirmation via notification of the action [#23575]
|
||||||
|
- Enhanced gcube-smartgears-bom version
|
||||||
|
- Added support for JSON:API on 'licenses' collection [#24601]
|
||||||
|
|
||||||
|
|
||||||
|
## [v2.4.1] [r5.14.0] - 2022-12-07
|
||||||
|
|
||||||
|
- Integrating Sphinx for documentation [#23833]
|
||||||
|
- Migrated Social service interaction to social-service-client [#23151]
|
||||||
|
- Improved author rewrite in case of update [#23851]
|
||||||
|
- Fixed item listing [#23901]
|
||||||
|
|
||||||
|
|
||||||
|
## [v2.4.0] [r5.13.1] - 2022-09-16
|
||||||
|
|
||||||
|
- Added moderation link in moderation message [#23142]
|
||||||
|
- Added query parameter in item listing to get the whole item instead of just the name [#23691]
|
||||||
|
- Using renamed constant from gcat-api
|
||||||
|
- Explict request for approved items return only moderated and approved [#23696]
|
||||||
|
|
||||||
|
|
||||||
|
## [v2.3.0] [r5.13.0] - 2022-07-22
|
||||||
|
|
||||||
|
- Switched moderation messages to notification [#23317]
|
||||||
|
- Item listing returns items in the default organization and not in all supported organization
|
||||||
|
|
||||||
|
|
||||||
|
## [v2.2.0] [r5.11.0] - 2022-05-12
|
||||||
|
|
||||||
|
- Switched gcat credentials to new IAM authz [#21628][#22727]
|
||||||
|
- Added support to manage configurations [#22658][#22742]
|
||||||
|
- Migrated service to SecretManagerProvider [#22871]
|
||||||
|
- Migrated to ServiceClass corresponding to Maven groupId
|
||||||
|
- Added Enunciate to automatically create REST APIs documentation [#23096]
|
||||||
|
- Fixed 'offset' parameter behaviuor in item listing [#22999]
|
||||||
|
- Moderation message are sent using gcube messaging system via Social Service [#23117]
|
||||||
|
- Remove enforcement on approved item for Catalogue-Editor added enforcement to email [#23154]
|
||||||
|
- ClientID requests are now properly supported [#21903]
|
||||||
|
|
||||||
|
|
||||||
|
## [v2.1.0] [r5.7.0] - 2022-01-27
|
||||||
|
|
||||||
|
- Added query parameter social_post_notification to override default VRE behaviour [#21345]
|
||||||
|
- Users are created/referenced in the form <Surname Name> and not vice-versa [#21479]
|
||||||
|
- Added support for moderation [#21342]
|
||||||
|
- Added support for IAM authz [#21628]
|
||||||
|
- Added items bulk delete/purge [#22299]
|
||||||
|
- Using UriResolverManager to get item URL in place of direct HTTP call [#22549]
|
||||||
|
- Added empty trash API [#13322]
|
||||||
|
|
||||||
|
|
||||||
|
## [v2.0.0] [r5.2.0] - 2021-05-04
|
||||||
|
|
||||||
|
- Fixed retrieving of filename from content-disposition http header used to persist a resource [#21216]
|
||||||
|
- Fixed author and maintainer name and email [#21059][#21189]
|
||||||
|
- Improved check on controlled vocabulary to match corner cases [#20742]
|
||||||
|
- Added PATCH method on Item collection [#19768]
|
||||||
|
- Switched JSON management to gcube-jackson [#19735]
|
||||||
|
- Added support to publish an item organizations not matching the current context [#19365]
|
||||||
|
|
||||||
|
|
||||||
|
## [v1.4.5] [r5.1.0] - 2021-03-31
|
||||||
|
|
||||||
|
- Removed 'owner_org' field from result when reading an item [#20919]
|
||||||
|
- Dirty patched item validator [#20965]
|
||||||
|
- Improved error message return with message got from CKAN [#19516]
|
||||||
|
|
||||||
|
|
||||||
|
## [v1.4.4] [r5.0.0] - 2021-02-24
|
||||||
|
|
||||||
|
- Added count method for Item collection [#20627]
|
||||||
|
- Added count method for Organization, Group and Profile collection [#20629]
|
||||||
|
- Switched JSON management to gcube-jackson [#19735]
|
||||||
|
|
||||||
|
|
||||||
|
## [v1.4.3] [r4.23.0] - 2020-06-16
|
||||||
|
|
||||||
|
- Social Post is disabled if not explicitly enabled by the client [#19295]
|
||||||
|
- Solved null pointer exception while creating the JSON object to send to CKAN to create the user [#19395]
|
||||||
|
|
||||||
|
|
||||||
|
## [v1.4.2] [r4.20.0] - 2020-02-14
|
||||||
|
|
||||||
- Added method to retrieve a persisted file
|
- Added method to retrieve a persisted file
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
**Fixes**
|
|
||||||
|
|
||||||
- Considering that a file could not be persisted because it could be published via portlet
|
- Considering that a file could not be persisted because it could be published via portlet
|
||||||
|
|
||||||
|
|
||||||
## [1-4-1] - 2019-12-20
|
## [v1.4.1] [r4.18.0] - 2019-12-20
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
**Fixes**
|
|
||||||
|
|
||||||
- Fixed distro files and pom according to new release procedure
|
- Fixed distro files and pom according to new release procedure
|
||||||
|
|
||||||
- Fixed metadata checks
|
- Fixed metadata checks
|
||||||
|
|
||||||
|
|
||||||
## [1-4-0] - 2019-11-06
|
## [v1.4.0] [r4.15.0] - 2019-11-06
|
||||||
|
|
||||||
### Added
|
- Items listing API when invoked at VO level allow to get all the catalogue items or filtering them by organization [#17635]
|
||||||
|
- Enabled the possibility of filtering the result of items listing API [#17645]
|
||||||
**Features**
|
- User information are checked/updated on CKAN using portal information [#16360]
|
||||||
|
|
||||||
- [#17635] Items listing API when invoked at VO level allow to get all the catalogue items or filtering them by organization
|
|
||||||
|
|
||||||
- [#17645] Enabled the possibility of filtering the result of items listing API
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
**Fixes**
|
|
||||||
|
|
||||||
- [#16360] User information are checked/updated on CKAN using portal information
|
|
||||||
|
|
||||||
|
|
||||||
## [1-3-0] - 2019-06-12
|
## [v1.3.0] [r4.14.0] - 2019-06-12
|
||||||
|
|
||||||
### Changed
|
- Removed the requirement of providing the id of an item in update [#16740]
|
||||||
|
- Resource URL existence is not checked anymore [#16748]
|
||||||
**Fixes**
|
|
||||||
|
|
||||||
- [#16740] Removed the requirement of providing the id of an item in update
|
|
||||||
|
|
||||||
|
|
||||||
## [1-2-0] - 2019-06-12
|
## [v1.2.0-SNAPSHOT] - 2019-05-20
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
**Fixes**
|
|
||||||
|
|
||||||
- Separated REST class for Profile management from the logic which effectively manage profile on IS
|
- Separated REST class for Profile management from the logic which effectively manage profile on IS
|
||||||
|
- Tags are now properly created/added according to profile definition [#16182]
|
||||||
- [#16182] Tags are now properly created/added according to profile definition
|
- Groups are now properly created/added according to profile definition [#16183]
|
||||||
|
- Setting format in resources [#16347]
|
||||||
- [#16183] Groups are now properly created/added according to profile definition
|
- Validated provided LicenseId for Item [#16354]
|
||||||
|
- Capacity field is removed from the provided item content if any [#16410]
|
||||||
- [#16330] Fixed bug on getting responses from social service
|
- Fixed bug on getting responses from social service [#16330]
|
||||||
|
- Added the possibility to disable social post on item creation [#13335]
|
||||||
- [#16347] Setting format in resources
|
- Fixed bug on social post creation [#16322]
|
||||||
|
- Improved social post message [#16322]
|
||||||
- [#16354] Validated provided LicenseId for Item
|
- Added support to publish in specific organizations when the request is not performed at VRE level [#16635]
|
||||||
|
|
||||||
- [#16410] Capacity field is removed from the provided item content if any
|
|
||||||
|
|
||||||
- [#16322] Fixed bug on social post creation
|
|
||||||
|
|
||||||
- [#16322] Added the possibility to disable social post on item creation
|
|
||||||
|
|
||||||
- [#16322] Improved social post message
|
|
||||||
|
|
||||||
- [#16635] Added support to publish in specific organizations when the request is not performed at VRE level
|
|
||||||
|
|
||||||
|
|
||||||
## [1-1-0] - 2019-02-26
|
## [v1.1.0] [r4.13.1] - 2019-02-26
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
**Fixes**
|
|
||||||
|
|
||||||
- [#13309] Added Item URL via URI Resolver in extras field
|
|
||||||
|
|
||||||
- [#13303] The final URL is retrieved only URI resolver pointing to Storage Hub
|
|
||||||
|
|
||||||
- [#13306] Enforched items to be searchable in 'extras' field
|
|
||||||
|
|
||||||
- [#13307] Switched item listing to use package_search in place of package_list
|
|
||||||
|
|
||||||
- [#13076] Refactored to use storage-hub-persistence library
|
|
||||||
|
|
||||||
- [#13079] Added Create/Update/Delete in Profile Collection
|
|
||||||
|
|
||||||
|
- Added Item URL via URI Resolver in extras field [#13309]
|
||||||
|
- The final URL is retrieved only URI resolver pointing to Storage Hub [#13303]
|
||||||
|
- Enforced items to be searchable in 'extras' field [#13306]
|
||||||
|
- Switched item listing to use package_search in place of package_list [#13307]
|
||||||
|
- Refactored to use storage-hub-persistence library [#13076]
|
||||||
|
- Added Create/Update/Delete in Profile Collection [#13079]
|
||||||
- Fixed issues in Resource collection
|
- Fixed issues in Resource collection
|
||||||
|
|
||||||
- Refactored to use gcat-api library
|
- Refactored to use gcat-api library
|
||||||
|
|
||||||
|
|
||||||
## [1-0-0] - 2018-12-07
|
## [v1.0.0-SNAPSHOT] - 2019-01-10
|
||||||
|
|
||||||
|
- First Version
|
||||||
|
|
||||||
First Release
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Acknowledgments
|
||||||
|
|
||||||
|
The projects leading to this software have received funding from a series of European Union programmes including:
|
||||||
|
|
||||||
|
- the Sixth Framework Programme for Research and Technological Development
|
||||||
|
- [DILIGENT](https://cordis.europa.eu/project/id/004260) (grant no. 004260).
|
||||||
|
- the Seventh Framework Programme for research, technological development and demonstration
|
||||||
|
- [D4Science](https://cordis.europa.eu/project/id/212488) (grant no. 212488);
|
||||||
|
- [D4Science-II](https://cordis.europa.eu/project/id/239019) (grant no.239019);
|
||||||
|
- [ENVRI](https://cordis.europa.eu/project/id/283465) (grant no. 283465);
|
||||||
|
- [iMarine](https://cordis.europa.eu/project/id/283644) (grant no. 283644);
|
||||||
|
- [EUBrazilOpenBio](https://cordis.europa.eu/project/id/288754) (grant no. 288754).
|
||||||
|
- the H2020 research and innovation programme
|
||||||
|
- [SoBigData](https://cordis.europa.eu/project/id/654024) (grant no. 654024);
|
||||||
|
- [PARTHENOS](https://cordis.europa.eu/project/id/654119) (grant no. 654119);
|
||||||
|
- [EGI-Engage](https://cordis.europa.eu/project/id/654142) (grant no. 654142);
|
||||||
|
- [ENVRI PLUS](https://cordis.europa.eu/project/id/654182) (grant no. 654182);
|
||||||
|
- [BlueBRIDGE](https://cordis.europa.eu/project/id/675680) (grant no. 675680);
|
||||||
|
- [PerformFISH](https://cordis.europa.eu/project/id/727610) (grant no. 727610);
|
||||||
|
- [AGINFRA PLUS](https://cordis.europa.eu/project/id/731001) (grant no. 731001);
|
||||||
|
- [DESIRA](https://cordis.europa.eu/project/id/818194) (grant no. 818194);
|
||||||
|
- [ARIADNEplus](https://cordis.europa.eu/project/id/823914) (grant no. 823914);
|
||||||
|
- [RISIS 2](https://cordis.europa.eu/project/id/824091) (grant no. 824091);
|
||||||
|
- [EOSC-Pillar](https://cordis.europa.eu/project/id/857650) (grant no. 857650);
|
||||||
|
- [Blue Cloud](https://cordis.europa.eu/project/id/862409) (grant no. 862409);
|
||||||
|
- [SoBigData-PlusPlus](https://cordis.europa.eu/project/id/871042) (grant no. 871042);
|
37
README.md
37
README.md
|
@ -13,7 +13,7 @@ This service allows any client to publish on the gCube Catalogue.
|
||||||
|
|
||||||
## Change log
|
## Change log
|
||||||
|
|
||||||
See [Releases](https://code-repo.d4science.org/gCubeSystem/gcat/releases).
|
See [CHANGELOG.md](CHANGELOG.md).
|
||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
|
|
||||||
|
@ -25,14 +25,12 @@ Tell people how to cite this software.
|
||||||
* Cite an associated paper?
|
* Cite an associated paper?
|
||||||
* Use a specific BibTeX entry for the software?
|
* Use a specific BibTeX entry for the software?
|
||||||
|
|
||||||
|
@software{gcat,
|
||||||
@Manual{,
|
author = {{Luca Frosini}},
|
||||||
title = {gCube Catalogue (gCat) Service},
|
title = {gCube Catalogue (gCat) Service},
|
||||||
author = {{Frosini, Luca}},
|
abstract = {gCube Catalogue (gCat) Service allows the publication of items in the gCube Catalogue.},
|
||||||
organization = {ISTI - CNR},
|
url = {https://doi.org/10.5281/zenodo.7446641},
|
||||||
address = {Pisa, Italy},
|
keywords = {Catalogue, D4Science, gCube}
|
||||||
year = 2019,
|
|
||||||
url = {http://www.gcube-system.org/}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
@ -46,26 +44,5 @@ open-source software toolkit used for building and operating Hybrid Data
|
||||||
Infrastructures enabling the dynamic deployment of Virtual Research Environments
|
Infrastructures enabling the dynamic deployment of Virtual Research Environments
|
||||||
by favouring the realisation of reuse oriented policies.
|
by favouring the realisation of reuse oriented policies.
|
||||||
|
|
||||||
The projects leading to this software have received funding from a series of European Union programmes including:
|
The projects leading to this software have received funding from a series of European Union programmes see [FUNDING.md](FUNDING.md)
|
||||||
|
|
||||||
- the Sixth Framework Programme for Research and Technological Development
|
|
||||||
- DILIGENT (grant no. 004260).
|
|
||||||
- the Seventh Framework Programme for research, technological development and demonstration
|
|
||||||
- D4Science (grant no. 212488);
|
|
||||||
- D4Science-II (grant no.239019);
|
|
||||||
- ENVRI (grant no. 283465);
|
|
||||||
- iMarine(grant no. 283644);
|
|
||||||
- EUBrazilOpenBio (grant no. 288754).
|
|
||||||
- the H2020 research and innovation programme
|
|
||||||
- SoBigData (grant no. 654024);
|
|
||||||
- PARTHENOS (grant no. 654119);
|
|
||||||
- EGIEngage (grant no. 654142);
|
|
||||||
- ENVRIplus (grant no. 654182);
|
|
||||||
- BlueBRIDGE (grant no. 675680);
|
|
||||||
- PerformFish (grant no. 727610);
|
|
||||||
- AGINFRAplus (grant no. 731001);
|
|
||||||
- DESIRA (grant no. 818194);
|
|
||||||
- ARIADNEplus (grant no. 823914);
|
|
||||||
- RISIS2 (grant no. 824091);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<enunciate
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="http://enunciate.webcohesion.com/schemas/enunciate-2.14.0.xsd">
|
||||||
|
<description package="org.gcube.gcat.rest"/>
|
||||||
|
<api-classes>
|
||||||
|
<include pattern="org.gcube.gcat.rest.*" />
|
||||||
|
<include pattern="org.gcube.gcat.rest.administration.*" />
|
||||||
|
<exclude pattern="org.gcube.gcat.*" />
|
||||||
|
</api-classes>
|
||||||
|
<modules>
|
||||||
|
<gwt-json-overlay disabled="true " />
|
||||||
|
<php-json-client disabled="true" />
|
||||||
|
<ruby-json-client disabled="true" />
|
||||||
|
<java-json-client disabled="true" />
|
||||||
|
<javascript-client disabled="true" />
|
||||||
|
<java-xml-client disabled="true" />
|
||||||
|
<jaxws disabled="true" />
|
||||||
|
<jaxb disabled="true" />
|
||||||
|
|
||||||
|
<c-xml-client disabled="true" />
|
||||||
|
<csharp-xml-client disabled="true" />
|
||||||
|
<obj-c-xml-client disabled="true" />
|
||||||
|
<php-xml-client disabled="true" />
|
||||||
|
<spring-webnt disabled="true" />
|
||||||
|
|
||||||
|
<docs docsDir="${project.build.directory}" docsSubdir="api-docs" />
|
||||||
|
<swagger basePath="/${project.artifactId}" />
|
||||||
|
<docs
|
||||||
|
freemarkerTemplate="${project.basedir}${file.separator}src${file.separator}main${file.separator}resources${file.separator}META-INF${file.separator}enunciate${file.separator}d4science_docs.fmt">
|
||||||
|
<additional-css file="css${file.separator}d4science_enunciate_custom.css" />
|
||||||
|
</docs>
|
||||||
|
</modules>
|
||||||
|
</enunciate>
|
|
@ -0,0 +1 @@
|
||||||
|
/_build/
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line, and also
|
||||||
|
# from the environment for the first two.
|
||||||
|
SPHINXOPTS ?=
|
||||||
|
SPHINXBUILD ?= sphinx-build
|
||||||
|
SOURCEDIR = .
|
||||||
|
BUILDDIR = _build
|
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help:
|
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
.PHONY: help Makefile
|
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile
|
||||||
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
@ -0,0 +1,62 @@
|
||||||
|
# Configuration file for the Sphinx documentation builder.
|
||||||
|
#
|
||||||
|
# This file only contains a selection of the most common options. For a full
|
||||||
|
# list see the documentation:
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||||
|
|
||||||
|
# -- Path setup --------------------------------------------------------------
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#
|
||||||
|
import os
|
||||||
|
# import sys
|
||||||
|
# sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
_POM_PATH = "../.."
|
||||||
|
|
||||||
|
def getMvnVariable(variable):
|
||||||
|
cmd = """cd %s && mvn -q \
|
||||||
|
-Dexec.executable=echo \
|
||||||
|
-Dexec.args='${project.%s}' \
|
||||||
|
--non-recursive \
|
||||||
|
exec:exec""" % (_POM_PATH, variable)
|
||||||
|
stream = os.popen(cmd)
|
||||||
|
return stream.read().strip()
|
||||||
|
|
||||||
|
# -- Project information -----------------------------------------------------
|
||||||
|
# The full version, including alpha/beta/rc tags
|
||||||
|
release = getMvnVariable("version")
|
||||||
|
project = getMvnVariable("name")
|
||||||
|
|
||||||
|
copyright = '2022, %s' % getMvnVariable("organization.name")
|
||||||
|
author = '%s (%s)' % (getMvnVariable("developers.developer[0].name"),getMvnVariable("developers.developer[0].organization"))
|
||||||
|
|
||||||
|
# -- General configuration ---------------------------------------------------
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
|
# ones.
|
||||||
|
extensions = []
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
# This pattern also affects html_static_path and html_extra_path.
|
||||||
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output -------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
#
|
||||||
|
html_theme = 'sphinxdoc'
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
|
@ -0,0 +1,508 @@
|
||||||
|
***********************************************************
|
||||||
|
Welcome to gCube Catalogue Service (aka gCat) documentation
|
||||||
|
***********************************************************
|
||||||
|
|
||||||
|
gCat is a RESTful application that exposes operations via REST-API.
|
||||||
|
|
||||||
|
See the available `REST-API docs <../api-docs/index.html>`_.
|
||||||
|
|
||||||
|
Base URL
|
||||||
|
========
|
||||||
|
|
||||||
|
In the production environment, its current value is https://api.d4science.org/gcat
|
||||||
|
|
||||||
|
|
||||||
|
Authorization
|
||||||
|
=============
|
||||||
|
|
||||||
|
D4Science adopts state-of-the-art industry standards for authentication and authorization.
|
||||||
|
Specifically, the implementation fully adopts `OIDC (OpenID Connect) <https://openid.net/connect>`_ for authentication and UMA 2 (User-Managed Authorization) for authorization flows.
|
||||||
|
`JSON Web Token (JWT) Access token <https://jwt.io/>`_ are used for both authentication and authorization.
|
||||||
|
|
||||||
|
Obtain your Bearer token here: https://dev.d4science.org/how-to-access-resources
|
||||||
|
|
||||||
|
Service
|
||||||
|
=======
|
||||||
|
|
||||||
|
You can call the methods of the Web Service by writing your REST client application or using existing REST client plugins.
|
||||||
|
|
||||||
|
|
||||||
|
HTTP Statuses
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any successful operation returns a *200 OK* HTTP status code.
|
||||||
|
The create operation returns *201 Created*.
|
||||||
|
Any Background operation returns *202 Accepted*.
|
||||||
|
Any operation that does not provide any content returns *204 No Content*.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The most common error statuses a client can obtain are:
|
||||||
|
|
||||||
|
* **400 Bad Request** used to indicate a clients error `<https://tools.ietf.org/html/rfc7231#section-6.5.1>`_;
|
||||||
|
* **401 Unauthorized** used to indicate that the client does not provide the authorization token in the HTTP Header or the client does not have enough right to perform such request `<https://tools.ietf.org/html/rfc7235#section-3.1>`_;
|
||||||
|
* **404 Not Found** used to indicate that the requested instance does not exist `<https://tools.ietf.org/html/rfc7231#section-6.5.4>`_;
|
||||||
|
* **405 Method Not Allowed** the used HTTP method is not supported for the requested URL `<https://tools.ietf.org/html/rfc7231#section-6.5.5>`_.
|
||||||
|
The response contains the *Allow* HTTP Header indicating the supported HTTP method for such URL `<https://tools.ietf.org/html/rfc7231#section-7.4.1>`_;
|
||||||
|
* **409 Conflict** the request could not be completed due to a conflict with the current state of the target resource (e.g. the name of the resource already exists) `<https://tools.ietf.org/html/rfc7231#section-6.5.8>`_;
|
||||||
|
* **500 Internal Server Error** indicate a server failure `<https://tools.ietf.org/html/rfc7231#section-6.6.1>`_.
|
||||||
|
|
||||||
|
You can find a complete list of HTTP Status at `<https://httpstatuses.com/>`_
|
||||||
|
|
||||||
|
If you get a *500 Internal Server Error*, please report it in the `gCube ticketing system <https://support.d4science.org>`_.
|
||||||
|
|
||||||
|
Please use this checklist before reporting an error:
|
||||||
|
|
||||||
|
* Replicate the request;
|
||||||
|
* The failure could be temporal due to a network error, a server issue, and many other temporal issues. For this reason, please retry the request after a certain amount of time before reporting the issue;
|
||||||
|
* indicate how to replicate the error;
|
||||||
|
* indicate the time when the error occurred (this simplifies identifying the issue).
|
||||||
|
|
||||||
|
HTTP Methods
|
||||||
|
------------
|
||||||
|
|
||||||
|
gCat is a pure RESTful service. It uses standard HTTP Methods to perform a listing of collections and CRUD (Create Read Update Delete) operations on instances.
|
||||||
|
|
||||||
|
|
||||||
|
.. table::
|
||||||
|
|
||||||
|
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
| Operation | HTTP Method | URL | Success HTTP Status | Safe | Idempotent |
|
||||||
|
+==============+=============+========================================+=====================+========+============+
|
||||||
|
| Supported | OPTIONS | /{COLLECTION} | 204 No Content | Y | Y |
|
||||||
|
| HTTP Methods | | | [#allow]_ | | |
|
||||||
|
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
| List | GET | /{COLLECTION} | 200 OK | Y | Y |
|
||||||
|
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
| Count | GET | /{COLLECTION}?count=true | 200 OK | Y | Y |
|
||||||
|
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
| Exists | HEAD | /{COLLECTION} | 204 No Content | Y | Y |
|
||||||
|
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
| Create | POST | /{COLLECTION} | 201 Created | N | N |
|
||||||
|
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
| Supported | OPTIONS | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | Y | Y |
|
||||||
|
| HTTP Methods | | | [#allow]_ | | |
|
||||||
|
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
| Exist | HEAD | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | Y | Y |
|
||||||
|
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
| Read | GET | /{COLLECTION}/{INSTANCE_ID} | 200 OK | Y | Y |
|
||||||
|
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
| Update | PUT | /{COLLECTION}/{INSTANCE_ID} | 200 OK | N | Y |
|
||||||
|
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
| Patch | PATCH | /{COLLECTION}/{INSTANCE_ID} | 200 OK | N | Y |
|
||||||
|
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
| Delete | DELETE | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | N | N [#del]_ |
|
||||||
|
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
| Purge | DELETE | /{COLLECTION}/{INSTANCE_ID}?purge=true | 204 No Content | N | N [#del]_ |
|
||||||
|
+ +-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
| | PURGE | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | N | N [#del]_ |
|
||||||
|
+--------------+-------------+----------------------------------------+---------------------+--------+------------+
|
||||||
|
|
||||||
|
.. [#allow] Supported HTTP Methods in **Allow** HTTP Header
|
||||||
|
|
||||||
|
.. [#del] DELETE has been defined as idempotent.
|
||||||
|
|
||||||
|
*Allamaraju* [#Allamaraju]_ argues that DELETE idempotency should be accomplished client-side.
|
||||||
|
The server should inform the client if the delete operation succeeded because the resource was deleted or it was not found, i.e., **404 Not Found** error is suggested instead of **204 No Content**.
|
||||||
|
The latter situation should be treated as idempotent by the client.
|
||||||
|
|
||||||
|
We share the same vision. For this reason, gCat does not provide server-side idempotency for DELETE and PURGE operations.
|
||||||
|
|
||||||
|
.. [#Allamaraju] Allamaraju S. RESTful Web Services Cookbook: Solutions for Improving Scalability and Simplicity. O’Reilly. first ed. 2010
|
||||||
|
|
||||||
|
|
||||||
|
About URL
|
||||||
|
^^^^^^^^^
|
||||||
|
|
||||||
|
The presented URL uses the following convention:
|
||||||
|
|
||||||
|
* **{COLLECTION}** is the plural name of the entity type;
|
||||||
|
* **{INSTANCE_ID}** is an identification that enables univocally identifying the instance in the collection.
|
||||||
|
|
||||||
|
|
||||||
|
About Safety and Idempotency properties
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
||||||
|
* A method is *Safe* if it does not produce any side effects.
|
||||||
|
"This does not prevent an implementation from including behaviour that is potentially harmful, that is not entirely read-only, or that causes side effects while invoking a safe method"
|
||||||
|
`<https://tools.ietf.org/html/rfc7231#section-4.2.1>`_;
|
||||||
|
* A method is *Idempotent* if the same operation repeated multiple times has the same side effect as using it one time.
|
||||||
|
"repeating the request will have the same intended effect, even if the original request succeeded, though the response might differ"
|
||||||
|
`<https://tools.ietf.org/html/rfc7231#section-4.2.2>`_.
|
||||||
|
|
||||||
|
You can find more information about HTTP Methods at `<https://restfulapi.net/http-methods/>`_
|
||||||
|
|
||||||
|
Uncommon HTTP Methods
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
* PATCH method allows to perform a differential update (i.e. an update which provides only the differences and not the whole new representation);
|
||||||
|
* PURGE method is not a standard but is widely used in services that require this action
|
||||||
|
(e.g. `Varnish <https://varnish-cache.org/docs/3.0/tutorial/purging.html>`_, `Squid <https://wiki.squid-cache.org/SquidFaq/OperatingSquid#How_can_I_purge_an_object_from_my_cache.3F>`_).
|
||||||
|
gCat provides support for this method, but to support a wider range of clients, it also provides the Purge action via *DELETE* with the additional get parameter ``purge=true``.
|
||||||
|
|
||||||
|
|
||||||
|
Content-Type
|
||||||
|
------------
|
||||||
|
|
||||||
|
Any request must contain an indication of the interesting content type.
|
||||||
|
|
||||||
|
The client must specify the **Accept** HTTP Header for any operation returning a result.
|
||||||
|
|
||||||
|
.. code-block:: rest
|
||||||
|
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
For any operation sending content to the service, it is necessary to specify the **Content-Type** HTTP Header.
|
||||||
|
|
||||||
|
.. code-block:: rest
|
||||||
|
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
The service accepts and returns only JSON objects.
|
||||||
|
|
||||||
|
`Profile Collection <../api-docs/resource\_Profile.html>`_ instead can be manipulated in XML only.
|
||||||
|
|
||||||
|
Collections
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The following collections are available to any user.
|
||||||
|
Catalogue-Editor or above can invoke Non-safe methods only.
|
||||||
|
|
||||||
|
* `Item Collection <../api-docs/resource_Item.html>`_;
|
||||||
|
|
||||||
|
* `Resource Collection <../api-docs/resource_Resource.html>`_;
|
||||||
|
|
||||||
|
* `Profile Collection <../api-docs/resource_Profile.html>`_;
|
||||||
|
* `Namespace Collection <../api-docs/resource_Namespace.html>`_;
|
||||||
|
* `License Collection <../api-docs/resource_License.html>`_;
|
||||||
|
* `Trash Collection <../api-docs/resource_Trash.html>`_;
|
||||||
|
|
||||||
|
The following collections are available for Catalogue-Admins or above only:
|
||||||
|
|
||||||
|
* `Group Collection <../api-docs/resource_Group.html>`_;
|
||||||
|
* `Organization Collection <../api-docs/resource_Organization.html>`_;
|
||||||
|
* `User Collection <../api-docs/resource_User.html>`_;
|
||||||
|
* `Configuration Collection <../api-docs/resource_Configuration.html>`_.
|
||||||
|
|
||||||
|
An overview of the available collections is available at `<../api-docs/index.html>`_;
|
||||||
|
|
||||||
|
|
||||||
|
Roles
|
||||||
|
-----
|
||||||
|
|
||||||
|
Any user has one or more roles in the catalogue.
|
||||||
|
Only the VRE Manager can assign roles to VRE users.
|
||||||
|
|
||||||
|
|
||||||
|
The catalogue uses the following hierarchic roles:
|
||||||
|
|
||||||
|
**Catalogue-Member**:
|
||||||
|
A user with such a role is mainly capable of listing and reading items;
|
||||||
|
|
||||||
|
**Catalogue-Editor**:
|
||||||
|
A user with such a role is capable of managing the items he/she creates and capable of using other safe APIs;
|
||||||
|
|
||||||
|
**Catalogue-Admin**:
|
||||||
|
A user with such a role is capable of administrating many aspects of the catalogue;
|
||||||
|
|
||||||
|
**Catalogue-Manager**:
|
||||||
|
A user with such a role can use all the APIs exposed by the service except item moderation APIs (e.g. approve, reject, ...).
|
||||||
|
|
||||||
|
|
||||||
|
Another role that is not in the role hierarchy:
|
||||||
|
|
||||||
|
**Catalogue-Moderator**:
|
||||||
|
A user with such a role is capable of invoking the item moderation APIs.
|
||||||
|
|
||||||
|
|
||||||
|
.. TIP::
|
||||||
|
Please note that not all catalogues are moderated.
|
||||||
|
|
||||||
|
Moderated Catalogues
|
||||||
|
====================
|
||||||
|
|
||||||
|
|
||||||
|
Any catalogues can be declared as moderated.
|
||||||
|
This means that a **Catalogue-Moderator** must approve any submitted items to make them available to the other users of the catalogue.
|
||||||
|
|
||||||
|
In a moderated catalogue, an item can be in the following states:
|
||||||
|
|
||||||
|
**pending**:
|
||||||
|
The item is published by any allowed author (a Catalogue-Editor or above) but not available to the other users of the catalogue.
|
||||||
|
A Catalogue-Moderator has to approve or reject it;
|
||||||
|
|
||||||
|
**approved**:
|
||||||
|
A Catalogue-Moderator has approved the item published by any allowed users;
|
||||||
|
|
||||||
|
**rejected**:
|
||||||
|
A Catalogue-Moderator has rejected the item published by any allowed users.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The following are the moderation operations that an allowed user can perform on an item.
|
||||||
|
To present the moderation operations, we use the following convention:
|
||||||
|
|
||||||
|
``initial_state`` ---**operation** (*User/Role performing the operation*)---> ``final_state``
|
||||||
|
|
||||||
|
|
||||||
|
``initial_state`` can be ``none``, meaning the item does not exist.
|
||||||
|
|
||||||
|
|
||||||
|
The following are the allowed moderation operations on an item
|
||||||
|
|
||||||
|
``none`` ---**create** (*Author*)---> ``pending``
|
||||||
|
|
||||||
|
``pending`` ---**reject** (*Catalogue-Moderator*)---> ``rejected``
|
||||||
|
|
||||||
|
``pending`` ---**approve** (*Catalogue-Moderator*)---> ``approved``
|
||||||
|
|
||||||
|
``rejected`` ---**update** (*Author*)---> ``pending``
|
||||||
|
|
||||||
|
``approved`` ---**update** (*Author*)---> ``pending``
|
||||||
|
|
||||||
|
|
||||||
|
Please check the table below that summarises the item collection operation and the allowed users/roles.
|
||||||
|
|
||||||
|
In a moderated catalogue, both the Catalogue-Moderators and the item author can send messages to
|
||||||
|
discuss the approval process of the item. The messages are related to a specific item.
|
||||||
|
Any Catalogue-Moderators receive a message sent by an Author.
|
||||||
|
The author receives a message sent by a Catalogue-Moderator as well as the other Catalogue-Moderators (if any).
|
||||||
|
|
||||||
|
Messages can be sent both with an action which changes the status of the item or as an explicit action which does not change the status of the item:
|
||||||
|
|
||||||
|
``pending`` ---**message** (*Author OR Catalogue-Moderator*)---> ``pending``
|
||||||
|
|
||||||
|
``rejected`` ---**message** (*Author OR Catalogue-Moderator*)---> ``rejected``
|
||||||
|
|
||||||
|
``approved`` ---**message** (*Author OR Catalogue-Moderator*)---> ``approved``
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The following table summarizes the allowed/forbidden operations depending on the role of the user and the state of the item.
|
||||||
|
|
||||||
|
.. table::
|
||||||
|
|
||||||
|
+-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| Operation | Item State | Roles |
|
||||||
|
+ + +----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| | | Catalogue Moderator | Catalogue Admin/Manager | Catalogue Editor | Catalogue Member |
|
||||||
|
+=====================================+=============+======================+==========================+==========================================+===================+
|
||||||
|
| List | Yes all states | Yes all states | Yes only approved - All states if Author | Yes only approved |
|
||||||
|
+-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| Count | Yes all states | Yes all states | Yes only approved - All states if Author | Yes only approved |
|
||||||
|
+-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| Create | 403 Forbidden | Yes -> Pending | Yes -> Pending | 403 Forbidden |
|
||||||
|
+-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| Read | Yes all states | Yes all states | Yes only approved - All states if Author | Yes only approved |
|
||||||
|
+-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| Update | Pending | Yes -> Pending | Yes if Author -> Pending | Yes if Author -> Pending | 403 Forbidden |
|
||||||
|
+ +-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| | Rejected | Yes -> Pending | Yes if Author -> Pending | Yes if Author -> Pending | 403 Forbidden |
|
||||||
|
+ +-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| | Approved | 403 Forbidden | Yes -> Approved | Yes if Author -> Pending | 403 Forbidden |
|
||||||
|
+-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| Delete/Purge | Pending | Yes | Yes | Yes if Author | 403 Forbidden |
|
||||||
|
+ +-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| | Rejected | Yes | Yes | Yes if Author | 403 Forbidden |
|
||||||
|
+ +-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| | Approved | 403 Forbidden | Yes | Yes if Author | 403 Forbidden |
|
||||||
|
+-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| Approve a pending item | Yes | 403 Forbidden | 403 Forbidden | 403 Forbidden |
|
||||||
|
+-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| Reject a pending item | Yes | 403 Forbidden | 403 Forbidden | 403 Forbidden |
|
||||||
|
+-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
| Message about an item | Yes | Yes if Author | Yes if Author | 403 Forbidden |
|
||||||
|
+-------------------------------------+-------------+----------------------+--------------------------+------------------------------------------+-------------------+
|
||||||
|
|
||||||
|
|
||||||
|
The Moderation process has associated notification to authors and Catalogue-Moderators.
|
||||||
|
Please note that the user who has acted is not self-notified, e.g.
|
||||||
|
approve operation made by a Catalogue-Moderator notifies the item author and the other Catalogue-Moderators of the VRE.
|
||||||
|
|
||||||
|
The following table summarises the addressee of the notification for any action.
|
||||||
|
|
||||||
|
|
||||||
|
.. table::
|
||||||
|
|
||||||
|
+--------------+----------------------+--------+----------------------+
|
||||||
|
| Operation | Notified user/role |
|
||||||
|
+ +----------------------+--------+----------------------+
|
||||||
|
| | Catalogue-Moderators | Author | User made the action |
|
||||||
|
+==============+======================+========+======================+
|
||||||
|
| Create | Yes | Yes | Yes (custom message) |
|
||||||
|
+--------------+----------------------+--------+----------------------+
|
||||||
|
| Update | Yes | Yes | Yes (custom message) |
|
||||||
|
+--------------+----------------------+--------+----------------------+
|
||||||
|
| Approve | Yes + Social Post if | No |
|
||||||
|
| | requested (social_post=true) | |
|
||||||
|
| | and enabled for the VRE | |
|
||||||
|
+--------------+----------------------+--------+----------------------+
|
||||||
|
| Reject | Yes | Yes | No |
|
||||||
|
+--------------+----------------------+--------+----------------------+
|
||||||
|
| Delete/Purge | Yes | Yes | No |
|
||||||
|
+--------------+----------------------+--------+----------------------+
|
||||||
|
| Message | Yes | Yes | No |
|
||||||
|
+--------------+----------------------+--------+----------------------+
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Java Client
|
||||||
|
===========
|
||||||
|
|
||||||
|
We provide the following Java Client out-of-the-box.
|
||||||
|
|
||||||
|
.. TIP::
|
||||||
|
If you're coding in Java, it is recommended that you use this Java Client.
|
||||||
|
|
||||||
|
**Maven Coordinates**
|
||||||
|
|
||||||
|
.. code:: xml
|
||||||
|
|
||||||
|
<groupId>org.gcube.data-catalogue</groupId>
|
||||||
|
<artifactId>gcat-client</artifactId>
|
||||||
|
<version>[2.2.0, 3.0.0-SNAPSHOT)</version>
|
||||||
|
|
||||||
|
**Methods Result**
|
||||||
|
|
||||||
|
The service exposes `its methods <../api-docs/index.html>`_ using a standard naming approach. Moreover, they accept (in the case of HTTP POST/PUT methods) JSON objects.
|
||||||
|
|
||||||
|
.. IMPORTANT::
|
||||||
|
The result of all methods is always a JSON object as per below:
|
||||||
|
|
||||||
|
.. code:: javascript
|
||||||
|
|
||||||
|
{
|
||||||
|
"rating": 0.0,
|
||||||
|
"license_title": "Creative Commons Attribution Share-Alike 4.0",
|
||||||
|
"maintainer": "Frosini Luca",
|
||||||
|
"relationships_as_object": [],
|
||||||
|
"private": false,
|
||||||
|
"maintainer_email": "luca.frosini@isti.cnr.it",
|
||||||
|
"num_tags": 1,
|
||||||
|
"id": "17051d86-c127-4928-9296-d3d7590161fe",
|
||||||
|
"metadata_created": "2022-10-17T12:45:53.118318",
|
||||||
|
"metadata_modified": "2022-10-18T10:30:03.362756",
|
||||||
|
"author": "Frosini Luca",
|
||||||
|
"author_email": "luca.frosini@isti.cnr.it",
|
||||||
|
"state": "active",
|
||||||
|
"version": null,
|
||||||
|
"creator_user_id": "f1b0265c-9983-4f97-a7b6-be3cc0544b27",
|
||||||
|
"type": "dataset",
|
||||||
|
"resources": [],
|
||||||
|
"num_resources": 0,
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"vocabulary_id": null,
|
||||||
|
"state": "active",
|
||||||
|
"display_name": "Test",
|
||||||
|
"id": "fec9de86-51a2-41b0-aef4-ba06eb39e16d",
|
||||||
|
"name": "Test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"groups": [],
|
||||||
|
"license_id": "CC-BY-SA-4.0",
|
||||||
|
"relationships_as_subject": [],
|
||||||
|
"organization": {
|
||||||
|
"description": "",
|
||||||
|
"created": "2016-05-30T11:30:41.710079",
|
||||||
|
"title": "devVRE",
|
||||||
|
"name": "devvre",
|
||||||
|
"is_organization": true,
|
||||||
|
"state": "active",
|
||||||
|
"image_url": "",
|
||||||
|
"revision_id": "a7eee485-a6d5-4a7b-8f73-b0ed999d5b03",
|
||||||
|
"type": "organization",
|
||||||
|
"id": "3571cca5-b0ae-4dc6-b791-434a8e062ce5",
|
||||||
|
"approval_status": "approved"
|
||||||
|
},
|
||||||
|
"name": "my_test_item_devvre",
|
||||||
|
"isopen": true,
|
||||||
|
"url": "http://www.d4science.org",
|
||||||
|
"notes": "A test item of Luca Frosini",
|
||||||
|
"extras": [
|
||||||
|
{
|
||||||
|
"key": "Item URL",
|
||||||
|
"value": "https://data.dev.d4science.org/ctlg/devVRE/my_test_item_devvre"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Language",
|
||||||
|
"value": "EN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system:cm_item_status",
|
||||||
|
"value": "approved"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system:cm_item_visibility",
|
||||||
|
"value": "public"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system:type",
|
||||||
|
"value": "EmptyProfile"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license_url": "https://creativecommons.org/licenses/by-sa/4.0/",
|
||||||
|
"ratings_count": 0,
|
||||||
|
"title": "My Test Item",
|
||||||
|
"revision_id": "bc0d1f2a-4e97-4810-b951-8b72e8279719"
|
||||||
|
}
|
||||||
|
|
||||||
|
*Inputs are automatically validated before the request is served.*
|
||||||
|
|
||||||
|
|
||||||
|
**Usage examples**
|
||||||
|
|
||||||
|
- Example 1
|
||||||
|
|
||||||
|
.. code:: java
|
||||||
|
|
||||||
|
import org.gcube.gcat.client.Item;
|
||||||
|
|
||||||
|
// count item number
|
||||||
|
Item item = new Item();
|
||||||
|
int count = item.count();
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Service Discovery on IS
|
||||||
|
=======================
|
||||||
|
|
||||||
|
The service can be discovered in the IS as EService with the following JSON query:
|
||||||
|
|
||||||
|
.. code:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"@class": "EService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"@class": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"@class": "SoftwareFacet",
|
||||||
|
"group": "org.gcube.data-catalogue",
|
||||||
|
"name": "gcat"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Service Maven Coordinates
|
||||||
|
=========================
|
||||||
|
|
||||||
|
The maven coordinates of the gCat service are:
|
||||||
|
|
||||||
|
.. code:: xml
|
||||||
|
|
||||||
|
<groupId>org.gcube.data-catalogue</groupId>
|
||||||
|
<artifactId>gcat</artifactId>
|
||||||
|
|
||||||
|
|
||||||
|
Postman Collection
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Here you can download the `postman collection to interact with the gCat service <../api-docs/postman/gcat.postman_collection.json>`_.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
pushd %~dp0
|
||||||
|
|
||||||
|
REM Command file for Sphinx documentation
|
||||||
|
|
||||||
|
if "%SPHINXBUILD%" == "" (
|
||||||
|
set SPHINXBUILD=sphinx-build
|
||||||
|
)
|
||||||
|
set SOURCEDIR=.
|
||||||
|
set BUILDDIR=_build
|
||||||
|
|
||||||
|
if "%1" == "" goto help
|
||||||
|
|
||||||
|
%SPHINXBUILD% >NUL 2>NUL
|
||||||
|
if errorlevel 9009 (
|
||||||
|
echo.
|
||||||
|
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||||
|
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||||
|
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||||
|
echo.may add the Sphinx directory to PATH.
|
||||||
|
echo.
|
||||||
|
echo.If you don't have Sphinx installed, grab it from
|
||||||
|
echo.https://www.sphinx-doc.org/
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:help
|
||||||
|
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
|
|
||||||
|
:end
|
||||||
|
popd
|
|
@ -0,0 +1,23 @@
|
||||||
|
Dev and Pre Users used for moderation tests
|
||||||
|
========
|
||||||
|
|
||||||
|
To perform moderation tests in dev and preproduction infrastructure we use different users with the indicated roles.
|
||||||
|
|
||||||
|
.. table::
|
||||||
|
|
||||||
|
+---------------+---------------+----------------------------------------+
|
||||||
|
| User | Username | Role |
|
||||||
|
+===============+===============+========================================+
|
||||||
|
| Mister Blonde | mister.blonde | Catalogue-Admin + Catalogue-Moderator |
|
||||||
|
+---------------+---------------+----------------------------------------+
|
||||||
|
| Mister Blue | mister.blue | Catalogue-Admin |
|
||||||
|
+---------------+---------------+----------------------------------------+
|
||||||
|
| Mister Brown | mister.brown | Catalogue-Moderator |
|
||||||
|
+---------------+---------------+----------------------------------------+
|
||||||
|
| Mister Orange | mister.orange | Catalogue-Editor |
|
||||||
|
+---------------+---------------+----------------------------------------+
|
||||||
|
| Mister Pink | mister.pink | NO ROLE (means Catalogue-Member) |
|
||||||
|
+---------------+---------------+----------------------------------------+
|
||||||
|
| Mister White | mister.white | Catalogue-Manager |
|
||||||
|
+---------------+---------------+----------------------------------------+
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE xml>
|
|
||||||
<application mode='online'>
|
|
||||||
<name>${project.artifactId}</name>
|
|
||||||
<group>${serviceClass}</group>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
<description>${project.description}</description>
|
|
||||||
<local-persistence location='target' />
|
|
||||||
</application>
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE xml>
|
||||||
|
<application mode='online'>
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<group>${project.groupId}</group>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<description>${project.description}</description>
|
||||||
|
<exclude>/api-docs.*</exclude>
|
||||||
|
<exclude>/docs.*</exclude>
|
||||||
|
<exclude>/profiles/SCHEMA</exclude>
|
||||||
|
</application>
|
|
@ -0,0 +1,329 @@
|
||||||
|
{
|
||||||
|
"global": {
|
||||||
|
"name": "gcat",
|
||||||
|
"group": "data-catalogue",
|
||||||
|
"title": "gCube Catalogue (gCat) Service {{version}}",
|
||||||
|
"license": {
|
||||||
|
"id": "EUPL-1.1",
|
||||||
|
"url": "https://opensource.org/licenses/EUPL-1.1"
|
||||||
|
},
|
||||||
|
"keywords": ["gCube", "Catalogue", "D4Science"],
|
||||||
|
"description": "gCube Catalogue (gCat) Service allows the publication of items in the gCube Catalogue.",
|
||||||
|
"html_description": "<p><a href=\"https://www.gcube-system.org/\">gCube</a> Catalogue (gCat) Service allows any client to publish items in the gCube Catalogue.</p>",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"affiliation": "Istituto di Scienza e Tecnologie dell'Informazione \"A. Faedo\" - CNR, Italy",
|
||||||
|
"name": "Frosini, Luca",
|
||||||
|
"orcid": "0000-0003-3183-2291"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"url": "https://code-repo.d4science.org/gCubeSystem/{{name}}/archive/v{{version}}.zip",
|
||||||
|
"desired_name": "{{name}}-v{{version}}.zip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://code-repo.d4science.org/gCubeSystem/{{name}}/archive/v{{version}}.tar.gz",
|
||||||
|
"desired_name": "{{name}}-v{{version}}.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://nexus.d4science.org/nexus/service/local/repo_groups/gcube-releases-all/content/org/gcube/{{group}}/{{name}}/{{version}}/{{name}}-{{version}}.war",
|
||||||
|
"desired_name": "{{name}}-v{{version}}.war"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"code_location": "https://code-repo.d4science.org/gCubeSystem/{{name}}/releases/tag/v{{version}}",
|
||||||
|
"grants": [
|
||||||
|
{
|
||||||
|
"id": "004260",
|
||||||
|
"name": "DILIGENT",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/004260"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "212488",
|
||||||
|
"name": "D4Science",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/212488"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "239019",
|
||||||
|
"name": "D4Science-II",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/239019"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "283465",
|
||||||
|
"name": "ENVRI",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/283465"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "283644",
|
||||||
|
"name": "iMarine",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/283644"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "288754",
|
||||||
|
"name": "EUBrazilOpenBio",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/288754"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "654024",
|
||||||
|
"name": "SoBigData",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/654024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "654119",
|
||||||
|
"name": "PARTHENOS",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/654119"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "654142",
|
||||||
|
"name": "EGI-Engage",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/654142"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "654182",
|
||||||
|
"name": "ENVRI PLUS",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/654182"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "675680",
|
||||||
|
"name": "BlueBRIDGE",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/675680"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "727610",
|
||||||
|
"name": "PerformFISH",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/727610"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "731001",
|
||||||
|
"name": "AGINFRA PLUS",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/731001"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "818194",
|
||||||
|
"name": "DESIRA",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/818194"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "823914",
|
||||||
|
"name": "ARIADNEplus",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/823914"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "824091",
|
||||||
|
"name": "RISIS 2",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/824091"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "857650",
|
||||||
|
"name": "EOSC-Pillar",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/857650"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "862409",
|
||||||
|
"name": "Blue Cloud",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/862409"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "871042",
|
||||||
|
"name": "SoBigData-PlusPlus",
|
||||||
|
"url": "https://cordis.europa.eu/project/id/871042"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"publishers": {
|
||||||
|
"ZenodoSoftwareVersionPublisher": {
|
||||||
|
"elaboration": "NONE",
|
||||||
|
"additional_html_description": "\n\n<p><a href=\"https://www.gcube-system.org/\">gCube</a> is an open-source software toolkit used for building and operating Hybrid Data Infrastructures enabling the dynamic deployment of Virtual Research Environments, such as the <a href=\"https://www.d4science.org/\">D4Science Infrastructure</a>, by favouring the realisation of reuse-oriented policies.</p>\n\n<p><a href=\"https://www.gcube-system.org/\">gCube</a> has been used to successfully build and operate infrastructures and virtual research environments for application domains ranging from biodiversity to environmental data management and cultural heritage.</p>\n\n<p><a href=\"https://www.gcube-system.org/\">gCube</a> offers components supporting typical data management workflows including data access, curation, processing, and visualisation on a large set of data typologies ranging from primary biodiversity data to geospatial and tabular data.</p>\n\n<p><a href=\"https://www.d4science.org/\">D4Science</a> is a Hybrid Data Infrastructure combining over 500 software components and integrating data from more than 50 different data providers into a coherent and managed system of hardware, software, and data resources. The D4Science infrastructure drastically reduces the cost of ownership, maintenance, and operation thanks to the exploitation of gCube.</p>\n\n<p> </p>\n\n<p>The official source code location of this software version is available at:</p>\n\n<p><a href=\"{{code_location}}\">{{code_location}}</a></p>",
|
||||||
|
"skip_grants": ["004260"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exporters": {
|
||||||
|
"BibLaTeXSoftwareVersionExporter": {
|
||||||
|
"elaboration": "ALL"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"export_filename": "{{name}}"
|
||||||
|
},
|
||||||
|
"versions": [
|
||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"date": "2019-01-10",
|
||||||
|
"group": "data-publishing",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"url": "https://nexus.d4science.org/nexus/service/local/repositories/gcube-snapshots/content/org/gcube/data-publishing/gcat/1.0.0-SNAPSHOT/gcat-1.0.0-20190109.172827-2.war",
|
||||||
|
"desired_name": "{{name}}-v{{version}}.war"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"gcube_release_version": null,
|
||||||
|
"gcube_release_ticket": null,
|
||||||
|
"concept_doi_url": null,
|
||||||
|
"version_doi_url": null,
|
||||||
|
"code_location": "https://code-repo.d4science.org/gCubeSystem/{{name}}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.1.0",
|
||||||
|
"date": "2019-02-26",
|
||||||
|
"group": "data-publishing",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"url": "https://nexus.d4science.org/nexus/service/local/repo_groups/gcube-releases-all/content/org/gcube/data-publishing/gcat/1.1.0-4.13.1-177071/gcat-1.1.0-4.13.1-177071-src.zip",
|
||||||
|
"desired_name": "{{name}}-v{{version}}.zip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://nexus.d4science.org/nexus/service/local/repo_groups/gcube-releases-all/content/org/gcube/data-publishing/gcat/1.1.0-4.13.1-177071/gcat-1.1.0-4.13.1-177071.war",
|
||||||
|
"desired_name": "{{name}}-v{{version}}.war"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"gcube_release_version": "4.13.1",
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/12988",
|
||||||
|
"concept_doi_url": "PREVIOUS",
|
||||||
|
"version_doi_url": null,
|
||||||
|
"code_location": "https://code-repo.d4science.org/gCubeSystem/{{name}}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.2.0",
|
||||||
|
"date": "2019-05-20",
|
||||||
|
"group": "data-publishing",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"url": "https://nexus.d4science.org/nexus/service/local/repositories/gcube-snapshots/content/org/gcube/data-publishing/gcat/1.2.0-SNAPSHOT/gcat-1.2.0-20190520.132914-10.war",
|
||||||
|
"desired_name": "{{name}}-v{{version}}.war"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"gcube_release_version": null,
|
||||||
|
"gcube_release_ticket": null,
|
||||||
|
"concept_doi_url": "PREVIOUS",
|
||||||
|
"version_doi_url": null,
|
||||||
|
"code_location": "https://code-repo.d4science.org/gCubeSystem/{{name}}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.3.0",
|
||||||
|
"date": "2019-06-27",
|
||||||
|
"group": "data-publishing",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"url": "https://nexus.d4science.org/nexus/service/local/repo_groups/gcube-releases-all/content/org/gcube/data-publishing/gcat/1.3.0-4.14.0-179505/gcat-1.3.0-4.14.0-179505-src.zip",
|
||||||
|
"desired_name": "{{name}}-v{{version}}.zip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://nexus.d4science.org/nexus/service/local/repo_groups/gcube-releases-all/content/org/gcube/data-publishing/gcat/1.3.0-4.14.0-179505/gcat-1.3.0-4.14.0-179505.war",
|
||||||
|
"desired_name": "{{name}}-v{{version}}.war"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"gcube_release_version": "4.14.0",
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/16743",
|
||||||
|
"concept_doi_url": "PREVIOUS",
|
||||||
|
"version_doi_url": null,
|
||||||
|
"code_location": "https://code-repo.d4science.org/gCubeSystem/{{name}}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.4.0",
|
||||||
|
"date": "2019-11-06",
|
||||||
|
"group": "data-publishing",
|
||||||
|
"gcube_release_version": "4.15.0",
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/17294",
|
||||||
|
"concept_doi_url": "PREVIOUS",
|
||||||
|
"version_doi_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.4.1",
|
||||||
|
"date": "2019-12-20",
|
||||||
|
"group": "data-publishing",
|
||||||
|
"gcube_release_version": "4.18.0",
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/18335",
|
||||||
|
"concept_doi_url": "PREVIOUS",
|
||||||
|
"version_doi_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.4.2",
|
||||||
|
"date": "2020-02-14",
|
||||||
|
"group": "data-publishing",
|
||||||
|
"gcube_release_version": "4.20.0",
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/18507",
|
||||||
|
"concept_doi_url": "PREVIOUS",
|
||||||
|
"version_doi_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.4.3",
|
||||||
|
"date": "2020-06-16",
|
||||||
|
"group": "data-publishing",
|
||||||
|
"gcube_release_version": "4.23.0",
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/19322",
|
||||||
|
"concept_doi_url": "PREVIOUS",
|
||||||
|
"version_doi_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.4.4",
|
||||||
|
"date": "2021-02-24",
|
||||||
|
"group": "data-publishing",
|
||||||
|
"gcube_release_version": "5.0.0",
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/20648",
|
||||||
|
"concept_doi_url": "PREVIOUS",
|
||||||
|
"version_doi_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "1.4.5",
|
||||||
|
"date": "2021-03-31",
|
||||||
|
"group": "data-publishing",
|
||||||
|
"gcube_release_version": "5.1.0",
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/20920",
|
||||||
|
"concept_doi_url": "PREVIOUS",
|
||||||
|
"version_doi_url": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"date": "2021-05-04",
|
||||||
|
"gcube_release_version": "5.2.0",
|
||||||
|
"concept_doi_url": "https://doi.org/10.5281/zenodo.7446641",
|
||||||
|
"version_doi_url": "https://doi.org/10.5281/zenodo.7446642"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.1.0",
|
||||||
|
"date": "2022-01-27",
|
||||||
|
"gcube_release_version": "5.7.0",
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/21685/",
|
||||||
|
"concept_doi_url": "https://doi.org/10.5281/zenodo.7446641",
|
||||||
|
"version_doi_url": "https://doi.org/10.5281/zenodo.7447405"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.2.0",
|
||||||
|
"date": "2022-05-12",
|
||||||
|
"gcube_release_version": "5.11.0",
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/22943",
|
||||||
|
"concept_doi_url": "https://doi.org/10.5281/zenodo.7446641",
|
||||||
|
"version_doi_url": "https://doi.org/10.5281/zenodo.7447427"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.3.0",
|
||||||
|
"date": "2022-07-22",
|
||||||
|
"gcube_release_version": "5.13.0",
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/23374",
|
||||||
|
"concept_doi_url": "https://doi.org/10.5281/zenodo.7446641",
|
||||||
|
"version_doi_url": "https://doi.org/10.5281/zenodo.7448628"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.4.0",
|
||||||
|
"date": "2022-09-16",
|
||||||
|
"gcube_release_version": "5.13.1",
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/23650",
|
||||||
|
"concept_doi_url": "https://doi.org/10.5281/zenodo.7446641",
|
||||||
|
"version_doi_url": "https://doi.org/10.5281/zenodo.7448730"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.4.1",
|
||||||
|
"date": "2022-12-07",
|
||||||
|
"gcube_release_version": "5.14.0",
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/23885",
|
||||||
|
"concept_doi_url": "https://doi.org/10.5281/zenodo.7446641",
|
||||||
|
"version_doi_url": "https://doi.org/10.5281/zenodo.7467222"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "2.5.0",
|
||||||
|
"date": null,
|
||||||
|
"gcube_release_version": null,
|
||||||
|
"gcube_release_ticket": "https://support.d4science.org/issues/24601",
|
||||||
|
"concept_doi_url": "https://doi.org/10.5281/zenodo.7446641",
|
||||||
|
"version_doi_url": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
277
pom.xml
277
pom.xml
|
@ -5,14 +5,14 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>maven-parent</artifactId>
|
<artifactId>maven-parent</artifactId>
|
||||||
<groupId>org.gcube.tools</groupId>
|
<groupId>org.gcube.tools</groupId>
|
||||||
<version>1.1.0</version>
|
<version>1.2.0</version>
|
||||||
<relativePath />
|
<relativePath />
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>org.gcube.data-publishing</groupId>
|
<groupId>org.gcube.data-catalogue</groupId>
|
||||||
<artifactId>gcat</artifactId>
|
<artifactId>gcat</artifactId>
|
||||||
<packaging>war</packaging>
|
<packaging>war</packaging>
|
||||||
<version>1.4.3</version>
|
<version>2.6.0</version>
|
||||||
<name>gCube Catalogue (gCat) Service</name>
|
<name>gCube Catalogue (gCat) Service</name>
|
||||||
<description>
|
<description>
|
||||||
This service allows any client to publish on the gCube Catalogue.
|
This service allows any client to publish on the gCube Catalogue.
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<webappDirectory>${project.basedir}${file.separator}src${file.separator}main${file.separator}webapp${file.separator}WEB-INF</webappDirectory>
|
<webappDirectory>${project.basedir}${file.separator}src${file.separator}main${file.separator}webapp${file.separator}WEB-INF</webappDirectory>
|
||||||
<serviceClass>DataPublishing</serviceClass>
|
<enunciate.version>2.14.0</enunciate.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<scm>
|
<scm>
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.gcube.distribution</groupId>
|
<groupId>org.gcube.distribution</groupId>
|
||||||
<artifactId>gcube-smartgears-bom</artifactId>
|
<artifactId>gcube-smartgears-bom</artifactId>
|
||||||
<version>1.1.0</version>
|
<version>2.5.1</version>
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
@ -43,16 +43,6 @@
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>org.gcube.data-publishing</groupId>
|
|
||||||
<artifactId>gcat-api</artifactId>
|
|
||||||
<version>[1.2.0, 2.0.0-SNAPSHOT)</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.tika</groupId>
|
|
||||||
<artifactId>tika-core</artifactId>
|
|
||||||
<version>1.20</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
@ -61,11 +51,74 @@
|
||||||
<groupId>org.gcube.core</groupId>
|
<groupId>org.gcube.core</groupId>
|
||||||
<artifactId>common-encryption</artifactId>
|
<artifactId>common-encryption</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.core</groupId>
|
||||||
|
<artifactId>common-scope</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.core</groupId>
|
||||||
|
<artifactId>common-smartgears-app</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.data-catalogue</groupId>
|
||||||
|
<artifactId>catalogue-core</artifactId>
|
||||||
|
<version>[1.0.0, 2.0.0-SNAPSHOT)</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.data-catalogue</groupId>
|
||||||
|
<artifactId>gcat-api</artifactId>
|
||||||
|
<version>[2.3.2, 3.0.0-SNAPSHOT)</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.social-networking</groupId>
|
||||||
|
<artifactId>social-service-client</artifactId>
|
||||||
|
<version>[1.0.0, 2.0.0-SNAPSHOT)</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.common</groupId>
|
||||||
|
<artifactId>authorization-utils</artifactId>
|
||||||
|
<version>[2.2.0, 3.0.0-SNAPSHOT)</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.common</groupId>
|
||||||
|
<artifactId>storagehub-model</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish.jersey.media</groupId>
|
||||||
|
<artifactId>jersey-media-multipart</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.information-system</groupId>
|
||||||
|
<artifactId>information-system-model</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.resource-management</groupId>
|
||||||
|
<artifactId>gcube-model</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.information-system</groupId>
|
||||||
|
<artifactId>resource-registry-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.information-system</groupId>
|
||||||
|
<artifactId>resource-registry-publisher</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.information-system</groupId>
|
||||||
|
<artifactId>resource-registry-query-template-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.resources</groupId>
|
||||||
|
<artifactId>common-gcore-resources</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.resources</groupId>
|
||||||
|
<artifactId>registry-publisher</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.gcube.data-catalogue</groupId>
|
<groupId>org.gcube.data-catalogue</groupId>
|
||||||
<artifactId>gcubedatacatalogue-metadata-discovery</artifactId>
|
<artifactId>gcubedatacatalogue-metadata-discovery</artifactId>
|
||||||
<version>[3.0.0-SNAPSHOT, 4.0.0-SNAPSHOT)</version>
|
<version>[3.0.0, 4.0.0-SNAPSHOT)</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>log4j</groupId>
|
<groupId>log4j</groupId>
|
||||||
|
@ -77,30 +130,52 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.portlets.user</groupId>
|
||||||
|
<artifactId>uri-resolver-manager</artifactId>
|
||||||
|
<version>[1.0.0, 2.0.0-SNAPSHOT)</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.glassfish.jersey.containers</groupId>
|
<groupId>org.glassfish.jersey.containers</groupId>
|
||||||
<artifactId>jersey-container-servlet</artifactId>
|
<artifactId>jersey-container-servlet</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Required with jersey 2.27 <dependency> <groupId>org.glassfish.jersey.inject</groupId>
|
|
||||||
<artifactId>jersey-hk2</artifactId> </dependency> -->
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.glassfish.jersey.media</groupId>
|
<groupId>javax.ws.rs</groupId>
|
||||||
<artifactId>jersey-media-multipart</artifactId>
|
<artifactId>javax.ws.rs-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Added to support Java 11 JDK -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.gcube.common</groupId>
|
<groupId>javax.xml.ws</groupId>
|
||||||
<artifactId>storagehub-client-library</artifactId>
|
<artifactId>jaxws-api</artifactId>
|
||||||
<version>[1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT)</version>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- END Added to support Java 11 JDK -->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.gcube.common</groupId>
|
<groupId>org.gcube.common</groupId>
|
||||||
<artifactId>gxHTTP</artifactId>
|
<artifactId>gxHTTP</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.common</groupId>
|
||||||
|
<artifactId>keycloak-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.core</groupId>
|
||||||
|
<artifactId>common-smartgears</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
<version>42.2.19</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<!-- ehCAChe -->
|
<!-- ehCAChe -->
|
||||||
|
@ -117,11 +192,7 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- END ehCAChe -->
|
<!-- END ehCAChe -->
|
||||||
|
|
||||||
<dependency>
|
<!-- Used for validation in oldutils. TO BE REMOVED ASAP -->
|
||||||
<groupId>org.gcube.core</groupId>
|
|
||||||
<artifactId>common-smartgears</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>de.grundid.opendatalab</groupId>
|
<groupId>de.grundid.opendatalab</groupId>
|
||||||
<artifactId>geojson-jackson</artifactId>
|
<artifactId>geojson-jackson</artifactId>
|
||||||
|
@ -129,29 +200,75 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-lang</groupId>
|
<groupId>org.gcube.data-publishing</groupId>
|
||||||
<artifactId>commons-lang</artifactId>
|
<artifactId>storagehub-application-persistence</artifactId>
|
||||||
<version>2.3</version>
|
<version>[3.3.0,4.0.0-SNAPSHOT)</version>
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>commons-io</groupId>
|
|
||||||
<artifactId>commons-io</artifactId>
|
|
||||||
<version>2.6</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Used to automatically convert XML to JSON -->
|
<!-- Used to automatically convert XML to JSON -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.json</groupId>
|
<groupId>org.json</groupId>
|
||||||
<artifactId>json</artifactId>
|
<artifactId>json</artifactId>
|
||||||
<version>20140107</version>
|
<version>20211205</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Used to detect Resource MimeTypes -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.gcube.data-publishing</groupId>
|
<groupId>org.apache.tika</groupId>
|
||||||
<artifactId>storagehub-application-persistence</artifactId>
|
<artifactId>tika-core</artifactId>
|
||||||
<version>[1.0.0,2.0.0-SNAPSHOT)</version>
|
<version>2.1.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Used by Validator -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-lang</groupId>
|
||||||
|
<artifactId>commons-lang</artifactId>
|
||||||
|
<version>2.6</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Zulip is not used at the moment
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.taliox</groupId>
|
||||||
|
<artifactId>zulip-java-rest</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.common</groupId>
|
||||||
|
<artifactId>gcube-jackson-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.common</groupId>
|
||||||
|
<artifactId>gcube-jackson-annotations</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.gcube.common</groupId>
|
||||||
|
<artifactId>gcube-jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Required for Enunciate plugin -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.webcohesion.enunciate</groupId>
|
||||||
|
<artifactId>enunciate-core-annotations</artifactId>
|
||||||
|
<version>${enunciate.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.webcohesion.enunciate</groupId>
|
||||||
|
<artifactId>enunciate-rt-util</artifactId>
|
||||||
|
<version>${enunciate.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- END Required for Enunciate plugin -->
|
||||||
|
|
||||||
|
|
||||||
<!-- Test libraries -->
|
<!-- Test libraries -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -167,50 +284,74 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<!-- Sphinx plugin' -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>kr.motd.maven</groupId>
|
||||||
|
<artifactId>sphinx-maven-plugin</artifactId>
|
||||||
|
<version>2.10.0</version>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}${file.separator}${project.build.finalName}${file.separator}docs</outputDirectory>
|
||||||
|
<builder>html</builder>
|
||||||
|
<configDirectory>${project.basedir}${file.separator}documentation${file.separator}sphinx</configDirectory>
|
||||||
|
<sourceDirectory>${project.basedir}${file.separator}documentation${file.separator}sphinx</sourceDirectory>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>process-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>generate</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<!-- Enunciate Maven plugin -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.webcohesion.enunciate</groupId>
|
||||||
|
<artifactId>enunciate-maven-plugin</artifactId>
|
||||||
|
<version>${enunciate.version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>assemble</id>
|
||||||
|
<goals>
|
||||||
|
<goal>assemble</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<!-- This does not works as expected. We need to use the maven-resources-plugin below
|
||||||
|
<docsDir>${project.build.directory}${file.separator}${project.artifactId}-${project.version}${file.separator}api-docs</docsDir>
|
||||||
|
-->
|
||||||
|
<configFile>${project.basedir}${file.separator}documentation${file.separator}enunciate${file.separator}enunciate.xml</configFile>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<!-- Copy Enunciate Documentation from your-application/api-docs into your war -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>copy-profile</id>
|
<id>copy-enunciate-docs</id>
|
||||||
|
<phase>process-resources</phase>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>copy-resources</goal>
|
<goal>copy-resources</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<phase>process-resources</phase>
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<outputDirectory>${webappDirectory}</outputDirectory>
|
<outputDirectory>target</outputDirectory>
|
||||||
<resources>
|
<resources>
|
||||||
<resource>
|
<resource>
|
||||||
<directory>${project.basedir}</directory>
|
<targetPath>${project.build.directory}${file.separator}${project.artifactId}-${project.version}${file.separator}api-docs</targetPath>
|
||||||
|
<directory>${project.build.directory}/api-docs</directory>
|
||||||
<filtering>true</filtering>
|
<filtering>true</filtering>
|
||||||
<excludes>
|
|
||||||
<exclude>src</exclude>
|
|
||||||
</excludes>
|
|
||||||
<includes>
|
|
||||||
<include>LICENSE.md</include>
|
|
||||||
<include>README.md</include>
|
|
||||||
<include>CHANGELOG.md</include>
|
|
||||||
<include>gcube-app.xml</include>
|
|
||||||
</includes>
|
|
||||||
</resource>
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>make-servicearchive</id>
|
|
||||||
<phase>package</phase>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
package org.gcube.gcat;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
|
||||||
|
import org.gcube.gcat.configuration.CatalogueConfigurationFactory;
|
||||||
|
import org.gcube.gcat.configuration.isproxies.impl.FacetBasedISConfigurationProxy;
|
||||||
|
import org.gcube.gcat.configuration.isproxies.impl.FacetBasedISConfigurationProxyFactory;
|
||||||
|
import org.gcube.gcat.configuration.isproxies.impl.GCoreISConfigurationProxyFactory;
|
||||||
|
import org.gcube.gcat.rest.RequestFilter;
|
||||||
|
import org.gcube.smartgears.ApplicationManager;
|
||||||
|
import org.gcube.smartgears.ContextProvider;
|
||||||
|
import org.gcube.smartgears.context.application.ApplicationContext;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
|
public class GCatInitializator implements ApplicationManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logger
|
||||||
|
*/
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(GCatInitializator.class);
|
||||||
|
|
||||||
|
public static boolean initialised;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* The method discover the plugins available on classpath and their own
|
||||||
|
* supported capabilities and publish a ServiceEndpoint with the
|
||||||
|
* discovered information.
|
||||||
|
* Furthermore create/connect to DB
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public synchronized void onInit() {
|
||||||
|
RequestFilter requestFilter = new RequestFilter();
|
||||||
|
try {
|
||||||
|
requestFilter.filter(null);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
String context = SecretManagerProvider.instance.get().getContext();
|
||||||
|
|
||||||
|
logger.trace(
|
||||||
|
"\n-------------------------------------------------------\n"
|
||||||
|
+ "GCat is Starting on context {}\n"
|
||||||
|
+ "-------------------------------------------------------",
|
||||||
|
context);
|
||||||
|
|
||||||
|
ApplicationContext applicationContext = ContextProvider.get();
|
||||||
|
String gcatEServiceID = applicationContext.id();
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
FacetBasedISConfigurationProxyFactory fbigcpf = new FacetBasedISConfigurationProxyFactory();
|
||||||
|
FacetBasedISConfigurationProxy facetBasedISConfigurationProxy = fbigcpf.getInstance(context);
|
||||||
|
if(!initialised) {
|
||||||
|
CatalogueConfigurationFactory.addISConfigurationProxyFactory(fbigcpf);
|
||||||
|
GCoreISConfigurationProxyFactory gcigcpf = new GCoreISConfigurationProxyFactory();
|
||||||
|
CatalogueConfigurationFactory.addISConfigurationProxyFactory(gcigcpf);
|
||||||
|
initialised = true;
|
||||||
|
facetBasedISConfigurationProxy.installQueryTemplate();
|
||||||
|
}
|
||||||
|
facetBasedISConfigurationProxy.setServiceEServiceID(gcatEServiceID);
|
||||||
|
facetBasedISConfigurationProxy.createCallsForToVirtualService();
|
||||||
|
}catch (Exception e) {
|
||||||
|
logger.warn("Gcat is not configured through the Facet Based IS in context {}. Please create/addToContext the expected resources ASAP. The Gcore IS will be used.", context);
|
||||||
|
logger.trace("Gcat is not configured through the Facet Based IS in context {}. The reason is:\n", context, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
logger.trace(
|
||||||
|
"\n-------------------------------------------------------\n"
|
||||||
|
+ "GCat Started Successfully on context {}\n"
|
||||||
|
+ "-------------------------------------------------------",
|
||||||
|
context);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* This function is invoked before the service will stop and unpublish the
|
||||||
|
* resource from the IS to maintain the infrastructure integrity.
|
||||||
|
* Furthermore close the connection to DB.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public synchronized void onShutdown(){
|
||||||
|
RequestFilter requestFilter = new RequestFilter();
|
||||||
|
try {
|
||||||
|
requestFilter.filter(null);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
String context = SecretManagerProvider.instance.get().getContext();
|
||||||
|
|
||||||
|
logger.trace(
|
||||||
|
"\n-------------------------------------------------------\n"
|
||||||
|
+ "GCat is Stopping on context {}\n"
|
||||||
|
+ "-------------------------------------------------------",
|
||||||
|
context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't delete the relation.
|
||||||
|
* Thanks to the propagation constraint,
|
||||||
|
* it will be deleted when the EService will be deleted.
|
||||||
|
*
|
||||||
|
ApplicationContext applicationContext = ContextProvider.get();
|
||||||
|
String gcatEServiceID = applicationContext.id();
|
||||||
|
|
||||||
|
FacetBasedISGcatConfigurationProxyFactory fbigcpf = new FacetBasedISGcatConfigurationProxyFactory();
|
||||||
|
FacetBasedGcatISConfigurationProxy facetBasedISConfigurationProxy = fbigcpf.getInstance(context);
|
||||||
|
facetBasedISConfigurationProxy.setGcatEServiceID(gcatEServiceID);
|
||||||
|
try {
|
||||||
|
facetBasedISConfigurationProxy.deleteCallsForToVirtualService();
|
||||||
|
}catch (Exception e) {
|
||||||
|
logger.warn("Gcat is not configured through the Facet Based IS in context {}. Please create/addToContext the expected resources ASAP. The Gcore IS will be used.", context, e);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
logger.trace(
|
||||||
|
"\n-------------------------------------------------------\n"
|
||||||
|
+ "GCat Stopped Successfully on context {}\n"
|
||||||
|
+ "-------------------------------------------------------",
|
||||||
|
context);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,22 @@
|
||||||
package org.gcube.gcat;
|
package org.gcube.gcat;
|
||||||
|
|
||||||
import javax.ws.rs.ApplicationPath;
|
import javax.ws.rs.ApplicationPath;
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.gcube.gcat.rest.Group;
|
import org.gcube.gcat.rest.Item;
|
||||||
|
import org.gcube.gcat.rest.administration.Configuration;
|
||||||
|
import org.gcube.smartgears.annotations.ManagedBy;
|
||||||
import org.glassfish.jersey.server.ResourceConfig;
|
import org.glassfish.jersey.server.ResourceConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
*/
|
*/
|
||||||
@ApplicationPath("/")
|
@ApplicationPath("/")
|
||||||
|
@ManagedBy(GCatInitializator.class)
|
||||||
public class ResourceInitializer extends ResourceConfig {
|
public class ResourceInitializer extends ResourceConfig {
|
||||||
|
|
||||||
public static final String APPLICATION_JSON_CHARSET_UTF_8 = MediaType.APPLICATION_JSON + ";charset=UTF-8";
|
|
||||||
|
|
||||||
public ResourceInitializer() {
|
public ResourceInitializer() {
|
||||||
packages(Group.class.getPackage().toString());
|
packages(Item.class.getPackage().toString());
|
||||||
|
packages(Configuration.class.getPackage().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,9 @@ import java.lang.annotation.Target;
|
||||||
|
|
||||||
import javax.ws.rs.HttpMethod;
|
import javax.ws.rs.HttpMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
@Target({ElementType.METHOD})
|
@Target({ElementType.METHOD})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@HttpMethod("PATCH")
|
@HttpMethod("PATCH")
|
||||||
|
|
|
@ -7,6 +7,9 @@ import java.lang.annotation.Target;
|
||||||
|
|
||||||
import javax.ws.rs.HttpMethod;
|
import javax.ws.rs.HttpMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
@Target({ElementType.METHOD})
|
@Target({ElementType.METHOD})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@HttpMethod("PURGE")
|
@HttpMethod("PURGE")
|
||||||
|
|
|
@ -1,131 +0,0 @@
|
||||||
package org.gcube.gcat.oldutils;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A custom field bean. It also stores index of the category and of the metadata field associated.
|
|
||||||
* These are used to sort them before pushing the content to CKAN.
|
|
||||||
* If they are missing, indexes are set to Integer.MAX_VALUE.
|
|
||||||
* @author Costantino Perciante (ISTI - CNR)
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class CustomField implements Comparable<CustomField> {
|
|
||||||
|
|
||||||
private String key;
|
|
||||||
private String qualifiedKey;
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
private int indexCategory = Integer.MAX_VALUE;
|
|
||||||
private int indexMetadataField = Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
private void init(String key, String value, int indexCategory, int indexMetadataField) {
|
|
||||||
if(key == null || value == null || key.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"A custom field must have a key and a value! Provided values are " + key + "=" + value);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.key = key;
|
|
||||||
this.qualifiedKey = key;
|
|
||||||
this.value = value;
|
|
||||||
|
|
||||||
this.indexMetadataField = indexMetadataField;
|
|
||||||
this.indexCategory = indexCategory;
|
|
||||||
|
|
||||||
if(this.indexCategory < 0) {
|
|
||||||
this.indexCategory = Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.indexMetadataField < 0) {
|
|
||||||
this.indexMetadataField = Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public CustomField(JsonNode object) {
|
|
||||||
super();
|
|
||||||
init(object.get("key").asText(), object.get("value").asText(), -1, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param key
|
|
||||||
* @param value
|
|
||||||
*/
|
|
||||||
public CustomField(String key, String value) {
|
|
||||||
super();
|
|
||||||
init(key, value, -1, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param key
|
|
||||||
* @param value
|
|
||||||
* @param indexMetadataField
|
|
||||||
* @param indexCategory
|
|
||||||
*/
|
|
||||||
public CustomField(String key, String value, int indexCategory, int indexMetadataField) {
|
|
||||||
super();
|
|
||||||
init(key, value, indexCategory, indexMetadataField);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKey(String key) {
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getQualifiedKey() {
|
|
||||||
return qualifiedKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setQualifiedKey(String qualifiedKey) {
|
|
||||||
this.qualifiedKey = qualifiedKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIndexCategory() {
|
|
||||||
return indexCategory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIndexCategory(int indexCategory) {
|
|
||||||
this.indexCategory = indexCategory;
|
|
||||||
if(this.indexCategory < 0)
|
|
||||||
this.indexCategory = Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIndexMetadataField() {
|
|
||||||
return indexMetadataField;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIndexMetadataField(int indexMetadataField) {
|
|
||||||
this.indexMetadataField = indexMetadataField;
|
|
||||||
if(this.indexMetadataField < 0) {
|
|
||||||
this.indexMetadataField = Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "CustomField [key=" + key + ", qualifiedKey=" + qualifiedKey + ", value=" + value
|
|
||||||
+ ", indexMetadataField=" + indexMetadataField + ", indexCategory=" + indexCategory + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(CustomField o) {
|
|
||||||
if(this.indexCategory == o.indexCategory) {
|
|
||||||
if(this.indexMetadataField == o.indexMetadataField) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return this.indexMetadataField > o.indexMetadataField ? 1 : -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return this.indexCategory > o.indexCategory ? 1 : -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,614 +0,0 @@
|
||||||
package org.gcube.gcat.oldutils;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.ws.rs.BadRequestException;
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
import javax.ws.rs.WebApplicationException;
|
|
||||||
import javax.ws.rs.core.Response.Status;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.math.NumberUtils;
|
|
||||||
import org.gcube.common.scope.api.ScopeProvider;
|
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.DataType;
|
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataField;
|
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataFormat;
|
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataGrouping;
|
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataTagging;
|
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.NamespaceCategory;
|
|
||||||
import org.gcube.gcat.persistence.ckan.CKANGroup;
|
|
||||||
import org.gcube.gcat.persistence.ckan.CKANPackage;
|
|
||||||
import org.gcube.gcat.persistence.ckan.CKANUser;
|
|
||||||
import org.gcube.gcat.persistence.ckan.CKANUtility;
|
|
||||||
import org.gcube.gcat.profile.MetadataUtility;
|
|
||||||
import org.gcube.gcat.social.SocialPost;
|
|
||||||
import org.geojson.GeoJsonObject;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate creation item requests utilities.
|
|
||||||
* @author Costantino Perciante (ISTI - CNR)
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class Validator {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(Validator.class);
|
|
||||||
|
|
||||||
private static final SimpleDateFormat DATE_SIMPLE = new SimpleDateFormat("yyyy-MM-dd");
|
|
||||||
private static final SimpleDateFormat DATE_HOUR_MINUTES = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
|
||||||
|
|
||||||
public static final int MAX_TAG_CHARS = 100;
|
|
||||||
|
|
||||||
protected ObjectMapper mapper;
|
|
||||||
|
|
||||||
public Validator() {
|
|
||||||
this.mapper = new ObjectMapper();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Validator(ObjectMapper mapper) {
|
|
||||||
this.mapper = mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectNode validateAgainstProfile(ObjectNode objectNode, MetadataUtility metadataUtility) throws Exception {
|
|
||||||
|
|
||||||
ArrayNode extrasArrayOriginal = (ArrayNode) objectNode.get(CKANPackage.EXTRAS_KEY);
|
|
||||||
if(extrasArrayOriginal == null || extrasArrayOriginal.size() == 0) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
"'extras' field is missing in context where metadata profile(s) are defined!");
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayNode groupsArrayOriginal = (ArrayNode) objectNode.get(CKANPackage.GROUPS_KEY);
|
|
||||||
if(groupsArrayOriginal == null) {
|
|
||||||
groupsArrayOriginal = mapper.createArrayNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayNode tagsArrayOriginal = (ArrayNode) objectNode.get(CKANPackage.TAGS_KEY);
|
|
||||||
if(tagsArrayOriginal == null) {
|
|
||||||
tagsArrayOriginal = mapper.createArrayNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the metadata profile specifying the type
|
|
||||||
CustomField metadataTypeCF = null;
|
|
||||||
List<CustomField> customFields = new ArrayList<CustomField>(extrasArrayOriginal.size());
|
|
||||||
Iterator<JsonNode> iterator = extrasArrayOriginal.iterator();
|
|
||||||
while(iterator.hasNext()) {
|
|
||||||
JsonNode object = iterator.next();
|
|
||||||
CustomField cf = new CustomField(object);
|
|
||||||
if(cf.getKey().equals(CKANPackage.EXTRAS_KEY_VALUE_SYSTEM_TYPE)) {
|
|
||||||
metadataTypeCF = cf;
|
|
||||||
} else if(cf.getKey().equals(SocialPost.ITEM_URL)) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
customFields.add(cf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(metadataTypeCF == null) {
|
|
||||||
throw new BadRequestException("'" + CKANPackage.EXTRAS_KEY_VALUE_SYSTEM_TYPE
|
|
||||||
+ "' extra field is missing in context where metadata profile(s) are defined!");
|
|
||||||
}
|
|
||||||
|
|
||||||
String profileName = metadataTypeCF.getValue();
|
|
||||||
// fetch the profile by metadata type specified above
|
|
||||||
|
|
||||||
if(metadataUtility == null) {
|
|
||||||
metadataUtility = new MetadataUtility();
|
|
||||||
}
|
|
||||||
MetadataFormat profile = metadataUtility.getMetadataFormat(profileName);
|
|
||||||
if(profile == null) {
|
|
||||||
throw new BadRequestException("'" + CKANPackage.EXTRAS_KEY_VALUE_SYSTEM_TYPE + "' extra field's value ('"
|
|
||||||
+ profileName
|
|
||||||
+ "') specified as custom field doesn't match any of the profiles defined in this context!");
|
|
||||||
} else {
|
|
||||||
ArrayNode extrasArrayUpdated = null;
|
|
||||||
List<MetadataField> metadataFields = profile.getMetadataFields();
|
|
||||||
|
|
||||||
if(metadataFields == null || metadataFields.isEmpty()) {
|
|
||||||
extrasArrayUpdated = extrasArrayOriginal;
|
|
||||||
} else {
|
|
||||||
extrasArrayUpdated = mapper.createArrayNode();
|
|
||||||
|
|
||||||
List<NamespaceCategory> categories = metadataUtility.getNamespaceCategories();
|
|
||||||
logger.debug("Retrieved namespaces are {}", categories);
|
|
||||||
List<String> categoriesIds = new ArrayList<String>(categories == null ? 0 : categories.size());
|
|
||||||
if(categories == null || categories.isEmpty()) {
|
|
||||||
logger.warn("No category defined in context {}", ScopeProvider.instance.get());
|
|
||||||
} else {
|
|
||||||
for(NamespaceCategory metadataCategory : categories) {
|
|
||||||
categoriesIds.add(metadataCategory.getId()); // save them later for matching with custom fields
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the list of already validated customFields
|
|
||||||
List<CustomField> validatedCustomFields = new ArrayList<CustomField>(customFields.size());
|
|
||||||
|
|
||||||
// keep track of mandatory fields and their cardinality
|
|
||||||
Map<String,Integer> fieldsMandatoryLowerBoundMap = new HashMap<String,Integer>(metadataFields.size());
|
|
||||||
Map<String,Integer> fieldsMandatoryUpperBoundMap = new HashMap<String,Integer>(metadataFields.size());
|
|
||||||
Map<String,Integer> numberFieldsMandatorySameKeyMap = new HashMap<String,Integer>(
|
|
||||||
metadataFields.size());
|
|
||||||
|
|
||||||
// keep track of the groups that must be created AFTER validation but BEFORE item creation
|
|
||||||
List<String> groupsToCreateAfterValidation = new ArrayList<String>();
|
|
||||||
|
|
||||||
// now validate fields
|
|
||||||
int metadataIndex = 0;
|
|
||||||
for(MetadataField metadataField : metadataFields) {
|
|
||||||
|
|
||||||
int categoryIdIndex = categoriesIds.indexOf(metadataField.getCategoryRef());
|
|
||||||
logger.debug("Found index for category " + metadataField.getCategoryRef() + " " + categoryIdIndex);
|
|
||||||
List<CustomField> validCFs = validateAgainstMetadataField(metadataIndex, categoryIdIndex,
|
|
||||||
customFields, tagsArrayOriginal, groupsArrayOriginal, metadataField, categories,
|
|
||||||
fieldsMandatoryLowerBoundMap, fieldsMandatoryUpperBoundMap, numberFieldsMandatorySameKeyMap,
|
|
||||||
groupsToCreateAfterValidation);
|
|
||||||
validatedCustomFields.addAll(validCFs);
|
|
||||||
metadataIndex++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// check mandatory fields
|
|
||||||
Iterator<Entry<String,Integer>> iteratorLowerBounds = fieldsMandatoryLowerBoundMap.entrySet()
|
|
||||||
.iterator();
|
|
||||||
while(iteratorLowerBounds.hasNext()) {
|
|
||||||
Map.Entry<java.lang.String,java.lang.Integer> entry = (Map.Entry<java.lang.String,java.lang.Integer>) iteratorLowerBounds
|
|
||||||
.next();
|
|
||||||
int lowerBound = entry.getValue();
|
|
||||||
int upperBound = fieldsMandatoryUpperBoundMap.get(entry.getKey());
|
|
||||||
int inserted = numberFieldsMandatorySameKeyMap.get(entry.getKey());
|
|
||||||
|
|
||||||
logger.info("Field with key '" + entry.getKey() + "' has been found " + inserted
|
|
||||||
+ " times and its lower bound is " + lowerBound + " and upper bound " + upperBound);
|
|
||||||
|
|
||||||
if(inserted < lowerBound || inserted > upperBound) {
|
|
||||||
throw new BadRequestException("Field with key '" + entry.getKey()
|
|
||||||
+ "' is mandatory, but it's not present among the provided fields or its cardinality is not respected ([min = "
|
|
||||||
+ lowerBound + ", max=" + upperBound + "]).");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there are no tags, throw an exception
|
|
||||||
if(tagsArrayOriginal.size() == 0) {
|
|
||||||
throw new BadRequestException("Please define at least one tag for this item!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort validated custom fields and add to the extrasArrayUpdated json array
|
|
||||||
Collections.sort(validatedCustomFields);
|
|
||||||
|
|
||||||
logger.debug("Sorted list of custom fields is " + validatedCustomFields);
|
|
||||||
|
|
||||||
// add missing fields with no match (append them at the end, since no metadataIndex or categoryIndex was defined for them)
|
|
||||||
for(CustomField cf : customFields)
|
|
||||||
validatedCustomFields.add(cf);
|
|
||||||
|
|
||||||
// convert back to json
|
|
||||||
for(CustomField customField : validatedCustomFields) {
|
|
||||||
ObjectNode jsonObj = mapper.createObjectNode();
|
|
||||||
jsonObj.put(CKANPackage.EXTRAS_KEY_KEY, customField.getQualifiedKey());
|
|
||||||
jsonObj.put(CKANPackage.EXTRAS_VALUE_KEY, customField.getValue());
|
|
||||||
extrasArrayUpdated.add(jsonObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add metadata type field as last element
|
|
||||||
ObjectNode metadataTypeJSON = mapper.createObjectNode();
|
|
||||||
metadataTypeJSON.put(CKANPackage.EXTRAS_KEY_KEY, metadataTypeCF.getKey());
|
|
||||||
metadataTypeJSON.put(CKANPackage.EXTRAS_VALUE_KEY, metadataTypeCF.getValue());
|
|
||||||
extrasArrayUpdated.add(metadataTypeJSON);
|
|
||||||
|
|
||||||
// create groups
|
|
||||||
for(String title : groupsToCreateAfterValidation) {
|
|
||||||
try {
|
|
||||||
createGroupAsSysAdmin(title);
|
|
||||||
} catch(Exception e) {
|
|
||||||
logger.trace("Failed to create group with title " + title, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
objectNode.replace(CKANPackage.TAGS_KEY, tagsArrayOriginal);
|
|
||||||
objectNode.replace(CKANPackage.GROUPS_KEY, groupsArrayOriginal);
|
|
||||||
objectNode.replace(CKANPackage.EXTRAS_KEY, extrasArrayUpdated);
|
|
||||||
|
|
||||||
return objectNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an instance of the library for the scope
|
|
||||||
*/
|
|
||||||
public void createGroupAsSysAdmin(String title) throws Exception {
|
|
||||||
String sysAdminAPI = CKANUtility.getSysAdminAPI();
|
|
||||||
CKANGroup ckanGroup = new CKANGroup();
|
|
||||||
ckanGroup.setApiKey(sysAdminAPI);
|
|
||||||
ckanGroup.setName(CKANGroup.getCKANGroupName(title));
|
|
||||||
try {
|
|
||||||
ckanGroup.read();
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
if(e.getResponse().getStatus() == Status.NOT_FOUND.getStatusCode()) {
|
|
||||||
ckanGroup.create();
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
addUserToGroupAsSysAdmin(title);
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addUserToGroupAsSysAdmin(String groupName) throws Exception {
|
|
||||||
String username = CKANUser.getCKANUsername();
|
|
||||||
addUserToGroupAsSysAdmin(groupName, username);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addUserToGroupAsSysAdmin(String groupName, String username) throws Exception {
|
|
||||||
String sysAdminAPI = CKANUtility.getSysAdminAPI();
|
|
||||||
CKANUser ckanUser = new CKANUser();
|
|
||||||
ckanUser.setApiKey(sysAdminAPI);
|
|
||||||
ckanUser.setName(username);
|
|
||||||
ckanUser.addToGroup(CKANGroup.getCKANGroupName(groupName));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate this field and generate a new value (or returns the same if there is nothing to update)
|
|
||||||
* @param metadataIndex
|
|
||||||
* @param categoryIndex
|
|
||||||
* @param customFields
|
|
||||||
* @param tagsArrayOriginal
|
|
||||||
* @param groupsArrayOriginal
|
|
||||||
* @param metadataField
|
|
||||||
* @param categories
|
|
||||||
* @param numberFieldsSameKeyMap
|
|
||||||
* @param fieldsMandatoryLowerBoundMap
|
|
||||||
* @param isApplication
|
|
||||||
* @return
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private List<CustomField> validateAgainstMetadataField(int metadataIndex, int categoryIndex,
|
|
||||||
List<CustomField> customFields, ArrayNode tagsArrayOriginal, ArrayNode groupsArrayOriginal,
|
|
||||||
MetadataField metadataField, List<NamespaceCategory> categories,
|
|
||||||
Map<String,Integer> fieldsMandatoryLowerBoundMap, Map<String,Integer> fieldsMandatoryUpperBoundMap,
|
|
||||||
Map<String,Integer> numberFieldsMandatorySameKeyMap, List<String> groupToCreate) throws Exception {
|
|
||||||
|
|
||||||
List<CustomField> toReturn = new ArrayList<CustomField>();
|
|
||||||
String metadataFieldName = metadataField.getCategoryFieldQName(); // get the qualified one, if any
|
|
||||||
int fieldsFoundWithThisKey = 0;
|
|
||||||
|
|
||||||
Iterator<CustomField> iterator = customFields.iterator();
|
|
||||||
while(iterator.hasNext()) {
|
|
||||||
CustomField cf = (CustomField) iterator.next();
|
|
||||||
if(cf.getKey().equals(metadataFieldName)) {
|
|
||||||
validate(cf, metadataField);
|
|
||||||
fieldsFoundWithThisKey++;
|
|
||||||
cf.setIndexCategory(categoryIndex);
|
|
||||||
cf.setIndexMetadataField(metadataIndex);
|
|
||||||
checkAsGroup(cf, metadataField, groupsArrayOriginal, groupToCreate);
|
|
||||||
checkAsTag(cf, metadataField, tagsArrayOriginal);
|
|
||||||
toReturn.add(cf);
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// in case of mandatory fields, keep track of the number of times they appear
|
|
||||||
if(metadataField.getMandatory()) {
|
|
||||||
// lower bound
|
|
||||||
int lowerBound = 1;
|
|
||||||
if(fieldsMandatoryLowerBoundMap.containsKey(metadataFieldName))
|
|
||||||
lowerBound = fieldsMandatoryLowerBoundMap.get(metadataFieldName) + 1;
|
|
||||||
fieldsMandatoryLowerBoundMap.put(metadataFieldName, lowerBound);
|
|
||||||
|
|
||||||
// upper bound
|
|
||||||
boolean hasVocabulary = metadataField.getVocabulary() != null;
|
|
||||||
int upperBound = hasVocabulary ? (metadataField.getVocabulary().isMultiSelection()
|
|
||||||
? metadataField.getVocabulary().getVocabularyFields().size()
|
|
||||||
: 1) : 1;
|
|
||||||
|
|
||||||
if(fieldsMandatoryUpperBoundMap.containsKey(metadataFieldName))
|
|
||||||
upperBound += fieldsMandatoryUpperBoundMap.get(metadataFieldName);
|
|
||||||
|
|
||||||
fieldsMandatoryUpperBoundMap.put(metadataFieldName, upperBound);
|
|
||||||
|
|
||||||
// fields with this same key
|
|
||||||
int countPerFields = fieldsFoundWithThisKey;
|
|
||||||
if(numberFieldsMandatorySameKeyMap.containsKey(metadataFieldName))
|
|
||||||
countPerFields += numberFieldsMandatorySameKeyMap.get(metadataFieldName);
|
|
||||||
numberFieldsMandatorySameKeyMap.put(metadataFieldName, countPerFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there was no field with this key and it was not mandatory, just add an entry of the kind {"key": "key-value", "value" : ""}.
|
|
||||||
// Sometimes it is important to view the field as empty.
|
|
||||||
if(fieldsFoundWithThisKey == 0 && !metadataField.getMandatory()) {
|
|
||||||
toReturn.add(new CustomField(metadataFieldName, "", -1, -1));
|
|
||||||
}
|
|
||||||
|
|
||||||
return toReturn;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a tag must be generated
|
|
||||||
* @param fieldToValidate
|
|
||||||
* @param metadataField
|
|
||||||
* @param tagsArrayOriginal
|
|
||||||
*/
|
|
||||||
private void checkAsTag(CustomField fieldToValidate, MetadataField metadataField, ArrayNode tagsArrayOriginal) {
|
|
||||||
MetadataTagging tagging = metadataField.getTagging();
|
|
||||||
if(tagging != null) {
|
|
||||||
|
|
||||||
String tag = "";
|
|
||||||
|
|
||||||
switch(tagging.getTaggingValue()) {
|
|
||||||
case onFieldName:
|
|
||||||
tag = metadataField.getFieldName();
|
|
||||||
break;
|
|
||||||
case onValue:
|
|
||||||
tag = fieldToValidate.getValue();
|
|
||||||
break;
|
|
||||||
case onFieldName_onValue:
|
|
||||||
tag = metadataField.getFieldName() + tagging.getSeparator() + fieldToValidate.getValue();
|
|
||||||
break;
|
|
||||||
case onValue_onFieldName:
|
|
||||||
tag = fieldToValidate.getValue() + tagging.getSeparator() + metadataField.getFieldName();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tag = tag.substring(0, MAX_TAG_CHARS > tag.length() ? tag.length() : MAX_TAG_CHARS);
|
|
||||||
logger.debug("Tag is " + tag);
|
|
||||||
|
|
||||||
ObjectNode tagJSON = mapper.createObjectNode();
|
|
||||||
tagJSON.put("name", tag);
|
|
||||||
tagJSON.put("display_name", tag);
|
|
||||||
tagsArrayOriginal.add(tagJSON);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a group must be generated
|
|
||||||
* @param fieldToValidate
|
|
||||||
* @param metadataField
|
|
||||||
* @param groupsArrayOriginal
|
|
||||||
* @param isApplication
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private void checkAsGroup(CustomField fieldToValidate, MetadataField metadataField, ArrayNode groupsArrayOriginal,
|
|
||||||
List<String> groupToCreate) throws Exception {
|
|
||||||
|
|
||||||
logger.debug("Custom field is " + fieldToValidate);
|
|
||||||
logger.debug("MetadataField field is " + metadataField);
|
|
||||||
logger.debug("JSONArray field is " + groupsArrayOriginal);
|
|
||||||
|
|
||||||
MetadataGrouping grouping = metadataField.getGrouping();
|
|
||||||
if(grouping != null) {
|
|
||||||
|
|
||||||
boolean propagateUp = grouping.getPropagateUp();
|
|
||||||
final Set<String> groupNames = new HashSet<String>();
|
|
||||||
|
|
||||||
switch(grouping.getGroupingValue()) {
|
|
||||||
case onFieldName:
|
|
||||||
groupNames.add(metadataField.getFieldName());
|
|
||||||
break;
|
|
||||||
case onValue:
|
|
||||||
if(fieldToValidate.getValue() != null && !fieldToValidate.getValue().isEmpty())
|
|
||||||
groupNames.add(fieldToValidate.getValue());
|
|
||||||
break;
|
|
||||||
case onFieldName_onValue:
|
|
||||||
case onValue_onFieldName:
|
|
||||||
groupNames.add(metadataField.getFieldName());
|
|
||||||
if(fieldToValidate.getValue() != null && !fieldToValidate.getValue().isEmpty())
|
|
||||||
groupNames.add(fieldToValidate.getValue());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(String title : groupNames) {
|
|
||||||
String groupName = CKANGroup.getCKANGroupName(title);
|
|
||||||
logger.debug("Adding group to which add this item {}", groupName);
|
|
||||||
ObjectNode group = mapper.createObjectNode();
|
|
||||||
group.put("name", groupName);
|
|
||||||
if(propagateUp) {
|
|
||||||
List<String> parents = Validator.getGroupHierarchyNames(groupName);
|
|
||||||
for(String parent : parents) {
|
|
||||||
ObjectNode groupP = mapper.createObjectNode();
|
|
||||||
groupP.put("name", parent);
|
|
||||||
groupsArrayOriginal.add(groupP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
groupsArrayOriginal.add(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
// force group creation if needed
|
|
||||||
if(grouping.getCreate()) {
|
|
||||||
for(String title : groupNames)
|
|
||||||
groupToCreate.add(title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate the single field
|
|
||||||
* @param fieldToValidate
|
|
||||||
* @param metadataField
|
|
||||||
* @param isFirst
|
|
||||||
* @return
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private void validate(CustomField fieldToValidate, MetadataField metadataField) throws Exception {
|
|
||||||
|
|
||||||
DataType dataType = metadataField.getDataType();
|
|
||||||
String regex = metadataField.getValidator() != null ? metadataField.getValidator().getRegularExpression()
|
|
||||||
: null;
|
|
||||||
boolean hasControlledVocabulary = metadataField.getVocabulary() != null;
|
|
||||||
String value = fieldToValidate.getValue();
|
|
||||||
String key = fieldToValidate.getKey();
|
|
||||||
String defaultValue = metadataField.getDefaultValue();
|
|
||||||
|
|
||||||
// replace key by prepending the qualified name of the category, if needed
|
|
||||||
fieldToValidate.setQualifiedKey(metadataField.getCategoryFieldQName());
|
|
||||||
|
|
||||||
if((value == null || value.isEmpty()))
|
|
||||||
if(metadataField.getMandatory() || hasControlledVocabulary)
|
|
||||||
throw new BadRequestException("Mandatory field with name '" + key
|
|
||||||
+ "' doesn't have a value but it is mandatory or has a controlled vocabulary!");
|
|
||||||
else {
|
|
||||||
if(defaultValue != null && !defaultValue.isEmpty()) {
|
|
||||||
value = defaultValue;
|
|
||||||
fieldToValidate.setValue(defaultValue);
|
|
||||||
}
|
|
||||||
return; // there is no need to check other stuff
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(dataType) {
|
|
||||||
|
|
||||||
case String:
|
|
||||||
case Text:
|
|
||||||
|
|
||||||
if(regex != null && !value.matches(regex))
|
|
||||||
throw new BadRequestException("Field with key '" + key
|
|
||||||
+ "' doesn't match the provided regular expression (" + regex + ")!");
|
|
||||||
|
|
||||||
if(hasControlledVocabulary) {
|
|
||||||
|
|
||||||
List<String> valuesVocabulary = metadataField.getVocabulary().getVocabularyFields();
|
|
||||||
|
|
||||||
if(valuesVocabulary == null || valuesVocabulary.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
boolean match = false;
|
|
||||||
for(String valueVocabulary : valuesVocabulary) {
|
|
||||||
match = value.equals(valueVocabulary);
|
|
||||||
if(match)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!match)
|
|
||||||
throw new BadRequestException("Field with key '" + key + "' has a value '" + value
|
|
||||||
+ "' but it doesn't match any of the vocabulary's values (" + valuesVocabulary + ")!");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Time:
|
|
||||||
|
|
||||||
if(!isValidDate(value))
|
|
||||||
throw new BadRequestException("Field with key '" + key + "' doesn't seem a valid time!");
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Time_Interval:
|
|
||||||
|
|
||||||
String[] timeValues = value.split("/");
|
|
||||||
for(int i = 0; i < timeValues.length; i++) {
|
|
||||||
String time = timeValues[i];
|
|
||||||
if(!isValidDate(time))
|
|
||||||
throw new BadRequestException(
|
|
||||||
"Field with key '" + key + "' doesn't seem a valid time interval!");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Times_ListOf:
|
|
||||||
|
|
||||||
String[] timeIntervals = value.split(",");
|
|
||||||
for(int i = 0; i < timeIntervals.length; i++) {
|
|
||||||
String[] timeIntervalValues = timeIntervals[i].split("/");
|
|
||||||
if(timeIntervalValues.length > 2)
|
|
||||||
throw new BadRequestException(
|
|
||||||
"Field with key '" + key + "' doesn't seem a valid list of times!");
|
|
||||||
for(i = 0; i < timeIntervalValues.length; i++) {
|
|
||||||
String time = timeIntervalValues[i];
|
|
||||||
if(!isValidDate(time))
|
|
||||||
throw new BadRequestException(
|
|
||||||
"Field with key '" + key + "' doesn't seem a valid list of times!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Boolean:
|
|
||||||
|
|
||||||
if(value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
|
|
||||||
|
|
||||||
} else
|
|
||||||
throw new BadRequestException("Field with key '" + key + "' doesn't seem a valid boolean value!");
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Number:
|
|
||||||
|
|
||||||
if(!NumberUtils.isNumber(value))
|
|
||||||
throw new BadRequestException("Field's value with key '" + key + "' is not a valid number!");
|
|
||||||
|
|
||||||
break;
|
|
||||||
case GeoJSON:
|
|
||||||
|
|
||||||
try {
|
|
||||||
new ObjectMapper().readValue(fieldToValidate.getValue(), GeoJsonObject.class);
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new BadRequestException("GeoJSON field with key '" + key + "' seems not valid!");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate a time date against a formatter
|
|
||||||
* @param value
|
|
||||||
* @param formatter
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static boolean isValidDate(String value) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
DATE_HOUR_MINUTES.parse(value);
|
|
||||||
return true;
|
|
||||||
} catch(Exception e) {
|
|
||||||
logger.debug("failed to parse date with hours and minutes, trying the other one");
|
|
||||||
try {
|
|
||||||
DATE_SIMPLE.parse(value);
|
|
||||||
return true;
|
|
||||||
} catch(Exception e2) {
|
|
||||||
logger.warn("failed to parse date with simple format, returning false");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the group hierarchy
|
|
||||||
* @param groupName
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public static List<String> getGroupHierarchyNames(String groupName) throws Exception {
|
|
||||||
CKANGroup ckanGroup = new CKANGroup();
|
|
||||||
ckanGroup.setName(groupName);
|
|
||||||
return ckanGroup.getGroups();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,333 +0,0 @@
|
||||||
package org.gcube.gcat.persistence.ckan;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.ws.rs.BadRequestException;
|
|
||||||
import javax.ws.rs.ForbiddenException;
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
import javax.ws.rs.NotFoundException;
|
|
||||||
import javax.ws.rs.WebApplicationException;
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
|
||||||
import javax.ws.rs.core.UriInfo;
|
|
||||||
|
|
||||||
import org.gcube.common.gxhttp.request.GXHTTPStringRequest;
|
|
||||||
import org.gcube.gcat.utils.HTTPUtility;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.node.NullNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public abstract class CKAN {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CKAN.class);
|
|
||||||
|
|
||||||
protected static final String ID_KEY = "id";
|
|
||||||
protected static final String NAME_KEY = "name";
|
|
||||||
|
|
||||||
protected static final String ERROR_KEY = "error";
|
|
||||||
protected static final String ERROR_TYPE_KEY = "__type";
|
|
||||||
protected static final String MESSAGE_KEY = "message";
|
|
||||||
protected static final String OWNER_ORG_KEY = "owner_org";
|
|
||||||
|
|
||||||
protected static final String RESULT_KEY = "result";
|
|
||||||
protected static final String SUCCESS_KEY = "success";
|
|
||||||
|
|
||||||
public static final String LIMIT_KEY = "limit";
|
|
||||||
public static final String OFFSET_KEY = "offset";
|
|
||||||
|
|
||||||
protected static final String NOT_FOUND_ERROR = "Not Found Error";
|
|
||||||
protected static final String AUTHORIZATION_ERROR = "Authorization Error";
|
|
||||||
protected static final String VALIDATION_ERROR = "Validation Error";
|
|
||||||
|
|
||||||
// api rest path CKAN
|
|
||||||
public final static String CKAN_API_PATH = "/api/3/action/";
|
|
||||||
|
|
||||||
// ckan header authorization
|
|
||||||
public final static String AUTH_CKAN_HEADER = HttpHeaders.AUTHORIZATION;
|
|
||||||
|
|
||||||
public final static String NAME_REGEX = "^[a-z0-9_\\\\-]{2,100}$";
|
|
||||||
|
|
||||||
protected String LIST;
|
|
||||||
protected String CREATE;
|
|
||||||
protected String READ;
|
|
||||||
protected String UPDATE;
|
|
||||||
protected String PATCH;
|
|
||||||
protected String DELETE;
|
|
||||||
protected String PURGE;
|
|
||||||
|
|
||||||
protected final ObjectMapper mapper;
|
|
||||||
|
|
||||||
protected String name;
|
|
||||||
protected String apiKey;
|
|
||||||
|
|
||||||
protected JsonNode result;
|
|
||||||
|
|
||||||
protected String nameRegex;
|
|
||||||
|
|
||||||
protected UriInfo uriInfo;
|
|
||||||
|
|
||||||
public void setUriInfo(UriInfo uriInfo) {
|
|
||||||
this.uriInfo = uriInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getApiKey() {
|
|
||||||
if(apiKey == null) {
|
|
||||||
try {
|
|
||||||
return CKANUtility.getApiKey();
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return apiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setApiKey(String apiKey) {
|
|
||||||
this.apiKey = apiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectMapper getMapper() {
|
|
||||||
return mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonNode getJsonNodeResult() {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected CKAN() {
|
|
||||||
try {
|
|
||||||
this.mapper = new ObjectMapper();
|
|
||||||
this.nameRegex = CKAN.NAME_REGEX;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected JsonNode getAsJsonNode(String json) {
|
|
||||||
try {
|
|
||||||
return mapper.readTree(json);
|
|
||||||
} catch(IOException e) {
|
|
||||||
throw new BadRequestException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate the CKAN response and return the
|
|
||||||
* @param json
|
|
||||||
* @return the validated response as JsonNode
|
|
||||||
*/
|
|
||||||
protected JsonNode validateCKANResponse(String json) {
|
|
||||||
JsonNode jsonNode = getAsJsonNode(json);
|
|
||||||
if(jsonNode.get(SUCCESS_KEY).asBoolean()) {
|
|
||||||
return jsonNode.get(RESULT_KEY);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
JsonNode error = jsonNode.get(ERROR_KEY);
|
|
||||||
|
|
||||||
String errorType = error.get(ERROR_TYPE_KEY).asText();
|
|
||||||
|
|
||||||
if(errorType.compareTo(VALIDATION_ERROR) == 0) {
|
|
||||||
throw new BadRequestException(getAsString(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
String message = error.get(MESSAGE_KEY).asText();
|
|
||||||
|
|
||||||
if(errorType.compareTo(NOT_FOUND_ERROR) == 0) {
|
|
||||||
throw new NotFoundException(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(errorType.compareTo(AUTHORIZATION_ERROR) == 0) {
|
|
||||||
throw new ForbiddenException(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO parse more cases
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new BadRequestException(json);
|
|
||||||
}
|
|
||||||
throw new BadRequestException(json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getAsString(JsonNode node) {
|
|
||||||
try {
|
|
||||||
String json = mapper.writeValueAsString(node);
|
|
||||||
return json;
|
|
||||||
} catch(JsonProcessingException e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected JsonNode checkName(JsonNode jsonNode) {
|
|
||||||
try {
|
|
||||||
String gotName = jsonNode.get(NAME_KEY).asText();
|
|
||||||
if(!gotName.matches(nameRegex)) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
"The 'name' must be between 2 and 100 characters long and contain only lowercase alphanumeric characters, '-' and '_'. You can validate your name using the regular expression : "
|
|
||||||
+ NAME_REGEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(name == null) {
|
|
||||||
name = gotName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(gotName != null && gotName.compareTo(name) != 0) {
|
|
||||||
String error = String.format(
|
|
||||||
"The name (%s) does not match with the '%s' contained in the provided content (%s).", name,
|
|
||||||
NAME_KEY, gotName);
|
|
||||||
throw new BadRequestException(error);
|
|
||||||
}
|
|
||||||
return jsonNode;
|
|
||||||
} catch(BadRequestException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new BadRequestException("Unable to obtain a correct 'name' from the provided content");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected JsonNode checkName(String json) {
|
|
||||||
JsonNode jsonNode = getAsJsonNode(json);
|
|
||||||
checkName(jsonNode);
|
|
||||||
return jsonNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected JsonNode createJsonNodeWithID(String id) {
|
|
||||||
ObjectNode objectNode = mapper.createObjectNode();
|
|
||||||
objectNode.put(ID_KEY, id);
|
|
||||||
return objectNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected JsonNode createJsonNodeWithNameAsID() {
|
|
||||||
return createJsonNodeWithID(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Map<String,String> getMapWithNameAsID() {
|
|
||||||
return getMapWithID(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Map<String,String> getMapWithID(String id) {
|
|
||||||
Map<String,String> map = new HashMap<>();
|
|
||||||
map.put(ID_KEY, id);
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected GXHTTPStringRequest getGXHTTPStringRequest(String path, boolean post)
|
|
||||||
throws UnsupportedEncodingException {
|
|
||||||
String catalogueURL = CKANInstance.getInstance().getCKANURL();
|
|
||||||
|
|
||||||
GXHTTPStringRequest gxhttpStringRequest = HTTPUtility.createGXHTTPStringRequest(catalogueURL, path, post);
|
|
||||||
gxhttpStringRequest.isExternalCall(true);
|
|
||||||
gxhttpStringRequest.header(AUTH_CKAN_HEADER, getApiKey());
|
|
||||||
|
|
||||||
return gxhttpStringRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getResultAndValidate(HttpURLConnection httpURLConnection) throws IOException {
|
|
||||||
String ret = HTTPUtility.getResultAsString(httpURLConnection);
|
|
||||||
logger.trace("Got Respose is {}", ret);
|
|
||||||
result = validateCKANResponse(ret);
|
|
||||||
if(result instanceof NullNode) {
|
|
||||||
result = mapper.createObjectNode();
|
|
||||||
}
|
|
||||||
return getAsString(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String sendGetRequest(String path, Map<String,String> parameters) {
|
|
||||||
try {
|
|
||||||
logger.debug("Going to send GET request with parameters {}", parameters);
|
|
||||||
GXHTTPStringRequest gxhttpStringRequest = getGXHTTPStringRequest(path, false);
|
|
||||||
gxhttpStringRequest.queryParams(parameters);
|
|
||||||
HttpURLConnection httpURLConnection = gxhttpStringRequest.get();
|
|
||||||
return getResultAndValidate(httpURLConnection);
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String sendPostRequest(String path, String body) {
|
|
||||||
try {
|
|
||||||
logger.debug("Going to send POST request with body {}", body);
|
|
||||||
GXHTTPStringRequest gxhttpStringRequest = getGXHTTPStringRequest(path, true);
|
|
||||||
HttpURLConnection httpURLConnection = gxhttpStringRequest.post(body);
|
|
||||||
return getResultAndValidate(httpURLConnection);
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String sendPostRequest(String path, JsonNode jsonNode) {
|
|
||||||
return sendPostRequest(path, getAsString(jsonNode));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String list(int limit, int offset) {
|
|
||||||
Map<String,String> parameters = new HashMap<>();
|
|
||||||
if(limit > 0) {
|
|
||||||
parameters.put(LIMIT_KEY, String.valueOf(limit));
|
|
||||||
}
|
|
||||||
if(offset >= 0) {
|
|
||||||
parameters.put(OFFSET_KEY, String.valueOf(offset));
|
|
||||||
}
|
|
||||||
return sendGetRequest(LIST, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String create(String json) {
|
|
||||||
return sendPostRequest(CREATE, json);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String read() {
|
|
||||||
return sendGetRequest(READ, getMapWithNameAsID());
|
|
||||||
}
|
|
||||||
|
|
||||||
public String update(String json) {
|
|
||||||
checkName(json);
|
|
||||||
return sendPostRequest(UPDATE, json);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String patch(String json) {
|
|
||||||
JsonNode jsonNode = checkName(json);
|
|
||||||
ObjectNode objectNode = ((ObjectNode) jsonNode);
|
|
||||||
objectNode.put(ID_KEY, name);
|
|
||||||
objectNode.remove(NAME_KEY);
|
|
||||||
return sendPostRequest(PATCH, objectNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void delete() {
|
|
||||||
sendPostRequest(DELETE, createJsonNodeWithNameAsID());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete(boolean purge) {
|
|
||||||
if(purge) {
|
|
||||||
purge();
|
|
||||||
} else {
|
|
||||||
delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void purge() {
|
|
||||||
sendPostRequest(PURGE, createJsonNodeWithNameAsID());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
package org.gcube.gcat.persistence.ckan;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
import javax.ws.rs.WebApplicationException;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class CKANGroup extends CKAN {
|
|
||||||
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.group_list
|
|
||||||
public static final String GROUP_LIST = CKAN.CKAN_API_PATH + "group_list";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.create.group_create
|
|
||||||
public static final String GROUP_CREATE = CKAN.CKAN_API_PATH + "group_create";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.group_show
|
|
||||||
public static final String GROUP_SHOW = CKAN.CKAN_API_PATH + "group_show";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.update.group_update
|
|
||||||
public static final String GROUP_UPDATE = CKAN.CKAN_API_PATH + "group_update";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.patch.group_patch
|
|
||||||
public static final String GROUP_PATCH = CKAN.CKAN_API_PATH + "group_patch";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.delete.group_delete
|
|
||||||
public static final String GROUP_DELETE = CKAN.CKAN_API_PATH + "group_delete";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.delete.group_purge
|
|
||||||
public static final String GROUP_PURGE = CKAN.CKAN_API_PATH + "group_purge";
|
|
||||||
|
|
||||||
public static final String GROUPS_KEY = "groups";
|
|
||||||
|
|
||||||
public CKANGroup() {
|
|
||||||
super();
|
|
||||||
LIST = GROUP_LIST;
|
|
||||||
CREATE = GROUP_CREATE;
|
|
||||||
READ = GROUP_SHOW;
|
|
||||||
UPDATE = GROUP_UPDATE;
|
|
||||||
PATCH = GROUP_PATCH;
|
|
||||||
DELETE = GROUP_DELETE;
|
|
||||||
PURGE = GROUP_PURGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String fromGroupTitleToName(String groupName) {
|
|
||||||
if(groupName == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
String regexGroupNameTransform = "[^A-Za-z0-9-]";
|
|
||||||
String modified = groupName.trim().replaceAll(regexGroupNameTransform, "-").replaceAll("-+", "-").toLowerCase();
|
|
||||||
|
|
||||||
if(modified.startsWith("-"))
|
|
||||||
modified = modified.substring(1);
|
|
||||||
if(modified.endsWith("-"))
|
|
||||||
modified = modified.substring(0, modified.length() - 1);
|
|
||||||
|
|
||||||
return modified;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getCKANGroupName(String name) {
|
|
||||||
return CKANGroup.fromGroupTitleToName(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String create() throws WebApplicationException {
|
|
||||||
try {
|
|
||||||
ObjectNode objectNode = mapper.createObjectNode();
|
|
||||||
objectNode.put(NAME_KEY, CKANGroup.getCKANGroupName(name));
|
|
||||||
objectNode.put("title", name);
|
|
||||||
objectNode.put("display_name", name);
|
|
||||||
objectNode.put("description", "");
|
|
||||||
return super.create(mapper.writeValueAsString(objectNode));
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getGroups() {
|
|
||||||
if(result == null) {
|
|
||||||
read();
|
|
||||||
}
|
|
||||||
List<String> groups = new ArrayList<String>();
|
|
||||||
if(result.has(GROUPS_KEY)) {
|
|
||||||
JsonNode jsonNode = result.get(GROUPS_KEY);
|
|
||||||
if(jsonNode.isArray()) {
|
|
||||||
ArrayNode arrayNode = (ArrayNode) jsonNode;
|
|
||||||
if(arrayNode.size() > 0) {
|
|
||||||
Iterator<JsonNode> iterator = arrayNode.iterator();
|
|
||||||
while(iterator.hasNext()) {
|
|
||||||
groups.add(iterator.next().asText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,186 +0,0 @@
|
||||||
package org.gcube.gcat.persistence.ckan;
|
|
||||||
|
|
||||||
import static org.gcube.resources.discovery.icclient.ICFactory.clientFor;
|
|
||||||
import static org.gcube.resources.discovery.icclient.ICFactory.queryFor;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
import javax.ws.rs.WebApplicationException;
|
|
||||||
|
|
||||||
import org.gcube.common.encryption.encrypter.StringEncrypter;
|
|
||||||
import org.gcube.common.resources.gcore.ServiceEndpoint;
|
|
||||||
import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint;
|
|
||||||
import org.gcube.common.resources.gcore.ServiceEndpoint.Property;
|
|
||||||
import org.gcube.gcat.utils.ContextUtility;
|
|
||||||
import org.gcube.resources.discovery.client.api.DiscoveryClient;
|
|
||||||
import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class CKANInstance {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CKANInstance.class);
|
|
||||||
|
|
||||||
// CKAN Instance info
|
|
||||||
private final static String RUNTIME_CATALOGUE_RESOURCE_NAME = "CKanDataCatalogue";
|
|
||||||
private final static String PLATFORM_CATALOGUE_NAME = "Tomcat";
|
|
||||||
|
|
||||||
// property to retrieve the master service endpoint into the /root scope
|
|
||||||
private final static String IS_MASTER_ROOT_KEY_PROPERTY = "IS_ROOT_MASTER"; // true, false.. missing means false as well
|
|
||||||
|
|
||||||
private final static String API_KEY_PROPERTY = "API_KEY";
|
|
||||||
private final static String SOCIAL_POST = "SOCIAL_POST";
|
|
||||||
private final static String ALERT_USERS_ON_POST_CREATION = "ALERT_USERS_ON_POST_CREATION";
|
|
||||||
private final static String URL_RESOLVER = "URL_RESOLVER";
|
|
||||||
|
|
||||||
private static final Map<String,CKANInstance> ckanInstancePerScope;
|
|
||||||
|
|
||||||
protected String ckanURL;
|
|
||||||
protected String sysAdminToken;
|
|
||||||
protected boolean socialPostEnabled;
|
|
||||||
protected boolean notificationToUsersEnabled;
|
|
||||||
protected String uriResolverURL;
|
|
||||||
|
|
||||||
static {
|
|
||||||
ckanInstancePerScope = new HashMap<String,CKANInstance>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CKANInstance getInstance() {
|
|
||||||
CKANInstance ckanInstance = ckanInstancePerScope.get(ContextUtility.getCurrentContext());
|
|
||||||
if(ckanInstance == null) {
|
|
||||||
ckanInstance = new CKANInstance();
|
|
||||||
ckanInstance.getConfigurationFromIS();
|
|
||||||
ckanInstancePerScope.put(ContextUtility.getCurrentContext(), ckanInstance);
|
|
||||||
}
|
|
||||||
return ckanInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve endpoints information from IS for DataCatalogue URL
|
|
||||||
* @return list of endpoints for ckan data catalogue
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private static List<ServiceEndpoint> getServiceEndpoints() {
|
|
||||||
SimpleQuery query = queryFor(ServiceEndpoint.class);
|
|
||||||
query.addCondition("$resource/Profile/Name/text() eq '" + RUNTIME_CATALOGUE_RESOURCE_NAME + "'");
|
|
||||||
query.addCondition("$resource/Profile/Platform/Name/text() eq '" + PLATFORM_CATALOGUE_NAME + "'");
|
|
||||||
DiscoveryClient<ServiceEndpoint> client = clientFor(ServiceEndpoint.class);
|
|
||||||
List<ServiceEndpoint> serviceEndpoints = client.submit(query);
|
|
||||||
if(serviceEndpoints.size() == 0) {
|
|
||||||
logger.error("There is no {} having name {} and Platform {} in this context.",
|
|
||||||
ServiceEndpoint.class.getSimpleName(), RUNTIME_CATALOGUE_RESOURCE_NAME, PLATFORM_CATALOGUE_NAME);
|
|
||||||
throw new InternalServerErrorException("No CKAN configuration on IS");
|
|
||||||
}
|
|
||||||
return serviceEndpoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getConfigurationFromIS() {
|
|
||||||
try {
|
|
||||||
List<ServiceEndpoint> serviceEndpoints = getServiceEndpoints();
|
|
||||||
ServiceEndpoint serviceEndpoint = null;
|
|
||||||
|
|
||||||
if(serviceEndpoints.size() > 1) {
|
|
||||||
logger.info("Too many {} having name {} in this context. Looking for the one that has the property {}",
|
|
||||||
ServiceEndpoint.class.getSimpleName(), RUNTIME_CATALOGUE_RESOURCE_NAME,
|
|
||||||
IS_MASTER_ROOT_KEY_PROPERTY);
|
|
||||||
|
|
||||||
for(ServiceEndpoint se : serviceEndpoints) {
|
|
||||||
Iterator<AccessPoint> accessPointIterator = se.profile().accessPoints().iterator();
|
|
||||||
while(accessPointIterator.hasNext()) {
|
|
||||||
ServiceEndpoint.AccessPoint accessPoint = accessPointIterator.next();
|
|
||||||
|
|
||||||
// get the is master property
|
|
||||||
Property entry = accessPoint.propertyMap().get(IS_MASTER_ROOT_KEY_PROPERTY);
|
|
||||||
String isMaster = entry != null ? entry.value() : null;
|
|
||||||
|
|
||||||
if(isMaster == null || !isMaster.equals("true")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set this variable
|
|
||||||
serviceEndpoint = se;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if none of them was master, throw an exception
|
|
||||||
if(serviceEndpoint == null) {
|
|
||||||
throw new InternalServerErrorException(
|
|
||||||
"Too many CKAN configuration on IS and no one with MASTER property");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
serviceEndpoint = serviceEndpoints.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterator<AccessPoint> accessPointIterator = serviceEndpoint.profile().accessPoints().iterator();
|
|
||||||
while(accessPointIterator.hasNext()) {
|
|
||||||
AccessPoint accessPoint = accessPointIterator.next();
|
|
||||||
|
|
||||||
// add this host
|
|
||||||
ckanURL = accessPoint.address();
|
|
||||||
|
|
||||||
// retrieve sys admin token
|
|
||||||
sysAdminToken = accessPoint.propertyMap().get(API_KEY_PROPERTY).value();
|
|
||||||
sysAdminToken = StringEncrypter.getEncrypter().decrypt(sysAdminToken);
|
|
||||||
|
|
||||||
// retrieve option to check if the social post has to be made
|
|
||||||
socialPostEnabled = true; // default is true
|
|
||||||
if(accessPoint.propertyMap().containsKey(SOCIAL_POST)) {
|
|
||||||
if(accessPoint.propertyMap().get(SOCIAL_POST).value().trim().equalsIgnoreCase("false")) {
|
|
||||||
socialPostEnabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrieve option for user alert
|
|
||||||
notificationToUsersEnabled = false;
|
|
||||||
if(accessPoint.propertyMap().containsKey(ALERT_USERS_ON_POST_CREATION)) {
|
|
||||||
if(accessPoint.propertyMap().get(ALERT_USERS_ON_POST_CREATION).value().trim()
|
|
||||||
.equalsIgnoreCase("true")) {
|
|
||||||
notificationToUsersEnabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrieve URL_RESOLVER
|
|
||||||
if(accessPoint.propertyMap().containsKey(URL_RESOLVER)) {
|
|
||||||
uriResolverURL = accessPoint.propertyMap().get(URL_RESOLVER).value();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException("Error while getting configuration on IS", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUriResolverURL() throws Exception {
|
|
||||||
return uriResolverURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCKANURL() {
|
|
||||||
return ckanURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSocialPostEnabled() throws Exception {
|
|
||||||
return socialPostEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isNotificationToUsersEnabled() throws Exception {
|
|
||||||
return notificationToUsersEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSysAdminToken() throws Exception {
|
|
||||||
return sysAdminToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package org.gcube.gcat.persistence.ckan;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class CKANLicense extends CKAN {
|
|
||||||
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.license_list
|
|
||||||
public static final String LICENSES_LIST = CKAN.CKAN_API_PATH + "license_list";
|
|
||||||
|
|
||||||
public CKANLicense() {
|
|
||||||
super();
|
|
||||||
LIST = LICENSES_LIST;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static ArrayNode getLicenses() {
|
|
||||||
CKANLicense ckanLicense = new CKANLicense();
|
|
||||||
ckanLicense.list(-1, -1);
|
|
||||||
ArrayNode arrayNode = (ArrayNode) ckanLicense.getJsonNodeResult();
|
|
||||||
return arrayNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean checkLicenseId(String licenseId) throws Exception {
|
|
||||||
return checkLicenseId(getLicenses(), licenseId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Use a Cache
|
|
||||||
protected static boolean checkLicenseId(ArrayNode arrayNode, String licenseId) throws Exception {
|
|
||||||
try {
|
|
||||||
for(JsonNode jsonNode : arrayNode) {
|
|
||||||
try {
|
|
||||||
String id = jsonNode.get(ID_KEY).asText();
|
|
||||||
if(id.compareTo(licenseId) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
package org.gcube.gcat.persistence.ckan;
|
|
||||||
|
|
||||||
import org.gcube.common.scope.impl.ScopeBean;
|
|
||||||
import org.gcube.gcat.utils.ContextUtility;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class CKANOrganization extends CKAN {
|
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(CKANOrganization.class);
|
|
||||||
|
|
||||||
// CKAN Connector sanitize the Organization name as following
|
|
||||||
//organizationName.replaceAll(" ", "_").replace(".", "_").toLowerCase()
|
|
||||||
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.organization_list
|
|
||||||
public static final String ORGANIZATION_LIST = CKAN.CKAN_API_PATH + "organization_list";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.create.organization_create
|
|
||||||
public static final String ORGANIZATION_CREATE = CKAN.CKAN_API_PATH + "organization_create";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.organization_show
|
|
||||||
public static final String ORGANIZATION_SHOW = CKAN.CKAN_API_PATH + "organization_show";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.update.organization_update
|
|
||||||
public static final String ORGANIZATION_UPDATE = CKAN.CKAN_API_PATH + "organization_update";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.patch.organization_patch
|
|
||||||
public static final String ORGANIZATION_PATCH = CKAN.CKAN_API_PATH + "organization_patch";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.delete.organization_delete
|
|
||||||
public static final String ORGANIZATION_DELETE = CKAN.CKAN_API_PATH + "organization_delete";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.delete.organization_purge
|
|
||||||
public static final String ORGANIZATION_PURGE = CKAN.CKAN_API_PATH + "organization_purge";
|
|
||||||
|
|
||||||
// see https://docs.ckan.org/en/latest/api/#ckan.logic.action.create.organization_member_create
|
|
||||||
public static final String ORGANIZATION_MEMBER_CREATE = CKAN.CKAN_API_PATH + "organization_member_create";
|
|
||||||
|
|
||||||
// https://docs.ckan.org/en/latest/api/index.html#ckan.logic.action.get.organization_list_for_user
|
|
||||||
public static final String ORGANIZATION_LIST_FOR_USER = CKAN.CKAN_API_PATH + "organization_list_for_user";
|
|
||||||
|
|
||||||
protected static final String USERNAME_KEY = "username";
|
|
||||||
protected static final String ROLE_KEY = "role";
|
|
||||||
|
|
||||||
public CKANOrganization() {
|
|
||||||
super();
|
|
||||||
LIST = ORGANIZATION_LIST;
|
|
||||||
CREATE = ORGANIZATION_CREATE;
|
|
||||||
READ = ORGANIZATION_SHOW;
|
|
||||||
UPDATE = ORGANIZATION_UPDATE;
|
|
||||||
PATCH = ORGANIZATION_PATCH;
|
|
||||||
DELETE = ORGANIZATION_DELETE;
|
|
||||||
PURGE = ORGANIZATION_PURGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static final String ORGANIZATION_PERMISSION_KEY = "permission";
|
|
||||||
protected static final String ORGANIZATION_PERMISSION_VALUE_READ = "read";
|
|
||||||
|
|
||||||
public void addUserToOrganisation(String gCubeUsername, String role) {
|
|
||||||
String ckanUsername = CKANUser.getCKANUsername(gCubeUsername);
|
|
||||||
|
|
||||||
ObjectNode objectNode = mapper.createObjectNode();
|
|
||||||
objectNode.put(ID_KEY, name);
|
|
||||||
objectNode.put(USERNAME_KEY, ckanUsername);
|
|
||||||
objectNode.put(ROLE_KEY, role);
|
|
||||||
sendPostRequest(ORGANIZATION_MEMBER_CREATE, getAsString(objectNode));
|
|
||||||
logger.debug("User {} successfully added to Organisation {} with role {}", ckanUsername, name, role);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getCKANOrganizationName() {
|
|
||||||
String context = ContextUtility.getCurrentContext();
|
|
||||||
return getCKANOrganizationName(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getCKANOrganizationName(String context) {
|
|
||||||
ScopeBean scopeBean = new ScopeBean(context);
|
|
||||||
return scopeBean.name().toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,620 +0,0 @@
|
||||||
package org.gcube.gcat.persistence.ckan;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import javax.ws.rs.BadRequestException;
|
|
||||||
import javax.ws.rs.DELETE;
|
|
||||||
import javax.ws.rs.GET;
|
|
||||||
import javax.ws.rs.HEAD;
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
import javax.ws.rs.NotAllowedException;
|
|
||||||
import javax.ws.rs.OPTIONS;
|
|
||||||
import javax.ws.rs.PUT;
|
|
||||||
import javax.ws.rs.WebApplicationException;
|
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
|
||||||
import javax.ws.rs.core.Response.Status;
|
|
||||||
|
|
||||||
import org.gcube.common.scope.impl.ScopeBean;
|
|
||||||
import org.gcube.common.scope.impl.ScopeBean.Type;
|
|
||||||
import org.gcube.gcat.annotation.PURGE;
|
|
||||||
import org.gcube.gcat.api.GCatConstants;
|
|
||||||
import org.gcube.gcat.oldutils.Validator;
|
|
||||||
import org.gcube.gcat.profile.MetadataUtility;
|
|
||||||
import org.gcube.gcat.social.SocialPost;
|
|
||||||
import org.gcube.gcat.utils.ContextUtility;
|
|
||||||
import org.gcube.gcat.utils.URIResolver;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class CKANPackage extends CKAN {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CKANPackage.class);
|
|
||||||
/*
|
|
||||||
// see https://docs.ckan.org/en/latest/api/#ckan.logic.action.get.package_list
|
|
||||||
public static final String ITEM_LIST = CKAN.CKAN_API_PATH + "package_list";
|
|
||||||
*/
|
|
||||||
// see https://docs.ckan.org/en/latest/api/index.html#ckan.logic.action.get.package_search
|
|
||||||
public static final String ITEM_LIST = CKAN.CKAN_API_PATH + "package_search";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.create.package_create
|
|
||||||
public static final String ITEM_CREATE = CKAN.CKAN_API_PATH + "package_create";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.package_show
|
|
||||||
public static final String ITEM_SHOW = CKAN.CKAN_API_PATH + "package_show";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.update.package_update
|
|
||||||
public static final String ITEM_UPDATE = CKAN.CKAN_API_PATH + "package_update";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.patch.package_patch
|
|
||||||
public static final String ITEM_PATCH = CKAN.CKAN_API_PATH + "package_patch";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.delete.package_delete
|
|
||||||
public static final String ITEM_DELETE = CKAN.CKAN_API_PATH + "package_delete";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.delete.dataset_purge
|
|
||||||
public static final String ITEM_PURGE = CKAN.CKAN_API_PATH + "dataset_purge";
|
|
||||||
|
|
||||||
// limit in https://docs.ckan.org/en/latest/api/index.html#ckan.logic.action.get.package_search
|
|
||||||
protected static final String ROWS_KEY = "rows";
|
|
||||||
// offset in https://docs.ckan.org/en/latest/api/index.html#ckan.logic.action.get.package_search
|
|
||||||
protected static final String START_KEY = "start";
|
|
||||||
|
|
||||||
protected static final String ORGANIZATION_FILTER_TEMPLATE = GCatConstants.ORGANIZATION_PARAMETER + ":%s";
|
|
||||||
|
|
||||||
protected static final String ORGANIZATION_REGEX = GCatConstants.ORGANIZATION_PARAMETER + ":[a-zA-Z_\"]*";
|
|
||||||
|
|
||||||
protected static final Pattern ORGANIZATION_REGEX_PATTERN;
|
|
||||||
|
|
||||||
static {
|
|
||||||
ORGANIZATION_REGEX_PATTERN = Pattern.compile(ORGANIZATION_REGEX);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static final String LICENSE_KEY = "license_id";
|
|
||||||
|
|
||||||
protected static final String EXTRAS_ITEM_URL_KEY = "Item URL";
|
|
||||||
|
|
||||||
protected static final String AUTHOR_KEY = "author";
|
|
||||||
protected static final String AUTHOR_EMAIL_KEY = "author_email";
|
|
||||||
protected static final String OWNER_ORG_KEY = "owner_org";
|
|
||||||
protected static final String RESOURCES_KEY = "resources";
|
|
||||||
protected static final String TITLE_KEY = "title";
|
|
||||||
|
|
||||||
public static final String EXTRAS_KEY = "extras";
|
|
||||||
public static final String EXTRAS_KEY_KEY = "key";
|
|
||||||
public static final String EXTRAS_KEY_VALUE_SYSTEM_TYPE = "system:type";
|
|
||||||
public static final String EXTRAS_VALUE_KEY = "value";
|
|
||||||
|
|
||||||
// The 'results' array is included in the 'result' object for package_search
|
|
||||||
private static final String RESULTS_KEY = "results";
|
|
||||||
|
|
||||||
protected static final String PRIVATE_KEY = "private";
|
|
||||||
protected static final String SEARCHABLE_KEY = "searchable";
|
|
||||||
protected static final String CAPACITY_KEY = "capacity";
|
|
||||||
|
|
||||||
// protected static final String INCLUDE_PRIVATE_KEY = "include_private";
|
|
||||||
// protected static final String INCLUDE_DRAFTS_KEY = "include_drafts";
|
|
||||||
|
|
||||||
public static final String GROUPS_KEY = "groups";
|
|
||||||
public static final String TAGS_KEY = "tags";
|
|
||||||
|
|
||||||
protected final List<CKANResource> managedResources;
|
|
||||||
|
|
||||||
protected String itemID;
|
|
||||||
|
|
||||||
public CKANPackage() {
|
|
||||||
super();
|
|
||||||
LIST = ITEM_LIST;
|
|
||||||
CREATE = ITEM_CREATE;
|
|
||||||
READ = ITEM_SHOW;
|
|
||||||
UPDATE = ITEM_UPDATE;
|
|
||||||
PATCH = ITEM_PATCH;
|
|
||||||
DELETE = ITEM_DELETE;
|
|
||||||
PURGE = ITEM_PURGE;
|
|
||||||
managedResources = new ArrayList<CKANResource>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the CKAN organization name using the current context name
|
|
||||||
*/
|
|
||||||
protected String getOrganizationName(ScopeBean scopeBean) {
|
|
||||||
String contextName = scopeBean.name();
|
|
||||||
return contextName.toLowerCase().replace(" ", "_");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getOrganizationName() {
|
|
||||||
ScopeBean scopeBean = new ScopeBean(ContextUtility.getCurrentContext());
|
|
||||||
return getOrganizationName(scopeBean);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectNode checkBaseInformation(String json) throws Exception {
|
|
||||||
ObjectNode objectNode = (ObjectNode) mapper.readTree(json);
|
|
||||||
|
|
||||||
objectNode = (ObjectNode) checkName(objectNode);
|
|
||||||
|
|
||||||
// We need to enforce the itemID to properly manage resource persistence
|
|
||||||
if(objectNode.has(ID_KEY)) {
|
|
||||||
itemID = objectNode.get(ID_KEY).asText();
|
|
||||||
}
|
|
||||||
|
|
||||||
// To include private item in search result (e.g. listing) a private package must be searchable
|
|
||||||
// The package it is jsut included in the search and listing results but remain private and cannot be accessed
|
|
||||||
// if not authorized
|
|
||||||
if(objectNode.has(PRIVATE_KEY)) {
|
|
||||||
boolean privatePackage = objectNode.get(PRIVATE_KEY).asBoolean();
|
|
||||||
if(privatePackage) {
|
|
||||||
objectNode.put(SEARCHABLE_KEY, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check license
|
|
||||||
String licenseId = null;
|
|
||||||
if(objectNode.has(LICENSE_KEY)) {
|
|
||||||
licenseId = objectNode.get(LICENSE_KEY).asText();
|
|
||||||
}
|
|
||||||
if(licenseId == null || licenseId.isEmpty()) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
"You must specify a license identifier for the item. License list can be retrieved using licence collection");
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
CKANLicense.checkLicenseId(licenseId);
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
"You must specify an existing license identifier for the item. License list can be retrieved using licence collection");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(objectNode.has(CAPACITY_KEY)) {
|
|
||||||
/*
|
|
||||||
* When a client provides the 'capacity' field as 'private', the item is not counted in the
|
|
||||||
* total number of items in the GUI. We want to avoid such a behavior
|
|
||||||
* See https://support.d4science.org/issues/16410
|
|
||||||
*/
|
|
||||||
objectNode.remove(CAPACITY_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
CKANUser ckanUser = CKANUserCache.getCurrrentCKANUser();
|
|
||||||
|
|
||||||
objectNode.put(AUTHOR_KEY, ckanUser.getName());
|
|
||||||
objectNode.put(AUTHOR_EMAIL_KEY, ckanUser.getPortalUser().getEMail());
|
|
||||||
|
|
||||||
// owner organization must be specified if the token belongs to a VRE
|
|
||||||
ScopeBean scopeBean = new ScopeBean(ContextUtility.getCurrentContext());
|
|
||||||
|
|
||||||
String gotOrganization = null;
|
|
||||||
if(objectNode.has(OWNER_ORG_KEY)) {
|
|
||||||
gotOrganization = objectNode.get(OWNER_ORG_KEY).asText();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(scopeBean.is(Type.VRE)) {
|
|
||||||
String organizationFromContext = getOrganizationName(scopeBean);
|
|
||||||
if(gotOrganization != null) {
|
|
||||||
if(gotOrganization.compareTo(organizationFromContext) != 0) {
|
|
||||||
CKANOrganization ckanOrganization = new CKANOrganization();
|
|
||||||
ckanOrganization.setName(organizationFromContext);
|
|
||||||
ckanOrganization.read();
|
|
||||||
String organizationID = null;
|
|
||||||
if(ckanOrganization.result.has(ID_KEY)) {
|
|
||||||
organizationID = ckanOrganization.result.get(ID_KEY).asText();
|
|
||||||
}
|
|
||||||
if(organizationID == null || gotOrganization.compareTo(organizationID) != 0) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
"You can only publish in the Organization associated to the current VRE");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
gotOrganization = organizationFromContext;
|
|
||||||
objectNode.put(OWNER_ORG_KEY, organizationFromContext);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(gotOrganization == null) {
|
|
||||||
throw new BadRequestException("You must specify an Organization usign " + OWNER_ORG_KEY + " field");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ckanUser.addUserToOrganization(gotOrganization);
|
|
||||||
|
|
||||||
return objectNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected JsonNode validateJson(String json) {
|
|
||||||
try {
|
|
||||||
// check base information (and set them if needed)
|
|
||||||
ObjectNode objectNode = checkBaseInformation(json);
|
|
||||||
|
|
||||||
// Validating against profiles if any
|
|
||||||
MetadataUtility metadataUtility = new MetadataUtility();
|
|
||||||
if(!metadataUtility.getMetadataProfiles().isEmpty()) {
|
|
||||||
Validator validator = new Validator(mapper);
|
|
||||||
objectNode = validator.validateAgainstProfile(objectNode, metadataUtility);
|
|
||||||
}
|
|
||||||
|
|
||||||
return objectNode;
|
|
||||||
} catch(BadRequestException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new BadRequestException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String list(int limit, int offset) {
|
|
||||||
Map<String,String> parameters = new HashMap<>();
|
|
||||||
if(limit <= 0) {
|
|
||||||
// According to CKAN documentation
|
|
||||||
// the number of matching rows to return. There is a hard limit of 1000 datasets per query.
|
|
||||||
// see https://docs.ckan.org/en/2.6/api/index.html#ckan.logic.action.get.package_search
|
|
||||||
limit = 1000;
|
|
||||||
}
|
|
||||||
parameters.put(ROWS_KEY, String.valueOf(limit));
|
|
||||||
|
|
||||||
if(offset < 0) {
|
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
parameters.put(START_KEY, String.valueOf(offset * limit));
|
|
||||||
|
|
||||||
if(uriInfo != null) {
|
|
||||||
MultivaluedMap<String,String> queryParameters = uriInfo.getQueryParameters();
|
|
||||||
parameters = checkListParameters(queryParameters, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
return list(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void checkOrganizationFilter(String q) {
|
|
||||||
Matcher m = ORGANIZATION_REGEX_PATTERN.matcher(q);
|
|
||||||
|
|
||||||
List<String> matches = new ArrayList<>();
|
|
||||||
while(m.find()) {
|
|
||||||
matches.add(q.substring(m.start(), m.end()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(matches.size() > 0) {
|
|
||||||
StringBuilder error = new StringBuilder();
|
|
||||||
error.append("Organization filters is constrained to VRE. ");
|
|
||||||
if(matches.size() > 1) {
|
|
||||||
error.append("Remove these filters ");
|
|
||||||
} else {
|
|
||||||
error.append("Remove this filter ");
|
|
||||||
}
|
|
||||||
boolean first = true;
|
|
||||||
for(String match : matches) {
|
|
||||||
if(!first) {
|
|
||||||
error.append(", ");
|
|
||||||
} else {
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
error.append("[");
|
|
||||||
error.append(match);
|
|
||||||
error.append("]");
|
|
||||||
}
|
|
||||||
throw new BadRequestException(error.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static String[] allowedListQueryParameters = new String[] {"q", "fq", "fq_list", "sort",
|
|
||||||
/* "facet", "facet.mincount", "facet.limit", "facet.field", */
|
|
||||||
"include_drafts", "include_private", "ext_bbox"};
|
|
||||||
|
|
||||||
protected Map<String,String> checkListParameters(MultivaluedMap<String,String> queryParameters,
|
|
||||||
Map<String,String> parameters) {
|
|
||||||
|
|
||||||
String organizationName = getOrganizationName();
|
|
||||||
ScopeBean scopeBean = new ScopeBean(ContextUtility.getCurrentContext());
|
|
||||||
|
|
||||||
String q = null;
|
|
||||||
if(queryParameters.containsKey(GCatConstants.Q_KEY)) {
|
|
||||||
q = queryParameters.getFirst(GCatConstants.Q_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List is filter per organization only is the request arriving in a VRE.
|
|
||||||
* If the request arrive form a VO or from ROOT the request return the item in all organizations
|
|
||||||
* in the catalog.
|
|
||||||
*/
|
|
||||||
if(scopeBean.is(Type.VRE)) {
|
|
||||||
if(q != null) {
|
|
||||||
checkOrganizationFilter(q);
|
|
||||||
|
|
||||||
// Adding organization filter to q
|
|
||||||
parameters.put(GCatConstants.Q_KEY,
|
|
||||||
String.format("%s:%s AND %s", GCatConstants.ORGANIZATION_PARAMETER, organizationName, q));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
parameters.put(GCatConstants.Q_KEY,
|
|
||||||
String.format(GCatConstants.ORGANIZATION_FILTER_TEMPLATE, organizationName));
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// We are in VO or in ROOT.
|
|
||||||
// The organization filtering is allowed for the client which must provide q parameter in the form:
|
|
||||||
// q=organization:nextnext
|
|
||||||
}
|
|
||||||
|
|
||||||
for(String key : allowedListQueryParameters) {
|
|
||||||
if(queryParameters.containsKey(key)) {
|
|
||||||
parameters.put(key, queryParameters.getFirst(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parameters.put(INCLUDE_PRIVATE_KEY, String.valueOf(true));
|
|
||||||
|
|
||||||
// By default not including draft
|
|
||||||
// parameters.put(INCLUDE_DRAFTS_KEY, String.valueOf(false));
|
|
||||||
|
|
||||||
return parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String list(Map<String,String> parameters) {
|
|
||||||
|
|
||||||
sendGetRequest(LIST, parameters);
|
|
||||||
|
|
||||||
ArrayNode results = (ArrayNode) result.get(RESULTS_KEY);
|
|
||||||
|
|
||||||
ArrayNode arrayNode = mapper.createArrayNode();
|
|
||||||
for(JsonNode node : results) {
|
|
||||||
try {
|
|
||||||
String name = node.get(NAME_KEY).asText();
|
|
||||||
arrayNode.add(name);
|
|
||||||
} catch(Exception e) {
|
|
||||||
try {
|
|
||||||
logger.error("Unable to get the ID of {}. the result will not be included in the result",
|
|
||||||
mapper.writeValueAsString(node));
|
|
||||||
} catch(Exception ex) {
|
|
||||||
logger.error("", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return getAsString(arrayNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void rollbackManagedResources() {
|
|
||||||
for(CKANResource ckanResource : managedResources) {
|
|
||||||
try {
|
|
||||||
ckanResource.rollback();
|
|
||||||
} catch(Exception e) {
|
|
||||||
logger.error("Unable to rollback resource {} to the original value", ckanResource.getResourceID());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ArrayNode createResources(ArrayNode resourcesToBeCreated) {
|
|
||||||
ArrayNode created = mapper.createArrayNode();
|
|
||||||
for(JsonNode resourceNode : resourcesToBeCreated) {
|
|
||||||
CKANResource ckanResource = new CKANResource(itemID);
|
|
||||||
String json = ckanResource.create(getAsString(resourceNode));
|
|
||||||
created.add(getAsJsonNode(json));
|
|
||||||
managedResources.add(ckanResource);
|
|
||||||
}
|
|
||||||
return created;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected JsonNode addExtraField(JsonNode jsonNode, String key, String value) {
|
|
||||||
ArrayNode extras = null;
|
|
||||||
boolean found = false;
|
|
||||||
if(jsonNode.has(EXTRAS_KEY)) {
|
|
||||||
extras = (ArrayNode) jsonNode.get(EXTRAS_KEY);
|
|
||||||
for(JsonNode extra : extras) {
|
|
||||||
if(extra.has(EXTRAS_KEY_KEY) && extra.get(EXTRAS_KEY_KEY).asText().compareTo(key) == 0) {
|
|
||||||
((ObjectNode) extra).put(EXTRAS_VALUE_KEY, value);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
extras = mapper.createArrayNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!found) {
|
|
||||||
ObjectNode extra = mapper.createObjectNode();
|
|
||||||
extra.put(EXTRAS_KEY_KEY, key);
|
|
||||||
extra.put(EXTRAS_VALUE_KEY, value);
|
|
||||||
extras.add(extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String addItemURLViaResolver(JsonNode jsonNode) {
|
|
||||||
// Adding Item URL via Resolver
|
|
||||||
URIResolver uriResolver = new URIResolver();
|
|
||||||
String catalogueItemURL = uriResolver.getCatalogueItemURL(name);
|
|
||||||
addExtraField(jsonNode, EXTRAS_ITEM_URL_KEY, catalogueItemURL);
|
|
||||||
return catalogueItemURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void sendSocialPost(String title, String catalogueItemURL) {
|
|
||||||
try {
|
|
||||||
boolean socialPost = false;
|
|
||||||
try {
|
|
||||||
MultivaluedMap<String,String> queryParameters = uriInfo.getQueryParameters();
|
|
||||||
if(queryParameters.containsKey(GCatConstants.SOCIAL_POST_PARAMETER)) {
|
|
||||||
socialPost = Boolean.parseBoolean(queryParameters.getFirst(GCatConstants.SOCIAL_POST_PARAMETER));
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
socialPost = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(socialPost) {
|
|
||||||
ArrayNode arrayNode = (ArrayNode) result.get(TAGS_KEY);
|
|
||||||
SocialPost socialService = new SocialPost();
|
|
||||||
socialService.setItemID(itemID);
|
|
||||||
socialService.setItemURL(catalogueItemURL);
|
|
||||||
socialService.setTags(arrayNode);
|
|
||||||
socialService.setItemTitle(title);
|
|
||||||
socialService.start();
|
|
||||||
} else {
|
|
||||||
logger.info("The request explicitly disabled the Social Post.");
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
logger.warn(
|
|
||||||
"error dealing with Social Post. The service will not raise the exception belove. Please contact the administrator to let him know about this message.",
|
|
||||||
e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.create.package_create
|
|
||||||
@Override
|
|
||||||
public String create(String json) {
|
|
||||||
try {
|
|
||||||
logger.debug("Going to create Item {}", json);
|
|
||||||
|
|
||||||
JsonNode jsonNode = validateJson(json);
|
|
||||||
|
|
||||||
ArrayNode resourcesToBeCreated = mapper.createArrayNode();
|
|
||||||
if(jsonNode.has(RESOURCES_KEY)) {
|
|
||||||
resourcesToBeCreated = (ArrayNode) jsonNode.get(RESOURCES_KEY);
|
|
||||||
((ObjectNode) jsonNode).remove(RESOURCES_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopeBean scopeBean = new ScopeBean(ContextUtility.getCurrentContext());
|
|
||||||
|
|
||||||
String catalogueItemURL = "";
|
|
||||||
if(scopeBean.is(Type.VRE)) {
|
|
||||||
catalogueItemURL = addItemURLViaResolver(jsonNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
super.create(getAsString(jsonNode));
|
|
||||||
|
|
||||||
this.itemID = result.get(ID_KEY).asText();
|
|
||||||
ArrayNode created = createResources(resourcesToBeCreated);
|
|
||||||
((ObjectNode) result).replace(RESOURCES_KEY, created);
|
|
||||||
|
|
||||||
// Adding Item URL via Resolver as
|
|
||||||
// ((ObjectNode) result).put(ITEM_URL_KEY, catalogueItemURL);
|
|
||||||
|
|
||||||
// Actions performed after a package has been correctly created on ckan.
|
|
||||||
String title = result.get(TITLE_KEY).asText();
|
|
||||||
|
|
||||||
if(scopeBean.is(Type.VRE)) {
|
|
||||||
sendSocialPost(title, catalogueItemURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return getAsString(result);
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
rollbackManagedResources();
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
rollbackManagedResources();
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.update.package_update
|
|
||||||
@Override
|
|
||||||
public String update(String json) {
|
|
||||||
try {
|
|
||||||
JsonNode jsonNode = validateJson(json);
|
|
||||||
|
|
||||||
read();
|
|
||||||
this.itemID = result.get(ID_KEY).asText();
|
|
||||||
|
|
||||||
Map<String,CKANResource> originalResources = new HashMap<>();
|
|
||||||
ArrayNode originalResourcesarrayNode = (ArrayNode) result.get(RESOURCES_KEY);
|
|
||||||
if(originalResources != null) {
|
|
||||||
for(JsonNode resourceNode : originalResourcesarrayNode) {
|
|
||||||
CKANResource ckanResource = new CKANResource(itemID);
|
|
||||||
ckanResource.setPreviousRepresentation(resourceNode);
|
|
||||||
String resourceID = ckanResource.getResourceID();
|
|
||||||
originalResources.put(resourceID, ckanResource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(jsonNode.has(RESOURCES_KEY)) {
|
|
||||||
ArrayNode resourcesToBeSend = mapper.createArrayNode();
|
|
||||||
ArrayNode receivedResources = (ArrayNode) jsonNode.get(RESOURCES_KEY);
|
|
||||||
for(JsonNode resourceNode : receivedResources) {
|
|
||||||
CKANResource ckanResource = new CKANResource(itemID);
|
|
||||||
String resourceId = CKANResource.extractResourceID(resourceNode);
|
|
||||||
if(resourceId != null) {
|
|
||||||
if(originalResources.containsKey(resourceId)) {
|
|
||||||
ckanResource = originalResources.get(resourceId);
|
|
||||||
originalResources.remove(resourceId);
|
|
||||||
} else {
|
|
||||||
throw new BadRequestException(
|
|
||||||
"The content contains a resource with id " + resourceId + " which does not exists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(originalResources.get(resourceId)!= null && (!originalResources.get(resourceId).getPreviousRepresentation().equals(resourceNode))) {
|
|
||||||
resourceNode = ckanResource.createOrUpdate(resourceNode);
|
|
||||||
}
|
|
||||||
resourcesToBeSend.add(resourceNode);
|
|
||||||
managedResources.add(ckanResource);
|
|
||||||
|
|
||||||
}
|
|
||||||
((ObjectNode) jsonNode).replace(RESOURCES_KEY, resourcesToBeSend);
|
|
||||||
}
|
|
||||||
|
|
||||||
addItemURLViaResolver(jsonNode);
|
|
||||||
|
|
||||||
sendPostRequest(ITEM_UPDATE, getAsString(jsonNode));
|
|
||||||
|
|
||||||
for(String resourceId : originalResources.keySet()) {
|
|
||||||
CKANResource ckanResource = originalResources.get(resourceId);
|
|
||||||
ckanResource.deleteFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Adding Item URL via Resolver
|
|
||||||
URIResolver uriResolver = new URIResolver();
|
|
||||||
String catalogueItemURL = uriResolver.getCatalogueItemURL(name);
|
|
||||||
((ObjectNode) result).put(ITEM_URL_KEY, catalogueItemURL);
|
|
||||||
*/
|
|
||||||
|
|
||||||
return getAsString(result);
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
rollbackManagedResources();
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
rollbackManagedResources();
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.patch.package_patch
|
|
||||||
@Override
|
|
||||||
public String patch(String json) {
|
|
||||||
String[] moreAllowed = new String[] {HEAD.class.getSimpleName(), GET.class.getSimpleName(),
|
|
||||||
PUT.class.getSimpleName(), DELETE.class.getSimpleName(), PURGE.class.getSimpleName()};
|
|
||||||
throw new NotAllowedException(OPTIONS.class.getSimpleName(), moreAllowed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void delete() {
|
|
||||||
super.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void purge() {
|
|
||||||
try {
|
|
||||||
delete();
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
// If the item was deleted but not purged we obtain Not Found. This is accepted. The item has to be purged
|
|
||||||
// with SysAdmin right.
|
|
||||||
Status status = Status.fromStatusCode(e.getResponse().getStatusInfo().getStatusCode());
|
|
||||||
if(status != Status.NOT_FOUND) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setApiKey(CKANUtility.getSysAdminAPI());
|
|
||||||
read();
|
|
||||||
if(result.has(RESOURCES_KEY)) {
|
|
||||||
itemID = result.get(ID_KEY).asText();
|
|
||||||
ArrayNode arrayNode = (ArrayNode) result.get(RESOURCES_KEY);
|
|
||||||
for(JsonNode jsonNode : arrayNode) {
|
|
||||||
CKANResource ckanResource = new CKANResource(itemID);
|
|
||||||
ckanResource.setPreviousRepresentation(jsonNode);
|
|
||||||
ckanResource.deleteFile(); // Only delete file is required because the item has been deleted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.purge();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,523 +0,0 @@
|
||||||
package org.gcube.gcat.persistence.ckan;
|
|
||||||
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import javax.ws.rs.BadRequestException;
|
|
||||||
import javax.ws.rs.DELETE;
|
|
||||||
import javax.ws.rs.GET;
|
|
||||||
import javax.ws.rs.HEAD;
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
import javax.ws.rs.NotAllowedException;
|
|
||||||
import javax.ws.rs.OPTIONS;
|
|
||||||
import javax.ws.rs.PUT;
|
|
||||||
import javax.ws.rs.WebApplicationException;
|
|
||||||
|
|
||||||
import org.apache.commons.io.FilenameUtils;
|
|
||||||
import org.apache.tika.mime.MimeType;
|
|
||||||
import org.apache.tika.mime.MimeTypes;
|
|
||||||
import org.gcube.common.gxhttp.request.GXHTTPStringRequest;
|
|
||||||
import org.gcube.gcat.utils.Constants;
|
|
||||||
import org.gcube.gcat.utils.ContextUtility;
|
|
||||||
import org.gcube.gcat.utils.HTTPCall;
|
|
||||||
import org.gcube.gcat.workspace.CatalogueStorageHubManagement;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class CKANResource extends CKAN {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CKANResource.class);
|
|
||||||
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.create.resource_create
|
|
||||||
public static final String RESOURCE_CREATE = CKAN.CKAN_API_PATH + "resource_create";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.resource_show
|
|
||||||
public static final String RESOURCE_SHOW = CKAN.CKAN_API_PATH + "resource_show";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.update.resource_update
|
|
||||||
public static final String RESOURCE_UPDATE = CKAN.CKAN_API_PATH + "resource_update";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.patch.resource_patch
|
|
||||||
public static final String RESOURCE_PATCH = CKAN.CKAN_API_PATH + "resource_patch";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.delete.resource_delete
|
|
||||||
public static final String RESOURCE_DELETE = CKAN.CKAN_API_PATH + "resource_delete";
|
|
||||||
|
|
||||||
protected static final String URL_KEY = "url";
|
|
||||||
|
|
||||||
private static final String RESOURCES_KEY = "resources";
|
|
||||||
private static final String PACKAGE_ID_KEY = "package_id";
|
|
||||||
private static final String FORMAT_KEY = "format";
|
|
||||||
private static final String MIME_TYPE_KEY = "mimetype";
|
|
||||||
private static final String REVISION_ID_KEY = "revision_id";
|
|
||||||
|
|
||||||
private static final String TEMP = "TEMP_";
|
|
||||||
|
|
||||||
public final static String RESOURCE_NAME_REGEX = "^[\\s\\S]*$";
|
|
||||||
|
|
||||||
public static final MimeTypes ALL_MIME_TYPES;
|
|
||||||
|
|
||||||
/* TODO Remove this code ASAP. It requires a function from Storage HUB */
|
|
||||||
private static final String URI_RESOLVER_STORAGE_HUB_HOST_PROD = "data.d4science.org";
|
|
||||||
private static final String URI_RESOLVER_STORAGE_HUB_HOST_PRE = "data-pre.d4science.org";
|
|
||||||
private static final String URI_RESOLVER_STORAGE_HUB_HOST_DEV = "data-d.d4science.org";
|
|
||||||
|
|
||||||
public static final String URI_RESOLVER_STORAGE_HUB_HOST;
|
|
||||||
public static final String URI_RESOLVER_STORAGE_HUB_PATH = "/shub/";
|
|
||||||
|
|
||||||
static {
|
|
||||||
String context = ContextUtility.getCurrentContext();
|
|
||||||
if(context.startsWith("/gcube")) {
|
|
||||||
URI_RESOLVER_STORAGE_HUB_HOST = URI_RESOLVER_STORAGE_HUB_HOST_DEV;
|
|
||||||
} else if(context.startsWith("/pred4s")){
|
|
||||||
URI_RESOLVER_STORAGE_HUB_HOST = URI_RESOLVER_STORAGE_HUB_HOST_PRE;
|
|
||||||
} else {
|
|
||||||
URI_RESOLVER_STORAGE_HUB_HOST = URI_RESOLVER_STORAGE_HUB_HOST_PROD;
|
|
||||||
}
|
|
||||||
// If you might be dealing with custom mimetypes too, then Tika supports those, and change line one to be:
|
|
||||||
// TikaConfig config = TikaConfig.getDefaultConfig();
|
|
||||||
// MimeTypes ALL_MIME_TYPES = config.getMimeRepository();
|
|
||||||
ALL_MIME_TYPES = MimeTypes.getDefaultMimeTypes();
|
|
||||||
}
|
|
||||||
/* TODO END Code to be Removed */
|
|
||||||
|
|
||||||
protected String itemID;
|
|
||||||
|
|
||||||
public String getItemID() {
|
|
||||||
return itemID;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String resourceID;
|
|
||||||
|
|
||||||
protected boolean persisted;
|
|
||||||
protected URL persistedURL;
|
|
||||||
|
|
||||||
protected String mimeType;
|
|
||||||
protected String originalFileExtension;
|
|
||||||
|
|
||||||
protected JsonNode previousRepresentation;
|
|
||||||
|
|
||||||
protected CatalogueStorageHubManagement storageHubManagement;
|
|
||||||
|
|
||||||
public URL getPersistedURL() {
|
|
||||||
return persistedURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String extractResourceID(JsonNode jsonNode) {
|
|
||||||
String resourceID = null;
|
|
||||||
if(jsonNode.has(ID_KEY)) {
|
|
||||||
resourceID = jsonNode.get(ID_KEY).asText();
|
|
||||||
}
|
|
||||||
return resourceID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getResourceID() {
|
|
||||||
if(resourceID == null && previousRepresentation != null) {
|
|
||||||
resourceID = CKANResource.extractResourceID(previousRepresentation);
|
|
||||||
}
|
|
||||||
return resourceID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setResourceID(String resourceID) {
|
|
||||||
this.resourceID = resourceID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPreviousRepresentation(JsonNode jsonNode) {
|
|
||||||
validate(jsonNode);
|
|
||||||
previousRepresentation = jsonNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonNode getPreviousRepresentation() {
|
|
||||||
if(previousRepresentation == null && resourceID != null) {
|
|
||||||
sendGetRequest(READ, getMapWithID(resourceID));
|
|
||||||
validate(result);
|
|
||||||
previousRepresentation = result;
|
|
||||||
}
|
|
||||||
return previousRepresentation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CKANResource(String itemID) {
|
|
||||||
super();
|
|
||||||
this.nameRegex = RESOURCE_NAME_REGEX;
|
|
||||||
this.itemID = itemID;
|
|
||||||
CREATE = RESOURCE_CREATE;
|
|
||||||
READ = RESOURCE_SHOW;
|
|
||||||
UPDATE = RESOURCE_UPDATE;
|
|
||||||
PATCH = RESOURCE_PATCH;
|
|
||||||
DELETE = RESOURCE_DELETE;
|
|
||||||
PURGE = null;
|
|
||||||
persisted = false;
|
|
||||||
previousRepresentation = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String list(int limit, int offeset) {
|
|
||||||
return list();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String list() {
|
|
||||||
CKANPackage ckanPackage = new CKANPackage();
|
|
||||||
ckanPackage.setName(itemID);
|
|
||||||
String itemJson = ckanPackage.read();
|
|
||||||
JsonNode item = getAsJsonNode(itemJson);
|
|
||||||
JsonNode resources = item.get(RESOURCES_KEY);
|
|
||||||
return getAsString(resources);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getFormat() {
|
|
||||||
String format = null;
|
|
||||||
if(originalFileExtension != null) {
|
|
||||||
format = originalFileExtension;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
MimeType mimeTypeClzInstance = ALL_MIME_TYPES.forName(mimeType);
|
|
||||||
format = mimeTypeClzInstance.getExtension();
|
|
||||||
// List<String> extensions = mimeTypeClzInstance.getExtensions();
|
|
||||||
if(format == null || format.compareTo("") == 0) {
|
|
||||||
format = mimeType.split("/")[1].split(";")[0];
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
try {
|
|
||||||
format = mimeType.split("/")[1].split(";")[0];
|
|
||||||
} catch(Exception ex) {
|
|
||||||
format = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(format != null && format.startsWith(".")) {
|
|
||||||
format = format.substring(1);
|
|
||||||
}
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ObjectNode persistStorageFile(ObjectNode objectNode) {
|
|
||||||
|
|
||||||
if(objectNode.has(URL_KEY)) {
|
|
||||||
String urlString = objectNode.get(URL_KEY).asText();
|
|
||||||
|
|
||||||
URL url;
|
|
||||||
try {
|
|
||||||
url = new URL(urlString);
|
|
||||||
} catch(MalformedURLException e) {
|
|
||||||
throw new BadRequestException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
url = copyStorageResource(url);
|
|
||||||
|
|
||||||
if(name != null) {
|
|
||||||
objectNode.put(NAME_KEY, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mimeType != null) {
|
|
||||||
objectNode.put(MIME_TYPE_KEY, mimeType);
|
|
||||||
|
|
||||||
if(!objectNode.has(FORMAT_KEY)) {
|
|
||||||
String format = getFormat();
|
|
||||||
if(format != null) {
|
|
||||||
objectNode.put(FORMAT_KEY, format);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
objectNode.put(URL_KEY, url.toString());
|
|
||||||
return objectNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
String error = String.format("The content must contains the %s property", URL_KEY);
|
|
||||||
throw new BadRequestException(error);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ObjectNode validate(String json) throws MalformedURLException {
|
|
||||||
JsonNode jsonNode = getAsJsonNode(json);
|
|
||||||
return validate(jsonNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ObjectNode validate(JsonNode jsonNode) {
|
|
||||||
|
|
||||||
ObjectNode objectNode = (ObjectNode) jsonNode;
|
|
||||||
|
|
||||||
if(objectNode.has(PACKAGE_ID_KEY)) {
|
|
||||||
String packageId = objectNode.get(PACKAGE_ID_KEY).asText();
|
|
||||||
if(packageId.compareTo(itemID) != 0) {
|
|
||||||
String error = String.format(
|
|
||||||
"Item ID %s does not match %s which is the value of %s contained in the representation.",
|
|
||||||
itemID, packageId, PACKAGE_ID_KEY);
|
|
||||||
throw new BadRequestException(error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
objectNode.put(PACKAGE_ID_KEY, itemID);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(objectNode.has(ID_KEY)) {
|
|
||||||
String gotId = objectNode.get(ID_KEY).asText();
|
|
||||||
if(resourceID == null) {
|
|
||||||
resourceID = gotId;
|
|
||||||
} else {
|
|
||||||
if(resourceID.compareTo(gotId) != 0) {
|
|
||||||
String error = String.format(
|
|
||||||
"Resource ID %s does not match %s which is the value of %s contained in the representation.",
|
|
||||||
resourceID, gotId, ID_KEY);
|
|
||||||
throw new BadRequestException(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resourceID = TEMP + UUID.randomUUID().toString();
|
|
||||||
logger.trace(
|
|
||||||
"The id of the resource with name {} for package {} has not been provided. It has been generated : {}",
|
|
||||||
name, itemID, resourceID);
|
|
||||||
}
|
|
||||||
|
|
||||||
return objectNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected URL getFinalURL(String url) {
|
|
||||||
try {
|
|
||||||
URL urlURL = new URL(url);
|
|
||||||
return CKANResource.getFinalURL(urlURL);
|
|
||||||
} catch(MalformedURLException e) {
|
|
||||||
throw new BadRequestException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static URL getFinalURL(URL url) {
|
|
||||||
HTTPCall httpCall = new HTTPCall(url.toString());
|
|
||||||
httpCall.setgCubeTargetService(false);
|
|
||||||
URL finalURL = httpCall.getFinalURL(url);
|
|
||||||
return finalURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isStorageFile(URL url) {
|
|
||||||
URL urlToCheck = url;
|
|
||||||
try {
|
|
||||||
urlToCheck = getFinalURL(url);
|
|
||||||
} catch(Exception e) {
|
|
||||||
// TODO Evaluate if we want to validate the URL. If the URL does not exists the service
|
|
||||||
// could decide to refuse the Resource Creation
|
|
||||||
}
|
|
||||||
if(urlToCheck.getHost().compareTo(URI_RESOLVER_STORAGE_HUB_HOST) == 0) {
|
|
||||||
if(urlToCheck.getPath().startsWith(URI_RESOLVER_STORAGE_HUB_PATH)) {
|
|
||||||
persistedURL = urlToCheck;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the URl is a workspace URL so that is has to copy the resource to guarantee
|
|
||||||
* the resource remain persistent
|
|
||||||
* @param url the URL to check
|
|
||||||
* @return the public URL of the copied resource if any. It return the original URL otherwise
|
|
||||||
*/
|
|
||||||
protected URL copyStorageResource(URL url) {
|
|
||||||
persistedURL = url;
|
|
||||||
if(isStorageFile(persistedURL)) {
|
|
||||||
storageHubManagement = new CatalogueStorageHubManagement();
|
|
||||||
try {
|
|
||||||
persistedURL = storageHubManagement.ensureResourcePersistence(persistedURL, itemID, resourceID);
|
|
||||||
String originalFilename = storageHubManagement.getOriginalFilename();
|
|
||||||
name = FilenameUtils.removeExtension(originalFilename);
|
|
||||||
originalFileExtension = FilenameUtils.getExtension(originalFilename);
|
|
||||||
mimeType = storageHubManagement.getMimeType();
|
|
||||||
persisted = true;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return persistedURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void deleteStorageResource(URL url, String resourceID, String mimetype) {
|
|
||||||
persistedURL = url;
|
|
||||||
if(isStorageFile(persistedURL)) {
|
|
||||||
try {
|
|
||||||
GXHTTPStringRequest gxhttpStringRequest = GXHTTPStringRequest.newRequest(persistedURL.toString());
|
|
||||||
HttpURLConnection httpURLConnection = gxhttpStringRequest.from(Constants.CATALOGUE_NAME).head();
|
|
||||||
String storageHubContentType = httpURLConnection.getContentType().split(";")[0];
|
|
||||||
if(mimetype.compareTo(storageHubContentType) != 0) {
|
|
||||||
mimetype = storageHubContentType;
|
|
||||||
// Using storage hub mimetype
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
// using provided mimetype
|
|
||||||
}
|
|
||||||
storageHubManagement = new CatalogueStorageHubManagement();
|
|
||||||
try {
|
|
||||||
storageHubManagement.deleteResourcePersistence(itemID, resourceID, mimetype);
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String create(JsonNode jsonNode) {
|
|
||||||
try {
|
|
||||||
ObjectNode objectNode = validate(jsonNode);
|
|
||||||
objectNode = persistStorageFile(objectNode);
|
|
||||||
String ret = super.create(getAsString(objectNode));
|
|
||||||
if(persisted) {
|
|
||||||
String gotResourceID = result.get(ID_KEY).asText();
|
|
||||||
if(gotResourceID != null && gotResourceID.compareTo(resourceID) != 0) {
|
|
||||||
resourceID = gotResourceID;
|
|
||||||
String revisionID = result.get(REVISION_ID_KEY).asText();
|
|
||||||
storageHubManagement.renameFile(resourceID, revisionID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
// TODO Remove created file if any
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
// TODO Remove created file if any
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String create(String json) {
|
|
||||||
JsonNode jsonNode = getAsJsonNode(json);
|
|
||||||
return create(jsonNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String read() {
|
|
||||||
return sendGetRequest(READ, getMapWithID(resourceID));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String update(JsonNode jsonNode) throws Exception {
|
|
||||||
ObjectNode resourceNode = (ObjectNode) jsonNode;
|
|
||||||
// This cannot be moved outside otherwise we don't
|
|
||||||
resourceNode = validate(resourceNode);
|
|
||||||
|
|
||||||
getPreviousRepresentation();
|
|
||||||
|
|
||||||
String oldURL = previousRepresentation.get(CKANResource.URL_KEY).asText();
|
|
||||||
String newURL = resourceNode.get(CKANResource.URL_KEY).asText();
|
|
||||||
|
|
||||||
if(!previousRepresentation.equals(resourceNode)) {
|
|
||||||
if(oldURL.compareTo(newURL) != 0) {
|
|
||||||
logger.trace("The URL of the resource with id {} was not changed", resourceID);
|
|
||||||
storageHubManagement = new CatalogueStorageHubManagement();
|
|
||||||
this.mimeType = previousRepresentation.get(CKANResource.MIME_TYPE_KEY).asText();
|
|
||||||
|
|
||||||
try {
|
|
||||||
storageHubManagement.retrievePersistedFile(resourceID, mimeType);
|
|
||||||
}catch (Exception e) {
|
|
||||||
// If the file was not persisted by gCat (e.g. created with the portlet) some errors can occurs
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
logger.trace("The URL of resource with id {} has been changed the old URL was {}, the new URL is {}",
|
|
||||||
resourceID, oldURL, newURL);
|
|
||||||
resourceNode = persistStorageFile(resourceNode);
|
|
||||||
/*
|
|
||||||
try {
|
|
||||||
URL urlOLD = new URL(oldURL);
|
|
||||||
deleteStorageResource(urlOLD);
|
|
||||||
}catch (Exception e) {
|
|
||||||
logger.error("Unable to remove old file at URL {}", oldURL);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
String ret = super.update(getAsString(resourceNode));
|
|
||||||
if(storageHubManagement.getPersistedFile()!= null) {
|
|
||||||
String revisionID = result.get(REVISION_ID_KEY).asText();
|
|
||||||
storageHubManagement.addRevisionID(resourceID, revisionID);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return previousRepresentation.asText();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String update(String json) {
|
|
||||||
try {
|
|
||||||
JsonNode jsonNode = getAsJsonNode(json);
|
|
||||||
return update(jsonNode);
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new WebApplicationException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String patch(String json) {
|
|
||||||
String[] moreAllowed = new String[] {HEAD.class.getSimpleName(), GET.class.getSimpleName(),
|
|
||||||
PUT.class.getSimpleName(), DELETE.class.getSimpleName()};
|
|
||||||
throw new NotAllowedException(OPTIONS.class.getSimpleName(), moreAllowed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete(boolean purge) {
|
|
||||||
delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete() {
|
|
||||||
try {
|
|
||||||
deleteFile();
|
|
||||||
sendPostRequest(DELETE, createJsonNodeWithID(resourceID));
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new WebApplicationException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void purge() {
|
|
||||||
String[] moreAllowed = new String[] {HEAD.class.getSimpleName(), GET.class.getSimpleName(),
|
|
||||||
PUT.class.getSimpleName(), DELETE.class.getSimpleName()};
|
|
||||||
throw new NotAllowedException(OPTIONS.class.getSimpleName(), moreAllowed);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonNode createOrUpdate(JsonNode jsonNode) {
|
|
||||||
ObjectNode resourceNode = (ObjectNode) jsonNode;
|
|
||||||
if(resourceNode.has(ID_KEY)) {
|
|
||||||
try {
|
|
||||||
update(resourceNode);
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new WebApplicationException(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
create(resourceNode);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deleteFile() {
|
|
||||||
try {
|
|
||||||
getPreviousRepresentation();
|
|
||||||
URL url = new URL(previousRepresentation.get(URL_KEY).asText());
|
|
||||||
mimeType = previousRepresentation.get(MIME_TYPE_KEY).asText();
|
|
||||||
deleteStorageResource(url, resourceID, mimeType);
|
|
||||||
} catch(Exception e) {
|
|
||||||
logger.error("Unable to delete resource {}",
|
|
||||||
previousRepresentation != null ? getAsString(previousRepresentation) : "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void rollback() {
|
|
||||||
if(previousRepresentation != null) {
|
|
||||||
try {
|
|
||||||
update(previousRepresentation);
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new WebApplicationException(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,314 +0,0 @@
|
||||||
package org.gcube.gcat.persistence.ckan;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
import javax.ws.rs.WebApplicationException;
|
|
||||||
import javax.ws.rs.core.Response.Status;
|
|
||||||
|
|
||||||
import org.gcube.gcat.social.PortalUser;
|
|
||||||
import org.gcube.gcat.utils.ContextUtility;
|
|
||||||
import org.gcube.gcat.utils.RandomString;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class CKANUser extends CKAN {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CKANUser.class);
|
|
||||||
|
|
||||||
/* User Paths */
|
|
||||||
// see https://docs.ckan.org/en/latest/api/#ckan.logic.action.get.user_list
|
|
||||||
public static final String USER_LIST = CKAN.CKAN_API_PATH + "user_list";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.create.user_create
|
|
||||||
public static final String USER_CREATE = CKAN.CKAN_API_PATH + "user_create";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.get.user_show
|
|
||||||
public static final String USER_SHOW = CKAN.CKAN_API_PATH + "user_show";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.update.user_update
|
|
||||||
public static final String USER_UPDATE = CKAN.CKAN_API_PATH + "user_update";
|
|
||||||
// see http://docs.ckan.org/en/latest/api/#ckan.logic.action.delete.user_delete
|
|
||||||
public static final String USER_DELETE = CKAN.CKAN_API_PATH + "user_delete";
|
|
||||||
|
|
||||||
public static final String ADD_USER_TO_GROUP = CKAN.CKAN_API_PATH + "member_create";
|
|
||||||
|
|
||||||
public static final String NAME = "name";
|
|
||||||
public static final String DISPLAY_NAME = "display_name";
|
|
||||||
public static final String FULL_NAME = "fullname";
|
|
||||||
public static final String ABOUT = "about";
|
|
||||||
public static final String EMAIL = "email";
|
|
||||||
public static final String PASSWORD = "password";
|
|
||||||
|
|
||||||
private static final String API_KEY = "apikey";
|
|
||||||
|
|
||||||
public enum Role {
|
|
||||||
MEMBER("Catalogue-Member", "member"), EDITOR("Catalogue-Editor", "editor"), ADMIN("Catalogue-Admin", "admin");
|
|
||||||
|
|
||||||
private final String portalRole;
|
|
||||||
private final String ckanRole;
|
|
||||||
|
|
||||||
Role(String portalRole, String ckanRole) {
|
|
||||||
this.portalRole = portalRole;
|
|
||||||
this.ckanRole = ckanRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPortalRole() {
|
|
||||||
return portalRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCkanRole() {
|
|
||||||
return ckanRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static final Map<String,Role> ROLE_BY_PORTAL_ROLE;
|
|
||||||
protected static final Map<String,Role> ROLE_BY_CKAN_ROLE;
|
|
||||||
|
|
||||||
static {
|
|
||||||
ROLE_BY_PORTAL_ROLE = new HashMap<String,Role>();
|
|
||||||
|
|
||||||
// null or empty string identify a member
|
|
||||||
ROLE_BY_PORTAL_ROLE.put(null, MEMBER);
|
|
||||||
ROLE_BY_PORTAL_ROLE.put("", MEMBER);
|
|
||||||
|
|
||||||
ROLE_BY_CKAN_ROLE = new HashMap<String,Role>();
|
|
||||||
|
|
||||||
for(Role role : Role.values()) {
|
|
||||||
ROLE_BY_PORTAL_ROLE.put(role.getPortalRole(), role);
|
|
||||||
ROLE_BY_CKAN_ROLE.put(role.getCkanRole(), role);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Role getRoleFromPortalRole(String portalRole) {
|
|
||||||
return ROLE_BY_PORTAL_ROLE.get(portalRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getCkanRoleFromPortalRole(String portalRole) {
|
|
||||||
return getRoleFromPortalRole(portalRole).getCkanRole();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Role getRoleFromCkanRole(String ckanRole) {
|
|
||||||
return ROLE_BY_CKAN_ROLE.get(ckanRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getPortalRoleFromCkanRole(String ckanRole) {
|
|
||||||
return getRoleFromCkanRole(ckanRole).getPortalRole();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected PortalUser portalUser;
|
|
||||||
protected Role role;
|
|
||||||
|
|
||||||
public CKANUser() {
|
|
||||||
super();
|
|
||||||
LIST = USER_LIST;
|
|
||||||
CREATE = USER_CREATE;
|
|
||||||
READ = USER_SHOW;
|
|
||||||
UPDATE = USER_UPDATE;
|
|
||||||
PATCH = null;
|
|
||||||
DELETE = USER_DELETE;
|
|
||||||
PURGE = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String create() {
|
|
||||||
RandomString randomString = new RandomString(12);
|
|
||||||
ObjectNode objectNode = mapper.createObjectNode();
|
|
||||||
objectNode.put(NAME, name);
|
|
||||||
objectNode.put(PASSWORD, randomString.nextString());
|
|
||||||
checkAndSetEMail(objectNode);
|
|
||||||
checkAndSetFullName(objectNode);
|
|
||||||
checkAndSetJobTitle(objectNode);
|
|
||||||
return create(getAsString(objectNode));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete(boolean purge) {
|
|
||||||
this.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param objectNode
|
|
||||||
* @return true if the display name and the full name has been updated in objectNode
|
|
||||||
*/
|
|
||||||
private boolean checkAndSetJobTitle(ObjectNode objectNode) {
|
|
||||||
String portalJobTitle = getPortalUser().getJobTitle();
|
|
||||||
|
|
||||||
String ckanJobTitle = "";
|
|
||||||
if(objectNode.has(ABOUT)) {
|
|
||||||
ckanJobTitle = objectNode.get(ABOUT).asText();
|
|
||||||
}
|
|
||||||
if(portalJobTitle.compareTo(ckanJobTitle) != 0) {
|
|
||||||
objectNode.put(ABOUT, portalJobTitle);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param objectNode
|
|
||||||
* @return true if the display name and the full name has been updated in objectNode
|
|
||||||
*/
|
|
||||||
private boolean checkAndSetFullName(ObjectNode objectNode) {
|
|
||||||
String portalFullname = getPortalUser().getFullName();
|
|
||||||
|
|
||||||
String ckanFullname = "";
|
|
||||||
if(objectNode.has(FULL_NAME)) {
|
|
||||||
ckanFullname = objectNode.get(FULL_NAME).asText();
|
|
||||||
}
|
|
||||||
if(portalFullname.compareTo(ckanFullname) != 0) {
|
|
||||||
objectNode.put(FULL_NAME, portalFullname);
|
|
||||||
objectNode.put(DISPLAY_NAME, portalFullname);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param objectNode
|
|
||||||
* @return true if the display name and the full name has been updated
|
|
||||||
*/
|
|
||||||
private boolean checkAndSetEMail(ObjectNode objectNode) {
|
|
||||||
String portalEmail = getPortalUser().getEMail();
|
|
||||||
|
|
||||||
String ckanEmail = "";
|
|
||||||
if(objectNode.has(EMAIL)) {
|
|
||||||
ckanEmail = objectNode.get(EMAIL).asText();
|
|
||||||
}
|
|
||||||
if(portalEmail.compareTo(ckanEmail) != 0) {
|
|
||||||
objectNode.put(EMAIL, portalEmail);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the user profile on CKAN if the got got informations differs from the portal information
|
|
||||||
* @return true if the profile information has been updated
|
|
||||||
*/
|
|
||||||
protected boolean updateProfileIfNeeded() {
|
|
||||||
ObjectNode objectNode = (ObjectNode) result;
|
|
||||||
boolean toBeUpdated = false;
|
|
||||||
|
|
||||||
toBeUpdated = checkAndSetEMail(objectNode) || toBeUpdated;
|
|
||||||
toBeUpdated = checkAndSetFullName(objectNode) || toBeUpdated;
|
|
||||||
toBeUpdated = checkAndSetJobTitle(objectNode) || toBeUpdated;
|
|
||||||
|
|
||||||
if(toBeUpdated) {
|
|
||||||
update(getAsString(objectNode));
|
|
||||||
}
|
|
||||||
return toBeUpdated;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void retrieve() {
|
|
||||||
setApiKey(CKANUtility.getSysAdminAPI());
|
|
||||||
try {
|
|
||||||
if(name == null || name.compareTo("") == 0) {
|
|
||||||
setName(getCKANUsername());
|
|
||||||
}
|
|
||||||
read();
|
|
||||||
updateProfileIfNeeded();
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
if(e.getResponse().getStatusInfo() == Status.NOT_FOUND) {
|
|
||||||
create();
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
addUserToOrganization();
|
|
||||||
}catch (Exception e) {
|
|
||||||
// The organization could not exists and this is fine in some cases like organization create or
|
|
||||||
// for listing items at VO level. The organization corresponding to the VO could not exists.
|
|
||||||
logger.warn("Add user to organization {} failed. This is acceptable in the case the request is at VO level and the corresponding orgnization does not esists and should not, as well as when the organization is going to be created", CKANOrganization.getCKANOrganizationName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void parseResult() {
|
|
||||||
name = result.get(NAME).asText();
|
|
||||||
try {
|
|
||||||
apiKey = result.get(API_KEY).asText();
|
|
||||||
}catch (Exception e) {
|
|
||||||
if(name.compareTo(getCKANUsername())==0) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static String getCKANUsername(String username) {
|
|
||||||
if(username == null)
|
|
||||||
return null;
|
|
||||||
return username.trim().replaceAll("\\.", "_");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getCKANUsername() {
|
|
||||||
return getCKANUsername(ContextUtility.getUsername());
|
|
||||||
}
|
|
||||||
|
|
||||||
public String read() {
|
|
||||||
String ret = super.read();
|
|
||||||
parseResult();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void addUserToOrganization(String organizationName, String ckanUsername, String role) {
|
|
||||||
logger.trace("Going to add user {} to organization {} with role {}", ckanUsername, organizationName, role);
|
|
||||||
CKANOrganization ckanOrganization = new CKANOrganization();
|
|
||||||
ckanOrganization.setApiKey(CKANUtility.getSysAdminAPI());
|
|
||||||
ckanOrganization.setName(organizationName);
|
|
||||||
ckanOrganization.addUserToOrganisation(ckanUsername, role);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Role getRole() {
|
|
||||||
if(role == null) {
|
|
||||||
role = Role.MEMBER;
|
|
||||||
List<String> roles = getPortalUser().getRoles();
|
|
||||||
for(String portalRole : roles) {
|
|
||||||
Role gotRole = Role.getRoleFromPortalRole(portalRole);
|
|
||||||
if(gotRole != null && gotRole.ordinal() > role.ordinal()) {
|
|
||||||
role = gotRole;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return role;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addUserToOrganization(String organizationName) {
|
|
||||||
addUserToOrganization(organizationName, name, getRole().getCkanRole());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addUserToOrganization() {
|
|
||||||
String organizationName = CKANOrganization.getCKANOrganizationName();
|
|
||||||
addUserToOrganization(organizationName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addToGroup(String groupName) throws WebApplicationException {
|
|
||||||
try {
|
|
||||||
ObjectNode objectNode = mapper.createObjectNode();
|
|
||||||
objectNode.put(ID_KEY, CKANGroup.getCKANGroupName(groupName));
|
|
||||||
objectNode.put("object", name);
|
|
||||||
objectNode.put("object_type", "user");
|
|
||||||
objectNode.put("capacity", "member");
|
|
||||||
sendPostRequest(ADD_USER_TO_GROUP, getAsString(objectNode));
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PortalUser getPortalUser() {
|
|
||||||
if(portalUser == null) {
|
|
||||||
portalUser = new PortalUser();
|
|
||||||
}
|
|
||||||
return portalUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
package org.gcube.gcat.persistence.ckan;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.cache.Cache;
|
|
||||||
import javax.cache.CacheManager;
|
|
||||||
import javax.cache.Caching;
|
|
||||||
import javax.cache.configuration.MutableConfiguration;
|
|
||||||
import javax.cache.expiry.CreatedExpiryPolicy;
|
|
||||||
import javax.cache.expiry.Duration;
|
|
||||||
import javax.cache.spi.CachingProvider;
|
|
||||||
|
|
||||||
import org.gcube.gcat.utils.ContextUtility;
|
|
||||||
|
|
||||||
public abstract class CKANUserCache {
|
|
||||||
|
|
||||||
private static final CacheManager cacheManager;
|
|
||||||
|
|
||||||
private static final MutableConfiguration<String,CKANUser> userCacheConfiguration;
|
|
||||||
|
|
||||||
private static final Map<String,Cache<String,CKANUser>> userCachePerContext;
|
|
||||||
|
|
||||||
static {
|
|
||||||
CachingProvider provider = Caching.getCachingProvider();
|
|
||||||
cacheManager = provider.getCacheManager();
|
|
||||||
|
|
||||||
userCacheConfiguration = new MutableConfiguration<String,CKANUser>().setTypes(String.class, CKANUser.class)
|
|
||||||
.setStoreByValue(false)
|
|
||||||
.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.MINUTES, 15)));
|
|
||||||
|
|
||||||
userCachePerContext = new HashMap<String,Cache<String,CKANUser>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private CKANUserCache() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CKANUser getCurrrentCKANUser() {
|
|
||||||
String context = ContextUtility.getCurrentContext();
|
|
||||||
Cache<String,CKANUser> userCache = userCachePerContext.get(context);
|
|
||||||
if(userCache == null) {
|
|
||||||
userCache = cacheManager.createCache(context, userCacheConfiguration);
|
|
||||||
userCachePerContext.put(context, userCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
String gcubeUsername = ContextUtility.getUsername();
|
|
||||||
CKANUser ckanUser = userCache.get(gcubeUsername);
|
|
||||||
if(ckanUser == null) {
|
|
||||||
ckanUser = new CKANUser();
|
|
||||||
ckanUser.retrieve();
|
|
||||||
userCache.put(gcubeUsername, ckanUser);
|
|
||||||
}
|
|
||||||
return ckanUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void finalize() throws Throwable {
|
|
||||||
super.finalize();
|
|
||||||
cacheManager.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package org.gcube.gcat.persistence.ckan;
|
|
||||||
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class CKANUtility {
|
|
||||||
|
|
||||||
public static String getSysAdminAPI() {
|
|
||||||
try {
|
|
||||||
return CKANInstance.getInstance().getSysAdminToken();
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getApiKey() {
|
|
||||||
try {
|
|
||||||
CKANUser ckanUser = CKANUserCache.getCurrrentCKANUser();
|
|
||||||
return ckanUser.getApiKey();
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,21 +1,36 @@
|
||||||
package org.gcube.gcat.profile;
|
package org.gcube.gcat.profile;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.regex.PatternSyntaxException;
|
||||||
|
|
||||||
|
import javax.ws.rs.BadRequestException;
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
import javax.ws.rs.InternalServerErrorException;
|
||||||
import javax.ws.rs.NotAuthorizedException;
|
import javax.ws.rs.NotAuthorizedException;
|
||||||
import javax.ws.rs.NotFoundException;
|
import javax.ws.rs.NotFoundException;
|
||||||
import javax.ws.rs.WebApplicationException;
|
import javax.ws.rs.WebApplicationException;
|
||||||
|
import javax.xml.bind.JAXBContext;
|
||||||
|
import javax.xml.bind.JAXBException;
|
||||||
|
import javax.xml.bind.Unmarshaller;
|
||||||
|
|
||||||
|
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import org.gcube.common.resources.gcore.GenericResource;
|
import org.gcube.common.resources.gcore.GenericResource;
|
||||||
import org.gcube.common.resources.gcore.Resources;
|
import org.gcube.common.resources.gcore.Resources;
|
||||||
|
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataField;
|
||||||
|
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataFormat;
|
||||||
|
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataValidator;
|
||||||
|
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataVocabulary;
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.reader.MetadataFormatDiscovery;
|
import org.gcube.datacatalogue.metadatadiscovery.reader.MetadataFormatDiscovery;
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.reader.QueryForResourceUtil;
|
import org.gcube.datacatalogue.metadatadiscovery.reader.QueryForResourceUtil;
|
||||||
|
import org.gcube.gcat.api.roles.Role;
|
||||||
import org.gcube.gcat.persistence.ckan.CKANUser;
|
import org.gcube.gcat.persistence.ckan.CKANUser;
|
||||||
import org.gcube.gcat.persistence.ckan.CKANUser.Role;
|
import org.gcube.gcat.persistence.ckan.cache.CKANUserCache;
|
||||||
import org.gcube.gcat.persistence.ckan.CKANUserCache;
|
|
||||||
import org.gcube.gcat.utils.Constants;
|
import org.gcube.gcat.utils.Constants;
|
||||||
import org.gcube.informationsystem.publisher.RegistryPublisher;
|
import org.gcube.informationsystem.publisher.RegistryPublisher;
|
||||||
import org.gcube.informationsystem.publisher.RegistryPublisherFactory;
|
import org.gcube.informationsystem.publisher.RegistryPublisherFactory;
|
||||||
|
@ -29,9 +44,9 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
/**
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
public class ISProfile {
|
public class ISProfile {
|
||||||
|
|
||||||
public static int PRETTY_PRINT_INDENT_FACTOR = 4;
|
public static int PRETTY_PRINT_INDENT_FACTOR = 4;
|
||||||
|
@ -48,6 +63,14 @@ public class ISProfile {
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int count() {
|
||||||
|
try {
|
||||||
|
return (new MetadataUtility()).getProfilesNames().size();
|
||||||
|
}catch(Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ArrayNode list() {
|
public ArrayNode list() {
|
||||||
ArrayNode arrayNode = mapper.createArrayNode();
|
ArrayNode arrayNode = mapper.createArrayNode();
|
||||||
|
|
||||||
|
@ -158,15 +181,74 @@ public class ISProfile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean createOrUpdate(String name, String xml) throws SAXException {
|
public static MetadataFormat getMetadataFormat(String xml) throws JAXBException {
|
||||||
|
JAXBContext jaxbContext = JAXBContext.newInstance(MetadataFormat.class);
|
||||||
|
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
|
||||||
|
InputStream stream = new ByteArrayInputStream(xml.getBytes());
|
||||||
|
MetadataFormat metadataFormat = (MetadataFormat) unmarshaller.unmarshal(stream);
|
||||||
|
return metadataFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkRegex(String regex, String text) {
|
||||||
|
try {
|
||||||
|
Pattern pattern = Pattern.compile(regex);
|
||||||
|
if(text!=null) {
|
||||||
|
Matcher matcher = pattern.matcher(text);
|
||||||
|
if(!matcher.find()) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (PatternSyntaxException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void validateMetadataFormat(MetadataFormat metadataFormat) throws BadRequestException {
|
||||||
|
List<MetadataField> metadataFields = metadataFormat.getMetadataFields();
|
||||||
|
for(MetadataField metadataField : metadataFields) {
|
||||||
|
String defaultValue = metadataField.getDefaultValue();
|
||||||
|
|
||||||
|
MetadataValidator metadataValidator = metadataField.getValidator();
|
||||||
|
if(metadataValidator!=null) {
|
||||||
|
String regularExpression = metadataValidator.getRegularExpression();
|
||||||
|
if(regularExpression!=null && regularExpression.length()>0) {
|
||||||
|
try {
|
||||||
|
checkRegex(regularExpression, defaultValue);
|
||||||
|
} catch (PatternSyntaxException e) {
|
||||||
|
throw new BadRequestException("The regular expression provided for metadata field '" + metadataField.getFieldName() + "' (i.e. '" + regularExpression + "') is not valid." );
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw new BadRequestException("The default value provided for metadata field '" + metadataField.getFieldName() + "' (i.e. '" + defaultValue + "') does not match the regular expression defined (i.e. '" + regularExpression + "')");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MetadataVocabulary metadataVocabulary = metadataField.getVocabulary();
|
||||||
|
if(metadataVocabulary!=null && defaultValue!=null) {
|
||||||
|
List<String> vocabularyFields = metadataVocabulary.getVocabularyFields();
|
||||||
|
if(!vocabularyFields.contains(defaultValue)) {
|
||||||
|
throw new BadRequestException("The default value provided for metadata field '" + metadataField.getFieldName() + "' (i.e. '" + defaultValue + "') does not match the vocabulary (i.e. '" + vocabularyFields.toString() + "')");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean createOrUpdate(String name, String xml) throws Exception {
|
||||||
try {
|
try {
|
||||||
CKANUser ckanUser = CKANUserCache.getCurrrentCKANUser();
|
CKANUser ckanUser = CKANUserCache.getCurrrentCKANUser();
|
||||||
if(ckanUser.getRole().ordinal()<Role.ADMIN.ordinal()) {
|
if(ckanUser.getRole().ordinal()<Role.EDITOR.ordinal()) {
|
||||||
throw new NotAuthorizedException("You are not authorized to manage profiles, only Catalogue Editor can manipulate profiles.");
|
throw new NotAuthorizedException("You are not authorized to manage profiles, only Catalogue Editor can manipulate profiles.");
|
||||||
}
|
}
|
||||||
MetadataUtility metadataUtility = new MetadataUtility();
|
MetadataUtility metadataUtility = new MetadataUtility();
|
||||||
metadataUtility.validateProfile(xml);
|
metadataUtility.validateProfile(xml);
|
||||||
if(metadataUtility.getMetadataFormat(name) == null) {
|
|
||||||
|
MetadataFormat newMetadataFormat = getMetadataFormat(xml);
|
||||||
|
validateMetadataFormat(newMetadataFormat);
|
||||||
|
|
||||||
|
MetadataFormat oldMetadataFormat = metadataUtility.getMetadataFormat(name);
|
||||||
|
if(oldMetadataFormat == null) {
|
||||||
createGenericResource(name, xml);
|
createGenericResource(name, xml);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
package org.gcube.gcat.profile;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
|
||||||
|
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.DataCalogueMetadataFormatReader;
|
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.bean.MetadataProfile;
|
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.MetadataFormat;
|
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.NamespaceCategory;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
|
|
||||||
public class MetadataUtility {
|
|
||||||
|
|
||||||
private DataCalogueMetadataFormatReader dataCalogueMetadataFormatReader;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* this map contains the Metadata Profiles. The key is the name of the profile.
|
|
||||||
*/
|
|
||||||
private Map<String,MetadataProfile> metadataProfiles;
|
|
||||||
|
|
||||||
public MetadataUtility() throws Exception {
|
|
||||||
dataCalogueMetadataFormatReader = new DataCalogueMetadataFormatReader();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void validateProfile(String xmlProfile) throws ParserConfigurationException, SAXException, IOException {
|
|
||||||
dataCalogueMetadataFormatReader.validateProfile(xmlProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String,MetadataProfile> getMetadataProfiles() throws Exception {
|
|
||||||
if(metadataProfiles == null) {
|
|
||||||
metadataProfiles = new HashMap<>();
|
|
||||||
List<MetadataProfile> list = dataCalogueMetadataFormatReader.getListOfMetadataProfiles();
|
|
||||||
for(MetadataProfile profile : list) {
|
|
||||||
metadataProfiles.put(profile.getName(), profile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return metadataProfiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the names of the metadata profiles in a given context
|
|
||||||
* @return the set of profile names
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public Set<String> getProfilesNames() throws Exception {
|
|
||||||
return getMetadataProfiles().keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MetadataFormat getMetadataFormat(String profileName) throws Exception {
|
|
||||||
MetadataProfile profile = getMetadataProfiles().get(profileName);
|
|
||||||
if(profile != null) {
|
|
||||||
return dataCalogueMetadataFormatReader.getMetadataFormatForMetadataProfile(profile);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<NamespaceCategory> getNamespaceCategories() throws Exception {
|
|
||||||
return dataCalogueMetadataFormatReader.getListOfNamespaceCategories();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,285 @@
|
||||||
|
package org.gcube.gcat.profile;
|
||||||
|
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.ws.rs.InternalServerErrorException;
|
||||||
|
import javax.ws.rs.NotAuthorizedException;
|
||||||
|
import javax.ws.rs.NotFoundException;
|
||||||
|
import javax.ws.rs.WebApplicationException;
|
||||||
|
|
||||||
|
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import org.gcube.common.resources.gcore.GenericResource;
|
||||||
|
import org.gcube.common.resources.gcore.Resources;
|
||||||
|
import org.gcube.datacatalogue.metadatadiscovery.reader.MetadataFormatDiscovery;
|
||||||
|
import org.gcube.datacatalogue.metadatadiscovery.reader.QueryForResourceUtil;
|
||||||
|
import org.gcube.gcat.api.roles.Role;
|
||||||
|
import org.gcube.gcat.persistence.ckan.CKANUser;
|
||||||
|
import org.gcube.gcat.persistence.ckan.cache.CKANUserCache;
|
||||||
|
import org.gcube.gcat.utils.Constants;
|
||||||
|
import org.gcube.informationsystem.publisher.RegistryPublisher;
|
||||||
|
import org.gcube.informationsystem.publisher.RegistryPublisherFactory;
|
||||||
|
import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException;
|
||||||
|
import org.gcube.informationsystem.resourceregistry.client.ResourceRegistryClient;
|
||||||
|
import org.gcube.informationsystem.resourceregistry.client.ResourceRegistryClientFactory;
|
||||||
|
import org.gcube.resourcemanagement.model.impl.entities.facets.SchemaFacetImpl;
|
||||||
|
import org.gcube.resourcemanagement.model.impl.entities.facets.SimpleFacetImpl;
|
||||||
|
import org.gcube.resourcemanagement.model.impl.entities.resources.ConfigurationImpl;
|
||||||
|
import org.gcube.resourcemanagement.model.reference.entities.facets.IdentifierFacet;
|
||||||
|
import org.gcube.resourcemanagement.model.reference.entities.facets.SchemaFacet;
|
||||||
|
import org.gcube.resourcemanagement.model.reference.entities.facets.SimpleFacet;
|
||||||
|
import org.gcube.resourcemanagement.model.reference.entities.resources.Configuration;
|
||||||
|
import org.gcube.resourcemanagement.model.reference.relations.consistsof.IsIdentifiedBy;
|
||||||
|
import org.gcube.resources.discovery.client.api.DiscoveryClient;
|
||||||
|
import org.gcube.resources.discovery.client.queries.api.Query;
|
||||||
|
import org.gcube.resources.discovery.client.queries.impl.QueryBox;
|
||||||
|
import org.gcube.resources.discovery.icclient.ICFactory;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.json.XML;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
|
public class ResourceRegistryProfile {
|
||||||
|
|
||||||
|
public static int PRETTY_PRINT_INDENT_FACTOR = 4;
|
||||||
|
|
||||||
|
public static final String PROFILE = "profile";
|
||||||
|
public static final String PROFILE_SCHEMA = "profileSchema";
|
||||||
|
|
||||||
|
public static final String SCHEMA_URL_ADDRESS = "https://code-repo.d4science.org/gCubeSystem/gcubedatacatalogue-metadata-discovery/raw/branch/master/src/main/resources/org/gcube/datacatalogue/metadatadiscovery/Gdcmetadataprofilev3.xsd";
|
||||||
|
public static final URL SCHEMA_URL;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
SCHEMA_URL = new URL(SCHEMA_URL_ADDRESS);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(ResourceRegistryProfile.class);
|
||||||
|
|
||||||
|
protected ObjectMapper mapper;
|
||||||
|
|
||||||
|
public ResourceRegistryProfile() {
|
||||||
|
mapper = new ObjectMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectMapper getMapper() {
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayNode list() {
|
||||||
|
ArrayNode arrayNode = mapper.createArrayNode();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Set<String> names = (new MetadataUtility()).getProfilesNames();
|
||||||
|
for(String name : names) {
|
||||||
|
arrayNode.add(name);
|
||||||
|
}
|
||||||
|
return arrayNode;
|
||||||
|
} catch(Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* https://wiki.gcube-system.org/gcube/GCube_Model#Configuration
|
||||||
|
*/
|
||||||
|
protected Configuration instantiateConfiguration(String name, String xml) throws Exception {
|
||||||
|
Configuration configuration = new ConfigurationImpl();
|
||||||
|
|
||||||
|
/*
|
||||||
|
IdentifierFacet identifierFacet = new IdentifierFacetImpl();
|
||||||
|
identifierFacet.setValue(name);
|
||||||
|
identifierFacet.setType(IdentificationType.STRING);
|
||||||
|
IsIdentifiedBy<Configuration, IdentifierFacet> isIdentifiedBy = new IsIdentifiedByImpl<Configuration, IdentifierFacet>(configuration, identifierFacet);
|
||||||
|
configuration.addFacet(isIdentifiedBy);
|
||||||
|
*/
|
||||||
|
|
||||||
|
SimpleFacet profile = new SimpleFacetImpl();
|
||||||
|
profile.setAdditionalProperty(PROFILE, xml);
|
||||||
|
configuration.addFacet(profile);
|
||||||
|
|
||||||
|
SchemaFacet schemaFacet = new SchemaFacetImpl();
|
||||||
|
schemaFacet.setName("gCube Item Profile");
|
||||||
|
schemaFacet.setDescription("gCube Metadata Profile defines a Metadata schema XML-based for adding custom metadata fields.");
|
||||||
|
|
||||||
|
/*
|
||||||
|
ValueSchema valueSchema = new ValueSchemaImpl();
|
||||||
|
valueSchema.setSchema(SCHEMA_URL.toURI());
|
||||||
|
valueSchema.setValue(DataCalogueMetadataFormatReader.getProfileSchemaString());
|
||||||
|
schemaFacet.setSchema(valueSchema);
|
||||||
|
*/
|
||||||
|
|
||||||
|
configuration.addFacet(schemaFacet);
|
||||||
|
|
||||||
|
return configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected Configuration getConfiguration(String name) throws ResourceRegistryException {
|
||||||
|
ResourceRegistryClient resourceRegistryClient = ResourceRegistryClientFactory.create();
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map.put(IdentifierFacet.VALUE_PROPERTY, name);
|
||||||
|
List<Configuration> configurations = resourceRegistryClient.getFilteredResources(Configuration.class, IsIdentifiedBy.class, IdentifierFacet.class, false, map);
|
||||||
|
return configurations.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO Check the Queries because the name in the Profile differs from the name in
|
||||||
|
* <metadataformat type="Dataset">
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected GenericResource instantiateGenericResource(String name, String xml) throws Exception {
|
||||||
|
GenericResource genericResource = new GenericResource();
|
||||||
|
org.gcube.common.resources.gcore.GenericResource.Profile profile = genericResource.newProfile();
|
||||||
|
profile.type(MetadataFormatDiscovery.DATA_CATALOGUE_METADATA_SECONDARY_TYPE);
|
||||||
|
profile.name(name);
|
||||||
|
profile.description("Profile create using " + Constants.CATALOGUE_NAME);
|
||||||
|
// appendXmlFragment(profile, xml);
|
||||||
|
profile.newBody(xml);
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
Resources.marshal(genericResource, stringWriter);
|
||||||
|
logger.debug("The generated {} is\n{}", GenericResource.class.getSimpleName(), stringWriter.toString());
|
||||||
|
return genericResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createGenericResource(String name, String xml) throws Exception {
|
||||||
|
GenericResource genericResource = instantiateGenericResource(name, xml);
|
||||||
|
RegistryPublisher registryPublisher = RegistryPublisherFactory.create();
|
||||||
|
genericResource = registryPublisher.create(genericResource);
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
Resources.marshal(genericResource, stringWriter);
|
||||||
|
logger.trace("The {} with ID {} has been created \n{}", GenericResource.class.getSimpleName(),
|
||||||
|
genericResource.id(), stringWriter.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected GenericResource getGenericResource(String name) {
|
||||||
|
String query = QueryForResourceUtil.getGcubeGenericQueryStringForSecondaryTypeAndName(name,
|
||||||
|
MetadataFormatDiscovery.DATA_CATALOGUE_METADATA_SECONDARY_TYPE);
|
||||||
|
Query q = new QueryBox(query);
|
||||||
|
DiscoveryClient<GenericResource> client = ICFactory.clientFor(GenericResource.class);
|
||||||
|
List<GenericResource> resources = client.submit(q);
|
||||||
|
|
||||||
|
if(resources == null || resources.size() == 0) {
|
||||||
|
throw new InternalServerErrorException(
|
||||||
|
"No Resources with secondaryType '" + MetadataFormatDiscovery.DATA_CATALOGUE_METADATA_SECONDARY_TYPE
|
||||||
|
+ "' and name '" + name + "' exists in the current context");
|
||||||
|
} else {
|
||||||
|
if(resources.size() == 1) {
|
||||||
|
GenericResource genericResource = resources.get(0);
|
||||||
|
return genericResource;
|
||||||
|
} else {
|
||||||
|
throw new InternalServerErrorException("More than one Resource with secondaryType '"
|
||||||
|
+ MetadataFormatDiscovery.DATA_CATALOGUE_METADATA_SECONDARY_TYPE + "' and name '" + name
|
||||||
|
+ "' exists in the current context");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateGenericResource(String name, String xml) {
|
||||||
|
|
||||||
|
GenericResource genericResource = getGenericResource(name);
|
||||||
|
logger.info("The {} with ID {} is going to be updated", GenericResource.class.getSimpleName(),
|
||||||
|
genericResource.id());
|
||||||
|
|
||||||
|
genericResource.profile().newBody(xml);
|
||||||
|
RegistryPublisher registryPublisher = RegistryPublisherFactory.create();
|
||||||
|
registryPublisher.update(genericResource);
|
||||||
|
|
||||||
|
StringWriter stringWriter = new StringWriter();
|
||||||
|
Resources.marshal(genericResource, stringWriter);
|
||||||
|
logger.trace("The {} with ID {} has been updated to \n{}", GenericResource.class.getSimpleName(),
|
||||||
|
genericResource.id(), stringWriter.toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void removeGenericResource(String name) {
|
||||||
|
GenericResource genericResource = getGenericResource(name);
|
||||||
|
RegistryPublisher registryPublisher = RegistryPublisherFactory.create();
|
||||||
|
registryPublisher.remove(genericResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String read(String name, boolean xml) {
|
||||||
|
try {
|
||||||
|
String profile = (new MetadataUtility()).getMetadataFormat(name).getMetadataSource();
|
||||||
|
if(profile != null) {
|
||||||
|
if(xml) {
|
||||||
|
return profile;
|
||||||
|
} else {
|
||||||
|
JSONObject xmlJSONObj = XML.toJSONObject(profile);
|
||||||
|
String jsonString = xmlJSONObj.toString(PRETTY_PRINT_INDENT_FACTOR);
|
||||||
|
return jsonString;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new NotFoundException("Profile with name " + name + " not found");
|
||||||
|
}
|
||||||
|
} catch(WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
} catch(Exception e) {
|
||||||
|
throw new InternalServerErrorException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean createOrUpdate(String name, String xml) throws SAXException {
|
||||||
|
try {
|
||||||
|
CKANUser ckanUser = CKANUserCache.getCurrrentCKANUser();
|
||||||
|
if(ckanUser.getRole().ordinal()<Role.ADMIN.ordinal()) {
|
||||||
|
throw new NotAuthorizedException("You are not authorized to manage profiles, only Catalogue Editor can manipulate profiles.");
|
||||||
|
}
|
||||||
|
MetadataUtility metadataUtility = new MetadataUtility();
|
||||||
|
metadataUtility.validateProfile(xml);
|
||||||
|
if(metadataUtility.getMetadataFormat(name) == null) {
|
||||||
|
createGenericResource(name, xml);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
updateGenericResource(name, xml);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch(WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
} catch(SAXException e) {
|
||||||
|
throw e;
|
||||||
|
} catch(Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
} finally {
|
||||||
|
// TOOD Actually Cache has been removed. Remove the following code if it will not be re-introduced
|
||||||
|
// Cleaning the cache
|
||||||
|
// MetadataUtility.clearCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean delete(String name) {
|
||||||
|
try {
|
||||||
|
CKANUser ckanUser = CKANUserCache.getCurrrentCKANUser();
|
||||||
|
if(ckanUser.getRole().ordinal()<Role.EDITOR.ordinal()) {
|
||||||
|
throw new NotAuthorizedException("You are not authorized to manage profiles, only Catalogue Editor can manipulate profiles.");
|
||||||
|
}
|
||||||
|
MetadataUtility metadataUtility = new MetadataUtility();
|
||||||
|
if(metadataUtility.getMetadataFormat(name) == null) {
|
||||||
|
throw new NotFoundException("Profile with name " + name + " not found");
|
||||||
|
} else {
|
||||||
|
removeGenericResource(name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch(WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
} catch(Exception e) {
|
||||||
|
throw new InternalServerErrorException(e.getMessage());
|
||||||
|
} finally {
|
||||||
|
// Cleaning the cache
|
||||||
|
// MetadataUtility.clearCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,31 @@
|
||||||
package org.gcube.gcat.rest;
|
package org.gcube.gcat.rest;
|
||||||
|
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
import org.gcube.common.authorization.library.provider.CalledMethodProvider;
|
import org.gcube.common.authorization.library.provider.CalledMethodProvider;
|
||||||
|
import org.gcube.gcat.api.GCatConstants;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
|
@RequestHeaders ({
|
||||||
|
@RequestHeader( name = "Authorization", description = "Bearer token, see <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>")
|
||||||
|
})
|
||||||
public class BaseREST {
|
public class BaseREST {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected HttpHeaders httpHeaders;
|
||||||
|
|
||||||
@Context
|
@Context
|
||||||
protected UriInfo uriInfo;
|
protected UriInfo uriInfo;
|
||||||
|
|
||||||
|
@ -27,4 +41,14 @@ public class BaseREST {
|
||||||
String.format("%s/%s", uriInfo.getAbsolutePath().toString(), id));
|
String.format("%s/%s", uriInfo.getAbsolutePath().toString(), id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String createCountJson(int count) {
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder.append("{\"");
|
||||||
|
stringBuilder.append(GCatConstants.COUNT_KEY);
|
||||||
|
stringBuilder.append("\":");
|
||||||
|
stringBuilder.append(count);
|
||||||
|
stringBuilder.append("}");
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import javax.ws.rs.ext.Provider;
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
*/
|
*/
|
||||||
@Provider
|
@Provider
|
||||||
public class ScienceCatalogueExceptionMapper implements ExceptionMapper<Exception> {
|
public class GCatExceptionMapper implements ExceptionMapper<Exception> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response toResponse(Exception exception) {
|
public Response toResponse(Exception exception) {
|
|
@ -1,95 +0,0 @@
|
||||||
package org.gcube.gcat.rest;
|
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
|
||||||
import javax.ws.rs.DELETE;
|
|
||||||
import javax.ws.rs.DefaultValue;
|
|
||||||
import javax.ws.rs.GET;
|
|
||||||
import javax.ws.rs.POST;
|
|
||||||
import javax.ws.rs.PUT;
|
|
||||||
import javax.ws.rs.Path;
|
|
||||||
import javax.ws.rs.PathParam;
|
|
||||||
import javax.ws.rs.Produces;
|
|
||||||
import javax.ws.rs.QueryParam;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
|
|
||||||
import org.gcube.gcat.ResourceInitializer;
|
|
||||||
import org.gcube.gcat.annotation.PATCH;
|
|
||||||
import org.gcube.gcat.annotation.PURGE;
|
|
||||||
import org.gcube.gcat.api.GCatConstants;
|
|
||||||
import org.gcube.gcat.persistence.ckan.CKANGroup;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
@Path(Group.GROUPS)
|
|
||||||
public class Group extends REST<CKANGroup> implements org.gcube.gcat.api.interfaces.Group<Response,Response> {
|
|
||||||
|
|
||||||
protected static final String GROUP_ID_PARAMETER = "GROUP_ID";
|
|
||||||
|
|
||||||
public Group() {
|
|
||||||
super(GROUPS, GROUP_ID_PARAMETER, CKANGroup.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Override
|
|
||||||
public String list(@QueryParam(GCatConstants.LIMIT_PARAMETER) @DefaultValue("10") int limit,
|
|
||||||
@QueryParam(GCatConstants.OFFSET_PARAMETER) @DefaultValue("0") int offset) {
|
|
||||||
return super.list(limit, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Override
|
|
||||||
public Response create(String json) {
|
|
||||||
return super.create(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/{" + GROUP_ID_PARAMETER + "}")
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Override
|
|
||||||
public String read(@PathParam(GROUP_ID_PARAMETER) String id) {
|
|
||||||
return super.read(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PUT
|
|
||||||
@Path("/{" + GROUP_ID_PARAMETER + "}")
|
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Override
|
|
||||||
public String update(@PathParam(GROUP_ID_PARAMETER) String id, String json) {
|
|
||||||
return super.update(id, json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PATCH
|
|
||||||
@Path("/{" + GROUP_ID_PARAMETER + "}")
|
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Override
|
|
||||||
public String patch(@PathParam(GROUP_ID_PARAMETER) String id, String json) {
|
|
||||||
return super.patch(id, json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@DELETE
|
|
||||||
@Path("/{" + GROUP_ID_PARAMETER + "}")
|
|
||||||
@Override
|
|
||||||
public Response delete(@PathParam(GROUP_ID_PARAMETER) String id,
|
|
||||||
@QueryParam(GCatConstants.PURGE_QUERY_PARAMETER) @DefaultValue("false") Boolean purge) {
|
|
||||||
return super.delete(id, purge);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PURGE
|
|
||||||
@Path("/{" + GROUP_ID_PARAMETER + "}")
|
|
||||||
@Override
|
|
||||||
public Response purge(@PathParam(GROUP_ID_PARAMETER) String id) {
|
|
||||||
return delete(id, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response delete(String name, boolean purge) {
|
|
||||||
return delete(name, new Boolean(purge));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,101 +1,506 @@
|
||||||
package org.gcube.gcat.rest;
|
package org.gcube.gcat.rest;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
import javax.ws.rs.DefaultValue;
|
import javax.ws.rs.DefaultValue;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
//import javax.ws.rs.NotAuthorizedException;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.PUT;
|
import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||||
|
import javax.ws.rs.core.Response.Status;
|
||||||
import javax.xml.ws.WebServiceException;
|
import javax.xml.ws.WebServiceException;
|
||||||
|
|
||||||
import org.gcube.gcat.ResourceInitializer;
|
//import org.gcube.common.authorization.control.annotations.AuthorizationControl;
|
||||||
|
import org.gcube.gcat.annotation.PATCH;
|
||||||
import org.gcube.gcat.annotation.PURGE;
|
import org.gcube.gcat.annotation.PURGE;
|
||||||
import org.gcube.gcat.api.GCatConstants;
|
import org.gcube.gcat.api.GCatConstants;
|
||||||
|
//import org.gcube.gcat.api.moderation.Moderated;
|
||||||
|
//import org.gcube.gcat.api.roles.Role;
|
||||||
import org.gcube.gcat.persistence.ckan.CKANPackage;
|
import org.gcube.gcat.persistence.ckan.CKANPackage;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceLabel;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Item is a set of metadata to describe a concept.
|
||||||
|
* An Item can has one or more <a href="./resource_Resource.html">Resource</a> attached.
|
||||||
|
*
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
*/
|
*/
|
||||||
@Path(Item.ITEMS)
|
@Path(Item.ITEMS)
|
||||||
|
@ResourceGroup("Item APIs")
|
||||||
|
@ResourceLabel("Item APIs")
|
||||||
public class Item extends REST<CKANPackage> implements org.gcube.gcat.api.interfaces.Item<Response,Response> {
|
public class Item extends REST<CKANPackage> implements org.gcube.gcat.api.interfaces.Item<Response,Response> {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(Item.class);
|
||||||
|
|
||||||
public static final String ITEM_ID_PARAMETER = "ITEM_ID";
|
public static final String ITEM_ID_PARAMETER = "ITEM_ID";
|
||||||
|
|
||||||
|
protected String moderationMessage;
|
||||||
|
|
||||||
public Item() {
|
public Item() {
|
||||||
super(ITEMS, ITEM_ID_PARAMETER, CKANPackage.class);
|
super(ITEMS, ITEM_ID_PARAMETER, CKANPackage.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Not used as REST method, implemented to respect {@link org.gcube.gcat.api.interfaces.Item} interface
|
* <p>
|
||||||
|
* The listing API provides paginated results by using the query parameters limit and offset.<br/>
|
||||||
|
* It returns an array list of string containing the ids (i.e. names) of the items.<br/>
|
||||||
|
* Each name can be used as <code>{ITEM_ID}</code> path parameter to manage such item.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <h3>Filtering options</h3>
|
||||||
|
* <p>
|
||||||
|
* The listing method offers options to filter the results, thus enacting to search for items including spatial search (see ext_bbox below).<br/>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <h4>Basic Filtering options</h4>
|
||||||
|
* <dl>
|
||||||
|
* <dt>include_private (bool)</dt>
|
||||||
|
* <dd>
|
||||||
|
* <em>Optional</em>.<em>Default:false</em>.<br/>
|
||||||
|
* If True, private datasets will be included in the results.<br/>
|
||||||
|
* Only private datasets from the user’s organizations will be returned.
|
||||||
|
* For the sysadmins will be returned all private datasets.<br/>
|
||||||
|
* E.g. <code>/items?include_private=true</code>
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">ext_bbox</dt>
|
||||||
|
* <dd>
|
||||||
|
* <em>Optional</em>.<em>Default:null</em>.<br/>
|
||||||
|
* The coordinates of the upper-right and bottom-left angle of a rectangular to query for.
|
||||||
|
* The form is <code>Lat,Long,Lat,Long</code><br/>
|
||||||
|
* E.g. <code>/items?limit=10&offset=0&q=Pollution&ext_bbox=-7.535093,49.208494,3.890688,57.372349</code>
|
||||||
|
* returns the first 10 items with 'Pollution' having a spatial coverage in the specified bounding box.
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">own_only (bool)</dt>
|
||||||
|
* <dd>
|
||||||
|
* <em>Optional</em>.<em>Default:false</em>.<br/>
|
||||||
|
* If True, only the items of the requester user will be included in the result.<br/>
|
||||||
|
* E.g. <code>/items?limit=10&offset=0&own_only=true</code>
|
||||||
|
* </dd>
|
||||||
|
* </dl>
|
||||||
|
*
|
||||||
|
* <h4>Filtering options based on Solr query parameters</h4>
|
||||||
|
* <p>
|
||||||
|
* It accepts the following query parameters (a subset of Solr search query parameters, see {@see <a href="https://solrtutorial.com/solr-query-syntax.html">Solr Query Syntax</a>}):
|
||||||
|
* </p>
|
||||||
|
* <dl>
|
||||||
|
* <dt>q (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* <em>Optional</em>.<em>Default:"*:*"</em><br/>
|
||||||
|
* The solr query.<br/>
|
||||||
|
* E.g. <code>/items?q=title:foo</code> returns the items with word "foo" in the title.<br/>
|
||||||
|
* E.g. <code>/items?q=extras_systemtype:MyProfile</code> returns the items having the profile <em>MyProfile</em>
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">fq (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* <em>Optional</em>.<em>Default:null</em>.<br/>
|
||||||
|
* Filter query. A query string that limits the query results without influencing their scores.<br/>
|
||||||
|
* E.g. <code>/items?q=title:foo&fq=notes:bar</code> returns with word "foo" in the 'title' and the word "bar" in the 'notes'.
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">fq_list (list of strings)</dt>
|
||||||
|
* <dd>
|
||||||
|
* <em>Optional</em>.<em>Default:null</em>.<br/>
|
||||||
|
* Additional filter queries to apply.<br/>
|
||||||
|
* E.g. <code>/items?q=title:foo&fq_list=...</code> returns the items with word "foo" in the 'title'.
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">sort (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* <em>Optional</em>.<em>Default:"relevance asc, metadata_modified desc"</em>.<br/>
|
||||||
|
* Sorting of the search results.<br/>
|
||||||
|
* As per the solr documentation, this is a comma-separated string of field names and sort-orderings.<br/>
|
||||||
|
* E.g. <code>/items?q=title:foo&sort=name+asc</code> returns the items with word "foo" in the 'title'
|
||||||
|
* sorting the results by name ascending.
|
||||||
|
* </dd>
|
||||||
|
* <dl>
|
||||||
|
*
|
||||||
|
* <h4>Moderated Catalogue filtering options</h4>
|
||||||
|
* <dl>
|
||||||
|
* <dt>status (enum)</dt>
|
||||||
|
* <dd>
|
||||||
|
* <em>Optional</em>.<em>Default:null</em>.<br/>
|
||||||
|
*
|
||||||
|
* <span style="font-weight:bold;">It has sense only for moderated catalogues</span>.<br/>
|
||||||
|
* When no value is provided, it returns both the items which have been published before the activation of the moderation,
|
||||||
|
* as well as the items explicitly <code>approved</code> after the moderation activation.
|
||||||
|
*
|
||||||
|
* It can assume the following values:
|
||||||
|
* <ul>
|
||||||
|
* <li><code>pending</code>: it returns only the pending items, i.e. the item published by any allowed users and still not <span style="font-weight:bold;">Catalogue-Moderator</span>;</li>
|
||||||
|
* <li><code>rejected</code>: it returns only the rejected items, i.e. the item published by any allowed users and rejected by a <span style="font-weight:bold;">Catalogue-Moderator</span>;</li>
|
||||||
|
* <li><code>approved</code>: it returns only the approved items, i.e. the item published by any allowed users and approved by a <span style="font-weight:bold;">Catalogue-Moderator</span>.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Please note that only Catalogue-Moderators can filter all items by status.<br />
|
||||||
|
* Other users using this query parameter will get only its own items with such a status in the results.
|
||||||
|
* </p>
|
||||||
|
* </dd>
|
||||||
|
* </dl>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <h3>Query results options</h3>
|
||||||
|
* <p>
|
||||||
|
* The result is by default an array list of string containing the ids (i.e. names) of the items.
|
||||||
|
* Anyway, there are two options to get a different results.
|
||||||
|
* </p>
|
||||||
|
* <dl>
|
||||||
|
* <dt>count (bool)</dt>
|
||||||
|
* <dd>
|
||||||
|
* <em>Optional</em>.<em>Default:false</em>.<br/>
|
||||||
|
* If True, it indicates that the result must contains only the total number of items of the query.<br/>
|
||||||
|
* E.g. <code>/items?limit=10&offset=0&count=true</code>
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">all_fields (bool)</dt>
|
||||||
|
* <dd>
|
||||||
|
* <em>Optional</em>.<em>Default:false</em>.
|
||||||
|
* If True, the returned array list contains the whole item representation and not only the id (i.e. the name).</br/>
|
||||||
|
* E.g. <code>/items?limit=10&offset=0&all_fields=true</code>
|
||||||
|
* </dd>
|
||||||
|
* </dl>
|
||||||
|
* <p>
|
||||||
|
* Please note that, <code>count</code> query parameter has priority over <code>all_fields</code> query parameter.
|
||||||
|
* In other words, <code>all_fields</code> query parameter is not considered is <code>count</code> query parameter is true.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param limit (<em>Default:10</em>) To get unlimited results the limit query parameters must be set to -1.
|
||||||
|
* If the results are too much the operation could fail.
|
||||||
|
* It is recommended to request no more than 1000 results.
|
||||||
|
* @param offset (<em>Default:0</em>) The offset parameter indicates the starting position of the result.
|
||||||
|
* @return It returns an array list of string containing the ids (i.e. names) of the items.
|
||||||
|
* E.g.<pre>["item0","items1",...,"item10"]</pre>
|
||||||
|
*
|
||||||
|
* In the case the query parameter <code>count=true</code> it returns the total number of items of the query.
|
||||||
|
* E.g. <pre>{"count":148}</pre>
|
||||||
|
*
|
||||||
|
* In the case the query parameter <code>all_fields=true</code> each element of the resulting array contains the item representation:
|
||||||
|
* E.g.
|
||||||
|
* <pre>
|
||||||
|
* [
|
||||||
|
* {
|
||||||
|
* "name"="item0",
|
||||||
|
* ...,
|
||||||
|
* "private": false,
|
||||||
|
* "license_url": "http://www.opensource.org/licenses/AFL-3.0"
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* "name"="item1",
|
||||||
|
* ...,
|
||||||
|
* "private": true,
|
||||||
|
* "license_url": "http://www.opensource.org/licenses/AFL-3.0"
|
||||||
|
* },
|
||||||
|
* ...,
|
||||||
|
* {
|
||||||
|
* "name"="itemN",
|
||||||
|
* ...,
|
||||||
|
* "private": false,
|
||||||
|
* "license_url": "http://www.opensource.org/licenses/AFL-3.0"
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* ]</pre>
|
||||||
|
*
|
||||||
|
* @pathExample /items?limit=10&offset=0
|
||||||
|
* @responseExample application/json;charset=UTF-8 ["item0","items1",...,"item10"]
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Produces({GCatConstants.APPLICATION_JSON_CHARSET_UTF_8, GCatConstants.APPLICATION_JSON_API})
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode ( code = 200, condition = "The request succeeded.")
|
||||||
|
})
|
||||||
|
/* Catalogue-Member is not added to VRE members and is assumed as the default role in the catalogue for the VRE members. So we can't enforce
|
||||||
|
* @AuthorizationControl(allowedRoles={Role.CATALOGUE_MEMBER, Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String list(Map<String,String> parameters) throws WebServiceException {
|
public String list(@QueryParam(GCatConstants.LIMIT_QUERY_PARAMETER) @DefaultValue("10") int limit,
|
||||||
|
@QueryParam(GCatConstants.OFFSET_QUERY_PARAMETER) @DefaultValue("0") int offset) {
|
||||||
|
|
||||||
|
Boolean countOnly = false;
|
||||||
|
MultivaluedMap<String,String> queryParameters = uriInfo.getQueryParameters();
|
||||||
|
if(queryParameters.containsKey(GCatConstants.COUNT_QUERY_PARAMETER)) {
|
||||||
|
countOnly = Boolean.parseBoolean(queryParameters.get(GCatConstants.COUNT_QUERY_PARAMETER).get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
String ret = null;
|
||||||
|
|
||||||
|
if(countOnly) {
|
||||||
|
setCalledMethod("GET /" + COLLECTION_PARAMETER);
|
||||||
CKANPackage ckan = getInstance();
|
CKANPackage ckan = getInstance();
|
||||||
return ckan.list(parameters);
|
int size = ckan.count();
|
||||||
|
ret = createCountJson(size);
|
||||||
|
}else {
|
||||||
|
ret = super.list(limit, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
String accept = httpHeaders.getHeaderString("Accept");
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
if(accept!=null && accept.contains(GCatConstants.APPLICATION_JSON_API)) {
|
||||||
@Override
|
return resultAsJsonAPI(ret);
|
||||||
public String list(@QueryParam(GCatConstants.LIMIT_PARAMETER) @DefaultValue("10") int limit,
|
}
|
||||||
@QueryParam(GCatConstants.OFFSET_PARAMETER) @DefaultValue("0") int offset) {
|
return ret;
|
||||||
return super.list(limit, offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* The create API allows to create an item.
|
||||||
|
* An Item is mainly described by the following attributes (* indicate mandatory attributes):
|
||||||
|
*
|
||||||
|
* <dl>
|
||||||
|
* <dt>name* (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the name of the new item, must be between 2 and 100 characters long
|
||||||
|
* and contain only lowercase alphanumeric characters, '-' and '_';
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">title (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* If not specified it assumes the same value of <em>name</em> attribute.
|
||||||
|
* The title of the item;
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">maintainer (string)</dt>
|
||||||
|
* <dd>the name of the item’s maintainer;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">maintainer_email (string)</dt>
|
||||||
|
* <dd>the email address of the item’s maintainer;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">license_id* (string)</dt>
|
||||||
|
* <dd>the id of the item’s license, use <a href="resource_License.html">License listing</a> API to get available values;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">notes (string)</dt>
|
||||||
|
* <dd>a description of the item;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">url (string)</dt>
|
||||||
|
* <dd>a URL for the item’s source;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">version (string)</dt>
|
||||||
|
* <dd>must be between no longer than 100 characters</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">state (string, default='active')</dt>
|
||||||
|
* <dd>
|
||||||
|
* the current state of the item, e.g. 'active' or 'deleted', only active items show up in search results and other lists of items,
|
||||||
|
* this parameter will be ignored if you are not authorized to change the state of the item;
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">groups (list of dictionaries)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the groups to which the item belongs, each group dictionary should have one or more of the
|
||||||
|
* following keys which identify an existing group:
|
||||||
|
* 'id' (the id of the group, string), or 'name' (the name of the group, string).
|
||||||
|
* To see which groups exist use <a href="resource_Group.html#resource_Group_list_limit_offset_countOnly_GET">Group listing</a> API.</dd>
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">tags (list of tag dictionaries)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the item’s tags. The tag is a dictionary in the format:
|
||||||
|
* name : the name for the tag, i.e. a string between 2 and 100 characters long containing only alphanumeric characters and '-', '_' and '.'.
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">resources (list of resource dictionaries)</dt>
|
||||||
|
* <dd>the item’s resources, see <a href="resource_Resource.html">Resource collection</a> for the format of resource dictionaries;</dd>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">extras (list of item extra dictionaries)</dt>
|
||||||
|
* <dd>the item’s extras, extras are arbitrary (key: value) metadata items that can be added to items, each extra dictionary should have keys 'key' (a string), 'value' (a string);</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">owner_org (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the id of the item’s owning organization, see <code>supportedOrganizations</code> property in
|
||||||
|
* <a href="resource_Configuration.html#resource_Configuration_read_context_GET">Read Configuration</a>.
|
||||||
|
* The <code>defaultOrganization</code> is used if the author does not specify the organization.
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* </dl>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <h3>Parameter automatically managed:</h3>
|
||||||
|
* <dl>
|
||||||
|
*
|
||||||
|
* <dt>author (string)</dt>
|
||||||
|
* <dd>the name of the item’s author (the owner of the gcube-token);</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">author_email (string)</dt>
|
||||||
|
* <dd>the email address of the item’s author (the email of the owner of gcube-token);</dd>
|
||||||
|
*
|
||||||
|
* </dl>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <h3>Geo-Indexing your datasets:</h3>
|
||||||
|
* <p>
|
||||||
|
* In order to make an Item searchable by location, it must have a special extra, with its key named ‘spatial’.
|
||||||
|
* The value must be a valid GeoJSON geometry, for example:
|
||||||
|
* </p>
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "type":"Polygon",
|
||||||
|
* "coordinates":[[[2.05827, 49.8625],[2.05827, 55.7447], [-6.41736, 55.7447], [-6.41736, 49.8625], [2.05827, 49.8625]]]
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* or
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "type": "Point",
|
||||||
|
* "coordinates": [-3.145,53.078]
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <h3>Profile</h3>
|
||||||
|
* <p>
|
||||||
|
* If at least one profile has been defined within this context, then you need to specify the profile's
|
||||||
|
* type when creating the item.
|
||||||
|
* You need to insert, among the extras of the JSON object describing the item, a <code>system:type</code>
|
||||||
|
* property with one of the available profile, see <a href="resource_Profile.html#resource_Profile_listOrCount_countOnly_GET">List Profiles</a> API.
|
||||||
|
* The validation of the submitted request will be performed against the profile whose type has been specified.
|
||||||
|
* The profile's properties need to be specified within the extras field as well.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* If no profile has been defined, then no validation will be performed.
|
||||||
|
* Thus you do not need to set any <code>system:type</code> property.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <h3>Social Post</h3>
|
||||||
|
* <p>
|
||||||
|
* The user is going to crreate an item can indicate if he/she desires the creation of a social post
|
||||||
|
* to inform all the users of the VRE that he/she has created the item.</br>
|
||||||
|
* To request the social post he/she must indicate <code>social_post=true</code> query parameter.<br/>
|
||||||
|
* The <code>social_post</code> query parameter is optional and the default value is <code>false</code>.<br/>
|
||||||
|
* Please note that even the user indicates <code>social_post=true</code> the social post
|
||||||
|
* is create only and only if the Catalogue-Manager enabled this feature, see <code>socialPostEnabled</code> property in
|
||||||
|
* <a href="resource_Configuration.html#resource_Configuration_read_context_GET">Read Configuration</a>.
|
||||||
|
* When the social post is created the a notification to the VRe user is sent if the property
|
||||||
|
* <code>notificationToUsersEnabled</code> is true in the above mentioned configuration.
|
||||||
|
* If false <code>notificationToUsersEnabled=false</code> the social post will not produce a notification
|
||||||
|
* to the VRE user which will be only informed about the item pubblication by reading the generated
|
||||||
|
* social post in the VRE.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param json containing the item to be created
|
||||||
|
* @return <code>201 Created</code> HTTP Status to indicate that the item has been created.
|
||||||
|
* Some operation could continue in background such as the social post creation.
|
||||||
|
*
|
||||||
|
* @pathExample /items?social_post=true
|
||||||
|
* @requestExample application/json;charset=UTF-8 classpath:/api-docs-examples/item/create-item-request.json
|
||||||
|
* @responseExample application/json;charset=UTF-8 classpath:/api-docs-examples/item/create-item-response.json
|
||||||
|
*
|
||||||
|
*/
|
||||||
@POST
|
@POST
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode ( code = 201, condition = "The item has been created successfully.")
|
||||||
|
})
|
||||||
@Override
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
public Response create(String json) {
|
public Response create(String json) {
|
||||||
return super.create(json);
|
return super.create(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @pathExample /items/my_test_item
|
||||||
|
* @responseExample application/json;charset=UTF-8 classpath:/api-docs-examples/item/read-item-response.json
|
||||||
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Path("/{" + ITEM_ID_PARAMETER + "}")
|
@Path("/{" + ITEM_ID_PARAMETER + "}")
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode ( code = 200, condition = "The item exists.")
|
||||||
|
})
|
||||||
@Override
|
@Override
|
||||||
|
/* Catalogue-Member is not added to VRE members and is assumed as the default role in the catalogue for the VRE members. So we can't enforce
|
||||||
|
* @AuthorizationControl(allowedRoles={Role.CATALOGUE_MEMBER, Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
*/
|
||||||
public String read(@PathParam(ITEM_ID_PARAMETER) String id) {
|
public String read(@PathParam(ITEM_ID_PARAMETER) String id) {
|
||||||
return super.read(id);
|
return super.read(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any Catalogue-Admins or above is capable of modifying the authorship of the item.<br/>
|
||||||
|
* The service does not perform any checks on author information provided by the user.
|
||||||
|
*
|
||||||
|
* @pathExample /items/my_test_item
|
||||||
|
* @requestExample application/json;charset=UTF-8 classpath:/api-docs-examples/item/update-item-request.json
|
||||||
|
* @responseExample application/json;charset=UTF-8 classpath:/api-docs-examples/item/update-item-response.json
|
||||||
|
*/
|
||||||
@PUT
|
@PUT
|
||||||
@Path("/{" + ITEM_ID_PARAMETER + "}")
|
@Path("/{" + ITEM_ID_PARAMETER + "}")
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode ( code = 200, condition = "The item has been updated successfully.")
|
||||||
|
})
|
||||||
@Override
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
public String update(@PathParam(ITEM_ID_PARAMETER) String id, String json) {
|
public String update(@PathParam(ITEM_ID_PARAMETER) String id, String json) {
|
||||||
return super.update(id, json);
|
return super.update(id, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
|
* @pathExample /items/my_test_item
|
||||||
|
* @requestExample application/json;charset=UTF-8 classpath:/api-docs-examples/item/patch-item-request.json
|
||||||
|
* @responseExample application/json;charset=UTF-8 classpath:/api-docs-examples/item/patch-item-response.json
|
||||||
|
*/
|
||||||
@PATCH
|
@PATCH
|
||||||
@Path("/{" + ITEM_ID_PARAMETER + "}")
|
@Path("/{" + ITEM_ID_PARAMETER + "}")
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode ( code = 200, condition = "The item has been patched successfully.")
|
||||||
|
})
|
||||||
@Override
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
public String patch(@PathParam(ITEM_ID_PARAMETER) String id, String json) {
|
public String patch(@PathParam(ITEM_ID_PARAMETER) String id, String json) {
|
||||||
return super.patch(id, json);
|
return super.patch(id, json);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @pathExample /items/my_test_item
|
||||||
|
*/
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("/{" + ITEM_ID_PARAMETER + "}")
|
@Path("/{" + ITEM_ID_PARAMETER + "}")
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode ( code = 204, condition = "The item has been deleted successfully."),
|
||||||
|
@ResponseCode ( code = 404, condition = "The item was not found.")
|
||||||
|
})
|
||||||
@Override
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
public Response delete(@PathParam(ITEM_ID_PARAMETER) String id,
|
public Response delete(@PathParam(ITEM_ID_PARAMETER) String id,
|
||||||
@QueryParam(GCatConstants.PURGE_QUERY_PARAMETER) @DefaultValue("false") Boolean purge) {
|
@QueryParam(GCatConstants.PURGE_QUERY_PARAMETER) @DefaultValue("false") Boolean purge) {
|
||||||
return super.delete(id, purge);
|
return super.delete(id, purge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @pathExample /items/my_test_item
|
||||||
|
*/
|
||||||
@PURGE
|
@PURGE
|
||||||
@Path("/{" + ITEM_ID_PARAMETER + "}")
|
@Path("/{" + ITEM_ID_PARAMETER + "}")
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode ( code = 204, condition = "The item has been purged successfully."),
|
||||||
|
@ResponseCode ( code = 404, condition = "The item was not found.")
|
||||||
|
})
|
||||||
@Override
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
public Response purge(@PathParam(ITEM_ID_PARAMETER) String id) {
|
public Response purge(@PathParam(ITEM_ID_PARAMETER) String id) {
|
||||||
return super.purge(id);
|
return super.purge(id);
|
||||||
}
|
}
|
||||||
|
@ -105,4 +510,174 @@ public class Item extends REST<CKANPackage> implements org.gcube.gcat.api.interf
|
||||||
return delete(name, new Boolean(purge));
|
return delete(name, new Boolean(purge));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void deleteAll(boolean purge) {
|
||||||
|
Thread thread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
CKANPackage ckan = getInstance();
|
||||||
|
String ret = ckan.deleteAll(purge);
|
||||||
|
logger.info("Result of delete all is {}", ret);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* The bulk delete API provides a way to delete all the items matching certain criteria
|
||||||
|
* (see Filtering Listing options).
|
||||||
|
* The operation returns immediately to the client and continues in background.
|
||||||
|
* There is no way to monitor or stop the running operation.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* When invoked with no arguments, it deletes all the items of the invoking user in the default
|
||||||
|
* CKAN organization for the current context.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If a <em>Catalogue-Admin</em> or above specifies the query parameter <em>own_only=false</em>
|
||||||
|
* it deletes all the items of all users for the CKAN organization for the current context.
|
||||||
|
* The service ignores the query parameter <em>own_only=false</em> if the requesting user is not
|
||||||
|
* Catalogue-Admin or above.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p style="font-weight:bold;">
|
||||||
|
* Please check the result using the items listing APIs to verify what you will delete.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The deleted items are moved to the thrash by default.
|
||||||
|
* To completely remove the items (i.e. purge) the user can use the query parameter <em>purge=true</em>.
|
||||||
|
* <span style="font-weight:bold;">Please note that the purge action is not reversible.</span>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* By indicating the query parameter <em>purge=true</em> has the same result of using the bulk
|
||||||
|
* purge API using the <code>PURGE</code> HTTP Method.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param purge <em>(Default:false)</em> it completely removes all the items in the default CKAN organization.
|
||||||
|
* @return <code>202 Accepted</code> HTTP Status to indicate that the request has been properly take in charge.
|
||||||
|
* The operation will continue in background.
|
||||||
|
* @throws WebServiceException if an error occurs.
|
||||||
|
*
|
||||||
|
* @pathExample /items?purge=false
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@DELETE
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode ( code = 202, condition = "The bulk delete/purge has been accepted successfully.")
|
||||||
|
})
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response bulkDelete(@QueryParam(GCatConstants.PURGE_QUERY_PARAMETER) @DefaultValue("false") boolean purge) {
|
||||||
|
deleteAll(purge);
|
||||||
|
return Response.status(Status.ACCEPTED).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* The bulk purge API provides a way to completely remove all the items matching certain criteria
|
||||||
|
* (see Filtering Listing options). Please note that this action is not reversible.
|
||||||
|
* The operation returns immediately to the client and continues in background.
|
||||||
|
* There is no way to monitor or stop the running operation.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* When invoked with no arguments, it purges all the items of the invoking user in the default
|
||||||
|
* CKAN organization for the current context.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If a Catalogue-Admin or above specifies the query parameter <em>own_only=false</em>,
|
||||||
|
* it purges all the items of all users for the CKAN organization for the current context.
|
||||||
|
* The service ignores the query parameter <em>own_only=false</em> if the requesting user is not
|
||||||
|
* Catalogue-Admin or above.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p style="font-weight:bold;">
|
||||||
|
* Please check the result using the items listing APIs to verify what you will purge.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Invoking this API has the same result of using the bulk delete API using the <code>DELETE</code> HTTP Method
|
||||||
|
* with the query parameters <em>purge=true</em>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return <code>202 Accepted</code> HTTP Status to indicate that the request has been
|
||||||
|
* properly taken in charge.<br />
|
||||||
|
* The operation will continue in background.
|
||||||
|
*
|
||||||
|
* @throws WebServiceException if an error occurs.
|
||||||
|
*
|
||||||
|
* @pathExample /items
|
||||||
|
*/
|
||||||
|
@PURGE
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode ( code = 202, condition = "The bulk purge has been accepted successfully.")
|
||||||
|
})
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response bulkPurge() {
|
||||||
|
return bulkDelete(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param id the name of the item to moderate
|
||||||
|
* @param json It contains the moderation action to perform on the item.
|
||||||
|
*
|
||||||
|
* <p>The format is the following</p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "system:cm_item_status":"approved",
|
||||||
|
* "message": "..."
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The field <code>system:cm_item_status</code> can have
|
||||||
|
* the following values: <code>approved</code> or <code>rejected</code>, indicating the
|
||||||
|
* item's new status.<br />
|
||||||
|
* It is possible to send an optional message together with the new status by adding
|
||||||
|
* the field <code>message</code>.<br />
|
||||||
|
* This operation is available for Catalogue-Moderator only.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Catalogue-Moderator and item author can send just a message to discuss
|
||||||
|
* the approval of the item by indicating the field <code>message</code> only.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return <code>202 Accepted</code> HTTP Status to indicate that the request has been
|
||||||
|
* properly taken in charge.<br />
|
||||||
|
* The operation will continue in background.
|
||||||
|
*
|
||||||
|
* @pathExample /items/my_test_item
|
||||||
|
* @requestExample application/json;charset=UTF-8 classpath:/api-docs-examples/item/reject.json
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/{" + ITEM_ID_PARAMETER + "}")
|
||||||
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode ( code = 202, condition = "The moderation operation has been accepted successfully.")
|
||||||
|
})
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER, Moderated.CATALOGUE_MODERATOR}, exception=NotAuthorizedException.class)
|
||||||
|
public Response moderate(@PathParam(ITEM_ID_PARAMETER) String id, String json) {
|
||||||
|
setCalledMethod("POST /" + COLLECTION_PARAMETER + "/{" + ID_PARAMETER + "}");
|
||||||
|
CKANPackage ckanPackage = getInstance();
|
||||||
|
ckanPackage.setName(id);
|
||||||
|
String ret = ckanPackage.moderate(json);
|
||||||
|
ResponseBuilder responseBuilder = Response.status(Status.ACCEPTED);
|
||||||
|
if(ret!=null) {
|
||||||
|
responseBuilder.entity(ret).type(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8);
|
||||||
|
}
|
||||||
|
return responseBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,24 +3,47 @@ package org.gcube.gcat.rest;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.xml.ws.WebServiceException;
|
||||||
|
|
||||||
import org.gcube.gcat.ResourceInitializer;
|
import org.gcube.gcat.api.GCatConstants;
|
||||||
import org.gcube.gcat.persistence.ckan.CKANLicense;
|
import org.gcube.gcat.persistence.ckan.CKANLicense;
|
||||||
|
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceLabel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A collection to interact with licenses that are available in the catalogue.
|
||||||
|
* A license is associated with items to define the legal right
|
||||||
|
* to use such an item.
|
||||||
|
*
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
*/
|
*/
|
||||||
@Path(License.LICENSES)
|
@Path(License.LICENSES)
|
||||||
|
@ResourceGroup("Item Related APIs")
|
||||||
|
@ResourceLabel("License APIs")
|
||||||
public class License extends REST<CKANLicense> implements org.gcube.gcat.api.interfaces.License {
|
public class License extends REST<CKANLicense> implements org.gcube.gcat.api.interfaces.License {
|
||||||
|
|
||||||
public License() {
|
public License() {
|
||||||
super(LICENSES, null, CKANLicense.class);
|
super(LICENSES, null, CKANLicense.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the json array containing the licenses available in the catalogue
|
||||||
|
*
|
||||||
|
* @pathExample /licenses
|
||||||
|
* @responseExample application/json classpath:/api-docs-examples/license/list-license-response.json
|
||||||
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Produces({MediaType.APPLICATION_JSON,GCatConstants.APPLICATION_JSON_API})
|
||||||
public String list() {
|
public String list() throws WebServiceException {
|
||||||
return super.list(-1, -1);
|
String ret = super.list(-1, -1);
|
||||||
|
|
||||||
|
String accept = httpHeaders.getHeaderString("Accept");
|
||||||
|
if(accept!=null && accept.contains(GCatConstants.APPLICATION_JSON_API)) {
|
||||||
|
return resultAsJsonAPI(ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,23 +6,35 @@ import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
import javax.ws.rs.InternalServerErrorException;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.NamespaceCategory;
|
import org.gcube.datacatalogue.metadatadiscovery.bean.jaxb.NamespaceCategory;
|
||||||
import org.gcube.gcat.ResourceInitializer;
|
|
||||||
import org.gcube.gcat.profile.MetadataUtility;
|
import org.gcube.gcat.profile.MetadataUtility;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.webcohesion.enunciate.metadata.rs.ResourceLabel;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A namespace defines a logical grouping for metadata contained in items.
|
||||||
|
*
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
*/
|
*/
|
||||||
@Path(Namespace.NAMESPACES)
|
@Path(Namespace.NAMESPACES)
|
||||||
|
@ResourceGroup("Item Related APIs")
|
||||||
|
@ResourceLabel("Namespace APIs")
|
||||||
public class Namespace extends BaseREST implements org.gcube.gcat.api.interfaces.Namespace {
|
public class Namespace extends BaseREST implements org.gcube.gcat.api.interfaces.Namespace {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the json array containing the licenses available in the catalogue
|
||||||
|
*
|
||||||
|
* @pathExample /namespaces
|
||||||
|
* @responseExample application/json classpath:/api-docs-examples/namespace/list-namespace-response.json
|
||||||
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public String list() {
|
public String list() {
|
||||||
setCalledMethod("GET /" + NAMESPACES);
|
setCalledMethod("GET /" + NAMESPACES);
|
||||||
|
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
package org.gcube.gcat.rest;
|
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
|
||||||
import javax.ws.rs.DELETE;
|
|
||||||
import javax.ws.rs.DefaultValue;
|
|
||||||
import javax.ws.rs.GET;
|
|
||||||
import javax.ws.rs.POST;
|
|
||||||
import javax.ws.rs.PUT;
|
|
||||||
import javax.ws.rs.Path;
|
|
||||||
import javax.ws.rs.PathParam;
|
|
||||||
import javax.ws.rs.Produces;
|
|
||||||
import javax.ws.rs.QueryParam;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
|
|
||||||
import org.gcube.gcat.ResourceInitializer;
|
|
||||||
import org.gcube.gcat.annotation.PATCH;
|
|
||||||
import org.gcube.gcat.annotation.PURGE;
|
|
||||||
import org.gcube.gcat.api.GCatConstants;
|
|
||||||
import org.gcube.gcat.persistence.ckan.CKANOrganization;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
@Path(Organization.ORGANIZATIONS)
|
|
||||||
public class Organization extends REST<CKANOrganization>
|
|
||||||
implements org.gcube.gcat.api.interfaces.Organization<Response,Response> {
|
|
||||||
|
|
||||||
public static final String ORGANIZATION_ID_PARAMETER = "ORGANIZATION_ID";
|
|
||||||
|
|
||||||
public Organization() {
|
|
||||||
super(ORGANIZATIONS, ORGANIZATION_ID_PARAMETER, CKANOrganization.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Override
|
|
||||||
public String list(@QueryParam(GCatConstants.LIMIT_PARAMETER) @DefaultValue("10") int limit,
|
|
||||||
@QueryParam(GCatConstants.OFFSET_PARAMETER) @DefaultValue("0") int offset) {
|
|
||||||
return super.list(limit, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Override
|
|
||||||
public Response create(String json) {
|
|
||||||
return super.create(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/{" + ORGANIZATION_ID_PARAMETER + "}")
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Override
|
|
||||||
public String read(@PathParam(ORGANIZATION_ID_PARAMETER) String id) {
|
|
||||||
return super.read(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PUT
|
|
||||||
@Path("/{" + ORGANIZATION_ID_PARAMETER + "}")
|
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Override
|
|
||||||
public String update(@PathParam(ORGANIZATION_ID_PARAMETER) String id, String json) {
|
|
||||||
return super.update(id, json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PATCH
|
|
||||||
@Path("/{" + ORGANIZATION_ID_PARAMETER + "}")
|
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Override
|
|
||||||
public String patch(@PathParam(ORGANIZATION_ID_PARAMETER) String id, String json) {
|
|
||||||
return super.patch(id, json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@DELETE
|
|
||||||
@Path("/{" + ORGANIZATION_ID_PARAMETER + "}")
|
|
||||||
@Override
|
|
||||||
public Response delete(@PathParam(ORGANIZATION_ID_PARAMETER) String id,
|
|
||||||
@QueryParam(GCatConstants.PURGE_QUERY_PARAMETER) @DefaultValue("false") Boolean purge) {
|
|
||||||
return super.delete(id, purge);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PURGE
|
|
||||||
@Path("/{" + ORGANIZATION_ID_PARAMETER + "}")
|
|
||||||
public Response purge(@PathParam(ORGANIZATION_ID_PARAMETER) String id) {
|
|
||||||
return super.purge(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -5,33 +5,42 @@ import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
import javax.ws.rs.DefaultValue;
|
import javax.ws.rs.DefaultValue;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.HeaderParam;
|
|
||||||
import javax.ws.rs.HttpMethod;
|
import javax.ws.rs.HttpMethod;
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
import javax.ws.rs.InternalServerErrorException;
|
||||||
import javax.ws.rs.NotAllowedException;
|
import javax.ws.rs.NotAllowedException;
|
||||||
|
//import javax.ws.rs.NotAuthorizedException;
|
||||||
import javax.ws.rs.PUT;
|
import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.WebApplicationException;
|
import javax.ws.rs.WebApplicationException;
|
||||||
import javax.ws.rs.core.Context;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
import javax.ws.rs.core.UriInfo;
|
|
||||||
|
|
||||||
|
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
//import org.gcube.common.authorization.control.annotations.AuthorizationControl;
|
||||||
import org.gcube.datacatalogue.metadatadiscovery.DataCalogueMetadataFormatReader;
|
import org.gcube.datacatalogue.metadatadiscovery.DataCalogueMetadataFormatReader;
|
||||||
import org.gcube.gcat.ResourceInitializer;
|
import org.gcube.gcat.api.GCatConstants;
|
||||||
|
//import org.gcube.gcat.api.roles.Role;
|
||||||
import org.gcube.gcat.profile.ISProfile;
|
import org.gcube.gcat.profile.ISProfile;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceLabel;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A profile defines a type and the schema any item of such a type must comply with.
|
||||||
|
*
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
*/
|
*/
|
||||||
@Path(Profile.PROFILES)
|
@Path(Profile.PROFILES)
|
||||||
|
@ResourceGroup("Item Related APIs")
|
||||||
|
@ResourceLabel("Profile APIs")
|
||||||
public class Profile extends BaseREST implements org.gcube.gcat.api.interfaces.Profile<Response,Response> {
|
public class Profile extends BaseREST implements org.gcube.gcat.api.interfaces.Profile<Response,Response> {
|
||||||
|
|
||||||
public static final String PROFILE_NAME_PARAMETER = "PROFILE_NAME";
|
public static final String PROFILE_NAME_PARAMETER = "PROFILE_NAME";
|
||||||
|
@ -50,13 +59,58 @@ public class Profile extends BaseREST implements org.gcube.gcat.api.interfaces.P
|
||||||
PROFILE_VALIDATION_ERROR = validationError.toString();
|
PROFILE_VALIDATION_ERROR = validationError.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Context
|
/*
|
||||||
private UriInfo uriInfo;
|
* Not used as REST method, implemented to respect {@link org.gcube.gcat.api.interfaces.Item} interface
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int count() {
|
||||||
|
setCalledMethod("GET /" + PROFILES);
|
||||||
|
try {
|
||||||
|
ISProfile isProfile = new ISProfile();
|
||||||
|
return isProfile.count();
|
||||||
|
} catch(WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
} catch(Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of profiles name available for the
|
||||||
|
* context of the request
|
||||||
|
* (i.e. the context where the token has been generated).
|
||||||
|
*
|
||||||
|
* @param count (<em>Default:false</em>) If <code>count=true</code> the API returns total number of profile instead of the list.
|
||||||
|
* @return a JSON Array.
|
||||||
|
*
|
||||||
|
* @pathExample /profiles
|
||||||
|
* @responseExample application/json ["EmptyProfile","TestProfile","ComplexProfile"]
|
||||||
|
*
|
||||||
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public String list() {
|
public String listOrCount(@QueryParam(GCatConstants.COUNT_QUERY_PARAMETER) @DefaultValue("false") Boolean count) {
|
||||||
setCalledMethod("GET /" + PROFILES);
|
setCalledMethod("GET /" + PROFILES);
|
||||||
|
try {
|
||||||
|
ISProfile isProfile = new ISProfile();
|
||||||
|
if(count) {
|
||||||
|
return createCountJson(isProfile.count());
|
||||||
|
}else{
|
||||||
|
ArrayNode arrayNode = isProfile.list();
|
||||||
|
return isProfile.getMapper().writeValueAsString(arrayNode);
|
||||||
|
}
|
||||||
|
} catch(WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
} catch(Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Not used as REST method, implemented to respect {@link org.gcube.gcat.api.interfaces.Item} interface
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String list() {
|
||||||
try {
|
try {
|
||||||
ISProfile isProfile = new ISProfile();
|
ISProfile isProfile = new ISProfile();
|
||||||
ArrayNode arrayNode = isProfile.list();
|
ArrayNode arrayNode = isProfile.list();
|
||||||
|
@ -70,11 +124,32 @@ public class Profile extends BaseREST implements org.gcube.gcat.api.interfaces.P
|
||||||
|
|
||||||
public static int PRETTY_PRINT_INDENT_FACTOR = 4;
|
public static int PRETTY_PRINT_INDENT_FACTOR = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This API allow to read a profile definition.<br/>
|
||||||
|
*
|
||||||
|
* This API return by default the content in XML.
|
||||||
|
* <p>
|
||||||
|
* It is possible reading a profile in JSON by specifing the HTTP Header:<br/>
|
||||||
|
* <code>Accept: application/json</code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param name the name of the profile
|
||||||
|
* @return the profile definition
|
||||||
|
*
|
||||||
|
* @pathExample /profiles/EmptyProfile
|
||||||
|
* @responseExample application/xml classpath:/api-docs-examples/profile/read-profile-response.xml
|
||||||
|
*
|
||||||
|
* @pathExample /profiles/EmptyProfile
|
||||||
|
* @responseExample application/json classpath:/api-docs-examples/profile/read-profile-response.json
|
||||||
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Path("/{" + PROFILE_NAME_PARAMETER + "}")
|
@Path("/{" + PROFILE_NAME_PARAMETER + "}")
|
||||||
@Produces({MediaType.APPLICATION_XML, ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8})
|
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
|
||||||
public String read(@PathParam(PROFILE_NAME_PARAMETER) String name,
|
@StatusCodes ({
|
||||||
@DefaultValue(MediaType.APPLICATION_XML) @HeaderParam("Accept") String accept) {
|
@ResponseCode ( code = 200, condition = "The profile exists.")
|
||||||
|
})
|
||||||
|
@Override
|
||||||
|
public String read(@PathParam(PROFILE_NAME_PARAMETER) String name) {
|
||||||
setCalledMethod("GET /" + PROFILES + "/{" + PROFILE_NAME_PARAMETER + "}");
|
setCalledMethod("GET /" + PROFILES + "/{" + PROFILE_NAME_PARAMETER + "}");
|
||||||
try {
|
try {
|
||||||
// If the name is SCHEMA
|
// If the name is SCHEMA
|
||||||
|
@ -82,10 +157,12 @@ public class Profile extends BaseREST implements org.gcube.gcat.api.interfaces.P
|
||||||
return DataCalogueMetadataFormatReader.getProfileSchemaString();
|
return DataCalogueMetadataFormatReader.getProfileSchemaString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String accept = httpHeaders.getHeaderString("Accept");
|
||||||
|
|
||||||
ISProfile isProfile = new ISProfile();
|
ISProfile isProfile = new ISProfile();
|
||||||
boolean xml = false;
|
boolean xml = true;
|
||||||
if(accept.startsWith(MediaType.APPLICATION_XML)) {
|
if(accept.startsWith(MediaType.APPLICATION_JSON)) {
|
||||||
xml = true;
|
xml = false;
|
||||||
}
|
}
|
||||||
return isProfile.read(name, xml);
|
return isProfile.read(name, xml);
|
||||||
} catch(WebApplicationException e) {
|
} catch(WebApplicationException e) {
|
||||||
|
@ -95,10 +172,21 @@ public class Profile extends BaseREST implements org.gcube.gcat.api.interfaces.P
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A profile must comply with the defined <a href="../profiles/SCHEMA">XSD schema</a> .
|
||||||
|
*
|
||||||
|
* Please find the documentation of profile schema at:
|
||||||
|
* at <a href="https://wiki.gcube-system.org/gcube/GCat_Background#Metadata_Profile_v.4">Metadata Profile</>
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @param xml
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@PUT
|
@PUT
|
||||||
@Path("/{" + PROFILE_NAME_PARAMETER + "}")
|
@Path("/{" + PROFILE_NAME_PARAMETER + "}")
|
||||||
@Consumes(MediaType.APPLICATION_XML)
|
@Consumes(MediaType.APPLICATION_XML)
|
||||||
@Produces(MediaType.APPLICATION_XML)
|
@Produces(MediaType.APPLICATION_XML)
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
public Response createOrUpdate(@PathParam(PROFILE_NAME_PARAMETER) String name, String xml) {
|
public Response createOrUpdate(@PathParam(PROFILE_NAME_PARAMETER) String name, String xml) {
|
||||||
setCalledMethod("PUT /" + PROFILES + "/{" + PROFILE_NAME_PARAMETER + "}");
|
setCalledMethod("PUT /" + PROFILES + "/{" + PROFILE_NAME_PARAMETER + "}");
|
||||||
try {
|
try {
|
||||||
|
@ -129,6 +217,7 @@ public class Profile extends BaseREST implements org.gcube.gcat.api.interfaces.P
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("/{" + PROFILE_NAME_PARAMETER + "}")
|
@Path("/{" + PROFILE_NAME_PARAMETER + "}")
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
public Response delete(@PathParam(PROFILE_NAME_PARAMETER) String name) {
|
public Response delete(@PathParam(PROFILE_NAME_PARAMETER) String name) {
|
||||||
setCalledMethod("DELETE /" + PROFILES + "/{" + PROFILE_NAME_PARAMETER + "}");
|
setCalledMethod("DELETE /" + PROFILES + "/{" + PROFILE_NAME_PARAMETER + "}");
|
||||||
try {
|
try {
|
||||||
|
@ -152,11 +241,6 @@ public class Profile extends BaseREST implements org.gcube.gcat.api.interfaces.P
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String read(String name) {
|
|
||||||
return read(name, MediaType.APPLICATION_XML);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String update(String name, String xml) {
|
public String update(String name, String xml) {
|
||||||
return createOrUpdate(name, xml).getEntity().toString();
|
return createOrUpdate(name, xml).getEntity().toString();
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
package org.gcube.gcat.rest;
|
package org.gcube.gcat.rest;
|
||||||
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
import javax.ws.rs.InternalServerErrorException;
|
||||||
|
import javax.ws.rs.WebApplicationException;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
|
||||||
import org.gcube.gcat.ResourceInitializer;
|
import org.gcube.gcat.api.GCatConstants;
|
||||||
import org.gcube.gcat.api.interfaces.CRUD;
|
import org.gcube.gcat.api.interfaces.CRUD;
|
||||||
import org.gcube.gcat.persistence.ckan.CKAN;
|
import org.gcube.gcat.persistence.ckan.CKAN;
|
||||||
|
import org.gcube.gcat.persistence.ckan.CKANPackage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
public class REST<C extends CKAN> extends BaseREST implements CRUD<Response,Response> {
|
public class REST<C extends CKAN> extends BaseREST implements CRUD<Response,Response> {
|
||||||
|
|
||||||
|
protected static final String CONTENT_LOCATION_HEADER = "Content-Location";
|
||||||
|
|
||||||
protected final String COLLECTION_PARAMETER;
|
protected final String COLLECTION_PARAMETER;
|
||||||
protected final String ID_PARAMETER;
|
protected final String ID_PARAMETER;
|
||||||
protected final Class<C> reference;
|
protected final Class<C> reference;
|
||||||
|
@ -26,8 +33,10 @@ public class REST<C extends CKAN> extends BaseREST implements CRUD<Response,Resp
|
||||||
C ckan = reference.newInstance();
|
C ckan = reference.newInstance();
|
||||||
ckan.setUriInfo(uriInfo);
|
ckan.setUriInfo(uriInfo);
|
||||||
return ckan;
|
return ckan;
|
||||||
|
} catch (WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
throw new InternalServerErrorException();
|
throw new InternalServerErrorException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +46,10 @@ public class REST<C extends CKAN> extends BaseREST implements CRUD<Response,Resp
|
||||||
return ckan.list(limit, offset);
|
return ckan.list(limit, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ResponseBuilder addContentLocation(ResponseBuilder responseBuilder, String url) {
|
||||||
|
return responseBuilder.header(CONTENT_LOCATION_HEADER, url);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response create(String json) {
|
public Response create(String json) {
|
||||||
setCalledMethod("POST /" + COLLECTION_PARAMETER);
|
setCalledMethod("POST /" + COLLECTION_PARAMETER);
|
||||||
|
@ -45,7 +58,10 @@ public class REST<C extends CKAN> extends BaseREST implements CRUD<Response,Resp
|
||||||
|
|
||||||
ResponseBuilder responseBuilder = Response.status(Status.CREATED).entity(ret);
|
ResponseBuilder responseBuilder = Response.status(Status.CREATED).entity(ret);
|
||||||
responseBuilder = addLocation(responseBuilder, ckan.getName());
|
responseBuilder = addLocation(responseBuilder, ckan.getName());
|
||||||
return responseBuilder.type(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8).build();
|
if(ckan instanceof CKANPackage) {
|
||||||
|
addContentLocation(responseBuilder, ((CKANPackage) ckan).getRecordURL());
|
||||||
|
}
|
||||||
|
return responseBuilder.type(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -92,4 +108,12 @@ public class REST<C extends CKAN> extends BaseREST implements CRUD<Response,Resp
|
||||||
return delete(id, true);
|
return delete(id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String resultAsJsonAPI(String data) {
|
||||||
|
StringBuffer stringBuffer = new StringBuffer();
|
||||||
|
stringBuffer.append("{ \"data\":");
|
||||||
|
stringBuffer.append(data);
|
||||||
|
stringBuffer.append("}");
|
||||||
|
return stringBuffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
package org.gcube.gcat.rest;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
|
import javax.ws.rs.container.ContainerRequestFilter;
|
||||||
|
import javax.ws.rs.container.ContainerResponseContext;
|
||||||
|
import javax.ws.rs.container.ContainerResponseFilter;
|
||||||
|
import javax.ws.rs.container.PreMatching;
|
||||||
|
import javax.ws.rs.ext.Provider;
|
||||||
|
|
||||||
|
import org.gcube.common.authorization.library.provider.AccessTokenProvider;
|
||||||
|
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
|
||||||
|
import org.gcube.common.authorization.utils.manager.SecretManager;
|
||||||
|
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
|
||||||
|
import org.gcube.common.authorization.utils.secret.GCubeSecret;
|
||||||
|
import org.gcube.common.authorization.utils.secret.JWTSecret;
|
||||||
|
import org.gcube.common.authorization.utils.secret.Secret;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
|
@Provider
|
||||||
|
@PreMatching
|
||||||
|
public class RequestFilter implements ContainerRequestFilter, ContainerResponseFilter {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(RequestFilter.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filter(ContainerRequestContext requestContext) throws IOException {
|
||||||
|
logger.trace("PreMatching RequestFilter");
|
||||||
|
|
||||||
|
SecretManagerProvider.instance.remove();
|
||||||
|
SecretManager secretManager = new SecretManager();
|
||||||
|
|
||||||
|
String token = AccessTokenProvider.instance.get();
|
||||||
|
if(token!=null) {
|
||||||
|
Secret secret = new JWTSecret(token);
|
||||||
|
secretManager.addSecret(secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
token = SecurityTokenProvider.instance.get();
|
||||||
|
if(token!=null) {
|
||||||
|
Secret secret = new GCubeSecret(token);
|
||||||
|
secretManager.addSecret(secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
SecretManagerProvider.instance.set(secretManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
|
||||||
|
throws IOException {
|
||||||
|
logger.trace("ResponseFilter");
|
||||||
|
SecretManagerProvider.instance.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package org.gcube.gcat.rest;
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
//import javax.ws.rs.NotAuthorizedException;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.PUT;
|
import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
@ -12,14 +13,24 @@ import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
|
||||||
import org.gcube.gcat.ResourceInitializer;
|
//import org.gcube.common.authorization.control.annotations.AuthorizationControl;
|
||||||
import org.gcube.gcat.annotation.PATCH;
|
import org.gcube.gcat.annotation.PATCH;
|
||||||
|
import org.gcube.gcat.api.GCatConstants;
|
||||||
|
//import org.gcube.gcat.api.roles.Role;
|
||||||
import org.gcube.gcat.persistence.ckan.CKANResource;
|
import org.gcube.gcat.persistence.ckan.CKANResource;
|
||||||
|
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceLabel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* An Item's Resource is an URL (e.g. URL pointing to a file)
|
||||||
|
* and can exists only attached to an <a href="./resource_Item.html">item</a>.
|
||||||
|
*
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
*/
|
*/
|
||||||
@Path(Resource.COLLECTION)
|
@Path(Resource.COLLECTION)
|
||||||
|
@ResourceGroup("Item APIs")
|
||||||
|
@ResourceLabel("Item's Resource APIs")
|
||||||
public class Resource extends BaseREST implements org.gcube.gcat.api.interfaces.Resource<Response,Response> {
|
public class Resource extends BaseREST implements org.gcube.gcat.api.interfaces.Resource<Response,Response> {
|
||||||
|
|
||||||
protected static final String ITEM_ID_PARAMETER = Item.ITEM_ID_PARAMETER;
|
protected static final String ITEM_ID_PARAMETER = Item.ITEM_ID_PARAMETER;
|
||||||
|
@ -28,7 +39,10 @@ public class Resource extends BaseREST implements org.gcube.gcat.api.interfaces.
|
||||||
protected static final String COLLECTION = Item.ITEMS + "/{" + ITEM_ID_PARAMETER + "}/" + RESOURCES;
|
protected static final String COLLECTION = Item.ITEMS + "/{" + ITEM_ID_PARAMETER + "}/" + RESOURCES;
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
/* Catalogue-Member is not added to VRE members and is assumed as the default role in the catalogue for the VRE members. So we can't enforce
|
||||||
|
* @AuthorizationControl(allowedRoles={Role.CATALOGUE_MEMBER, Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
*/
|
||||||
public String list(@PathParam(ITEM_ID_PARAMETER) String itemID) {
|
public String list(@PathParam(ITEM_ID_PARAMETER) String itemID) {
|
||||||
setCalledMethod("GET /" + COLLECTION);
|
setCalledMethod("GET /" + COLLECTION);
|
||||||
CKANResource ckanResource = new CKANResource(itemID);
|
CKANResource ckanResource = new CKANResource(itemID);
|
||||||
|
@ -36,9 +50,43 @@ public class Resource extends BaseREST implements org.gcube.gcat.api.interfaces.
|
||||||
return ckanResource.list();
|
return ckanResource.list();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Resource is mainly described by the following attributes (* indicate mandatory attributes):
|
||||||
|
*
|
||||||
|
* <dl>
|
||||||
|
* <dt>name* (string)</dt>
|
||||||
|
* <dd>the name of the resource;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">url* (string)</dt>
|
||||||
|
* <dd>url of resource;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">package_id* (string)</dt>
|
||||||
|
* <dd>id of the item that the resource should be added to;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">revision_id (string)</dt>
|
||||||
|
* <dd></dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">description (string)</dt>
|
||||||
|
* <dd>a description for the resource;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">format (string)</dt>
|
||||||
|
* <dd></dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">mimetype (string)</dt>
|
||||||
|
* <dd>resource mimetype;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">created (iso date string)</dt>
|
||||||
|
* <dd>resource creation time;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">last_modified (iso date string)</dt>
|
||||||
|
* <dd>resource last update time;</dd>
|
||||||
|
*
|
||||||
|
* </dl>
|
||||||
|
*/
|
||||||
@POST
|
@POST
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
public Response create(@PathParam(ITEM_ID_PARAMETER) String itemID, String json) {
|
public Response create(@PathParam(ITEM_ID_PARAMETER) String itemID, String json) {
|
||||||
setCalledMethod("POST /" + COLLECTION);
|
setCalledMethod("POST /" + COLLECTION);
|
||||||
CKANResource ckanResource = new CKANResource(itemID);
|
CKANResource ckanResource = new CKANResource(itemID);
|
||||||
|
@ -46,12 +94,15 @@ public class Resource extends BaseREST implements org.gcube.gcat.api.interfaces.
|
||||||
|
|
||||||
ResponseBuilder responseBuilder = Response.status(Status.CREATED).entity(ret);
|
ResponseBuilder responseBuilder = Response.status(Status.CREATED).entity(ret);
|
||||||
responseBuilder = addLocation(responseBuilder, ckanResource.getResourceID());
|
responseBuilder = addLocation(responseBuilder, ckanResource.getResourceID());
|
||||||
return responseBuilder.type(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8).build();
|
return responseBuilder.type(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/{" + RESOURCE_ID_PARAMETER + "}")
|
@Path("/{" + RESOURCE_ID_PARAMETER + "}")
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
/* Catalogue-Member is not added to VRE members and is assumed as the default role in the catalogue for the VRE members. So we can't enforce
|
||||||
|
* @AuthorizationControl(allowedRoles={Role.CATALOGUE_MEMBER, Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
*/
|
||||||
public String read(@PathParam(ITEM_ID_PARAMETER) String itemID,
|
public String read(@PathParam(ITEM_ID_PARAMETER) String itemID,
|
||||||
@PathParam(RESOURCE_ID_PARAMETER) String resourceID) {
|
@PathParam(RESOURCE_ID_PARAMETER) String resourceID) {
|
||||||
setCalledMethod("GET /" + COLLECTION + "/{" + RESOURCE_ID_PARAMETER + "}");
|
setCalledMethod("GET /" + COLLECTION + "/{" + RESOURCE_ID_PARAMETER + "}");
|
||||||
|
@ -62,8 +113,9 @@ public class Resource extends BaseREST implements org.gcube.gcat.api.interfaces.
|
||||||
|
|
||||||
@PUT
|
@PUT
|
||||||
@Path("/{" + RESOURCE_ID_PARAMETER + "}")
|
@Path("/{" + RESOURCE_ID_PARAMETER + "}")
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
public String update(@PathParam(ITEM_ID_PARAMETER) String itemID,
|
public String update(@PathParam(ITEM_ID_PARAMETER) String itemID,
|
||||||
@PathParam(RESOURCE_ID_PARAMETER) String resourceID, String json) {
|
@PathParam(RESOURCE_ID_PARAMETER) String resourceID, String json) {
|
||||||
setCalledMethod("PUT /" + COLLECTION + "/{" + RESOURCE_ID_PARAMETER + "}");
|
setCalledMethod("PUT /" + COLLECTION + "/{" + RESOURCE_ID_PARAMETER + "}");
|
||||||
|
@ -74,8 +126,9 @@ public class Resource extends BaseREST implements org.gcube.gcat.api.interfaces.
|
||||||
|
|
||||||
@PATCH
|
@PATCH
|
||||||
@Path("/{" + RESOURCE_ID_PARAMETER + "}")
|
@Path("/{" + RESOURCE_ID_PARAMETER + "}")
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
public String patch(@PathParam(ITEM_ID_PARAMETER) String itemID,
|
public String patch(@PathParam(ITEM_ID_PARAMETER) String itemID,
|
||||||
@PathParam(RESOURCE_ID_PARAMETER) String resourceID, String json) {
|
@PathParam(RESOURCE_ID_PARAMETER) String resourceID, String json) {
|
||||||
setCalledMethod("PATCH /" + COLLECTION + "/{" + RESOURCE_ID_PARAMETER + "}");
|
setCalledMethod("PATCH /" + COLLECTION + "/{" + RESOURCE_ID_PARAMETER + "}");
|
||||||
|
@ -86,6 +139,7 @@ public class Resource extends BaseREST implements org.gcube.gcat.api.interfaces.
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("/{" + RESOURCE_ID_PARAMETER + "}")
|
@Path("/{" + RESOURCE_ID_PARAMETER + "}")
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
public Response delete(@PathParam(ITEM_ID_PARAMETER) String itemID,
|
public Response delete(@PathParam(ITEM_ID_PARAMETER) String itemID,
|
||||||
@PathParam(RESOURCE_ID_PARAMETER) String resourceID) {
|
@PathParam(RESOURCE_ID_PARAMETER) String resourceID) {
|
||||||
setCalledMethod("DELETE /" + COLLECTION + "/{" + RESOURCE_ID_PARAMETER + "}");
|
setCalledMethod("DELETE /" + COLLECTION + "/{" + RESOURCE_ID_PARAMETER + "}");
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
package org.gcube.gcat.rest;
|
||||||
|
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.DefaultValue;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
//import javax.ws.rs.NotAuthorizedException;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
import javax.xml.ws.WebServiceException;
|
||||||
|
|
||||||
|
//import org.gcube.common.authorization.control.annotations.AuthorizationControl;
|
||||||
|
import org.gcube.gcat.annotation.PURGE;
|
||||||
|
import org.gcube.gcat.api.GCatConstants;
|
||||||
|
//import org.gcube.gcat.api.roles.Role;
|
||||||
|
import org.gcube.gcat.persistence.ckan.CKANPackageTrash;
|
||||||
|
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceLabel;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This collection allow to interact with thrashed items
|
||||||
|
*
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
|
@Path(Trash.TRASH)
|
||||||
|
@ResourceGroup("Item Related APIs")
|
||||||
|
@ResourceLabel("Trash APIs")
|
||||||
|
public class Trash extends BaseREST implements org.gcube.gcat.api.interfaces.Trash<Response> {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List the thrashed items.<br/>
|
||||||
|
* By default, it lists only the trashed items of the requesting user.<br/>
|
||||||
|
*
|
||||||
|
* The listed items belong to the supported organizations for the
|
||||||
|
* context of the request (i.e. the context where the token has been generated).<br/>
|
||||||
|
*
|
||||||
|
* See <a href="./resource_Configuration.html">supportedOrganizations parameter in
|
||||||
|
* Configuration</a>.
|
||||||
|
*
|
||||||
|
* If the user specifies <code>own_only=false</code> and the user is
|
||||||
|
* <em>Catalogue-Admin</em> or above it return the thrashed items of all the
|
||||||
|
* users for all the supported organizations.
|
||||||
|
*
|
||||||
|
* @param ownOnly indicates that the user is interested only in its own items or the items of all the users.
|
||||||
|
* @return the json array containing the items in the trash
|
||||||
|
*
|
||||||
|
* @pathExample /trash
|
||||||
|
* @responseExample application/json ["item1","item54"]
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public String list(@QueryParam(GCatConstants.OWN_ONLY_QUERY_PARAMETER) @DefaultValue("true") Boolean ownOnly) throws WebServiceException {
|
||||||
|
CKANPackageTrash ckanPackageTrash = new CKANPackageTrash();
|
||||||
|
ckanPackageTrash.setOwnOnly(ownOnly);
|
||||||
|
return ckanPackageTrash.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete in background all items in the trash.<br/>
|
||||||
|
* The operation returns immediately to the client and continues in background.<br/>
|
||||||
|
* There is no way to monitor or stop the running operation.<br/>
|
||||||
|
* The thrashed items cannot be recovered.</br/>
|
||||||
|
*
|
||||||
|
* By default, it empty only the trashed items of the requesting user.<br/>
|
||||||
|
*
|
||||||
|
* The listed items belong to the supported organizations for the
|
||||||
|
* context of the request (i.e. the context where the token has been generated).<br/>
|
||||||
|
*
|
||||||
|
* See <a href="./resource_Configuration.html">supportedOrganizations parameter in
|
||||||
|
* Configuration</a>.
|
||||||
|
*
|
||||||
|
* If the user specifies <code>own_only=false</code> and the user is
|
||||||
|
* <em>Catalogue-Admin</em> or above it empty the thrash of all the
|
||||||
|
* users for all the supported organizations.
|
||||||
|
*
|
||||||
|
* @param ownOnly indicates that the user is interested only in its own items or the items of all the users.
|
||||||
|
* @return 202 Accepted if the request has been accepted successfully
|
||||||
|
* @throws WebServiceException
|
||||||
|
*
|
||||||
|
* @pathExample /trash
|
||||||
|
*/
|
||||||
|
@DELETE
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode ( code = 202, condition = "The empty trash operation has been accepted successfully.")
|
||||||
|
})
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response empty(@QueryParam(GCatConstants.OWN_ONLY_QUERY_PARAMETER) @DefaultValue("true") Boolean ownOnly) throws WebServiceException {
|
||||||
|
Thread thread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
CKANPackageTrash ckanPackageTrash = new CKANPackageTrash();
|
||||||
|
ckanPackageTrash.setOwnOnly(ownOnly);
|
||||||
|
ckanPackageTrash.empty();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
return Response.status(Status.ACCEPTED).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It is the same of
|
||||||
|
* <a href="resource_Trash.html#resource_Trash_empty_ownOnly_DELETE">DELETE</a> operation
|
||||||
|
*/
|
||||||
|
@PURGE
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode ( code = 202, condition = "The empty trash operation has been accepted successfully.")
|
||||||
|
})
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response emptyViaPurge(@QueryParam(GCatConstants.OWN_ONLY_QUERY_PARAMETER) @DefaultValue("true") Boolean ownOnly) throws WebServiceException {
|
||||||
|
return empty(ownOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,65 +0,0 @@
|
||||||
package org.gcube.gcat.rest;
|
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
|
||||||
import javax.ws.rs.DELETE;
|
|
||||||
import javax.ws.rs.GET;
|
|
||||||
import javax.ws.rs.POST;
|
|
||||||
import javax.ws.rs.PUT;
|
|
||||||
import javax.ws.rs.Path;
|
|
||||||
import javax.ws.rs.PathParam;
|
|
||||||
import javax.ws.rs.Produces;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
|
|
||||||
import org.gcube.gcat.ResourceInitializer;
|
|
||||||
import org.gcube.gcat.persistence.ckan.CKANUser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
@Path(User.USERS)
|
|
||||||
public class User extends REST<CKANUser> implements org.gcube.gcat.api.interfaces.User<Response,Response> {
|
|
||||||
|
|
||||||
protected static final String USER_ID_PARAMETER = "USER_ID";
|
|
||||||
|
|
||||||
public User() {
|
|
||||||
super(USERS, USER_ID_PARAMETER, CKANUser.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
public String list() {
|
|
||||||
return super.list(-1, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Override
|
|
||||||
public Response create(String json) {
|
|
||||||
return super.create(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/{" + USER_ID_PARAMETER + "}")
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Override
|
|
||||||
public String read(@PathParam(USER_ID_PARAMETER) String username) {
|
|
||||||
return super.read(username);
|
|
||||||
}
|
|
||||||
|
|
||||||
@PUT
|
|
||||||
@Path("/{" + USER_ID_PARAMETER + "}")
|
|
||||||
@Consumes(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Produces(ResourceInitializer.APPLICATION_JSON_CHARSET_UTF_8)
|
|
||||||
@Override
|
|
||||||
public String update(@PathParam(USER_ID_PARAMETER) String username, String json) {
|
|
||||||
return super.update(username, json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@DELETE
|
|
||||||
@Path("/{" + USER_ID_PARAMETER + "}")
|
|
||||||
public Response delete(@PathParam(USER_ID_PARAMETER) String username) {
|
|
||||||
return super.delete(username, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,578 @@
|
||||||
|
package org.gcube.gcat.rest.administration;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import javax.ws.rs.BadRequestException;
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.DefaultValue;
|
||||||
|
import javax.ws.rs.ForbiddenException;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.InternalServerErrorException;
|
||||||
|
//import javax.ws.rs.NotAuthorizedException;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.WebApplicationException;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||||
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
import javax.xml.ws.WebServiceException;
|
||||||
|
|
||||||
|
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
//import org.gcube.common.authorization.control.annotations.AuthorizationControl;
|
||||||
|
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
|
||||||
|
import org.gcube.gcat.annotation.PATCH;
|
||||||
|
import org.gcube.gcat.annotation.PURGE;
|
||||||
|
import org.gcube.gcat.api.GCatConstants;
|
||||||
|
import org.gcube.gcat.api.roles.Role;
|
||||||
|
import org.gcube.gcat.configuration.CatalogueConfigurationFactory;
|
||||||
|
import org.gcube.gcat.configuration.service.ServiceCatalogueConfiguration;
|
||||||
|
import org.gcube.gcat.persistence.ckan.CKANUser;
|
||||||
|
import org.gcube.gcat.persistence.ckan.cache.CKANUserCache;
|
||||||
|
import org.gcube.gcat.rest.BaseREST;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceLabel;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
|
import com.webcohesion.enunciate.metadata.swagger.OperationId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The catalogue configuration for the context of the request
|
||||||
|
* (i.e. the context where the token has been generated).
|
||||||
|
*
|
||||||
|
* Only Catalogue-Managers are able to invoke non-safe methods.
|
||||||
|
*
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
|
@Path(Configuration.CONFIGURATIONS)
|
||||||
|
@ResourceGroup("Administration APIs")
|
||||||
|
@ResourceLabel("Configuration APIs")
|
||||||
|
public class Configuration extends BaseREST implements org.gcube.gcat.api.interfaces.Configuration<Response,Response> {
|
||||||
|
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(Configuration.class);
|
||||||
|
|
||||||
|
public static final String CONTEXT_FULLNAME_PARAMETER = "CONTEXT_FULLNAME_PARAMETER";
|
||||||
|
|
||||||
|
protected String checkContext(String context) throws WebServiceException {
|
||||||
|
if(context==null || context.compareTo("")==0) {
|
||||||
|
throw new BadRequestException("Please provide a valid context as path parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
String c = SecretManagerProvider.instance.get().getContext();
|
||||||
|
if(context.compareTo(Configuration.CURRENT_CONTEXT_PATH_PARAMETER)==0) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(context.compareTo(c)!=0) {
|
||||||
|
throw new BadRequestException("Context provided as path parameter (i.e. " + context + ") does not match with token request context (i.e. " + c + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String checkContext(String context, ServiceCatalogueConfiguration catalogueConfiguration) {
|
||||||
|
String c = checkContext(context);
|
||||||
|
if(c.compareTo(catalogueConfiguration.getContext())!=0) {
|
||||||
|
throw new BadRequestException("Context provided in the configuration (i.e. " + catalogueConfiguration.getContext() + ") does not match with token request context (i.e. " + c + ")");
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkRole(Role required) {
|
||||||
|
CKANUser ckanUser = CKANUserCache.getCurrrentCKANUser();
|
||||||
|
if(ckanUser.getRole().ordinal() < required.ordinal()) {
|
||||||
|
throw new ForbiddenException("To perform such a request you must have " + required.getPortalRole() + " role");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createOrUpdate(ServiceCatalogueConfiguration catalogueConfiguration) throws WebServiceException {
|
||||||
|
try {
|
||||||
|
ServiceCatalogueConfiguration gotCatalogueConfiguration = CatalogueConfigurationFactory.createOrUpdate(catalogueConfiguration);
|
||||||
|
String configuration = gotCatalogueConfiguration.toJsonString();
|
||||||
|
logger.debug("The new configuration in context {} is {}", catalogueConfiguration.getContext(), configuration);
|
||||||
|
return configuration;
|
||||||
|
}catch (WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
}catch (Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This API allows to create the catalogue configuration for the
|
||||||
|
* context of the request (i.e. the context where the token has been generated)
|
||||||
|
* using the json provided as request body.<br/>
|
||||||
|
*
|
||||||
|
* Only a <a href="../docs/index.html#roles">Catalogue-Manager</a> can invoke this API.<br/>
|
||||||
|
*
|
||||||
|
* The configuration will be persisted in the infrastructure
|
||||||
|
* Information System (IS) in the context of the request.<br/>
|
||||||
|
* * A Configuration is described by the following attributes:
|
||||||
|
*
|
||||||
|
* <dl>
|
||||||
|
*
|
||||||
|
* <dt>context (string)</dt>
|
||||||
|
* <dd>it must contains the same value of requesting context;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">defaultOrganization (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the default ckan organization where an item is created for
|
||||||
|
* the context of the request;
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">supportedOrganizations (array of string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* it defines the list of organization where an item can be created.
|
||||||
|
* All the organizations must be present in the catalogue of the VRE.
|
||||||
|
* It must contains at least the defaultOrganization value;
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">sysAdminToken (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the token can be provided encrypted with the context key as well as plain.
|
||||||
|
* It any case it is persisted encrypted in IS;
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">ckanURL (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the Ckan URL used for the catalogue of the VRE.
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">solrURL (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the Solr URL used by the Ckan instance.
|
||||||
|
* This is normally, but not necessary, the same URL of Ckan plus the path <code>/solr</code>
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">socialPostEnabled (bool)</dt>
|
||||||
|
* <dd>
|
||||||
|
* it indicates if the social posts are enabled in the context of the request.
|
||||||
|
* The creation of a social post can be requested by a user when he/she creates
|
||||||
|
* an item;
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">notificationToUsersEnabled (bool)</dt>
|
||||||
|
* <dd>
|
||||||
|
* it indicates if a notification must be created when a social post
|
||||||
|
* is created. It is ignored if <code>socialPostEnabled=false</code>;
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">moderationEnabled (bool)</dt>
|
||||||
|
* <dd>
|
||||||
|
* it indicates if the catalogue is moderated.
|
||||||
|
* See <a href="../docs/index.html#moderated-catalogues">Moderated Catalogues</a>
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">ckanDB (object)</dt>
|
||||||
|
* <dd>
|
||||||
|
* it contains the url, username and password to contact the db used by the
|
||||||
|
* Ckan instance used for the catalogue of the VRE.
|
||||||
|
* The password can be provided encrypted with the context key as well as plain.
|
||||||
|
* It any case it is persisted encrypted in IS.
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* </dl>
|
||||||
|
*
|
||||||
|
* Here is an example
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "context": "/gcube/devsec/devVRE",
|
||||||
|
* "defaultOrganization": "devvre",
|
||||||
|
* "supportedOrganizations": [ "devvre" "nextnext" ],
|
||||||
|
* "sysAdminToken": "SYS_ADMIN_TOKEN_HERE",
|
||||||
|
* "ckanURL": "https://ckan-d-d4s.d4science.org",
|
||||||
|
* "solrURL": "https://ckan-d-d4s.d4science.org/solr/",
|
||||||
|
* "socialPostEnabled": false,
|
||||||
|
* "notificationToUsersEnabled": true,
|
||||||
|
* "moderationEnabled": true,
|
||||||
|
* "ckanDB": {
|
||||||
|
* "url": "jdbc:postgresql://ckan-d-d4s.d4science.org:5432/ckan_dev",
|
||||||
|
* "username": "ckan",
|
||||||
|
* "password": "PWD_HERE"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param json the configuration representation
|
||||||
|
* @return the created configuration
|
||||||
|
* @throws WebServiceException when the request fails
|
||||||
|
*
|
||||||
|
* @requestExample application/json;charset=UTF-8 classpath:/api-docs-examples/configuration/create-configuration-request.json
|
||||||
|
* @responseExample application/json;charset=UTF-8 classpath:/api-docs-examples/configuration/create-configuration-response.json
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode(code = 201, condition = "Catalogue configuration successfully created."),
|
||||||
|
@ResponseCode(code = 401, condition = "Only Catalogue-Managers can create catalogue configuration."),
|
||||||
|
@ResponseCode(code = 500, condition = "Error while persisting catalogue configuration."),
|
||||||
|
})
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response create(String json) throws WebServiceException {
|
||||||
|
try {
|
||||||
|
ServiceCatalogueConfiguration catalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json, ServiceCatalogueConfiguration.class);
|
||||||
|
checkContext(CURRENT_CONTEXT_PATH_PARAMETER, catalogueConfiguration);
|
||||||
|
String ret = createOrUpdate(catalogueConfiguration);
|
||||||
|
ResponseBuilder responseBuilder = Response.status(Status.CREATED);
|
||||||
|
if(ret!=null) {
|
||||||
|
responseBuilder.entity(ret).type(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8);
|
||||||
|
}
|
||||||
|
return responseBuilder.build();
|
||||||
|
}catch (WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
}catch (Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This API allows to read the catalogue configuration for the
|
||||||
|
* current context (i.e. the context where the token has been generated).<br/>
|
||||||
|
*
|
||||||
|
* Please refers to <a href="#resource_Configuration_create_json_POST">Create Configuration API</a>
|
||||||
|
* for the list of configuration properties and their meaning.<br/>
|
||||||
|
*
|
||||||
|
* This API can be invoked by any user with at least
|
||||||
|
* <a href="../docs/index.html#roles">Catalogue-Editor</a> role.<br/>
|
||||||
|
*
|
||||||
|
* For any non <a href="../docs/index.html#roles">Catalogue-Manager</a> user the configuration
|
||||||
|
* will contain only a subset of the properties of the configuration.
|
||||||
|
* Here is an example:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "context": "/gcube/devsec/devVRE",
|
||||||
|
* "defaultOrganization": "devvre",
|
||||||
|
* "supportedOrganizations": [ "devvre" "nextnext" ],
|
||||||
|
* "ckanURL": "https://ckan-d-d4s.d4science.org",
|
||||||
|
* "solrURL": "https://ckan-d-d4s.d4science.org/solr/",
|
||||||
|
* "socialPostEnabled": false,
|
||||||
|
* "notificationToUsersEnabled": true,
|
||||||
|
* "moderationEnabled": true
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param context must contains the context of the request
|
||||||
|
* (i.e. the context where the token has been generated)
|
||||||
|
* or the placeholder <code>CURRENT_CONTEXT</code>.<br/>
|
||||||
|
* Please note that the context must be URL encoded,
|
||||||
|
* e.g. /gcube/devsec/devVRE -> %2Fgcube%2Fdevsec%2FdevVRE
|
||||||
|
*
|
||||||
|
* @pathExample /configurations/CURRENT_CONTEXT
|
||||||
|
* @responseExample application/json;charset=UTF-8 classpath:/api-docs-examples/configuration/read-configuration-response.json
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}")
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode(code = 200, condition = "Catalogue configuration successfully read."),
|
||||||
|
@ResponseCode(code = 401, condition = "Only User with role Catalogue-Editors or above can read a catalogue configuration."),
|
||||||
|
@ResponseCode(code = 500, condition = "Error while reading catalogue configuration."),
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* @param context
|
||||||
|
* @return
|
||||||
|
* @throws WebServiceException
|
||||||
|
*/
|
||||||
|
public Response read(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context) throws WebServiceException {
|
||||||
|
try {
|
||||||
|
checkContext(context);
|
||||||
|
return read();
|
||||||
|
}catch (WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
}catch (Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response read() throws WebServiceException {
|
||||||
|
try {
|
||||||
|
ServiceCatalogueConfiguration catalogueConfiguration = CatalogueConfigurationFactory.getInstance();
|
||||||
|
String configuration = catalogueConfiguration.toJsonString();
|
||||||
|
logger.debug("Configuration in context {} is {}", catalogueConfiguration.getContext(), configuration);
|
||||||
|
ResponseBuilder responseBuilder = Response.status(Status.OK);
|
||||||
|
if(configuration!=null) {
|
||||||
|
responseBuilder.entity(configuration).type(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8);
|
||||||
|
}
|
||||||
|
return responseBuilder.build();
|
||||||
|
}catch (WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
}catch (Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This API allows to create/update the catalogue configuration for the
|
||||||
|
* context of the request (i.e. the context where the token has been generated)
|
||||||
|
* using the json provided as request body.<br/>
|
||||||
|
*
|
||||||
|
* Only a <a href="../docs/index.html#roles">Catalogue-Manager</a> can invoke this API.<br/>
|
||||||
|
*
|
||||||
|
* The configuration will be persisted in the infrastructure
|
||||||
|
* Information System (IS) in the context of the request.<br/>
|
||||||
|
*
|
||||||
|
* @param context must contains the context of the request
|
||||||
|
* (i.e. the context where the token has been generated)
|
||||||
|
* or the placeholder <code>CURRENT_CONTEXT</code>.<br/>
|
||||||
|
* Please note that the context must be URL encoded,
|
||||||
|
* e.g. /gcube/devsec/devVRE -> %2Fgcube%2Fdevsec%2FdevVRE
|
||||||
|
* @param json the configuration representation
|
||||||
|
* @return the updated/created configuration
|
||||||
|
* @throws WebServiceException when the request fails
|
||||||
|
*
|
||||||
|
* @pathExample /configurations/CURRENT_CONTEXT
|
||||||
|
* @requestExample application/json;charset=UTF-8 classpath:/api-docs-examples/configuration/create-configuration-request.json
|
||||||
|
* @responseExample application/json;charset=UTF-8 classpath:/api-docs-examples/configuration/create-configuration-response.json
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}")
|
||||||
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode(code = 200, condition = "Catalogue configuration successfully created/updated."),
|
||||||
|
@ResponseCode(code = 401, condition = "Only Catalogue-Managers can create/update catalogue configuration."),
|
||||||
|
@ResponseCode(code = 500, condition = "Error while creating/updating catalogue configuration."),
|
||||||
|
})
|
||||||
|
@OperationId("Create or Update")
|
||||||
|
public String createOrUpdate(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context, String json) throws WebServiceException {
|
||||||
|
try {
|
||||||
|
ServiceCatalogueConfiguration catalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json, ServiceCatalogueConfiguration.class);
|
||||||
|
checkContext(context, catalogueConfiguration);
|
||||||
|
return createOrUpdate(catalogueConfiguration);
|
||||||
|
}catch (WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
}catch (Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response update(String json) throws WebServiceException {
|
||||||
|
try {
|
||||||
|
ServiceCatalogueConfiguration catalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(json, ServiceCatalogueConfiguration.class);
|
||||||
|
checkContext(CURRENT_CONTEXT_PATH_PARAMETER);
|
||||||
|
catalogueConfiguration = CatalogueConfigurationFactory.createOrUpdate(catalogueConfiguration);
|
||||||
|
String configuration = catalogueConfiguration.toJsonString();
|
||||||
|
logger.debug("Configuration in context {} has been updated to {}", catalogueConfiguration.getContext(), configuration);
|
||||||
|
ResponseBuilder responseBuilder = Response.status(Status.OK);
|
||||||
|
if(configuration!=null) {
|
||||||
|
responseBuilder.entity(configuration).type(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8);
|
||||||
|
}
|
||||||
|
return responseBuilder.build();
|
||||||
|
}catch (WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
}catch (Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This API allows to patch the catalogue configuration for the
|
||||||
|
* context of the request (i.e. the context where the token has been generated)
|
||||||
|
* using the json provided as request body.<br/>
|
||||||
|
* Only a <a href="../docs/index.html#roles">Catalogue-Manager</a> can invoke this API. <br/>
|
||||||
|
* The configuration will be persisted in the infrastructure
|
||||||
|
* Information System (IS) in the context of the request.<br/>
|
||||||
|
*
|
||||||
|
* Please refers to <a href="#resource_Configuration_create_json_POST">Create Configuration API</a>
|
||||||
|
* for the list of configuration properties and their meaning.
|
||||||
|
*
|
||||||
|
* @param context must contains the context of the request
|
||||||
|
* (i.e. the context where the token has been generated)
|
||||||
|
* or the placeholder <code>CURRENT_CONTEXT</code>.<br/>
|
||||||
|
* Please note that the context must be URL encoded,
|
||||||
|
* e.g. /gcube/devsec/devVRE -> %2Fgcube%2Fdevsec%2FdevVRE
|
||||||
|
* @param json must contains only the properties of the configuratoin the user wants to change.
|
||||||
|
* @return the patched configuration.
|
||||||
|
* @throws WebServiceException when the request fails
|
||||||
|
*
|
||||||
|
* @pathExample /configurations/CURRENT_CONTEXT
|
||||||
|
* @requestExample application/json;charset=UTF-8 classpath:/api-docs-examples/configuration/create-configuration-request.json
|
||||||
|
* @responseExample application/json;charset=UTF-8 classpath:/api-docs-examples/configuration/create-configuration-response.json
|
||||||
|
*/
|
||||||
|
@PATCH
|
||||||
|
@Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}")
|
||||||
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode(code = 200, condition = "Catalogue configuration successfully updated."),
|
||||||
|
@ResponseCode(code = 401, condition = "Only Catalogue-Managers can update catalogue configuration."),
|
||||||
|
@ResponseCode(code = 500, condition = "Error while updating catalogue configuration."),
|
||||||
|
})
|
||||||
|
public Response patch(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context, String json) throws WebServiceException {
|
||||||
|
try {
|
||||||
|
checkContext(context);
|
||||||
|
return patch(json);
|
||||||
|
}catch (WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
}catch (Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response patch(String json) throws WebServiceException {
|
||||||
|
try {
|
||||||
|
ServiceCatalogueConfiguration catalogueConfiguration = CatalogueConfigurationFactory.getInstance();
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
ObjectNode node = (ObjectNode) mapper.readTree(json);
|
||||||
|
|
||||||
|
if(node.has(ServiceCatalogueConfiguration.CONTEXT_KEY)) {
|
||||||
|
String context = node.get(ServiceCatalogueConfiguration.CONTEXT_KEY).asText();
|
||||||
|
String c = SecretManagerProvider.instance.get().getContext();
|
||||||
|
if(c.compareTo(context)!=0) {
|
||||||
|
throw new BadRequestException("Context provided in the configuration (i.e. " + catalogueConfiguration.getContext() + ") does not match with token request context (i.e. " + c + ")");
|
||||||
|
}
|
||||||
|
node.remove(CURRENT_CONTEXT_PATH_PARAMETER);
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectNode configuration = mapper.valueToTree(catalogueConfiguration);
|
||||||
|
|
||||||
|
Iterator<String> fieldNames = node.fieldNames();
|
||||||
|
while(fieldNames.hasNext()) {
|
||||||
|
String fieldName = fieldNames.next();
|
||||||
|
configuration.set(fieldName, node.get(fieldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceCatalogueConfiguration newCatalogueConfiguration = ServiceCatalogueConfiguration.getServiceCatalogueConfiguration(configuration, ServiceCatalogueConfiguration.class);
|
||||||
|
newCatalogueConfiguration = CatalogueConfigurationFactory.createOrUpdate(newCatalogueConfiguration);
|
||||||
|
String ret = newCatalogueConfiguration.toJsonString();
|
||||||
|
logger.debug("Configuration in context {} has been patched to {}", catalogueConfiguration.getContext(), ret);
|
||||||
|
ResponseBuilder responseBuilder = Response.status(Status.OK);
|
||||||
|
if(ret!=null) {
|
||||||
|
responseBuilder.entity(ret).type(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8);
|
||||||
|
}
|
||||||
|
return responseBuilder.build();
|
||||||
|
}catch (WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
}catch (Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It removes from the cache the configuration for the
|
||||||
|
* context of the request (i.e. the context where the token has been generated).<br/>
|
||||||
|
*
|
||||||
|
* This API forces the service to read again from the Information System (IS)
|
||||||
|
* the catalogue configuration for the context of the request.<br/>
|
||||||
|
*
|
||||||
|
* If the user specifies the <code>purge</code> query parameter this API
|
||||||
|
* remove the configuration from the IS. Please note that this implies that
|
||||||
|
* the catalogue is no more configured for the context of the request.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param context context must contains the context of the request
|
||||||
|
* (i.e. the context where the token has been generated)
|
||||||
|
* or the placeholder <code>CURRENT_CONTEXT</code>.<br/>
|
||||||
|
* Please note that the context must be URL encoded,
|
||||||
|
* e.g. /gcube/devsec/devVRE -> %2Fgcube%2Fdevsec%2FdevVRE
|
||||||
|
* @param purge indicates to the service to remvoe the configuration from the IS
|
||||||
|
* @throws WebServiceException
|
||||||
|
*
|
||||||
|
* @pathExample /configurations/CURRENT_CONTEXT
|
||||||
|
*/
|
||||||
|
@DELETE
|
||||||
|
@Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}")
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode(code = 200, condition = "Catalogue configuration successfully deleted."),
|
||||||
|
@ResponseCode(code = 401, condition = "Only Catalogue-Managers can delete catalogue configuration."),
|
||||||
|
@ResponseCode(code = 500, condition = "Error while deleting catalogue configuration."),
|
||||||
|
})
|
||||||
|
public Response delete(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context,
|
||||||
|
@QueryParam(GCatConstants.PURGE_QUERY_PARAMETER) @DefaultValue("false") Boolean purge) throws WebServiceException {
|
||||||
|
try {
|
||||||
|
checkContext(context);
|
||||||
|
if(purge) {
|
||||||
|
return purge();
|
||||||
|
}else {
|
||||||
|
return delete();
|
||||||
|
}
|
||||||
|
}catch (WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
}catch (Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the configuration from cache and force reload
|
||||||
|
@Override
|
||||||
|
public Response delete() throws WebServiceException {
|
||||||
|
try {
|
||||||
|
CatalogueConfigurationFactory.renew();
|
||||||
|
return Response.status(Status.NO_CONTENT).build();
|
||||||
|
}catch (WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
}catch (Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It removes remove the configuration from the IS for the
|
||||||
|
* context of the request (i.e. the context where the token has been generated).<br/>
|
||||||
|
*
|
||||||
|
* Please note that this implies that
|
||||||
|
* the catalogue is no more configured for the context of the request.
|
||||||
|
*
|
||||||
|
* @param context context must contains the context of the request
|
||||||
|
* (i.e. the context where the token has been generated)
|
||||||
|
* or the placeholder <code>CURRENT_CONTEXT</code>.<br/>
|
||||||
|
* Please note that the context must be URL encoded,
|
||||||
|
* e.g. /gcube/devsec/devVRE -> %2Fgcube%2Fdevsec%2FdevVRE
|
||||||
|
* @throws WebServiceException
|
||||||
|
*
|
||||||
|
* @pathExample /configurations/CURRENT_CONTEXT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@PURGE
|
||||||
|
@Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}")
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
@StatusCodes ({
|
||||||
|
@ResponseCode(code = 200, condition = "Catalogue configuration successfully deleted."),
|
||||||
|
@ResponseCode(code = 401, condition = "Only Catalogue-Managers can delete catalogue configuration."),
|
||||||
|
@ResponseCode(code = 500, condition = "Error while deleting catalogue configuration."),
|
||||||
|
})
|
||||||
|
public Response purge(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context) throws WebServiceException {
|
||||||
|
try {
|
||||||
|
checkContext(context);
|
||||||
|
return purge();
|
||||||
|
}catch (WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
}catch (Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the configuration from cache and from IS
|
||||||
|
@Override
|
||||||
|
public Response purge() throws WebServiceException {
|
||||||
|
try {
|
||||||
|
CatalogueConfigurationFactory.purge();
|
||||||
|
return Response.status(Status.NO_CONTENT).build();
|
||||||
|
}catch (WebApplicationException e) {
|
||||||
|
throw e;
|
||||||
|
}catch (Exception e) {
|
||||||
|
throw new InternalServerErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
package org.gcube.gcat.rest.administration;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.DefaultValue;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
//import javax.ws.rs.NotAuthorizedException;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.xml.ws.WebServiceException;
|
||||||
|
|
||||||
|
//import org.gcube.common.authorization.control.annotations.AuthorizationControl;
|
||||||
|
import org.gcube.gcat.annotation.PATCH;
|
||||||
|
import org.gcube.gcat.annotation.PURGE;
|
||||||
|
import org.gcube.gcat.api.GCatConstants;
|
||||||
|
//import org.gcube.gcat.api.roles.Role;
|
||||||
|
import org.gcube.gcat.persistence.ckan.CKANGroup;
|
||||||
|
import org.gcube.gcat.rest.REST;
|
||||||
|
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceLabel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This concept is mutated by Ckan which is used as underling technology to persist items.
|
||||||
|
*
|
||||||
|
* Only Catalogue-Admins or above are able to invoke non-safe methods.
|
||||||
|
*
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Path(Group.GROUPS)
|
||||||
|
@ResourceGroup("Administration APIs")
|
||||||
|
@ResourceLabel("Group APIs")
|
||||||
|
public class Group extends REST<CKANGroup> implements org.gcube.gcat.api.interfaces.Group<Response,Response> {
|
||||||
|
|
||||||
|
protected static final String GROUP_ID_PARAMETER = "GROUP_ID";
|
||||||
|
|
||||||
|
public Group() {
|
||||||
|
super(GROUPS, GROUP_ID_PARAMETER, CKANGroup.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Not used as REST method, implemented to respect {@link org.gcube.gcat.api.interfaces.Item} interface
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int count() throws WebServiceException {
|
||||||
|
CKANGroup ckan = getInstance();
|
||||||
|
return ckan.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public String list(@QueryParam(GCatConstants.LIMIT_QUERY_PARAMETER) @DefaultValue("10") int limit,
|
||||||
|
@QueryParam(GCatConstants.OFFSET_QUERY_PARAMETER) @DefaultValue("0") int offset,
|
||||||
|
@QueryParam(GCatConstants.COUNT_QUERY_PARAMETER) @DefaultValue("false") Boolean countOnly) {
|
||||||
|
if(countOnly) {
|
||||||
|
int count = count();
|
||||||
|
return createCountJson(count);
|
||||||
|
}else {
|
||||||
|
return list(limit, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Not used as REST method, implemented to respect {@link org.gcube.gcat.api.interfaces.Item} interface
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String list(@QueryParam(GCatConstants.LIMIT_QUERY_PARAMETER) @DefaultValue("10") int limit,
|
||||||
|
@QueryParam(GCatConstants.OFFSET_QUERY_PARAMETER) @DefaultValue("0") int offset) {
|
||||||
|
return super.list(limit, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* A group is mainly described by the following attributes (* indicate mandatory attributes):
|
||||||
|
*
|
||||||
|
* <dl>
|
||||||
|
*
|
||||||
|
* <dt>name* (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the name of the group, a string between 2 and 100 characters long,
|
||||||
|
* containing only lowercase alphanumeric characters, '-' and '_' ;
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">id (string)</dt>
|
||||||
|
* <dd>the id of the group;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">title (string)</dt>
|
||||||
|
* <dd>the title of the group;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">description (string)</dt>
|
||||||
|
* <dd>the description of the group;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">image_url (string)</dt>
|
||||||
|
* <dd>the URL to an image to be displayed on the group’s page;</dd>
|
||||||
|
*
|
||||||
|
* <dt>state (string, default: 'active')</dt>
|
||||||
|
* <dd>
|
||||||
|
* the current state of the group, e.g. 'active' or 'deleted',
|
||||||
|
* only active groups show up in search results and other lists of groups,
|
||||||
|
* this parameter will be ignored if you are not authorized
|
||||||
|
* to change the state of the group;
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">extras (list of dataset extra dictionaries)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the group’s extras, extras are arbitrary (key: value) metadata that can be added to groups,
|
||||||
|
* each extra dictionary should have keys 'key' (a string),
|
||||||
|
* 'value' (a string), and optionally 'deleted'.
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* </dl>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response create(String json) {
|
||||||
|
return super.create(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{" + GROUP_ID_PARAMETER + "}")
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR}, exception=NotAuthorizedException.class)
|
||||||
|
public String read(@PathParam(GROUP_ID_PARAMETER) String id) {
|
||||||
|
return super.read(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("/{" + GROUP_ID_PARAMETER + "}")
|
||||||
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public String update(@PathParam(GROUP_ID_PARAMETER) String id, String json) {
|
||||||
|
return super.update(id, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PATCH
|
||||||
|
@Path("/{" + GROUP_ID_PARAMETER + "}")
|
||||||
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public String patch(@PathParam(GROUP_ID_PARAMETER) String id, String json) {
|
||||||
|
return super.patch(id, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("/{" + GROUP_ID_PARAMETER + "}")
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response delete(@PathParam(GROUP_ID_PARAMETER) String id,
|
||||||
|
@QueryParam(GCatConstants.PURGE_QUERY_PARAMETER) @DefaultValue("false") Boolean purge) {
|
||||||
|
return super.delete(id, purge);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PURGE
|
||||||
|
@Path("/{" + GROUP_ID_PARAMETER + "}")
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response purge(@PathParam(GROUP_ID_PARAMETER) String id) {
|
||||||
|
return delete(id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response delete(String name, boolean purge) {
|
||||||
|
return delete(name, new Boolean(purge));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
package org.gcube.gcat.rest.administration;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.DefaultValue;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
//import javax.ws.rs.NotAuthorizedException;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.xml.ws.WebServiceException;
|
||||||
|
|
||||||
|
//import org.gcube.common.authorization.control.annotations.AuthorizationControl;
|
||||||
|
import org.gcube.gcat.annotation.PATCH;
|
||||||
|
import org.gcube.gcat.annotation.PURGE;
|
||||||
|
import org.gcube.gcat.api.GCatConstants;
|
||||||
|
//import org.gcube.gcat.api.roles.Role;
|
||||||
|
import org.gcube.gcat.persistence.ckan.CKANOrganization;
|
||||||
|
import org.gcube.gcat.rest.REST;
|
||||||
|
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceLabel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This concept is mutated by Ckan which is used as underling technology to persist items.
|
||||||
|
*
|
||||||
|
* Only Catalogue-Admins or above are able to invoke non-safe methods.
|
||||||
|
*
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
|
@Path(Organization.ORGANIZATIONS)
|
||||||
|
@ResourceGroup("Administration APIs")
|
||||||
|
@ResourceLabel("Organization APIs")
|
||||||
|
public class Organization extends REST<CKANOrganization>
|
||||||
|
implements org.gcube.gcat.api.interfaces.Organization<Response,Response> {
|
||||||
|
|
||||||
|
public static final String ORGANIZATION_ID_PARAMETER = "ORGANIZATION_ID";
|
||||||
|
|
||||||
|
public Organization() {
|
||||||
|
super(ORGANIZATIONS, ORGANIZATION_ID_PARAMETER, CKANOrganization.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Not used as REST method, implemented to respect {@link org.gcube.gcat.api.interfaces.Item} interface
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int count() throws WebServiceException {
|
||||||
|
CKANOrganization ckan = getInstance();
|
||||||
|
return ckan.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public String list(@QueryParam(GCatConstants.LIMIT_QUERY_PARAMETER) @DefaultValue("10") int limit,
|
||||||
|
@QueryParam(GCatConstants.OFFSET_QUERY_PARAMETER) @DefaultValue("0") int offset,
|
||||||
|
@QueryParam(GCatConstants.COUNT_QUERY_PARAMETER) @DefaultValue("false") Boolean countOnly) {
|
||||||
|
if(countOnly) {
|
||||||
|
int count = count();
|
||||||
|
return createCountJson(count);
|
||||||
|
}else {
|
||||||
|
return list(limit, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Not used as REST method, implemented to respect {@link org.gcube.gcat.api.interfaces.Item} interface
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String list(@QueryParam(GCatConstants.LIMIT_QUERY_PARAMETER) @DefaultValue("10") int limit,
|
||||||
|
@QueryParam(GCatConstants.OFFSET_QUERY_PARAMETER) @DefaultValue("0") int offset) {
|
||||||
|
return super.list(limit, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Organization is mainly described by the following attributes (* indicate mandatory attributes):
|
||||||
|
*
|
||||||
|
* <dl>
|
||||||
|
*
|
||||||
|
* <dt>name* (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the name of the organization, a string between 2 and 100 characters long,
|
||||||
|
* containing only lowercase alphanumeric characters, '-' and '_' ;
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">id (string)</dt>
|
||||||
|
* <dd>the id of the organization;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">title (string)</dt>
|
||||||
|
* <dd>the title of the organization;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">description (string)</dt>
|
||||||
|
* <dd>the description of the organization;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">image_url (string)</dt>
|
||||||
|
* <dd>the URL to an image to be displayed on the organization's page;</dd>
|
||||||
|
*
|
||||||
|
* <dt>state (string, default: 'active')</dt>
|
||||||
|
* <dd>
|
||||||
|
* the current state of the organization, e.g. 'active' or 'deleted',
|
||||||
|
* only active organizations show up in search results and other lists of organizations,
|
||||||
|
* this parameter will be ignored if you are not authorized
|
||||||
|
* to change the state of the organization;
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">extras (list of dataset extra dictionaries)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the organization's extras, extras are arbitrary (key: value)
|
||||||
|
* metadata that can be added to organizations,
|
||||||
|
* each extra dictionary should have keys 'key' (a string),
|
||||||
|
* 'value' (a string), and optionally 'deleted'.
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* </dl>
|
||||||
|
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response create(String json) {
|
||||||
|
return super.create(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{" + ORGANIZATION_ID_PARAMETER + "}")
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public String read(@PathParam(ORGANIZATION_ID_PARAMETER) String id) {
|
||||||
|
return super.read(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("/{" + ORGANIZATION_ID_PARAMETER + "}")
|
||||||
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public String update(@PathParam(ORGANIZATION_ID_PARAMETER) String id, String json) {
|
||||||
|
return super.update(id, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PATCH
|
||||||
|
@Path("/{" + ORGANIZATION_ID_PARAMETER + "}")
|
||||||
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public String patch(@PathParam(ORGANIZATION_ID_PARAMETER) String id, String json) {
|
||||||
|
return super.patch(id, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("/{" + ORGANIZATION_ID_PARAMETER + "}")
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response delete(@PathParam(ORGANIZATION_ID_PARAMETER) String id,
|
||||||
|
@QueryParam(GCatConstants.PURGE_QUERY_PARAMETER) @DefaultValue("false") Boolean purge) {
|
||||||
|
return super.delete(id, purge);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PURGE
|
||||||
|
@Path("/{" + ORGANIZATION_ID_PARAMETER + "}")
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response purge(@PathParam(ORGANIZATION_ID_PARAMETER) String id) {
|
||||||
|
return super.purge(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,164 @@
|
||||||
|
package org.gcube.gcat.rest.administration;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.ForbiddenException;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
//import javax.ws.rs.NotAuthorizedException;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
|
||||||
|
import org.gcube.common.authorization.utils.manager.SecretManager;
|
||||||
|
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
|
||||||
|
import org.gcube.gcat.annotation.PURGE;
|
||||||
|
//import org.gcube.common.authorization.control.annotations.AuthorizationControl;
|
||||||
|
import org.gcube.gcat.api.GCatConstants;
|
||||||
|
import org.gcube.gcat.api.roles.Role;
|
||||||
|
//import org.gcube.gcat.api.roles.Role;
|
||||||
|
import org.gcube.gcat.persistence.ckan.CKANUser;
|
||||||
|
import org.gcube.gcat.persistence.ckan.cache.CKANUserCache;
|
||||||
|
import org.gcube.gcat.rest.REST;
|
||||||
|
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResourceLabel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This collection allows to interact with the catalogue users.
|
||||||
|
*
|
||||||
|
* Only Catalogue-Admins or above are able to invoke non-safe methods.
|
||||||
|
*
|
||||||
|
* @author Luca Frosini (ISTI - CNR)
|
||||||
|
*/
|
||||||
|
@Path(User.USERS)
|
||||||
|
@ResourceGroup("Administration APIs")
|
||||||
|
@ResourceLabel("User APIs")
|
||||||
|
public class User extends REST<CKANUser> implements org.gcube.gcat.api.interfaces.User<Response,Response> {
|
||||||
|
|
||||||
|
protected static final String GCUBE_USERNAME_PARAMETER = "GCUBE_USERNAME";
|
||||||
|
protected static final String CKAN_USER_ID_PARAMETER = "USER_ID";
|
||||||
|
|
||||||
|
public User() {
|
||||||
|
super(USERS, CKAN_USER_ID_PARAMETER, CKANUser.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @pathExample /users
|
||||||
|
* @responseExample application/json;charset=UTF-8 classpath:/api-docs-examples/user/list-user-response.json
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public String list() {
|
||||||
|
return super.list(-1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A User is mainly described by the following attributes (* indicate mandatory attributes):
|
||||||
|
*
|
||||||
|
* <dl>
|
||||||
|
*
|
||||||
|
* <dt>name* (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the name of the user, a string between 2 and 100 characters long,
|
||||||
|
* containing only lowercase alphanumeric characters, '-' and '_' ;
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">id (string)</dt>
|
||||||
|
* <dd>the id of the user;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">fullname (string)</dt>
|
||||||
|
* <dd>the fullname of the user;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">email (string)</dt>
|
||||||
|
* <dd>the email address for the user;</dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">password (string)</dt>
|
||||||
|
* <dd>
|
||||||
|
* the password of the user, a string of at least 4 characters
|
||||||
|
* (parameter only used internally to create a new user in Ckan);
|
||||||
|
* </dd>
|
||||||
|
*
|
||||||
|
* <dt style="margin-top: 5px;">about (string)</dt>
|
||||||
|
* <dd>a description of the user.</dd>
|
||||||
|
*
|
||||||
|
* </dl>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response create(String json) {
|
||||||
|
return super.create(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the profile of the user in Ckan
|
||||||
|
* The <code>apikey</code> to interact directly with Ckan are returned only for Catalogue-Managers
|
||||||
|
* and only for its own profile.
|
||||||
|
*
|
||||||
|
* The service add the user's role in <code>portal_role</code> property when the user read its own profile.
|
||||||
|
* See <a href="../docs/index.html#roles">Roles</a> section for more information.
|
||||||
|
*
|
||||||
|
* @param username Ckan or gCube username are both accepted
|
||||||
|
*
|
||||||
|
* @pathExample /users/luca.frosini
|
||||||
|
* @responseExample application/json;charset=UTF-8 classpath:/api-docs-examples/user/read-user-response.json
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/{" + CKAN_USER_ID_PARAMETER + "}")
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public String read(@PathParam(CKAN_USER_ID_PARAMETER) String username) {
|
||||||
|
return super.read(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("/{" + CKAN_USER_ID_PARAMETER + "}")
|
||||||
|
@Consumes(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Produces(GCatConstants.APPLICATION_JSON_CHARSET_UTF_8)
|
||||||
|
@Override
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public String update(@PathParam(CKAN_USER_ID_PARAMETER) String username, String json) {
|
||||||
|
return super.update(username, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PURGE
|
||||||
|
@Path("/{" + CKAN_USER_ID_PARAMETER + "}")
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response purge(@PathParam(CKAN_USER_ID_PARAMETER) String username) {
|
||||||
|
return super.delete(username, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response removeCallerFromCache() {
|
||||||
|
// Any user can remove self from cache
|
||||||
|
CKANUserCache.removeUserFromCache();
|
||||||
|
return Response.status(Status.NO_CONTENT).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DELETE
|
||||||
|
@Path("/{" + GCUBE_USERNAME_PARAMETER + "}")
|
||||||
|
// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class)
|
||||||
|
public Response removeUserFromCache(@PathParam(GCUBE_USERNAME_PARAMETER) String username) {
|
||||||
|
SecretManager secretManager = SecretManagerProvider.instance.get();
|
||||||
|
org.gcube.common.authorization.utils.user.User user = secretManager.getUser();
|
||||||
|
if(user.getRoles().contains(Role.MANAGER.getPortalRole()) || user.getUsername().compareTo(username)==0) {
|
||||||
|
// Any user can remove self from cache
|
||||||
|
CKANUserCache.removeUserFromCache(username);
|
||||||
|
}else {
|
||||||
|
throw new ForbiddenException("Only " + Role.MANAGER.getPortalRole() + "s are authorized to remove an user from the cache");
|
||||||
|
}
|
||||||
|
return Response.status(Status.NO_CONTENT).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
/**
|
||||||
|
* <h1>gCube Catalogue (gCat) Service</h1>
|
||||||
|
*
|
||||||
|
* <p>Welcome to gCube Catalogue Service (aka gCat) API documentation.</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To get a complete overview of gCat service take a look at
|
||||||
|
* <a href="../docs/index.html">wiki page</a>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.gcube.gcat.rest;
|
|
@ -1,130 +0,0 @@
|
||||||
package org.gcube.gcat.social;
|
|
||||||
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
|
|
||||||
import org.gcube.common.gxhttp.request.GXHTTPStringRequest;
|
|
||||||
import org.gcube.gcat.utils.HTTPUtility;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
||||||
|
|
||||||
public class PortalUser {
|
|
||||||
|
|
||||||
protected static final String RESPONSE_SUCCESS_KEY = "success";
|
|
||||||
protected static final String RESPONSE_MESSAGE_KEY = "message";
|
|
||||||
protected static final String RESPONSE_RESULT_KEY = "result";
|
|
||||||
|
|
||||||
// https://wiki.gcube-system.org/gcube/Social_Networking_Service
|
|
||||||
// private static final String SOCIAL_SERVICE_GET_GCUBE_USER_PROFILE_PATH = "2/users/get-profile";
|
|
||||||
|
|
||||||
protected static final String SOCIAL_SERVICE_GET_OAUTH_USER_PROFILE_PATH = "2/users/get-oauth-profile";
|
|
||||||
|
|
||||||
// This key contains the fullname
|
|
||||||
protected static final String OAUTH_USER_PROFILE_NAME_KEY = "name";
|
|
||||||
protected static final String OAUTH_USER_PROFILE_EMAIL_KEY = "email";
|
|
||||||
protected static final String OAUTH_USER_PROFILE_JOB_TITLE_KEY = "job_title";
|
|
||||||
protected static final String OAUTH_USER_PROFILE_ROLES_KEY = "roles";
|
|
||||||
|
|
||||||
protected final ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
// private JsonNode gCubeUserProfile;
|
|
||||||
protected JsonNode oAuthUserProfile;
|
|
||||||
|
|
||||||
protected String fullName;
|
|
||||||
protected String eMail;
|
|
||||||
protected String jobTitle;
|
|
||||||
|
|
||||||
protected List<String> roles;
|
|
||||||
|
|
||||||
public PortalUser() {
|
|
||||||
this.objectMapper = new ObjectMapper();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
private JsonNode validateGCubeUserProfileResponse(JsonNode jsonNode) {
|
|
||||||
if(jsonNode.get(RESPONSE_SUCCESS_KEY).asBoolean()) {
|
|
||||||
return jsonNode.get(RESPONSE_RESULT_KEY);
|
|
||||||
} else {
|
|
||||||
String message = jsonNode.get(RESPONSE_MESSAGE_KEY).asText();
|
|
||||||
throw new InternalServerErrorException(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private JsonNode getGCubeUserProfile() throws Exception {
|
|
||||||
if(gCubeUserProfile == null) {
|
|
||||||
String socialServiceBasePath = SocialServiceDiscovery.getCurrentScopeSocialServiceBasePath();
|
|
||||||
GXHTTPStringRequest gxhttpStringRequest = HTTPUtility.createGXHTTPStringRequest(socialServiceBasePath,
|
|
||||||
SOCIAL_SERVICE_GET_GCUBE_USER_PROFILE_PATH, false);
|
|
||||||
HttpURLConnection httpURLConnection = gxhttpStringRequest.get();
|
|
||||||
String ret = HTTPUtility.getResultAsString(httpURLConnection);
|
|
||||||
JsonNode jsonNode = objectMapper.readTree(ret);
|
|
||||||
gCubeUserProfile = validateGCubeUserProfileResponse(jsonNode);
|
|
||||||
}
|
|
||||||
return gCubeUserProfile;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
public JsonNode getOAuthUserProfile() {
|
|
||||||
if(oAuthUserProfile == null) {
|
|
||||||
try {
|
|
||||||
String socialServiceBasePath = SocialService.getSocialService().getServiceBasePath();
|
|
||||||
|
|
||||||
GXHTTPStringRequest gxhttpStringRequest = HTTPUtility.createGXHTTPStringRequest(socialServiceBasePath,
|
|
||||||
SOCIAL_SERVICE_GET_OAUTH_USER_PROFILE_PATH, false);
|
|
||||||
HttpURLConnection httpURLConnection = gxhttpStringRequest.get();
|
|
||||||
|
|
||||||
String ret = HTTPUtility.getResultAsString(httpURLConnection);
|
|
||||||
|
|
||||||
oAuthUserProfile = objectMapper.readTree(ret);
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return oAuthUserProfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFullName() {
|
|
||||||
if(fullName == null) {
|
|
||||||
fullName = getOAuthUserProfile().get(OAUTH_USER_PROFILE_NAME_KEY).asText();
|
|
||||||
}
|
|
||||||
return fullName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEMail() {
|
|
||||||
if(eMail == null) {
|
|
||||||
eMail = getOAuthUserProfile().get(OAUTH_USER_PROFILE_EMAIL_KEY).asText();
|
|
||||||
}
|
|
||||||
return eMail;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getRoles() {
|
|
||||||
if(roles == null) {
|
|
||||||
JsonNode jsonNode = getOAuthUserProfile().get(OAUTH_USER_PROFILE_ROLES_KEY);
|
|
||||||
roles = new ArrayList<String>();
|
|
||||||
if(jsonNode.isArray()) {
|
|
||||||
ArrayNode arrayNode = (ArrayNode) jsonNode;
|
|
||||||
if(arrayNode.size() > 0) {
|
|
||||||
Iterator<JsonNode> iterator = arrayNode.iterator();
|
|
||||||
while(iterator.hasNext()) {
|
|
||||||
roles.add(iterator.next().asText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getJobTitle() {
|
|
||||||
if(jobTitle == null) {
|
|
||||||
jobTitle = getOAuthUserProfile().get(OAUTH_USER_PROFILE_JOB_TITLE_KEY).asText();
|
|
||||||
}
|
|
||||||
return jobTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,184 +0,0 @@
|
||||||
package org.gcube.gcat.social;
|
|
||||||
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.gcube.common.gxhttp.request.GXHTTPStringRequest;
|
|
||||||
import org.gcube.gcat.persistence.ckan.CKANInstance;
|
|
||||||
import org.gcube.gcat.persistence.ckan.CKANUserCache;
|
|
||||||
import org.gcube.gcat.utils.Constants;
|
|
||||||
import org.gcube.gcat.utils.ContextUtility;
|
|
||||||
import org.gcube.gcat.utils.HTTPUtility;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class SocialPost extends Thread {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(SocialPost.class);
|
|
||||||
|
|
||||||
public static final String ITEM_URL = "Item URL";
|
|
||||||
|
|
||||||
// https://wiki.gcube-system.org/gcube/Social_Networking_Service#Write_application_post_2
|
|
||||||
protected static final String SOCIAL_SERVICE_WRITE_APPLICATION_POST_PATH = "/2/posts/write-post-app";
|
|
||||||
|
|
||||||
// String.format(NOTIFICATION_MESSAGE, fullName, title, url)
|
|
||||||
protected static final String NOTIFICATION_MESSAGE = "%s just published the item \"%s\"\n"
|
|
||||||
+ "Please find it at %s\n";
|
|
||||||
|
|
||||||
protected static final String SOCIAL_POST_TEXT_KEY = "text";
|
|
||||||
protected static final String SOCIAL_POST_ENABLE_NOTIFICATION_KEY = "enable_notification";
|
|
||||||
protected static final String SOCIAL_POST_RESPONSE_SUCCESS_KEY = "success";
|
|
||||||
protected static final String SOCIAL_POST_RESPONSE_MESSAGE_KEY = "message";
|
|
||||||
|
|
||||||
protected final ObjectMapper objectMapper;
|
|
||||||
|
|
||||||
protected String itemID;
|
|
||||||
protected String itemURL;
|
|
||||||
protected String itemTitle;
|
|
||||||
protected List<String> tags;
|
|
||||||
|
|
||||||
public SocialPost() throws Exception {
|
|
||||||
super();
|
|
||||||
this.objectMapper = new ObjectMapper();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getItemID() {
|
|
||||||
return itemID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setItemID(String itemID) {
|
|
||||||
this.itemID = itemID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getItemURL() {
|
|
||||||
return itemURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setItemURL(String itemURL) {
|
|
||||||
this.itemURL = itemURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getItemTitle() {
|
|
||||||
return itemTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setItemTitle(String itemTitle) {
|
|
||||||
this.itemTitle = itemTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getTags() {
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTags(List<String> tags) {
|
|
||||||
this.tags = tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTags(ArrayNode tags) {
|
|
||||||
this.tags = new ArrayList<>();
|
|
||||||
if(tags != null && tags.size() > 0) {
|
|
||||||
for(int i = 0; i < tags.size(); i++) {
|
|
||||||
JsonNode jsonNode = tags.get(i);
|
|
||||||
String tagName = "";
|
|
||||||
if(jsonNode.has("display_name")) {
|
|
||||||
tagName = jsonNode.get("display_name").asText();
|
|
||||||
} else {
|
|
||||||
tagName = jsonNode.get("name").asText();
|
|
||||||
}
|
|
||||||
this.tags.add(tagName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
CKANInstance instance = CKANInstance.getInstance();
|
|
||||||
|
|
||||||
if(!instance.isSocialPostEnabled()) {
|
|
||||||
logger.info("Social Post are disabled in the context {}", ContextUtility.getCurrentContext());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.info("Going to send Social Post about the Item {} available at {}", itemID, itemURL);
|
|
||||||
|
|
||||||
boolean notifyUsers = instance.isNotificationToUsersEnabled();
|
|
||||||
// write notification post
|
|
||||||
sendSocialPost(notifyUsers);
|
|
||||||
|
|
||||||
} catch(Exception e) {
|
|
||||||
logger.error("Error while executing post creation actions", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendSocialPost(boolean notifyUsers) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
String fullName = CKANUserCache.getCurrrentCKANUser().getPortalUser().getFullName();
|
|
||||||
|
|
||||||
String basePath = SocialService.getSocialService().getServiceBasePath();
|
|
||||||
if(basePath == null) {
|
|
||||||
logger.info("Unable to write a post because there is no social networking service available");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
basePath = basePath.endsWith("/") ? basePath : basePath + "/";
|
|
||||||
|
|
||||||
StringWriter messageWriter = new StringWriter();
|
|
||||||
messageWriter.append(String.format(NOTIFICATION_MESSAGE, fullName, itemTitle, itemURL));
|
|
||||||
|
|
||||||
for(String tag : tags) {
|
|
||||||
tag = tag.trim();
|
|
||||||
tag = tag.replaceAll(" ", "_").replace("_+", "_");
|
|
||||||
if(tag.endsWith("_")) {
|
|
||||||
tag = tag.substring(0, tag.length() - 1);
|
|
||||||
}
|
|
||||||
messageWriter.append("#");
|
|
||||||
messageWriter.append(tag);
|
|
||||||
messageWriter.append(" ");
|
|
||||||
}
|
|
||||||
String message = messageWriter.toString();
|
|
||||||
|
|
||||||
logger.debug("The post that is going to be written is\n{}", message);
|
|
||||||
|
|
||||||
ObjectNode objectNode = objectMapper.createObjectNode();
|
|
||||||
objectNode.put(SOCIAL_POST_TEXT_KEY, message);
|
|
||||||
objectNode.put(SOCIAL_POST_ENABLE_NOTIFICATION_KEY, notifyUsers);
|
|
||||||
|
|
||||||
// Do not use ApplicationMode class here because is a thread and change the current token could impact
|
|
||||||
// on the other threads.
|
|
||||||
|
|
||||||
GXHTTPStringRequest gxhttpStringRequest = GXHTTPStringRequest.newRequest(basePath);
|
|
||||||
gxhttpStringRequest.from(Constants.CATALOGUE_NAME);
|
|
||||||
gxhttpStringRequest.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
|
||||||
gxhttpStringRequest.setSecurityToken(Constants.getCatalogueApplicationToken());
|
|
||||||
gxhttpStringRequest.path(SOCIAL_SERVICE_WRITE_APPLICATION_POST_PATH);
|
|
||||||
|
|
||||||
HttpURLConnection httpURLConnection = gxhttpStringRequest.post(objectMapper.writeValueAsString(objectNode));
|
|
||||||
String ret = HTTPUtility.getResultAsString(httpURLConnection);
|
|
||||||
JsonNode jsonNode = objectMapper.readTree(ret);
|
|
||||||
if(jsonNode.get(SOCIAL_POST_RESPONSE_SUCCESS_KEY).asBoolean()) {
|
|
||||||
logger.info("Post written : {}", message);
|
|
||||||
} else {
|
|
||||||
logger.info("Failed to write the post {}. Reason {}", message,
|
|
||||||
jsonNode.get(SOCIAL_POST_RESPONSE_MESSAGE_KEY).asText());
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
logger.error("Unable to send Social Post", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
package org.gcube.gcat.social;
|
|
||||||
|
|
||||||
import static org.gcube.resources.discovery.icclient.ICFactory.client;
|
|
||||||
import static org.gcube.resources.discovery.icclient.ICFactory.queryFor;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.gcube.common.resources.gcore.GCoreEndpoint;
|
|
||||||
import org.gcube.gcat.utils.ContextUtility;
|
|
||||||
import org.gcube.resources.discovery.client.api.DiscoveryClient;
|
|
||||||
import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Discover the Social Networking Service in the Infrastructure.
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class SocialService {
|
|
||||||
|
|
||||||
private static final String RESOURCE = "jersey-servlet";
|
|
||||||
private static final String SERVICE_NAME = "SocialNetworking";
|
|
||||||
private static final String SERVICE_CLASSE = "Portal";
|
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(SocialService.class);
|
|
||||||
private String serviceBasePath;
|
|
||||||
|
|
||||||
// Map<String contextFullName, SocialService socialService>
|
|
||||||
private static Map<String,SocialService> socialServicePerContext;
|
|
||||||
|
|
||||||
static {
|
|
||||||
socialServicePerContext = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SocialService getSocialService() throws Exception {
|
|
||||||
String contex = ContextUtility.getCurrentContext();
|
|
||||||
SocialService socialService = socialServicePerContext.get(contex);
|
|
||||||
if(socialService == null) {
|
|
||||||
socialService = new SocialService();
|
|
||||||
socialServicePerContext.put(contex, socialService);
|
|
||||||
}
|
|
||||||
return socialService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Discover the gcore endpoint for the social networking service.
|
|
||||||
* @throws Exception the exception
|
|
||||||
*/
|
|
||||||
private SocialService() throws Exception {
|
|
||||||
getServiceBasePathViaGCoreEndpoint();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void getServiceBasePathViaGCoreEndpoint() throws Exception {
|
|
||||||
try {
|
|
||||||
SimpleQuery query = queryFor(GCoreEndpoint.class);
|
|
||||||
query.addCondition(String.format("$resource/Profile/ServiceClass/text() eq '%s'", SERVICE_CLASSE));
|
|
||||||
query.addCondition("$resource/Profile/DeploymentData/Status/text() eq 'ready'");
|
|
||||||
query.addCondition(String.format("$resource/Profile/ServiceName/text() eq '%s'", SERVICE_NAME));
|
|
||||||
query.setResult(
|
|
||||||
"$resource/Profile/AccessPoint/RunningInstanceInterfaces//Endpoint[@EntryName/string() eq \""
|
|
||||||
+ RESOURCE + "\"]/text()");
|
|
||||||
|
|
||||||
DiscoveryClient<String> client = client();
|
|
||||||
List<String> endpoints = client.submit(query);
|
|
||||||
if(endpoints == null || endpoints.isEmpty()) {
|
|
||||||
throw new Exception("Cannot retrieve the GCoreEndpoint SERVICE_NAME: " + SERVICE_NAME
|
|
||||||
+ ", SERVICE_CLASSE: " + SERVICE_CLASSE + ", in scope: " + ContextUtility.getCurrentContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.serviceBasePath = endpoints.get(0);
|
|
||||||
|
|
||||||
if(serviceBasePath == null)
|
|
||||||
throw new Exception("Endpoint:" + RESOURCE + ", is null for SERVICE_NAME: " + SERVICE_NAME
|
|
||||||
+ ", SERVICE_CLASSE: " + SERVICE_CLASSE + ", in scope: " + ContextUtility.getCurrentContext());
|
|
||||||
|
|
||||||
serviceBasePath = serviceBasePath.endsWith("/") ? serviceBasePath : serviceBasePath + "/";
|
|
||||||
|
|
||||||
} catch(Exception e) {
|
|
||||||
String error = "An error occurred during GCoreEndpoint discovery, SERVICE_NAME: " + SERVICE_NAME
|
|
||||||
+ ", SERVICE_CLASSE: " + SERVICE_CLASSE + ", in scope: " + ContextUtility.getCurrentContext() + ".";
|
|
||||||
logger.error(error, e);
|
|
||||||
throw new Exception(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the base path of the service
|
|
||||||
*/
|
|
||||||
public String getServiceBasePath() {
|
|
||||||
return serviceBasePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
package org.gcube.gcat.utils;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
import javax.ws.rs.WebApplicationException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class Constants {
|
|
||||||
|
|
||||||
public static final String CATALOGUE_NAME = "gCat";
|
|
||||||
|
|
||||||
private static final String PROPERTY_FILENAME = "config.properties";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Key : Context
|
|
||||||
* Value : Application Token
|
|
||||||
*/
|
|
||||||
protected static final Map<String,String> applicationTokens;
|
|
||||||
|
|
||||||
public static String getCatalogueApplicationToken() {
|
|
||||||
try {
|
|
||||||
return applicationTokens.get(ContextUtility.getCurrentContext());
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(
|
|
||||||
"Unable to retrieve Application Token for context " + ContextUtility.getCurrentContext(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
applicationTokens = new HashMap<>();
|
|
||||||
Properties properties = new Properties();
|
|
||||||
InputStream input = Constants.class.getClassLoader().getResourceAsStream(PROPERTY_FILENAME);
|
|
||||||
// load a properties file
|
|
||||||
properties.load(input);
|
|
||||||
Enumeration<?> enumeration = properties.propertyNames();
|
|
||||||
while(enumeration.hasMoreElements()) {
|
|
||||||
String context = (String) enumeration.nextElement();
|
|
||||||
String applicationToken = properties.getProperty(context);
|
|
||||||
applicationTokens.put(context, applicationToken);
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new WebApplicationException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
package org.gcube.gcat.utils;
|
|
||||||
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
|
|
||||||
import org.gcube.common.authorization.client.Constants;
|
|
||||||
import org.gcube.common.authorization.client.exceptions.ObjectNotFound;
|
|
||||||
import org.gcube.common.authorization.library.AuthorizationEntry;
|
|
||||||
import org.gcube.common.authorization.library.ClientType;
|
|
||||||
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
|
|
||||||
import org.gcube.common.authorization.library.provider.ClientInfo;
|
|
||||||
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
|
|
||||||
import org.gcube.common.authorization.library.utils.Caller;
|
|
||||||
import org.gcube.common.scope.api.ScopeProvider;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Luca Frosini (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class ContextUtility {
|
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(ContextUtility.class);
|
|
||||||
|
|
||||||
public static void setContext(String token) throws ObjectNotFound, Exception {
|
|
||||||
SecurityTokenProvider.instance.set(token);
|
|
||||||
AuthorizationEntry authorizationEntry = Constants.authorizationService().get(token);
|
|
||||||
ClientInfo clientInfo = authorizationEntry.getClientInfo();
|
|
||||||
logger.debug("User : {} - Type : {}", clientInfo.getId(), clientInfo.getType().name());
|
|
||||||
String qualifier = authorizationEntry.getQualifier();
|
|
||||||
Caller caller = new Caller(clientInfo, qualifier);
|
|
||||||
AuthorizationProvider.instance.set(caller);
|
|
||||||
ScopeProvider.instance.set(getCurrentContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getCurrentContext() {
|
|
||||||
try {
|
|
||||||
String token = SecurityTokenProvider.instance.get();
|
|
||||||
return Constants.authorizationService().get(token).getContext();
|
|
||||||
} catch(Exception e) {
|
|
||||||
String context = ScopeProvider.instance.get();
|
|
||||||
if(context != null) {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ClientInfo getClientInfo() {
|
|
||||||
try {
|
|
||||||
Caller caller = AuthorizationProvider.instance.get();
|
|
||||||
if(caller != null) {
|
|
||||||
return caller.getClient();
|
|
||||||
} else {
|
|
||||||
String token = SecurityTokenProvider.instance.get();
|
|
||||||
AuthorizationEntry authorizationEntry = Constants.authorizationService().get(token);
|
|
||||||
return authorizationEntry.getClientInfo();
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String GET_USERNAME_ERROR = "Unable to retrieve user";
|
|
||||||
|
|
||||||
public static String getUsername() {
|
|
||||||
try {
|
|
||||||
return getClientInfo().getId();
|
|
||||||
} catch(Exception e) {
|
|
||||||
logger.error(GET_USERNAME_ERROR);
|
|
||||||
throw new InternalServerErrorException(GET_USERNAME_ERROR, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isApplication() {
|
|
||||||
try {
|
|
||||||
ClientInfo clientInfo = getClientInfo();
|
|
||||||
return clientInfo.getType() == ClientType.EXTERNALSERVICE;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
package org.gcube.gcat.utils;
|
|
||||||
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
import javax.ws.rs.WebApplicationException;
|
|
||||||
import javax.ws.rs.core.Response.Status;
|
|
||||||
|
|
||||||
public class HTTPCall {
|
|
||||||
|
|
||||||
protected static final String USER_AGENT_KEY = "User-Agent";
|
|
||||||
protected static final String USER_AGENT_NAME = "gCat";
|
|
||||||
|
|
||||||
protected final String address;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When the target service is a gCube Service it adds the HTTP header
|
|
||||||
* to provide gCube authorization token and/or scope
|
|
||||||
*/
|
|
||||||
protected boolean gCubeTargetService;
|
|
||||||
|
|
||||||
public boolean isgCubeTargetService() {
|
|
||||||
return gCubeTargetService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setgCubeTargetService(boolean gCubeTargetService) {
|
|
||||||
this.gCubeTargetService = gCubeTargetService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HTTPCall(String address) {
|
|
||||||
this(address, HTTPCall.USER_AGENT_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected HTTPCall(String address, String userAgent) {
|
|
||||||
this.address = address;
|
|
||||||
this.gCubeTargetService = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected URL getURL(String urlString) throws MalformedURLException {
|
|
||||||
URL url = new URL(urlString);
|
|
||||||
if(url.getProtocol().compareTo("https") == 0) {
|
|
||||||
url = new URL(url.getProtocol(), url.getHost(), url.getDefaultPort(), url.getFile());
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public URL getFinalURL(URL url) {
|
|
||||||
try {
|
|
||||||
URL finalURL = url;
|
|
||||||
|
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
||||||
connection.setInstanceFollowRedirects(false);
|
|
||||||
connection.setRequestProperty(USER_AGENT_KEY, USER_AGENT_NAME);
|
|
||||||
// connection.setRequestMethod(HEAD.class.getSimpleName());
|
|
||||||
|
|
||||||
int responseCode = connection.getResponseCode();
|
|
||||||
|
|
||||||
if(responseCode >= Status.BAD_REQUEST.getStatusCode()) {
|
|
||||||
Status status = Status.fromStatusCode(responseCode);
|
|
||||||
String responseMessage = connection.getResponseMessage();
|
|
||||||
throw new WebApplicationException(responseMessage, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == HttpURLConnection.HTTP_MOVED_PERM
|
|
||||||
|| responseCode == HttpURLConnection.HTTP_SEE_OTHER
|
|
||||||
|| responseCode == Status.TEMPORARY_REDIRECT.getStatusCode() || responseCode == 308) {
|
|
||||||
|
|
||||||
URL newURL = getURL(connection.getHeaderField("Location"));
|
|
||||||
connection.disconnect();
|
|
||||||
finalURL = getFinalURL(newURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalURL;
|
|
||||||
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new InternalServerErrorException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
package org.gcube.gcat.utils;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
|
|
||||||
import javax.ws.rs.WebApplicationException;
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
import javax.ws.rs.core.Response.Status;
|
|
||||||
|
|
||||||
import org.gcube.common.gxhttp.request.GXHTTPStringRequest;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class HTTPUtility {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(HTTPUtility.class);
|
|
||||||
|
|
||||||
public static StringBuilder getStringBuilder(InputStream inputStream) throws IOException {
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
try(BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
|
||||||
String line;
|
|
||||||
while((line = reader.readLine()) != null) {
|
|
||||||
result.append(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GXHTTPStringRequest createGXHTTPStringRequest(String url, String path, boolean post)
|
|
||||||
throws UnsupportedEncodingException {
|
|
||||||
GXHTTPStringRequest gxhttpStringRequest = GXHTTPStringRequest.newRequest(url);
|
|
||||||
gxhttpStringRequest.from(Constants.CATALOGUE_NAME);
|
|
||||||
if(post) {
|
|
||||||
gxhttpStringRequest.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
|
||||||
}
|
|
||||||
gxhttpStringRequest.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON);
|
|
||||||
gxhttpStringRequest.path(path);
|
|
||||||
return gxhttpStringRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getResultAsString(HttpURLConnection httpURLConnection) throws IOException {
|
|
||||||
int responseCode = httpURLConnection.getResponseCode();
|
|
||||||
if(responseCode >= Status.BAD_REQUEST.getStatusCode()) {
|
|
||||||
Status status = Status.fromStatusCode(responseCode);
|
|
||||||
InputStream inputStream = httpURLConnection.getErrorStream();
|
|
||||||
StringBuilder result = getStringBuilder(inputStream);
|
|
||||||
logger.trace(result.toString());
|
|
||||||
throw new WebApplicationException(status);
|
|
||||||
}
|
|
||||||
InputStream inputStream = httpURLConnection.getInputStream();
|
|
||||||
String ret = getStringBuilder(inputStream).toString();
|
|
||||||
logger.trace("Got Respose is {}", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package org.gcube.gcat.utils;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Lucio Lelii (ISTI - CNR)
|
|
||||||
*/
|
|
||||||
public class RandomString {
|
|
||||||
|
|
||||||
private static final char[] symbols;
|
|
||||||
|
|
||||||
static {
|
|
||||||
StringBuilder tmp = new StringBuilder();
|
|
||||||
for(char ch = '0'; ch <= '9'; ++ch)
|
|
||||||
tmp.append(ch);
|
|
||||||
for(char ch = 'a'; ch <= 'z'; ++ch)
|
|
||||||
tmp.append(ch);
|
|
||||||
symbols = tmp.toString().toCharArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Random random = new Random();
|
|
||||||
|
|
||||||
private final char[] buf;
|
|
||||||
|
|
||||||
public RandomString(int length) {
|
|
||||||
if(length < 1)
|
|
||||||
throw new IllegalArgumentException("length < 1: " + length);
|
|
||||||
buf = new char[length];
|
|
||||||
}
|
|
||||||
|
|
||||||
public String nextString() {
|
|
||||||
for(int idx = 0; idx < buf.length; ++idx)
|
|
||||||
buf[idx] = symbols[random.nextInt(symbols.length)];
|
|
||||||
return new String(buf);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
package org.gcube.gcat.utils;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
|
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
|
||||||
import javax.ws.rs.WebApplicationException;
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.gcube.common.gxhttp.request.GXHTTPStringRequest;
|
|
||||||
import org.gcube.gcat.persistence.ckan.CKANInstance;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
public class URIResolver {
|
|
||||||
|
|
||||||
private static final String CATALOGUE_CONTEXT = "gcube_scope";
|
|
||||||
private static final String ENTITY_TYPE = "entity_context";
|
|
||||||
private static final String ENTITY_NAME = "entity_name";
|
|
||||||
|
|
||||||
private static final String DATASET = "dataset";
|
|
||||||
|
|
||||||
protected ObjectMapper mapper;
|
|
||||||
|
|
||||||
public URIResolver() {
|
|
||||||
this.mapper = new ObjectMapper();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected StringBuilder getStringBuilder(InputStream inputStream) throws IOException {
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
try(BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
|
||||||
String line;
|
|
||||||
while((line = reader.readLine()) != null) {
|
|
||||||
result.append(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCatalogueItemURL(String name) {
|
|
||||||
try {
|
|
||||||
String uriResolverURL = CKANInstance.getInstance().getUriResolverURL();
|
|
||||||
|
|
||||||
ObjectNode requestContent = mapper.createObjectNode();
|
|
||||||
requestContent.put(CATALOGUE_CONTEXT, ContextUtility.getCurrentContext());
|
|
||||||
|
|
||||||
requestContent.put(ENTITY_TYPE, DATASET);
|
|
||||||
requestContent.put(ENTITY_NAME, name);
|
|
||||||
|
|
||||||
GXHTTPStringRequest gxhttpStringRequest = GXHTTPStringRequest.newRequest(uriResolverURL);
|
|
||||||
gxhttpStringRequest.from(Constants.CATALOGUE_NAME);
|
|
||||||
gxhttpStringRequest.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
|
|
||||||
gxhttpStringRequest.isExternalCall(true);
|
|
||||||
String body = mapper.writeValueAsString(requestContent);
|
|
||||||
HttpURLConnection httpURLConnection = gxhttpStringRequest.post(body);
|
|
||||||
|
|
||||||
if(httpURLConnection.getResponseCode() != 200) {
|
|
||||||
throw new InternalServerErrorException("Unable to get Item URL via URI Resolver");
|
|
||||||
}
|
|
||||||
|
|
||||||
String url = getStringBuilder(httpURLConnection.getInputStream()).toString();
|
|
||||||
|
|
||||||
return url;
|
|
||||||
} catch(WebApplicationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new WebApplicationException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package org.gcube.gcat.workspace;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.gcube.common.storagehub.model.Metadata;
|
|
||||||
import org.gcube.storagehub.MetadataMatcher;
|
|
||||||
|
|
||||||
public class CatalogueMetadata implements MetadataMatcher {
|
|
||||||
|
|
||||||
public static final String ORIGINAL_URL = "OriginalURL";
|
|
||||||
public static final String ORIGINAL_NAME = "OriginalName";
|
|
||||||
|
|
||||||
public static final String CATALOGUE_ITEM_ID = "CatalogueItemID";
|
|
||||||
public static final String CATALOGUE_RESOURCE_ID = "CatalogueResourceID";
|
|
||||||
public static final String CATALOGUE_RESOURCE_REVISION_ID = "CatalogueResourceRevisionID";
|
|
||||||
|
|
||||||
protected String itemID;
|
|
||||||
|
|
||||||
public CatalogueMetadata(String itemID) {
|
|
||||||
this.itemID = itemID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check(Metadata metadata) {
|
|
||||||
Map<String,Object> map = metadata.getMap();
|
|
||||||
Object obj = map.get(CATALOGUE_ITEM_ID);
|
|
||||||
if(obj!=null && obj.toString().compareTo(itemID) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Metadata getMetadata(URL url, String originalName, String resourceID) {
|
|
||||||
Map<String,Object> map = new HashMap<>();
|
|
||||||
map.put(ORIGINAL_URL, url.toString());
|
|
||||||
map.put(ORIGINAL_NAME, originalName);
|
|
||||||
map.put(CATALOGUE_ITEM_ID, itemID);
|
|
||||||
map.put(CATALOGUE_RESOURCE_ID, resourceID);
|
|
||||||
Metadata metadata = new Metadata(map);
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,134 +0,0 @@
|
||||||
package org.gcube.gcat.workspace;
|
|
||||||
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.gcube.common.gxhttp.request.GXHTTPStringRequest;
|
|
||||||
import org.gcube.common.storagehub.client.dsl.FileContainer;
|
|
||||||
import org.gcube.common.storagehub.model.Metadata;
|
|
||||||
import org.gcube.gcat.utils.Constants;
|
|
||||||
import org.gcube.storagehub.ApplicationMode;
|
|
||||||
import org.gcube.storagehub.StorageHubManagement;
|
|
||||||
import org.glassfish.jersey.media.multipart.ContentDisposition;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class CatalogueStorageHubManagement {
|
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(CatalogueStorageHubManagement.class);
|
|
||||||
|
|
||||||
protected StorageHubManagement storageHubManagement;
|
|
||||||
|
|
||||||
protected String originalFilename;
|
|
||||||
protected String mimeType;
|
|
||||||
|
|
||||||
public String getOriginalFilename() {
|
|
||||||
return originalFilename;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMimeType() {
|
|
||||||
return mimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CatalogueStorageHubManagement() {
|
|
||||||
this.storageHubManagement = new StorageHubManagement();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getOriginalFileName(HttpURLConnection httpURLConnection) throws ParseException {
|
|
||||||
String contentDisposition = httpURLConnection.getHeaderFields().get("Content-Disposition").get(0);
|
|
||||||
contentDisposition = contentDisposition.replaceAll("= ", "=").replaceAll(" =", "=");
|
|
||||||
ContentDisposition formDataContentDisposition = new ContentDisposition(contentDisposition);
|
|
||||||
return formDataContentDisposition.getFileName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public URL ensureResourcePersistence(URL persistedURL, String itemID, String resourceID) throws Exception {
|
|
||||||
ApplicationMode applicationMode = new ApplicationMode(Constants.getCatalogueApplicationToken());
|
|
||||||
try {
|
|
||||||
applicationMode.start();
|
|
||||||
GXHTTPStringRequest gxhttpStringRequest = GXHTTPStringRequest.newRequest(persistedURL.toString());
|
|
||||||
gxhttpStringRequest.from(Constants.CATALOGUE_NAME);
|
|
||||||
gxhttpStringRequest.isExternalCall(true);
|
|
||||||
HttpURLConnection httpURLConnection = gxhttpStringRequest.get();
|
|
||||||
mimeType = httpURLConnection.getContentType().split(";")[0];
|
|
||||||
originalFilename = getOriginalFileName(httpURLConnection);
|
|
||||||
CatalogueMetadata catalogueMetadata = new CatalogueMetadata(itemID);
|
|
||||||
storageHubManagement.setCheckMetadata(catalogueMetadata);
|
|
||||||
Metadata metadata = catalogueMetadata.getMetadata(persistedURL, originalFilename, resourceID);
|
|
||||||
persistedURL = storageHubManagement.persistFile(httpURLConnection.getInputStream(), resourceID, mimeType,
|
|
||||||
metadata);
|
|
||||||
mimeType = storageHubManagement.getMimeType();
|
|
||||||
return persistedURL;
|
|
||||||
} finally {
|
|
||||||
applicationMode.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deleteResourcePersistence(String itemID, String resourceID, String mimeType) throws Exception {
|
|
||||||
ApplicationMode applicationMode = new ApplicationMode(Constants.getCatalogueApplicationToken());
|
|
||||||
try {
|
|
||||||
applicationMode.start();
|
|
||||||
storageHubManagement = new StorageHubManagement();
|
|
||||||
CatalogueMetadata catalogueMetadata = new CatalogueMetadata(itemID);
|
|
||||||
storageHubManagement.setCheckMetadata(catalogueMetadata);
|
|
||||||
storageHubManagement.removePersistedFile(resourceID, mimeType);
|
|
||||||
} finally {
|
|
||||||
applicationMode.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void internalAddRevisionID(String resourceID, String revisionID) throws Exception {
|
|
||||||
FileContainer fileContainer = storageHubManagement.getPersistedFile();
|
|
||||||
if(fileContainer != null) {
|
|
||||||
Metadata metadata = fileContainer.get().getMetadata();
|
|
||||||
Map<String,Object> map = metadata.getMap();
|
|
||||||
map.put(CatalogueMetadata.CATALOGUE_RESOURCE_ID, resourceID);
|
|
||||||
map.put(CatalogueMetadata.CATALOGUE_RESOURCE_REVISION_ID, revisionID);
|
|
||||||
metadata.setMap(map);
|
|
||||||
fileContainer.setMetadata(metadata);
|
|
||||||
} else {
|
|
||||||
logger.warn(
|
|
||||||
"Unable to set revision id {} to the file of resource with id {} because the file was NOT found on storage-hub. This could be related to an item not created via gCat. Skipping it.",
|
|
||||||
revisionID, resourceID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void renameFile(String resourceID, String revisionID) throws Exception {
|
|
||||||
ApplicationMode applicationMode = new ApplicationMode(Constants.getCatalogueApplicationToken());
|
|
||||||
try {
|
|
||||||
applicationMode.start();
|
|
||||||
FileContainer createdfile = storageHubManagement.getPersistedFile();
|
|
||||||
createdfile.rename(resourceID);
|
|
||||||
internalAddRevisionID(resourceID, revisionID);
|
|
||||||
} finally {
|
|
||||||
applicationMode.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addRevisionID(String resourceID, String revisionID) throws Exception {
|
|
||||||
ApplicationMode applicationMode = new ApplicationMode(Constants.getCatalogueApplicationToken());
|
|
||||||
try {
|
|
||||||
applicationMode.start();
|
|
||||||
internalAddRevisionID(resourceID, revisionID);
|
|
||||||
} finally {
|
|
||||||
applicationMode.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FileContainer retrievePersistedFile(String id, String mimeType) throws Exception {
|
|
||||||
ApplicationMode applicationMode = new ApplicationMode(Constants.getCatalogueApplicationToken());
|
|
||||||
try {
|
|
||||||
applicationMode.start();
|
|
||||||
return storageHubManagement.getPersistedFile(id, mimeType);
|
|
||||||
} finally {
|
|
||||||
applicationMode.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public FileContainer getPersistedFile() {
|
|
||||||
return storageHubManagement.getPersistedFile();
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"context": "/gcube/devsec/devVRE",
|
||||||
|
"defaultOrganization": "devvre",
|
||||||
|
"supportedOrganizations": [
|
||||||
|
"nextnext",
|
||||||
|
"devvre"
|
||||||
|
],
|
||||||
|
"sysAdminToken": "SYS_ADMIN_TOKEN_HERE",
|
||||||
|
"ckanURL": "https://ckan-d-d4s.d4science.org",
|
||||||
|
"solrURL": "https://ckan-d-d4s.d4science.org/solr/",
|
||||||
|
"socialPostEnabled": false,
|
||||||
|
"notificationToUsersEnabled": true,
|
||||||
|
"moderationEnabled": false,
|
||||||
|
"ckanDB": {
|
||||||
|
"url": "jdbc:postgresql://ckan-d-d4s.d4science.org:5432/ckan_dev",
|
||||||
|
"username": "ckan",
|
||||||
|
"password": "PWD_HERE"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"id": "584b8503-a490-4a89-8372-e21830fa716c",
|
||||||
|
"context": "/gcube/devsec/devVRE",
|
||||||
|
"defaultOrganization": "devvre",
|
||||||
|
"supportedOrganizations": [
|
||||||
|
"nextnext",
|
||||||
|
"devvre"
|
||||||
|
],
|
||||||
|
"sysAdminToken": "SYS_ADMIN_TOKEN",
|
||||||
|
"ckanURL": "https://ckan-d-d4s.d4science.org",
|
||||||
|
"solrURL": "https://ckan-d-d4s.d4science.org/solr/",
|
||||||
|
"socialPostEnabled": false,
|
||||||
|
"notificationToUsersEnabled": true,
|
||||||
|
"moderationEnabled": false,
|
||||||
|
"ckanDB": {
|
||||||
|
"url": "jdbc:postgresql://ckan-d-d4s.d4science.org:5432/ckan_dev",
|
||||||
|
"username": "ckan",
|
||||||
|
"password": "PWD"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"id": "584b8503-a490-4a89-8372-e21830fa716c",
|
||||||
|
"context": "/gcube/devsec/devVRE",
|
||||||
|
"defaultOrganization": "devvre",
|
||||||
|
"supportedOrganizations": [
|
||||||
|
"nextnext",
|
||||||
|
"devvre"
|
||||||
|
],
|
||||||
|
"ckanURL": "https://ckan-d-d4s.d4science.org",
|
||||||
|
"solrURL": "https://ckan-d-d4s.d4science.org/solr/",
|
||||||
|
"socialPostEnabled": false,
|
||||||
|
"notificationToUsersEnabled": true,
|
||||||
|
"moderationEnabled": false
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name": "my_test_item",
|
||||||
|
"title": "My Test Item",
|
||||||
|
"license_id": "CC-BY-SA-4.0",
|
||||||
|
"private": false,
|
||||||
|
"notes": "A test item of Luca Frosini",
|
||||||
|
"url": "http://www.d4science.org",
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"name": "Test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resources": [],
|
||||||
|
"extras": [
|
||||||
|
{
|
||||||
|
"key": "Language",
|
||||||
|
"value": "EN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system:type",
|
||||||
|
"value": "EmptyProfile"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"rating": 0.0,
|
||||||
|
"license_title": "Creative Commons Attribution Share-Alike 4.0",
|
||||||
|
"maintainer": "Frosini Luca",
|
||||||
|
"relationships_as_object": [],
|
||||||
|
"private": false,
|
||||||
|
"maintainer_email": "luca.frosini@isti.cnr.it",
|
||||||
|
"num_tags": 1,
|
||||||
|
"id": "93b3e578-5cb2-4560-adcb-5512f2449b98",
|
||||||
|
"metadata_created": "2022-09-27T13:54:23.505142",
|
||||||
|
"metadata_modified": "2022-09-27T13:54:23.505150",
|
||||||
|
"author": "Frosini Luca",
|
||||||
|
"author_email": "luca.frosini@isti.cnr.it",
|
||||||
|
"state": "active",
|
||||||
|
"version": null,
|
||||||
|
"creator_user_id": "aabddbb0-b761-4f30-9629-3ca2fdc08eb4",
|
||||||
|
"type": "dataset",
|
||||||
|
"resources": [],
|
||||||
|
"num_resources": 0,
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"vocabulary_id": null,
|
||||||
|
"state": "active",
|
||||||
|
"display_name": "Test",
|
||||||
|
"id": "fec9de86-51a2-41b0-aef4-ba06eb39e16d",
|
||||||
|
"name": "Test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"groups": [],
|
||||||
|
"license_id": "CC-BY-SA-4.0",
|
||||||
|
"relationships_as_subject": [],
|
||||||
|
"organization": {
|
||||||
|
"description": "",
|
||||||
|
"created": "2016-05-30T11:30:41.710079",
|
||||||
|
"title": "devVRE",
|
||||||
|
"name": "devvre",
|
||||||
|
"is_organization": true,
|
||||||
|
"state": "active",
|
||||||
|
"image_url": "",
|
||||||
|
"revision_id": "a7eee485-a6d5-4a7b-8f73-b0ed999d5b03",
|
||||||
|
"type": "organization",
|
||||||
|
"id": "3571cca5-b0ae-4dc6-b791-434a8e062ce5",
|
||||||
|
"approval_status": "approved"
|
||||||
|
},
|
||||||
|
"name": "my_test_item",
|
||||||
|
"isopen": true,
|
||||||
|
"url": "http://www.d4science.org",
|
||||||
|
"notes": "A test item of Luca Frosini",
|
||||||
|
"extras": [
|
||||||
|
{
|
||||||
|
"key": "Item URL",
|
||||||
|
"value": "https://data.dev.d4science.org/ctlg/devVRE/my_test_item"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Language",
|
||||||
|
"value": "EN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system:type",
|
||||||
|
"value": "EmptyProfile"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license_url": "https://creativecommons.org/licenses/by-sa/4.0/",
|
||||||
|
"ratings_count": 0,
|
||||||
|
"title": "My Test Item",
|
||||||
|
"revision_id": "47af0aa8-bf5e-4ff8-adf3-d2f120cbd4a5"
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "my_test_item",
|
||||||
|
"title": "Patched Title of My Test Item"
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"rating": 0.0,
|
||||||
|
"license_title": "Creative Commons Attribution Share-Alike 4.0",
|
||||||
|
"maintainer": "Frosini Luca",
|
||||||
|
"relationships_as_object": [],
|
||||||
|
"private": false,
|
||||||
|
"maintainer_email": "luca.frosini@isti.cnr.it",
|
||||||
|
"num_tags": 1,
|
||||||
|
"id": "93b3e578-5cb2-4560-adcb-5512f2449b98",
|
||||||
|
"metadata_created": "2022-09-27T13:54:23.505142",
|
||||||
|
"metadata_modified": "2022-09-27T13:57:28.825790",
|
||||||
|
"author": "Frosini Luca",
|
||||||
|
"author_email": "luca.frosini@isti.cnr.it",
|
||||||
|
"state": "active",
|
||||||
|
"version": null,
|
||||||
|
"creator_user_id": "aabddbb0-b761-4f30-9629-3ca2fdc08eb4",
|
||||||
|
"type": "dataset",
|
||||||
|
"resources": [],
|
||||||
|
"num_resources": 0,
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"vocabulary_id": null,
|
||||||
|
"state": "active",
|
||||||
|
"display_name": "Test",
|
||||||
|
"id": "fec9de86-51a2-41b0-aef4-ba06eb39e16d",
|
||||||
|
"name": "Test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"groups": [],
|
||||||
|
"license_id": "CC-BY-SA-4.0",
|
||||||
|
"relationships_as_subject": [],
|
||||||
|
"organization": {
|
||||||
|
"description": "",
|
||||||
|
"created": "2016-05-30T11:30:41.710079",
|
||||||
|
"title": "devVRE",
|
||||||
|
"name": "devvre",
|
||||||
|
"is_organization": true,
|
||||||
|
"state": "active",
|
||||||
|
"image_url": "",
|
||||||
|
"revision_id": "a7eee485-a6d5-4a7b-8f73-b0ed999d5b03",
|
||||||
|
"type": "organization",
|
||||||
|
"id": "3571cca5-b0ae-4dc6-b791-434a8e062ce5",
|
||||||
|
"approval_status": "approved"
|
||||||
|
},
|
||||||
|
"name": "my_test_item",
|
||||||
|
"isopen": true,
|
||||||
|
"url": "http://www.d4science.org",
|
||||||
|
"notes": "A test item of Luca Frosini",
|
||||||
|
"extras": [
|
||||||
|
{
|
||||||
|
"key": "Item URL",
|
||||||
|
"value": "https://data.dev.d4science.org/ctlg/devVRE/my_test_item"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Language",
|
||||||
|
"value": "EN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system:type",
|
||||||
|
"value": "EmptyProfile"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license_url": "https://creativecommons.org/licenses/by-sa/4.0/",
|
||||||
|
"ratings_count": 0,
|
||||||
|
"title": "Patched Title of My Test Item",
|
||||||
|
"revision_id": "65c6bce2-c5c6-47e9-9264-5891abe78000"
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"rating": 0.0,
|
||||||
|
"license_title": "Creative Commons Attribution Share-Alike 4.0",
|
||||||
|
"maintainer": "Frosini Luca",
|
||||||
|
"relationships_as_object": [],
|
||||||
|
"private": false,
|
||||||
|
"maintainer_email": "luca.frosini@isti.cnr.it",
|
||||||
|
"num_tags": 1,
|
||||||
|
"id": "93b3e578-5cb2-4560-adcb-5512f2449b98",
|
||||||
|
"metadata_created": "2022-09-27T13:54:23.505142",
|
||||||
|
"metadata_modified": "2022-09-27T13:54:23.505150",
|
||||||
|
"author": "Frosini Luca",
|
||||||
|
"author_email": "luca.frosini@isti.cnr.it",
|
||||||
|
"state": "active",
|
||||||
|
"version": null,
|
||||||
|
"creator_user_id": "aabddbb0-b761-4f30-9629-3ca2fdc08eb4",
|
||||||
|
"type": "dataset",
|
||||||
|
"resources": [],
|
||||||
|
"num_resources": 0,
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"vocabulary_id": null,
|
||||||
|
"state": "active",
|
||||||
|
"display_name": "Test",
|
||||||
|
"id": "fec9de86-51a2-41b0-aef4-ba06eb39e16d",
|
||||||
|
"name": "Test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"groups": [],
|
||||||
|
"license_id": "CC-BY-SA-4.0",
|
||||||
|
"relationships_as_subject": [],
|
||||||
|
"organization": {
|
||||||
|
"description": "",
|
||||||
|
"created": "2016-05-30T11:30:41.710079",
|
||||||
|
"title": "devVRE",
|
||||||
|
"name": "devvre",
|
||||||
|
"is_organization": true,
|
||||||
|
"state": "active",
|
||||||
|
"image_url": "",
|
||||||
|
"revision_id": "a7eee485-a6d5-4a7b-8f73-b0ed999d5b03",
|
||||||
|
"type": "organization",
|
||||||
|
"id": "3571cca5-b0ae-4dc6-b791-434a8e062ce5",
|
||||||
|
"approval_status": "approved"
|
||||||
|
},
|
||||||
|
"name": "my_test_item",
|
||||||
|
"isopen": true,
|
||||||
|
"url": "http://www.d4science.org",
|
||||||
|
"notes": "A test item of Luca Frosini",
|
||||||
|
"extras": [
|
||||||
|
{
|
||||||
|
"key": "Item URL",
|
||||||
|
"value": "https://data.dev.d4science.org/ctlg/devVRE/my_test_item"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Language",
|
||||||
|
"value": "EN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system:type",
|
||||||
|
"value": "EmptyProfile"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license_url": "https://creativecommons.org/licenses/by-sa/4.0/",
|
||||||
|
"ratings_count": 0,
|
||||||
|
"title": "My Test Item",
|
||||||
|
"revision_id": "47af0aa8-bf5e-4ff8-adf3-d2f120cbd4a5"
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"system:cm_item_status":"rejected",
|
||||||
|
"message": "The indicated language does not match with the item resource language"
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"rating": 0.0,
|
||||||
|
"license_title": "Creative Commons Attribution Share-Alike 4.0",
|
||||||
|
"maintainer": "Frosini Luca",
|
||||||
|
"relationships_as_object": [],
|
||||||
|
"private": false,
|
||||||
|
"maintainer_email": "luca.frosini@isti.cnr.it",
|
||||||
|
"num_tags": 1,
|
||||||
|
"id": "93b3e578-5cb2-4560-adcb-5512f2449b98",
|
||||||
|
"metadata_created": "2022-09-27T13:54:23.505142",
|
||||||
|
"metadata_modified": "2022-09-27T13:54:23.505150",
|
||||||
|
"author": "Frosini Luca",
|
||||||
|
"author_email": "luca.frosini@isti.cnr.it",
|
||||||
|
"state": "active",
|
||||||
|
"version": null,
|
||||||
|
"creator_user_id": "aabddbb0-b761-4f30-9629-3ca2fdc08eb4",
|
||||||
|
"type": "dataset",
|
||||||
|
"resources": [],
|
||||||
|
"num_resources": 0,
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"vocabulary_id": null,
|
||||||
|
"state": "active",
|
||||||
|
"display_name": "Test",
|
||||||
|
"id": "fec9de86-51a2-41b0-aef4-ba06eb39e16d",
|
||||||
|
"name": "Test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"groups": [],
|
||||||
|
"license_id": "CC-BY-SA-4.0",
|
||||||
|
"relationships_as_subject": [],
|
||||||
|
"organization": {
|
||||||
|
"description": "",
|
||||||
|
"created": "2016-05-30T11:30:41.710079",
|
||||||
|
"title": "devVRE",
|
||||||
|
"name": "devvre",
|
||||||
|
"is_organization": true,
|
||||||
|
"state": "active",
|
||||||
|
"image_url": "",
|
||||||
|
"revision_id": "a7eee485-a6d5-4a7b-8f73-b0ed999d5b03",
|
||||||
|
"type": "organization",
|
||||||
|
"id": "3571cca5-b0ae-4dc6-b791-434a8e062ce5",
|
||||||
|
"approval_status": "approved"
|
||||||
|
},
|
||||||
|
"name": "my_test_item",
|
||||||
|
"isopen": true,
|
||||||
|
"url": "http://www.d4science.org",
|
||||||
|
"notes": "A test item of Luca Frosini",
|
||||||
|
"extras": [
|
||||||
|
{
|
||||||
|
"key": "Item URL",
|
||||||
|
"value": "https://data.dev.d4science.org/ctlg/devVRE/my_test_item"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Language",
|
||||||
|
"value": "EN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system:type",
|
||||||
|
"value": "EmptyProfile"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license_url": "https://creativecommons.org/licenses/by-sa/4.0/",
|
||||||
|
"ratings_count": 0,
|
||||||
|
"title": "Updated title of My Test Item",
|
||||||
|
"revision_id": "47af0aa8-bf5e-4ff8-adf3-d2f120cbd4a5"
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"rating": 0.0,
|
||||||
|
"license_title": "Creative Commons Attribution Share-Alike 4.0",
|
||||||
|
"maintainer": "Frosini Luca",
|
||||||
|
"relationships_as_object": [],
|
||||||
|
"private": false,
|
||||||
|
"maintainer_email": "luca.frosini@isti.cnr.it",
|
||||||
|
"num_tags": 1,
|
||||||
|
"id": "93b3e578-5cb2-4560-adcb-5512f2449b98",
|
||||||
|
"metadata_created": "2022-09-27T13:54:23.505142",
|
||||||
|
"metadata_modified": "2022-09-27T13:56:52.746494",
|
||||||
|
"author": "Frosini Luca",
|
||||||
|
"author_email": "luca.frosini@isti.cnr.it",
|
||||||
|
"state": "active",
|
||||||
|
"version": null,
|
||||||
|
"creator_user_id": "aabddbb0-b761-4f30-9629-3ca2fdc08eb4",
|
||||||
|
"type": "dataset",
|
||||||
|
"resources": [],
|
||||||
|
"num_resources": 0,
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"vocabulary_id": null,
|
||||||
|
"state": "active",
|
||||||
|
"display_name": "Test",
|
||||||
|
"id": "fec9de86-51a2-41b0-aef4-ba06eb39e16d",
|
||||||
|
"name": "Test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"groups": [],
|
||||||
|
"license_id": "CC-BY-SA-4.0",
|
||||||
|
"relationships_as_subject": [],
|
||||||
|
"organization": {
|
||||||
|
"description": "",
|
||||||
|
"created": "2016-05-30T11:30:41.710079",
|
||||||
|
"title": "devVRE",
|
||||||
|
"name": "devvre",
|
||||||
|
"is_organization": true,
|
||||||
|
"state": "active",
|
||||||
|
"image_url": "",
|
||||||
|
"revision_id": "a7eee485-a6d5-4a7b-8f73-b0ed999d5b03",
|
||||||
|
"type": "organization",
|
||||||
|
"id": "3571cca5-b0ae-4dc6-b791-434a8e062ce5",
|
||||||
|
"approval_status": "approved"
|
||||||
|
},
|
||||||
|
"name": "my_test_item",
|
||||||
|
"isopen": true,
|
||||||
|
"url": "http://www.d4science.org",
|
||||||
|
"notes": "A test item of Luca Frosini",
|
||||||
|
"extras": [
|
||||||
|
{
|
||||||
|
"key": "Item URL",
|
||||||
|
"value": "https://data.dev.d4science.org/ctlg/devVRE/my_test_item"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Language",
|
||||||
|
"value": "EN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "system:type",
|
||||||
|
"value": "EmptyProfile"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license_url": "https://creativecommons.org/licenses/by-sa/4.0/",
|
||||||
|
"ratings_count": 0,
|
||||||
|
"title": "Updated title of My Test Item",
|
||||||
|
"revision_id": "bfa50b95-e936-4843-a405-3d6ff7e427aa"
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,32 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "ClassificationInformation",
|
||||||
|
"title": "Classification Information",
|
||||||
|
"name": "ClassificationInformation",
|
||||||
|
"description": "Classification Information"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "LocationInformation",
|
||||||
|
"title": "Location Information",
|
||||||
|
"name": "LocationInformation",
|
||||||
|
"description": "Location Information"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ContactInformation",
|
||||||
|
"title": "Contact Information",
|
||||||
|
"name": "ContactInformation",
|
||||||
|
"description": "Contact Information"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "OtherInformation",
|
||||||
|
"title": "Other Information",
|
||||||
|
"name": "OtherInformation",
|
||||||
|
"description": "Other Information"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "AvailabilityInformation",
|
||||||
|
"title": "Availability Information",
|
||||||
|
"name": "AvailabilityInformation",
|
||||||
|
"description": "Availability Information"
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,8 @@
|
||||||
|
<metadataformat type="EmptyProfile">
|
||||||
|
<metadatafield>
|
||||||
|
<fieldName>test</fieldName>
|
||||||
|
<mandatory>false</mandatory>
|
||||||
|
<dataType>String</dataType>
|
||||||
|
<note>Test field</note>
|
||||||
|
</metadatafield>
|
||||||
|
</metadataformat>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<metadataformat type="EmptyProfile">
|
||||||
|
<metadatafield>
|
||||||
|
<fieldName>test</fieldName>
|
||||||
|
<mandatory>false</mandatory>
|
||||||
|
<dataType>String</dataType>
|
||||||
|
<note>Test field</note>
|
||||||
|
</metadatafield>
|
||||||
|
</metadataformat>
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"metadataformat": {
|
||||||
|
"metadatafield": {
|
||||||
|
"note": "Test field",
|
||||||
|
"fieldName": "test",
|
||||||
|
"dataType": "String",
|
||||||
|
"mandatory": false
|
||||||
|
},
|
||||||
|
"type": "EmptyProfile"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<metadataformat type="EmptyProfile">
|
||||||
|
<metadatafield>
|
||||||
|
<fieldName>test</fieldName>
|
||||||
|
<mandatory>false</mandatory>
|
||||||
|
<dataType>String</dataType>
|
||||||
|
<note>Test field</note>
|
||||||
|
</metadatafield>
|
||||||
|
</metadataformat>
|
|
@ -0,0 +1,32 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"openid": null,
|
||||||
|
"about": "",
|
||||||
|
"display_name": "Blue Mister",
|
||||||
|
"name": "mister_blue",
|
||||||
|
"created": "2022-04-08T14:31:03.978191",
|
||||||
|
"email_hash": "XXXXXXX",
|
||||||
|
"sysadmin": false,
|
||||||
|
"activity_streams_email_notifications": false,
|
||||||
|
"state": "active",
|
||||||
|
"number_of_edits": 28,
|
||||||
|
"fullname": "Blue Mister",
|
||||||
|
"id": "c9f7a81d-75f6-4cf8-aab9-b4c90dce9405",
|
||||||
|
"number_created_packages": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"openid": null,
|
||||||
|
"about": "",
|
||||||
|
"display_name": "Brown Mister",
|
||||||
|
"name": "mister_brown",
|
||||||
|
"created": "2022-04-12T17:06:51.075728",
|
||||||
|
"email_hash": "XXXXXXX",
|
||||||
|
"sysadmin": false,
|
||||||
|
"activity_streams_email_notifications": false,
|
||||||
|
"state": "active",
|
||||||
|
"number_of_edits": 0,
|
||||||
|
"fullname": "Brown Mister",
|
||||||
|
"id": "7445ef41-08d8-4559-8750-3232eb0e1914",
|
||||||
|
"number_created_packages": 0
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"openid": null,
|
||||||
|
"about": "",
|
||||||
|
"apikey": "XXXXXXX",
|
||||||
|
"display_name": "Frosini Luca",
|
||||||
|
"name": "luca_frosini",
|
||||||
|
"created": "2016-12-09T15:18:21.915742",
|
||||||
|
"email_hash": "XXXXXXX",
|
||||||
|
"email": "XXXXXXX@XXXXXXX",
|
||||||
|
"sysadmin": false,
|
||||||
|
"activity_streams_email_notifications": false,
|
||||||
|
"state": "active",
|
||||||
|
"number_of_edits": 1622,
|
||||||
|
"fullname": "Frosini Luca",
|
||||||
|
"id": "aabddbb0-b761-4f30-9629-3ca2fdc08eb4",
|
||||||
|
"number_created_packages": 4
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"type" : "QueryTemplate",
|
||||||
|
"name" : "GCat-Get-CallsFor-to-VirtualService",
|
||||||
|
"description" : "The following query return all CallsFor relation from the EService of gcat identified by the UUID provided as parameter to the Virtual Service with name 'catalogue-virtual-service'. The content of the request to run this query template will be something like {\"$uuid\": \"335580b6-c164-4506-980a-21e5bcf8dbcf\"}",
|
||||||
|
"template" : {
|
||||||
|
"type" : "CallsFor",
|
||||||
|
"source" : {
|
||||||
|
"type" : "EService",
|
||||||
|
"id" : "$uuid",
|
||||||
|
"consistsOf" : [ {
|
||||||
|
"type" : "IsIdentifiedBy",
|
||||||
|
"target" : {
|
||||||
|
"type" : "SoftwareFacet",
|
||||||
|
"group" : "org.gcube.data-catalogue",
|
||||||
|
"name" : "gcat"
|
||||||
|
}
|
||||||
|
} ]
|
||||||
|
},
|
||||||
|
"target" : {
|
||||||
|
"type" : "VirtualService",
|
||||||
|
"consistsOf" : [ {
|
||||||
|
"type" : "IsIdentifiedBy",
|
||||||
|
"target" : {
|
||||||
|
"type" : "SoftwareFacet",
|
||||||
|
"group" : "org.gcube.data-catalogue",
|
||||||
|
"name" : "catalogue-virtual-service"
|
||||||
|
}
|
||||||
|
} ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"templateVariables" : {
|
||||||
|
"$uuid" : {
|
||||||
|
"type": "TemplateVariable",
|
||||||
|
"name": "$uuid",
|
||||||
|
"description": "The uuid of the gCat EService, e.g. 335580b6-c164-4506-980a-21e5bcf8dbcf. Please note that the default value has no meaning but we need a valid value so that the query can be tested by the resource-registry service.",
|
||||||
|
"defaultValue": "335580b6-c164-4506-980a-21e5bcf8dbcf"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"type": "VirtualService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "org.gcube.data-catalogue",
|
||||||
|
"name": "catalogue-virtual-service"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"type": "Configuration",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "IdentifierFacet",
|
||||||
|
"value": "catalogue-configuration"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isRelatedTo": [
|
||||||
|
{
|
||||||
|
"type": "IsCustomizedBy",
|
||||||
|
"source": {
|
||||||
|
"type": "VirtualService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "org.gcube.data-catalogue",
|
||||||
|
"name": "catalogue-virtual-service"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"type": "SimpleFacet",
|
||||||
|
"_source": {
|
||||||
|
"type": "ConsistsOf",
|
||||||
|
"source": {
|
||||||
|
"type": "Configuration",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "IdentifierFacet",
|
||||||
|
"value": "gcat-configuration"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isRelatedTo": [
|
||||||
|
{
|
||||||
|
"type": "IsCustomizedBy",
|
||||||
|
"source": {
|
||||||
|
"type": "VirtualService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "org.gcube.data-catalogue",
|
||||||
|
"name": "catalogue-virtual-service"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"type": "EService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "ckan-organization",
|
||||||
|
"name": "ckan"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isRelatedTo": [
|
||||||
|
{
|
||||||
|
"type": "CallsFor",
|
||||||
|
"source": {
|
||||||
|
"type": "VirtualService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "org.gcube.data-catalogue",
|
||||||
|
"name": "catalogue-virtual-service"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"type": "AccessPointFacet",
|
||||||
|
"_source": {
|
||||||
|
"type": "ConsistsOf",
|
||||||
|
"source": {
|
||||||
|
"type": "EService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "ckan-organization",
|
||||||
|
"name": "ckan"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isRelatedTo": [
|
||||||
|
{
|
||||||
|
"type": "CallsFor",
|
||||||
|
"source": {
|
||||||
|
"type": "VirtualService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "org.gcube.data-catalogue",
|
||||||
|
"name": "catalogue-virtual-service"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"type": "EService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "org.postgresql",
|
||||||
|
"name": "postgresql"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isRelatedTo": [
|
||||||
|
{
|
||||||
|
"type": "Uses",
|
||||||
|
"source": {
|
||||||
|
"type": "EService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "ckan-organization",
|
||||||
|
"name": "ckan"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isRelatedTo": [
|
||||||
|
{
|
||||||
|
"type": "CallsFor",
|
||||||
|
"source": {
|
||||||
|
"type": "VirtualService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "org.gcube.data-catalogue",
|
||||||
|
"name": "catalogue-virtual-service"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"type": "AccessPointFacet",
|
||||||
|
"_source": {
|
||||||
|
"type": "ConsistsOf",
|
||||||
|
"source": {
|
||||||
|
"type": "EService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "org.postgresql",
|
||||||
|
"name": "postgresql"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isRelatedTo": [
|
||||||
|
{
|
||||||
|
"type": "Uses",
|
||||||
|
"source": {
|
||||||
|
"type": "EService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "ckan-organization",
|
||||||
|
"name": "ckan"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isRelatedTo": [
|
||||||
|
{
|
||||||
|
"type": "CallsFor",
|
||||||
|
"source": {
|
||||||
|
"type": "VirtualService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "org.gcube.data-catalogue",
|
||||||
|
"name": "catalogue-virtual-service"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"type": "EService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "org.apache.solr",
|
||||||
|
"name": "solr"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isRelatedTo": [
|
||||||
|
{
|
||||||
|
"type": "Uses",
|
||||||
|
"source": {
|
||||||
|
"type": "EService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "ckan-organization",
|
||||||
|
"name": "ckan"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isRelatedTo": [
|
||||||
|
{
|
||||||
|
"type": "CallsFor",
|
||||||
|
"source": {
|
||||||
|
"type": "VirtualService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "org.gcube.data-catalogue",
|
||||||
|
"name": "catalogue-virtual-service"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"type": "AccessPointFacet",
|
||||||
|
"_source": {
|
||||||
|
"type": "ConsistsOf",
|
||||||
|
"source": {
|
||||||
|
"type": "EService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "org.apache.solr",
|
||||||
|
"name": "solr"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isRelatedTo": [
|
||||||
|
{
|
||||||
|
"type": "Uses",
|
||||||
|
"source": {
|
||||||
|
"type": "EService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "ckan-organization",
|
||||||
|
"name": "ckan"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isRelatedTo": [
|
||||||
|
{
|
||||||
|
"type": "CallsFor",
|
||||||
|
"source": {
|
||||||
|
"type": "VirtualService",
|
||||||
|
"consistsOf": [
|
||||||
|
{
|
||||||
|
"type": "IsIdentifiedBy",
|
||||||
|
"target": {
|
||||||
|
"type": "SoftwareFacet",
|
||||||
|
"group": "org.gcube.data-catalogue",
|
||||||
|
"name": "catalogue-virtual-service"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
---------------------------
|
||||||
|
IsCustomizedBy | |
|
||||||
|
-----------------> | catalogue-configuration |
|
||||||
|
/ | |
|
||||||
|
/ ---------------------------
|
||||||
|
EService VirtualService /
|
||||||
|
------------ -----------------------------
|
||||||
|
| | CallsFor | |
|
||||||
|
| gcat | ------------> | catalogue-virtual-service |
|
||||||
|
| | | |
|
||||||
|
------------ -----------------------------
|
||||||
|
\ EService
|
||||||
|
\ --------------------
|
||||||
|
\ Uses | |
|
||||||
|
\ ------------------> | postgres-ckan-db |
|
||||||
|
\ / | |
|
||||||
|
\ EService / --------------------
|
||||||
|
\ -----------------
|
||||||
|
\ CallsFor | |
|
||||||
|
-------------> | ckan |
|
||||||
|
| |
|
||||||
|
----------------- EService
|
||||||
|
\ --------------------
|
||||||
|
\ Uses | |
|
||||||
|
------------------> | solr |
|
||||||
|
| |
|
||||||
|
--------------------
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
/README.md
|
/README.md
|
||||||
/changelog.xml
|
/changelog.xml
|
||||||
/gcube-app.xml
|
/gcube-app.xml
|
||||||
|
/CHANGELOG.md
|
||||||
|
|
|
@ -4,6 +4,14 @@
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>org.gcube.gcat.ResourceInitializer</servlet-name>
|
<servlet-name>org.gcube.gcat.ResourceInitializer</servlet-name>
|
||||||
</servlet>
|
</servlet>
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>default</servlet-name>
|
||||||
|
<url-pattern>/docs/*</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>default</servlet-name>
|
||||||
|
<url-pattern>/api-docs/*</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
<servlet-mapping>
|
<servlet-mapping>
|
||||||
<servlet-name>org.gcube.gcat.ResourceInitializer</servlet-name>
|
<servlet-name>org.gcube.gcat.ResourceInitializer</servlet-name>
|
||||||
<url-pattern>/*</url-pattern>
|
<url-pattern>/*</url-pattern>
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
.d4science_intro {
|
||||||
|
top: 0;
|
||||||
|
z-index: 2000;
|
||||||
|
position: fixed;
|
||||||
|
display: block ruby;
|
||||||
|
padding: 10px;
|
||||||
|
background: white;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-fixed-top {
|
||||||
|
top: 100px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
top: 160px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
margin-bottom: 40px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
top: 90px;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue